{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "filter-bar",
  "title": "Filter Bar",
  "description": "The nx-ui Filter Bar block.",
  "dependencies": [
    "@untitledui/icons@^0.0.22"
  ],
  "registryDependencies": [
    "@nx-ui/badge",
    "@nx-ui/button",
    "@nx-ui/checkbox",
    "@nx-ui/input-group",
    "@nx-ui/label",
    "@nx-ui/popover",
    "@nx-ui/tag",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/filter-bar.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { FilterLines, SearchLg } from \"@untitledui/icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n} from \"@/components/ui/input-group\"\nimport { Label } from \"@/components/ui/label\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport { Tag } from \"@/components/ui/tag\"\n\n/**\n * FilterBar — the UUI \"filter bar\" toolbar shown above a table or list.\n *\n * Composed from nx-ui primitives: a search input, a slot for inline filter\n * triggers (selects/dropdowns), a \"More filters\" popover of checkable options\n * with an active-count badge, and a row of removable active-filter chips with a\n * \"Clear all\" action. Fully controlled — filter state lives with the caller.\n */\n\nexport type FilterGroup = {\n  id: string\n  label: string\n  options: { value: string; label: string }[]\n}\n\nexport type ActiveFilter = { id: string; label: string; value: string }\n\nfunction FilterBar({\n  search,\n  onSearchChange,\n  searchPlaceholder = \"Search\",\n  children,\n  moreFilters,\n  selectedFilters,\n  onToggleFilter,\n  activeFilters,\n  onClearFilter,\n  onClearAll,\n  className,\n  ...props\n}: Omit<React.ComponentProps<\"div\">, \"onChange\"> & {\n  /** Controlled search text. */\n  search?: string\n  onSearchChange?: (value: string) => void\n  searchPlaceholder?: string\n  /** Inline filter triggers (Selects / DropdownMenus) rendered before \"More filters\". */\n  children?: React.ReactNode\n  /** Extra filter groups shown inside the \"More filters\" popover. */\n  moreFilters?: FilterGroup[]\n  /** Set of selected option keys, formatted `${groupId}:${value}`. */\n  selectedFilters?: Set<string>\n  onToggleFilter?: (groupId: string, value: string, next: boolean) => void\n  /** Active filter chips to display beneath the toolbar. */\n  activeFilters?: ActiveFilter[]\n  onClearFilter?: (filter: ActiveFilter) => void\n  onClearAll?: () => void\n}) {\n  const activeMoreCount = React.useMemo(() => {\n    if (!moreFilters || !selectedFilters) return 0\n    const ids = new Set(moreFilters.map((g) => g.id))\n    let count = 0\n    for (const key of selectedFilters) {\n      if (ids.has(key.split(\":\")[0])) count += 1\n    }\n    return count\n  }, [moreFilters, selectedFilters])\n\n  return (\n    <div\n      data-slot=\"filter-bar\"\n      className={cn(\"flex flex-col gap-3\", className)}\n      {...props}\n    >\n      <div className=\"flex flex-wrap items-center gap-3\">\n        {onSearchChange || search !== undefined ? (\n          <InputGroup className=\"h-9 w-full max-w-xs\">\n            <InputGroupAddon>\n              <SearchLg />\n            </InputGroupAddon>\n            <InputGroupInput\n              value={search}\n              placeholder={searchPlaceholder}\n              onChange={(e) => onSearchChange?.(e.target.value)}\n              aria-label=\"Search\"\n            />\n          </InputGroup>\n        ) : null}\n\n        {children}\n\n        {moreFilters && moreFilters.length > 0 ? (\n          <Popover>\n            <PopoverTrigger\n              render={<Button variant=\"secondary\" size=\"sm\" />}\n              aria-label=\"More filters\"\n            >\n              <FilterLines data-icon=\"inline-start\" />\n              More filters\n              {activeMoreCount > 0 ? (\n                <Badge variant=\"brand\" className=\"ml-0.5\">\n                  {activeMoreCount}\n                </Badge>\n              ) : null}\n            </PopoverTrigger>\n            <PopoverContent align=\"end\" className=\"w-64 gap-4\">\n              {moreFilters.map((group) => (\n                <div key={group.id} className=\"flex flex-col gap-2\">\n                  <p className=\"text-xs font-semibold text-text-tertiary\">\n                    {group.label}\n                  </p>\n                  <div className=\"flex flex-col gap-2\">\n                    {group.options.map((option) => {\n                      const key = `${group.id}:${option.value}`\n                      const checked = selectedFilters?.has(key) ?? false\n                      const id = `${group.id}-${option.value}`\n                      return (\n                        <div key={key} className=\"flex items-center gap-2\">\n                          <Checkbox\n                            id={id}\n                            checked={checked}\n                            onCheckedChange={(next) =>\n                              onToggleFilter?.(\n                                group.id,\n                                option.value,\n                                Boolean(next)\n                              )\n                            }\n                          />\n                          <Label htmlFor={id} className=\"font-medium\">\n                            {option.label}\n                          </Label>\n                        </div>\n                      )\n                    })}\n                  </div>\n                </div>\n              ))}\n            </PopoverContent>\n          </Popover>\n        ) : null}\n      </div>\n\n      {activeFilters && activeFilters.length > 0 ? (\n        <div className=\"flex flex-wrap items-center gap-2\">\n          {activeFilters.map((filter) => (\n            <Tag\n              key={`${filter.id}:${filter.value}`}\n              size=\"sm\"\n              onRemove={\n                onClearFilter ? () => onClearFilter(filter) : undefined\n              }\n            >\n              {filter.label}\n            </Tag>\n          ))}\n          {onClearAll ? (\n            <Button\n              variant=\"link\"\n              size=\"sm\"\n              onClick={onClearAll}\n              className=\"h-auto px-1 py-0\"\n            >\n              Clear all\n            </Button>\n          ) : null}\n        </div>\n      ) : null}\n    </div>\n  )\n}\n\nexport { FilterBar }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}