{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "filter-panel", "title": "Filter Panel", "description": "A faceted filter panel: groups of checkboxes with optional result counts, an active-filter count, and a clear-all control. Fully controlled.", "dependencies": [ "lucide-react" ], "files": [ { "path": "packages/core/src/components/lumo/filter-panel.tsx", "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface FilterOption {\n value: string\n label: string\n /** Optional result count shown next to the option. */\n count?: number\n}\n\nexport interface FilterGroup {\n id: string\n label: string\n options: FilterOption[]\n}\n\n/** Selected values keyed by group id. */\nexport type FilterValue = Record\n\nexport interface FilterPanelProps extends Omit, \"onChange\"> {\n groups: FilterGroup[]\n value: FilterValue\n onValueChange: (value: FilterValue) => void\n title?: string\n}\n\nfunction countActive(value: FilterValue): number {\n return Object.values(value).reduce((total, values) => total + values.length, 0)\n}\n\nconst FilterPanel = React.forwardRef(\n ({ className, groups, value, onValueChange, title = \"Filters\", ...props }, ref) => {\n const active = countActive(value)\n\n const toggle = (groupId: string, optionValue: string) => {\n const current = value[groupId] ?? []\n const next = current.includes(optionValue)\n ? current.filter((v) => v !== optionValue)\n : [...current, optionValue]\n onValueChange({ ...value, [groupId]: next })\n }\n\n return (\n
\n
\n

\n {title}\n {active > 0 && (\n \n {active}\n \n )}\n

\n {active > 0 && (\n onValueChange({})}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground\"\n >\n \n Clear all\n \n )}\n
\n\n {groups.map((group) => (\n
\n \n {group.label}\n \n {group.options.map((option) => {\n const checked = (value[group.id] ?? []).includes(option.value)\n return (\n \n toggle(group.id, option.value)}\n className=\"h-4 w-4 shrink-0 cursor-pointer rounded border-input accent-[hsl(var(--primary))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n />\n {option.label}\n {option.count !== undefined && (\n {option.count}\n )}\n \n )\n })}\n
\n ))}\n
\n )\n }\n)\nFilterPanel.displayName = \"FilterPanel\"\n\nexport { FilterPanel }\n", "type": "registry:ui", "target": "components/lumo/filter-panel.tsx" } ], "type": "registry:ui" }