'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import PageView from './PageView';
import BreakView from './BreakView';
import { classNameIfDefined } from './utils';
export default class PaginationBoxView extends Component {
static propTypes = {
pageCount: PropTypes.number.isRequired,
pageRangeDisplayed: PropTypes.number,
marginPagesDisplayed: PropTypes.number,
previousLabel: PropTypes.node,
previousAriaLabel: PropTypes.string,
prevPageRel: PropTypes.string,
prevRel: PropTypes.string,
nextLabel: PropTypes.node,
nextAriaLabel: PropTypes.string,
nextPageRel: PropTypes.string,
nextRel: PropTypes.string,
breakLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
hrefBuilder: PropTypes.func,
hrefAllControls: PropTypes.bool,
onPageChange: PropTypes.func,
onPageActive: PropTypes.func,
onClick: PropTypes.func,
initialPage: PropTypes.number,
forcePage: PropTypes.number,
disableInitialCallback: PropTypes.bool,
containerClassName: PropTypes.string,
className: PropTypes.string,
pageClassName: PropTypes.string,
pageLinkClassName: PropTypes.string,
pageLabelBuilder: PropTypes.func,
activeClassName: PropTypes.string,
activeLinkClassName: PropTypes.string,
previousClassName: PropTypes.string,
nextClassName: PropTypes.string,
previousLinkClassName: PropTypes.string,
nextLinkClassName: PropTypes.string,
disabledClassName: PropTypes.string,
disabledLinkClassName: PropTypes.string,
breakClassName: PropTypes.string,
breakLinkClassName: PropTypes.string,
extraAriaContext: PropTypes.string,
ariaLabelBuilder: PropTypes.func,
eventListener: PropTypes.string,
renderOnZeroPageCount: PropTypes.func,
selectedPageRel: PropTypes.string,
};
static defaultProps = {
pageRangeDisplayed: 2,
marginPagesDisplayed: 3,
activeClassName: 'selected',
previousLabel: 'Previous',
previousClassName: 'previous',
previousAriaLabel: 'Previous page',
prevPageRel: 'prev',
prevRel: 'prev',
nextLabel: 'Next',
nextClassName: 'next',
nextAriaLabel: 'Next page',
nextPageRel: 'next',
nextRel: 'next',
breakLabel: '...',
disabledClassName: 'disabled',
disableInitialCallback: false,
pageLabelBuilder: (page) => page,
eventListener: 'onClick',
renderOnZeroPageCount: undefined,
selectedPageRel: 'canonical',
hrefAllControls: false,
};
constructor(props) {
super(props);
if (props.initialPage !== undefined && props.forcePage !== undefined) {
console.warn(
`(react-paginate): Both initialPage (${props.initialPage}) and forcePage (${props.forcePage}) props are provided, which is discouraged.` +
' Use exclusively forcePage prop for a controlled component.\nSee https://reactjs.org/docs/forms.html#controlled-components'
);
}
let initialSelected;
if (props.initialPage) {
initialSelected = props.initialPage;
} else if (props.forcePage) {
initialSelected = props.forcePage;
} else {
initialSelected = 0;
}
this.state = {
selected: initialSelected,
};
}
componentDidMount() {
const {
initialPage,
disableInitialCallback,
extraAriaContext,
pageCount,
forcePage,
} = this.props;
// Call the callback with the initialPage item:
if (typeof initialPage !== 'undefined' && !disableInitialCallback) {
this.callCallback(initialPage);
}
if (extraAriaContext) {
console.warn(
'DEPRECATED (react-paginate): The extraAriaContext prop is deprecated. You should now use the ariaLabelBuilder instead.'
);
}
if (!Number.isInteger(pageCount)) {
console.warn(
`(react-paginate): The pageCount prop value provided is not an integer (${pageCount}). Did you forget a Math.ceil()?`
);
}
if (initialPage !== undefined && initialPage > pageCount - 1) {
console.warn(
`(react-paginate): The initialPage prop provided is greater than the maximum page index from pageCount prop (${initialPage} > ${
pageCount - 1
}).`
);
}
if (forcePage !== undefined && forcePage > pageCount - 1) {
console.warn(
`(react-paginate): The forcePage prop provided is greater than the maximum page index from pageCount prop (${forcePage} > ${
pageCount - 1
}).`
);
}
}
componentDidUpdate(prevProps) {
if (
this.props.forcePage !== undefined &&
this.props.forcePage !== prevProps.forcePage
) {
if (this.props.forcePage > this.props.pageCount - 1) {
console.warn(
`(react-paginate): The forcePage prop provided is greater than the maximum page index from pageCount prop (${
this.props.forcePage
} > ${this.props.pageCount - 1}).`
);
}
this.setState({ selected: this.props.forcePage });
}
if (
Number.isInteger(prevProps.pageCount) &&
!Number.isInteger(this.props.pageCount)
) {
console.warn(
`(react-paginate): The pageCount prop value provided is not an integer (${this.props.pageCount}). Did you forget a Math.ceil()?`
);
}
}
handlePreviousPage = (event) => {
const { selected } = this.state;
this.handleClick(event, null, selected > 0 ? selected - 1 : undefined, {
isPrevious: true,
});
};
handleNextPage = (event) => {
const { selected } = this.state;
const { pageCount } = this.props;
this.handleClick(
event,
null,
selected < pageCount - 1 ? selected + 1 : undefined,
{ isNext: true }
);
};
handlePageSelected = (selected, event) => {
if (this.state.selected === selected) {
this.callActiveCallback(selected);
this.handleClick(event, null, undefined, { isActive: true });
return;
}
this.handleClick(event, null, selected);
};
handlePageChange = (selected) => {
if (this.state.selected === selected) {
return;
}
this.setState({ selected });
// Call the callback with the new selected item:
this.callCallback(selected);
};
getEventListener = (handlerFunction) => {
const { eventListener } = this.props;
return {
[eventListener]: handlerFunction,
};
};
getForwardJump() {
const { selected } = this.state;
const { pageCount, pageRangeDisplayed } = this.props;
const forwardJump = selected + pageRangeDisplayed;
return forwardJump >= pageCount ? pageCount - 1 : forwardJump;
}
getBackwardJump() {
const { selected } = this.state;
const { pageRangeDisplayed } = this.props;
const backwardJump = selected - pageRangeDisplayed;
return backwardJump < 0 ? 0 : backwardJump;
}
handleClick = (
event,
index,
nextSelectedPage,
{
isPrevious = false,
isNext = false,
isBreak = false,
isActive = false,
} = {}
) => {
event.preventDefault ? event.preventDefault() : (event.returnValue = false);
const { selected } = this.state;
const { onClick } = this.props;
let newPage = nextSelectedPage;
if (onClick) {
const onClickReturn = onClick({
index,
selected,
nextSelectedPage,
event,
isPrevious,
isNext,
isBreak,
isActive,
});
if (onClickReturn === false) {
// We abord standard behavior and let parent handle
// all behavior.
return;
}
if (Number.isInteger(onClickReturn)) {
// We assume parent want to go to the returned page.
newPage = onClickReturn;
}
}
if (newPage !== undefined) {
this.handlePageChange(newPage);
}
};
handleBreakClick = (index, event) => {
const { selected } = this.state;
this.handleClick(
event,
index,
selected < index ? this.getForwardJump() : this.getBackwardJump(),
{ isBreak: true }
);
};
getElementHref(pageIndex) {
const { hrefBuilder, pageCount, hrefAllControls } = this.props;
if (!hrefBuilder) return;
if (hrefAllControls || (pageIndex >= 0 && pageIndex < pageCount)) {
return hrefBuilder(pageIndex + 1, pageCount, this.state.selected);
}
}
ariaLabelBuilder(pageIndex) {
const selected = pageIndex === this.state.selected;
if (
this.props.ariaLabelBuilder &&
pageIndex >= 0 &&
pageIndex < this.props.pageCount
) {
let label = this.props.ariaLabelBuilder(pageIndex + 1, selected);
// DEPRECATED: The extraAriaContext prop was used to add additional context
// to the aria-label. Users should now use the ariaLabelBuilder instead.
if (this.props.extraAriaContext && !selected) {
label = label + ' ' + this.props.extraAriaContext;
}
return label;
}
}
callCallback = (selectedItem) => {
if (
this.props.onPageChange !== undefined &&
typeof this.props.onPageChange === 'function'
) {
this.props.onPageChange({ selected: selectedItem });
}
};
callActiveCallback = (selectedItem) => {
if (
this.props.onPageActive !== undefined &&
typeof this.props.onPageActive === 'function'
) {
this.props.onPageActive({ selected: selectedItem });
}
};
getElementPageRel = (index) => {
const { selected } = this.state;
const { nextPageRel, prevPageRel, selectedPageRel } = this.props;
if (selected - 1 === index) {
return prevPageRel;
} else if (selected === index) {
return selectedPageRel;
} else if (selected + 1 === index) {
return nextPageRel;
}
return undefined;
};
getPageElement(index) {
const { selected } = this.state;
const {
pageClassName,
pageLinkClassName,
activeClassName,
activeLinkClassName,
extraAriaContext,
pageLabelBuilder,
} = this.props;
return (