import { ChevronRightIcon } from "@heroicons/react/24/outline"; import { ChevronLeftIcon } from "@heroicons/react/24/solid"; import { Link, useLocation } from "@remix-run/react"; import { cn } from "~/utils/cn"; import { LinkButton } from "./Buttons"; /** Pass `hasNextPage` when the total page count is unknown; use `showPageNumbers={false}` in that case. */ export function PaginationControls({ currentPage, totalPages, hasNextPage, showPageNumbers = true, }: { currentPage: number; totalPages: number; /** When set, navigation uses this instead of `totalPages`. */ hasNextPage?: boolean; showPageNumbers?: boolean; }) { const location = useLocation(); const hasNextMode = hasNextPage !== undefined; const showPagination = hasNextMode ? currentPage > 1 || hasNextPage : totalPages > 1; const nextDisabled = hasNextMode ? !hasNextPage : currentPage === totalPages; if (!showPagination) { return null; } return ( ); } function pageUrl(location: ReturnType, page: number): string { const search = new URLSearchParams(location.search); search.set("page", String(page)); return location.pathname + "?" + search.toString(); } const baseClass = "flex items-center justify-center border border-transparent min-w-6 h-6 px-1 text-xs font-medium transition text-text-dimmed rounded-sm focus-visible:focus-custom"; const unselectedClass = "hover:bg-tertiary hover:text-text-bright"; const selectedClass = "border-border-bright bg-tertiary text-text-bright hover:bg-surface-control/50"; function PageLinkComponent({ page, location, }: { page: PageLink; location: ReturnType; }) { if (page.type === "specific") { return ( {page.page} ); } else { return ...; } } type PageLink = EllipsisPageLink | SpecificPageLink; type EllipsisPageLink = { type: "ellipses"; }; type SpecificPageLink = { type: "specific"; page: number; isCurrent: boolean; }; // If there are less than or equal to 6 pages, just show all the pages. // If there are more than 5 pages, show the first 3, the current page, and the last 3. function calculatePageLinks(currentPage: number, totalPages: number): Array { const pageLinks: Array = []; if (totalPages <= 10) { for (let i = 1; i <= totalPages; i++) { pageLinks.push({ type: "specific", page: i, isCurrent: i === currentPage, }); } } else { if (currentPage <= 3) { for (let i = 1; i <= 4; i++) { pageLinks.push({ type: "specific", page: i, isCurrent: i === currentPage, }); } pageLinks.push({ type: "ellipses", }); for (let i = totalPages - 3; i <= totalPages; i++) { pageLinks.push({ type: "specific", page: i, isCurrent: i === currentPage, }); } } else if (currentPage >= totalPages - 3) { for (let i = 1; i <= 3; i++) { pageLinks.push({ type: "specific", page: i, isCurrent: i === currentPage, }); } pageLinks.push({ type: "ellipses", }); for (let i = totalPages - 4; i <= totalPages; i++) { pageLinks.push({ type: "specific", page: i, isCurrent: i === currentPage, }); } } else { for (let i = 1; i <= 3; i++) { pageLinks.push({ type: "specific", page: i, isCurrent: i === currentPage, }); } pageLinks.push({ type: "ellipses", }); for (let i = currentPage - 1; i <= currentPage + 1; i++) { pageLinks.push({ type: "specific", page: i, isCurrent: i === currentPage, }); } pageLinks.push({ type: "ellipses", }); for (let i = totalPages - 2; i <= totalPages; i++) { pageLinks.push({ type: "specific", page: i, isCurrent: i === currentPage, }); } } } return pageLinks; }