@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
JavaScript
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
};