UNPKG

@spaced-out/ui-design-system

Version:
159 lines (141 loc) 4.38 kB
// @flow strict import * as React from 'react'; import {usePagination} from '../../hooks/usePagination'; import {useWindowSize} from '../../hooks/useWindowSize'; import classify from '../../utils/classify'; import {range} from '../../utils/helpers'; import {Dropdown} from '../Dropdown'; import {SubTitleExtraSmall} from '../Text'; import {PaginationItem} from './PaginationItem'; import css from './Pagination.module.css'; type ClassNames = $ReadOnly<{ wrapper?: string, children?: string, paginator?: string, }>; type MixedEvent = SyntheticEvent<EventTarget> | Event; type PaginationItemType = | 'first' | 'previous' | 'page' | 'start-ellipsis' | 'end-ellipsis' | 'last' | 'next'; export type PaginationBaseProps = { currentPage?: number, disabled?: boolean, onChange?: (value?: ?number, event?: ?MixedEvent) => void, totalPages?: number, showFirstButton?: boolean, showLastButton?: boolean, hideNextButton?: boolean, hidePrevButton?: boolean, boundaryCount?: number, siblingCount?: number, style?: 'primary' | 'secondary', staticPaginationLabel?: string, }; export type PaginationProps = { children?: React.Node, ...PaginationBaseProps, classNames?: ClassNames, }; export type PaginationItemProps = { id: string, disabled: boolean, onClick: (event: SyntheticEvent<HTMLElement>) => void, page: ?number, selected: boolean, type: PaginationItemType, }; const LARGE_LIST_DROPDOWN_OPTIONS = 50; const TABLET_SCREEN_SIZE = 640; export const Pagination: React$AbstractComponent< PaginationProps, HTMLDivElement, > = React.forwardRef<PaginationProps, HTMLDivElement>( (props: PaginationProps, ref) => { const {width} = useWindowSize(); const showExtraSecPaginationButtons = width > TABLET_SCREEN_SIZE; const { classNames, style = 'primary', children, currentPage = 1, totalPages = 1, onChange, showFirstButton = style !== 'primary' && showExtraSecPaginationButtons, showLastButton = style !== 'primary' && showExtraSecPaginationButtons, staticPaginationLabel, ...restPaginationProps } = props; const showPageSelectionDropDown = style !== 'primary' && showExtraSecPaginationButtons; const {items} = usePagination({ style, showFirstButton, showLastButton, currentPage, totalPages, onChange, ...restPaginationProps, }); const menuVirtualization = { enable: totalPages > LARGE_LIST_DROPDOWN_OPTIONS ? true : false, }; const allPages = range(1, totalPages); const options = style !== 'secondary' ? [] : allPages.map((page) => ({ key: page.toString(), label: page.toString(), })); return ( <div ref={ref} data-testid="Pagination" className={classify(css.wrapper, classNames?.wrapper)} > <div className={classify(css.childrenSlot, classNames?.children)}> {children} </div> <div className={css.paginatorSlots}> {style === 'secondary' && showPageSelectionDropDown && ( <> <Dropdown size="small" classNames={{wrapper: css.dropdownWrapper}} menu={{ options, isFluid: true, classNames: {wrapper: css.menuWrapper}, selectedKeys: currentPage ? [currentPage.toString()] : [], virtualization: menuVirtualization, }} dropdownInputText={currentPage.toString()} onChange={(option) => onChange?.(parseInt(option.key))} /> {!!totalPages && !!currentPage && ( <SubTitleExtraSmall color="tertiary" className={css.secondaryLabel} > {staticPaginationLabel ? staticPaginationLabel : `of ${totalPages} ${totalPages > 1 ? 'Pages' : 'Page'}`} </SubTitleExtraSmall> )} </> )} <div className={classify(css.paginatorSlot, classNames?.paginator)}> {items?.map((item) => ( <PaginationItem key={item.id} {...item} /> ))} </div> </div> </div> ); }, );