Initial commit
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
var _ = require('underscore');
|
||||
var WidgetModel = require('../widget-model');
|
||||
var LockedCategoriesCollection = require('./locked-categories-collection');
|
||||
|
||||
/**
|
||||
* Model for a category widget
|
||||
*/
|
||||
module.exports = WidgetModel.extend({
|
||||
|
||||
defaults: _.extend({
|
||||
type: 'category',
|
||||
search: false,
|
||||
locked: false
|
||||
},
|
||||
WidgetModel.prototype.defaults
|
||||
),
|
||||
|
||||
defaultState: _.extend({
|
||||
acceptedCategories: [],
|
||||
locked: false,
|
||||
autoStyle: false
|
||||
},
|
||||
WidgetModel.prototype.defaultState
|
||||
),
|
||||
|
||||
initialize: function () {
|
||||
WidgetModel.prototype.initialize.apply(this, arguments);
|
||||
this.lockedCategories = new LockedCategoriesCollection();
|
||||
|
||||
this.listenTo(this.dataviewModel, 'change:allCategoryNames', this._onDataviewAllCategoryNamesChange);
|
||||
|
||||
this.on('change:locked', this._onLockedChange, this);
|
||||
this.on('change:collapsed', this._onCollapsedChange, this);
|
||||
|
||||
if (this.isAutoStyleEnabled()) {
|
||||
this.on('change:style', this._updateAutoStyle, this);
|
||||
}
|
||||
|
||||
this.dataviewModel.filter.on('change', function () {
|
||||
this.set('acceptedCategories', this._acceptedCategories().pluck('name'));
|
||||
}, this);
|
||||
this.dataviewModel.once('change:allCategoryNames', function () {
|
||||
if (this.get('autoStyle')) {
|
||||
this.autoStyle();
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
setupSearch: function () {
|
||||
this.dataviewModel.setupSearch();
|
||||
this.lockedCategories.addItems(this._acceptedCategories().toJSON());
|
||||
this.toggleSearch();
|
||||
},
|
||||
|
||||
toggleSearch: function () {
|
||||
this.set('search', !this.get('search'));
|
||||
},
|
||||
|
||||
enableSearch: function () {
|
||||
this.set('search', true);
|
||||
},
|
||||
|
||||
disableSearch: function () {
|
||||
this.set('search', false);
|
||||
},
|
||||
|
||||
isSearchEnabled: function () {
|
||||
return this.get('search');
|
||||
},
|
||||
|
||||
cleanSearch: function () {
|
||||
this.dataviewModel.cleanSearch();
|
||||
this.lockedCategories.reset([]);
|
||||
},
|
||||
|
||||
autoStyle: function () {
|
||||
// NOTE: maybe not pre-assing colors to categories?
|
||||
this.autoStyler.colors.updateData(this.dataviewModel.get('allCategoryNames'));
|
||||
WidgetModel.prototype.autoStyle.call(this);
|
||||
},
|
||||
|
||||
isLocked: function () {
|
||||
return this.get('locked');
|
||||
},
|
||||
|
||||
canBeLocked: function () {
|
||||
return this.isLocked() || this._acceptedCategories().size() > 0;
|
||||
},
|
||||
|
||||
canApplyLocked: function () {
|
||||
if (this._acceptedCategories().size() !== this.lockedCategories.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this._acceptedCategories().any(function (m) {
|
||||
return !this.lockedCategories.isItemLocked(m.get('name'));
|
||||
}, this);
|
||||
},
|
||||
|
||||
applyLocked: function () {
|
||||
var currentLocked = this.lockedCategories.getItemsName();
|
||||
if (!currentLocked.length) {
|
||||
this.unlockCategories();
|
||||
return false;
|
||||
}
|
||||
|
||||
this.set('locked', true);
|
||||
|
||||
var f = this.dataviewModel.filter;
|
||||
f.cleanFilter(false);
|
||||
f.accept(currentLocked);
|
||||
f.applyFilter();
|
||||
|
||||
this.cleanSearch();
|
||||
},
|
||||
|
||||
lockCategories: function () {
|
||||
this.set('locked', true);
|
||||
this.dataviewModel.fetch();
|
||||
},
|
||||
|
||||
unlockCategories: function () {
|
||||
this.set('locked', false);
|
||||
this.dataviewModel.filter.acceptAll();
|
||||
},
|
||||
|
||||
_onDataviewAllCategoryNamesChange: function (m, names) {
|
||||
if (this.isAutoStyleEnabled()) {
|
||||
if (!this.isAutoStyle()) {
|
||||
this.autoStyler.colors.updateData(names);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onLockedChange: function (m, isLocked) {
|
||||
if (isLocked) {
|
||||
this.dataviewModel.enableFilter();
|
||||
} else {
|
||||
this.dataviewModel.disableFilter();
|
||||
}
|
||||
},
|
||||
|
||||
_acceptedCategories: function () {
|
||||
return this.dataviewModel.filter.acceptedCategories;
|
||||
},
|
||||
|
||||
_onCollapsedChange: function (m, isCollapsed) {
|
||||
this.dataviewModel.set('enabled', !isCollapsed);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="CDB-Widget-header js-header"></div>
|
||||
<div class="CDB-Widget-content CDB-Widget-content--noSidesMargin">
|
||||
<div class="CDB-Widget-listWrapper js-content"></div>
|
||||
</div>
|
||||
<div class="CDB-Widget-footer js-footer"></div>
|
||||
@@ -0,0 +1,125 @@
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var SearchTitleView = require('./title/search-title-view');
|
||||
var CategoryOptionsView = require('./options/options-view');
|
||||
var CategoryItemsView = require('./list/items-view');
|
||||
var CategoryStatsView = require('./stats/stats-view');
|
||||
var CategoryPaginatorView = require('./paginator/paginator-view');
|
||||
var SearchCategoryItemsView = require('./list/search-items-view');
|
||||
var SearchCategoryPaginatorView = require('./paginator/search-paginator-view');
|
||||
var template = require('./content-template.tpl');
|
||||
|
||||
var STARTING_PAGE = 1;
|
||||
|
||||
/**
|
||||
* Content view for category widget
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
className: 'CDB-Widget-body',
|
||||
|
||||
_ITEMS_PER_PAGE: 6,
|
||||
|
||||
initialize: function () {
|
||||
this._dataviewModel = this.model.dataviewModel;
|
||||
this._layerModel = this.model.layerModel;
|
||||
|
||||
this._paginatorModel = new Backbone.Model({
|
||||
page: STARTING_PAGE
|
||||
});
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
|
||||
this.$el.toggleClass('is-collapsed', !!this.model.get('collapsed'));
|
||||
|
||||
this.$el.html(template());
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
if (this.model.get('hasInitialState') === true) {
|
||||
this._onInitialState();
|
||||
} else {
|
||||
this.model.bind('change:hasInitialState', this._onInitialState, this);
|
||||
}
|
||||
this.model.bind('change:collapsed', function (mdl, isCollapsed) {
|
||||
this.$el.toggleClass('is-collapsed', !!isCollapsed);
|
||||
}, this);
|
||||
},
|
||||
|
||||
_onInitialState: function () {
|
||||
this.render();
|
||||
|
||||
if (this.model.get('autoStyle') === true) {
|
||||
this.model.autoStyle();
|
||||
}
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var searchTitle = new SearchTitleView({
|
||||
widgetModel: this.model,
|
||||
dataviewModel: this._dataviewModel,
|
||||
layerModel: this._layerModel
|
||||
});
|
||||
this.$('.js-header').append(searchTitle.render().el);
|
||||
this.addView(searchTitle);
|
||||
|
||||
var stats = new CategoryStatsView({
|
||||
widgetModel: this.model,
|
||||
dataviewModel: this._dataviewModel
|
||||
});
|
||||
this.$('.js-header').append(stats.render().el);
|
||||
this.addView(stats);
|
||||
|
||||
var options = new CategoryOptionsView({
|
||||
widgetModel: this.model,
|
||||
dataviewModel: this._dataviewModel
|
||||
});
|
||||
this.$('.js-content').html(options.render().el);
|
||||
this.addView(options);
|
||||
|
||||
var dataList = new CategoryItemsView({
|
||||
widgetModel: this.model,
|
||||
dataviewModel: this._dataviewModel,
|
||||
itemsPerPage: this._ITEMS_PER_PAGE,
|
||||
paginatorModel: this._paginatorModel
|
||||
});
|
||||
this.$('.js-content').append(dataList.render().el);
|
||||
this.addView(dataList);
|
||||
|
||||
var pagination = new CategoryPaginatorView({
|
||||
widgetModel: this.model,
|
||||
dataviewModel: this._dataviewModel,
|
||||
itemsPerPage: this._ITEMS_PER_PAGE,
|
||||
paginatorModel: this._paginatorModel
|
||||
});
|
||||
this.$('.js-footer').append(pagination.render().el);
|
||||
this.addView(pagination);
|
||||
|
||||
var searchList = new SearchCategoryItemsView({
|
||||
widgetModel: this.model,
|
||||
dataviewModel: this._dataviewModel,
|
||||
itemsPerPage: this._ITEMS_PER_PAGE,
|
||||
paginator: true,
|
||||
paginatorModel: this._paginatorModel
|
||||
});
|
||||
this.$('.js-content').append(searchList.render().el);
|
||||
this.addView(searchList);
|
||||
|
||||
var searchPagination = new SearchCategoryPaginatorView({
|
||||
widgetModel: this.model,
|
||||
dataviewModel: this._dataviewModel,
|
||||
itemsPerPage: this._ITEMS_PER_PAGE,
|
||||
paginator: true,
|
||||
paginatorModel: this._paginatorModel
|
||||
});
|
||||
this.$('.js-footer').append(searchPagination.render().el);
|
||||
this.addView(searchPagination);
|
||||
}
|
||||
});
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<button type="button" class="CDB-Widget-listItemInner CDB-Widget-listButton js-button <%- isDisabled ? 'is-disabled' : '' %>">
|
||||
<div class="CDB-Widget-contentSpaced">
|
||||
<p class="CDB-Text is-semibold u-upperCase CDB-Size-medium u-ellipsis u-rSpace--xl" title="<%- name %>"><%- name %></p>
|
||||
<p class="CDB-Text CDB-Size-small u-secondaryTextColor" title="<%- value %>"><%- prefix %><%- formattedValue %><%- suffix %></p>
|
||||
</div>
|
||||
<div class="CDB-Widget-progressBar">
|
||||
<div class="CDB-Widget-progressState <%- isAggregated ? 'CDB-Widget-progressState--pattern' : '' %> <%- isAccepted ? 'is-accepted' : '' %>" style="width: <%- percentage %>%; background-color: <%- color %>"></div>
|
||||
</div>
|
||||
</button>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<div class="CDB-Widget-listItemInner <%- isDisabled ? 'is-disabled' : '' %>">
|
||||
<div class="CDB-Widget-contentSpaced">
|
||||
<p class="CDB-Text is-semibold u-upperCase CDB-Size-medium u-ellipsis u-rSpace--xl" title="<%- name %>"><%- name %></p>
|
||||
<p class="CDB-Text CDB-Size-small u-secondaryTextColor" title="<%- value %>"><%- prefix %><%- formattedValue %><%- suffix %></p>
|
||||
</div>
|
||||
<div class="CDB-Widget-progressBar">
|
||||
<div class="CDB-Widget-progressState CDB-Widget-progressState--pattern <%- isAggregated ? 'CDB-Widget-progressState--inactive' : '' %>"
|
||||
style="width: <%- percentage %>%; background-color: <%- color %>"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,61 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var formatter = require('../../../../formatter');
|
||||
var _ = require('underscore');
|
||||
var clickableTemplate = require('./item-clickable-template.tpl');
|
||||
var unclickableTemplate = require('./item-unclickable-template.tpl');
|
||||
|
||||
/**
|
||||
* Category list item view
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
tagName: 'li',
|
||||
className: 'CDB-Widget-listItem',
|
||||
|
||||
events: {
|
||||
'click .js-button': '_onItemClick'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.widgetModel = this.options.widgetModel;
|
||||
this.dataviewModel = this.options.dataviewModel;
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var name = this.model.get('name');
|
||||
var value = this.model.get('value');
|
||||
var template = this.model.get('agg') || this.widgetModel.isLocked()
|
||||
? unclickableTemplate
|
||||
: clickableTemplate;
|
||||
var acceptedCategories = this.dataviewModel.filter.getAcceptedCategoryNames();
|
||||
var isAccepted = _.contains(acceptedCategories, name);
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
isAggregated: this.model.get('agg'),
|
||||
name: name,
|
||||
value: value,
|
||||
formattedValue: formatter.formatNumber(value),
|
||||
percentage: ((value / this.dataviewModel.get('max')) * 100),
|
||||
color: this.widgetModel.getColor(name),
|
||||
isDisabled: !this.model.get('selected') ? 'is-disabled' : '',
|
||||
isAccepted: isAccepted,
|
||||
prefix: this.widgetModel.get('prefix'),
|
||||
suffix: this.widgetModel.get('suffix')
|
||||
})
|
||||
);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.model.bind('change', this.render, this);
|
||||
this.widgetModel.bind('change:search change:prefix change:suffix change:autoStyle', this.render, this);
|
||||
this.add_related_model(this.widgetModel);
|
||||
},
|
||||
|
||||
_onItemClick: function () {
|
||||
this.trigger('itemClicked', this.model, this);
|
||||
}
|
||||
|
||||
});
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<button type="button" class="CDB-Widget-listItemInner CDB-Widget-listItemInner--fullSpace CDB-Widget-listButton js-button <%- isDisabled ? 'is-disabled' : '' %>">
|
||||
<span class="CDB-Widget-checkbox <%- isDisabled ? '' : 'is-checked' %>"></span>
|
||||
<div class="u-lSpace u-ellipsis">
|
||||
<div class="CDB-Widget-contentSpaced">
|
||||
<p class="CDB-Text is-semibold u-upperCase CDB-Size-medium u-ellipsis u-rSpace--xl" title="<%- name %>"><%- name %></p>
|
||||
<p class="CDB-Text CDB-Size-small u-secondaryTextColor" title="<%- value %>"><%- prefix %><%- formattedValue %><%- suffix %></p>
|
||||
</div>
|
||||
<div class="CDB-Widget-progressBar">
|
||||
<div class="CDB-Widget-progressState is-accepted" style="width: <%- percentage %>%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -0,0 +1,54 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var formatter = require('../../../../formatter');
|
||||
var template = require('./search-item-clickable-template.tpl');
|
||||
|
||||
/**
|
||||
* Category search list view
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
tagName: 'li',
|
||||
className: 'CDB-Widget-listItem',
|
||||
|
||||
events: {
|
||||
'click .js-button': '_onItemClick'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
// This data model comes from the original data in order to get
|
||||
// the max value and set properly the progress bar and add the
|
||||
// necessary suffix and prefix for the item.
|
||||
this.dataviewModel = this.options.dataviewModel;
|
||||
this.widgetModel = this.options.widgetModel;
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var value = this.model.get('value');
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
name: this.model.get('name'),
|
||||
value: value,
|
||||
formattedValue: formatter.formatNumber(value),
|
||||
percentage: ((value / this.dataviewModel.get('max')) * 100),
|
||||
isDisabled: !this.model.get('selected'),
|
||||
prefix: this.widgetModel.get('prefix'),
|
||||
suffix: this.widgetModel.get('suffix')
|
||||
})
|
||||
);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.model.bind('change:selected', this.render, this);
|
||||
|
||||
this.widgetModel.bind('change:prefix change:suffix', this.render, this);
|
||||
this.add_related_model(this.widgetModel);
|
||||
},
|
||||
|
||||
_onItemClick: function () {
|
||||
this.model.set('selected', !this.model.get('selected'));
|
||||
}
|
||||
|
||||
});
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
<li class="CDB-Widget-listItem CDB-Widget-listItem--fake"></li>
|
||||
<li class="CDB-Widget-listItem CDB-Widget-listItem--fake"></li>
|
||||
<li class="CDB-Widget-listItem CDB-Widget-listItem--fake"></li>
|
||||
<li class="CDB-Widget-listItem CDB-Widget-listItem--fake"></li>
|
||||
@@ -0,0 +1,175 @@
|
||||
var _ = require('underscore');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var CategoryItemView = require('./item/item-view');
|
||||
var placeholder = require('./items-placeholder-template.tpl');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'dataviewModel',
|
||||
'widgetModel',
|
||||
'paginatorModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* Category list view
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
options: {
|
||||
paginator: false,
|
||||
itemsPerPage: 6
|
||||
},
|
||||
|
||||
className: 'CDB-Widget-list js-list',
|
||||
|
||||
tagName: 'ul',
|
||||
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
|
||||
if (!this._dataviewModel.get('sync_on_bbox_change')) {
|
||||
this.$el.addClass('CDB-Widget-list--nodynamic');
|
||||
}
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
var data = this._dataviewModel.getData();
|
||||
var isDataEmpty = _.isEmpty(data) || _.size(data) === 0;
|
||||
|
||||
if (isDataEmpty) {
|
||||
this._renderPlaceholder();
|
||||
} else {
|
||||
this._renderList();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._paginatorModel, 'change:page', this.render);
|
||||
|
||||
this.listenTo(this._widgetModel, 'change:search', this.toggle);
|
||||
this.listenTo(this._widgetModel, 'change:style change:autoStyle', this.render);
|
||||
|
||||
this.listenTo(this._dataviewModel, 'change:data', this.render);
|
||||
this.listenTo(this._dataviewModel, 'change:sync_on_bbox_change', this.blockFiltering);
|
||||
},
|
||||
|
||||
_renderPlaceholder: function () {
|
||||
// Change view classes
|
||||
this.$el.addClass('CDB-Widget-list--withBorders CDB-Widget-list--fake');
|
||||
this.$el.append(placeholder());
|
||||
},
|
||||
|
||||
_renderList: function () {
|
||||
this._createList();
|
||||
|
||||
this.$el.removeClass('CDB-Widget-list--withBorders CDB-Widget-list--fake CDB-Widget-list--noresults');
|
||||
|
||||
this._renderItems();
|
||||
},
|
||||
|
||||
_renderItems: function () {
|
||||
var currentPage = this._paginatorModel.get('page');
|
||||
var pageIndex = currentPage - 1; // Transform from 1-based index to zero-based index
|
||||
var items = this.pagesData[pageIndex];
|
||||
|
||||
_.each(items, function (model) {
|
||||
this._addItem(model);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_createList: function () {
|
||||
this.pagesData = {};
|
||||
|
||||
var data = this._getData();
|
||||
var currentIndex = 0;
|
||||
|
||||
data.each(function (model, index) {
|
||||
if (index % this.options.itemsPerPage === 0) {
|
||||
if (index !== 0) {
|
||||
currentIndex += 1;
|
||||
}
|
||||
this.pagesData[currentIndex] = [];
|
||||
}
|
||||
|
||||
this.pagesData[currentIndex].push(model);
|
||||
}, this);
|
||||
},
|
||||
|
||||
_getData: function () {
|
||||
return this._dataviewModel.getData();
|
||||
},
|
||||
|
||||
_addItem: function (model) {
|
||||
var view = new CategoryItemView({
|
||||
model: model,
|
||||
widgetModel: this._widgetModel,
|
||||
dataviewModel: this._dataviewModel
|
||||
});
|
||||
|
||||
view.bind('itemClicked', this._setFilters, this);
|
||||
|
||||
this.addView(view);
|
||||
this.$el.append(view.render().el);
|
||||
},
|
||||
|
||||
_setFilters: function (mdl) {
|
||||
var isSelected = mdl.get('selected');
|
||||
var filter = this._dataviewModel.filter;
|
||||
var clickedName = mdl.get('name');
|
||||
|
||||
if (isSelected) {
|
||||
// If there isn't any filter applied,
|
||||
// clicking over one will turn rest into as "unselected"
|
||||
if (filter.rejectedCategories.size() === 0 &&
|
||||
filter.acceptedCategories.size() === 0
|
||||
) {
|
||||
var data = this._dataviewModel.getData();
|
||||
// Make elements "unselected"
|
||||
data.each(function (m) {
|
||||
var name = m.get('name');
|
||||
if (name !== clickedName) {
|
||||
m.set('selected', false);
|
||||
}
|
||||
});
|
||||
filter.accept(mdl.get('name'));
|
||||
} else {
|
||||
mdl.set('selected', false);
|
||||
filter.reject(clickedName);
|
||||
}
|
||||
} else {
|
||||
mdl.set('selected', true);
|
||||
filter.accept(clickedName);
|
||||
}
|
||||
this._widgetModel.set('acceptedCategories', this._widgetModel._acceptedCategories().pluck('name'));
|
||||
},
|
||||
|
||||
blockFiltering: function (e) {
|
||||
if (e.changed['sync_on_bbox_change']) { // Is dynamic
|
||||
this.$el.removeClass('CDB-Widget-list--nodynamic');
|
||||
} else { // It is not
|
||||
this.$el.addClass('CDB-Widget-list--nodynamic');
|
||||
this._dataviewModel.filter.acceptAll();
|
||||
}
|
||||
},
|
||||
|
||||
removeSelections: function () {
|
||||
this._dataviewModel.filter.acceptAll();
|
||||
},
|
||||
|
||||
toggle: function () {
|
||||
this[!this._widgetModel.isSearchEnabled() ? 'show' : 'hide']();
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.$el.removeClass('is-hidden');
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.$el.addClass('is-hidden');
|
||||
}
|
||||
|
||||
});
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<li class="CDB-Widget-listItem">
|
||||
<h4 class="CDB-Text CDB-Size-large">No results</h4>
|
||||
<p class="CDB-Text CDB-Size-medium u-tSpace-xl u-altTextColor">Your search "<%- q %>" didn't match<br/>with any value.</p>
|
||||
<p class="CDB-Text CDB-Size-medium u-tSpace-xl u-altTextColor">Try searching again.</p>
|
||||
</li>
|
||||
@@ -0,0 +1,63 @@
|
||||
var CategoryItemsView = require('./items-view');
|
||||
var WidgetSearchCategoryItemView = require('./item/search-item-view');
|
||||
var placeholder = require('./search-items-no-results-template.tpl');
|
||||
|
||||
/**
|
||||
* Category list view
|
||||
*/
|
||||
module.exports = CategoryItemsView.extend({
|
||||
className: 'CDB-Widget-list is-hidden js-list',
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
var data = this._searchResultsCollection;
|
||||
var isDataEmpty = data.isEmpty() || data.size() === 0;
|
||||
|
||||
if (isDataEmpty) {
|
||||
this._renderPlaceholder();
|
||||
} else {
|
||||
this._renderList();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
CategoryItemsView.prototype._initBinds.apply(this, arguments);
|
||||
this._searchResultsCollection = this._dataviewModel.getSearchResult();
|
||||
this.listenTo(this._searchResultsCollection, 'change:selected', this._onSelectedItemChange);
|
||||
this.listenTo(this._searchResultsCollection, 'reset', this.render);
|
||||
},
|
||||
|
||||
_getData: function () {
|
||||
return this._searchResultsCollection;
|
||||
},
|
||||
|
||||
_renderPlaceholder: function () {
|
||||
this.$el.addClass('CDB-Widget-list--noresults');
|
||||
|
||||
this.$el.html(
|
||||
placeholder({
|
||||
q: this._dataviewModel.getSearchQuery()
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
_addItem: function (model) {
|
||||
var view = new WidgetSearchCategoryItemView({
|
||||
model: model,
|
||||
widgetModel: this._widgetModel,
|
||||
dataviewModel: this._dataviewModel
|
||||
});
|
||||
this.addView(view);
|
||||
this.$el.append(view.render().el);
|
||||
},
|
||||
|
||||
toggle: function () {
|
||||
this[this._widgetModel.isSearchEnabled() ? 'show' : 'hide']();
|
||||
},
|
||||
|
||||
_onSelectedItemChange: function (model, isSelected) {
|
||||
this._widgetModel.lockedCategories[isSelected ? 'addItem' : 'removeItem'](model);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
|
||||
/**
|
||||
* Locked categories collection
|
||||
*
|
||||
*/
|
||||
module.exports = Backbone.Collection.extend({
|
||||
|
||||
addItem: function (mdl) {
|
||||
if (!this.isItemLocked(mdl.get('name'))) {
|
||||
this.add(mdl);
|
||||
}
|
||||
},
|
||||
|
||||
addItems: function (mdls) {
|
||||
_.each(mdls, function (m) {
|
||||
if (!this.isItemLocked(m.name)) {
|
||||
this.add(m);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
resetItems: function (mdls) {
|
||||
this.reset(mdls);
|
||||
},
|
||||
|
||||
removeItem: function (mdl) {
|
||||
var lockedItem = this.isItemLocked(mdl.get('name'));
|
||||
if (lockedItem) {
|
||||
this.remove(lockedItem);
|
||||
}
|
||||
},
|
||||
|
||||
removeItems: function () {
|
||||
this.reset([]);
|
||||
},
|
||||
|
||||
isItemLocked: function (name) {
|
||||
return this.find(function (d) {
|
||||
return d.get('name') === name;
|
||||
});
|
||||
},
|
||||
|
||||
getItemsName: function () {
|
||||
return this.pluck('name');
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
<% if (isSearchEnabled) { %>
|
||||
<p class="CDB-Text is-semibold CDB-Size-small u-upperCase js-lockCategories"><%- totalLocked %> selected</p>
|
||||
<% } else { %>
|
||||
<p class="CDB-Text is-semibold CDB-Size-small u-upperCase js-textInfo">
|
||||
<% if (isLocked) { %>
|
||||
<%- totalCats %> blocked <button class="CDB-Text CDB-Size-small u-upperCase u-actionTextColor CDB-Widget-link u-lSpace js-unlock">unlock</button>
|
||||
<% } else { %>
|
||||
<% if (noneSelected) { %>
|
||||
None selected
|
||||
<% } else { %>
|
||||
<%- allSelected ? "All selected" : acceptedCats + " selected" %>
|
||||
<% if (canBeLocked) { %>
|
||||
<button class="CDB-Text CDB-Size-small u-upperCase u-actionTextColor CDB-Widget-link u-lSpace js-lock">lock</button>
|
||||
<% }%>
|
||||
<% }%>
|
||||
<% } %>
|
||||
</p>
|
||||
<% if (canSelectAll) { %>
|
||||
<div class="CDB-Widget-filterButtons">
|
||||
<button class="CDB-Text CDB-Size-small u-upperCase u-actionTextColor CDB-Widget-link CDB-Widget-filterButton js-all">all</button>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
@@ -0,0 +1,75 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var template = require('./options-template.tpl');
|
||||
|
||||
/**
|
||||
* Category filter view
|
||||
*
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
className: 'CDB-Widget-filter CDB-Widget-contentSpaced CDB-Widget-contentSpaced--sideMargins',
|
||||
|
||||
events: {
|
||||
'click .js-all': '_onSelectAll',
|
||||
'click .js-lock': '_lockCategories',
|
||||
'click .js-unlock': '_unlockCategories'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.dataviewModel = this.options.dataviewModel;
|
||||
this.widgetModel = this.options.widgetModel;
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var acceptedCats = this.dataviewModel.filter.acceptedCategories.size();
|
||||
var rejectedCats = this.dataviewModel.filter.rejectedCategories.size();
|
||||
var areAllRejected = this.dataviewModel.filter.areAllRejected();
|
||||
var totalCats = this.dataviewModel.getData().size();
|
||||
var isLocked = this.widgetModel.isLocked();
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
isSearchEnabled: this.widgetModel.isSearchEnabled(),
|
||||
isSearchApplied: this.dataviewModel.isSearchApplied(),
|
||||
isLocked: isLocked,
|
||||
canBeLocked: this.widgetModel.canBeLocked(),
|
||||
allSelected: (rejectedCats === 0 && acceptedCats === 0 && !areAllRejected),
|
||||
canSelectAll: !isLocked && (rejectedCats > 0 || acceptedCats > 0 || areAllRejected),
|
||||
noneSelected: areAllRejected || (!totalCats && !acceptedCats),
|
||||
acceptedCats: acceptedCats,
|
||||
totalLocked: this.widgetModel.lockedCategories.size(),
|
||||
totalCats: totalCats
|
||||
})
|
||||
);
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.widgetModel.bind('change:search change:locked', this.render, this);
|
||||
this.widgetModel.lockedCategories.bind('change add remove', this.render, this);
|
||||
this.add_related_model(this.widgetModel);
|
||||
this.add_related_model(this.widgetModel.lockedCategories);
|
||||
|
||||
this.dataviewModel.bind('change:data', this.render, this);
|
||||
this.add_related_model(this.dataviewModel);
|
||||
|
||||
var f = this.dataviewModel.filter;
|
||||
f.acceptedCategories.bind('add remove reset', this.render, this);
|
||||
f.rejectedCategories.bind('add remove reset', this.render, this);
|
||||
this.add_related_model(f.rejectedCategories);
|
||||
this.add_related_model(f.acceptedCategories);
|
||||
},
|
||||
|
||||
_lockCategories: function () {
|
||||
this.widgetModel.lockCategories();
|
||||
},
|
||||
|
||||
_unlockCategories: function () {
|
||||
this.widgetModel.unlockCategories();
|
||||
},
|
||||
|
||||
_onSelectAll: function () {
|
||||
this.dataviewModel.filter.acceptAll();
|
||||
}
|
||||
|
||||
});
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<section class="CDB-Widget-pagination">
|
||||
<button class="CDB-Widget-paginationButton u-rSpace--m js-prev">
|
||||
<svg width="6px" height="10px" viewBox="0 0 6 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g fill="#1785FB">
|
||||
<path d="M5.09530663,4.3528203 C5.12374481,4.37148693 5.15072296,4.39333837 5.17574487,4.41836028 C5.20217765,4.44479306 5.22503325,4.47380933 5.24426546,4.50470442 C5.4081023,4.70181793 5.39325947,4.99960853 5.21412378,5.17874423 C5.15044967,5.24241833 5.07410755,5.28556158 4.99327465,5.30793729 L0.85159735,9.44961458 C0.653891927,9.64732 0.339500325,9.64512912 0.144238179,9.44986697 C-0.0453133946,9.2603154 -0.0509109683,8.93790934 0.144490568,8.7425078 L4.10763557,4.7793628 L0.182869475,0.85459671 C-0.012532062,0.659195174 -0.00693448836,0.336789113 0.182617085,0.14723754 C0.377879231,-0.0480246061 0.692270834,-0.050215493 0.889976256,0.147489929 L5.09530663,4.3528203 Z" transform="translate(2.679181, 4.798552) scale(-1, 1) translate(-2.679181, -4.798552) "></path>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
<p class="CDB-Text CDB-Size-small is-semibold u-upperCase">
|
||||
<span class="u-mainTextColor"><%- currentPage %></span>
|
||||
<span class="u-altTextColor u-lSpace u-rSpace">of</span>
|
||||
<span class="u-mainTextColor"><%- pages %></span>
|
||||
</p>
|
||||
<button class="CDB-Widget-paginationButton u-lSpace--m js-next">
|
||||
<svg width="6px" height="10px" viewBox="0 0 6 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g fill="#1785FB">
|
||||
<path d="M5.28600662,4.78918286 C5.3144448,4.80784949 5.34142295,4.82970094 5.36644486,4.85472284 C5.39287764,4.88115562 5.41573324,4.91017189 5.43496545,4.94106698 C5.59880229,5.13818049 5.58395946,5.4359711 5.40482376,5.61510679 C5.34114966,5.6787809 5.26480754,5.72192414 5.18397463,5.74429985 L1.04229734,9.88597714 C0.844591917,10.0836826 0.530200314,10.0814917 0.334938168,9.88622953 C0.145386595,9.69667796 0.139789021,9.3742719 0.335190558,9.17887036 L4.29833556,5.21572536 L0.373569464,1.29095927 C0.178167927,1.09555773 0.183765501,0.773151675 0.373317075,0.583600101 C0.56857922,0.388337955 0.882970823,0.386147068 1.08067625,0.58385249 L5.28600662,4.78918286 Z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</section>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<div class="CDB-Widget-contentFlex">
|
||||
<button class="u-rSpace--m CDB-Text is-semibold u-upperCase CDB-Size-small js-searchToggle">
|
||||
<div class="CDB-Shape u-iBlock">
|
||||
<span class="CDB-Shape-magnify is-small is-blue"></span>
|
||||
</div>
|
||||
<span class="u-iBlock u-actionTextColor">
|
||||
search in <%- categoriesCount %> categor<%- categoriesCount === 1 ? 'y' : 'ies' %>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
var $ = require('jquery');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var defaultTemplate = require('./paginator-template.tpl');
|
||||
var paginationTemplate = require('./pagination-template.tpl');
|
||||
|
||||
var MIN_CATEGORIES = 5;
|
||||
var STARTING_PAGE = 1;
|
||||
var PAGINATION_STEP = 1;
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'dataviewModel',
|
||||
'widgetModel',
|
||||
'paginatorModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* Display paginator for category widget
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
options: {
|
||||
itemsPerPage: 6,
|
||||
template: defaultTemplate,
|
||||
paginator: false
|
||||
},
|
||||
|
||||
className: 'CDB-Widget-nav CDB-Widget-contentSpaced',
|
||||
|
||||
events: {
|
||||
'click .js-searchToggle': '_onSearchClicked',
|
||||
'click .js-next': '_onNextPage',
|
||||
'click .js-prev': '_onPrevPage'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._paginatorModel, 'change:page', this.render);
|
||||
|
||||
this.listenTo(this._dataviewModel, 'change:categoriesCount', this.render);
|
||||
this.listenTo(this._dataviewModel, 'change:data change:searchData', this._onDataChanged);
|
||||
|
||||
this.listenTo(this._widgetModel, 'change:search', this.toggle);
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var categoriesCount = this._dataviewModel.getCount();
|
||||
|
||||
if (categoriesCount > MIN_CATEGORIES) {
|
||||
var template = this.options.template;
|
||||
|
||||
this.$el.html(template({
|
||||
categoriesCount: categoriesCount
|
||||
}));
|
||||
|
||||
if (this.options.paginator) {
|
||||
this.$el.append(paginationTemplate({
|
||||
currentPage: this._paginatorModel.get('page'),
|
||||
pages: this._totalPages()
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
this._paginatorModel.set('page', STARTING_PAGE);
|
||||
}
|
||||
},
|
||||
|
||||
// If current page doesn't exist due to a data change, we should reset it
|
||||
_setPage: function () {
|
||||
var pages = this._totalPages();
|
||||
|
||||
if (this._paginatorModel.get('page') > pages || this._paginatorModel.get('page') < STARTING_PAGE) {
|
||||
this._paginatorModel.set({ page: STARTING_PAGE }, { silent: true });
|
||||
}
|
||||
},
|
||||
|
||||
_onSearchClicked: function () {
|
||||
this._widgetModel.setupSearch();
|
||||
},
|
||||
|
||||
_onDataChanged: function () {
|
||||
this._setPage();
|
||||
this.render();
|
||||
},
|
||||
|
||||
_onPrevPage: function () {
|
||||
this._changePage(-PAGINATION_STEP);
|
||||
},
|
||||
|
||||
_onNextPage: function () {
|
||||
this._changePage(PAGINATION_STEP);
|
||||
},
|
||||
|
||||
_changePage: function (step) {
|
||||
var totalPages = this._totalPages();
|
||||
var currentPage = this._paginatorModel.get('page');
|
||||
var nextPage = currentPage + step;
|
||||
|
||||
if (nextPage > totalPages) {
|
||||
nextPage = STARTING_PAGE;
|
||||
}
|
||||
|
||||
if (nextPage < STARTING_PAGE) {
|
||||
nextPage = totalPages;
|
||||
}
|
||||
|
||||
this._paginatorModel.set('page', nextPage);
|
||||
},
|
||||
|
||||
_totalPages: function () {
|
||||
return Math.ceil(this._dataviewModel.getSize() / this.options.itemsPerPage);
|
||||
},
|
||||
|
||||
toggle: function () {
|
||||
this[ this._widgetModel.isSearchEnabled() ? 'hide' : 'show' ]();
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.$el.addClass('is-hidden');
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.$el.removeClass('is-hidden');
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
$(window).unbind('resize.' + this.cid);
|
||||
CoreView.prototype.clean.call(this);
|
||||
}
|
||||
});
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<div class="CDB-Widget-contentFlex u-rSpace--xl">
|
||||
<button class="CDB-Text is-semibold u-upperCase CDB-Size-small u-rSpace--m js-searchToggle u-actionTextColor">cancel</button>
|
||||
</div>
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
var PaginatorView = require('./paginator-view');
|
||||
var searchTemplate = require('./search-paginator-template.tpl');
|
||||
var paginationTemplate = require('./pagination-template.tpl');
|
||||
|
||||
module.exports = PaginatorView.extend({
|
||||
className: 'CDB-Widget-nav is-hidden CDB-Widget-contentSpaced',
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
|
||||
var pages = this._totalPages();
|
||||
|
||||
this.$el.html(searchTemplate());
|
||||
|
||||
if (pages > 1) {
|
||||
this.$el.append(paginationTemplate({
|
||||
currentPage: this._paginatorModel.get('page'),
|
||||
pages: pages
|
||||
}));
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
_totalPages: function () {
|
||||
return Math.ceil(this._dataviewModel.getSearchCount() / this.options.itemsPerPage);
|
||||
},
|
||||
|
||||
toggle: function () {
|
||||
this[ !this._widgetModel.isSearchEnabled() ? 'hide' : 'show' ]();
|
||||
},
|
||||
|
||||
_onSearchClicked: function () {
|
||||
this._widgetModel.cleanSearch();
|
||||
this._widgetModel.toggleSearch();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
<%- value %>
|
||||
@@ -0,0 +1,11 @@
|
||||
<% if (isSearchEnabled) { %>
|
||||
<% if (isSearchApplied) { %>
|
||||
<dt class="CDB-Widget-infoCount"><%- resultsCount %></dt><dd class="CDB-Widget-infoDescription">found</dd>
|
||||
<% } else { %>
|
||||
|
||||
<% } %>
|
||||
</dt>
|
||||
<% } else { %>
|
||||
<dt class="CDB-Widget-infoCount"><%- nullsPer %>%</dt><dd class="CDB-Widget-infoDescription"> null rows</dd>
|
||||
<dt class="CDB-Widget-infoCount"><span class="js-cats"><%- catsPer %></span>%</dt><dd class="CDB-Widget-infoDescription">of total</dd>
|
||||
<% } %>
|
||||
@@ -0,0 +1,103 @@
|
||||
var _ = require('underscore');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var formatter = require('../../../formatter');
|
||||
var template = require('./stats-template.tpl');
|
||||
var animationTemplate = require('./cats-template.tpl');
|
||||
var AnimateValues = require('../../animate-values');
|
||||
|
||||
/**
|
||||
* Category stats info view
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
className: 'CDB-Widget-info CDB-Text CDB-Size-small u-secondaryTextColor u-upperCase u-tSpace',
|
||||
tagName: 'dl',
|
||||
|
||||
initialize: function () {
|
||||
this.widgetModel = this.options.widgetModel;
|
||||
this.dataviewModel = this.options.dataviewModel;
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(
|
||||
template({
|
||||
isSearchEnabled: this.widgetModel.isSearchEnabled(),
|
||||
isSearchApplied: this.dataviewModel.isSearchApplied(),
|
||||
isLocked: this.widgetModel.isLocked(),
|
||||
isOtherAvailable: this.dataviewModel.isOtherAvailable(),
|
||||
resultsCount: this.dataviewModel.getSearchCount(),
|
||||
totalCats: this._getCategoriesSize(),
|
||||
nullsPer: this._getNullPercentage(),
|
||||
catsPer: this._getCurrentCategoriesPercentage()
|
||||
})
|
||||
);
|
||||
|
||||
var animator = new AnimateValues({
|
||||
el: this.$el
|
||||
});
|
||||
|
||||
animator.animateFromValues(this._getPreviousCategoriesPercentage(), this._getCurrentCategoriesPercentage(), '.js-cats',
|
||||
animationTemplate, { defaultValue: '-', animationSpeed: 700, formatter: formatter.formatValue }
|
||||
);
|
||||
|
||||
this._checkVisibility();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.dataviewModel.bind('change:data change:totalCount', this.render, this);
|
||||
this.widgetModel.bind('change:search change:locked', this.render, this);
|
||||
this.widgetModel.bind('change:show_stats change:collapsed', this._checkVisibility, this);
|
||||
this.add_related_model(this.dataviewModel);
|
||||
this.add_related_model(this.widgetModel);
|
||||
},
|
||||
|
||||
_getNullPercentage: function () {
|
||||
var nulls = this.dataviewModel.get('nulls');
|
||||
var total = this.dataviewModel.get('totalCount') || 0;
|
||||
return !nulls ? 0 : ((nulls / total) * 100).toFixed(2);
|
||||
},
|
||||
|
||||
_getPreviousCategoriesPercentage: function () {
|
||||
var total = this.dataviewModel.previous('totalCount') || 0;
|
||||
var data = this.dataviewModel.getPreviousData();
|
||||
return this._getCategoriesPercentage(data, total);
|
||||
},
|
||||
|
||||
_getCurrentCategoriesPercentage: function () {
|
||||
var total = this.dataviewModel.get('totalCount') || 0;
|
||||
var data = this.dataviewModel.getData().toJSON();
|
||||
return this._getCategoriesPercentage(data, total);
|
||||
},
|
||||
|
||||
_getCategoriesPercentage: function (data, total) {
|
||||
if (!total) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var currentTotal = data.reduce(function (memo, mdl) {
|
||||
return !mdl.agg ? (memo + parseFloat(mdl.value)) : memo;
|
||||
}, 0);
|
||||
|
||||
if (!currentTotal) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ((currentTotal / total) * 100).toFixed(2);
|
||||
},
|
||||
|
||||
_getCategoriesSize: function () {
|
||||
return _.pluck(
|
||||
this.dataviewModel.getData().reject(function (mdl) {
|
||||
return mdl.get('agg');
|
||||
}), 'name').length;
|
||||
},
|
||||
|
||||
_checkVisibility: function () {
|
||||
var isVisible = !!this.widgetModel.get('show_stats') && !this.widgetModel.get('collapsed');
|
||||
this.$el.toggle(isVisible);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
<% if (isSearchEnabled) { %>
|
||||
<form class="CDB-Widget-search js-form">
|
||||
<div class="CDB-Shape CDB-Widget-searchLens u-iBlock u-rSpace js-searchIcon">
|
||||
<span class="CDB-Shape-magnify is-small is-blue"></span>
|
||||
</div>
|
||||
<input type="text" class="CDB-Text CDB-Size-large CDB-Widget-textInput CDB-Widget-searchTextInput js-textInput" value="<%- q %>" placeholder="Search by <%- columnName %>"/>
|
||||
<% if (canShowApply) { %>
|
||||
<button type="button" class="CDB-Text is-semibold u-upperCase CDB-Size-small CDB-Widget-searchApply js-applyLocked u-actionTextColor">apply</button>
|
||||
<% } %>
|
||||
</form>
|
||||
<% } else { %>
|
||||
<div class="CDB-Widget-title CDB-Widget-contentSpaced js-title">
|
||||
<h3 class="CDB-Text CDB-Size-large u-ellipsis js-titleText" title="<%- title %>"><%- title %></h3>
|
||||
<div class="CDB-Widget-options CDB-Widget-contentSpaced">
|
||||
<% if (isAutoStyleEnabled) { %>
|
||||
<button class="CDB-Widget-buttonIcon CDB-Widget-buttonIcon--circle js-colors
|
||||
<%- isAutoStyle ? 'is-selected' : '' %>
|
||||
<%- isAutoStyle ? 'js-cancelAutoStyle' : 'js-autoStyle' %>
|
||||
" data-tooltip="
|
||||
<%- isAutoStyle ? 'Remove Auto style' : 'Apply Auto Style' %>
|
||||
">
|
||||
<i class="CDB-IconFont CDB-IconFont-drop CDB-IconFont--small CDB-IconFont--top"></i>
|
||||
</button>
|
||||
<% } %>
|
||||
<button class="CDB-Shape CDB-Widget-actions js-actions u-lSpace" data-tooltip="More options">
|
||||
<div class="CDB-Shape-threePoints is-blue is-small">
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<% if (showSource) { %>
|
||||
<dl class="CDB-Widget-info u-tSpace">
|
||||
<div class="u-flex u-alignCenter u-ellipsis">
|
||||
<span class="CDB-Text CDB-Size-small is-semibold u-upperCase" style="color: <%- sourceColor %>;">
|
||||
<%- sourceId %>
|
||||
</span>
|
||||
|
||||
<% if (!isSourceType) { %>
|
||||
<span class="CDB-Text CDB-Size-small u-lSpace--s u-flex" style="color: <%- sourceColor %>;">
|
||||
<i class="CDB-IconFont CDB-IconFont-ray CDB-Size-small"></i>
|
||||
</span>
|
||||
<% } %>
|
||||
|
||||
<span class="CDB-Text CDB-Size-small u-mainTextColor u-lSpace">
|
||||
<%= sourceType %>
|
||||
</span>
|
||||
|
||||
<span class="CDB-Text CDB-Size-small u-altTextColor u-ellipsis u-lSpace" title="<%= layerName %>">
|
||||
<%= layerName %>
|
||||
</span>
|
||||
</div>
|
||||
</dl>
|
||||
<% } %>
|
||||
<% } %>
|
||||
@@ -0,0 +1,186 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var DropdownView = require('../../dropdown/widget-dropdown-view');
|
||||
var TipsyTooltipView = require('../../../../builder/components/tipsy-tooltip-view');
|
||||
var template = require('./search-title-template.tpl');
|
||||
var layerColors = require('../../../util/layer-colors');
|
||||
var analyses = require('../../../data/analyses');
|
||||
var escapeHTML = require('../../../util/escape-html');
|
||||
|
||||
/**
|
||||
* Show category title or search any category
|
||||
* + another options for this widget, as in,
|
||||
* colorize categories, lock defined categories...
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
events: {
|
||||
'keyup .js-textInput': '_onKeyupInput',
|
||||
'submit .js-form': '_onSubmitForm',
|
||||
'click .js-applyLocked': '_applyLocked',
|
||||
'click .js-autoStyle': '_autoStyle',
|
||||
'click .js-cancelAutoStyle': '_cancelAutoStyle'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
if (!this.options.widgetModel) throw new Error('widgetModel is required');
|
||||
if (!this.options.dataviewModel) throw new Error('dataviewModel is required');
|
||||
if (!this.options.layerModel) throw new Error('layerModel is required');
|
||||
|
||||
this.model = this.options.widgetModel;
|
||||
this.dataviewModel = this.options.dataviewModel;
|
||||
this.layerModel = this.options.layerModel;
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
|
||||
var sourceId = this.dataviewModel.get('source').id;
|
||||
var letter = layerColors.letter(sourceId);
|
||||
var sourceColor = layerColors.getColorForLetter(letter);
|
||||
var sourceType = this.dataviewModel.getSourceType() || '';
|
||||
var isSourceType = this.dataviewModel.isSourceType();
|
||||
var layerName = isSourceType
|
||||
? this.model.get('table_name')
|
||||
: this.layerModel.get('layer_name');
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
isCollapsed: this.model.get('collapsed'),
|
||||
isAutoStyleEnabled: this._isAutoStyleButtonVisible(),
|
||||
isAutoStyle: this.model.isAutoStyle(),
|
||||
title: this.model.get('title'),
|
||||
sourceId: sourceId,
|
||||
sourceType: analyses.title(sourceType),
|
||||
isSourceType: isSourceType,
|
||||
showSource: this.model.get('show_source') && letter !== '',
|
||||
sourceColor: sourceColor,
|
||||
layerName: escapeHTML(layerName),
|
||||
columnName: this.dataviewModel.get('column'),
|
||||
q: this.dataviewModel.getSearchQuery(),
|
||||
isLocked: this.model.isLocked(),
|
||||
canBeLocked: this.model.canBeLocked(),
|
||||
isSearchEnabled: this.model.isSearchEnabled(),
|
||||
canShowApply: this.model.canApplyLocked()
|
||||
})
|
||||
);
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this.model, 'change:search', this._onSearchToggled);
|
||||
this.listenTo(this.model, 'change:title change:collapsed change:autoStyle change:style', this.render);
|
||||
this.listenTo(this.model.lockedCategories, 'change add remove', this.render);
|
||||
this.listenTo(this.dataviewModel, 'change:column', this.render);
|
||||
this.listenTo(this.dataviewModel.filter, 'change', this.render);
|
||||
this.listenTo(this.layerModel, 'change:visible change:cartocss change:layer_name', this.render);
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var dropdown = new DropdownView({
|
||||
model: this.model,
|
||||
target: '.js-actions',
|
||||
container: this.$el
|
||||
});
|
||||
this.addView(dropdown);
|
||||
|
||||
var colorsTooltip = new TipsyTooltipView({
|
||||
el: this.$el.find('.js-colors'),
|
||||
gravity: 'auto'
|
||||
});
|
||||
this.addView(colorsTooltip);
|
||||
|
||||
var actionsTooltip = new TipsyTooltipView({
|
||||
el: this.$el.find('.js-actions'),
|
||||
gravity: 'auto'
|
||||
});
|
||||
this.addView(actionsTooltip);
|
||||
},
|
||||
|
||||
_isAutoStyleButtonVisible: function () {
|
||||
return this.model.isAutoStyleEnabled() &&
|
||||
this.layerModel.get('visible') &&
|
||||
this.model.hasColorsAutoStyle();
|
||||
},
|
||||
|
||||
_onSearchToggled: function () {
|
||||
var isSearchEnabled = this.model.isSearchEnabled();
|
||||
this[isSearchEnabled ? '_bindESC' : '_unbindESC']();
|
||||
this.render();
|
||||
if (isSearchEnabled) {
|
||||
this._focusOnInput();
|
||||
}
|
||||
},
|
||||
|
||||
_onSubmitForm: function (ev) {
|
||||
if (ev) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
var q = this.$('.js-textInput').val();
|
||||
if (this.dataviewModel.getSearchQuery() !== q) {
|
||||
this.dataviewModel.setSearchQuery(q);
|
||||
if (this.dataviewModel.isSearchValid()) {
|
||||
this.dataviewModel.applySearch();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_focusOnInput: function () {
|
||||
var self = this;
|
||||
setTimeout(function () {
|
||||
self.$('.js-textInput').focus();
|
||||
}, 0);
|
||||
},
|
||||
|
||||
_onKeyupInput: _.debounce(
|
||||
function (ev) {
|
||||
var q = this.$('.js-textInput').val();
|
||||
if (ev.keyCode !== 13 && ev.keyCode !== 27 && q !== '') {
|
||||
this._onSubmitForm();
|
||||
}
|
||||
}, 250
|
||||
),
|
||||
|
||||
_bindESC: function () {
|
||||
$(document).bind('keyup.' + this.cid, _.bind(this._onKeyUp, this));
|
||||
},
|
||||
|
||||
_unbindESC: function () {
|
||||
$(document).unbind('keyup.' + this.cid);
|
||||
},
|
||||
|
||||
_onKeyUp: function (ev) {
|
||||
if (ev.keyCode === 27) {
|
||||
this._cancelSearch();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_applyLocked: function () {
|
||||
this.model.toggleSearch();
|
||||
this.model.applyLocked();
|
||||
},
|
||||
|
||||
_autoStyle: function () {
|
||||
this.model.autoStyle();
|
||||
},
|
||||
|
||||
_cancelAutoStyle: function () {
|
||||
this.model.cancelAutoStyle();
|
||||
},
|
||||
|
||||
_cancelSearch: function () {
|
||||
this.model.cleanSearch();
|
||||
this.model.disableSearch();
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._unbindESC();
|
||||
CoreView.prototype.clean.call(this);
|
||||
}
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user