feat(tables): name a filter whose column is off screen, and let its chip clear it

A tabletHidden/narrowHidden column below its width, or any column in a card
layout, took its header funnel with it: the rows were narrowed by a filter
nobody could see. The cue is shown by the inverse of the column's own
container query, so it agrees with the table at every width and text size.
Wired into DataTable, the playlist table and the channel cards (E28 F2).
This commit is contained in:
2026-09-27 16:22:26 +02:00
parent 27483ae1d6
commit 51f881681b
10 changed files with 342 additions and 40 deletions
+22 -3
View File
@@ -51,15 +51,26 @@ export interface Column<T> {
// The classes that hide a column below its width (`narrowHidden` / `tabletHidden`), for its cells and,
// in a fixed layout, for its <col> too: a hidden cell under a <col> that keeps its width would leave an
// empty stripe. The table's OWN width decides, so the caller wraps the table in an `@container`.
// `cue` is the inverse: shown ONLY while the column is hidden — the hidden-filter cue (E28 F2) names a
// filter whose funnel went off screen, and by the same container query, so it cannot disagree with
// the table about which columns are gone (not at any text size: both are rem).
// Whole literals, because Tailwind only generates a class it can read in one piece.
const HIDE_BELOW = {
narrow: { cell: " hidden @2xl:table-cell", col: " hidden @2xl:table-column" },
tablet: { cell: " hidden @5xl:table-cell", col: " hidden @5xl:table-column" },
narrow: {
cell: " hidden @2xl:table-cell",
col: " hidden @2xl:table-column",
cue: " @2xl:hidden",
},
tablet: {
cell: " hidden @5xl:table-cell",
col: " hidden @5xl:table-column",
cue: " @5xl:hidden",
},
} as const;
export function columnHideClass(
col: { narrowHidden?: boolean; tabletHidden?: boolean },
part: "cell" | "col" = "cell",
part: "cell" | "col" | "cue" = "cell",
): string {
if (col.narrowHidden) return HIDE_BELOW.narrow[part];
if (col.tabletHidden) return HIDE_BELOW.tablet[part];
@@ -86,3 +97,11 @@ export function filterRows<T>(rows: T[], columns: Column<T>[], filters: FilterMa
}),
);
}
/** What a set filter says, in the words its funnel uses: an option's label, not its value. */
export function filterValueLabel<T>(col: Column<T>, value: string | string[]): string {
const f = col.filter;
if (!f || f.kind === "text") return String(value);
const label = (v: string) => f.options.find((o) => o.value === v)?.label ?? v;
return Array.isArray(value) ? value.map(label).join(", ") : label(value);
}