"use client"; import { type ColumnDef, flexRender, getCoreRowModel, useReactTable, getPaginationRowModel, getSortedRowModel, getFilteredRowModel, getFacetedUniqueValues, type RowData, } from "@tanstack/react-table"; import { cn } from "~/lib/utils"; import { DataTablePagination } from "./DataTablePagination"; import { DataTableFilter } from "./filters/DataTableFilter"; import { DebouncedInput } from "../inputs/DebouncedInput"; import { DataTableRowCount } from "./DataTableRowCount"; import { DataTableColumnHeader } from "./DataTableColumnHeader"; import { useQueryStringState } from "./hooks/useQueryStringState"; import { type ReactNode, useState } from "react"; import { Button } from "../ui/button"; import { DownloadIcon, FolderOpenIcon, MaximizeIcon, MinimizeIcon, } from "lucide-react"; import { useCellSelection } from "./hooks/useCellSelection"; import { useCsvExport } from "./hooks/useCsvExport"; import { useRowVirtualizer } from "./hooks/useRowVirtualizer"; import { LoadingIcon } from "yet-another-react-lightbox"; declare module "@tanstack/react-table" { // eslint-disable-next-line @typescript-eslint/no-unused-vars interface ColumnMeta { flex?: boolean; } } export type DataTableProps = { className?: string; columns: ColumnDef[]; data: TData[]; pagination: boolean; enableTopBar?: boolean; enableHeaderFilter?: boolean; isLoading?: boolean; maxCellLines?: number; children?: ReactNode; }; export function DataTable({ className, columns, data, children, pagination, enableTopBar = true, enableHeaderFilter = true, isLoading, maxCellLines = 3, }: DataTableProps) { const [isFullscreen, setIsFullscreen] = useState(false); const { sorting, setSorting, columnFilters, setColumnFilters, globalFilter, setGlobalFilter, } = useQueryStringState(); const table = useReactTable({ data, columns, defaultColumn: { sortUndefined: "last", //size: "auto" as unknown as number, minSize: 10, maxSize: 500, }, getCoreRowModel: getCoreRowModel(), ...(pagination && { getPaginationRowModel: getPaginationRowModel() }), onSortingChange: setSorting, getSortedRowModel: getSortedRowModel(), state: { sorting, columnFilters, globalFilter, }, onColumnFiltersChange: setColumnFilters, onGlobalFilterChange: setGlobalFilter, globalFilterFn: "includesString", getColumnCanGlobalFilter: (column) => column.getCanFilter(), getFilteredRowModel: getFilteredRowModel(), getFacetedUniqueValues: getFacetedUniqueValues(), }); const { rows } = table.getRowModel(); const { tableContainerRef, tableRowRef, virtualRows, virtualHeight, scrollToRow, } = useRowVirtualizer({ table, }); const { isCellSelected, isRowSelected, isCellCopied, ...cellSelection } = useCellSelection({ table, scrollToRow, }); const { exportData } = useCsvExport({ table, }); const hasRows = virtualRows?.length > 0; return (
{enableTopBar && (
{children} setGlobalFilter(String(value))} className="font-lg border-blockmb-0 flex-1 border p-2 shadow" placeholder="Search all columns..." />
)}
{table.getHeaderGroups().map((headerGroup) => (
{headerGroup.headers.map((header) => { return (
{header.isPlaceholder ? null : ( <> {flexRender( header.column.columnDef.header, header.getContext(), )} {(enableHeaderFilter || isFullscreen) && header.column.getCanFilter() ? ( ) : null} )}
); })}
))}
{hasRows && virtualRows.map((virtualRow) => { const row = rows[virtualRow.index]!; return (
{row.getVisibleCells().map((cell) => (
cellSelection.handleCellMouseDown(e, cell) } onMouseUp={(e) => cellSelection.handleCellMouseUp(e, cell) } onMouseOver={(e) => cellSelection.handleCellMouseOver(e, cell) } key={cell.id} className={cn( "relative flex select-none overflow-hidden border-x border-b border-x-transparent bg-background p-2 align-middle transition-colors before:transition-colors focus:outline-none [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", isCellSelected(cell) && "bg-primary/10 before:pointer-events-none before:absolute before:bottom-0 before:left-0 before:right-0 before:top-0 before:border before:border-primary hover:before:bg-primary/10", isCellCopied(cell) && "bg-copy duration-300 before:border-copy-border before:duration-300", )} style={{ width: cell.column.getSize(), minWidth: cell.column.getSize(), flex: cell.column.columnDef.meta?.flex ? 1 : undefined, }} >
{flexRender( cell.column.columnDef.cell, cell.getContext(), )}
))}
); })} {!hasRows && !isLoading && (
No results.
)} {!hasRows && isLoading && (
Loading...
)}
{pagination && }
); }