import { MouseEvent, useCallback, useEffect, useState } from 'react'; import { PaginationProps } from '../types'; import { twMerge } from 'tailwind-merge'; import clsx from 'clsx'; import { Link, Button, LoaderSpinner } from '.'; import usePagination from '@akinon/next/hooks/use-pagination'; import { useLocalization } from '@akinon/next/hooks'; import { useRouter } from '@akinon/next/hooks'; import { useInView } from 'react-intersection-observer'; export const Pagination = (props: PaginationProps) => { const { t } = useLocalization(); const router = useRouter(); const { total, limit, currentPage, numberOfPages, containerClassName, prevClassName, pageClassName, nextClassName, moreButtonClassName, threshold = 1, type = 'list', onPageChange, direction, render, isLoading } = props; const pagination = usePagination(total, limit, currentPage, numberOfPages); const { total: paginationTotal, limit: paginationLimit, page, pageList, prev, next, last, setTotal, setLimit } = pagination; const [paginationItems, setPaginationItems] = useState([]); const showNext = currentPage * paginationLimit < paginationTotal; const { ref, inView } = useInView({ threshold: 0.75 }); const [prevPage, setPrevPage] = useState(page); const [nextPage, setNextPage] = useState(page); const createListItems = useCallback(() => { setPaginationItems([]); const delta = 2; const startPage = Math.max(Number(page) - delta, 1); const endPage = Math.min(Number(page) + delta, numberOfPages); setPaginationItems((prev) => [ ...prev, { page: pageList[0].page, url: pageList[0].url } ]); if (delta < startPage) { setPaginationItems((prev) => [...prev, { page: '...', url: '#' }]); } for (let i = startPage; i <= endPage; i++) { if (i === 1) continue; setPaginationItems((prev) => [ ...prev, { page: pageList[i - 1]?.page, url: pageList[i - 1]?.url } ]); } if (endPage < numberOfPages - threshold) { setPaginationItems((prev) => [...prev, { page: '...', url: '#' }]); } if (page < numberOfPages - delta) { setPaginationItems((prev) => [ ...prev, { page: pageList[pageList.length - threshold].page, url: pageList[pageList.length - threshold].url } ]); } }, [numberOfPages, page, pageList, threshold]); const handleClick = (e: MouseEvent, url: string) => { e.preventDefault(); const newUrl = new URL(url, window.location.origin); const page = newUrl.searchParams.get('page'); if (page === '1') { newUrl.searchParams.delete('page'); } router.push(newUrl.pathname + newUrl.search, undefined); }; const handlePageChange = () => { let changingPage; if (direction === 'prev') { changingPage = Number(type !== 'list' ? prevPage : page) - 1; setPrevPage(changingPage); } else { changingPage = Number(type !== 'list' ? nextPage : page) + 1; setNextPage(changingPage); } onPageChange(changingPage); }; useEffect(() => { if (type === 'infinite' && page === 1) { setPrevPage(1); setNextPage(1); } }, [page]); useEffect(() => { if (inView) { handlePageChange(); } }, [inView]); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { if (type === 'list') { createListItems(); } }, [page]); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { if (total && total !== paginationTotal) { setTotal(total); } }, [total, paginationTotal, setTotal]); useEffect(() => { if (limit && limit !== paginationLimit) { setLimit(limit); } }, [limit, paginationLimit, setLimit]); if (render) { return <>{render(pagination)}; } return direction === 'prev' && type !== 'list' ? ( <>
) : ( <> {type === 'more' && (
)} {type === 'infinite' && Number(nextPage) !== last && (
)} {(type === 'infinite' || type === 'more') && Number(nextPage) === last && !isLoading && (

{t('category.pagination.shown_items')}

)} {type === 'list' && ( )} ); };