import React, { useState, useMemo } from 'react'; import { cn } from '../../utils/cn'; import { motion } from 'framer-motion'; import { paginationControlsVariants } from '../../motions/motion'; export interface TablePaginationProps { /** * Current page number (1-based) */ currentPage: number; /** * Number of items per page */ pageSize: number; /** * Total number of records */ totalRecords: number; /** * Page size options */ pageSizeOptions?: number[]; /** * Whether to show page size dropdown */ showPageSizeOptions?: boolean; /** * Whether to show jump to page input */ showJumpToPage?: boolean; /** * Whether to show total records count */ showTotalRecords?: boolean; /** * Number of pages to show in pagination */ siblingCount?: number; /** * Handle page change */ onPageChange: (page: number) => void; /** * Handle page size change */ onPageSizeChange?: (pageSize: number) => void; /** * Visual variant of the pagination * @default 'default' */ variant?: 'default' | 'compact' | 'simple' | 'bordered'; /** * Custom className */ className?: string; } /** * TablePagination component - Provides pagination controls for the table */ export const TablePagination = ({ currentPage, pageSize, totalRecords, pageSizeOptions = [10, 20, 50, 100], showPageSizeOptions = true, showJumpToPage = false, showTotalRecords = true, siblingCount = 1, onPageChange, onPageSizeChange, variant = 'default', className }: TablePaginationProps) => { // Local state for jump to page input const [jumpToPageValue, setJumpToPageValue] = useState(''); // Calculate total pages const totalPages = Math.max(1, Math.ceil(totalRecords / pageSize)); // Generate page numbers to display based on current page and sibling count const pageNumbers = useMemo(() => { const totalPageNumbers = siblingCount * 2 + 3; // siblings on both sides + first + current + last if (totalPageNumbers >= totalPages) { return Array.from({ length: totalPages }, (_, i) => i + 1); } const leftSiblingIndex = Math.max(currentPage - siblingCount, 1); const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages); const shouldShowLeftDots = leftSiblingIndex > 2; const shouldShowRightDots = rightSiblingIndex < totalPages - 1; if (!shouldShowLeftDots && shouldShowRightDots) { const leftItemCount = 1 + 2 * siblingCount; return [ ...Array.from({ length: leftItemCount }, (_, i) => i + 1), 'dots', totalPages ]; } if (shouldShowLeftDots && !shouldShowRightDots) { const rightItemCount = 1 + 2 * siblingCount; return [ 1, 'dots', ...Array.from( { length: rightItemCount }, (_, i) => totalPages - rightItemCount + i + 1 ) ]; } return [ 1, 'dots', ...Array.from( { length: rightSiblingIndex - leftSiblingIndex + 1 }, (_, i) => leftSiblingIndex + i ), 'dots', totalPages ]; }, [currentPage, totalPages, siblingCount]); // Handle page change const handlePageChange = (page: number) => { if (page < 1 || page > totalPages || page === currentPage) return; onPageChange(page); }; // Handle page size change const handlePageSizeChange = (e: React.ChangeEvent) => { const newSize = parseInt(e.target.value, 10); if (onPageSizeChange) { onPageSizeChange(newSize); } }; // Handle jump to page const handleJumpToPage = () => { const page = parseInt(jumpToPageValue, 10); if (!isNaN(page) && page >= 1 && page <= totalPages) { onPageChange(page); setJumpToPageValue(''); } }; // Handle jump to page input change const handleJumpToPageInput = (e: React.ChangeEvent) => { setJumpToPageValue(e.target.value); }; // Handle jump to page key press const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { handleJumpToPage(); } }; // Calculate the range of records shown const startRecord = totalRecords === 0 ? 0 : (currentPage - 1) * pageSize + 1; const endRecord = Math.min(currentPage * pageSize, totalRecords); // Jump to page input with enhanced scrollbar const renderJumpToPage = () => { if (!showJumpToPage) return null; return (
Go to:
); }; return ( {/* Pagination info */} {showTotalRecords && (
{totalRecords > 0 ? `${startRecord}-${endRecord} of ${totalRecords} items` : 'No items' }
)} {/* Pagination controls */}
{/* Page size selector */} {showPageSizeOptions && onPageSizeChange && (
)} {/* Previous button */} {/* Page numbers */} {pageNumbers.map((pageNumber, index) => { if (pageNumber === 'dots') { return ( ); } return ( ); })} {/* Next button */} {/* Jump to page */} {renderJumpToPage()}
); }; export default TablePagination;