Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: persist table filters, add reset button, multi-select, negate

Per mara's issue: tables should remember their filters (with a reset
button), the labels filter should be multi-select, and any filter should
support negation (search vs exclude).

All in the shared Table component (ui/table/Table.tsx), used by
AgentsPage/HivesPage/IssueReportPage:

- filters now persist via the same useLocalSetting hook IssueReportPage
  already used for its own state, keyed by a new required storageKey
  prop (required, not optional, so no caller can forget it and every
  table gets persistence for free)
- a small 'reset filters' button clears every column's filter at once,
  shown only when at least one is active
- new filterMode: "multiselect" (+ a filterValues extractor, alongside
  the existing single-value filterValue) renders a checkbox list and
  matches on any overlap - IssueReportPage's own bespoke label-checkbox
  sidebar is folded into this instead of staying a second, separate
  filter mechanism
- a negate toggle ('exclude') sits under every filter mode's control,
  applying uniformly to text/select/multiselect

Verified: tsc --noEmit and the esbuild bundle both clean.
This commit is contained in:
iris 2026-09-09 19:03:03 +02:00 • committed by mara
commit 915c6c6f92
7 changed files with 386 additions and 197 deletions

View file

@ -141,3 +141,60 @@
font: inherit;
font-size: 0.9em;
}
/* `filterMode: "multiselect"` — a scrollable checkbox list rather than
a `<select multiple>`, which needs a modifier key to pick more than
one option and reads as unfamiliar to most operators; a plain
checkbox list needs neither. `max-height` + scroll keeps a
long option set (many labels) from pushing the popover off-screen. */
.ui-table-filter-multiselect {
display: flex;
flex-direction: column;
gap: 0.3em;
max-height: 12em;
overflow-y: auto;
}
.ui-table-filter-checkbox {
display: flex;
align-items: center;
gap: 0.35em;
cursor: pointer;
font-size: 0.9em;
}
/* The negate toggle sits under every filter mode's own control (mara:
"you should be able to change if the filter is negated or not") —
a hairline rule separates it from the control above so "exclude"
doesn't read as one more option belonging to that control. */
.ui-table-filter-negate {
display: flex;
align-items: center;
gap: 0.35em;
margin-top: 0.5em;
padding-top: 0.5em;
border-top: 1px solid var(--border);
color: var(--muted);
font-size: 0.85em;
cursor: pointer;
}
/* One button clears every column's filter on this table at once (mara:
"have a small reset filters btn") — text-button styling matching
`.ui-table-sort-button`'s "no chrome, just a clickable label"
treatment, not a full `Badge`/bordered-button — this is a quiet
secondary action, not a primary one. */
.ui-table-reset-filters {
display: inline-block;
background: none;
border: none;
padding: 0;
margin: 0 0 0.5em;
font: inherit;
font-size: 0.85em;
color: var(--muted);
text-decoration: underline;
cursor: pointer;
}
.ui-table-reset-filters:hover {
color: var(--fg);
}