ant-design-vue/components/vc-pagination/Pagination.jsx
tangjinzhou 73bef787cd
Feat 1.5.0 (#1853)
* feat: add Result component

* fix: update md template tag html>tpl
- fix `result` typo
- update jest `result` snapshots

* refactor: svg file to functional component icon
- update jest snapshot

* feat: add result

* Feat descriptions (#1251)

* feat: add descriptions

* fix: add descriptions types and fix docs

* fix: lint change code

* fix: demo warning

* fix: update demo, snapshot and remove classnames

* test: add descriptions test

* fix: descriptions demo (#1498)

* feat: add page header (#1250)

* feat: add page-header component

* update site: page-header

* ts definition update: page-header

* get page-header props with getComponentFromProp func

* optimize page-header

* doc: add page-header actions.md responsive.md

* breadcrumb itemRender add pure function support

* style: format code

* feat: update style to 3.23.6 from 2.13.6

* feat: update style to 3.26.8 from 3.23.6

* chore: update util

* chore: update util

* feat: update affix

* feat: update alert

* feat: update anchor

* feat: update auto-complete

* feat: update avatar

* feat: update back-top

* feat: update badge

* feat: update button

* feat: update breadcrumb

* feat: update ts

* docs: update doc

* feat: update calendat

* feat: update card

* feat: update carousel

* feat: update carousel

* feat: update checkbox

* feat: update comment

* feat: update config-provider

* docs: update doc

* feat: update collapse

* feat: update locale

* feat: update date-picker

* feat: update divider

* feat: update drawer

* feat: update dropdown

* feat: update rc-trigger

* feat: update dropdown

* feat: update empty

* test: add empty test

* feat: update form

* feat: update form

* feat: update spin

* feat: update grid

* docs: update grid doc

* feat: update icon

* feat: update slider

* feat: update textarea

* feat: update input-number

* feat: update layout

* feat: update list

* feat: update menu

* feat: meaage add key for update content

* feat: modal add closeIcon support

* feat: update notification

* feat: add pagination disabled support

* feat: popconfirm add disabled support

* test: update popover

* feat: progress support custom line-gradiend

* feat: update radio

* test: update radio test

* docs: update rate demo

* feat: skeleton add avatar support number type

* test: add switch test

* test: update statistic test

* fix: input clear icon event

* feat: steps add type、 v-model、subTitle

* feat: delete typography component

* feat: delete Typography style

* perf: update select

* feat: add download transformFile previewFile actio

* docs: update upload

* feat: update  tree-select

* docs: update tree-select

* feat: tree add blockNode selectable

* docs: add tree demo

* test: update snap

* docs: updatedoc

* feat: update tag

* docs: update ad doc

* feat: update tooltip

* feat: update timeline

* feat: time-picker add clearIcon

* docs: update tabs

* feat: transfer support custom children

* test: update transfer test

* feat: update table

* test: update table test

* test: update test

* feat: calendar update locale

* test: update test snap

* feat: add mentions (#1790)

* feat: mentions style

* feat: theme default

* feat: add mentions component

* feat: mentions API

* feat: add unit test for mentions

* feat: update mentions demo

* perf: model and inheritAttrs for mentions

* perf: use getComponentFromProp instead of this.$props

* perf: mentions rm defaultProps

* feat: rm rows in mentionsProps

* fix: mentions keyDown didn't work

* docs: update mentions api

* perf: mentions code

* feat: update mentions

* bump 1.5.0-alpha.1

* feat: pageheader add ghost prop

* docs: update descriptions demo

* chore: page-header add ghost type

* fix: color error

* feat: update to 3.26.12

* fix: some prop default value

* fix(typo): form, carousel, upload. duplicate identifier (#1848)

* Add Mentions Type (#1845)

* feat: add mentions type

* feat: add mentions in ant-design-vue.d.ts

* docs: update doc

* docs: add changelog

* fix: mentions getPopupCotainer value (#1850)

* docs: update doc

* docs: uptate demo

* docs: update demo

* docs: delete demo

* docs: delete doc

* test: update snapshots

* style: format code

* chore: update travis

* docs: update demo

Co-authored-by: Sendya <18x@loacg.com>
Co-authored-by: zkwolf <chenhao5866@gmail.com>
Co-authored-by: drafish <xwlyy1991@163.com>
Co-authored-by: Amour1688 <31695475+Amour1688@users.noreply.github.com>
2020-03-07 19:45:13 +08:00

612 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import PropTypes from '../_util/vue-types';
import BaseMixin from '../_util/BaseMixin';
import { hasProp, getComponentFromProp, getOptionProps } from '../_util/props-util';
import Pager from './Pager';
import Options from './Options';
import LOCALE from './locale/zh_CN';
import KEYCODE from './KeyCode';
function noop() {}
// 是否是正整数
function isInteger(value) {
return typeof value === 'number' && isFinite(value) && Math.floor(value) === value;
}
function defaultItemRender(page, type, element) {
return element;
}
function calculatePage(p, state, props) {
let pageSize = p;
if (typeof pageSize === 'undefined') {
pageSize = state.statePageSize;
}
return Math.floor((props.total - 1) / pageSize) + 1;
}
export default {
name: 'Pagination',
mixins: [BaseMixin],
model: {
prop: 'current',
event: 'change.current',
},
props: {
disabled: PropTypes.bool,
prefixCls: PropTypes.string.def('rc-pagination'),
selectPrefixCls: PropTypes.string.def('rc-select'),
current: PropTypes.number,
defaultCurrent: PropTypes.number.def(1),
total: PropTypes.number.def(0),
pageSize: PropTypes.number,
defaultPageSize: PropTypes.number.def(10),
hideOnSinglePage: PropTypes.bool.def(false),
showSizeChanger: PropTypes.bool.def(false),
showLessItems: PropTypes.bool.def(false),
// showSizeChange: PropTypes.func.def(noop),
selectComponentClass: PropTypes.any,
showPrevNextJumpers: PropTypes.bool.def(true),
showQuickJumper: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]).def(false),
showTitle: PropTypes.bool.def(true),
pageSizeOptions: PropTypes.arrayOf(PropTypes.string),
buildOptionText: PropTypes.func,
showTotal: PropTypes.func,
simple: PropTypes.bool,
locale: PropTypes.object.def(LOCALE),
itemRender: PropTypes.func.def(defaultItemRender),
prevIcon: PropTypes.any,
nextIcon: PropTypes.any,
jumpPrevIcon: PropTypes.any,
jumpNextIcon: PropTypes.any,
},
data() {
const props = getOptionProps(this);
const hasOnChange = this.onChange !== noop;
const hasCurrent = 'current' in props;
if (hasCurrent && !hasOnChange) {
console.warn(
'Warning: You provided a `current` prop to a Pagination component without an `onChange` handler. This will render a read-only component.',
); // eslint-disable-line
}
let current = this.defaultCurrent;
if ('current' in props) {
current = this.current;
}
let pageSize = this.defaultPageSize;
if ('pageSize' in props) {
pageSize = this.pageSize;
}
current = Math.min(current, calculatePage(pageSize, undefined, props));
return {
stateCurrent: current,
stateCurrentInputValue: current,
statePageSize: pageSize,
};
},
watch: {
current(val) {
this.setState({
stateCurrent: val,
stateCurrentInputValue: val,
});
},
pageSize(val) {
const newState = {};
let current = this.stateCurrent;
const newCurrent = calculatePage(val, this.$data, this.$props);
current = current > newCurrent ? newCurrent : current;
if (!hasProp(this, 'current')) {
newState.stateCurrent = current;
newState.stateCurrentInputValue = current;
}
newState.statePageSize = val;
this.setState(newState);
},
stateCurrent(val, oldValue) {
// When current page change, fix focused style of prev item
// A hacky solution of https://github.com/ant-design/ant-design/issues/8948
this.$nextTick(() => {
if (this.$refs.paginationNode) {
const lastCurrentNode = this.$refs.paginationNode.querySelector(
`.${this.prefixCls}-item-${oldValue}`,
);
if (lastCurrentNode && document.activeElement === lastCurrentNode) {
lastCurrentNode.blur();
}
}
});
},
},
methods: {
getJumpPrevPage() {
return Math.max(1, this.stateCurrent - (this.showLessItems ? 3 : 5));
},
getJumpNextPage() {
return Math.min(
calculatePage(undefined, this.$data, this.$props),
this.stateCurrent + (this.showLessItems ? 3 : 5),
);
},
getItemIcon(icon) {
const { prefixCls } = this.$props;
const iconNode = getComponentFromProp(this, icon, this.$props) || (
<a class={`${prefixCls}-item-link`} />
);
return iconNode;
},
getValidValue(e) {
const inputValue = e.target.value;
const allPages = calculatePage(undefined, this.$data, this.$props);
const { stateCurrentInputValue } = this.$data;
let value;
if (inputValue === '') {
value = inputValue;
} else if (isNaN(Number(inputValue))) {
value = stateCurrentInputValue;
} else if (inputValue >= allPages) {
value = allPages;
} else {
value = Number(inputValue);
}
return value;
},
isValid(page) {
return isInteger(page) && page !== this.stateCurrent;
},
shouldDisplayQuickJumper() {
const { showQuickJumper, pageSize, total } = this.$props;
if (total <= pageSize) {
return false;
}
return showQuickJumper;
},
// calculatePage (p) {
// let pageSize = p
// if (typeof pageSize === 'undefined') {
// pageSize = this.statePageSize
// }
// return Math.floor((this.total - 1) / pageSize) + 1
// },
handleKeyDown(event) {
if (event.keyCode === KEYCODE.ARROW_UP || event.keyCode === KEYCODE.ARROW_DOWN) {
event.preventDefault();
}
},
handleKeyUp(e) {
if (e.target.composing) return;
const value = this.getValidValue(e);
const stateCurrentInputValue = this.stateCurrentInputValue;
if (value !== stateCurrentInputValue) {
this.setState({
stateCurrentInputValue: value,
});
}
if (e.keyCode === KEYCODE.ENTER) {
this.handleChange(value);
} else if (e.keyCode === KEYCODE.ARROW_UP) {
this.handleChange(value - 1);
} else if (e.keyCode === KEYCODE.ARROW_DOWN) {
this.handleChange(value + 1);
}
},
changePageSize(size) {
let current = this.stateCurrent;
const preCurrent = current;
const newCurrent = calculatePage(size, this.$data, this.$props);
current = current > newCurrent ? newCurrent : current;
// fix the issue:
// Once 'total' is 0, 'current' in 'onShowSizeChange' is 0, which is not correct.
if (newCurrent === 0) {
current = this.stateCurrent;
}
if (typeof size === 'number') {
if (!hasProp(this, 'pageSize')) {
this.setState({
statePageSize: size,
});
}
if (!hasProp(this, 'current')) {
this.setState({
stateCurrent: current,
stateCurrentInputValue: current,
});
}
}
this.$emit('update:pageSize', size);
this.$emit('showSizeChange', current, size);
if (current !== preCurrent) {
this.$emit('change.current', current, size);
}
},
handleChange(p) {
const { disabled } = this.$props;
let page = p;
if (this.isValid(page) && !disabled) {
const currentPage = calculatePage(undefined, this.$data, this.$props);
if (page > currentPage) {
page = currentPage;
} else if (page < 1) {
page = 1;
}
if (!hasProp(this, 'current')) {
this.setState({
stateCurrent: page,
stateCurrentInputValue: page,
});
}
// this.$emit('input', page)
this.$emit('change', page, this.statePageSize);
this.$emit('change.current', page, this.statePageSize);
return page;
}
return this.stateCurrent;
},
prev() {
if (this.hasPrev()) {
this.handleChange(this.stateCurrent - 1);
}
},
next() {
if (this.hasNext()) {
this.handleChange(this.stateCurrent + 1);
}
},
jumpPrev() {
this.handleChange(this.getJumpPrevPage());
},
jumpNext() {
this.handleChange(this.getJumpNextPage());
},
hasPrev() {
return this.stateCurrent > 1;
},
hasNext() {
return this.stateCurrent < calculatePage(undefined, this.$data, this.$props);
},
runIfEnter(event, callback, ...restParams) {
if (event.key === 'Enter' || event.charCode === 13) {
callback(...restParams);
}
},
runIfEnterPrev(event) {
this.runIfEnter(event, this.prev);
},
runIfEnterNext(event) {
this.runIfEnter(event, this.next);
},
runIfEnterJumpPrev(event) {
this.runIfEnter(event, this.jumpPrev);
},
runIfEnterJumpNext(event) {
this.runIfEnter(event, this.jumpNext);
},
handleGoTO(event) {
if (event.keyCode === KEYCODE.ENTER || event.type === 'click') {
this.handleChange(this.stateCurrentInputValue);
}
},
},
render() {
const { prefixCls, disabled } = this.$props;
// When hideOnSinglePage is true and there is only 1 page, hide the pager
if (this.hideOnSinglePage === true && this.total <= this.statePageSize) {
return null;
}
const props = this.$props;
const locale = this.locale;
const allPages = calculatePage(undefined, this.$data, this.$props);
const pagerList = [];
let jumpPrev = null;
let jumpNext = null;
let firstPager = null;
let lastPager = null;
let gotoButton = null;
const goButton = this.showQuickJumper && this.showQuickJumper.goButton;
const pageBufferSize = this.showLessItems ? 1 : 2;
const { stateCurrent, statePageSize } = this;
const prevPage = stateCurrent - 1 > 0 ? stateCurrent - 1 : 0;
const nextPage = stateCurrent + 1 < allPages ? stateCurrent + 1 : allPages;
if (this.simple) {
if (goButton) {
if (typeof goButton === 'boolean') {
gotoButton = (
<button type="button" onClick={this.handleGoTO} onKeyup={this.handleGoTO}>
{locale.jump_to_confirm}
</button>
);
} else {
gotoButton = (
<span onClick={this.handleGoTO} onKeyup={this.handleGoTO}>
{goButton}
</span>
);
}
gotoButton = (
<li
title={this.showTitle ? `${locale.jump_to}${this.stateCurrent}/${allPages}` : null}
class={`${prefixCls}-simple-pager`}
>
{gotoButton}
</li>
);
}
const hasPrev = this.hasPrev();
const hasNext = this.hasNext();
return (
<ul class={`${prefixCls} ${prefixCls}-simple`}>
<li
title={this.showTitle ? locale.prev_page : null}
onClick={this.prev}
tabIndex={hasPrev ? 0 : null}
onKeypress={this.runIfEnterPrev}
class={`${hasPrev ? '' : `${prefixCls}-disabled`} ${prefixCls}-prev`}
aria-disabled={!this.hasPrev()}
>
{this.itemRender(prevPage, 'prev', this.getItemIcon('prevIcon'))}
</li>
<li
title={this.showTitle ? `${stateCurrent}/${allPages}` : null}
class={`${prefixCls}-simple-pager`}
>
<input
type="text"
value={this.stateCurrentInputValue}
onKeydown={this.handleKeyDown}
onKeyup={this.handleKeyUp}
onInput={this.handleKeyUp}
size="3"
{...{
directives: [
{
name: 'ant-input',
},
],
}}
/>
<span class={`${prefixCls}-slash`}></span>
{allPages}
</li>
<li
title={this.showTitle ? locale.next_page : null}
onClick={this.next}
tabIndex={this.hasNext ? 0 : null}
onKeypress={this.runIfEnterNext}
class={`${hasNext ? '' : `${prefixCls}-disabled`} ${prefixCls}-next`}
aria-disabled={!this.hasNext()}
>
{this.itemRender(nextPage, 'next', this.getItemIcon('nextIcon'))}
</li>
{gotoButton}
</ul>
);
}
if (allPages <= 5 + pageBufferSize * 2) {
const pagerProps = {
props: {
locale,
rootPrefixCls: prefixCls,
showTitle: props.showTitle,
itemRender: props.itemRender,
},
on: {
click: this.handleChange,
keypress: this.runIfEnter,
},
};
if (!allPages) {
pagerList.push(
<Pager {...pagerProps} key="noPager" page={allPages} class={`${prefixCls}-disabled`} />,
);
}
for (let i = 1; i <= allPages; i++) {
const active = stateCurrent === i;
pagerList.push(<Pager {...pagerProps} key={i} page={i} active={active} />);
}
} else {
const prevItemTitle = this.showLessItems ? locale.prev_3 : locale.prev_5;
const nextItemTitle = this.showLessItems ? locale.next_3 : locale.next_5;
if (this.showPrevNextJumpers) {
let jumpPrevClassString = `${prefixCls}-jump-prev`;
if (props.jumpPrevIcon) {
jumpPrevClassString += ` ${prefixCls}-jump-prev-custom-icon`;
}
jumpPrev = (
<li
title={this.showTitle ? prevItemTitle : null}
key="prev"
onClick={this.jumpPrev}
tabIndex="0"
onKeypress={this.runIfEnterJumpPrev}
class={jumpPrevClassString}
>
{this.itemRender(this.getJumpPrevPage(), 'jump-prev', this.getItemIcon('jumpPrevIcon'))}
</li>
);
let jumpNextClassString = `${prefixCls}-jump-next`;
if (props.jumpNextIcon) {
jumpNextClassString += ` ${prefixCls}-jump-next-custom-icon`;
}
jumpNext = (
<li
title={this.showTitle ? nextItemTitle : null}
key="next"
tabIndex="0"
onClick={this.jumpNext}
onKeypress={this.runIfEnterJumpNext}
class={jumpNextClassString}
>
{this.itemRender(this.getJumpNextPage(), 'jump-next', this.getItemIcon('jumpNextIcon'))}
</li>
);
}
lastPager = (
<Pager
locale={locale}
last
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={allPages}
page={allPages}
active={false}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>
);
firstPager = (
<Pager
locale={locale}
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={1}
page={1}
active={false}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>
);
let left = Math.max(1, stateCurrent - pageBufferSize);
let right = Math.min(stateCurrent + pageBufferSize, allPages);
if (stateCurrent - 1 <= pageBufferSize) {
right = 1 + pageBufferSize * 2;
}
if (allPages - stateCurrent <= pageBufferSize) {
left = allPages - pageBufferSize * 2;
}
for (let i = left; i <= right; i++) {
const active = stateCurrent === i;
pagerList.push(
<Pager
locale={locale}
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={i}
page={i}
active={active}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>,
);
}
if (stateCurrent - 1 >= pageBufferSize * 2 && stateCurrent !== 1 + 2) {
pagerList[0] = (
<Pager
locale={locale}
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={left}
page={left}
class={`${prefixCls}-item-after-jump-prev`}
active={false}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>
);
pagerList.unshift(jumpPrev);
}
if (allPages - stateCurrent >= pageBufferSize * 2 && stateCurrent !== allPages - 2) {
pagerList[pagerList.length - 1] = (
<Pager
locale={locale}
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeypress={this.runIfEnter}
key={right}
page={right}
class={`${prefixCls}-item-before-jump-next`}
active={false}
showTitle={this.showTitle}
itemRender={this.itemRender}
/>
);
pagerList.push(jumpNext);
}
if (left !== 1) {
pagerList.unshift(firstPager);
}
if (right !== allPages) {
pagerList.push(lastPager);
}
}
let totalText = null;
if (this.showTotal) {
totalText = (
<li class={`${prefixCls}-total-text`}>
{this.showTotal(this.total, [
this.total === 0 ? 0 : (stateCurrent - 1) * statePageSize + 1,
stateCurrent * statePageSize > this.total ? this.total : stateCurrent * statePageSize,
])}
</li>
);
}
const prevDisabled = !this.hasPrev() || !allPages;
const nextDisabled = !this.hasNext() || !allPages;
const buildOptionText = this.buildOptionText || this.$scopedSlots.buildOptionText;
return (
<ul
class={{ [`${prefixCls}`]: true, [`${prefixCls}-disabled`]: disabled }}
unselectable="unselectable"
ref="paginationNode"
>
{totalText}
<li
title={this.showTitle ? locale.prev_page : null}
onClick={this.prev}
tabIndex={prevDisabled ? null : 0}
onKeypress={this.runIfEnterPrev}
class={`${!prevDisabled ? '' : `${prefixCls}-disabled`} ${prefixCls}-prev`}
aria-disabled={prevDisabled}
>
{this.itemRender(prevPage, 'prev', this.getItemIcon('prevIcon'))}
</li>
{pagerList}
<li
title={this.showTitle ? locale.next_page : null}
onClick={this.next}
tabIndex={nextDisabled ? null : 0}
onKeypress={this.runIfEnterNext}
class={`${!nextDisabled ? '' : `${prefixCls}-disabled`} ${prefixCls}-next`}
aria-disabled={nextDisabled}
>
{this.itemRender(nextPage, 'next', this.getItemIcon('nextIcon'))}
</li>
<Options
disabled={disabled}
locale={locale}
rootPrefixCls={prefixCls}
selectComponentClass={this.selectComponentClass}
selectPrefixCls={this.selectPrefixCls}
changeSize={this.showSizeChanger ? this.changePageSize : null}
current={stateCurrent}
pageSize={statePageSize}
pageSizeOptions={this.pageSizeOptions}
buildOptionText={buildOptionText || null}
quickGo={this.shouldDisplayQuickJumper() ? this.handleChange : null}
goButton={goButton}
/>
</ul>
);
},
};