Initial commit
This commit is contained in:
+47
@@ -0,0 +1,47 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
/**
|
||||
* Model representing the query string params for a "paged search" of a collection (matching the server-side APIs).
|
||||
*
|
||||
* @example usage
|
||||
* pagedSearch = new PaginationSearchModel({ … })
|
||||
* pagedSearch.fetch(collection) // => jqXHR, GET /collection/123?page=1&per_page20
|
||||
* pagedSearch.set({ page: 2, per_page: 10, q: 'test' });
|
||||
* pagedSearch.fetch(collection) // => GET /collection/123?page=2&per_page10&q=test
|
||||
*/
|
||||
module.exports = Backbone.Model.extend({
|
||||
|
||||
defaults: {
|
||||
per_page: 20,
|
||||
page: 1,
|
||||
q: ''
|
||||
},
|
||||
|
||||
initialize: function (attrs, opts) {
|
||||
if (!opts.collection) throw new Error('collection is required');
|
||||
if (!opts.collection.totalCount) throw new Error('collection requires to implement totalCount method');
|
||||
|
||||
this.collection = opts.collection;
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.collection.on('sync', this._updateStateFetched, this);
|
||||
this.collection.on('error', this._updateStateError, this);
|
||||
},
|
||||
|
||||
fetch: function () {
|
||||
this.trigger('fetching', this);
|
||||
return this.collection.fetch({
|
||||
data: this.attributes
|
||||
}, {reset: true});
|
||||
},
|
||||
|
||||
_updateStateFetched: function () {
|
||||
this.trigger('fetched', this);
|
||||
},
|
||||
|
||||
_updateStateError: function () {
|
||||
this.trigger('error', this);
|
||||
}
|
||||
});
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var _ = require('underscore');
|
||||
var PaginationModel = require('builder/components/pagination/pagination-model');
|
||||
var PaginationSearchModel = require('./pagination-search-model');
|
||||
var renderLoading = require('builder/components/loading/render-loading');
|
||||
var renderNoResults = require('builder/components/no-results/render-no-results');
|
||||
var ErrorView = require('builder/components/error/error-view');
|
||||
var template = require('./pagination-search.tpl');
|
||||
var Utils = require('builder/helpers/utils');
|
||||
var PaginationView = require('builder/components/pagination/pagination-view');
|
||||
var paginationTemplate = require('./pagination.tpl');
|
||||
|
||||
/**
|
||||
* View to render a searchable/pageable collection.
|
||||
* Also allows to filter/search list.
|
||||
*
|
||||
*/
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'listCollection',
|
||||
'createContentView'
|
||||
];
|
||||
|
||||
var ENTER_KEY_CODE = 13;
|
||||
var ESCAPE_KEY_CODE = 27;
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
events: {
|
||||
'click .js-search-link': '_onSearchClick',
|
||||
'click .js-clean-search': '_onCleanSearchClick',
|
||||
'keydown .js-search-input': '_onKeyDown'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
_.each(REQUIRED_OPTS, function (item) {
|
||||
if (opts[item] === undefined) throw new Error(item + ' is required');
|
||||
this['_' + item] = opts[item];
|
||||
}, this);
|
||||
|
||||
this._paginationModel = new PaginationModel({
|
||||
current_page: 1
|
||||
});
|
||||
|
||||
this._paginationSearchModel = new PaginationSearchModel({}, {
|
||||
collection: this._listCollection
|
||||
});
|
||||
|
||||
this._stateModel = new Backbone.Model({
|
||||
state: 'idle'
|
||||
});
|
||||
|
||||
this._initBinds();
|
||||
this._initPagination();
|
||||
this._paginationSearchModel.fetch();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.html(template({
|
||||
q: this._paginationSearchModel.get('q')
|
||||
}));
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this._paginationSearchModel.on('fetching', this.showLoading, this);
|
||||
this._paginationSearchModel.on('fetched', this._updateStateFetched, this);
|
||||
this._paginationSearchModel.on('error', this.showError, this);
|
||||
|
||||
this._paginationModel.bind('change:current_page', this._fetchByPagination, this);
|
||||
|
||||
this._stateModel.on('change:state', this.render, this);
|
||||
|
||||
this.add_related_model(this._paginationModel);
|
||||
this.add_related_model(this._stateModel);
|
||||
this.add_related_model(this._stateModel);
|
||||
},
|
||||
|
||||
_fetchByPagination: function () {
|
||||
this._paginationSearchModel.set('page', this._paginationModel.get('current_page'));
|
||||
this._paginationSearchModel.fetch();
|
||||
},
|
||||
|
||||
showError: function () {
|
||||
this._stateModel.set('state', 'error');
|
||||
},
|
||||
|
||||
showLoading: function () {
|
||||
this._stateModel.set('state', 'loading');
|
||||
},
|
||||
|
||||
_updateStateFetched: function () {
|
||||
this._paginationModel.set({
|
||||
per_page: this._paginationSearchModel.get('per_page'),
|
||||
total_count: this._listCollection.totalCount()
|
||||
});
|
||||
if (this._listCollection.length > 0) {
|
||||
this._stateModel.set('state', 'show');
|
||||
} else {
|
||||
this._stateModel.set('state', 'no-results');
|
||||
}
|
||||
},
|
||||
|
||||
_initPagination: function () {
|
||||
this._paginationView = new PaginationView({
|
||||
className: 'Pagination Pagination--search Pagination--searchShare',
|
||||
model: this._paginationModel,
|
||||
template: paginationTemplate
|
||||
});
|
||||
this.addView(this._paginationView);
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var state = this._stateModel.get('state');
|
||||
var view;
|
||||
|
||||
this.$el.append(this._paginationView.render().el);
|
||||
|
||||
if (state === 'loading') {
|
||||
this._content().html(renderLoading({
|
||||
title: _t('components.pagination-search.loading.title')
|
||||
}));
|
||||
}
|
||||
|
||||
if (state === 'error') {
|
||||
var errorView = new ErrorView({
|
||||
title: _t('components.pagination-search.error.title'),
|
||||
desc: _t('components.pagination-search.error.desc')
|
||||
});
|
||||
this._content().html(errorView.render().el);
|
||||
this.addView(errorView);
|
||||
}
|
||||
|
||||
if (state === 'show') {
|
||||
view = this._createContentView({
|
||||
hasOrganization: this._paginationSearchModel.get('q') === ''
|
||||
});
|
||||
this._content().html(view.render().el);
|
||||
this.addView(view);
|
||||
}
|
||||
|
||||
if (state === 'no-results') {
|
||||
this._content().html(renderNoResults({
|
||||
icon: 'CDB-IconFont-defaultUser',
|
||||
title: _t('components.pagination-search.no-results.title'),
|
||||
desc: _t('components.pagination-search.no-results.desc')
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_focusSearchInput: function () {
|
||||
this._searchInput().focus().val();
|
||||
},
|
||||
|
||||
_onSearchClick: function (e) {
|
||||
this.killEvent(e);
|
||||
this._searchInput().focus();
|
||||
},
|
||||
|
||||
_onCleanSearchClick: function (e) {
|
||||
this.killEvent(e);
|
||||
this._cleanSearch();
|
||||
},
|
||||
|
||||
_onKeyDown: function (e) {
|
||||
if (e.which === ENTER_KEY_CODE) {
|
||||
this.killEvent(e);
|
||||
this._submitSearch();
|
||||
} else if (e.which === ESCAPE_KEY_CODE) {
|
||||
if (this._paginationSearchModel.get('q')) {
|
||||
this.killEvent(e);
|
||||
this._cleanSearch();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_submitSearch: function (e) {
|
||||
this._makeNewSearch(Utils.stripHTML(this._searchInput().val().trim()));
|
||||
},
|
||||
|
||||
_cleanSearch: function () {
|
||||
this._searchInput().val('');
|
||||
this._makeNewSearch('');
|
||||
},
|
||||
|
||||
_makeNewSearch: function (query) {
|
||||
this._paginationSearchModel.set({
|
||||
q: query,
|
||||
page: 1
|
||||
});
|
||||
|
||||
this._paginationSearchModel.fetch();
|
||||
},
|
||||
|
||||
_searchInput: function () {
|
||||
return this.$('.js-search-input');
|
||||
},
|
||||
|
||||
_cleanSearchBtn: function () {
|
||||
return this.$('.js-clean-search');
|
||||
},
|
||||
|
||||
_content: function () {
|
||||
return this.$('.js-content');
|
||||
}
|
||||
|
||||
});
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<div class="Filters is-relative">
|
||||
<div class="Filters-inner">
|
||||
<div class="Filters-row Share-filtersRow js-filters">
|
||||
<div class="Filters-typeItem Filters-typeItem--searchEnabler">
|
||||
<p class="Filters-searchLink js-search-link u-alignCenter CDB-Text CDB-Size-medium u-upperCase">
|
||||
<i class="Filters-searchLinkIcon CDB-IconFont CDB-IconFont-lens u-rSpace--xl"></i> <%- _t('components.pagination-search.filter.search') %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="Filters-typeItem Filters-typeItem--searchField">
|
||||
<input class="Filters-searchInput CDB-Text CDB-Size-medium js-search-input" type="text" value="<%- q %>" placeholder="<%- _t('components.pagination-search.filter.placeholder') %>" />
|
||||
<% if (q !== '') { %>
|
||||
<button type="button" class="CDB-Shape Filters-cleanSearch js-clean-search u-actionTextColor">
|
||||
<div class="CDB-Shape-close is-blue is-large"></div>
|
||||
</button>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="js-content"></div>
|
||||
@@ -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' : '' %>">
|
||||
<button class="Pagination-listItemInner Pagination-listItemInner--link u-actionTextColor js-listItem" data-page="<%- page %>"><%- page %></button>
|
||||
</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