UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

471 lines 16.4 kB
import _noop from "lodash/noop"; var __rest = this && this.__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; /* eslint-disable jsx-a11y/no-noninteractive-element-to-interactive-role */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { FixedSizeList as List } from 'react-window'; import PaginationFoundation from '@douyinfe/semi-foundation/lib/es/pagination/foundation'; import { cssClasses, numbers } from '@douyinfe/semi-foundation/lib/es/pagination/constants'; import '@douyinfe/semi-foundation/lib/es/pagination/pagination.css'; import { numbers as popoverNumbers } from '@douyinfe/semi-foundation/lib/es/popover/constants'; import { IconChevronLeft, IconChevronRight } from '@douyinfe/semi-icons'; import warning from '@douyinfe/semi-foundation/lib/es/utils/warning'; import ConfigContext from '../configProvider/context'; import LocaleConsumer from '../locale/localeConsumer'; import Select from '../select/index'; import InputNumber from '../inputNumber/index'; import BaseComponent from '../_base/baseComponent'; import Popover from '../popover/index'; const prefixCls = cssClasses.PREFIX; const { Option } = Select; export default class Pagination extends BaseComponent { constructor(props) { super(props); const total = props.total; const pageSize = props.pageSize || props.pageSizeOpts[0] || numbers.DEFAULT_PAGE_SIZE; // Use pageSize first, use the first of pageSizeOpts when not, use the default value when none const shouldFillAllNumber = props.size === 'small' && props.hoverShowPageSelect && !props.disabled; this.state = { total, showTotal: props.showTotal, currentPage: props.currentPage || props.defaultCurrentPage, pageSize, pageList: [], prevDisabled: false, nextDisabled: false, restLeftPageList: [], restRightPageList: [], quickJumpPage: '', allPageNumbers: shouldFillAllNumber ? Array.from({ length: Math.ceil(total / pageSize) }, (v, i) => i + 1) : [] // only need to count in smallPage mode, when props.size = small }; this.foundation = new PaginationFoundation(this.adapter); this.renderDefaultPage = this.renderDefaultPage.bind(this); this.renderSmallPage = this.renderSmallPage.bind(this); warning(Boolean(props.showSizeChanger && props.hideOnSinglePage), '[Semi Pagination] You should not use showSizeChanger and hideOnSinglePage in ths same time. At this time, hideOnSinglePage no longer takes effect, otherwise there may be a problem that the switch entry disappears'); } get adapter() { return Object.assign(Object.assign({}, super.adapter), { setPageList: pageListState => { const { pageList, restLeftPageList, restRightPageList } = pageListState; this.setState({ pageList, restLeftPageList, restRightPageList }); }, setDisabled: (prevIsDisabled, nextIsDisabled) => { this.setState({ prevDisabled: prevIsDisabled, nextDisabled: nextIsDisabled }); }, updateTotal: total => this.setState({ total }), updatePageSize: pageSize => this.setState({ pageSize }), updateQuickJumpPage: quickJumpPage => this.setState({ quickJumpPage }), updateAllPageNumbers: allPageNumbers => this.setState({ allPageNumbers }), setCurrentPage: pageIndex => { this.setState({ currentPage: pageIndex }); }, registerKeyDownHandler: handler => { document.addEventListener('keydown', handler); }, unregisterKeyDownHandler: handler => { document.removeEventListener('keydown', handler); }, notifyPageChange: pageIndex => { this.props.onPageChange(pageIndex); }, notifyPageSizeChange: pageSize => { this.props.onPageSizeChange(pageSize); }, notifyChange: (pageIndex, pageSize) => { this.props.onChange(pageIndex, pageSize); } }); } componentDidMount() { this.foundation.init(); } componentWillUnmount() { this.foundation.destroy(); } componentDidUpdate(prevProps) { const pagerProps = { currentPage: this.props.currentPage, total: this.props.total, pageSize: this.props.pageSize }; let pagerHasChanged = false; let allPageNumberNeedUpdate = false; if (prevProps.currentPage !== this.props.currentPage) { pagerHasChanged = true; // this.foundation.updatePage(this.props.currentPage); } if (prevProps.total !== this.props.total) { pagerHasChanged = true; allPageNumberNeedUpdate = true; } if (prevProps.pageSize !== this.props.pageSize) { pagerHasChanged = true; allPageNumberNeedUpdate = true; } if (pagerHasChanged) { this.foundation.updatePage(pagerProps.currentPage, pagerProps.total, pagerProps.pageSize); } if (allPageNumberNeedUpdate) { this.foundation.updateAllPageNumbers(pagerProps.total, pagerProps.pageSize); } } renderPrevBtn() { const { prevText, disabled } = this.props; const { prevDisabled } = this.state; const isDisabled = prevDisabled || disabled; const preClassName = classNames({ [`${prefixCls}-item`]: true, [`${prefixCls}-prev`]: true, [`${prefixCls}-item-disabled`]: isDisabled }); return /*#__PURE__*/React.createElement("li", { role: "button", "aria-disabled": isDisabled ? true : false, "aria-label": "Previous", onClick: e => !isDisabled && this.foundation.goPrev(e), className: preClassName, "x-semi-prop": "prevText" }, prevText || /*#__PURE__*/React.createElement(IconChevronLeft, { size: "large" })); } renderNextBtn() { const { nextText, disabled } = this.props; const { nextDisabled } = this.state; const isDisabled = nextDisabled || disabled; const nextClassName = classNames({ [`${prefixCls}-item`]: true, [`${prefixCls}-item-disabled`]: isDisabled, [`${prefixCls}-next`]: true }); return /*#__PURE__*/React.createElement("li", { role: "button", "aria-disabled": isDisabled ? true : false, "aria-label": "Next", onClick: e => !isDisabled && this.foundation.goNext(e), className: nextClassName, "x-semi-prop": "nextText" }, nextText || /*#__PURE__*/React.createElement(IconChevronRight, { size: "large" })); } renderPageSizeSwitch(locale) { // rtl modify the default position const { direction } = this.context; const defaultPopoverPosition = direction === 'rtl' ? 'bottomRight' : 'bottomLeft'; const { showSizeChanger, popoverPosition = defaultPopoverPosition, disabled, popoverZIndex } = this.props; const { pageSize } = this.state; const switchCls = classNames(`${prefixCls}-switch`); if (!showSizeChanger) { return null; } const newPageSizeOpts = this.foundation.pageSizeInOpts(); const pageSizeToken = locale.pageSize; // Display pageSize in a specific language format order const options = newPageSizeOpts.map(size => (/*#__PURE__*/React.createElement(Option, { value: size, key: size }, /*#__PURE__*/React.createElement("span", null, pageSizeToken.replace('${pageSize}', size.toString()))))); return /*#__PURE__*/React.createElement("div", { className: switchCls }, /*#__PURE__*/React.createElement(Select, { "aria-label": "Page size selector", disabled: disabled, onChange: newPageSize => this.foundation.changePageSize(newPageSize), value: pageSize, key: pageSize + pageSizeToken, position: popoverPosition || 'bottomRight', clickToHide: true, zIndex: popoverZIndex, dropdownClassName: `${prefixCls}-select-dropdown` }, options)); } renderQuickJump(locale) { const { showQuickJumper, disabled } = this.props; const { quickJumpPage, total, pageSize } = this.state; if (!showQuickJumper) { return null; } const totalPageNum = this.foundation._getTotalPageNumber(total, pageSize); const isDisabled = totalPageNum === 1 || disabled; const quickJumpCls = classNames({ [`${prefixCls}-quickjump`]: true, [`${prefixCls}-quickjump-disabled`]: isDisabled }); return /*#__PURE__*/React.createElement("div", { className: quickJumpCls }, /*#__PURE__*/React.createElement("span", null, locale.jumpTo), /*#__PURE__*/React.createElement(InputNumber, { value: quickJumpPage, className: `${prefixCls}-quickjump-input-number`, hideButtons: true, disabled: isDisabled, onBlur: e => this.foundation.handleQuickJumpBlur(), onEnterPress: e => this.foundation.handleQuickJumpEnterPress(e.target.value), onChange: v => this.foundation.handleQuickJumpNumberChange(v) }), /*#__PURE__*/React.createElement("span", null, locale.page)); } renderPageList() { const { pageList, currentPage, restLeftPageList, restRightPageList } = this.state; const { popoverPosition, popoverZIndex, disabled } = this.props; return pageList.map((page, i) => { const pageListClassName = classNames(`${prefixCls}-item`, { [`${prefixCls}-item-active`]: currentPage === page, [`${prefixCls}-item-all-disabled`]: disabled, [`${prefixCls}-item-all-disabled-active`]: currentPage === page && disabled // [`${prefixCls}-item-rest-opening`]: (i < 3 && isLeftRestHover && page ==='...') || (i > 3 && isRightRestHover && page === '...') }); const pageEl = /*#__PURE__*/React.createElement("li", { key: `${page}${i}`, onClick: () => !disabled && this.foundation.goPage(page, i), className: pageListClassName, "aria-label": page === '...' ? 'More' : `Page ${page}`, "aria-current": currentPage === page ? "page" : false }, page); if (page === '...' && !disabled) { let content; i < 3 ? content = restLeftPageList : content = restRightPageList; return /*#__PURE__*/React.createElement(Popover, { rePosKey: this.props.currentPage, trigger: "hover", // onVisibleChange={visible=>this.handleRestHover(visible, i < 3 ? 'left' : 'right')} content: this.renderRestPageList(content), key: `${page}${i}`, position: popoverPosition, zIndex: popoverZIndex }, pageEl); } return pageEl; }); } renderRestPageList(restList) { // The number of pages may be tens of thousands, here is virtualized with the help of react-window const { direction } = this.context; const className = classNames(`${prefixCls}-rest-item`); const count = restList.length; const row = item => { const { index, style } = item; const page = restList[index]; return /*#__PURE__*/React.createElement("div", { role: "listitem", key: `${page}${index}`, className: className, onClick: () => this.foundation.goPage(page, index), style: style, "aria-label": `${page}` }, page); }; const itemHeight = 32; const listHeight = count >= 5 ? itemHeight * 5 : itemHeight * count; return ( /*#__PURE__*/ // @ts-ignore skip type check cause react-window not update with @types/react 18 React.createElement(List, { className: `${prefixCls}-rest-list`, itemData: restList, itemSize: itemHeight, width: 78, itemCount: count, height: listHeight, style: { direction } }, row) ); } renderSmallPageSelect(content) { const allPageNumbers = this.state.allPageNumbers; const pageList = this.renderRestPageList(allPageNumbers); return /*#__PURE__*/React.createElement(Popover, { content: pageList }, content); } renderSmallPage(locale) { const _a = this.props, { className, style, hideOnSinglePage, hoverShowPageSelect, showSizeChanger, disabled } = _a, rest = __rest(_a, ["className", "style", "hideOnSinglePage", "hoverShowPageSelect", "showSizeChanger", "disabled"]); const paginationCls = classNames(`${prefixCls}-small`, prefixCls, className, { [`${prefixCls}-disabled`]: disabled }); const { currentPage, total, pageSize } = this.state; const totalPageNum = Math.ceil(total / pageSize); if (totalPageNum < 2 && hideOnSinglePage && !showSizeChanger) { return null; } const pageCls = classNames({ [`${prefixCls}-item`]: true, [`${prefixCls}-item-small`]: true, [`${prefixCls}-item-all-disabled`]: disabled }); const content = /*#__PURE__*/React.createElement("div", { className: pageCls }, currentPage, "/", totalPageNum, " "); return /*#__PURE__*/React.createElement("div", Object.assign({ className: paginationCls, style: style }, this.getDataAttr(rest)), this.renderPrevBtn(), hoverShowPageSelect && !disabled ? this.renderSmallPageSelect(content) : content, this.renderNextBtn(), this.renderQuickJump(locale)); } renderDefaultPage(locale) { const { total, pageSize } = this.state; const _a = this.props, { showTotal, className, style, hideOnSinglePage, showSizeChanger, disabled } = _a, rest = __rest(_a, ["showTotal", "className", "style", "hideOnSinglePage", "showSizeChanger", "disabled"]); const paginationCls = classNames(className, `${prefixCls}`, { [`${prefixCls}-disabled`]: disabled }); const showTotalCls = `${prefixCls}-total`; const totalPageNum = Math.ceil(total / pageSize); if (totalPageNum < 2 && hideOnSinglePage && !showSizeChanger) { return null; } const totalNum = Math.ceil(total / pageSize); const totalToken = locale.total.replace('${total}', totalNum.toString()); return /*#__PURE__*/React.createElement("ul", Object.assign({ className: paginationCls, style: style }, this.getDataAttr(rest)), showTotal ? (/*#__PURE__*/React.createElement("span", { className: showTotalCls }, totalToken)) : null, this.renderPrevBtn(), this.renderPageList(), this.renderNextBtn(), this.renderPageSizeSwitch(locale), this.renderQuickJump(locale)); } render() { const { size } = this.props; return /*#__PURE__*/React.createElement(LocaleConsumer, { componentName: "Pagination" }, locale => size === 'small' ? this.renderSmallPage(locale) : this.renderDefaultPage(locale)); } } Pagination.contextType = ConfigContext; Pagination.propTypes = { total: PropTypes.number, showTotal: PropTypes.bool, pageSize: PropTypes.number, pageSizeOpts: PropTypes.array, size: PropTypes.string, currentPage: PropTypes.number, defaultCurrentPage: PropTypes.number, onPageChange: PropTypes.func, onPageSizeChange: PropTypes.func, onChange: PropTypes.func, prevText: PropTypes.node, nextText: PropTypes.node, showSizeChanger: PropTypes.bool, popoverZIndex: PropTypes.number, popoverPosition: PropTypes.string, style: PropTypes.object, className: PropTypes.string, hideOnSinglePage: PropTypes.bool, hoverShowPageSelect: PropTypes.bool, showQuickJumper: PropTypes.bool, disabled: PropTypes.bool }; Pagination.defaultProps = { total: 1, popoverZIndex: popoverNumbers.DEFAULT_Z_INDEX, showTotal: false, pageSize: null, pageSizeOpts: numbers.PAGE_SIZE_OPTION, defaultCurrentPage: 1, size: 'default', onPageChange: _noop, onPageSizeChange: _noop, onChange: _noop, showSizeChanger: false, className: '', hideOnSinglePage: false, showQuickJumper: false, disabled: false };