@spaced-out/ui-design-system
Version:
Sense UI components library
159 lines (141 loc) • 4.38 kB
Flow
// @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>
);
},
);