import React, { Component, createRef } from 'react';
import PropTypes from 'prop-types';
import setClassName from 'utilities/setClassName';
import styles from 'src/components/pagination/PaginationList.module.scss';
/**
* @namespace Pagination
* @description Default styled pagination component.
* @property {Array} children - Array of objects to make up pagination.
* @property {number|string} results - How many results to show on each page.
* @tutorial `src\stories\Pagination.stories.js`
*/
const getChildren = (props) => props.children.reduce((accumulator, child, index) => {
const current = Math.floor(index / (props.results || 10));
if (accumulator[current]) accumulator[current] = [ accumulator[current], child];
else accumulator[current] = child;
return accumulator;
}, {});
export class PaginationList extends Component {
state = {
active: false,
currentPageNo: 0,
list: getChildren(this.props),
offsetHeight: 0,
userList: createRef(PaginationList)
};
componentDidUpdate(prevProps) {
const { props: { children, watch } } = this;
// Old and new list keys
const oldPropKeys = prevProps.children.map((child) => child.key);
const newPropKeys = children.map((child) => child.key);
// Old and new user watched properties
const oldUserWatch = prevProps.watch;
const newUserWatch = watch;
// If keys changed
if (JSON.stringify(oldPropKeys) !== JSON.stringify(newPropKeys)) { this.updateList(); }
// If properties user wants to watch have changed, update list/state
else if (oldUserWatch !== newUserWatch) { this.updateStateFromPropChild(); }
}
updateList = () => this.setState({
currentPageNo: 0,
list: getChildren(this.props)
});
updateStateFromPropChild = () => this.setState({
list: getChildren(this.props)
});
render() {
// Reference to current page
const {
state: {
active,
currentPageNo,
list,
userList
}
} = this;
const currentPage = list[currentPageNo];
// List provided by user
const UserList = (providedListProps) => {
// Filter out unneeded props before they are passed to the <ul>
const defaultListProps = (() => {
const output = { ...this.props };
delete output.results;
delete output.children;
return output;
})();
return (
<ul { ...defaultListProps } { ...providedListProps } ref={ userList }>
{ currentPage }
</ul>
);
};
// Numbered options to change the page
const PageOptions = () => {
return (
<article>
{
Object.keys(list).map((page, index, array) => {
const handlePageChange = () => {
const { offsetHeight } = userList.current;
if (offsetHeight > 0 && !active) { this.setState({
offsetHeight,
active: true,
currentPageNo: index
}); }
else { this.setState({ currentPageNo: index }); }
};
if (array.length > 1) {
const cleanNumber = (() => {
if (array.length > 999) {
console.warn('PaginationList does not currently format numbers (e.g., adding leading zeros) for lists that contain over 1000 pages of results.');
return false;
}
if (array.length > 9 && index < 9) return `0${index + 1}`;
if (array.length > 99 && index < 9) return `00${index + 1}`;
if (array.length > 99 && index >= 9 && index < 99) return `0${index + 1}`;
return `${index + 1}`;
})();
return (
<span
key={ index }
onClick={ handlePageChange }
data-active={ currentPageNo === index }
role="list"
>
{ cleanNumber }
</span>
);
}
return false;
})
}
</article>
);
};
return (
<section className={ setClassName(this.props, styles['pagination-list']) } page={ currentPageNo + 1 }>
<UserList style={ { minHeight: this.state.offsetHeight } } />
<PageOptions />
</section>
);
}
}
PaginationList.propTypes = {
children: PropTypes.array,
results: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
watch: PropTypes.any
};
PaginationList.defaultProps = {
children: [],
results: '',
watch: null
};