From 710c76fb662807b0ab62037e35d556c7661fafe2 Mon Sep 17 00:00:00 2001 From: xarmian Date: Sat, 30 May 2026 22:47:00 -0400 Subject: [PATCH] feat(board): per-lane sort override in the kebab menu (TASK-1673) (#677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a "Sort lane by" drill-down to LaneActionsMenu — an ephemeral per-lane override on top of the page-wide sort. BoardView holds the override map (Record, not persisted), applies the effective mode (override ?? page default) per lane in propColumnData, and disables drag per-lane under a non-manual effective sort. The submenu lists the same options as the toolbar (Priority hidden when no priority field) plus a "Page default" entry to clear the override, with a check on the active one. Sort is a view preference, available to everyone — so the kebab now shows for any non-empty lane (not just editors), and the menu's separators are section-gated so nothing dangles when a viewer sees only Sort. --- .../components/collections/BoardView.svelte | 43 +++---- .../collections/LaneActionsMenu.svelte | 110 +++++++++++++++--- 2 files changed, 115 insertions(+), 38 deletions(-) diff --git a/web/src/lib/components/collections/BoardView.svelte b/web/src/lib/components/collections/BoardView.svelte index fefe53cd..1a4552dd 100644 --- a/web/src/lib/components/collections/BoardView.svelte +++ b/web/src/lib/components/collections/BoardView.svelte @@ -82,22 +82,18 @@ openMenuColumn = null; } - // Any bulk verb wired (each encodes its own owner/editor permission). - // The bulk menu entries only render for a NON-empty lane, so kebab - // visibility is computed per-lane below as `onCreateInColumn || - // (laneHasItems && hasBulkActions)` — otherwise a role-only bulk - // editor would get a ⋯ that opens an empty panel on an empty lane - // (TASK-1672 / Codex round 5). - let hasBulkActions = $derived( - !!( - onArchiveColumn || - onMoveColumn || - onTagColumn || - onUntagColumn || - onSetPriorityColumn || - onAssignColumn - ) - ); + // Ephemeral per-lane sort overrides (TASK-1673): a lane sorts by its + // override when set, else the page-wide `sortMode`. Not persisted — + // cleared on reload. Available to everyone (sort is a view preference). + let laneSortOverrides = $state>({}); + function setLaneSort(colValue: string, mode: SortMode | null) { + if (mode === null) { + delete laneSortOverrides[colValue]; + } else { + laneSortOverrides[colValue] = mode; + } + } + const laneSortFor = (colValue: string): SortMode => laneSortOverrides[colValue] ?? sortMode; // Dismiss the open lane menu on any click outside it (mirrors the // QuickActionsMenu pattern). The menu markup lives under @@ -201,12 +197,12 @@ } // `preserveOrder` opts out of the in-column sort so search rank // from the parent isn't overridden — TASK-1367. Otherwise sort - // each lane by the page-wide sort mode (TASK-1670); 'manual' + // each lane by its effective mode — the per-lane override if set, + // else the page-wide sort (TASK-1670 / TASK-1673); 'manual' // resolves to the stored sort_order, preserving prior behavior. if (!preserveOrder) { - const cmp = itemComparator(sortMode, collection); for (const col of columns) { - result[col].sort(cmp); + result[col].sort(itemComparator(laneSortFor(col), collection)); } } return result; @@ -339,7 +335,9 @@ onclick={() => onCreateInColumn?.(colValue)} >+ {/if} - {#if onCreateInColumn || (colItems.length > 0 && hasBulkActions)} + + {#if onCreateInColumn || colItems.length > 0}