{
  "name": "category-filter",
  "title": "CategoryFilter",
  "description": "Selectable category chips for filtering products.",
  "type": "component",
  "registryDependencies": [
    "cn"
  ],
  "files": [
    {
      "path": "category-filter.tsx",
      "content": "\"use client\";\n\nimport React, { useCallback } from \"react\";\nimport { Tabs } from \"@base-ui/react/tabs\";\nimport type { Category } from \"@cimplify/sdk\";\nimport { useCategories } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface CategoryFilterClassNames {\n  root?: string;\n  item?: string;\n  allButton?: string;\n  count?: string;\n}\n\nexport interface CategoryFilterProps {\n  /** Currently selected category ID. Null means \"all\". */\n  selectedId?: string | null;\n  /** Called when a category is selected. Null means \"all\". */\n  onSelect: (categoryId: string | null) => void;\n  /** Label for the \"all\" option. Default: \"All\". */\n  allLabel?: string;\n  /** Show product counts per category. Default: true. */\n  showCounts?: boolean;\n  className?: string;\n  classNames?: CategoryFilterClassNames;\n}\n\n/** Sentinel value representing the \"all\" tab (no category filter). */\nconst ALL_VALUE = \"__all__\";\n\n/**\n * CategoryFilter — horizontal or vertical list of category chips.\n *\n * Fetches categories via `useCategories` and renders selectable buttons.\n * The parent controls selection state via `selectedId` + `onSelect`.\n *\n * Built on Base UI Tabs for accessible keyboard navigation and ARIA roles.\n */\nexport function CategoryFilter({\n  selectedId = null,\n  onSelect,\n  allLabel = \"All\",\n  showCounts = true,\n  className,\n  classNames,\n}: CategoryFilterProps): React.ReactElement {\n  const { categories, isLoading } = useCategories();\n\n  const handleValueChange = useCallback(\n    (value: string | number | null) => {\n      onSelect(value === ALL_VALUE ? null : String(value));\n    },\n    [onSelect],\n  );\n\n  if (isLoading) {\n    return (\n      <div\n        data-cimplify-category-filter\n        aria-busy=\"true\"\n        className={cn(className, classNames?.root)}\n      />\n    );\n  }\n\n  return (\n    <Tabs.Root\n      value={selectedId ?? ALL_VALUE}\n      onValueChange={handleValueChange}\n    >\n      <Tabs.List\n        data-cimplify-category-filter\n        aria-label=\"Filter by category\"\n        className={cn(className, classNames?.root)}\n      >\n        <Tabs.Tab\n          value={ALL_VALUE}\n          data-cimplify-category-filter-item\n          data-selected={selectedId === null || undefined}\n          className={cn(classNames?.item, classNames?.allButton)}\n        >\n          {allLabel}\n        </Tabs.Tab>\n\n        {categories.map((category: Category) => (\n          <Tabs.Tab\n            key={category.id}\n            value={category.id}\n            data-cimplify-category-filter-item\n            data-selected={selectedId === category.id || undefined}\n            className={classNames?.item}\n          >\n            {category.name}\n            {showCounts && category.product_count != null && (\n              <span data-cimplify-category-count className={classNames?.count}>\n                {category.product_count}\n              </span>\n            )}\n          </Tabs.Tab>\n        ))}\n      </Tabs.List>\n    </Tabs.Root>\n  );\n}\n"
    }
  ]
}
