diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-24 18:48:57 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:15:24 +0100 |
| commit | 3f8ae3f558446aba3870e90c899db25ad9321499 (patch) | |
| tree | 30824d02705337309d9223f8c5a6bd8fc41d482c /src/components/molecules/nav/pagination.tsx | |
| parent | 98044be08600daf6bd7c7e1a4adada319dbcbbaf (diff) | |
refactor(components): rewrite Pagination component
Diffstat (limited to 'src/components/molecules/nav/pagination.tsx')
| -rw-r--r-- | src/components/molecules/nav/pagination.tsx | 216 |
1 files changed, 0 insertions, 216 deletions
diff --git a/src/components/molecules/nav/pagination.tsx b/src/components/molecules/nav/pagination.tsx deleted file mode 100644 index 73517c3..0000000 --- a/src/components/molecules/nav/pagination.tsx +++ /dev/null @@ -1,216 +0,0 @@ -/* eslint-disable max-statements */ -import { type FC, Fragment, type ReactNode } from 'react'; -import { useIntl } from 'react-intl'; -import { ButtonLink, List, ListItem } from '../../atoms'; -import styles from './pagination.module.scss'; - -export type PaginationProps = { - /** - * An accessible name for the pagination. - */ - 'aria-label'?: string; - /** - * The url part before page number. Default: /page/ - */ - baseUrl?: string; - /** - * Set additional classnames to the pagination wrapper. - */ - className?: string; - /** - * The current page number. - */ - current: number; - /** - * The number of items per page. - */ - perPage: number; - /** - * The number of siblings on one side of the current page. Default: 1. - */ - siblings?: number; - /** - * The total number of items. - */ - total: number; -}; - -/** - * Pagination component - * - * Render a page-based navigation. - */ -export const Pagination: FC<PaginationProps> = ({ - baseUrl = '/page/', - className = '', - current, - perPage, - siblings = 2, - total, - ...props -}) => { - const intl = useIntl(); - const totalPages = Math.round(total / perPage); - const hasPreviousPage = current > 1; - const previousPageName = intl.formatMessage( - { - defaultMessage: '{icon} Previous page', - description: 'Pagination: previous page link', - id: 'aMFqPH', - }, - { icon: '←' } - ); - const previousPageUrl = `${baseUrl}${current - 1}`; - const hasNextPage = current < totalPages; - const nextPageName = intl.formatMessage( - { - defaultMessage: 'Next page {icon}', - description: 'Pagination: Next page link', - id: 'R4yaW6', - }, - { icon: '→' } - ); - const nextPageUrl = `${baseUrl}${current + 1}`; - - /** - * Create an array with a range of values from start value to end value. - * - * @param {number} start - The first value. - * @param {number} end - The last value. - * @returns {number[]} An array from start value to end value. - */ - const range = (start: number, end: number): number[] => - Array.from({ length: end - start + 1 }, (_, index) => index + start); - - /** - * Get the pagination range. - * - * @param currentPage - The current page number. - * @param maxPages - The total pages number. - * @returns {(number|string)[]} An array of page numbers with or without dots. - */ - const getPaginationRange = ( - currentPage: number, - maxPages: number - ): (number | string)[] => { - const dots = '\u2026'; - - /** - * Show left dots if current page less left siblings is greater than the - * first two pages. - */ - const hasLeftDots = currentPage - siblings > 2; - - /** - * Show right dots if current page plus right siblings is lower than the - * total of pages less the last page. - */ - const hasRightDots = currentPage + siblings < maxPages - 1; - - if (hasLeftDots && hasRightDots) { - const middleItems = range(currentPage - siblings, currentPage + siblings); - return [1, dots, ...middleItems, dots, maxPages]; - } - - if (hasLeftDots) { - const rightItems = range(currentPage - siblings, maxPages); - return [1, dots, ...rightItems]; - } - - if (hasRightDots) { - const leftItems = range(1, currentPage + siblings); - return [...leftItems, dots, maxPages]; - } - - return range(1, maxPages); - }; - - /** - * Get a link or a span wrapped in a list item. - * - * @param {string} id - The item id. - * @param {ReactNode} body - The link body. - * @param {string} [link] - An URL. - * @returns {JSX.Element} The list item. - */ - const getItem = (id: string, body: ReactNode, link?: string): JSX.Element => { - const linkModifier = id.startsWith('page') ? 'link--number' : ''; - const kind = id === 'previous' || id === 'next' ? 'tertiary' : 'secondary'; - const linkClass = `${styles.link} ${styles[linkModifier]}`; - const disabledLinkClass = `${styles.link} ${styles['link--disabled']}`; - - return ( - <ListItem className={styles.item}> - {link ? ( - <ButtonLink className={linkClass} kind={kind} to={link}> - {body} - </ButtonLink> - ) : ( - <span className={disabledLinkClass}>{body}</span> - )} - </ListItem> - ); - }; - - /** - * Get the list of pages. - * - * @param {number} currentPage - The current page number. - * @param {number} maxPages - The total of pages. - * @returns {JSX.Element[]} The list items. - */ - const getPages = (currentPage: number, maxPages: number): JSX.Element[] => { - const pagesRange = getPaginationRange(currentPage, maxPages); - - return pagesRange.map((page, index) => { - const id = typeof page === 'string' ? `dots-${index}` : `page-${page}`; - const currentPagePrefix = intl.formatMessage({ - defaultMessage: 'You are here:', - description: 'Pagination: current page indication', - id: 'yE/Jdz', - }); - const body = - typeof page === 'string' - ? page // dots - : intl.formatMessage( - { - defaultMessage: '<a11y>Page </a11y>{number}', - description: 'Pagination: page number', - id: 'TSXPzr', - }, - { - number: page, - a11y: (chunks: ReactNode) => ( - // eslint-disable-next-line react/jsx-no-literals - <span className="screen-reader-text"> - {page === currentPage && currentPagePrefix} - {chunks} - </span> - ), - } - ); - const url = - page === currentPage || typeof page === 'string' - ? undefined - : `${baseUrl}${page}`; - - return <Fragment key={id}>{getItem(id, body, url)}</Fragment>; - }); - }; - const navClass = `${styles.wrapper} ${className}`; - const listClass = `${styles.list} ${styles['list--pages']}`; - - return ( - <nav {...props} className={navClass}> - <List className={listClass} hideMarker isInline spacing="2xs"> - {getPages(current, totalPages)} - </List> - <List className={styles.list} hideMarker isInline spacing="xs"> - {hasPreviousPage - ? getItem('previous', previousPageName, previousPageUrl) - : null} - {hasNextPage ? getItem('next', nextPageName, nextPageUrl) : null} - </List> - </nav> - ); -}; |
