2023-12-13 14:32:57 +03:00
|
|
|
'use client';
|
|
|
|
|
2023-11-26 02:24:16 +03:00
|
|
|
import {
|
2023-12-17 20:19:28 +03:00
|
|
|
ColumnSort,
|
|
|
|
createColumnHelper, getCoreRowModel,
|
|
|
|
getPaginationRowModel, getSortedRowModel,
|
|
|
|
PaginationState, RowData, type RowSelectionState,
|
2023-11-26 02:24:16 +03:00
|
|
|
SortingState, TableOptions, useReactTable, type VisibilityState
|
|
|
|
} from '@tanstack/react-table';
|
2023-12-17 20:19:28 +03:00
|
|
|
import clsx from 'clsx';
|
2023-11-26 02:24:16 +03:00
|
|
|
import { useState } from 'react';
|
|
|
|
|
|
|
|
import DefaultNoData from './DefaultNoData';
|
|
|
|
import PaginationTools from './PaginationTools';
|
2023-12-17 20:19:28 +03:00
|
|
|
import TableBody from './TableBody';
|
|
|
|
import TableFooter from './TableFooter';
|
|
|
|
import TableHeader from './TableHeader';
|
2023-11-26 02:24:16 +03:00
|
|
|
|
|
|
|
export { createColumnHelper, type ColumnSort, type RowSelectionState, type VisibilityState };
|
|
|
|
|
|
|
|
export interface IConditionalStyle<TData> {
|
|
|
|
when: (rowData: TData) => boolean
|
|
|
|
style: React.CSSProperties
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface DataTableProps<TData extends RowData>
|
|
|
|
extends Pick<TableOptions<TData>,
|
|
|
|
'data' | 'columns' |
|
|
|
|
'onRowSelectionChange' | 'onColumnVisibilityChange'
|
|
|
|
> {
|
2023-12-17 20:19:28 +03:00
|
|
|
style?: React.CSSProperties
|
|
|
|
className?: string
|
|
|
|
|
2023-11-26 02:24:16 +03:00
|
|
|
dense?: boolean
|
|
|
|
headPosition?: string
|
|
|
|
noHeader?: boolean
|
|
|
|
noFooter?: boolean
|
2023-12-17 20:19:28 +03:00
|
|
|
|
2023-11-26 02:24:16 +03:00
|
|
|
conditionalRowStyles?: IConditionalStyle<TData>[]
|
2023-12-17 20:19:28 +03:00
|
|
|
noDataComponent?: React.ReactNode
|
|
|
|
|
2023-11-26 02:24:16 +03:00
|
|
|
onRowClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
|
|
|
onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
|
|
|
|
|
|
|
enableRowSelection?: boolean
|
|
|
|
rowSelection?: RowSelectionState
|
|
|
|
|
|
|
|
enableHiding?: boolean
|
|
|
|
columnVisibility?: VisibilityState
|
|
|
|
|
|
|
|
enablePagination?: boolean
|
|
|
|
paginationPerPage?: number
|
|
|
|
paginationOptions?: number[]
|
|
|
|
onChangePaginationOption?: (newValue: number) => void
|
|
|
|
|
|
|
|
enableSorting?: boolean
|
|
|
|
initialSorting?: ColumnSort
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* UI element: data representation as a table.
|
|
|
|
*
|
|
|
|
* @param headPosition - Top position of sticky header (0 if no other sticky elements are present).
|
|
|
|
* No sticky header if omitted
|
|
|
|
*/
|
2023-12-13 14:32:57 +03:00
|
|
|
function DataTable<TData extends RowData>({
|
2023-12-17 20:19:28 +03:00
|
|
|
style, className,
|
2023-11-26 02:24:16 +03:00
|
|
|
dense, headPosition, conditionalRowStyles, noFooter, noHeader,
|
|
|
|
onRowClicked, onRowDoubleClicked, noDataComponent,
|
|
|
|
|
|
|
|
enableRowSelection,
|
|
|
|
rowSelection,
|
|
|
|
|
|
|
|
enableHiding,
|
|
|
|
columnVisibility,
|
|
|
|
|
|
|
|
enableSorting,
|
|
|
|
initialSorting,
|
|
|
|
|
|
|
|
enablePagination,
|
|
|
|
paginationPerPage=10,
|
|
|
|
paginationOptions=[10, 20, 30, 40, 50],
|
|
|
|
onChangePaginationOption,
|
|
|
|
|
2023-12-05 01:22:44 +03:00
|
|
|
...restProps
|
2023-11-26 02:24:16 +03:00
|
|
|
}: DataTableProps<TData>) {
|
|
|
|
const [sorting, setSorting] = useState<SortingState>(initialSorting ? [initialSorting] : []);
|
|
|
|
|
|
|
|
const [pagination, setPagination] = useState<PaginationState>({
|
|
|
|
pageIndex: 0,
|
|
|
|
pageSize: paginationPerPage,
|
|
|
|
});
|
|
|
|
|
|
|
|
const tableImpl = useReactTable({
|
|
|
|
getCoreRowModel: getCoreRowModel(),
|
|
|
|
getSortedRowModel: enableSorting ? getSortedRowModel() : undefined,
|
|
|
|
getPaginationRowModel: enablePagination ? getPaginationRowModel() : undefined,
|
|
|
|
|
|
|
|
state: {
|
|
|
|
pagination: pagination,
|
|
|
|
sorting: sorting,
|
|
|
|
rowSelection: rowSelection ?? {},
|
|
|
|
columnVisibility: columnVisibility ?? {}
|
|
|
|
},
|
|
|
|
enableHiding: enableHiding,
|
|
|
|
onPaginationChange: enablePagination ? setPagination : undefined,
|
|
|
|
onSortingChange: enableSorting ? setSorting : undefined,
|
|
|
|
enableMultiRowSelection: enableRowSelection,
|
2023-12-05 01:22:44 +03:00
|
|
|
...restProps
|
2023-11-26 02:24:16 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
const isEmpty = tableImpl.getRowModel().rows.length === 0;
|
|
|
|
|
|
|
|
return (
|
2023-12-17 20:19:28 +03:00
|
|
|
<div className={clsx(className)} style={style}>
|
|
|
|
<table className='w-full'>
|
2023-11-27 11:33:34 +03:00
|
|
|
{!noHeader ?
|
2023-12-17 20:19:28 +03:00
|
|
|
<TableHeader
|
|
|
|
table={tableImpl}
|
|
|
|
enableRowSelection={enableRowSelection}
|
|
|
|
enableSorting={enableSorting}
|
|
|
|
headPosition={headPosition}
|
|
|
|
/>: null}
|
2023-11-26 02:24:16 +03:00
|
|
|
|
2023-12-17 20:19:28 +03:00
|
|
|
<TableBody
|
|
|
|
table={tableImpl}
|
|
|
|
dense={dense}
|
|
|
|
conditionalRowStyles={conditionalRowStyles}
|
|
|
|
enableRowSelection={enableRowSelection}
|
|
|
|
onRowClicked={onRowClicked}
|
|
|
|
onRowDoubleClicked={onRowDoubleClicked}
|
|
|
|
/>
|
2023-11-26 02:24:16 +03:00
|
|
|
|
2023-11-27 11:33:34 +03:00
|
|
|
{!noFooter ?
|
2023-12-17 20:19:28 +03:00
|
|
|
<TableFooter
|
|
|
|
table={tableImpl}
|
|
|
|
/>: null}
|
2023-11-26 02:24:16 +03:00
|
|
|
</table>
|
|
|
|
|
2023-11-27 11:33:34 +03:00
|
|
|
{(enablePagination && !isEmpty) ?
|
2023-11-26 02:24:16 +03:00
|
|
|
<PaginationTools
|
|
|
|
table={tableImpl}
|
|
|
|
paginationOptions={paginationOptions}
|
|
|
|
onChangePaginationOption={onChangePaginationOption}
|
2023-11-27 11:33:34 +03:00
|
|
|
/> : null}
|
2023-12-17 20:19:28 +03:00
|
|
|
{isEmpty ? (noDataComponent ?? <DefaultNoData />) : null}
|
2023-11-26 02:24:16 +03:00
|
|
|
</div>);
|
|
|
|
}
|
2023-12-13 14:32:57 +03:00
|
|
|
|
|
|
|
export default DataTable;
|