import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/solid"; import { useCallback, useState } from "react"; import Button from "../button/button"; import IconButton from "../icon-button/icon-button"; import Typography from "../typography/typography"; interface UsePaginationProps { count?: number; defaultPage?: number; page?: number; onChange?: (page: number) => void; boundaryCount?: number; siblingCount?: number; } interface UsePaginationReturnType { items: { page?: number; type: "page" | "start-ellipsis" | "end-ellipsis"; selected?: boolean; goToThisPage?: () => void; }[]; currentPage: number; goToNextPage: () => void; goToPrevPage: () => void; goToFirstPage: () => void; goToLastPage: () => void; goToPage: (page: number) => void; prevPageExists: boolean; nextPageExists: boolean; } /* eslint-disable-next-line */ interface PaginationProps extends UsePaginationProps { shape?: React.ComponentProps["shape"]; } const range = (start: number, stop: number, step = 1) => Array(Math.ceil((stop - start) / step)) .fill(start) .map((x, y) => x + y * step); export function usePagination({ boundaryCount = 1, count = 1, defaultPage = 1, onChange, page, siblingCount = 1, }: UsePaginationProps): UsePaginationReturnType { const [internalPageIndex, setInternalPageIndex] = useState(defaultPage); const pageIndex = page ?? internalPageIndex; const goToPage = onChange ?? setInternalPageIndex; const initialPageIndex = 0; const finalPageIndex = count - 1; const prevPageExists = pageIndex > initialPageIndex; const nextPageExists = pageIndex < finalPageIndex; const prevPageIndex = prevPageExists ? pageIndex - 1 : pageIndex; const nextPageIndex = nextPageExists ? pageIndex + 1 : pageIndex; const goToNextPage = useCallback( () => goToPage(nextPageIndex), [goToPage, nextPageIndex] ); const goToPrevPage = useCallback( () => goToPage(prevPageIndex), [goToPage, prevPageIndex] ); const goToFirstPage = useCallback( () => goToPage(initialPageIndex), [goToPage, initialPageIndex] ); const goToLastPage = useCallback( () => goToPage(finalPageIndex), [goToPage, finalPageIndex] ); const createItem = useCallback( (page: number) => ({ page, type: "page", selected: page === pageIndex, goToThisPage: () => goToPage(page), }), [goToPage, pageIndex] ); let items = []; const minLength = 2 * boundaryCount + 2 * siblingCount + 3; if (count <= minLength) { items = range(initialPageIndex, initialPageIndex + count).map( createItem ); } else { const startingArea = initialPageIndex + boundaryCount + siblingCount + 1; const endingArea = finalPageIndex - boundaryCount - siblingCount - 1; const isIndexNearStarting = pageIndex >= initialPageIndex && pageIndex <= startingArea; const isIndexNearEnding = pageIndex <= finalPageIndex && pageIndex >= endingArea; if (isIndexNearStarting) { items = [ ...range(initialPageIndex, startingArea + siblingCount + 1).map( createItem ), { type: "end-ellipsis" }, ...range( finalPageIndex - boundaryCount + 1, finalPageIndex + 1 ).map(createItem), ]; } else if (isIndexNearEnding) { items = [ ...range( initialPageIndex, initialPageIndex + boundaryCount ).map(createItem), { type: "start-ellipsis" }, ...range(endingArea - siblingCount, finalPageIndex + 1).map( createItem ), ]; } else { items = [ ...range( initialPageIndex, initialPageIndex + boundaryCount ).map(createItem), { type: "start-ellipsis" }, ...range( pageIndex - siblingCount, pageIndex + siblingCount + 1 ).map(createItem), { type: "end-ellipsis" }, ...range( finalPageIndex - boundaryCount + 1, finalPageIndex + 1 ).map(createItem), ]; } } return { items, currentPage: pageIndex, goToFirstPage, goToLastPage, goToNextPage, goToPage, goToPrevPage, prevPageExists, nextPageExists, } as UsePaginationReturnType; } export function Pagination({ boundaryCount = 1, count = 1, defaultPage = 0, onChange, page, siblingCount = 1, shape = "square", }: PaginationProps) { const { items, prevPageExists, nextPageExists, currentPage, goToNextPage, goToPrevPage, } = usePagination({ page, onChange, count, boundaryCount, defaultPage, siblingCount, }); return (
Page {currentPage + 1} of {count}
); } export default Pagination;