nonebot2/website/src/components/Paginate/index.tsx

108 lines
3.3 KiB
TypeScript
Raw Normal View History

2021-12-31 11:27:37 +08:00
import clsx from "clsx";
2022-01-09 20:27:43 +08:00
import React, { useCallback, useState } 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";
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 {
2022-01-09 20:27:43 +08:00
const [containerElement, setContainerElement] = useState<HTMLElement | null>(
null
);
2022-01-12 11:54:01 +08:00
const ref = useCallback(
(element: HTMLElement | null) => {
setContainerElement(element);
},
[setContainerElement]
);
2022-01-09 20:27:43 +08:00
const maxWidth = useContentWidth(
containerElement?.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-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 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
}