{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "data-table-advanced", "title": "Advanced Data Table", "description": "Full-featured data table with search, faceted filters, sorting, pagination, and row selection", "dependencies": [ "@tanstack/react-table" ], "registryDependencies": [ "table", "button", "input", "badge", "checkbox", "command", "popover", "dropdown-menu", "select", "separator" ], "files": [ { "path": "registry/blocks/data-table-advanced/page.tsx", "content": "\"use client\"\n\nimport { type ColumnDef } from \"@tanstack/react-table\"\nimport { PlayIcon } from \"lucide-react\"\n\nimport { Badge } from \"@/registry/ui/badge\"\nimport { Checkbox } from \"@/registry/ui/checkbox\"\nimport { DataTable } from \"@/registry/blocks/data-table-advanced/components/data-table\"\nimport { DataTableColumnHeader } from \"@/registry/blocks/data-table-advanced/components/data-table-column-header\"\nimport { DataTableColumnCell } from \"@/registry/blocks/data-table-advanced/components/data-table-column-cell\"\nimport { DataTableToolbar } from \"@/registry/blocks/data-table-advanced/components/data-table-toolbar\"\nimport { DataTableSelectionToolbar } from \"@/registry/blocks/data-table-advanced/components/data-table-selection-toolbar\"\nimport {\n tasks,\n statuses,\n priorities,\n labels,\n type Task,\n} from \"@/registry/blocks/data-table-advanced/lib/demo-data\"\n\nconst columns: ColumnDef[] = [\n {\n id: \"select\",\n header: ({ table }) => (\n
\n table.toggleAllPageRowsSelected(!!value)}\n aria-label=\"Select all\"\n />\n
\n ),\n cell: ({ row }) => (\n
\n row.toggleSelected(!!value)}\n aria-label=\"Select row\"\n />\n
\n ),\n enableSorting: false,\n enableHiding: false,\n },\n {\n id: \"very_long_column_name_that_should_truncate_in_the_view_options_menu_to_prevent_layout_breakage\",\n accessorFn: (row) => row.title,\n header: ({ column }) => (\n \n ),\n cell: ({ row }) => (\n \n ),\n },\n {\n accessorKey: \"id\",\n header: ({ column }) => (\n \n ),\n cell: ({ row }) =>
{row.getValue(\"id\")}
,\n enableSorting: false,\n enableHiding: false,\n },\n {\n accessorKey: \"title\",\n header: ({ column }) => (\n \n ),\n cell: ({ row }) => {\n const label = labels.find((label) => label.value === row.original.label)\n\n return (\n
\n {label && {label.label}}\n \n {row.getValue(\"title\")}\n \n
\n )\n },\n },\n {\n accessorKey: \"status\",\n header: ({ column }) => (\n \n ),\n cell: ({ row }) => {\n const status = statuses.find(\n (status) => status.value === row.getValue(\"status\")\n )\n\n if (!status) {\n return null\n }\n\n return (\n
\n {status.label}\n
\n )\n },\n filterFn: (row, id, value) => {\n return value.includes(row.getValue(id))\n },\n },\n {\n accessorKey: \"priority\",\n header: ({ column }) => (\n \n ),\n cell: ({ row }) => {\n const priority = priorities.find(\n (priority) => priority.value === row.getValue(\"priority\")\n )\n\n if (!priority) {\n return null\n }\n\n return (\n
\n {priority.label}\n
\n )\n },\n filterFn: (row, id, value) => {\n return value.includes(row.getValue(id))\n },\n },\n {\n accessorKey: \"createdAt\",\n header: ({ column }) => (\n \n ),\n cell: ({ row }) => {\n return
{row.getValue(\"createdAt\")}
\n },\n },\n]\n\nexport default function DataTableDemo() {\n const handleProcessSelected = (selectedTasks: Task[]) => {\n console.log(\"Processing tasks:\", selectedTasks)\n alert(`Processing ${selectedTasks.length} task(s)`)\n }\n\n return (\n
\n
\n

Tasks

\n

\n Manage your tasks with search, filters, sorting, and pagination.\n

\n
\n (\n
\n \n }\n />\n
\n )}\n />\n
\n )\n}\n", "type": "registry:page", "target": "app/data-table-demo/page.tsx" }, { "path": "registry/blocks/data-table-advanced/components/data-table.tsx", "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n flexRender,\n getCoreRowModel,\n getFacetedRowModel,\n getFacetedUniqueValues,\n getFilteredRowModel,\n getPaginationRowModel,\n getSortedRowModel,\n useReactTable,\n type ColumnDef,\n type ColumnFiltersState,\n type SortingState,\n type VisibilityState,\n type Table as TanStackTable,\n} from \"@tanstack/react-table\"\n\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"@/registry/ui/table\"\nimport { DataTablePagination } from \"@/registry/blocks/data-table-advanced/components/data-table-pagination\"\n\ninterface DataTableProps {\n columns: ColumnDef[]\n data: TData[]\n toolbar?: (table: TanStackTable) => React.ReactNode\n showPagination?: boolean\n pageSize?: number\n}\n\nexport function DataTable({\n columns,\n data,\n toolbar,\n showPagination = true,\n pageSize = 10,\n}: DataTableProps) {\n const [rowSelection, setRowSelection] = React.useState({})\n const [columnVisibility, setColumnVisibility] =\n React.useState({})\n const [columnFilters, setColumnFilters] = React.useState(\n []\n )\n const [sorting, setSorting] = React.useState([])\n\n const table = useReactTable({\n data,\n columns,\n state: {\n sorting,\n columnVisibility,\n rowSelection,\n columnFilters,\n },\n initialState: {\n pagination: {\n pageSize,\n },\n },\n enableRowSelection: true,\n onRowSelectionChange: setRowSelection,\n onSortingChange: setSorting,\n onColumnFiltersChange: setColumnFilters,\n onColumnVisibilityChange: setColumnVisibility,\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n getPaginationRowModel: getPaginationRowModel(),\n getSortedRowModel: getSortedRowModel(),\n getFacetedRowModel: getFacetedRowModel(),\n getFacetedUniqueValues: getFacetedUniqueValues(),\n })\n\n return (\n
\n {toolbar?.(table)}\n
\n \n \n {table.getHeaderGroups().map((headerGroup) => (\n \n {headerGroup.headers.map((header) => {\n return (\n \n {header.isPlaceholder\n ? null\n : flexRender(\n header.column.columnDef.header,\n header.getContext()\n )}\n \n )\n })}\n \n ))}\n \n \n {table.getRowModel().rows?.length ? (\n table.getRowModel().rows.map((row) => (\n \n {row.getVisibleCells().map((cell) => (\n \n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext()\n )}\n \n ))}\n \n ))\n ) : (\n \n \n No results.\n \n \n )}\n \n
\n
\n {showPagination && }\n
\n )\n}\n\nexport { useReactTable, type ColumnDef, type TanStackTable as Table }\n", "type": "registry:component" }, { "path": "registry/blocks/data-table-advanced/components/data-table-column-header.tsx", "content": "\"use client\"\n\nimport { type Column } from \"@tanstack/react-table\"\nimport { ArrowDownIcon, ArrowUpIcon, ChevronsUpDownIcon, EyeOffIcon } from \"lucide-react\"\n\nimport { cn } from \"@/registry/lib/utils\"\nimport { Button } from \"@/registry/ui/button\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/registry/ui/dropdown-menu\"\n\ninterface DataTableColumnHeaderProps\n extends React.HTMLAttributes {\n column: Column\n title: string\n}\n\nexport function DataTableColumnHeader({\n column,\n title,\n className,\n}: DataTableColumnHeaderProps) {\n if (!column.getCanSort()) {\n return
{title}
\n }\n\n return (\n
\n \n \n \n {title}\n {column.getIsSorted() === \"desc\" ? (\n \n ) : column.getIsSorted() === \"asc\" ? (\n \n ) : (\n \n )}\n \n \n \n column.toggleSorting(false)}>\n \n Asc\n \n column.toggleSorting(true)}>\n \n Desc\n \n {column.getCanHide() && (\n <>\n \n column.toggleVisibility(false)}>\n \n Hide\n \n \n )}\n \n \n
\n )\n}\n", "type": "registry:component" }, { "path": "registry/blocks/data-table-advanced/components/data-table-faceted-filter.tsx", "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type Column } from \"@tanstack/react-table\"\nimport { CheckIcon, ChevronDownIcon } from \"lucide-react\"\n\nimport { cn } from \"@/registry/lib/utils\"\nimport { Badge } from \"@/registry/ui/badge\"\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandList,\n} from \"@/registry/ui/command\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/ui/popover\"\nimport { Separator } from \"@/registry/ui/separator\"\n\ninterface DataTableFacetedFilterProps {\n column?: Column\n title?: string\n options: {\n label: string\n value: string\n }[]\n}\n\nexport function DataTableFacetedFilter({\n column,\n title,\n options,\n}: DataTableFacetedFilterProps) {\n const facets = column?.getFacetedUniqueValues()\n const selectedValues = new Set(column?.getFilterValue() as string[])\n\n return (\n \n \n \n \n \n \n \n No results found.\n \n {options.map((option, index) => {\n const isSelected = selectedValues.has(option.value)\n return (\n {\n if (isSelected) {\n selectedValues.delete(option.value)\n } else {\n selectedValues.add(option.value)\n }\n const filterValues = Array.from(selectedValues)\n column?.setFilterValue(\n filterValues.length ? filterValues : undefined\n )\n }}\n className={cn(\n \"rounded-none data-[selected=true]:bg-transparent [&>svg:last-child]:hidden px-3 py-2.5\",\n index > 0 && \"border-t border-treasury-base-darkest\"\n )}\n >\n \n \n \n {option.label}\n {facets?.get(option.value) !== undefined && facets.get(option.value)! > 0 && (\n \n {facets.get(option.value)}\n \n )}\n \n )\n })}\n \n {selectedValues.size > 0 && (\n \n column?.setFilterValue(undefined)}\n className=\"justify-center text-center rounded-none border-t border-treasury-base-darkest data-[selected=true]:bg-transparent px-3 py-2.5\"\n >\n Clear filters\n \n \n )}\n \n \n \n \n )\n}\n", "type": "registry:component" }, { "path": "registry/blocks/data-table-advanced/components/data-table-pagination.tsx", "content": "\"use client\"\n\nimport { type Table } from \"@tanstack/react-table\"\nimport { ArrowLeftIcon, ArrowRightIcon } from \"lucide-react\"\n\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/registry/ui/select\"\nimport { Separator } from \"@/registry/ui/separator\"\nimport { cn } from \"@/lib/utils\"\n\ninterface DataTablePaginationProps {\n table: Table\n pageSizeOptions?: number[]\n}\n\nexport function DataTablePagination({\n table,\n pageSizeOptions = [10, 20, 25, 30, 40, 50],\n}: DataTablePaginationProps) {\n const pageCount = table.getPageCount()\n const currentPage = table.getState().pagination.pageIndex + 1\n\n // Generate page numbers to display\n const getPageNumbers = () => {\n const pages: (number | \"ellipsis\")[] = []\n const maxVisiblePages = 9\n\n if (pageCount <= maxVisiblePages) {\n // Show all pages if there are few enough\n for (let i = 1; i <= pageCount; i++) {\n pages.push(i)\n }\n } else {\n // Always show first few pages, then ellipsis, then last page\n for (let i = 1; i <= Math.min(maxVisiblePages - 1, pageCount - 1); i++) {\n pages.push(i)\n }\n if (pageCount > maxVisiblePages) {\n pages.push(\"ellipsis\")\n }\n }\n\n return pages\n }\n\n const pageNumbers = getPageNumbers()\n\n return (\n
\n \n
\n {/* Previous button */}\n table.previousPage()}\n disabled={!table.getCanPreviousPage()}\n aria-label=\"Go to previous page\"\n >\n \n \n\n {/* Page numbers */}\n
\n {pageNumbers.map((page, index) =>\n page === \"ellipsis\" ? (\n \n ...\n \n ) : (\n table.setPageIndex(page - 1)}\n className={cn(\n \"inline-flex items-center justify-center h-9 min-w-9 px-2 text-sm font-medium transition-colors\",\n currentPage === page\n ? \"text-treasury-primary font-bold\"\n : \"text-treasury-base-dark hover:text-treasury-primary\"\n )}\n aria-label={`Go to page ${page}`}\n aria-current={currentPage === page ? \"page\" : undefined}\n >\n {page}\n \n )\n )}\n
\n\n {/* Next button */}\n table.nextPage()}\n disabled={!table.getCanNextPage()}\n aria-label=\"Go to next page\"\n >\n \n \n
\n\n {/* Row info and page size selector */}\n
\n
\n {table.getFilteredSelectedRowModel().rows.length} of{\" \"}\n {table.getFilteredRowModel().rows.length} row(s) selected.\n
\n
\n Rows per page\n {\n table.setPageSize(Number(value))\n }}\n >\n \n \n \n \n {pageSizeOptions.map((pageSize) => (\n \n {pageSize}\n \n ))}\n \n \n
\n
\n
\n )\n}\n", "type": "registry:component" }, { "path": "registry/blocks/data-table-advanced/components/data-table-toolbar.tsx", "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type Table } from \"@tanstack/react-table\"\nimport { XIcon, SearchIcon } from \"lucide-react\"\n\nimport { Button } from \"@/registry/ui/button\"\nimport { Input } from \"@/registry/ui/input\"\nimport { DataTableViewOptions } from \"@/registry/blocks/data-table-advanced/components/data-table-view-options\"\nimport { DataTableFacetedFilter } from \"@/registry/blocks/data-table-advanced/components/data-table-faceted-filter\"\nimport { useDebounce } from \"@/registry/blocks/data-table-advanced/hooks/use-debounce\"\n\ninterface DataTableToolbarProps {\n table: Table\n searchColumn?: string\n searchPlaceholder?: string\n filters?: {\n column: string\n title: string\n options: {\n label: string\n value: string\n }[]\n }[]\n actions?: React.ReactNode\n}\n\nexport function DataTableToolbar({\n table,\n searchColumn,\n searchPlaceholder = \"Search...\",\n filters = [],\n actions,\n}: DataTableToolbarProps) {\n const isFiltered = table.getState().columnFilters.length > 0\n\n // Local state for search input (for debouncing)\n const currentValue = searchColumn\n ? (table.getColumn(searchColumn)?.getFilterValue() as string) ?? \"\"\n : \"\"\n const [searchInput, setSearchInput] = React.useState(currentValue)\n const debouncedSearch = useDebounce(searchInput, 300)\n\n // Sync local state when external filter changes\n React.useEffect(() => {\n setSearchInput(currentValue)\n }, [currentValue])\n\n // Update table filter when debounced search changes\n React.useEffect(() => {\n if (searchColumn && debouncedSearch !== currentValue) {\n table.getColumn(searchColumn)?.setFilterValue(debouncedSearch || undefined)\n }\n }, [debouncedSearch, searchColumn, table, currentValue])\n\n const handleClearFilters = () => {\n setSearchInput(\"\")\n table.resetColumnFilters()\n }\n\n return (\n
\n
\n {searchColumn && (\n
\n \n setSearchInput(e.target.value)}\n className=\"h-10 w-[200px] pl-8 lg:w-[300px]\"\n />\n
\n )}\n {filters.map((filter) => {\n const column = table.getColumn(filter.column)\n if (!column) return null\n return (\n \n )\n })}\n {isFiltered && (\n \n Clear filters\n \n \n )}\n
\n
\n \n {actions}\n
\n
\n )\n}\n", "type": "registry:component" }, { "path": "registry/blocks/data-table-advanced/components/data-table-view-options.tsx", "content": "\"use client\"\n\nimport { type Table } from \"@tanstack/react-table\"\nimport { Settings2Icon, ChevronDownIcon, CheckIcon } from \"lucide-react\"\n\nimport { cn } from \"@/registry/lib/utils\"\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandList,\n} from \"@/registry/ui/command\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/registry/ui/popover\"\n\ninterface DataTableViewOptionsProps {\n table: Table\n}\n\nexport function DataTableViewOptions({\n table,\n}: DataTableViewOptionsProps) {\n const columns = table\n .getAllColumns()\n .filter(\n (column) =>\n typeof column.accessorFn !== \"undefined\" && column.getCanHide()\n )\n\n return (\n \n \n \n \n \n \n \n No columns.\n \n {columns.map((column, index) => {\n const isVisible = column.getIsVisible()\n return (\n column.toggleVisibility(!isVisible)}\n className={cn(\n \"rounded-none data-[selected=true]:bg-transparent [&>svg:last-child]:hidden px-3 py-2.5 capitalize\",\n index > 0 && \"border-t border-treasury-base-darkest\"\n )}\n >\n \n \n \n {column.id}\n \n )\n })}\n \n \n \n \n \n )\n}\n", "type": "registry:component" }, { "path": "registry/blocks/data-table-advanced/components/data-table-selection-toolbar.tsx", "content": "\"use client\"\n\nimport { type Table } from \"@tanstack/react-table\"\nimport { XIcon } from \"lucide-react\"\n\nimport { Button } from \"@/registry/ui/button\"\n\ninterface DataTableSelectionToolbarProps {\n table: Table\n onProcessSelected?: (rows: TData[]) => void\n processLabel?: string\n processIcon?: React.ReactNode\n}\n\nexport function DataTableSelectionToolbar({\n table,\n onProcessSelected,\n processLabel = \"Process Selected\",\n processIcon,\n}: DataTableSelectionToolbarProps) {\n const selectedRows = table.getFilteredSelectedRowModel().rows\n const selectedCount = selectedRows.length\n\n const handleProcessSelected = () => {\n if (onProcessSelected) {\n onProcessSelected(selectedRows.map((row) => row.original))\n }\n table.resetRowSelection()\n }\n\n const handleClearSelection = () => {\n table.resetRowSelection()\n }\n\n if (selectedCount === 0) {\n return null\n }\n\n return (\n
\n \n {selectedCount} item{selectedCount !== 1 ? \"s\" : \"\"} selected\n \n
\n \n {onProcessSelected && (\n \n )}\n
\n
\n )\n}\n", "type": "registry:component" }, { "path": "registry/blocks/data-table-advanced/hooks/use-debounce.ts", "content": "\"use client\"\n\nimport { useState, useEffect } from \"react\"\n\nexport function useDebounce(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState(value)\n\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedValue(value)\n }, delay)\n\n return () => {\n clearTimeout(timer)\n }\n }, [value, delay])\n\n return debouncedValue\n}\n", "type": "registry:hook" }, { "path": "registry/blocks/data-table-advanced/lib/demo-data.ts", "content": "export interface Task {\n id: string\n title: string\n status: \"todo\" | \"in-progress\" | \"done\" | \"cancelled\"\n priority: \"low\" | \"medium\" | \"high\"\n label: \"bug\" | \"feature\" | \"documentation\"\n createdAt: string\n}\n\nexport const statuses = [\n {\n value: \"todo\",\n label: \"Todo\",\n },\n {\n value: \"in-progress\",\n label: \"In Progress\",\n },\n {\n value: \"done\",\n label: \"Done\",\n },\n {\n value: \"cancelled\",\n label: \"Cancelled\",\n },\n]\n\nexport const priorities = [\n {\n value: \"low\",\n label: \"Low\",\n },\n {\n value: \"medium\",\n label: \"Medium\",\n },\n {\n value: \"high\",\n label: \"High\",\n },\n]\n\nexport const labels = [\n {\n value: \"bug\",\n label: \"Bug\",\n },\n {\n value: \"feature\",\n label: \"Feature\",\n },\n {\n value: \"documentation\",\n label: \"Documentation\",\n },\n]\n\nexport const tasks: Task[] = [\n {\n id: \"TASK-8782\",\n title: \"You can't compress the program without quantifying the open-source SSD pixel!\",\n status: \"in-progress\",\n priority: \"medium\",\n label: \"documentation\",\n createdAt: \"2024-01-15\",\n },\n {\n id: \"TASK-7878\",\n title: \"Try to calculate the EXE feed, maybe it will index the multi-byte pixel!\",\n status: \"todo\",\n priority: \"high\",\n label: \"feature\",\n createdAt: \"2024-01-14\",\n },\n {\n id: \"TASK-7839\",\n title: \"We need to bypass the neural TCP card!\",\n status: \"done\",\n priority: \"low\",\n label: \"bug\",\n createdAt: \"2024-01-13\",\n },\n {\n id: \"TASK-5562\",\n title: \"The SAS interface is down, bypass the open-source sensor so we can get the SAS bandwidth!\",\n status: \"todo\",\n priority: \"medium\",\n label: \"feature\",\n createdAt: \"2024-01-12\",\n },\n {\n id: \"TASK-8686\",\n title: \"I'll parse the wireless SSL protocol, that should driver the API panel!\",\n status: \"cancelled\",\n priority: \"medium\",\n label: \"documentation\",\n createdAt: \"2024-01-11\",\n },\n {\n id: \"TASK-1280\",\n title: \"Use the digital TLS panel, then you can transmit the haptic system!\",\n status: \"done\",\n priority: \"high\",\n label: \"bug\",\n createdAt: \"2024-01-10\",\n },\n {\n id: \"TASK-7262\",\n title: \"The UTF8 application is down, parse the neural bandwidth so we can get the UTF8 matrix!\",\n status: \"in-progress\",\n priority: \"low\",\n label: \"feature\",\n createdAt: \"2024-01-09\",\n },\n {\n id: \"TASK-1138\",\n title: \"Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwidth!\",\n status: \"todo\",\n priority: \"high\",\n label: \"bug\",\n createdAt: \"2024-01-08\",\n },\n {\n id: \"TASK-7184\",\n title: \"We need to program the back-end THX pixel!\",\n status: \"done\",\n priority: \"medium\",\n label: \"documentation\",\n createdAt: \"2024-01-07\",\n },\n {\n id: \"TASK-5160\",\n title: \"Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!\",\n status: \"in-progress\",\n priority: \"low\",\n label: \"feature\",\n createdAt: \"2024-01-06\",\n },\n {\n id: \"TASK-5618\",\n title: \"I'll compress the virtual JSON pixel, that should card the JBOD transmitter!\",\n status: \"todo\",\n priority: \"high\",\n label: \"bug\",\n createdAt: \"2024-01-05\",\n },\n {\n id: \"TASK-6699\",\n title: \"Parsing the firewall won't do anything, we need to program the primary RAM bus!\",\n status: \"cancelled\",\n priority: \"low\",\n label: \"documentation\",\n createdAt: \"2024-01-04\",\n },\n {\n id: \"TASK-2858\",\n title: \"We need to hack the multi-byte CSS interface!\",\n status: \"done\",\n priority: \"medium\",\n label: \"feature\",\n createdAt: \"2024-01-03\",\n },\n {\n id: \"TASK-9864\",\n title: \"Try to override the ASCII protocol, maybe it will parse the virtual matrix!\",\n status: \"in-progress\",\n priority: \"high\",\n label: \"bug\",\n createdAt: \"2024-01-02\",\n },\n {\n id: \"TASK-8404\",\n title: \"The IP bandwidth is down, synthesize the neural hard drive so we can get the IP capacitor!\",\n status: \"todo\",\n priority: \"low\",\n label: \"documentation\",\n createdAt: \"2024-01-01\",\n },\n]\n", "type": "registry:lib" } ], "type": "registry:block" }