update package
This commit is contained in:
297
packages/ui/src/components/data-table/data-table.tsx
Normal file
297
packages/ui/src/components/data-table/data-table.tsx
Normal file
@@ -0,0 +1,297 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
type ColumnDef,
|
||||
type ColumnSort,
|
||||
type RowSelectionState,
|
||||
type SortingState,
|
||||
type Table,
|
||||
type VisibilityState
|
||||
} from '@tanstack/react-table'
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
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<TData, TValue> {
|
||||
children?: ReactNode
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
setSelectedRows?: (selectedRows: TData[]) => void
|
||||
pageIndex: number
|
||||
sort: SortingState
|
||||
pageSize: number
|
||||
rowCount: number
|
||||
columnVisibilityInit?: VisibilityState
|
||||
}
|
||||
|
||||
interface TableContextProps<TData> {
|
||||
table: Table<TData>
|
||||
}
|
||||
|
||||
const TableContext = createContext<TableContextProps<any> | null>(null)
|
||||
|
||||
export function useDataTable<TData>() {
|
||||
const ctx = useContext(TableContext)
|
||||
|
||||
if (!ctx) {
|
||||
throw new Error('TableContext is null')
|
||||
}
|
||||
|
||||
return ctx as { table: Table<TData> }
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
data,
|
||||
children,
|
||||
columns,
|
||||
sort,
|
||||
pageIndex,
|
||||
pageSize,
|
||||
rowCount,
|
||||
setSelectedRows,
|
||||
columnVisibilityInit = {}
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [dataTable, setDataTable] = useState<TData[]>(data)
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||
|
||||
const columnVisibilityInit_ = useMemo<VisibilityState>(() => {
|
||||
const columns = searchParams.get('columns')
|
||||
|
||||
if (columns === null) {
|
||||
return columnVisibilityInit
|
||||
}
|
||||
|
||||
const columnVisibility = columns ? columns.split(',') : []
|
||||
return Object.keys(columnVisibilityInit).reduce<VisibilityState>(
|
||||
(acc, col) => ({
|
||||
...acc,
|
||||
[col]: columnVisibility.includes(col)
|
||||
}),
|
||||
{}
|
||||
)
|
||||
}, [])
|
||||
|
||||
const [columnVisibility, setColumnVisibility_] = useState<VisibilityState>(
|
||||
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,
|
||||
pagination: { pageIndex, pageSize }
|
||||
},
|
||||
manualSorting: true,
|
||||
manualPagination: 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 (
|
||||
<TableContext value={{ table }}>
|
||||
<div className="space-y-2.5 max-md:mb-2">
|
||||
<Card className="relative w-full overflow-auto">
|
||||
<CardContent>
|
||||
{children}
|
||||
|
||||
<Table_ className="table-auto">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow
|
||||
key={headerGroup.id}
|
||||
className="hover:bg-transparent"
|
||||
>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead
|
||||
key={header.id}
|
||||
className={cn(
|
||||
'p-2.5',
|
||||
// @ts-ignore
|
||||
header.column.columnDef.meta?.className
|
||||
)}
|
||||
>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
)
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
className="has-[[data-state=open]]:bg-muted/50"
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
className={cn(
|
||||
'p-2.5',
|
||||
// @ts-ignore
|
||||
cell.column.columnDef.meta?.className
|
||||
)}
|
||||
>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
Nenhum resultado.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table_>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<DataTablePagination table={table} />
|
||||
</div>
|
||||
</TableContext>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user