'use client' import { flexRender, getCoreRowModel, useReactTable, type ColumnDef, type ColumnPinningState, type ColumnSort, type RowSelectionState, type SortingState, type Table, type VisibilityState } from '@tanstack/react-table' import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react' import { useSearchParams } from 'react-router' import { Card, CardContent } from '@repo/ui/components/ui/card' import { Table as Table_, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@repo/ui/components/ui/table' import { cn } from '@repo/ui/lib/utils' import { DataTablePagination } from './pagination' interface DataTableProps { children?: ReactNode columns: ColumnDef[] columnPinning?: ColumnPinningState data: TData[] setSelectedRows?: (selectedRows: TData[]) => void pageIndex: number sort: SortingState pageSize: number rowCount: number columnVisibilityInit?: VisibilityState } interface TableContextProps { table: Table } const TableContext = createContext | null>(null) export function useDataTable() { const ctx = useContext(TableContext) if (!ctx) { throw new Error('TableContext is null') } return ctx as { table: Table } } export function DataTable({ data, children, columns, columnPinning = { left: [], right: [] }, sort, pageIndex, pageSize, rowCount, setSelectedRows, columnVisibilityInit = {} }: DataTableProps) { const [dataTable, setDataTable] = useState(data) const [searchParams, setSearchParams] = useSearchParams() const [rowSelection, setRowSelection] = useState({}) const columnVisibilityInit_ = useMemo(() => { const columns = searchParams.get('columns') if (columns === null) { return columnVisibilityInit } const columnVisibility = columns ? columns.split(',') : [] return Object.keys(columnVisibilityInit).reduce( (acc, col) => ({ ...acc, [col]: columnVisibility.includes(col) }), {} ) }, []) const [columnVisibility, setColumnVisibility_] = useState( columnVisibilityInit_ ) const sorting = useMemo(() => { const sortParam = searchParams.get('sort') if (!sortParam) { return sort } return sortParam.split(',').map((s) => { const [id, dir] = s.split(':') return { id, desc: dir === 'desc' } }) }, [searchParams, sort]) const setPagination = useCallback( (updater: any) => { const newState = typeof updater === 'function' ? updater({ pageIndex, pageSize }) : updater setRowSelection({}) setSearchParams((prev) => { prev.set('p', newState?.pageIndex.toString()) prev.set('perPage', newState?.pageSize.toString()) return prev }) }, [pageIndex, pageSize, setSearchParams] ) const setSorting = useCallback( (updater: any) => { const newSorting = typeof updater === 'function' ? updater(sorting) : updater setSearchParams((prev) => { if (newSorting.length) { const sort = newSorting .map((s: ColumnSort) => `${s.id}:${s.desc ? 'desc' : 'asc'}`) .join(',') prev.set('sort', sort) } else { prev.delete('sort') } return prev }) }, [sorting, setSearchParams] ) const setColumnVisibility = useCallback( (updater: any) => { const newColumnVisibility = typeof updater === 'function' ? updater(columnVisibility) : updater setColumnVisibility_(newColumnVisibility) setSearchParams((prev) => { const columns = Object.entries(newColumnVisibility) .filter(([_, visible]) => visible) .map(([col, _]) => col) if (columns.length > 0) { prev.set('columns', columns.join(',')) } else { prev.set('columns', '') } return prev }) }, [setSearchParams, setColumnVisibility_] ) useEffect(() => { setDataTable(data) }, [data]) const getRowId = useCallback((row: any) => row.id, []) const removeRow = useCallback((rowId: string) => { setDataTable((rows) => rows.filter((row: any) => row.id !== rowId)) }, []) const table = useReactTable({ data: dataTable, columns, rowCount, state: { sorting, rowSelection, columnVisibility, columnPinning, pagination: { pageIndex, pageSize } }, manualSorting: true, manualPagination: true, enableColumnPinning: true, enableRowSelection: true, getRowId, getCoreRowModel: getCoreRowModel(), onRowSelectionChange: setRowSelection, onSortingChange: setSorting, onColumnVisibilityChange: setColumnVisibility, onPaginationChange: setPagination, meta: { removeRow } }) useEffect(() => { if (!setSelectedRows) return const selected = table.getSelectedRowModel().flatRows.map((r) => r.original) setSelectedRows(selected) }, [rowSelection, setSelectedRows, table]) return (
{children} {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { const isPinned = header.column.getIsPinned() return ( {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext() )} ) })} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => { const isSelected = row.getIsSelected() return ( {row.getVisibleCells().map((cell) => { const isPinned = cell.column.getIsPinned() return ( {flexRender( cell.column.columnDef.cell, cell.getContext() )} ) })} ) }) ) : ( Nenhum resultado. )}
) }