FilterDropdown

ui filteroverlay

Einheitlicher Filter-Trigger (Label + optionaler „· N"-Zähler + Chevron), der ein DropdownMenu-Popover öffnet. Markiert sich aktiv, sobald etwas gewählt ist. Inhalt bestimmt Single- vs. Multi-Select.

Barrierefreiheit

  • Trigger mit aria-haspopup="menu" und aria-expanded; aktiver Zustand via data-active.

Beispiele

Sortierung
<FilterDropdown ariaLabel="Sortierung" label="Titel (A–Z)" active>
  <DropdownMenuItem onSelect={() => sort('az')}>Titel (A–Z)</DropdownMenuItem>
  <DropdownMenuItem onSelect={() => sort('za')}>Titel (Z–A)</DropdownMenuItem>
</FilterDropdown>

Genutzte Tokens · 11

--accent--bg--border--control-height--ease-standard--fg--field-radius--focus-ring--motion-fast--muted--space-2

Quelldateien

components/ui/FilterDropdown.tsx tsx
import { type ReactNode } from 'react';
import { DropdownMenu } from './DropdownMenu';
import { ChevronDownIcon } from './Icon';
import styles from './FilterDropdown.module.css';

interface FilterDropdownProps {
  ariaLabel: string;
  /** Triggertext, z. B. „Buch" oder die aktive Sortierung „Titel (A–Z)". */
  label: string;
  /** Anzahl gewählter Optionen — als Suffix „ · N" am Trigger; 0 = kein Suffix. */
  count?: number;
  /** Trigger optisch als aktiv markieren (z. B. Sortierung ≠ Default). */
  active?: boolean;
  align?: 'left' | 'right';
  /** Inhalt des Popovers (Checkbox-Liste oder DropdownMenuItems). */
  children: ReactNode;
}

/** Einheitlicher Dropdown-Trigger (Label + optionaler Anzahl-Suffix + Chevron),
 *  der ein Popover öffnet. Kapselt das `DropdownMenu`-Verhalten (Klick-außerhalb,
 *  Escape) für die Filterleiste. Single- wie Multi-Select bestimmt der Inhalt. */
export function FilterDropdown({
  ariaLabel,
  label,
  count = 0,
  active = false,
  align = 'left',
  children,
}: FilterDropdownProps) {
  return (
    <DropdownMenu
      ariaLabel={ariaLabel}
      align={align}
      trigger={({ open, toggle }) => (
        <button
          type="button"
          className={styles.trigger}
          aria-haspopup="menu"
          aria-expanded={open}
          onClick={toggle}
          data-active={active || count > 0 || undefined}
        >
          <span className={styles.label}>
            {label}
            {count > 0 ? ` · ${count}` : ''}
          </span>
          <ChevronDownIcon size={14} />
        </button>
      )}
    >
      {children}
    </DropdownMenu>
  );
}
components/ui/FilterDropdown.module.css css
.trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: var(--control-height);
  padding: 0 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  border-radius: var(--field-radius);
  color: var(--muted);
  cursor: pointer;
  box-sizing: border-box;
  width: 100%;
  transition: background-color var(--motion-fast) var(--ease-standard),
    color var(--motion-fast) var(--ease-standard),
    border-color var(--motion-fast) var(--ease-standard);
}

.trigger:hover {
  color: var(--fg);
}

.trigger:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.trigger[data-active] {
  color: var(--fg);
  border-color: var(--accent);
}

.label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: left;
}

.trigger svg {
  flex-shrink: 0;
}

Holen

# MCP (Claude Code / Cursor)
get_component({ name: "filter-dropdown" })

# Registry direkt
https://www.ekklesi.tools/design/registry/components/filter-dropdown.json