import { columnFilteringFeature, columnVisibilityFeature, createCoreRowModel, createFilteredRowModel, createPaginatedRowModel, createSortedRowModel, filterFn_includesString, flexRender, globalFilteringFeature, rowPaginationFeature, rowSortingFeature, sortFn_alphanumeric, sortFn_basic, sortFn_text, tableFeatures, useTable, type ColumnDef, type RowData, type SortingState, } from "@tanstack/react-table" import { ArrowUpDown, ChevronLeft, ChevronRight } from "lucide-react" import { useMemo, useState } from "react" import { Button } from "@/components/ui/button" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { cn } from "@/lib/utils" /* =========================================================================== A sortable, filterable, paginated table built on TanStack Table v9. Horizontal overflow is contained by the wrapper, so a wide table never makes the application itself scroll sideways (Section 21). =========================================================================== */ const features = tableFeatures({ columnFilteringFeature, columnVisibilityFeature, globalFilteringFeature, rowSortingFeature, rowPaginationFeature, coreRowModel: createCoreRowModel(), sortedRowModel: createSortedRowModel(), filteredRowModel: createFilteredRowModel(), paginatedRowModel: createPaginatedRowModel(), filterFns: { includesString: filterFn_includesString }, sortFns: { alphanumeric: sortFn_alphanumeric, basic: sortFn_basic, text: sortFn_text, }, }) type Features = typeof features /** Column definition bound to this demo's feature set. */ export type DemoColumnDef = ColumnDef export function DataTable({ columns, data, globalFilter, pageSize = 8, emptyMessage = "Nothing to show.", onRowClick, }: { columns: DemoColumnDef[] data: T[] globalFilter?: string pageSize?: number emptyMessage?: string onRowClick?: (row: T) => void }) { const [sorting, setSorting] = useState([]) // Stable identities: a fresh `state`/`initialState` object on every render // makes the table store notify, re-render, and notify again. const state = useMemo( () => ({ sorting, globalFilter: globalFilter ?? "" }), [sorting, globalFilter] ) const initialState = useMemo( () => ({ pagination: { pageIndex: 0, pageSize } }), [pageSize] ) const table = useTable({ features, data, columns, state, onSortingChange: setSorting, initialState, globalFilterFn: "includesString", }) const rows = table.getRowModel().rows const pageCount = table.getPageCount() const pageIndex = table.state.pagination?.pageIndex ?? 0 return (
{table.getHeaderGroups().map((hg) => ( {hg.headers.map((header) => { const sortable = header.column.getCanSort() return ( {header.isPlaceholder ? null : sortable ? ( ) : ( flexRender(header.column.columnDef.header, header.getContext()) )} ) })} ))} {rows.length === 0 ? ( {emptyMessage} ) : ( rows.map((row) => ( onRowClick(row.original) : undefined} className={cn(onRowClick && "cursor-pointer")} > {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) )}
{pageCount > 1 && (

Page {pageIndex + 1} of {pageCount} ยท{" "} {table.getFilteredRowModel().rows.length} records

)} ) }