aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/Pagination/Pagination.tsx
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-03-09 00:38:02 +0100
committerGitHub <noreply@github.com>2022-03-09 00:38:02 +0100
commit5b6639a3cf9b6c63045cb82e6ef1a43b0742c367 (patch)
tree4e7cebf9f6b094d405e96febe743fea514cfca9f /src/components/Pagination/Pagination.tsx
parentb0d9d8cb1c8c4a4d2b9234bbfdc7195fb563b21a (diff)
feat: provide pagination for users with js disabled (#13)
* chore: add a Pagination component * chore: add blog pages * chore: fallback to page number based navigation if JS disabled * chore: update translation
Diffstat (limited to 'src/components/Pagination/Pagination.tsx')
-rw-r--r--src/components/Pagination/Pagination.tsx131
1 files changed, 131 insertions, 0 deletions
diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx
new file mode 100644
index 0000000..2c24a8c
--- /dev/null
+++ b/src/components/Pagination/Pagination.tsx
@@ -0,0 +1,131 @@
+import { settings } from '@utils/config';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+import { useIntl } from 'react-intl';
+import styles from './Pagination.module.scss';
+
+const Pagination = ({ baseUrl, total }: { baseUrl: string; total: number }) => {
+ const intl = useIntl();
+ const { asPath } = useRouter();
+ const totalPages = Math.floor(total / settings.postsPerPage);
+ const currentPage = asPath.includes('/page/')
+ ? Number(asPath.split(`${baseUrl}/page/`)[1])
+ : 1;
+ const hasPreviousPage = currentPage !== 1;
+ const hasNextPage = currentPage !== totalPages;
+
+ const getPreviousPageItem = () => {
+ return (
+ <li className={styles.item}>
+ <Link href={`${baseUrl}/page/${currentPage - 1}`}>
+ <a className={styles.link}>
+ {intl.formatMessage(
+ {
+ defaultMessage: '{icon} Previous page',
+ description: 'Pagination: previous page link',
+ },
+ { icon: '←' }
+ )}
+ </a>
+ </Link>
+ </li>
+ );
+ };
+
+ const getNextPageItem = () => {
+ return (
+ <li className={styles.item}>
+ <Link href={`${baseUrl}/page/${currentPage + 1}`}>
+ <a className={styles.link}>
+ {intl.formatMessage(
+ {
+ defaultMessage: 'Next page {icon}',
+ description: 'Pagination: Next page link',
+ },
+ { icon: '→' }
+ )}
+ </a>
+ </Link>
+ </li>
+ );
+ };
+
+ const getPages = () => {
+ const pages = [];
+ for (let i = 1; i <= totalPages; i++) {
+ if (i === currentPage) {
+ pages.push({
+ id: `page-${i}`,
+ link: (
+ <span className={`${styles.link} ${styles['link--current']}`}>
+ {intl.formatMessage(
+ {
+ defaultMessage: '<a11y>Page </a11y>{number}',
+ description: 'Pagination: page number',
+ },
+ {
+ number: i,
+ a11y: (chunks: string) => (
+ <span className="screen-reader-text">{chunks}</span>
+ ),
+ }
+ )}
+ </span>
+ ),
+ });
+ } else {
+ pages.push({
+ id: `page-${i}`,
+ link: (
+ <Link href={`${baseUrl}/page/${i}`}>
+ <a className={styles.link}>
+ {intl.formatMessage(
+ {
+ defaultMessage: '<a11y>Page </a11y>{number}',
+ description: 'Pagination: page number',
+ },
+ {
+ number: i,
+ a11y: (chunks: string) => (
+ <span className="screen-reader-text">{chunks}</span>
+ ),
+ }
+ )}
+ </a>
+ </Link>
+ ),
+ });
+ }
+ }
+
+ return pages;
+ };
+
+ const getItems = () => {
+ const pages = getPages();
+
+ return pages.map((page) => (
+ <li key={page.id} className={styles.item}>
+ {page.link}
+ </li>
+ ));
+ };
+
+ return (
+ <nav className={styles.wrapper} aria-labelledby="pagination-title">
+ <h2 id="pagination-title" className="screen-reader-text">
+ {intl.formatMessage({
+ defaultMessage: 'Pagination',
+ description: 'Pagination: pagination title',
+ })}
+ </h2>
+ <ul className={styles.list}>
+ {hasPreviousPage && getPreviousPageItem()}
+ {getItems()}
+ {hasNextPage && getNextPageItem()}
+ </ul>
+ </nav>
+ );
+};
+
+export default Pagination;