Filter Dropdown
Interactive dropdown filter (Lit web component) with a searchable panel, full keyboard navigation, and single or multi select. The trigger fills with the primary color when active and shows an inline clear control.
Preview
Click the trigger to open the panel, type to filter, and use Arrow/Enter/Escape to navigate. Outside-click and Escape close it. Options are set as a property in a small init script.
<esa-filter-dropdown name="status" label="Status"></esa-filter-dropdown>
<script>
import '@esa/ecology/esa-filter-dropdown';
customElements.whenDefined('esa-filter-dropdown').then(() => {
document.querySelector('esa-filter-dropdown').options = [
{ label: 'Open', value: 'open' },
{ label: 'In review', value: 'review' },
{ label: 'Closed', value: 'closed' },
];
});
</script> Select modes
Single select closes on choice; multiple keeps the panel open, shows checkmarks, and renders "first +N" in the trigger.
<esa-filter-dropdown name="status" label="Status"></esa-filter-dropdown>
<esa-filter-dropdown name="dept" label="Department" multiple></esa-filter-dropdown> Sizes
<esa-filter-dropdown label="XS" size="xs"></esa-filter-dropdown>
<esa-filter-dropdown label="Small" size="sm"></esa-filter-dropdown>
<esa-filter-dropdown label="Medium" size="md"></esa-filter-dropdown>
<esa-filter-dropdown label="Large" size="lg"></esa-filter-dropdown> States
Disabled options are dimmed, skipped by keyboard navigation, and not selectable.
<esa-filter-dropdown name="priority" label="Priority"></esa-filter-dropdown>
<!-- options include { label: 'High', value: 'high', disabled: true } --> API
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | '' | Filter key carried in emitted events. |
label | string | '' | Trigger label; selection appends "label: value". |
options | FilterOption[] | [] | Selectable options (set as a property, not an attribute). |
multiple | boolean | false | Allow multiple selections; trigger shows "first +N". |
placeholder | string | '' | Search-input placeholder (defaults to "Search…"). |
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Trigger height, padding, font size, and radius. |
Binding these events from Angular
Every one of these is a CustomEvent, so the payload is on
$event.detail — $event.target.value reads empty, with no
error. The names are lowercase and Angular passes them to
addEventListener verbatim, so camel-casing one means it never fires.
(esa-filter-change)="onEsaFilterChange($event.detail)"(selection-change)="onSelectionChange($event.detail)"
Events
| Prop | Type | Default | Description |
|---|---|---|---|
selection-change | { value: string | string[] } | — | Fires on every selection change. value is a string in single mode, an array in multiple mode. |
esa-filter-change | { name, filters: ActiveFilter[] } | — | Bubbling/composed. Carries the filter name plus the full active-filter list for a parent container to coordinate state. |
Accessibility
- Trigger is a native
<button>witharia-haspopup="listbox"andaria-expandedreflecting open state. - Panel is a
role="listbox"; each option isrole="option"witharia-selectedandaria-disabled. - Keyboard: ArrowUp/Down highlight (skipping disabled), Enter selects, Escape closes. Opening focuses the search input.
- Visible focus ring on the trigger via
--focus-ring-*tokens.