Files
sign/packages/ui/primitives/data-table.tsx

170 lines
4.9 KiB
TypeScript
Raw Normal View History

2023-06-09 18:21:18 +10:00
'use client';
import React, { useMemo } from 'react';
2024-08-27 20:34:39 +09:00
import { Trans } from '@lingui/macro';
2024-01-30 17:31:27 +11:00
import type {
2023-06-09 18:21:18 +10:00
ColumnDef,
PaginationState,
Table as TTable,
Updater,
2024-01-30 17:31:27 +11:00
VisibilityState,
2023-06-09 18:21:18 +10:00
} from '@tanstack/react-table';
2024-01-30 17:31:27 +11:00
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
2023-06-09 18:21:18 +10:00
2024-01-30 17:31:27 +11:00
import { Skeleton } from './skeleton';
2023-06-09 18:21:18 +10:00
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './table';
export type DataTableChildren<TData> = (_table: TTable<TData>) => React.ReactNode;
export type { ColumnDef as DataTableColumnDef } from '@tanstack/react-table';
2023-06-09 18:21:18 +10:00
export interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
2024-01-30 17:31:27 +11:00
columnVisibility?: VisibilityState;
2023-06-09 18:21:18 +10:00
data: TData[];
perPage?: number;
currentPage?: number;
totalPages?: number;
onPaginationChange?: (_page: number, _perPage: number) => void;
2024-01-30 17:31:27 +11:00
onClearFilters?: () => void;
hasFilters?: boolean;
2023-06-09 18:21:18 +10:00
children?: DataTableChildren<TData>;
2024-01-30 17:31:27 +11:00
skeleton?: {
enable: boolean;
rows: number;
component?: React.ReactNode;
};
error?: {
enable: boolean;
component?: React.ReactNode;
};
2023-06-09 18:21:18 +10:00
}
export function DataTable<TData, TValue>({
columns,
2024-01-30 17:31:27 +11:00
columnVisibility,
2023-06-09 18:21:18 +10:00
data,
2024-01-30 17:31:27 +11:00
error,
2023-06-09 18:21:18 +10:00
perPage,
currentPage,
totalPages,
2024-01-30 17:31:27 +11:00
skeleton,
hasFilters,
onClearFilters,
2023-06-09 18:21:18 +10:00
onPaginationChange,
children,
}: DataTableProps<TData, TValue>) {
const pagination = useMemo<PaginationState>(() => {
if (currentPage !== undefined && perPage !== undefined) {
return {
pageIndex: currentPage - 1,
pageSize: perPage,
};
}
return {
pageIndex: 0,
pageSize: 0,
};
}, [currentPage, perPage]);
const manualPagination = Boolean(currentPage !== undefined && totalPages !== undefined);
const onTablePaginationChange = (updater: Updater<PaginationState>) => {
if (typeof updater === 'function') {
const newState = updater(pagination);
onPaginationChange?.(newState.pageIndex + 1, newState.pageSize);
} else {
onPaginationChange?.(updater.pageIndex + 1, updater.pageSize);
}
};
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
state: {
pagination: manualPagination ? pagination : undefined,
2024-01-30 17:31:27 +11:00
columnVisibility,
2023-06-09 18:21:18 +10:00
},
manualPagination,
pageCount: totalPages,
onPaginationChange: onTablePaginationChange,
});
return (
<>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{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'}>
{row.getVisibleCells().map((cell) => (
2024-02-08 13:31:38 +05:30
<TableCell
key={cell.id}
style={{
2024-03-11 00:52:56 +00:00
width: `${cell.column.getSize()}px`,
2024-02-08 13:31:38 +05:30
}}
>
2023-06-09 18:21:18 +10:00
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
2024-01-30 17:31:27 +11:00
) : error?.enable ? (
<TableRow>
{error.component ?? (
<TableCell colSpan={columns.length} className="h-32 text-center">
2024-08-27 20:34:39 +09:00
<Trans>Something went wrong.</Trans>
2024-01-30 17:31:27 +11:00
</TableCell>
)}
</TableRow>
) : skeleton?.enable ? (
Array.from({ length: skeleton.rows }).map((_, i) => (
<TableRow key={`skeleton-row-${i}`}>{skeleton.component ?? <Skeleton />}</TableRow>
))
2023-06-09 18:21:18 +10:00
) : (
<TableRow>
2024-01-30 17:31:27 +11:00
<TableCell colSpan={columns.length} className="h-32 text-center">
2024-08-27 20:34:39 +09:00
<p>
<Trans>No results found</Trans>
</p>
2024-01-30 17:31:27 +11:00
{hasFilters && onClearFilters !== undefined && (
<button
onClick={() => onClearFilters()}
className="text-foreground mt-1 text-sm"
>
2024-08-27 20:34:39 +09:00
<Trans>Clear filters</Trans>
2024-01-30 17:31:27 +11:00
</button>
)}
2023-06-09 18:21:18 +10:00
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
{children && <div className="mt-8 w-full">{children(table)}</div>}
</>
);
}