import { flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, type ColumnDef, type ColumnFiltersState, type FilterFn, type RowSelectionState, type SortingState, type Table, } from "@tanstack/react-table"; import clsx from "clsx"; import { Checkbox as CheckboxPrimitive, Select as SelectPrimative, } from "radix-ui"; import * as React from "react"; import { SuffixContext, useGetKey, useGetSet, useSuffixContext } from "../.."; import { CheckboxIcon, IndeterminateIcon } from "../../icons"; import * as checkboxStyles from "../Checkbox/styles.module.css"; import * as selectStyles from "../Select/styles.module.css"; import * as styles from "./styles.module.css"; import { rankItem, type RankingInfo } from "@tanstack/match-sorter-utils"; const Table = "table"; const TableBody = "tbody"; const TableCell = "td"; const TableHead = "th"; const TableHeader = "thead"; const TableRow = "tr"; interface DataTableProps extends React.ComponentProps<"div"> { "data-id": string; columns: ColumnDef[]; data: TData[]; selectable?: boolean; type?: string; initialPageSize?: number; searchTerm?: string; } const INITIAL_VALUE: { selectedRows: any[] } = { selectedRows: [] }; export function DataTable({ columns = [], data = [], selectable = false, type: _type, children: _children, searchTerm, initialPageSize = 10, ...props }: DataTableProps) { const key = useGetKey(props); const suffix = useSuffixContext(); const [_, setState] = useGetSet<{ selectedRows: TData[] }>( key, INITIAL_VALUE, ); const [sorting, setSorting] = React.useState([]); const [rowSelection, setRowSelection] = React.useState({}); const [columnFilters, setColumnFilters] = React.useState( [], ); const [globalFilter, setGlobalFilter] = React.useState(""); const table = useReactTable({ data, columns, globalFilterFn: "fuzzy", filterFns: { fuzzy: fuzzyFilter, }, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), onSortingChange: setSorting, getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), onRowSelectionChange: (rowSelection) => { setRowSelection(rowSelection); }, onColumnFiltersChange: setColumnFilters, onGlobalFilterChange: setGlobalFilter, initialState: { pagination: { pageIndex: 0, pageSize: initialPageSize, }, }, state: { sorting, rowSelection, columnFilters, globalFilter, }, autoResetPageIndex: false, }); React.useEffect(() => { const selectedRows = table .getFilteredSelectedRowModel() .rows.map((row) => row.original); setState({ selectedRows }); }, [rowSelection, setState, table]); React.useEffect(() => { const handler = setTimeout(() => { if (searchTerm) { setGlobalFilter(searchTerm); table.setPageIndex(0); } else { setGlobalFilter(""); table.setPageIndex(0); } }, 72); // about 4 frames at 60fps return () => clearTimeout(handler); }, [searchTerm]); return (
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { return ( {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext(), )} ); })} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => { const key = suffix ? `${row.id}-${suffix}` : row.id; return ( {row.getVisibleCells().map((cell) => ( {flexRender( cell.column.columnDef.cell, cell.getContext(), )} ))} ); }) ) : ( No results. )}
); } interface DataTablePaginationProps { table: Table; selectable?: boolean; } function DataTablePagination({ table, selectable = false, }: DataTablePaginationProps) { return (
{selectable ? (
{table.getFilteredSelectedRowModel().rows.length} of{" "} {table.getFilteredRowModel().rows.length} row(s) selected.
) : (
)}

Rows per page

{ table.setPageSize(Number(value)); }} > {[10, 20, 30, 40, 50, 100, 500].map((pageSize) => ( {pageSize} ))}
Page {table.getState().pagination.pageIndex + 1} of{" "} {table.getPageCount()}
); } export const Checkbox = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(function Checkbox({ children, checked, disabled, ...props }, ref) { const id = React.useId(); return ( {checked === "indeterminate" && ( )} {checked === true ? ( ) : null} ); }); declare module "@tanstack/react-table" { //add fuzzy filter to the filterFns interface FilterFns { fuzzy: FilterFn; } interface FilterMeta { itemRank: RankingInfo; } } // Define a custom fuzzy filter function that will apply ranking info to rows (using match-sorter utils) const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { // Rank the item const itemRank = rankItem(row.getValue(columnId), value); // Store the itemRank info addMeta({ itemRank, }); // Return if the item should be filtered in/out return itemRank.passed; };