{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table",
  "type": "registry:component",
  "title": "Data Table",
  "description": "Enhanced data table with sorting, filtering, selection, and pagination controls.",
  "dependencies": [
    "@vllnt/ui@^0.2.1",
    "@tanstack/react-table"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/data-table/data-table.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  type ColumnDef,\n  type ColumnFiltersState,\n  flexRender,\n  getCoreRowModel,\n  getFilteredRowModel,\n  getPaginationRowModel,\n  getSortedRowModel,\n  type Row,\n  type RowData,\n  type RowSelectionState,\n  type SortingState,\n  useReactTable,\n} from \"@tanstack/react-table\";\nimport { ArrowDown, ArrowUp, ArrowUpDown } from \"lucide-react\";\n\nimport { cn } from \"@vllnt/ui\";\nimport { Button } from \"@vllnt/ui\";\nimport { Checkbox } from \"@vllnt/ui\";\nimport { Input } from \"@vllnt/ui\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@vllnt/ui\";\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@vllnt/ui\";\n\nexport type DataTableFilterOption = {\n  label: string;\n  value: string;\n};\n\nexport type DataTableFilter = {\n  columnId: string;\n  label: string;\n  options: DataTableFilterOption[];\n};\n\nexport type DataTableProps<TData extends RowData> =\n  React.HTMLAttributes<HTMLDivElement> & {\n    caption?: string;\n    columns: ColumnDef<TData>[];\n    data: TData[];\n    emptyMessage?: string;\n    enableFiltering?: boolean;\n    enablePagination?: boolean;\n    enableSelection?: boolean;\n    filterableColumns?: DataTableFilter[];\n    getRowId?: (\n      originalRow: TData,\n      index: number,\n      parent?: Row<TData>,\n    ) => string;\n    pageSize?: number;\n    searchPlaceholder?: string;\n  };\n\nfunction SortIcon({ direction }: { direction: \"asc\" | \"desc\" | false }) {\n  if (direction === \"asc\") {\n    return <ArrowUp className=\"size-4\" />;\n  }\n\n  if (direction === \"desc\") {\n    return <ArrowDown className=\"size-4\" />;\n  }\n\n  return <ArrowUpDown className=\"size-4\" />;\n}\n\nconst EMPTY_FILTERABLE_COLUMNS: DataTableFilter[] = [];\n\nfunction DataTableComponent<TData extends RowData>({\n  caption,\n  className,\n  columns,\n  data,\n  emptyMessage = \"No results found.\",\n  enableFiltering = true,\n  enablePagination = true,\n  enableSelection = false,\n  filterableColumns = EMPTY_FILTERABLE_COLUMNS,\n  getRowId,\n  pageSize = 10,\n  searchPlaceholder = \"Search rows...\",\n  ...props\n}: DataTableProps<TData>) {\n  const [sorting, setSorting] = React.useState<SortingState>([]);\n  const [globalFilter, setGlobalFilter] = React.useState(\"\");\n  const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(\n    [],\n  );\n  const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});\n\n  const selectionColumn = React.useMemo<ColumnDef<TData>>(\n    () => ({\n      cell: ({ row }) => (\n        <Checkbox\n          aria-label={`Select row ${row.index + 1}`}\n          checked={row.getIsSelected()}\n          onCheckedChange={(checked) => {\n            row.toggleSelected(Boolean(checked));\n          }}\n        />\n      ),\n      enableHiding: false,\n      enableSorting: false,\n      header: ({ table }) => (\n        <Checkbox\n          aria-label=\"Select all rows\"\n          checked={\n            table.getIsAllPageRowsSelected()\n              ? true\n              : table.getIsSomePageRowsSelected()\n                ? \"indeterminate\"\n                : false\n          }\n          onCheckedChange={(checked) => {\n            table.toggleAllPageRowsSelected(Boolean(checked));\n          }}\n        />\n      ),\n      id: \"select\",\n      size: 40,\n    }),\n    [],\n  );\n\n  const resolvedColumns = React.useMemo(\n    () => (enableSelection ? [selectionColumn, ...columns] : columns),\n    [columns, enableSelection, selectionColumn],\n  );\n\n  const table = useReactTable({\n    columns: resolvedColumns,\n    data,\n    enableRowSelection: enableSelection,\n    getCoreRowModel: getCoreRowModel(),\n    getFilteredRowModel: getFilteredRowModel(),\n    getPaginationRowModel: getPaginationRowModel(),\n    getRowId,\n    getSortedRowModel: getSortedRowModel(),\n    initialState: {\n      pagination: {\n        pageIndex: 0,\n        pageSize,\n      },\n    },\n    onColumnFiltersChange: setColumnFilters,\n    onGlobalFilterChange: setGlobalFilter,\n    onRowSelectionChange: setRowSelection,\n    onSortingChange: setSorting,\n    state: {\n      columnFilters,\n      globalFilter,\n      rowSelection,\n      sorting,\n    },\n  });\n\n  return (\n    <div className={cn(\"space-y-4\", className)} {...props}>\n      {enableFiltering ? (\n        <div className=\"flex flex-col gap-3 rounded-xl border bg-card p-4 sm:flex-row sm:items-center sm:justify-between\">\n          <Input\n            className=\"w-full sm:max-w-sm\"\n            onChange={(event) => {\n              setGlobalFilter(event.target.value);\n            }}\n            placeholder={searchPlaceholder}\n            value={globalFilter}\n          />\n          {filterableColumns.length > 0 ? (\n            <div className=\"flex flex-wrap gap-2\">\n              {filterableColumns.map((filter) => {\n                const column = table.getColumn(filter.columnId);\n                const value = column?.getFilterValue();\n                const selectValue =\n                  typeof value === \"string\" && value ? value : \"all\";\n\n                return column ? (\n                  <Select\n                    key={filter.columnId}\n                    onValueChange={(nextValue) => {\n                      column.setFilterValue(\n                        nextValue === \"all\" ? undefined : nextValue,\n                      );\n                    }}\n                    value={selectValue}\n                  >\n                    <SelectTrigger className=\"w-[180px]\">\n                      <SelectValue placeholder={filter.label} />\n                    </SelectTrigger>\n                    <SelectContent>\n                      <SelectItem value=\"all\">All {filter.label}</SelectItem>\n                      {filter.options.map((option) => (\n                        <SelectItem key={option.value} value={option.value}>\n                          {option.label}\n                        </SelectItem>\n                      ))}\n                    </SelectContent>\n                  </Select>\n                ) : null;\n              })}\n            </div>\n          ) : null}\n        </div>\n      ) : null}\n\n      <div className=\"rounded-xl border bg-card\">\n        <Table>\n          {caption ? <caption className=\"sr-only\">{caption}</caption> : null}\n          <TableHeader>\n            {table.getHeaderGroups().map((headerGroup) => (\n              <TableRow key={headerGroup.id}>\n                {headerGroup.headers.map((header) => (\n                  <TableHead key={header.id}>\n                    {header.isPlaceholder ? null : header.column.getCanSort() ? (\n                      <Button\n                        className=\"-ml-3 h-8 px-3 text-xs font-medium\"\n                        onClick={header.column.getToggleSortingHandler()}\n                        type=\"button\"\n                        variant=\"ghost\"\n                      >\n                        {flexRender(\n                          header.column.columnDef.header,\n                          header.getContext(),\n                        )}\n                        <SortIcon direction={header.column.getIsSorted()} />\n                      </Button>\n                    ) : (\n                      flexRender(\n                        header.column.columnDef.header,\n                        header.getContext(),\n                      )\n                    )}\n                  </TableHead>\n                ))}\n              </TableRow>\n            ))}\n          </TableHeader>\n          <TableBody>\n            {table.getRowModel().rows.length > 0 ? (\n              table.getRowModel().rows.map((row) => (\n                <TableRow\n                  data-state={row.getIsSelected() ? \"selected\" : undefined}\n                  key={row.id}\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                  className=\"h-24 text-center text-muted-foreground\"\n                  colSpan={resolvedColumns.length}\n                >\n                  {emptyMessage}\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n\n      <div className=\"flex flex-col gap-3 rounded-xl border bg-card p-4 text-sm text-muted-foreground sm:flex-row sm:items-center sm:justify-between\">\n        <div>\n          {enableSelection\n            ? `${table.getSelectedRowModel().rows.length} selected`\n            : `${table.getFilteredRowModel().rows.length} rows`}\n        </div>\n        {enablePagination ? (\n          <div className=\"flex items-center gap-2 self-end sm:self-auto\">\n            <Button\n              disabled={!table.getCanPreviousPage()}\n              onClick={() => {\n                table.previousPage();\n              }}\n              type=\"button\"\n              variant=\"outline\"\n            >\n              Previous\n            </Button>\n            <span className=\"min-w-24 text-center text-xs uppercase tracking-wide text-muted-foreground\">\n              Page {table.getState().pagination.pageIndex + 1} of{\" \"}\n              {table.getPageCount()}\n            </span>\n            <Button\n              disabled={!table.getCanNextPage()}\n              onClick={() => {\n                table.nextPage();\n              }}\n              type=\"button\"\n              variant=\"outline\"\n            >\n              Next\n            </Button>\n          </div>\n        ) : null}\n      </div>\n    </div>\n  );\n}\n\nexport { DataTableComponent as DataTable };\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
