mirror of
https://github.com/rustfs/console.git
synced 2026-08-29 03:52:28 +08:00
143 lines
4.5 KiB
TypeScript
143 lines
4.5 KiB
TypeScript
"use client"
|
|
|
|
import { Checkbox } from "@/components/ui/checkbox"
|
|
import { resolveDataTablePagination } from "@/lib/data-table-pagination"
|
|
import type {
|
|
ColumnDef,
|
|
ColumnFiltersState,
|
|
PaginationState,
|
|
RowData,
|
|
RowSelectionState,
|
|
SortingState,
|
|
TableOptions,
|
|
} from "@tanstack/react-table"
|
|
import {
|
|
getCoreRowModel,
|
|
getFilteredRowModel,
|
|
getPaginationRowModel,
|
|
getSortedRowModel,
|
|
useReactTable,
|
|
} from "@tanstack/react-table"
|
|
import { useEffect, useMemo, useState } from "react"
|
|
|
|
export interface UseDataTableOptions<TData extends RowData> {
|
|
data: TData[]
|
|
columns: ColumnDef<TData, unknown>[]
|
|
pageSize?: number
|
|
disablePagination?: boolean
|
|
manualPagination?: boolean
|
|
manualSorting?: boolean
|
|
getRowId?: TableOptions<TData>["getRowId"]
|
|
enableRowSelection?: boolean
|
|
initialSorting?: SortingState
|
|
}
|
|
|
|
export interface UseDataTableReturn<TData extends RowData> {
|
|
table: ReturnType<typeof useReactTable<TData>>
|
|
selectedRows: TData[]
|
|
selectedRowIds: string[]
|
|
}
|
|
|
|
function createSelectColumn<TData extends RowData>(): ColumnDef<TData> {
|
|
return {
|
|
id: "select",
|
|
enableSorting: false,
|
|
enableHiding: false,
|
|
header: ({ table }) => (
|
|
<Checkbox
|
|
checked={table.getIsAllPageRowsSelected()}
|
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
|
aria-label="Select all"
|
|
className="translate-y-0.5"
|
|
/>
|
|
),
|
|
cell: ({ row }) => (
|
|
<Checkbox
|
|
checked={row.getIsSelected()}
|
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
|
aria-label="Select row"
|
|
className="translate-y-0.5"
|
|
/>
|
|
),
|
|
size: 48,
|
|
}
|
|
}
|
|
|
|
export function useDataTable<TData extends RowData>(options: UseDataTableOptions<TData>): UseDataTableReturn<TData> {
|
|
const paginationConfig = useMemo(
|
|
() =>
|
|
resolveDataTablePagination({
|
|
disablePagination: options.disablePagination,
|
|
manualPagination: options.manualPagination,
|
|
pageSize: options.pageSize,
|
|
dataLength: options.data.length,
|
|
}),
|
|
[options.data.length, options.disablePagination, options.manualPagination, options.pageSize],
|
|
)
|
|
|
|
const [sorting, setSorting] = useState<SortingState>(options.initialSorting ?? [])
|
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
|
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
|
const [pagination, setPagination] = useState<PaginationState>({
|
|
pageIndex: 0,
|
|
pageSize: paginationConfig.pageSize,
|
|
})
|
|
|
|
const columns = useMemo(() => {
|
|
const base = options.columns
|
|
if (options.enableRowSelection) {
|
|
return [createSelectColumn<TData>(), ...base]
|
|
}
|
|
return base
|
|
}, [options.columns, options.enableRowSelection])
|
|
|
|
// eslint-disable-next-line react-hooks/incompatible-library -- tanstack table hook is required for table state
|
|
const table = useReactTable({
|
|
data: options.data,
|
|
columns,
|
|
state: {
|
|
sorting,
|
|
columnFilters,
|
|
rowSelection,
|
|
pagination,
|
|
},
|
|
enableSorting: !options.manualSorting,
|
|
enableRowSelection: options.enableRowSelection ?? false,
|
|
manualSorting: options.manualSorting ?? false,
|
|
manualPagination: paginationConfig.manualPagination,
|
|
getRowId: options.getRowId,
|
|
onSortingChange: setSorting,
|
|
onColumnFiltersChange: setColumnFilters,
|
|
onRowSelectionChange: setRowSelection,
|
|
onPaginationChange: setPagination,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
getFilteredRowModel: getFilteredRowModel(),
|
|
getPaginationRowModel: paginationConfig.manualPagination ? undefined : getPaginationRowModel(),
|
|
// Library resets pageIndex during render when data changes, which can trigger setState
|
|
// before commit (e.g. on route change). We do the reset in useEffect instead.
|
|
autoResetPageIndex: false,
|
|
})
|
|
|
|
// Reset to first page when data changes (after commit). Replaces autoResetPageIndex
|
|
// so we don't update state during render.
|
|
useEffect(() => {
|
|
setPagination((prev) => (prev.pageIndex === 0 ? prev : { ...prev, pageIndex: 0 }))
|
|
}, [options.data])
|
|
|
|
useEffect(() => {
|
|
setPagination((prev) =>
|
|
prev.pageSize === paginationConfig.pageSize ? prev : { ...prev, pageSize: paginationConfig.pageSize },
|
|
)
|
|
}, [paginationConfig.pageSize])
|
|
|
|
const selectedRows = table.getSelectedRowModel().rows.map((row) => row.original)
|
|
const selectedRowIds = table.getSelectedRowModel().rows.map((row) => row.id)
|
|
|
|
return {
|
|
table,
|
|
selectedRows,
|
|
selectedRowIds,
|
|
}
|
|
}
|