mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
160 lines
4.1 KiB
TypeScript
160 lines
4.1 KiB
TypeScript
import React from 'react';
|
|
import { clsx } from 'clsx';
|
|
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
|
|
import Button from './Button';
|
|
|
|
export interface PaginationProps {
|
|
currentPage: number;
|
|
totalPages: number;
|
|
onPageChange: (page: number) => void;
|
|
pageSize?: number;
|
|
totalItems?: number;
|
|
showPageNumbers?: boolean;
|
|
showFirstLast?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* Pagination component
|
|
*/
|
|
export const Pagination = ({
|
|
currentPage,
|
|
totalPages,
|
|
onPageChange,
|
|
pageSize,
|
|
totalItems,
|
|
showPageNumbers = true,
|
|
showFirstLast = true,
|
|
className,
|
|
}: PaginationProps) => {
|
|
const getPageNumbers = () => {
|
|
const delta = 2;
|
|
const range = [];
|
|
const rangeWithDots = [];
|
|
|
|
for (
|
|
let i = Math.max(2, currentPage - delta);
|
|
i <= Math.min(totalPages - 1, currentPage + delta);
|
|
i++
|
|
) {
|
|
range.push(i);
|
|
}
|
|
|
|
if (currentPage - delta > 2) {
|
|
rangeWithDots.push(1, '...');
|
|
} else {
|
|
rangeWithDots.push(1);
|
|
}
|
|
|
|
rangeWithDots.push(...range);
|
|
|
|
if (currentPage + delta < totalPages - 1) {
|
|
rangeWithDots.push('...', totalPages);
|
|
} else {
|
|
rangeWithDots.push(totalPages);
|
|
}
|
|
|
|
return rangeWithDots;
|
|
};
|
|
|
|
const pageNumbers = getPageNumbers();
|
|
|
|
if (totalPages <= 1) return null;
|
|
|
|
return (
|
|
<div className={clsx('flex items-center justify-between gap-2', className)}>
|
|
{/* Info */}
|
|
{totalItems !== undefined && pageSize && (
|
|
<div className="text-sm text-muted-foreground hidden sm:block">
|
|
Showing {(currentPage - 1) * pageSize + 1} to{' '}
|
|
{Math.min(currentPage * pageSize, totalItems)} of {totalItems} results
|
|
</div>
|
|
)}
|
|
|
|
{/* Pagination Controls */}
|
|
<div className="flex items-center gap-1">
|
|
{showFirstLast && (
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => onPageChange(1)}
|
|
disabled={currentPage === 1}
|
|
aria-label="First page"
|
|
>
|
|
<ChevronsLeft className="h-4 w-4" />
|
|
</Button>
|
|
)}
|
|
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => onPageChange(currentPage - 1)}
|
|
disabled={currentPage === 1}
|
|
aria-label="Previous page"
|
|
>
|
|
<ChevronLeft className="h-4 w-4" />
|
|
</Button>
|
|
|
|
{showPageNumbers && (
|
|
<div className="flex items-center gap-1">
|
|
{pageNumbers.map((page, index) => {
|
|
if (page === '...') {
|
|
return (
|
|
<span
|
|
key={`ellipsis-${index}`}
|
|
className="px-3 py-2 text-sm text-muted-foreground"
|
|
>
|
|
...
|
|
</span>
|
|
);
|
|
}
|
|
|
|
const pageNum = page as number;
|
|
const isActive = pageNum === currentPage;
|
|
|
|
return (
|
|
<Button
|
|
key={pageNum}
|
|
variant={isActive ? 'primary' : 'outline'}
|
|
size="sm"
|
|
onClick={() => onPageChange(pageNum)}
|
|
className={clsx('min-w-[2.5rem]', {
|
|
'pointer-events-none': isActive,
|
|
})}
|
|
aria-label={`Page ${pageNum}`}
|
|
aria-current={isActive ? 'page' : undefined}
|
|
>
|
|
{pageNum}
|
|
</Button>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => onPageChange(currentPage + 1)}
|
|
disabled={currentPage === totalPages}
|
|
aria-label="Next page"
|
|
>
|
|
<ChevronRight className="h-4 w-4" />
|
|
</Button>
|
|
|
|
{showFirstLast && (
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => onPageChange(totalPages)}
|
|
disabled={currentPage === totalPages}
|
|
aria-label="Last page"
|
|
>
|
|
<ChevronsRight className="h-4 w-4" />
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|