Initial commit
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
|
||||
/**
|
||||
* View model intended to be responsible for pagination logic, and to be used in conjunction with a Pagination view.
|
||||
*/
|
||||
module.exports = Backbone.Model.extend({
|
||||
defaults: {
|
||||
total_count: 0,
|
||||
per_page: 10,
|
||||
current_page: 1,
|
||||
display_count: 5,
|
||||
extras_display_count: 1,
|
||||
url_to: undefined
|
||||
},
|
||||
|
||||
pagesCount: function () {
|
||||
return Math.max(
|
||||
Math.ceil(
|
||||
this.get('total_count') / this.get('per_page')
|
||||
), 1);
|
||||
},
|
||||
|
||||
isCurrentPage: function (page) {
|
||||
return this.get('current_page') === page;
|
||||
},
|
||||
|
||||
shouldBeVisible: function () {
|
||||
var pagesCount = this.pagesCount();
|
||||
return this.get('total_count') > 0 && pagesCount > 1 && this.get('current_page') <= pagesCount;
|
||||
},
|
||||
|
||||
urlTo: function (page) {
|
||||
if (this.hasUrl()) {
|
||||
return this.get('url_to')(page);
|
||||
}
|
||||
},
|
||||
|
||||
hasUrl: function () {
|
||||
return typeof this.get('url_to') === 'function';
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the pages that are expected to be displayed.
|
||||
* The current page will be in the middle of the returned sequence.
|
||||
*
|
||||
* @returns {number[]} a sequence of Numbers
|
||||
*/
|
||||
pagesToDisplay: function () {
|
||||
var rangeStart;
|
||||
|
||||
if (this._inLowRange()) {
|
||||
rangeStart = 1;
|
||||
} else if (this._inHighRange()) {
|
||||
rangeStart = this.get('current_page') - this._startOffset();
|
||||
} else {
|
||||
// Somewhere between the low and high boundary
|
||||
rangeStart = this.pagesCount() - this.get('display_count') + 1;
|
||||
}
|
||||
rangeStart = Math.max(rangeStart, 1);
|
||||
|
||||
return this._withExtraPages(
|
||||
_.range(rangeStart, this._rangeEnd(rangeStart))
|
||||
);
|
||||
},
|
||||
|
||||
_withExtraPages: function (pagesRelativeToCurrentPage) {
|
||||
var lastPage = this.pagesCount();
|
||||
var extraCount = this.get('extras_display_count');
|
||||
var extraStartPages = _.range(1, extraCount + 1);
|
||||
var extraEndPages = _.range(lastPage - extraCount + 1, lastPage + 1);
|
||||
|
||||
var startPagesDiff = pagesRelativeToCurrentPage[0] - extraStartPages.slice(-1)[0];
|
||||
if (startPagesDiff === 2) {
|
||||
// There is only one missing page in the gap, so add it
|
||||
extraStartPages.push(pagesRelativeToCurrentPage[0] - 1);
|
||||
} else if (startPagesDiff > 2) {
|
||||
// There are more hidden pages at low range, add padding at end
|
||||
extraStartPages.push(-1);
|
||||
}
|
||||
|
||||
var endPagesDiff = extraEndPages[0] - pagesRelativeToCurrentPage.slice(-1);
|
||||
if (endPagesDiff === 2) {
|
||||
// There is only one missing page in the gap, so add it
|
||||
extraEndPages.unshift(extraEndPages[0] - 1);
|
||||
} if (endPagesDiff > 2) {
|
||||
// There are more hidden pages at high range, add padding at beginning
|
||||
extraEndPages.unshift(-2);
|
||||
}
|
||||
|
||||
return _.union(extraStartPages, pagesRelativeToCurrentPage, extraEndPages);
|
||||
},
|
||||
|
||||
_inLowRange: function () {
|
||||
return this.get('current_page') < this._startOffset();
|
||||
},
|
||||
|
||||
_inHighRange: function () {
|
||||
return this.get('current_page') < this._highBoundary();
|
||||
},
|
||||
|
||||
_highBoundary: function () {
|
||||
return this.pagesCount() - this._startOffset();
|
||||
},
|
||||
|
||||
_startOffset: function () {
|
||||
return Math.floor(this.get('display_count') / 2);
|
||||
},
|
||||
|
||||
_rangeEnd: function (rangeStart) {
|
||||
// If we are too close to the range end then cap to the pages count.
|
||||
return Math.min(rangeStart + this.get('display_count'), this.pagesCount() + 1);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var $ = require('jquery');
|
||||
var navigateThroughRouter = require('builder/helpers/navigate-through-router');
|
||||
var template = require('./pagination.tpl');
|
||||
|
||||
/**
|
||||
* Responsible for pagination.
|
||||
*
|
||||
* Expected to be created with a pagination model, see the model for available params, here we create w/ the minimum:
|
||||
* new PaginationView({
|
||||
* model: new PaginationModel({
|
||||
* // Compulsory:
|
||||
* urlTo: function(page) { return '/?page='+ page },
|
||||
|
||||
// Optional, to router clicks on <a> tags through router.navigate by default
|
||||
* routerModel: new Router(...)
|
||||
* })
|
||||
* });
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
className: 'Pagination',
|
||||
|
||||
events: {
|
||||
'click .js-listItem': '_paginate'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
this._routerModel = opts.routerModel; // Optional
|
||||
|
||||
if (this.router && !this.model.hasUrl()) {
|
||||
throw new Error('since router is set the model must have a url method set too');
|
||||
}
|
||||
|
||||
this.template = opts.template || template;
|
||||
this.model.bind('change', this.render, this);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
if (this.model.shouldBeVisible()) {
|
||||
this.$el.html(
|
||||
this.template({
|
||||
m: this.model,
|
||||
pagesCount: this.model.pagesCount(),
|
||||
currentPage: this.model.get('current_page')
|
||||
})
|
||||
);
|
||||
this.$el.addClass(this.className);
|
||||
this.delegateEvents();
|
||||
} else {
|
||||
this.$el.html('');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_paginate: function (ev) {
|
||||
if (this._routerModel) {
|
||||
navigateThroughRouter.apply(this, arguments);
|
||||
} else if (!this.model.hasUrl()) {
|
||||
this.killEvent(ev);
|
||||
}
|
||||
|
||||
var page = $(ev.target).data('page');
|
||||
this.model.set('current_page', page);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
<span class="CDB-Text CDB-Size-medium u-altTextColor Pagination-label">
|
||||
Page <%- currentPage %> of <%- pagesCount %>
|
||||
</span>
|
||||
<ul class="Pagination-list CDB-Text CDB-Size-medium">
|
||||
<% m.pagesToDisplay().forEach(function(page) { %>
|
||||
<% if (page > 0) { %>
|
||||
<li class="Pagination-listItem <%- m.isCurrentPage(page) ? 'is-current' : '' %>">
|
||||
<a class="Pagination-listItemInner Pagination-listItemInner--link js-listItem" href="<%- m.urlTo(page) %>" data-page="<%- page %>"><%- page %></a>
|
||||
</li>
|
||||
<% } else { %>
|
||||
<li class="Pagination-listItem Pagination-listItem">
|
||||
<span class="Pagination-listItemInner Pagination-listItemInner--more">…</span>
|
||||
</li>
|
||||
<% } %>
|
||||
<% }) %>
|
||||
</ul>
|
||||
Reference in New Issue
Block a user