FilterPopover
$ npx shadcn@latest add https://lacodda.github.io/dowel/r/filter-popover.jsonA heading carries a funnel, shown when the pointer is over the heading and staying on, filled, while that column is narrowing the list. Press it and a small panel opens for that column — a text box, a handful of checkboxes — with one way to clear it. The shell only: what goes in the panel is yours, since a stage is ticked and a title is typed and the popover has no opinion.
<TableHeader className="group"> <span className="flex items-center gap-1"> Owner <FilterPopover title={t('columns.owner')} label={t('table.filterBy', { column: t('columns.owner') })} active={chosen.length > 0} clearLabel={t('table.clear')} onClear={() => setChosen([])} className="opacity-0 group-hover:opacity-100 focus-visible:opacity-100 data-[active]:opacity-100 data-[popup-open]:opacity-100" > {owners.map((owner) => ( <Checkbox key={owner} checked={chosen.includes(owner)} onCheckedChange={…}> {owner} </Checkbox> ))} </FilterPopover> </span></TableHeader>The funnel says whether the column is narrowing, and that is all it says.
A column with a filter on it has to say so from the header, or a table
narrowed by a funnel opened last week looks like a table with fewer rows in
it. active fills the funnel and sets data-active on the trigger.
Whether it shows at rest is your rule. A header with five funnels always
showing is a header nobody can read, but hiding the active one would hide the
one thing that matters — so the classes above show it on hover, on focus,
while active, and while open, and data-active is there for that rule to key
off.
Clear is inside the panel. Unticking three boxes one by one is the failure this exists to prevent, and the panel is where a person is already looking. It is disabled while there is nothing to clear.
The panel reads in sentence case even under a header set in uppercase: the header is scanned, the panel is read.
Props
Read from the types in ui/filter-popover.tsx, the file a product copies - so the description of each prop is also what an editor shows over it there.
| Prop | Type | Default | |
|---|---|---|---|
titlerequired | string | The heading inside the panel: the column's name. | |
labelrequired | string | What the funnel says to assistive technology and on hover. | |
activerequired | boolean | Whether the column's filter holds anything; fills the funnel. | |
clearLabelrequired | string | The words of the button inside the panel that empties the filter. | |
onClearrequired | () => void | Called when that button is pressed; the button is disabled while active is false. | |
childrenrequired | ReactNode | The controls: a text box, checkboxes, whatever narrows this column. | |
className | string | On the trigger, for showing it on hover or always. | |
align | center | start | end | Which edge of the funnel the panel lines up with. Defaults to start. |
Accessibility
The examples on the stand, through axe 4.14.0 in a browser, in both themes, against WCAG 2.2 A and AA and axe's best practices: 18 rules applied and passed, and none failed. The accessibility report has every item.
History
Each release that changed ui/filter-popover.tsx - the file a product copies - newest first.