ifixkart-admin/components/ui/TablePagination.tsx

107 lines
3.6 KiB
TypeScript

'use client';
import { useEffect, useMemo, useState } from 'react';
import { ChevronLeft, ChevronRight } from 'lucide-react';
export const TABLE_PAGE_SIZE = 10;
export interface TablePagerProps {
page: number;
totalPages: number;
total: number;
pageSize: number;
onPageChange: (page: number) => void;
}
export function useClientPagination<T>(items: T[], pageSize: number = TABLE_PAGE_SIZE) {
const [page, setPage] = useState(1);
const total = items.length;
const totalPages = Math.max(1, Math.ceil(total / pageSize));
const currentPage = Math.min(Math.max(1, page), totalPages);
useEffect(() => {
if (page !== currentPage) setPage(currentPage);
}, [page, currentPage]);
const pagedItems = useMemo(() => {
const start = (currentPage - 1) * pageSize;
return items.slice(start, start + pageSize);
}, [items, currentPage, pageSize]);
return {
items: pagedItems,
page: currentPage,
totalPages,
total,
pageSize,
onPageChange: setPage,
};
}
function pageWindow(current: number, totalPages: number) {
if (totalPages <= 5) return Array.from({ length: totalPages }, (_, i) => i + 1);
if (current <= 3) return [1, 2, 3, 4, 5];
if (current >= totalPages - 2) return [totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1, totalPages];
return [current - 2, current - 1, current, current + 1, current + 2];
}
export function TablePagination({
page,
totalPages,
total,
pageSize,
onPageChange,
}: TablePagerProps & { items?: unknown }) {
if (total === 0) return null;
const from = (page - 1) * pageSize + 1;
const to = Math.min(page * pageSize, total);
const pages = pageWindow(page, totalPages);
const btnClass =
'inline-flex items-center justify-center h-8 min-w-8 px-2.5 crm-radius-control border border-border bg-card text-[12px] font-medium text-foreground hover:bg-muted cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-card';
return (
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 px-3 sm:px-4 py-3 border-t border-border bg-card">
<p className="text-[12px] text-muted-foreground">
Showing <span className="font-semibold text-foreground">{from}</span> to{' '}
<span className="font-semibold text-foreground">{to}</span> of{' '}
<span className="font-semibold text-foreground">{total}</span>
</p>
<div className="flex items-center gap-1 overflow-x-auto max-w-full">
<button
type="button"
aria-label="Previous page"
disabled={page <= 1}
onClick={() => onPageChange(page - 1)}
className={btnClass}
>
<ChevronLeft className="w-3.5 h-3.5" />
<span className="hidden sm:inline">Previous</span>
</button>
{pages.map((n) => (
<button
key={n}
type="button"
aria-label={`Page ${n}`}
aria-current={n === page ? 'page' : undefined}
onClick={() => onPageChange(n)}
className={`${btnClass} ${n === page ? 'bg-primary text-white border-primary hover:bg-primary/90 hover:text-white' : ''}`}
>
{n}
</button>
))}
<button
type="button"
aria-label="Next page"
disabled={page >= totalPages}
onClick={() => onPageChange(page + 1)}
className={btnClass}
>
<span className="hidden sm:inline">Next</span>
<ChevronRight className="w-3.5 h-3.5" />
</button>
</div>
</div>
);
}