import { ArrowPathIcon } from "@heroicons/react/24/solid"; import clsx from "clsx"; import { useEffect, useState } from "react"; import { Pagination as ShadCnPagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "../ui/pagination"; type PaginationItemWrapperProps = { /** * Whether a page is currently loading. */ isLoadingPage: boolean; /** * The children to render. */ children: React.ReactNode; }; function PaginationItemWrapper({ isLoadingPage, children, }: PaginationItemWrapperProps) { return ( {children} ); } type Props = { /** * If true, the pagination will be rendered as a mobile-friendly pagination. */ mobilePagination: boolean; /** * The current page. */ page: number; /** * The total number of pages. */ totalPages: number; /** * The page to show a loading icon on. */ loadingPage: number | undefined; /** * Callback function that is called when the user clicks on a page number. */ onPageChange: (page: number) => void; }; export default function Pagination({ mobilePagination, page, totalPages, loadingPage, onPageChange, }: Props) { totalPages = Math.round(totalPages); const isLoading = loadingPage !== undefined; const [currentPage, setCurrentPage] = useState(page); useEffect(() => { setCurrentPage(page); }, [page]); const handlePageChange = (newPage: number) => { if ( newPage < 1 || newPage > totalPages || newPage == currentPage || isLoading ) { return; } setCurrentPage(newPage); onPageChange(newPage); }; const renderPageNumbers = () => { const pageNumbers = []; const maxPagesToShow = mobilePagination ? 3 : 4; let startPage = Math.max(1, currentPage - Math.floor(maxPagesToShow / 2)); const endPage = Math.min(totalPages, startPage + maxPagesToShow - 1); if (endPage - startPage < maxPagesToShow - 1) { startPage = Math.max(1, endPage - maxPagesToShow + 1); } // Show "Jump to Start" with Ellipsis if currentPage is greater than 3 in desktop view if (startPage > 1 && !mobilePagination) { pageNumbers.push( <> handlePageChange(1)}> 1 {/* Only show ellipsis if more than 2 pages from the start */} {startPage > 2 && ( )} , ); } // Generate page numbers between startPage and endPage for desktop view for (let i = startPage; i <= endPage; i++) { pageNumbers.push( handlePageChange(i)} > {loadingPage === i ? ( ) : ( i )} , ); } return pageNumbers; }; return ( {/* Previous button for mobile and desktop */} handlePageChange(currentPage - 1)} /> {renderPageNumbers()} {/* For desktop, show ellipsis and link to the last page */} {!mobilePagination && currentPage < totalPages && totalPages - currentPage > 2 && ( <> handlePageChange(totalPages)}> {totalPages} )} {/* Next button for mobile and desktop */} handlePageChange(currentPage + 1)} /> ); }