Files
DABEEO-DETECTION-APPLICATION/web-app/app/shared/components/pagination/Pagination.tsx

122 lines
4.4 KiB
TypeScript

import { useCallback, useMemo } from 'react';
import { Button as AriaButton } from 'react-aria-components';
type PaginationProps = {
totalPages: number;
currentPage: number;
pageCount?: number;
onPageChange: (page: number) => void;
};
// https://design-system.w3.org/components/pagination.html
export const Pagination = ({ totalPages, currentPage, pageCount = 10, onPageChange }: PaginationProps) => {
const start = useMemo(() => {
return Math.floor(currentPage / pageCount) * pageCount;
}, [currentPage, pageCount]);
const handlePageClick = useCallback(
(page: number) => () => {
if (typeof onPageChange === 'function' && page >= 0 && page < totalPages) {
onPageChange(page);
}
},
[totalPages, onPageChange],
);
const pageArray = useMemo(() => {
const arr: number[] = [];
// totalPage가 0이면 아무것도 안나와서 최소 1이 되도록 수정
const _totalPages = totalPages > 0 ? totalPages : 1;
if (Number.isNaN(start)) {
return arr;
}
for (let i = 0; i < pageCount; i++) {
const pageNumber = start + i;
if (pageNumber >= _totalPages) {
continue;
}
arr.push(pageNumber);
}
return arr;
}, [start, pageCount, totalPages]);
const noPrev = start === 0;
const noNext = start + pageCount >= totalPages;
return (
<nav className="text-dabeeo-gray-99 text-sm" aria-label="pagination">
<ul className="flex items-center justify-center list-none">
<li className="flex">
<AriaButton
className="cursor-pointer data-[disabled=true]:cursor-default"
isDisabled={noPrev}
aria-label="첫 페이지"
onClick={handlePageClick(0)}
>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="15.8571" y="15" width="1.5" height="10" fill="currentColor" />
<path d="M24.1429 15.3571L19.1429 19.8571L24.1429 24.3571" stroke="currentColor" strokeWidth="1.5" />
</svg>
</AriaButton>
</li>
<li className="mr-2.5 flex">
<AriaButton
className="cursor-pointer data-[disabled=true]:cursor-default"
isDisabled={noPrev}
aria-label="이전 페이지"
onClick={handlePageClick(start - 1)}
>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.5 15.5L17.5 20L22.5 24.5" stroke="currentColor" strokeWidth="1.5" />
</svg>
</AriaButton>
</li>
{pageArray.map((pageNumber) => (
<li key={pageNumber}>
<AriaButton
isDisabled={currentPage === pageNumber}
className="w-10 h-10 flex items-center justify-center tabular-nums not-disabled:cursor-pointer aria-[current=page]:text-sm aria-[current=page]:text-primary aria-[current=page]:font-bold"
onClick={handlePageClick(pageNumber)}
aria-current={currentPage === pageNumber ? 'page' : undefined}
>
{pageNumber + 1}
</AriaButton>
</li>
))}
<li className="ml-2.5 flex">
<AriaButton
isDisabled={noNext}
className="cursor-pointer data-[disabled=true]:cursor-default"
// className="text-[#CCCCCC]"
aria-label="다음 페이지"
onClick={handlePageClick(start + pageCount)}
>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.5 15.5L22.5 20L17.5 24.5" stroke="currentColor" strokeWidth="1.5" />
</svg>
</AriaButton>
</li>
<li className="flex">
<AriaButton
isDisabled={noNext}
className="cursor-pointer data-[disabled=true]:cursor-default"
// className="text-[#CCCCCC]"
aria-label="마지막 페이지"
onClick={handlePageClick(totalPages - 1)}
>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.75 15.5L20.75 20L15.75 24.5" stroke="currentColor" strokeWidth="1.5" />
<path d="M24.25 15H22.75V25H24.25V15Z" fill="currentColor" />
</svg>
</AriaButton>
</li>
</ul>
</nav>
);
};