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)} />
);
}