{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table",
  "title": "Data Table",
  "description": "Semantic TanStack table rendering, sortable headers and pagination; the application owns data and state.",
  "dependencies": [
    "@tanstack/react-table@^8.21.3"
  ],
  "registryDependencies": [
    "@neumorphism-ui/utils",
    "@neumorphism-ui/button",
    "@neumorphism-ui/select",
    "@neumorphism-ui/table"
  ],
  "files": [
    {
      "path": "src/components/ui/data-table.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { flexRender, type Column, type Table as TableInstance } from \"@tanstack/react-table\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { Select, SelectItem } from \"@/components/ui/select\";\nimport { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from \"@/components/ui/table\";\n\ntype DataTableProps<TData> = { table: TableInstance<TData>; caption: string; emptyMessage?: string; children?: React.ReactNode; className?: string };\n\n// The application owns TanStack state and data access; this component owns rendering.\nfunction DataTable<TData>({ table, caption, emptyMessage = \"No results.\", children, className }: DataTableProps<TData>) {\n  \"use no memo\"; // The caller-owned TanStack table is a stable mutable handle.\n  return <section data-slot=\"data-table\" className={cn(\"grid min-w-0 w-full gap-4\", className)}>\n    {children}\n    <Table containerProps={{ role: \"region\", \"aria-label\": caption, tabIndex: 0, className: \"outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\" }}>\n      <TableCaption>{caption}</TableCaption>\n      <TableHeader>{table.getHeaderGroups().map(group => <TableRow key={group.id}>{group.headers.map(header => {\n        const sorted = header.column.getIsSorted();\n        return <TableHead key={header.id} colSpan={header.colSpan} scope={header.colSpan > 1 ? \"colgroup\" : \"col\"} aria-sort={header.column.getCanSort() ? sorted === \"asc\" ? \"ascending\" : sorted === \"desc\" ? \"descending\" : \"none\" : undefined}>{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}</TableHead>;\n      })}</TableRow>)}</TableHeader>\n      <TableBody>{table.getRowModel().rows.length ? table.getRowModel().rows.map(row => <TableRow key={row.id} data-state={row.getIsSelected() ? \"selected\" : undefined}>{row.getVisibleCells().map(cell => <TableCell key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCell>)}</TableRow>) : <TableRow><TableCell colSpan={Math.max(1, table.getVisibleLeafColumns().length)} className=\"h-32 text-center\"><span role=\"status\">{emptyMessage}</span></TableCell></TableRow>}</TableBody>\n    </Table>\n  </section>;\n}\n\nfunction DataTableColumnHeader<TData, TValue>({ column, title }: { column: Column<TData, TValue>; title: string }) {\n  \"use no memo\";\n  if (!column.getCanSort()) return <span>{title}</span>;\n  return <Button variant=\"ghost\" size=\"sm\" className=\"-ms-3 gap-2 text-inherit\" onClick={column.getToggleSortingHandler()}>{title}<svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d={column.getIsSorted() === \"asc\" ? \"M12 19V5m-5 5 5-5 5 5\" : column.getIsSorted() === \"desc\" ? \"M12 5v14m-5-5 5 5 5-5\" : \"M8 19V5m-4 4 4-4 4 4M16 5v14m-4-4 4 4 4-4\"} /></svg></Button>;\n}\n\ntype PaginationLabels = { previous: string; next: string; rowsPerPage: string; page: (current: number, total: number) => string };\nconst defaultLabels: PaginationLabels = { previous: \"Previous\", next: \"Next\", rowsPerPage: \"Rows per page\", page: (current, total) => `Page ${current} of ${total}` };\n\nfunction DataTablePagination<TData>({ table, labels = defaultLabels, pageSizes = [5, 10, 20, 50] }: { table: TableInstance<TData>; labels?: PaginationLabels; pageSizes?: readonly number[] }) {\n  \"use no memo\";\n  const id = React.useId();\n  const count = table.getPageCount();\n  const size = table.getState().pagination.pageSize;\n  const sizes = Array.from(new Set([size, ...pageSizes])).sort((a, b) => a - b);\n  return <div data-slot=\"data-table-pagination\" className=\"flex flex-wrap items-center justify-between gap-3 text-sm\">\n    <div className=\"flex items-center gap-2\"><label htmlFor={id} className=\"whitespace-nowrap text-[var(--muted-foreground)]\">{labels.rowsPerPage}</label><div className=\"w-20 shrink-0\"><Select id={id} value={size} className=\"h-9\" onChange={event => table.setPageSize(Number(event.target.value))}>{sizes.map(value => <SelectItem key={value} value={value}>{value}</SelectItem>)}</Select></div></div>\n    <div className=\"flex flex-wrap items-center gap-3\"><span aria-live=\"polite\" className=\"tabular-nums text-[var(--muted-foreground)]\">{labels.page(count ? table.getState().pagination.pageIndex + 1 : 0, count)}</span><Button size=\"sm\" disabled={!table.getCanPreviousPage()} onClick={() => table.previousPage()}>{labels.previous}</Button><Button size=\"sm\" disabled={!table.getCanNextPage()} onClick={() => table.nextPage()}>{labels.next}</Button></div>\n  </div>;\n}\n\nexport { DataTable, DataTableColumnHeader, DataTablePagination };\nexport type { DataTableProps, PaginationLabels };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}