'use client'; import { useCallback, useState } from 'react'; import { useRouter } from 'next/navigation'; import { flexRender, getCoreRowModel, getSortedRowModel, useReactTable, } from '@tanstack/react-table'; import type { ColumnDef, ColumnFiltersState, PaginationState, Table as ReactTable, Row, SortingState, VisibilityState, } from '@tanstack/react-table'; import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, } from 'lucide-react'; import { Button } from '../shadcn/button'; import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from '../shadcn/table'; import { Trans } from './trans'; interface ReactTableProps { data: T[]; columns: ColumnDef[]; renderSubComponent?: (props: { row: Row }) => React.ReactElement; pageIndex?: number; pageSize?: number; pageCount?: number; onPaginationChange?: (pagination: PaginationState) => void; onSortingChange?: (sorting: SortingState) => void; manualPagination?: boolean; manualSorting?: boolean; sorting?: SortingState; tableProps?: React.ComponentProps & Record<`data-${string}`, string>; } export function DataTable({ data, columns, pageIndex, pageSize, pageCount, onPaginationChange, onSortingChange, tableProps, manualPagination = true, manualSorting = false, sorting: initialSorting, }: ReactTableProps) { 'use no memo'; const [pagination, setPagination] = useState({ pageIndex: pageIndex ?? 0, pageSize: pageSize ?? 15, }); const [sorting, setSorting] = useState(initialSorting ?? []); const [columnFilters, setColumnFilters] = useState([]); const [columnVisibility, setColumnVisibility] = useState({}); const [rowSelection, setRowSelection] = useState({}); const navigateToPage = useNavigateToNewPage(); const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), manualPagination, manualSorting, onColumnFiltersChange: setColumnFilters, onColumnVisibilityChange: setColumnVisibility, onRowSelectionChange: setRowSelection, pageCount, state: { pagination, sorting, columnFilters, columnVisibility, rowSelection, }, onSortingChange: (updater) => { if (typeof updater === 'function') { const nextState = updater(sorting); setSorting(nextState); if (onSortingChange) { onSortingChange(nextState); } } else { setSorting(updater); if (onSortingChange) { onSortingChange(updater); } } }, onPaginationChange: (updater) => { const navigate = (page: number) => setTimeout(() => navigateToPage(page)); if (typeof updater === 'function') { setPagination((prevState) => { const nextState = updater(prevState); if (onPaginationChange) { onPaginationChange(nextState); } else { navigate(nextState.pageIndex); } return nextState; }); } else { setPagination(updater); if (onPaginationChange) { onPaginationChange(updater); } else { navigate(updater.pageIndex); } } }, }); return (
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext(), )} ))} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) ) : ( )}
); } function Pagination({ table, }: React.PropsWithChildren<{ table: ReactTable; }>) { return (
); } /** * Navigates to a new page using the provided page index and optional page parameter. */ function useNavigateToNewPage( props: { pageParam?: string } = { pageParam: 'page', }, ) { const router = useRouter(); const param = props.pageParam ?? 'page'; return useCallback( (pageIndex: number) => { const url = new URL(window.location.href); url.searchParams.set(param, String(pageIndex + 1)); router.push(url.pathname + url.search); }, [param, router], ); }