feat: add server-side table sorting
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
import { flexRender } from '@tanstack/react-table';
|
||||
import { TableHead, TableHeader as ShadTableHeader, TableRow } from '@/components/ui/table';
|
||||
import { Table } from '@tanstack/react-table';
|
||||
import { ArrowDownWideNarrow, ArrowUpDown, ArrowUpNarrowWide } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const TableHeader = <TData, _>({ table }: { table: Table<TData> }) => {
|
||||
return (
|
||||
@@ -9,14 +11,38 @@ const TableHeader = <TData, _>({ table }: { table: Table<TData> }) => {
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
const canSort = header.column.getCanSort();
|
||||
const sortDirection = header.column.getIsSorted();
|
||||
const SortIcon =
|
||||
sortDirection === 'asc'
|
||||
? ArrowUpNarrowWide
|
||||
: sortDirection === 'desc'
|
||||
? ArrowDownWideNarrow
|
||||
: ArrowUpDown;
|
||||
|
||||
return (
|
||||
<TableHead
|
||||
key={header.id}
|
||||
className="h-10 bg-card/80 px-2 text-foreground backdrop-blur-xl"
|
||||
onClick={canSort ? header.column.getToggleSortingHandler() : undefined}
|
||||
className={cn(
|
||||
'h-10 bg-card/80 px-2 text-foreground backdrop-blur-xl transition-colors',
|
||||
canSort && 'cursor-pointer select-none hover:bg-muted/60',
|
||||
sortDirection && 'bg-muted/70',
|
||||
)}
|
||||
>
|
||||
{header.isPlaceholder ? null : (
|
||||
<div className="flex items-center gap-2">
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
<div className="flex min-h-8 w-full items-center justify-between gap-2">
|
||||
<span className="truncate">
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</span>
|
||||
{canSort ? (
|
||||
<SortIcon
|
||||
className={cn(
|
||||
'size-3.5 shrink-0 transition-colors',
|
||||
sortDirection ? 'text-foreground' : 'text-muted-foreground/60',
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</TableHead>
|
||||
|
||||
@@ -38,6 +38,8 @@ export interface DataTableProps<TData, TValue> {
|
||||
onPageChange: (newSkip: number) => void;
|
||||
onLimitChange: (newLimit: number) => void;
|
||||
};
|
||||
sorting?: SortingState;
|
||||
onSortingChange?: (sorting: SortingState) => void;
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
@@ -53,9 +55,24 @@ export function DataTable<TData, TValue>({
|
||||
emptyTitle = 'No results found.',
|
||||
emptyDescription = 'Try adjusting your filters or search terms.',
|
||||
pagination,
|
||||
sorting: controlledSorting,
|
||||
onSortingChange,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [internalSorting, setInternalSorting] = React.useState<SortingState>([]);
|
||||
const sorting = controlledSorting ?? internalSorting;
|
||||
const isManualSorting = Boolean(onSortingChange);
|
||||
const handleSortingChange = React.useCallback(
|
||||
(updater: SortingState | ((old: SortingState) => SortingState)) => {
|
||||
const nextSorting = typeof updater === 'function' ? updater(sorting) : updater;
|
||||
if (onSortingChange) {
|
||||
onSortingChange(nextSorting);
|
||||
return;
|
||||
}
|
||||
setInternalSorting(nextSorting);
|
||||
},
|
||||
[onSortingChange, sorting],
|
||||
);
|
||||
|
||||
const columns = React.useMemo(() => {
|
||||
const cols: ColumnDef<TData, TValue>[] = [...initialColumns];
|
||||
@@ -105,10 +122,11 @@ export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onSortingChange: setSorting,
|
||||
onSortingChange: handleSortingChange,
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
manualPagination: !!pagination,
|
||||
manualSorting: isManualSorting,
|
||||
pageCount: pagination?.totalItems ? Math.ceil(pagination.totalItems / pagination.limit) : -1,
|
||||
state: {
|
||||
rowSelection,
|
||||
|
||||
14
src/components/data-table/sorting.ts
Normal file
14
src/components/data-table/sorting.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { SortingState } from '@tanstack/react-table';
|
||||
|
||||
export function getServerSortParams(sorting: SortingState) {
|
||||
const activeSort = sorting[0];
|
||||
|
||||
if (!activeSort) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
sort_by: activeSort.id,
|
||||
sort_order: activeSort.desc ? 'desc' : 'asc',
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user