devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
7.22 kB
import React from 'react';
import { Button } from '@/components/ui/button';
import { CustomDropdown } from './CustomDropdown';
import {
ChevronLeft,
ChevronRight,
ChevronsLeft,
ChevronsRight
} from 'lucide-react';
interface PaginationProps {
currentPage: number;
totalPages: number;
totalItems: number;
itemsPerPage: number;
onPageChange: (page: number) => void;
onItemsPerPageChange: (itemsPerPage: number) => void;
className?: string;
}
const ITEMS_PER_PAGE_OPTIONS = [10, 25, 50, 100];
const Pagination: React.FC<PaginationProps> = ({
currentPage,
totalPages,
totalItems,
itemsPerPage,
onPageChange,
onItemsPerPageChange,
className = ''
}) => {
const startItem = (currentPage - 1) * itemsPerPage + 1;
const endItem = Math.min(currentPage * itemsPerPage, totalItems);
const handlePageChange = (page: number) => {
if (page >= 1 && page <= totalPages) {
onPageChange(page);
}
};
// Generate page numbers to show
const getPageNumbers = () => {
const pages: (number | string)[] = [];
const maxVisiblePages = 5;
if (totalPages <= maxVisiblePages) {
// Show all pages if total is small
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
// Show first page
pages.push(1);
if (currentPage > 3) {
pages.push('...');
}
// Show pages around current page
const start = Math.max(2, currentPage - 1);
const end = Math.min(totalPages - 1, currentPage + 1);
for (let i = start; i <= end; i++) {
pages.push(i);
}
if (currentPage < totalPages - 2) {
pages.push('...');
}
// Show last page
if (totalPages > 1) {
pages.push(totalPages);
}
}
return pages;
};
if (totalItems === 0) {
return null;
}
return (
<nav
className={`flex flex-col sm:flex-row items-center justify-between gap-4 ${className}`}
role="navigation"
aria-label="Pagination Navigation"
>
{/* Items per page selector */}
<div className="flex items-center gap-2 text-sm">
<label htmlFor="items-per-page" className="text-muted-foreground">Show</label>
<CustomDropdown
options={ITEMS_PER_PAGE_OPTIONS.map(option => ({
value: option.toString(),
label: option.toString()
}))}
value={itemsPerPage.toString()}
onChange={(value) => onItemsPerPageChange(parseInt(value))}
className="w-20"
triggerClassName="h-8"
aria-label={`Items per page, currently ${itemsPerPage}`}
/>
<span className="text-muted-foreground">per page</span>
</div>
{/* Page info */}
<div className="text-sm text-muted-foreground" aria-live="polite" aria-atomic="true">
Showing {startItem} to {endItem} of {totalItems} items
</div>
{/* Page navigation */}
<div className="flex items-center gap-1" role="group" aria-label="Pagination controls">
{/* First page */}
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(1)}
disabled={currentPage === 1}
className="h-8 w-8 p-0"
aria-label="Go to first page"
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (currentPage !== 1) {
handlePageChange(1);
}
}
}}
tabIndex={0}
role="button"
>
<ChevronsLeft className="h-4 w-4" aria-hidden="true" />
<span className="sr-only">First page</span>
</Button>
{/* Previous page */}
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
className="h-8 w-8 p-0"
aria-label="Go to previous page"
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (currentPage > 1) {
handlePageChange(currentPage - 1);
}
}
}}
tabIndex={0}
role="button"
>
<ChevronLeft className="h-4 w-4" aria-hidden="true" />
<span className="sr-only">Previous page</span>
</Button>
{/* Page numbers */}
{getPageNumbers().map((page, index) => (
<React.Fragment key={`page-${index}`}>
{typeof page === 'number' ? (
<Button
variant={page === currentPage ? 'default' : 'outline'}
size="sm"
onClick={() => handlePageChange(page)}
className="h-8 w-8 p-0"
aria-label={`Go to page ${page}`}
aria-current={page === currentPage ? 'page' : undefined}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handlePageChange(page);
}
}}
tabIndex={0}
role="button"
>
{page}
</Button>
) : (
<span className="px-2 text-muted-foreground" aria-hidden="true">...</span>
)}
</React.Fragment>
))}
{/* Next page */}
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
className="h-8 w-8 p-0"
aria-label="Go to next page"
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (currentPage < totalPages) {
handlePageChange(currentPage + 1);
}
}
}}
tabIndex={0}
role="button"
>
<ChevronRight className="h-4 w-4" aria-hidden="true" />
<span className="sr-only">Next page</span>
</Button>
{/* Last page */}
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(totalPages)}
disabled={currentPage === totalPages}
className="h-8 w-8 p-0"
aria-label="Go to last page"
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (currentPage !== totalPages) {
handlePageChange(totalPages);
}
}
}}
tabIndex={0}
role="button"
>
<ChevronsRight className="h-4 w-4" aria-hidden="true" />
<span className="sr-only">Last page</span>
</Button>
</div>
</nav>
);
};
export default Pagination;