{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table",
  "title": "Data Table",
  "description": "Sortable, filterable, paginated client-side table with a composable filter toolbar.",
  "dependencies": [
    "@tanstack/react-table"
  ],
  "registryDependencies": [
    "table",
    "button",
    "select",
    "input"
  ],
  "files": [
    {
      "path": "src/components/basic/columns.tsx",
      "content": "'use client'\n\nimport type { ColumnDef } from '@tanstack/react-table'\n\n// This type is used to define the shape of our data.\n// You can use a Zod schema here if you want.\nexport type User = {\n  id: number\n  name: string\n  email: string\n  role: string\n  status: string\n}\n\nexport const columns: ColumnDef<User>[] = [\n  {\n    accessorKey: 'name',\n    header: 'Name',\n    enableSorting: false,\n  },\n  {\n    accessorKey: 'email',\n    header: 'Email',\n    sortingFn: 'alphanumeric',\n  },\n  {\n    accessorKey: 'role',\n    header: 'Role',\n    sortingFn: 'basic',\n    filterFn: 'equalsString',\n  },\n  {\n    accessorKey: 'status',\n    header: 'Status',\n    sortingFn: 'basic',\n    filterFn: 'equalsString',\n  },\n]\n",
      "type": "registry:component",
      "target": "@components/tables/data-table/columns.tsx"
    },
    {
      "path": "src/components/basic/data-table.tsx",
      "content": "'use client'\n\nimport {\n  flexRender,\n  getCoreRowModel,\n  getFilteredRowModel,\n  getPaginationRowModel,\n  getSortedRowModel,\n  useReactTable,\n} from '@tanstack/react-table'\n\nimport type {\n  ColumnDef,\n  ColumnFiltersState,\n  SortingState,\n  Table as ReactTable,\n} from '@tanstack/react-table'\n\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from '../ui/table'\nimport { useState } from 'react'\nimport { Button } from '../ui/button'\nimport {\n  ArrowDown,\n  ArrowUp,\n  ArrowUpDown,\n  ChevronLeft,\n  ChevronRight,\n  ChevronsLeft,\n  ChevronsRight,\n  X,\n} from 'lucide-react'\nimport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '../ui/select'\nimport { Input } from '../ui/input'\n\ninterface DataTableProps<TData, TValue> {\n  columns: ColumnDef<TData, TValue>[]\n  data: TData[]\n  filters?: (table: ReactTable<TData>) => React.ReactNode\n}\n\nconst PAGES_SIZE = [\n  {\n    label: '10',\n    value: '10',\n  },\n  {\n    label: '20',\n    value: '20',\n  },\n  {\n    label: '30',\n    value: '30',\n  },\n  {\n    label: '40',\n    value: '40',\n  },\n  {\n    label: '50',\n    value: '50',\n  },\n]\n\nexport function DataTable<TData, TValue>({\n  columns,\n  data,\n  filters,\n}: DataTableProps<TData, TValue>) {\n  const [pagination, setPagination] = useState({\n    pageIndex: 0,\n    pageSize: 10,\n  })\n\n  const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])\n  const [globalFilter, setGlobalFilter] = useState<string>('')\n  const [sorting, setSorting] = useState<SortingState>([])\n\n  const table = useReactTable({\n    data,\n    columns,\n    getCoreRowModel: getCoreRowModel(),\n    getFilteredRowModel: getFilteredRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    getPaginationRowModel: getPaginationRowModel(),\n    onPaginationChange: setPagination,\n    state: {\n      pagination,\n      sorting,\n      globalFilter,\n      columnFilters,\n    },\n    onSortingChange: setSorting,\n    onColumnFiltersChange: setColumnFilters,\n    onGlobalFilterChange: setGlobalFilter,\n  })\n\n  return (\n    <div>\n      <div className=\"flex mb-3 flex-row justify-between items-center\">\n        <Input\n          type=\"text\"\n          placeholder={'Search...'}\n          onChange={(e) => table.setGlobalFilter(e.target.value)}\n          className=\"filter-input  w-64\"\n        />\n        <div className=\"flex flex-row items-center gap-4\">\n          {table.getState().columnFilters.length > 0 ? (\n            <Button\n              variant=\"link\"\n              className=\"underline\"\n              onClick={() => {\n                table.resetColumnFilters()\n              }}\n            >\n              Clear Filters <X className=\"h-3 w-3\" />\n            </Button>\n          ) : null}\n          {filters?.(table)}\n        </div>\n      </div>\n\n      <div className=\"overflow-hidden rounded-md border\">\n        <Table>\n          <TableHeader>\n            {table.getHeaderGroups().map((headerGroup) => (\n              <TableRow key={headerGroup.id}>\n                {headerGroup.headers.map((header) => {\n                  const canSort = header.column.getCanSort()\n                  const sortHandler = header.column.getToggleSortingHandler()\n                  return (\n                    <TableHead\n                      key={header.id}\n                      onClick={sortHandler}\n                      onKeyDown={(e) => {\n                        if (canSort && (e.key === 'Enter' || e.key === ' ')) {\n                          e.preventDefault()\n                          sortHandler?.(e)\n                        }\n                      }}\n                      tabIndex={canSort ? 0 : undefined}\n                      role={canSort ? 'button' : undefined}\n                      className={\n                        canSort ? 'cursor-pointer select-none' : undefined\n                      }\n                    >\n                      <div className=\"flex flex-row items-center gap-2\">\n                        {header.isPlaceholder\n                          ? null\n                          : flexRender(\n                              header.column.columnDef.header,\n                              header.getContext(),\n                            )}\n                        {canSort ? (\n                          <span>\n                            {header.column.getIsSorted() === 'asc' ? (\n                              <ArrowUp className=\"h-3 w-3\" />\n                            ) : header.column.getIsSorted() === 'desc' ? (\n                              <ArrowDown className=\"h-3 w-3\" />\n                            ) : (\n                              <ArrowUpDown className=\"h-3 w-3 opacity-50\" />\n                            )}\n                          </span>\n                        ) : null}\n                      </div>\n                    </TableHead>\n                  )\n                })}\n              </TableRow>\n            ))}\n          </TableHeader>\n          <TableBody>\n            {table.getRowModel().rows.length ? (\n              table.getRowModel().rows.map((row) => (\n                <TableRow\n                  key={row.id}\n                  data-state={row.getIsSelected() && 'selected'}\n                >\n                  {row.getVisibleCells().map((cell) => (\n                    <TableCell key={cell.id}>\n                      {flexRender(\n                        cell.column.columnDef.cell,\n                        cell.getContext(),\n                      )}\n                    </TableCell>\n                  ))}\n                </TableRow>\n              ))\n            ) : (\n              <TableRow>\n                <TableCell\n                  colSpan={columns.length}\n                  className=\"h-24 text-center\"\n                >\n                  No results.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n      <div className=\"flex gap-1 justify-end  mt-5\">\n        <Button\n          onClick={() => table.firstPage()}\n          disabled={!table.getCanPreviousPage()}\n          variant={'outline'}\n        >\n          <ChevronsLeft className=\"h-3 w-3\" />\n        </Button>\n        <Button\n          onClick={() => table.previousPage()}\n          disabled={!table.getCanPreviousPage()}\n          variant={'outline'}\n        >\n          <ChevronLeft className=\"h-3 w-3\" />\n        </Button>\n        <Button\n          onClick={() => table.nextPage()}\n          disabled={!table.getCanNextPage()}\n          variant={'outline'}\n        >\n          <ChevronRight className=\"h-3 w-3\" />\n        </Button>\n        <Button\n          onClick={() => table.lastPage()}\n          disabled={!table.getCanNextPage()}\n          variant={'outline'}\n        >\n          <ChevronsRight className=\"h-3  w-3\" />\n        </Button>\n        <Select\n          value={pagination.pageSize.toString()}\n          onValueChange={(val) => table.setPageSize(Number(val))}\n        >\n          <SelectTrigger className=\"h-1/3\">\n            <SelectValue placeholder={PAGES_SIZE[0].label} />\n          </SelectTrigger>\n          <SelectContent>\n            <SelectGroup>\n              {PAGES_SIZE.map((item) => (\n                <SelectItem key={item.value} value={item.value}>\n                  {item.label}\n                </SelectItem>\n              ))}\n            </SelectGroup>\n          </SelectContent>\n        </Select>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/tables/data-table/data-table.tsx"
    },
    {
      "path": "src/components/basic/index.tsx",
      "content": "import { columns } from './columns'\nimport { DataTable } from './data-table'\nimport { DataTableFilter } from '../DataTableFilter'\nimport type { User } from '#/components/basic/columns'\n\nconst roleOptions = [\n  { label: 'Admin', value: 'admin' },\n  { label: 'Viewer', value: 'viewer' },\n  { label: 'Editor', value: 'editor' },\n]\n\nconst statusOptions = [\n  { label: 'Active', value: 'active' },\n  { label: 'Inactive', value: 'inactive' },\n  { label: 'Pending', value: 'pending' },\n]\n\nconst data: User[] = [\n  {\n    id: 1,\n    name: 'Ava Thompson',\n    email: 'ava.t@example.com',\n    role: 'Admin',\n    status: 'Active',\n  },\n  {\n    id: 2,\n    name: 'Liam Chen',\n    email: 'liam.chen@example.com',\n    role: 'Editor',\n    status: 'Active',\n  },\n  {\n    id: 3,\n    name: 'Sofia Patel',\n    email: 'sofia.p@example.com',\n    role: 'Viewer',\n    status: 'Inactive',\n  },\n  {\n    id: 4,\n    name: 'Noah Garcia',\n    email: 'noah.g@example.com',\n    role: 'Editor',\n    status: 'Active',\n  },\n  {\n    id: 5,\n    name: 'Mia Johnson',\n    email: 'mia.j@example.com',\n    role: 'Admin',\n    status: 'Pending',\n  },\n  {\n    id: 6,\n    name: 'Ethan Kim',\n    email: 'ethan.kim@example.com',\n    role: 'Viewer',\n    status: 'Active',\n  },\n  {\n    id: 7,\n    name: 'Isabella Rossi',\n    email: 'isabella.r@example.com',\n    role: 'Editor',\n    status: 'Active',\n  },\n  {\n    id: 8,\n    name: 'Lucas Martin',\n    email: 'lucas.m@example.com',\n    role: 'Admin',\n    status: 'Inactive',\n  },\n  {\n    id: 9,\n    name: 'Amelia Novak',\n    email: 'amelia.n@example.com',\n    role: 'Viewer',\n    status: 'Active',\n  },\n  {\n    id: 10,\n    name: 'Mason Lee',\n    email: 'mason.lee@example.com',\n    role: 'Editor',\n    status: 'Pending',\n  },\n  {\n    id: 11,\n    name: 'Charlotte Diaz',\n    email: 'charlotte.d@example.com',\n    role: 'Admin',\n    status: 'Active',\n  },\n  {\n    id: 12,\n    name: 'James Wilson',\n    email: 'james.w@example.com',\n    role: 'Viewer',\n    status: 'Active',\n  },\n  {\n    id: 13,\n    name: 'Harper Nguyen',\n    email: 'harper.n@example.com',\n    role: 'Editor',\n    status: 'Inactive',\n  },\n  {\n    id: 14,\n    name: 'Benjamin Cruz',\n    email: 'ben.cruz@example.com',\n    role: 'Admin',\n    status: 'Active',\n  },\n  {\n    id: 15,\n    name: 'Ella Fischer',\n    email: 'ella.f@example.com',\n    role: 'Viewer',\n    status: 'Pending',\n  },\n  {\n    id: 16,\n    name: 'Alexander Reed',\n    email: 'alex.reed@example.com',\n    role: 'Editor',\n    status: 'Active',\n  },\n  {\n    id: 17,\n    name: 'Grace Coleman',\n    email: 'grace.c@example.com',\n    role: 'Admin',\n    status: 'Active',\n  },\n  {\n    id: 18,\n    name: 'Daniel Torres',\n    email: 'daniel.t@example.com',\n    role: 'Viewer',\n    status: 'Inactive',\n  },\n  {\n    id: 19,\n    name: 'Chloe Bennett',\n    email: 'chloe.b@example.com',\n    role: 'Editor',\n    status: 'Active',\n  },\n  {\n    id: 20,\n    name: 'Henry Brooks',\n    email: 'henry.b@example.com',\n    role: 'Admin',\n    status: 'Pending',\n  },\n  {\n    id: 21,\n    name: 'Zoe Sanders',\n    email: 'zoe.s@example.com',\n    role: 'Viewer',\n    status: 'Active',\n  },\n  {\n    id: 22,\n    name: 'Sebastian Ortiz',\n    email: 'sebastian.o@example.com',\n    role: 'Editor',\n    status: 'Active',\n  },\n  {\n    id: 23,\n    name: 'Layla Morgan',\n    email: 'layla.m@example.com',\n    role: 'Admin',\n    status: 'Inactive',\n  },\n  {\n    id: 24,\n    name: 'Jack Foster',\n    email: 'jack.f@example.com',\n    role: 'Viewer',\n    status: 'Active',\n  },\n  {\n    id: 25,\n    name: 'Victoria Hayes',\n    email: 'victoria.h@example.com',\n    role: 'Editor',\n    status: 'Pending',\n  },\n]\n\nexport function BasicTableUsage() {\n  return (\n    <DataTable\n      columns={columns}\n      data={data}\n      filters={(table) => (\n        <>\n          <DataTableFilter\n            column={table.getColumn('status')}\n            options={statusOptions}\n            title=\"Select status\"\n          />\n          <DataTableFilter\n            column={table.getColumn('role')}\n            options={roleOptions}\n            title=\"Select role\"\n          />\n        </>\n      )}\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/tables/data-table/index.tsx"
    },
    {
      "path": "src/components/DataTableFilter.tsx",
      "content": "import type { Column } from '@tanstack/react-table'\nimport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from './ui/select'\n\ntype Props<TData> = {\n  column?: Column<TData, unknown>\n  title: string\n  options: { value: string; label: string }[]\n}\n\nexport function DataTableFilter<TData>({\n  column,\n  title,\n  options,\n}: Props<TData>) {\n  const filterValue = column?.getFilterValue() as string | undefined\n\n  return (\n    <Select\n      onValueChange={(val) => {\n        column?.setFilterValue(val)\n      }}\n      value={filterValue}\n    >\n      <SelectTrigger className=\"w-45\">\n        <SelectValue placeholder={title} />\n      </SelectTrigger>\n      <SelectContent>\n        <SelectGroup>\n          {options.map((option) => (\n            <SelectItem key={option.value} value={option.value}>\n              {option.label}\n            </SelectItem>\n          ))}\n        </SelectGroup>\n      </SelectContent>\n    </Select>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/tables/data-table/data-table-filter.tsx"
    }
  ],
  "type": "registry:block"
}