2021-12-31 11:27:37 +08:00
|
|
|
import clsx from "clsx";
|
2021-12-31 02:06:20 +08:00
|
|
|
import React, { useCallback, useRef } from "react";
|
2021-12-30 12:50:30 +08:00
|
|
|
import { usePagination } from "react-use-pagination";
|
2021-12-29 21:09:31 +08:00
|
|
|
|
2021-12-30 12:50:30 +08:00
|
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
|
|
|
2021-12-31 02:06:20 +08:00
|
|
|
import { useContentWidth } from "../../libs/width";
|
2021-12-30 16:05:05 +08:00
|
|
|
import styles from "./styles.module.css";
|
|
|
|
|
2021-12-30 12:50:30 +08:00
|
|
|
export default function Paginate({
|
|
|
|
totalPages,
|
2021-12-31 11:27:37 +08:00
|
|
|
setPreviousPage,
|
|
|
|
setNextPage,
|
2021-12-30 12:50:30 +08:00
|
|
|
setPage,
|
2021-12-30 22:46:15 +08:00
|
|
|
currentPage,
|
2021-12-31 11:27:37 +08:00
|
|
|
previousEnabled,
|
|
|
|
nextEnabled,
|
2021-12-30 12:50:30 +08:00
|
|
|
}: ReturnType<typeof usePagination>): JSX.Element {
|
2021-12-31 02:06:20 +08:00
|
|
|
const ref = useRef<HTMLElement>();
|
|
|
|
const maxWidth = useContentWidth(ref.current?.parentElement ?? undefined);
|
2021-12-31 11:27:37 +08:00
|
|
|
const maxLength = Math.min(
|
|
|
|
(maxWidth && Math.floor(maxWidth / 50) - 2) || totalPages,
|
|
|
|
totalPages
|
|
|
|
);
|
2021-12-31 02:06:20 +08:00
|
|
|
|
2021-12-30 12:50:30 +08:00
|
|
|
const onPageChange = useCallback(
|
|
|
|
(selectedItem: { selected: number }) => {
|
|
|
|
setPage(selectedItem.selected);
|
|
|
|
},
|
|
|
|
[setPage]
|
|
|
|
);
|
2021-12-31 11:27:37 +08:00
|
|
|
const range = useCallback((start: number, end: number) => {
|
|
|
|
const result = [];
|
|
|
|
start = start > 0 ? start : 1;
|
|
|
|
for (let i = start; i <= end; i++) {
|
|
|
|
result.push(i);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}, []);
|
2021-12-30 12:50:30 +08:00
|
|
|
|
2021-12-31 02:06:20 +08:00
|
|
|
// FIXME: responsive width
|
2021-12-31 11:27:37 +08:00
|
|
|
const pages: (React.ReactNode | number)[] = [];
|
|
|
|
const ellipsis = <FontAwesomeIcon icon="ellipsis-h" />;
|
|
|
|
|
|
|
|
const even = maxLength % 2 === 0 ? 1 : 0;
|
|
|
|
const left = Math.floor(maxLength / 2);
|
|
|
|
const right = totalPages - left + even + 1;
|
|
|
|
currentPage = currentPage + 1;
|
|
|
|
|
|
|
|
if (totalPages <= maxLength) {
|
|
|
|
pages.push(...range(1, totalPages));
|
|
|
|
} else if (currentPage > left && currentPage < right) {
|
|
|
|
const firstItem = 1;
|
|
|
|
const lastItem = totalPages;
|
|
|
|
const start = currentPage - left + 2;
|
|
|
|
const end = currentPage + left - 2 - even;
|
|
|
|
const secondItem = start - 1 === firstItem + 1 ? 2 : ellipsis;
|
|
|
|
const beforeLastItem = end + 1 === lastItem - 1 ? end + 1 : ellipsis;
|
|
|
|
|
|
|
|
pages.push(1, secondItem, ...range(start, end), beforeLastItem, totalPages);
|
|
|
|
} else if (currentPage === left) {
|
|
|
|
const end = currentPage + left - 1 - even;
|
|
|
|
pages.push(...range(1, end), ellipsis, totalPages);
|
|
|
|
} else if (currentPage === right) {
|
|
|
|
const start = currentPage - left + 1;
|
|
|
|
pages.push(1, ellipsis, ...range(start, totalPages));
|
|
|
|
} else {
|
|
|
|
pages.push(...range(1, left), ellipsis, ...range(right, totalPages));
|
|
|
|
}
|
|
|
|
|
2021-12-30 12:50:30 +08:00
|
|
|
return (
|
2021-12-31 02:06:20 +08:00
|
|
|
<nav role="navigation" aria-label="Pagination Navigation" ref={ref}>
|
2021-12-31 11:27:37 +08:00
|
|
|
<ul className={styles.container}>
|
|
|
|
<li
|
|
|
|
className={clsx(styles.li, { [styles.disabled]: !previousEnabled })}
|
|
|
|
>
|
|
|
|
<button className={styles.button} onClick={setPreviousPage}>
|
|
|
|
<FontAwesomeIcon icon="chevron-left" />
|
|
|
|
</button>
|
|
|
|
</li>
|
|
|
|
{pages.map((page, index) => (
|
|
|
|
<li className={styles.li} key={index}>
|
|
|
|
<button
|
|
|
|
className={clsx(styles.button, {
|
|
|
|
[styles.active]: page === currentPage,
|
|
|
|
"pointer-events-none": typeof page !== "number",
|
|
|
|
})}
|
|
|
|
onClick={() => typeof page === "number" && setPage(page - 1)}
|
|
|
|
>
|
|
|
|
{page}
|
|
|
|
</button>
|
|
|
|
</li>
|
|
|
|
))}
|
|
|
|
<li className={clsx(styles.li, { [styles.disabled]: !nextEnabled })}>
|
|
|
|
<button className={styles.button} onClick={setNextPage}>
|
|
|
|
<FontAwesomeIcon icon="chevron-right" />
|
|
|
|
</button>
|
|
|
|
</li>
|
|
|
|
</ul>
|
2021-12-30 12:50:30 +08:00
|
|
|
</nav>
|
|
|
|
);
|
2021-12-29 21:09:31 +08:00
|
|
|
}
|