From f7005f395feb67492f07dc70bfd611b9db017ed9 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 16 Jul 2026 09:39:07 +0200 Subject: [PATCH] feat(member-live): sort the composite Member column by first or last name --- assets/css/app.css | 15 ++ assets/js/app.js | 121 +++++++++++++++ .../components/member_name_sort_header.ex | 142 ++++++++++++++++++ lib/mv_web/live/member_live/index.ex | 32 +++- lib/mv_web/live/member_live/index.html.heex | 9 +- .../live/member_live/index/overview_query.ex | 13 +- priv/gettext/de/LC_MESSAGES/default.po | 30 +++- priv/gettext/default.pot | 30 +++- priv/gettext/en/LC_MESSAGES/default.po | 30 +++- .../index_sort_name_address_test.exs | 43 +++++- test/mv_web/member_live/index_test.exs | 12 +- 11 files changed, 443 insertions(+), 34 deletions(-) create mode 100644 lib/mv_web/live/components/member_name_sort_header.ex diff --git a/assets/css/app.css b/assets/css/app.css index 5d7ff107..f39dacbd 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -926,6 +926,21 @@ pointer-events: none; } +/* + * Sub-field sort menu (composite Member column). A native Popover-API element in + * the browser TOP LAYER so it escapes the members table's overflow; placement is + * pure CSS anchor positioning (the "⋮" trigger carries anchor-name, this popover + * carries position-anchor). `position-area` drops it just below the trigger, + * right-aligned, and `position-try-fallbacks` flips it above when there is no + * room below. The SubfieldMenu hook only toggles it — no coordinate math. This + * mirrors the payment-badge popover approach (§548) for a consistent look/feel. + */ +.popover-menu { + margin: 0; + position-area: bottom span-left; + position-try-fallbacks: flip-block; +} + /* * Vertically center the row checkbox in the sticky first column using the * table-native vertical-align: middle. This keeps display: table-cell intact so diff --git a/assets/js/app.js b/assets/js/app.js index 73e52fa5..85ea1875 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -407,6 +407,12 @@ Hooks.PopoverTooltip = { this.el.addEventListener("mouseleave", this.hide) this.el.addEventListener("blur", this.hide) this.el.addEventListener("keydown", this.onKey) + // Hide on activation: a click (sort/reset) triggers a LiveView re-render that + // re-emits the tooltip element without the JS-set inline top/left, which would + // otherwise leave the still-open popover stuck at the default (0,0) position, + // covering the header. Closing it here keeps it out of the re-render; the next + // hover repositions it cleanly. + this.el.addEventListener("click", this.hide) }, destroyed() { @@ -416,6 +422,121 @@ Hooks.PopoverTooltip = { this.el.removeEventListener("mouseleave", this.hide) this.el.removeEventListener("blur", this.hide) this.el.removeEventListener("keydown", this.onKey) + this.el.removeEventListener("click", this.hide) + } +} + +// SubfieldMenu hook: toggles a native Popover menu (top layer, so it escapes the +// overview table's overflow clipping) for choosing the composite Member column's +// sort sub-field. Attached to the "⋮" trigger; data-menu-id names the