{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "filter-table",
  "title": "Filter table",
  "description": "Controlled or uncontrolled status chips that filter a responsive semantic data table.",
  "registryDependencies": [
    "https://beadkit.dev/r/ai-core.json"
  ],
  "files": [
    {
      "path": "components/ai/FilterTable.tsx",
      "content": "\"use client\";\n\nimport { useMemo, useState, type ReactNode } from \"react\";\n\nexport interface FilterTableRow { id: string; cells: readonly ReactNode[]; status: string; }\nexport interface FilterTableFilter { key: string; label: ReactNode; }\nexport interface FilterTableProps { columns: readonly ReactNode[]; rows: readonly FilterTableRow[]; filters?: readonly FilterTableFilter[]; title?: ReactNode; value?: string; defaultValue?: string; onFilterChange?: (filter: string) => void; className?: string; }\n\nexport function FilterTable({ columns, rows, filters, title, value, defaultValue = \"all\", onFilterChange, className = \"\" }: FilterTableProps) {\n  const available = filters ?? [...new Set(rows.map((row) => row.status))].map((key) => ({ key, label: key }));\n  const [internalFilter, setInternalFilter] = useState(defaultValue);\n  const filter = value ?? internalFilter;\n  const setFilter = (next: string) => {\n    if (value === undefined) setInternalFilter(next);\n    onFilterChange?.(next);\n  };\n  const visible = useMemo(() => filter === \"all\" ? rows : rows.filter((row) => row.status === filter), [filter, rows]);\n  const chipClass = (selected: boolean) => `shrink-0 rounded-full px-3 py-1.5 text-xs font-medium transition-colors motion-reduce:transition-none ${selected ? \"bg-primary text-primary-foreground\" : \"text-muted-foreground hover:bg-muted\"}`;\n  return <div data-ai-table=\"filter\" className={`w-full max-w-2xl ${className}`}>{title ? <h3 className=\"mb-2 text-sm font-medium\">{title}</h3> : null}<div className=\"mb-1 flex gap-1 overflow-x-auto py-1\" role=\"group\" aria-label=\"Filter rows\"><button type=\"button\" aria-pressed={filter === \"all\"} onClick={() => setFilter(\"all\")} className={chipClass(filter === \"all\")}>All <span className={filter === \"all\" ? \"opacity-75\" : \"text-muted-foreground\"}>{rows.length}</span></button>{available.map((item) => <button key={item.key} type=\"button\" aria-pressed={filter === item.key} onClick={() => setFilter(item.key)} className={chipClass(filter === item.key)}>{item.label} <span className={filter === item.key ? \"opacity-75\" : \"\"}>{rows.filter((row) => row.status === item.key).length}</span></button>)}</div><div className=\"overflow-x-auto rounded-xl border bg-card shadow-sm\" tabIndex={0} aria-label=\"Scrollable filtered table\"><table className=\"w-full min-w-[32rem] border-collapse text-left text-sm\"><thead><tr className=\"border-b border-border\">{columns.map((column, index) => <th scope=\"col\" key={index} className=\"px-4 py-2.5 text-xs font-medium text-muted-foreground\">{column}</th>)}</tr></thead><tbody>{visible.map((row) => <tr key={row.id} className=\"border-b border-border last:border-0 transition-colors hover:bg-muted motion-reduce:transition-none\">{row.cells.map((cell, index) => <td key={index} className={`px-4 py-3 ${index === 0 ? \"font-medium\" : \"text-muted-foreground\"}`}>{cell}</td>)}</tr>)}</tbody></table>{visible.length === 0 ? <p className=\"p-6 text-center text-sm text-muted-foreground\" role=\"status\">No matching records</p> : null}</div></div>;\n}\nexport default FilterTable;\n",
      "type": "registry:component",
      "target": "@components/ai/FilterTable.tsx"
    }
  ],
  "type": "registry:component"
}
