{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table",
  "title": "Data table",
  "description": "sortable columns + built-in empty state · `columns rows rowKey caption?`",
  "dependencies": [
    "cn@^0.4.0",
    "lucide-react@^1.52.0"
  ],
  "registryDependencies": [
    "@kit/empty-state",
    "table"
  ],
  "files": [
    {
      "path": "components/kit/data-table.tsx",
      "content": "'use client';\nimport { ArrowDownIcon, ArrowUpIcon, ChevronsUpDownIcon, InboxIcon } from 'lucide-react';\nimport { cn } from 'cn';\nimport * as React from 'react';\nimport { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';\nimport { EmptyState } from '@/components/kit/empty-state';\n\nexport type Column<Row> = {\n  key: string;\n  header: string;\n  /** How a cell renders; defaults to the row's value at `key`. */\n  cell?: (row: Row) => React.ReactNode;\n  /** Sort by this value; omit to make the column unsortable. */\n  sortValue?: (row: Row) => string | number;\n  align?: 'left' | 'right';\n};\n\ntype Props<Row> = {\n  columns: Column<Row>[];\n  rows: Row[];\n  /** Stable key per row. */\n  rowKey: (row: Row) => string;\n  /** Read out by screen readers; also shown under the table. */\n  caption?: string;\n  emptyTitle?: string;\n  className?: string;\n};\n\n/**\n * A table with sortable columns and a built-in empty state. For dashboards and settings; not a data grid\n * (no paging, editing or virtualisation in a prototype).\n */\nexport function DataTable<Row>({ columns, rows, rowKey, caption, emptyTitle = 'Nothing here yet', className }: Props<Row>) {\n  const [sort, setSort] = React.useState<{ key: string; dir: 'asc' | 'desc' } | null>(null);\n  const sorted = React.useMemo(() => {\n    if (!sort) return rows;\n    const get = columns.find((c) => c.key === sort.key)?.sortValue;\n    if (!get) return rows;\n    const sign = sort.dir === 'asc' ? 1 : -1;\n    return [...rows].sort((a, b) => {\n      const [x, y] = [get(a), get(b)];\n      return (x < y ? -1 : x > y ? 1 : 0) * sign;\n    });\n  }, [rows, columns, sort]);\n\n  if (!rows.length) return <EmptyState icon={InboxIcon} title={emptyTitle} variant=\"compact\" />;\n\n  return (\n    <div className={cn('border-border overflow-hidden rounded-xl border', className)}>\n      <Table>\n        {caption ? <TableCaption className=\"mb-3\">{caption}</TableCaption> : null}\n        <TableHeader>\n          <TableRow>\n            {columns.map((c) => {\n              const active = sort?.key === c.key;\n              const Icon = !active ? ChevronsUpDownIcon : sort.dir === 'asc' ? ArrowUpIcon : ArrowDownIcon;\n              return (\n                <TableHead\n                  key={c.key}\n                  className={cn(c.align === 'right' && 'text-right')}\n                  aria-sort={active ? (sort.dir === 'asc' ? 'ascending' : 'descending') : undefined}>\n                  {c.sortValue ? (\n                    <button\n                      type=\"button\"\n                      onClick={() => setSort((s) => (s?.key === c.key && s.dir === 'asc' ? { key: c.key, dir: 'desc' } : { key: c.key, dir: 'asc' }))}\n                      className={cn('hover:text-foreground inline-flex items-center gap-1', c.align === 'right' && 'flex-row-reverse')}>\n                      {c.header}\n                      <Icon className=\"size-3.5\" aria-hidden />\n                    </button>\n                  ) : (\n                    c.header\n                  )}\n                </TableHead>\n              );\n            })}\n          </TableRow>\n        </TableHeader>\n        <TableBody>\n          {sorted.map((row) => (\n            <TableRow key={rowKey(row)}>\n              {columns.map((c) => (\n                <TableCell key={c.key} className={cn(c.align === 'right' && 'text-right tabular-nums')}>\n                  {c.cell ? c.cell(row) : String((row as Record<string, unknown>)[c.key] ?? '')}\n                </TableCell>\n              ))}\n            </TableRow>\n          ))}\n        </TableBody>\n      </Table>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/kit/data-table.tsx"
    }
  ],
  "meta": {
    "exports": [
      "DataTable"
    ],
    "api": "<DataTable columns={[{key,header,cell?,sortValue?,align?}]} rows rowKey caption? />"
  },
  "docs": "Needs the kit theme (`@kit/theme`) and a next-themes ThemeProvider (attribute=\"class\") at the root.",
  "categories": [
    "data"
  ],
  "type": "registry:component"
}