124 lines
3.7 KiB
TypeScript
124 lines
3.7 KiB
TypeScript
'use client';
|
|
|
|
import { Table } from '@tanstack/react-table';
|
|
import {
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
ChevronsLeft,
|
|
ChevronsRight,
|
|
} from 'lucide-react';
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@/components/ui/select';
|
|
|
|
const PAGE_SIZE_OPTIONS = [10, 20, 40, 50, 100];
|
|
|
|
export function TableFooter<TData>({
|
|
table,
|
|
totalItems,
|
|
pageSize,
|
|
onPageSizeChange,
|
|
}: {
|
|
table: Table<TData>;
|
|
totalItems: number;
|
|
pageSize?: number;
|
|
onPageSizeChange?: (pageSize: number) => void;
|
|
}) {
|
|
const currentPageSize = pageSize ?? table.getState().pagination.pageSize;
|
|
const selectedRowCount = Object.keys(table.getState().rowSelection).length;
|
|
const pageIndex = table.getState().pagination.pageIndex;
|
|
const rawPageCount = table.getPageCount();
|
|
const pageCount = Math.max(
|
|
1,
|
|
Number.isFinite(rawPageCount) && rawPageCount > 0 ? rawPageCount : 1,
|
|
);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-3 px-4 text-sm text-muted-foreground sm:flex-row sm:items-center sm:justify-between">
|
|
<div>
|
|
{selectedRowCount} of {totalItems} row(s) selected.
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center gap-4 sm:justify-end">
|
|
{onPageSizeChange ? (
|
|
<div className="flex items-center gap-2 text-foreground">
|
|
<span className="font-medium">Rows per page</span>
|
|
<Select
|
|
value={String(currentPageSize)}
|
|
onValueChange={(value) => table.setPageSize(Number(value))}
|
|
>
|
|
<SelectTrigger className="h-8 w-20 border-border bg-background shadow-none">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent align="end">
|
|
{PAGE_SIZE_OPTIONS.map((size) => (
|
|
<SelectItem key={size} value={String(size)}>
|
|
{size}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
) : null}
|
|
|
|
<div className="min-w-24 text-foreground font-medium">
|
|
Page {pageIndex + 1} of {pageCount}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon-sm"
|
|
className="size-8 shadow-none"
|
|
onClick={() => table.setPageIndex(0)}
|
|
disabled={!table.getCanPreviousPage()}
|
|
aria-label="Go to first page"
|
|
>
|
|
<ChevronsLeft className="size-4" />
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon-sm"
|
|
className="size-8 shadow-none"
|
|
onClick={() => table.previousPage()}
|
|
disabled={!table.getCanPreviousPage()}
|
|
aria-label="Go to previous page"
|
|
>
|
|
<ChevronLeft className="size-4" />
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon-sm"
|
|
className="size-8 shadow-none"
|
|
onClick={() => table.nextPage()}
|
|
disabled={!table.getCanNextPage()}
|
|
aria-label="Go to next page"
|
|
>
|
|
<ChevronRight className="size-4" />
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon-sm"
|
|
className="size-8 shadow-none"
|
|
onClick={() => table.setPageIndex(pageCount - 1)}
|
|
disabled={!table.getCanNextPage()}
|
|
aria-label="Go to last page"
|
|
>
|
|
<ChevronsRight className="size-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|