2016-07-07 23:23:18 +02:00
|
|
|
import React, { Component, PropTypes } from "react";
|
2016-08-01 00:26:52 +02:00
|
|
|
import { Link } from "react-router";
|
2016-07-29 23:57:21 +02:00
|
|
|
import CSSModules from "react-css-modules";
|
2016-07-29 00:50:08 +02:00
|
|
|
import { defineMessages, injectIntl, intlShape, FormattedMessage, FormattedHTMLMessage } from "react-intl";
|
2016-07-07 23:23:18 +02:00
|
|
|
|
2016-07-28 23:14:52 +02:00
|
|
|
import { messagesMap } from "../../utils";
|
|
|
|
import commonMessages from "../../locales/messagesDescriptors/common";
|
|
|
|
import messages from "../../locales/messagesDescriptors/elements/Pagination";
|
|
|
|
|
2016-07-29 23:57:21 +02:00
|
|
|
import css from "../../styles/elements/Pagination.scss";
|
|
|
|
|
2016-07-28 23:14:52 +02:00
|
|
|
const paginationMessages = defineMessages(messagesMap(Array.concat([], commonMessages, messages)));
|
|
|
|
|
2016-07-29 23:57:21 +02:00
|
|
|
class PaginationCSSIntl extends Component {
|
2016-07-07 23:23:18 +02:00
|
|
|
computePaginationBounds(currentPage, nPages, maxNumberPagesShown=5) {
|
|
|
|
// Taken from http://stackoverflow.com/a/8608998/2626416
|
|
|
|
var lowerLimit = currentPage;
|
|
|
|
var upperLimit = currentPage;
|
|
|
|
|
|
|
|
for (var b = 1; b < maxNumberPagesShown && b < nPages;) {
|
|
|
|
if (lowerLimit > 1 ) {
|
|
|
|
lowerLimit--;
|
|
|
|
b++;
|
|
|
|
}
|
|
|
|
if (b < maxNumberPagesShown && upperLimit < nPages) {
|
|
|
|
upperLimit++;
|
|
|
|
b++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
lowerLimit: lowerLimit,
|
|
|
|
upperLimit: upperLimit + 1 // +1 to ease iteration in for with <
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-08-01 00:26:52 +02:00
|
|
|
goToPage(ev) {
|
|
|
|
ev.preventDefault();
|
2016-07-07 23:23:18 +02:00
|
|
|
const pageNumber = parseInt(this.refs.pageInput.value);
|
2016-07-26 13:21:37 +02:00
|
|
|
$(this.refs.paginationModal).modal("hide");
|
|
|
|
if (pageNumber) {
|
2016-08-01 00:26:52 +02:00
|
|
|
this.props.goToPage(pageNumber);
|
2016-07-26 13:21:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dotsOnClick() {
|
|
|
|
$(this.refs.paginationModal).modal();
|
|
|
|
}
|
|
|
|
|
|
|
|
dotsOnKeyDown(ev) {
|
|
|
|
ev.preventDefault;
|
|
|
|
const code = ev.keyCode || ev.which;
|
|
|
|
if (code == 13 || code == 32) { // Enter or Space key
|
|
|
|
this.dotsOnClick(); // Fire same event as onClick
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cancelModalBox() {
|
|
|
|
$(this.refs.paginationModal).modal("hide");
|
2016-07-07 23:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
2016-07-28 23:14:52 +02:00
|
|
|
const { formatMessage } = this.props.intl;
|
2016-07-07 23:23:18 +02:00
|
|
|
const { lowerLimit, upperLimit } = this.computePaginationBounds(this.props.currentPage, this.props.nPages);
|
|
|
|
var pagesButton = [];
|
|
|
|
var key = 0; // key increment to ensure correct ordering
|
|
|
|
if (lowerLimit > 1) {
|
|
|
|
// Push first page
|
|
|
|
pagesButton.push(
|
|
|
|
<li className="page-item" key={key}>
|
2016-08-01 00:26:52 +02:00
|
|
|
<Link className="page-link" title={formatMessage(paginationMessages["app.pagination.goToPageWithoutMarkup"], { pageNumber: 1})} to={this.props.buildLinkToPage(1)}>
|
2016-07-29 00:50:08 +02:00
|
|
|
<FormattedHTMLMessage {...paginationMessages["app.pagination.goToPage"]} values={{ pageNumber: 1 }} />
|
2016-07-28 23:14:52 +02:00
|
|
|
</Link>
|
2016-07-07 23:23:18 +02:00
|
|
|
</li>
|
|
|
|
);
|
|
|
|
key++;
|
|
|
|
if (lowerLimit > 2) {
|
|
|
|
// Eventually push "…"
|
|
|
|
pagesButton.push(
|
|
|
|
<li className="page-item" key={key}>
|
2016-07-26 13:21:37 +02:00
|
|
|
<span tabIndex="0" role="button" onKeyDown={this.dotsOnKeyDown.bind(this)} onClick={this.dotsOnClick.bind(this)}>…</span>
|
2016-07-07 23:23:18 +02:00
|
|
|
</li>
|
|
|
|
);
|
|
|
|
key++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var i = 0;
|
|
|
|
for (i = lowerLimit; i < upperLimit; i++) {
|
|
|
|
var className = "page-item";
|
2016-07-26 13:21:37 +02:00
|
|
|
var currentSpan = null;
|
2016-07-07 23:23:18 +02:00
|
|
|
if (this.props.currentPage == i) {
|
|
|
|
className += " active";
|
2016-07-28 23:14:52 +02:00
|
|
|
currentSpan = <span className="sr-only">(<FormattedMessage {...paginationMessages["app.pagination.current"]} />)</span>;
|
2016-07-07 23:23:18 +02:00
|
|
|
}
|
2016-07-28 23:14:52 +02:00
|
|
|
const title = formatMessage(paginationMessages["app.pagination.goToPageWithoutMarkup"], { pageNumber: i });
|
2016-07-07 23:23:18 +02:00
|
|
|
pagesButton.push(
|
|
|
|
<li className={className} key={key}>
|
2016-08-01 00:26:52 +02:00
|
|
|
<Link className="page-link" title={title} to={this.props.buildLinkToPage(i)}>
|
2016-07-29 00:50:08 +02:00
|
|
|
<FormattedHTMLMessage {...paginationMessages["app.pagination.goToPage"]} values={{ pageNumber: i }} />
|
2016-07-28 23:14:52 +02:00
|
|
|
{currentSpan}
|
|
|
|
</Link>
|
2016-07-07 23:23:18 +02:00
|
|
|
</li>
|
|
|
|
);
|
|
|
|
key++;
|
|
|
|
}
|
|
|
|
if (i < this.props.nPages) {
|
|
|
|
if (i < this.props.nPages - 1) {
|
|
|
|
// Eventually push "…"
|
|
|
|
pagesButton.push(
|
|
|
|
<li className="page-item" key={key}>
|
2016-07-26 13:21:37 +02:00
|
|
|
<span tabIndex="0" role="button" onKeyDown={this.dotsOnKeyDown.bind(this)} onClick={this.dotsOnClick.bind(this)}>…</span>
|
2016-07-07 23:23:18 +02:00
|
|
|
</li>
|
|
|
|
);
|
|
|
|
key++;
|
|
|
|
}
|
2016-07-28 23:14:52 +02:00
|
|
|
const title = formatMessage(paginationMessages["app.pagination.goToPageWithoutMarkup"], { pageNumber: this.props.nPages });
|
2016-07-07 23:23:18 +02:00
|
|
|
// Push last page
|
|
|
|
pagesButton.push(
|
|
|
|
<li className="page-item" key={key}>
|
2016-08-01 00:26:52 +02:00
|
|
|
<Link className="page-link" title={title} to={this.props.buildLinkToPage(this.props.nPages)}>
|
2016-07-29 00:50:08 +02:00
|
|
|
<FormattedHTMLMessage {...paginationMessages["app.pagination.goToPage"]} values={{ pageNumber: this.props.nPages }} />
|
2016-07-28 23:14:52 +02:00
|
|
|
</Link>
|
2016-07-07 23:23:18 +02:00
|
|
|
</li>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (pagesButton.length > 1) {
|
|
|
|
return (
|
|
|
|
<div>
|
2016-07-29 23:57:21 +02:00
|
|
|
<nav className="pagination-nav" styleName="nav" aria-label={formatMessage(paginationMessages["app.pagination.pageNavigation"])}>
|
|
|
|
<ul className="pagination" styleName="pointer">
|
2016-07-07 23:23:18 +02:00
|
|
|
{ pagesButton }
|
|
|
|
</ul>
|
|
|
|
</nav>
|
2016-07-26 13:21:37 +02:00
|
|
|
<div className="modal fade" ref="paginationModal" tabIndex="-1" role="dialog" aria-labelledby="paginationModalLabel">
|
|
|
|
<div className="modal-dialog" role="document">
|
2016-07-07 23:23:18 +02:00
|
|
|
<div className="modal-content">
|
|
|
|
<div className="modal-header">
|
2016-07-28 23:14:52 +02:00
|
|
|
<button type="button" className="close" data-dismiss="modal" aria-label={formatMessage(paginationMessages["app.common.close"])}>×</button>
|
|
|
|
<h4 className="modal-title" id="paginationModalLabel">
|
|
|
|
<FormattedMessage {...paginationMessages["app.pagination.pageToGoTo"]} />
|
|
|
|
</h4>
|
2016-07-07 23:23:18 +02:00
|
|
|
</div>
|
|
|
|
<div className="modal-body">
|
2016-08-01 00:26:52 +02:00
|
|
|
<form onSubmit={this.goToPage.bind(this)}>
|
|
|
|
<input className="form-control" autoComplete="off" type="number" ref="pageInput" aria-label={formatMessage(paginationMessages["app.pagination.pageToGoTo"])} autoFocus />
|
2016-07-07 23:23:18 +02:00
|
|
|
</form>
|
|
|
|
</div>
|
|
|
|
<div className="modal-footer">
|
2016-07-28 23:14:52 +02:00
|
|
|
<button type="button" className="btn btn-default" onClick={this.cancelModalBox.bind(this)}>
|
|
|
|
<FormattedMessage {...paginationMessages["app.common.cancel"]} />
|
|
|
|
</button>
|
|
|
|
<button type="button" className="btn btn-primary" onClick={this.goToPage.bind(this)}>
|
|
|
|
<FormattedMessage {...paginationMessages["app.common.go"]} />
|
|
|
|
</button>
|
2016-07-07 23:23:18 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-29 23:57:21 +02:00
|
|
|
PaginationCSSIntl.propTypes = {
|
2016-07-07 23:23:18 +02:00
|
|
|
currentPage: PropTypes.number.isRequired,
|
2016-08-01 00:26:52 +02:00
|
|
|
goToPage: PropTypes.func.isRequired,
|
|
|
|
buildLinkToPage: PropTypes.func.isRequired,
|
2016-07-28 23:14:52 +02:00
|
|
|
nPages: PropTypes.number.isRequired,
|
|
|
|
intl: intlShape.isRequired,
|
2016-07-07 23:23:18 +02:00
|
|
|
};
|
|
|
|
|
2016-08-01 00:26:52 +02:00
|
|
|
export default injectIntl(CSSModules(PaginationCSSIntl, css));
|