{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table",
  "title": "Data Table",
  "description": "A data-table component.",
  "dependencies": [
    "@tanstack/react-table",
    "@hugeicons/react",
    "@hugeicons/core-free-icons"
  ],
  "registryDependencies": [
    "@cubby-ui/checkbox",
    "@cubby-ui/toolbar",
    "@cubby-ui/table",
    "@cubby-ui/dropdown-menu",
    "@cubby-ui/button",
    "@cubby-ui/input-group",
    "@cubby-ui/elevated"
  ],
  "files": [
    {
      "path": "registry/default/data-table/data-table.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n  useReactTable,\n  getCoreRowModel,\n  getSortedRowModel,\n  getFilteredRowModel,\n  getPaginationRowModel,\n  flexRender,\n  type ColumnDef,\n  type SortingState,\n  type RowSelectionState,\n  type ColumnFiltersState,\n  type PaginationState,\n  type VisibilityState,\n  type OnChangeFn,\n  type Row,\n  type Column,\n} from \"@tanstack/react-table\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport {\n  ArrowUpDownIcon,\n  SortByDown01Icon,\n  SortByUp01Icon,\n} from \"@hugeicons/core-free-icons\";\n\nimport { cn } from \"@/lib/utils\";\nimport { elevatedSurface } from \"@/registry/default/lib/elevated\";\nimport { Checkbox } from \"@/registry/default/checkbox/checkbox\";\nimport {\n  DataTableContext,\n  useDataTable,\n} from \"@/registry/default/data-table/data-table-context\";\nimport {\n  Toolbar,\n  ToolbarSeparator,\n  type ToolbarProps,\n} from \"@/registry/default/toolbar/toolbar\";\nimport {\n  Table,\n  TableHeader,\n  TableBody,\n  TableFooter,\n  TableHead,\n  TableRow,\n  TableCell,\n  type TableProps,\n} from \"@/registry/default/table/table\";\nimport { DataTableSearch } from \"@/registry/default/data-table/data-table-search\";\nimport { DataTableColumnToggle } from \"@/registry/default/data-table/data-table-column-toggle\";\nimport { DataTablePagination } from \"@/registry/default/data-table/data-table-pagination\";\n\nexport interface DataTableProps<TData, TValue> {\n  columns: ColumnDef<TData, TValue>[];\n  data: TData[];\n  children: React.ReactNode;\n  className?: string;\n\n  enableSorting?: boolean;\n  enableRowSelection?: boolean | ((row: Row<TData>) => boolean);\n  enableMultiRowSelection?: boolean;\n  enableFiltering?: boolean;\n  enablePagination?: boolean;\n\n  showSelectionColumn?: boolean;\n\n  sorting?: SortingState;\n  onSortingChange?: OnChangeFn<SortingState>;\n  rowSelection?: RowSelectionState;\n  onRowSelectionChange?: OnChangeFn<RowSelectionState>;\n  columnFilters?: ColumnFiltersState;\n  onColumnFiltersChange?: OnChangeFn<ColumnFiltersState>;\n  pagination?: PaginationState;\n  onPaginationChange?: OnChangeFn<PaginationState>;\n  columnVisibility?: VisibilityState;\n  onColumnVisibilityChange?: OnChangeFn<VisibilityState>;\n  globalFilter?: string;\n  onGlobalFilterChange?: OnChangeFn<string>;\n\n  getRowId?: (originalRow: TData, index: number, parent?: Row<TData>) => string;\n}\n\nfunction DataTable<TData, TValue>({\n  columns: userColumns,\n  data,\n  children,\n  className,\n  enableSorting = false,\n  enableRowSelection = false,\n  enableMultiRowSelection = true,\n  enableFiltering = false,\n  enablePagination = false,\n  showSelectionColumn = true,\n  sorting: controlledSorting,\n  onSortingChange,\n  rowSelection: controlledRowSelection,\n  onRowSelectionChange,\n  columnFilters: controlledColumnFilters,\n  onColumnFiltersChange,\n  pagination: controlledPagination,\n  onPaginationChange,\n  columnVisibility: controlledColumnVisibility,\n  onColumnVisibilityChange,\n  globalFilter: controlledGlobalFilter,\n  onGlobalFilterChange,\n  getRowId,\n}: DataTableProps<TData, TValue>) {\n  const [internalSorting, setInternalSorting] = React.useState<SortingState>(\n    [],\n  );\n  const [internalRowSelection, setInternalRowSelection] =\n    React.useState<RowSelectionState>({});\n  const [internalColumnFilters, setInternalColumnFilters] =\n    React.useState<ColumnFiltersState>([]);\n  const [internalPagination, setInternalPagination] =\n    React.useState<PaginationState>({\n      pageIndex: 0,\n      pageSize: 10,\n    });\n  const [internalGlobalFilter, setInternalGlobalFilter] = React.useState(\"\");\n  const [internalColumnVisibility, setInternalColumnVisibility] =\n    React.useState<VisibilityState>({});\n\n  const sorting = controlledSorting ?? internalSorting;\n  const rowSelection = controlledRowSelection ?? internalRowSelection;\n  const columnFilters = controlledColumnFilters ?? internalColumnFilters;\n  const pagination = controlledPagination ?? internalPagination;\n  const globalFilter = controlledGlobalFilter ?? internalGlobalFilter;\n  const columnVisibility =\n    controlledColumnVisibility ?? internalColumnVisibility;\n\n  const columns = React.useMemo(() => {\n    if (!enableRowSelection || !showSelectionColumn) {\n      return userColumns;\n    }\n\n    const selectionColumn: ColumnDef<TData, unknown> = {\n      id: \"__select__\",\n      header: ({ table }) =>\n        enableMultiRowSelection ? (\n          <div className=\"flex items-center\">\n            <Checkbox\n              checked={table.getIsAllPageRowsSelected()}\n              indeterminate={\n                table.getIsSomePageRowsSelected() &&\n                !table.getIsAllPageRowsSelected()\n              }\n              onCheckedChange={(value) =>\n                table.toggleAllPageRowsSelected(!!value)\n              }\n              aria-label=\"Select all\"\n            />\n          </div>\n        ) : null,\n      cell: ({ row }) => (\n        <div className=\"flex items-center\">\n          <Checkbox\n            checked={row.getIsSelected()}\n            disabled={!row.getCanSelect()}\n            onCheckedChange={(value) => row.toggleSelected(!!value)}\n            aria-label=\"Select row\"\n          />\n        </div>\n      ),\n      enableSorting: false,\n      enableHiding: false,\n    };\n\n    return [selectionColumn, ...userColumns];\n  }, [\n    userColumns,\n    enableRowSelection,\n    enableMultiRowSelection,\n    showSelectionColumn,\n  ]);\n\n  const table = useReactTable({\n    data,\n    columns,\n    getRowId,\n    state: {\n      sorting,\n      rowSelection,\n      columnFilters,\n      pagination,\n      globalFilter,\n      columnVisibility,\n    },\n    onSortingChange: onSortingChange ?? setInternalSorting,\n    onRowSelectionChange: onRowSelectionChange ?? setInternalRowSelection,\n    onColumnFiltersChange: onColumnFiltersChange ?? setInternalColumnFilters,\n    onPaginationChange: onPaginationChange ?? setInternalPagination,\n    onGlobalFilterChange: onGlobalFilterChange ?? setInternalGlobalFilter,\n    onColumnVisibilityChange:\n      onColumnVisibilityChange ?? setInternalColumnVisibility,\n    getCoreRowModel: getCoreRowModel(),\n    ...(enableSorting && { getSortedRowModel: getSortedRowModel() }),\n    ...(enableFiltering && { getFilteredRowModel: getFilteredRowModel() }),\n    ...(enablePagination && { getPaginationRowModel: getPaginationRowModel() }),\n    enableRowSelection,\n    enableMultiRowSelection,\n    globalFilterFn: \"includesString\",\n  });\n\n  return (\n    <DataTableContext.Provider value={{ table }}>\n      <div\n        className={cn(\n          // `relative` required: elevatedSurface's rim ::after must position inside\n          // this container, not climb to the nearest positioned ancestor.\n          \"relative w-full rounded-2xl md:max-w-2xl\",\n          // pseudo-element rim keeps the border line visible above opaque sticky\n          // children (DataTableToolbar, table header) in dark mode.\n          elevatedSurface(3, 1),\n          \"bg-muted\",\n          className,\n        )}\n      >\n        {children}\n      </div>\n    </DataTableContext.Provider>\n  );\n}\n\n// A plain row above the table: its controls are fields and triggers, not\n// toolbar buttons, so it skips the shell's fixed height and highlight.\nfunction DataTableToolbar({\n  className,\n  variant = \"ghost\",\n  highlight = false,\n  ...props\n}: ToolbarProps) {\n  return (\n    <Toolbar\n      variant={variant}\n      highlight={highlight}\n      className={cn(\n        \"w-full items-center gap-1 rounded-b-none px-2 pt-2 pb-0 data-[orientation=horizontal]:h-auto\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport interface DataTableContentProps extends Omit<TableProps, \"children\"> {\n  children: React.ReactNode;\n}\n\nfunction DataTableContent({\n  bordered,\n  striped,\n  hoverable,\n  rowDividers = true,\n  className,\n  children,\n  ...tableProps\n}: DataTableContentProps) {\n  return (\n    <Table\n      bordered={bordered}\n      striped={striped}\n      hoverable={hoverable}\n      rowDividers={rowDividers}\n      className={cn(\n        // Strips Table's own elevation; outer DataTable container owns it.\n        // `after:hidden` removes the dark-mode rim ::after (would paint a\n        // square-cornered rim over the DataTable's rounded one).\n        \"rounded-none bg-transparent shadow-none ring-0 after:hidden\",\n        className,\n      )}\n      {...tableProps}\n    >\n      {children}\n    </Table>\n  );\n}\n\nfunction SortableHeader<TData>({\n  column,\n  children,\n  align,\n  isFirst,\n  isLast,\n}: {\n  column: Column<TData, unknown>;\n  children: React.ReactNode;\n  align?: \"left\" | \"center\" | \"right\";\n  isFirst?: boolean;\n  isLast?: boolean;\n}) {\n  return (\n    <button\n      type=\"button\"\n      className={cn(\n        \"group hover:text-foreground -mx-3 -my-2 flex w-[calc(100%+1.5rem)] cursor-pointer items-center gap-1.5 px-3 py-2 transition-colors\",\n        isFirst && \"rounded-l-lg\",\n        isLast && \"rounded-r-lg\",\n      )}\n      onClick={() => column.toggleSorting(column.getIsSorted() === \"asc\")}\n    >\n      <span\n        className={cn(\n          \"flex flex-1 items-center\",\n          align === \"right\" && \"justify-end\",\n          align === \"center\" && \"justify-center\",\n        )}\n      >\n        {children}\n      </span>\n      {column.getIsSorted() === \"asc\" ? (\n        <HugeiconsIcon\n          icon={SortByUp01Icon}\n          strokeWidth={2}\n          className=\"size-4\"\n        />\n      ) : column.getIsSorted() === \"desc\" ? (\n        <HugeiconsIcon\n          icon={SortByDown01Icon}\n          strokeWidth={2}\n          className=\"size-4\"\n        />\n      ) : (\n        <HugeiconsIcon\n          icon={ArrowUpDownIcon}\n          strokeWidth={2}\n          className=\"size-4 opacity-0 group-hover:opacity-50\"\n        />\n      )}\n    </button>\n  );\n}\n\nexport interface DataTableHeaderProps extends React.ComponentProps<\n  typeof TableHeader\n> {\n  enableSorting?: boolean;\n}\n\nfunction DataTableHeader({\n  enableSorting = false,\n  className,\n  ...props\n}: DataTableHeaderProps) {\n  const { table } = useDataTable();\n\n  const renderHeader = (\n    header: ReturnType<typeof table.getHeaderGroups>[0][\"headers\"][0],\n    index: number,\n    totalHeaders: number,\n  ) => {\n    if (header.isPlaceholder) return null;\n\n    const headerDef = header.column.columnDef.header;\n    const canSort = header.column.getCanSort();\n    const meta = header.column.columnDef.meta as\n      | { align?: \"left\" | \"center\" | \"right\" }\n      | undefined;\n\n    if (typeof headerDef === \"string\" && enableSorting && canSort) {\n      return (\n        <SortableHeader\n          column={header.column}\n          align={meta?.align}\n          isFirst={index === 0}\n          isLast={index === totalHeaders - 1}\n        >\n          {headerDef}\n        </SortableHeader>\n      );\n    }\n\n    return flexRender(headerDef, header.getContext());\n  };\n\n  return (\n    <TableHeader className={className} {...props}>\n      {table.getHeaderGroups().map((headerGroup) => (\n        <TableRow key={headerGroup.id}>\n          {headerGroup.headers.map((header, index) => (\n            <TableHead key={header.id} colSpan={header.colSpan}>\n              {renderHeader(header, index, headerGroup.headers.length)}\n            </TableHead>\n          ))}\n        </TableRow>\n      ))}\n    </TableHeader>\n  );\n}\n\nexport interface DataTableBodyProps extends React.ComponentProps<\n  typeof TableBody\n> {\n  emptyState?: React.ReactNode;\n}\n\nfunction DataTableBody({\n  emptyState,\n  className,\n  ...props\n}: DataTableBodyProps) {\n  const { table } = useDataTable();\n  const totalColumns = table.getAllColumns().length;\n\n  return (\n    <TableBody className={className} {...props}>\n      {table.getRowModel().rows?.length ? (\n        table.getRowModel().rows.map((row) => (\n          <TableRow key={row.id} selected={row.getIsSelected()}>\n            {row.getVisibleCells().map((cell) => (\n              <TableCell key={cell.id}>\n                {flexRender(cell.column.columnDef.cell, cell.getContext())}\n              </TableCell>\n            ))}\n          </TableRow>\n        ))\n      ) : (\n        <TableRow>\n          <TableCell colSpan={totalColumns} className=\"h-24 text-center\">\n            {emptyState ?? \"No results.\"}\n          </TableCell>\n        </TableRow>\n      )}\n    </TableBody>\n  );\n}\n\nexport type DataTableFooterProps = React.ComponentProps<typeof TableFooter>;\n\nfunction DataTableFooter({\n  className,\n  children,\n  ...props\n}: DataTableFooterProps) {\n  return (\n    <TableFooter className={className} {...props}>\n      {children}\n    </TableFooter>\n  );\n}\n\nexport {\n  DataTable,\n  DataTableToolbar,\n  ToolbarSeparator as DataTableToolbarSeparator,\n  DataTableSearch,\n  DataTableColumnToggle,\n  DataTableContent,\n  DataTableHeader,\n  DataTableBody,\n  DataTableFooter,\n  DataTablePagination,\n  useDataTable,\n};\n\nexport type {\n  ColumnDef,\n  SortingState,\n  RowSelectionState,\n  ColumnFiltersState,\n  VisibilityState,\n  PaginationState,\n};\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/data-table/data-table.tsx"
    },
    {
      "path": "registry/default/data-table/data-table-column-toggle.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { FilterHorizontalIcon } from \"@hugeicons/core-free-icons\";\n\nimport {\n  DropdownMenu,\n  DropdownMenuTrigger,\n  DropdownMenuContent,\n  DropdownMenuLabel,\n  DropdownMenuCheckboxItem,\n} from \"@/registry/default/dropdown-menu/dropdown-menu\";\nimport { Button } from \"@/registry/default/button/button\";\nimport { useDataTable } from \"@/registry/default/data-table/data-table-context\";\n\nfunction DataTableColumnToggle() {\n  const { table } = useDataTable();\n\n  const toggleableColumns = table\n    .getAllColumns()\n    .filter((col) => col.id !== \"__select__\" && col.getCanHide());\n\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger render={<Button variant=\"ghost\" size=\"icon_sm\" />}>\n        <HugeiconsIcon\n          icon={FilterHorizontalIcon}\n          className=\"size-4\"\n          strokeWidth={2}\n        />\n      </DropdownMenuTrigger>\n      <DropdownMenuContent side=\"bottom\" align=\"end\">\n        <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>\n        {toggleableColumns.map((column) => {\n          const label =\n            typeof column.columnDef.header === \"string\"\n              ? column.columnDef.header\n              : column.id;\n\n          return (\n            <DropdownMenuCheckboxItem\n              key={column.id}\n              checked={column.getIsVisible()}\n              onCheckedChange={(checked) => column.toggleVisibility(!!checked)}\n            >\n              {label}\n            </DropdownMenuCheckboxItem>\n          );\n        })}\n      </DropdownMenuContent>\n    </DropdownMenu>\n  );\n}\n\nexport { DataTableColumnToggle };\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/data-table/data-table-column-toggle.tsx"
    },
    {
      "path": "registry/default/data-table/data-table-context.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type Table } from \"@tanstack/react-table\"\n\ninterface DataTableContextValue<TData> {\n\ttable: Table<TData>\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Generic context requires any; type safety is enforced via useDataTable<TData>()\nconst DataTableContext = React.createContext<DataTableContextValue<any> | null>(\n\tnull\n)\n\nfunction useDataTable<TData>() {\n\tconst context = React.useContext(DataTableContext)\n\tif (!context) {\n\t\tthrow new Error(\"useDataTable must be used within a DataTable\")\n\t}\n\treturn context as DataTableContextValue<TData>\n}\n\nexport { DataTableContext, useDataTable }\nexport type { DataTableContextValue }\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/data-table/data-table-context.tsx"
    },
    {
      "path": "registry/default/data-table/data-table-pagination.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/button/button\"\nimport { useDataTable } from \"@/registry/default/data-table/data-table-context\"\n\ninterface DataTablePaginationProps {\n\tclassName?: string\n\tshowSelectedCount?: boolean\n}\n\nfunction DataTablePagination({\n\tclassName,\n\tshowSelectedCount = true,\n}: DataTablePaginationProps) {\n\tconst { table } = useDataTable()\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"flex items-center justify-between px-2 pb-2\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t>\n\t\t\t{showSelectedCount ? (\n\t\t\t\t<span className=\"text-muted-foreground text-sm\">\n\t\t\t\t\t{table.getFilteredSelectedRowModel().rows.length} of{\" \"}\n\t\t\t\t\t{table.getFilteredRowModel().rows.length} row(s) selected\n\t\t\t\t</span>\n\t\t\t) : (\n\t\t\t\t<div />\n\t\t\t)}\n\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tonClick={() => table.previousPage()}\n\t\t\t\t\tdisabled={!table.getCanPreviousPage()}\n\t\t\t\t>\n\t\t\t\t\tPrevious\n\t\t\t\t</Button>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tonClick={() => table.nextPage()}\n\t\t\t\t\tdisabled={!table.getCanNextPage()}\n\t\t\t\t>\n\t\t\t\t\tNext\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nexport { DataTablePagination }\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/data-table/data-table-pagination.tsx"
    },
    {
      "path": "registry/default/data-table/data-table-search.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Toolbar } from \"@base-ui/react/toolbar\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Search01Icon } from \"@hugeicons/core-free-icons\";\n\nimport { cn } from \"@/lib/utils\";\nimport { useDataTable } from \"@/registry/default/data-table/data-table-context\";\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n} from \"@/registry/default/input-group/input-group\";\n\ninterface DataTableSearchProps extends Omit<\n  React.ComponentProps<\"input\">,\n  \"value\" | \"onChange\" | \"size\"\n> {\n  placeholder?: string;\n  size?: \"default\" | \"sm\";\n}\n\nfunction DataTableSearch({\n  placeholder = \"Search and filter\",\n  size = \"sm\",\n  className,\n  ...props\n}: DataTableSearchProps) {\n  const { table } = useDataTable();\n\n  return (\n    <InputGroup className=\"flex-1 border-0 bg-transparent shadow-none outline-none! dark:bg-transparent\">\n      <InputGroupAddon>\n        <HugeiconsIcon\n          icon={Search01Icon}\n          className=\"text-muted-foreground size-4\"\n          strokeWidth={2}\n        />\n      </InputGroupAddon>\n      <InputGroupInput\n        placeholder={placeholder}\n        size={size}\n        value={table.getState().globalFilter ?? \"\"}\n        onChange={(e) => table.setGlobalFilter(e.target.value)}\n        className={cn(\"py-0\", className)}\n        render={<Toolbar.Input />}\n        {...props}\n      />\n    </InputGroup>\n  );\n}\n\nexport { DataTableSearch };\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/data-table/data-table-search.tsx"
    }
  ],
  "type": "registry:ui"
}