Initial commit
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
Mode: {
|
||||
NESTED: 'nested',
|
||||
FLOAT: 'float',
|
||||
DEFAULT: 'nested'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
module.exports = {
|
||||
Size: {
|
||||
MARKER_MIN: 7,
|
||||
IMAGE_MIN: 20,
|
||||
DEFAULT: {
|
||||
min: 1,
|
||||
max: 45,
|
||||
step: 0.5
|
||||
},
|
||||
DefaultInput100: {
|
||||
MIN: 0,
|
||||
MAX: 100,
|
||||
STEP: 1
|
||||
},
|
||||
DEFAULT_RANGE: [ 5, 20 ]
|
||||
},
|
||||
|
||||
Panes: {
|
||||
FIXED: 'fixed',
|
||||
BY_VALUE: 'value',
|
||||
FILE: 'file'
|
||||
},
|
||||
|
||||
Tabs: {
|
||||
BINS: 'bins',
|
||||
QUANTIFICATION: 'quantification'
|
||||
},
|
||||
|
||||
Quantification: {
|
||||
REFERENCE: {
|
||||
'Jenks': 'jenks',
|
||||
'Equal Interval': 'equal',
|
||||
'Heads/Tails': 'headtails',
|
||||
'Quantile': 'quantiles'
|
||||
}
|
||||
},
|
||||
|
||||
Settings: {
|
||||
COLOR: {
|
||||
quantifications: {
|
||||
items: ['jenks', 'equal', 'headtails', 'quantiles', 'category'],
|
||||
defaultIndex: 0
|
||||
}
|
||||
},
|
||||
|
||||
COLOR_RAMPS: {
|
||||
quantifications: {
|
||||
items: ['quantiles', 'jenks', 'equal', 'headtails', 'category'],
|
||||
defaultIndex: 0
|
||||
},
|
||||
bins: {
|
||||
items: ['2', '3', '4', '5', '6', '7'],
|
||||
defaultIndex: 3
|
||||
}
|
||||
},
|
||||
|
||||
NUMBER: {
|
||||
quantifications: {
|
||||
items: ['quantiles', 'jenks', 'equal', 'headtails'],
|
||||
defaultIndex: 0
|
||||
},
|
||||
bins: {
|
||||
items: ['2', '3', '4', '5', '6', '7'],
|
||||
defaultIndex: 3
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
module.exports = {
|
||||
Type: {
|
||||
SIMPLE: 'simple',
|
||||
ANIMATION: 'animation',
|
||||
HEATMAP: 'heatmap',
|
||||
POLYGON: 'polygon',
|
||||
REGIONS: 'regions',
|
||||
HEXABINS: 'hexabins',
|
||||
SQUARES: 'squares',
|
||||
NONE: 'none'
|
||||
},
|
||||
Blending: {
|
||||
SIMPLE: [
|
||||
'none',
|
||||
'multiply',
|
||||
'screen',
|
||||
'overlay',
|
||||
'darken',
|
||||
'lighten',
|
||||
'color-dodge',
|
||||
'color-burn',
|
||||
'xor',
|
||||
'src-over'
|
||||
],
|
||||
ANIMATION: [
|
||||
'lighter',
|
||||
'multiply',
|
||||
'source-over',
|
||||
'xor'
|
||||
]
|
||||
}
|
||||
};
|
||||
62
lib/assets/javascripts/builder/components/form-components/editors/base.js
Executable file
62
lib/assets/javascripts/builder/components/form-components/editors/base.js
Executable file
@@ -0,0 +1,62 @@
|
||||
var Backbone = require('backbone');
|
||||
var $ = require('jquery');
|
||||
var ESC_KEY_CODE = 27;
|
||||
|
||||
Backbone.Form.editors.Base = Backbone.Form.editors.Base.extend({
|
||||
applyESCBind: function (callback) {
|
||||
this._ESCBindCallback = callback;
|
||||
this._onKeyDown = this._onKeyDown.bind(this);
|
||||
document.addEventListener('keydown', this._onKeyDown);
|
||||
},
|
||||
|
||||
_onKeyDown: function (ev) {
|
||||
var anyModalOpen;
|
||||
if (ev.which === ESC_KEY_CODE) {
|
||||
anyModalOpen = this._anyModalOpen();
|
||||
!anyModalOpen && this._ESCBindCallback();
|
||||
}
|
||||
},
|
||||
|
||||
applyClickOutsideBind: function (callback) {
|
||||
this._clickBindCallback = callback;
|
||||
this._onDocumentClick = this._onDocumentClick.bind(this);
|
||||
this.$el.attr('data-cid', this.cid);
|
||||
document.addEventListener('click', this._onDocumentClick);
|
||||
},
|
||||
|
||||
_onDocumentClick: function (e) {
|
||||
var $el = $(e.target);
|
||||
var anyModalOpen = this._anyModalOpen();
|
||||
if ($el.closest('[data-cid="' + this.cid + '"]').length === 0 && !anyModalOpen) {
|
||||
this._clickBindCallback();
|
||||
}
|
||||
},
|
||||
|
||||
_anyModalOpen: function () {
|
||||
var modals = this.options.modals;
|
||||
if (!modals) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return modals.isOpen();
|
||||
},
|
||||
|
||||
_destroyBinds: function () {
|
||||
this._removeESCandClickListeners();
|
||||
},
|
||||
|
||||
_removeESCandClickListeners: function () {
|
||||
if (this._ESCBindCallback) {
|
||||
document.removeEventListener('keydown', this._onKeyDown);
|
||||
}
|
||||
if (this._clickBindCallback) {
|
||||
document.removeEventListener('click', this._onDocumentClick);
|
||||
}
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._destroyBinds && this._destroyBinds();
|
||||
Backbone.View.prototype.remove.call(this);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
var $ = require('jquery');
|
||||
var Backbone = require('backbone');
|
||||
var FactoryHints = require('builder/editor/editor-hints/factory-hints');
|
||||
var CodeMirrorView = require('builder/components/code-mirror/code-mirror-view');
|
||||
|
||||
Backbone.Form.editors.CodeEditor = Backbone.Form.editors.TextArea.extend({
|
||||
render: function () {
|
||||
this.setValue(this.value);
|
||||
|
||||
this._codemirrorModel = new Backbone.Model({
|
||||
content: this.value,
|
||||
readonly: false,
|
||||
lineNumbers: false
|
||||
});
|
||||
|
||||
FactoryHints.init({
|
||||
tokens: this.options.tokens,
|
||||
tableName: false,
|
||||
columnsName: false
|
||||
});
|
||||
|
||||
this._initViews();
|
||||
|
||||
this._toggleDisableState();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var val = this.$el.val();
|
||||
|
||||
val = this.codeMirrorView.getContent();
|
||||
|
||||
return (val === '') ? null : val;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
this._destroyEditor();
|
||||
|
||||
var hints = FactoryHints.reset().hints;
|
||||
this.codeMirrorView = new CodeMirrorView({
|
||||
model: this._codemirrorModel,
|
||||
hints: hints,
|
||||
mode: 'text/mustache',
|
||||
autocompleteChars: 2,
|
||||
autocompletePrefix: '{{',
|
||||
autocompleteSuffix: '}}',
|
||||
placeholder: this.options.placeholder
|
||||
});
|
||||
this.codeMirrorView.bind('codeChanged', function () {
|
||||
this.trigger('change', this.codeMirrorView.getContent());
|
||||
}, this);
|
||||
|
||||
var $codeMirrorEl = $(this.codeMirrorView.render().el);
|
||||
this.$el.replaceWith($codeMirrorEl);
|
||||
this.setElement($codeMirrorEl);
|
||||
this.$el.addClass('CodeMirror-formInput');
|
||||
// The default el is replace it with another dom node
|
||||
// we should add tracking class manually again
|
||||
this._addTrackingClass();
|
||||
},
|
||||
|
||||
_addTrackingClass: function () {
|
||||
if (this.options.trackingClass) {
|
||||
var trackClasses = this.options.trackingClass + ' track-' + this.options.key + this.options.editorType;
|
||||
this.$el.addClass(trackClasses);
|
||||
}
|
||||
},
|
||||
|
||||
_hasEditor: function () {
|
||||
return this.options.editor && !!this.codeMirrorView;
|
||||
},
|
||||
|
||||
_destroyEditor: function () {
|
||||
if (this._hasEditor()) {
|
||||
this.codeMirrorView.remove();
|
||||
}
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._destroyEditor();
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this.$el.remove();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,183 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var StackLayoutView = require('builder/components/stack-layout/stack-layout-view');
|
||||
var MeasurementsView = require('./measurements-list-view');
|
||||
var FiltersView = require('./filters-list-view');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'filtersCollection',
|
||||
'measurementsCollection',
|
||||
'measurementModel',
|
||||
'region'
|
||||
];
|
||||
|
||||
var braces = function (value) {
|
||||
var template = _.template("'{<%- value %>}'");
|
||||
return template({
|
||||
value: value
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
className: 'CDB-Box-modal CustomList',
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
// For internal state
|
||||
this.model = new Backbone.Model({
|
||||
visible: false
|
||||
});
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this._generateStackLayoutView();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this.model, 'change:visible', function (mdl, isVisible) {
|
||||
isVisible ? this.render() : this.clearSubViews();
|
||||
this._toggleVisibility();
|
||||
});
|
||||
},
|
||||
|
||||
_generateStackLayoutView: function () {
|
||||
var createListView = function (stackLayoutModel, opts) {
|
||||
return this._createListView(stackLayoutModel, opts);
|
||||
}.bind(this);
|
||||
|
||||
var createFilterView = function (stackLayoutModel, opts) {
|
||||
return this._createFilterView(stackLayoutModel, opts);
|
||||
}.bind(this);
|
||||
|
||||
var stackViewCollection = new Backbone.Collection([{
|
||||
createStackView: createListView
|
||||
}, {
|
||||
createStackView: createFilterView
|
||||
}]);
|
||||
|
||||
this._stackLayoutView = new StackLayoutView({ collection: stackViewCollection });
|
||||
this.addView(this._stackLayoutView);
|
||||
this.$el.append(this._stackLayoutView.render().$el);
|
||||
},
|
||||
|
||||
_buildFilters: function () {
|
||||
var selectedFilters = this._filtersCollection.getSelected();
|
||||
return _.map(selectedFilters, function (filter) {
|
||||
return filter.getValue();
|
||||
}).join(', ');
|
||||
},
|
||||
|
||||
_fetchMeasurements: function () {
|
||||
var region = this._region;
|
||||
var fetchOptions = {
|
||||
region: region && braces(region)
|
||||
};
|
||||
|
||||
return this._measurementsCollection.fetch(fetchOptions);
|
||||
},
|
||||
|
||||
_fetchMeasurementsWithFilter: function () {
|
||||
var region = this._region;
|
||||
var filters = this._buildFilters();
|
||||
|
||||
var fetchOptions = {
|
||||
filters: filters && braces(filters),
|
||||
region: region && braces(region)
|
||||
};
|
||||
|
||||
return this._measurementsCollection.fetch(fetchOptions);
|
||||
},
|
||||
|
||||
_searchMeasurements: function (keyword) {
|
||||
var region = this._region;
|
||||
var filters = this._buildFilters();
|
||||
|
||||
var fetchOptions = {
|
||||
filters: filters && braces(filters),
|
||||
region: region && braces(region),
|
||||
search: keyword,
|
||||
abortable: true
|
||||
};
|
||||
|
||||
return this._measurementsCollection.fetch(fetchOptions);
|
||||
},
|
||||
|
||||
_fetchFilters: function () {
|
||||
var region = this._region;
|
||||
var fetchOptions = {
|
||||
region: region && braces(region)
|
||||
};
|
||||
|
||||
return this._filtersCollection.fetch(fetchOptions);
|
||||
},
|
||||
|
||||
_fetchCollections: function () {
|
||||
var selectedFilters = this._filtersCollection.getSelected();
|
||||
|
||||
if (selectedFilters.length > 0) {
|
||||
this._fetchMeasurementsWithFilter();
|
||||
} else {
|
||||
this._fetchMeasurements();
|
||||
}
|
||||
},
|
||||
|
||||
_createListView: function (stackLayoutModel, opts) {
|
||||
this._fetchCollections();
|
||||
|
||||
var view = new MeasurementsView({
|
||||
filtersCollection: this._filtersCollection,
|
||||
measurementsCollection: this._measurementsCollection,
|
||||
searchMeasurements: this._searchMeasurements.bind(this),
|
||||
measurementModel: this._measurementModel
|
||||
});
|
||||
|
||||
view.bind('filters', function () {
|
||||
stackLayoutModel.nextStep();
|
||||
}, this);
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
_createFilterView: function (stackLayoutModel, opts) {
|
||||
// FIXME
|
||||
// we could save this request, it only depends on region
|
||||
if (this._filtersCollection.size() === 0) {
|
||||
this._fetchFilters();
|
||||
}
|
||||
|
||||
var view = new FiltersView({
|
||||
filtersCollection: this._filtersCollection
|
||||
});
|
||||
|
||||
view.bind('back', function () {
|
||||
this._measurementsCollection.trigger('maybeFiltersUpdated');
|
||||
stackLayoutModel.prevStep();
|
||||
}, this);
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.model.set('visible', false);
|
||||
},
|
||||
|
||||
toggle: function () {
|
||||
this.model.set('visible', !this.model.get('visible'));
|
||||
},
|
||||
|
||||
isVisible: function () {
|
||||
return this.model.get('visible');
|
||||
},
|
||||
|
||||
_toggleVisibility: function () {
|
||||
this.$el.toggleClass('is-visible', !!this.isVisible());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
<% if (typeof isLoading != 'undefined' && isLoading) { %>
|
||||
<div class="u-flex">
|
||||
<div class="CDB-LoaderIcon CDB-LoaderIcon--small is-dark">
|
||||
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
|
||||
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="u-lSpace u-secondaryTextColor"><%- _t('components.backbone-forms.select.loading') %></span>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<%- label %>
|
||||
<% } %>
|
||||
@@ -0,0 +1,241 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var template = require('./data-observatory-measurements.tpl');
|
||||
var selectedItemTemplate = require('./data-observatory-measurement-item.tpl');
|
||||
var DropdownDialogView = require('./data-observatory-dropdown-measurements-view');
|
||||
var PopupManager = require('builder/components/popup-manager');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var MeasurementsCollection = require('builder/data/data-observatory/measurements-collection');
|
||||
var FiltersCollection = require('builder/data/data-observatory/filters-collection');
|
||||
|
||||
var ENTER_KEY_CODE = 13;
|
||||
var STATE = {
|
||||
idle: 'idle',
|
||||
loading: 'loading',
|
||||
fetching: 'fetching',
|
||||
fetched: 'fetched',
|
||||
error: 'error'
|
||||
};
|
||||
var MEASUREMENT_ATTRIBUTES = ['aggregate', 'type', 'label', 'val', 'description', 'filter', 'license'];
|
||||
|
||||
Backbone.Form.editors.DataObservatoryDropdown = Backbone.Form.editors.Base.extend({
|
||||
|
||||
tagName: 'div',
|
||||
className: 'u-ellipsis Editor-formSelect',
|
||||
|
||||
events: {
|
||||
'click .js-button': '_onButtonClick',
|
||||
'keydown .js-button': '_onButtonKeyDown',
|
||||
'focus .js-button': function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
'blur': function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
options: {
|
||||
selectedItemTemplate: selectedItemTemplate
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
this.template = opts.template || template;
|
||||
this.dialogMode = this.options.dialogMode || DialogConstants.Mode.DEFAULT;
|
||||
|
||||
this.measurementModel = this.options.measurementModel;
|
||||
|
||||
var fetchOptions = {
|
||||
configModel: this.options.configModel,
|
||||
nodeDefModel: this.options.nodeDefModel
|
||||
};
|
||||
|
||||
this.measurementsCollection = new MeasurementsCollection([], fetchOptions);
|
||||
this.filtersCollection = new FiltersCollection([], fetchOptions);
|
||||
|
||||
this._initBinds();
|
||||
|
||||
this._dialogView = new DropdownDialogView({
|
||||
configModel: this.options.configModel,
|
||||
nodeDefModel: this.options.nodeDefModel,
|
||||
measurementsCollection: this.measurementsCollection,
|
||||
filtersCollection: this.filtersCollection,
|
||||
measurementModel: this.measurementModel,
|
||||
region: this.options.region
|
||||
});
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var isLoading = this._isLoading();
|
||||
var isDisabled = this.options.disabled;
|
||||
var item = this.measurementModel;
|
||||
var placeholder = this._getPlaceholder();
|
||||
var isNull = this._hasValue();
|
||||
var label = isNull ? placeholder : item.getName();
|
||||
var title = item.getName() || '';
|
||||
|
||||
this.$el.html(
|
||||
this.template({
|
||||
title: title,
|
||||
label: label,
|
||||
keyAttr: this.options.keyAttr,
|
||||
isDisabled: isDisabled,
|
||||
isLoading: isLoading,
|
||||
isNull: isNull
|
||||
})
|
||||
);
|
||||
|
||||
this._popupManager = new PopupManager(this.cid, this.$el, this._dialogView.$el);
|
||||
this._popupManager.append(this.dialogMode);
|
||||
|
||||
if (item) {
|
||||
this._renderLicense(item);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var hide = function () {
|
||||
this._dialogView.hide();
|
||||
this._popupManager && this._popupManager.untrack();
|
||||
}.bind(this);
|
||||
|
||||
this.applyESCBind(hide);
|
||||
this.applyClickOutsideBind(hide);
|
||||
|
||||
this.listenTo(this.measurementsCollection, 'change:selected', this._onItemSelected);
|
||||
},
|
||||
|
||||
_destroyBinds: function () {
|
||||
this.stopListening(this.measurementsCollection);
|
||||
Backbone.Form.editors.Base.prototype._destroyBinds.call(this);
|
||||
},
|
||||
|
||||
_getPlaceholder: function (isDisabled) {
|
||||
var keyAttr = this.options.keyAttr;
|
||||
var placeholder = this.options.placeholder || _t('components.backbone-forms.select.placeholder', { keyAttr: keyAttr });
|
||||
return placeholder;
|
||||
},
|
||||
|
||||
_hasValue: function () {
|
||||
var name = this.measurementModel.getValue();
|
||||
return name == null || name === '';
|
||||
},
|
||||
|
||||
_isLoading: function () {
|
||||
var state = this.measurementModel.getState();
|
||||
return state === STATE.fetching;
|
||||
},
|
||||
|
||||
_onItemSelected: function (mdl) {
|
||||
var selected = this.measurementsCollection.getSelectedItem();
|
||||
|
||||
if (selected) {
|
||||
this.measurementModel.clear({silent: true});
|
||||
this.measurementModel.set(_.pick(selected.attributes, MEASUREMENT_ATTRIBUTES));
|
||||
}
|
||||
|
||||
this._dialogView.hide();
|
||||
this._popupManager.untrack();
|
||||
this._renderLicense(selected);
|
||||
this._renderButton(selected).focus();
|
||||
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_onButtonClick: function () {
|
||||
this._dialogView.toggle();
|
||||
this._dialogView.isVisible() ? this._popupManager.track() : this._popupManager.untrack();
|
||||
},
|
||||
|
||||
_onButtonKeyDown: function (ev) {
|
||||
if (ev.which === ENTER_KEY_CODE) {
|
||||
ev.preventDefault();
|
||||
if (!this._dialogView.isVisible()) {
|
||||
ev.stopPropagation();
|
||||
this._onButtonClick();
|
||||
} else {
|
||||
this._popupManager.track();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
validate: function () {
|
||||
var value = this.getValue();
|
||||
var validators = this.schema.validators;
|
||||
var getValidator = this.getValidator;
|
||||
|
||||
if (!validators) return null;
|
||||
|
||||
// Run through validators until an error is found
|
||||
var error = null;
|
||||
_.every(validators, function (validator) {
|
||||
error = getValidator(validator)(value, {});
|
||||
|
||||
return !!error;
|
||||
});
|
||||
|
||||
// Return error to be aggregated by list
|
||||
return error ? error : null; // eslint-disable-line
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
this.$('.js-button').focus();
|
||||
},
|
||||
|
||||
blur: function () {
|
||||
this.$('.js-button').blur();
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var item = this.measurementModel;
|
||||
if (item) {
|
||||
return item.getValue();
|
||||
} else if (this.value) {
|
||||
return this.value;
|
||||
}
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
var selectedModel = this.measurementModel;
|
||||
if (selectedModel) {
|
||||
this._renderButton(selectedModel);
|
||||
}
|
||||
this.value = value;
|
||||
},
|
||||
|
||||
_renderButton: function (mdl) {
|
||||
var button = this.$('.js-button');
|
||||
var label = mdl.getName();
|
||||
var $html = this.options.selectedItemTemplate({
|
||||
label: label
|
||||
});
|
||||
|
||||
button
|
||||
.removeClass('is-empty')
|
||||
.attr('title', label)
|
||||
.html($html);
|
||||
|
||||
return button;
|
||||
},
|
||||
|
||||
_renderLicense: function (mdl) {
|
||||
var license = mdl.get('license');
|
||||
var $license = this.$('.js-license');
|
||||
$license
|
||||
.removeClass('u-isHidden')
|
||||
.find('span')
|
||||
.text(license);
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._popupManager && this._popupManager.destroy();
|
||||
this._dialogView && this._dialogView.clean();
|
||||
this._destroyBinds();
|
||||
Backbone.Form.editors.Base.prototype.remove.call(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
<div class="CDB-InputText CDB-Text is-cursor js-button u-ellipsis
|
||||
<% if (isDisabled) { %> is-disabled <% } %>
|
||||
<% if (!label) { %> is-empty <% } %>
|
||||
<% if (isNull) { %> is-empty <% } %>"
|
||||
tabindex="0"
|
||||
title="<%- title %>">
|
||||
<% if (isLoading) { %>
|
||||
<div class="CDB-LoaderIcon CDB-LoaderIcon--small is-dark u-iBlock">
|
||||
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
|
||||
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="u-lSpace u-secondaryTextColor"><%- _t('components.backbone-forms.select.loading') %></span>
|
||||
<% } else { %>
|
||||
<%- label %>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<div class="js-license CDB-Text CDB-FontSize-small u-altTextColor u-tSpace u-bSpace u-isHidden u-flex">
|
||||
<a href="https://cartodb.github.io/bigmetadata/licenses.html" target="_blank">
|
||||
<span class="u-tSpace u-bSpace DataObservatory-license"></span>
|
||||
</a>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
var _ = require('underscore');
|
||||
var CustomListMultiItemView = require('builder/components/custom-list/custom-list-multi-item-view');
|
||||
|
||||
var NAME = _.template('<%-name %> (<%- items %>)');
|
||||
|
||||
module.exports = CustomListMultiItemView.extend({
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
|
||||
var name = this.model.getName() == null ? 'null' : this.model.getName();
|
||||
name = name.replace(/"/g, '');
|
||||
|
||||
this.$el.append(
|
||||
this.options.template(
|
||||
_.extend({
|
||||
isSelected: this.model.get('selected'),
|
||||
isDisabled: this.model.get('disabled'),
|
||||
name: NAME({
|
||||
name: name,
|
||||
items: this.model.get('items')
|
||||
}),
|
||||
val: this.model.getValue(),
|
||||
description: this.model.get('description')
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
this.$el
|
||||
.attr('data-val', this.model.getValue())
|
||||
.toggleClass('is-disabled', !!this.model.get('disabled'));
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
<button type="button" class="CDB-ListDecoration-itemLink u-actionTextColor
|
||||
<% if (isSelected) { %> is-selected <% } %>
|
||||
" title="<%= name %>">
|
||||
<div class="u-flex">
|
||||
<div class="u-iBlock u-rSpace--m">
|
||||
<input class="CDB-Checkbox js-input" type="checkbox" name="" value="" <% if (isSelected) { %>checked<% } %> <% if (isDisabled) { %>disabled<% } %> />
|
||||
<span class="u-iBlock CDB-Checkbox-face"></span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="u-bSpace"><%= name %></div>
|
||||
<div class="CDB-Size-small u-altTextColor"><%- description %></div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="CDB-Box-modalHeader">
|
||||
<ul class="CDB-Box-modalHeaderItem CDB-Box-modalHeaderItem--block CDB-Box-modalHeaderItem--paddingHorizontal">
|
||||
<li class="CDB-ListDecoration-item CDB-ListDecoration-itemPadding--vertical CDB-Text CDB-Size-medium u-secondaryTextColor">
|
||||
<button class="u-actionTextColor js-back u-rSpace">
|
||||
<i class="CDB-IconFont CDB-IconFont-arrowPrev Size-large"></i>
|
||||
</button>
|
||||
<%- headerTitle%>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="js-content"></div>
|
||||
@@ -0,0 +1,75 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var CustomListView = require('builder/components/custom-list/custom-view');
|
||||
var CustomListItemView = require('./filter-list-item-view');
|
||||
var itemListTemplate = require('./filter-list-item.tpl');
|
||||
var template = require('./filter-list-view.tpl');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var statusTemplate = require('./list-view-states.tpl');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'filtersCollection'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
events: {
|
||||
'click .js-back': '_onClickBack'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this._initBinds();
|
||||
|
||||
this._listView = new CustomListView({
|
||||
className: 'DO-Filters',
|
||||
typeLabel: _t('components.backbone-forms.data-observatory.dropdown.filter.item-label'),
|
||||
showSearch: false,
|
||||
collection: this._filtersCollection,
|
||||
itemTemplate: itemListTemplate,
|
||||
itemView: CustomListItemView
|
||||
});
|
||||
|
||||
this.addView(this._listView);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.append(template({
|
||||
headerTitle: _t('components.backbone-forms.data-observatory.dropdown.filter.header')
|
||||
}));
|
||||
|
||||
this._renderListSection();
|
||||
return this;
|
||||
},
|
||||
|
||||
_renderListSection: function () {
|
||||
var status = this._filtersCollection.stateModel.get('state');
|
||||
if (status === 'fetched') {
|
||||
this.$('.js-content').html(this._listView.render().$el);
|
||||
} else {
|
||||
this._createStatusView(status);
|
||||
}
|
||||
},
|
||||
|
||||
_createStatusView: function (status) {
|
||||
var el = statusTemplate({
|
||||
status: status,
|
||||
type: _t('components.backbone-forms.data-observatory.dropdown.filter.type')
|
||||
});
|
||||
|
||||
this.$('.js-content').html(el);
|
||||
},
|
||||
|
||||
_onClickBack: function (e) {
|
||||
this.killEvent(e);
|
||||
this.trigger('back', this);
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._filtersCollection.stateModel, 'change:state', this._renderListSection, this);
|
||||
this.listenTo(this._filtersCollection, 'change:selected', this._onSelectItem, this);
|
||||
},
|
||||
|
||||
_onSelectItem: function (item) {
|
||||
this.trigger('selectItem', item, this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
<% if (status === 'fetching') { %>
|
||||
<div class="InputColorCategory-loader js-loader">
|
||||
<div class="CDB-LoaderIcon is-dark">
|
||||
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
|
||||
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<% } else if (status === 'error') { %>
|
||||
<div class="u-flex u-alignCenter u-justifyCenter CDB-Text CDB-Size-medium u-bSpace--m u-tSpace--m u-errorTextColor"><%- _t('components.backbone-forms.data-observatory.dropdown.error', {
|
||||
type: type
|
||||
}) %></div>
|
||||
<% } %>
|
||||
@@ -0,0 +1,53 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var NestedForm = require('builder/components/form-components/nested-form-custom');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
|
||||
Backbone.Form.editors.List.MeasurementModel = Backbone.Form.editors.NestedModel.extend({
|
||||
initialize: function (options) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, options);
|
||||
EditorHelpers.setOptions(this, options);
|
||||
|
||||
if (!this.form) throw new Error('Missing required option "form"');
|
||||
if (!options.schema.model) throw new Error('Missing required "schema.model" option for NestedModel editor');
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var data = this.value || {};
|
||||
var NestedModel = this.schema.model;
|
||||
|
||||
// Wrap the data in a model if it isn't already a model instance
|
||||
var modelInstance = (data.constructor === NestedModel) ? data : new NestedModel(data, this.options);
|
||||
|
||||
this.nestedForm = new NestedForm({
|
||||
className: 'Editor-formInner--nested',
|
||||
model: modelInstance,
|
||||
idPrefix: this.cid + '_',
|
||||
fieldTemplate: 'nestedField',
|
||||
template: _.template('<form data-fields="*"></form>'),
|
||||
trackingClass: this.options.trackingClass
|
||||
});
|
||||
|
||||
this._observeFormEvents();
|
||||
|
||||
this.listenTo(this.nestedForm, 'change', this._onChangeForm, this);
|
||||
// Render form
|
||||
this.$el.html(this.nestedForm.render().el);
|
||||
|
||||
if (this.hasFocus) {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_onChangeForm: function () {
|
||||
// To translate values and validation to parent form
|
||||
this.nestedForm.commit();
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this.nestedForm && this.nestedForm.remove();
|
||||
Backbone.Form.editors.NestedModel.prototype.remove.call(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
var _ = require('underscore');
|
||||
var BaseView = require('builder/components/custom-list/custom-list-item-view');
|
||||
|
||||
module.exports = BaseView.extend({
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
this.clearSubViews();
|
||||
|
||||
var name = this.model.getName() == null ? 'null' : this.model.getName();
|
||||
var isSelected = this.model.get('selected');
|
||||
|
||||
this.$el.append(
|
||||
this.options.template(
|
||||
_.extend({
|
||||
isSelected: isSelected,
|
||||
isDisabled: this.model.get('disabled'),
|
||||
name: name,
|
||||
val: this.model.getValue(),
|
||||
description: this.model.get('description')
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
this.$el
|
||||
.attr('data-val', this.model.getValue())
|
||||
.attr('data-selected', isSelected)
|
||||
.toggleClass('is-disabled', !!this.model.get('disabled'));
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_onMouseLeave: function () {
|
||||
var selected = this.$el.attr('data-selected');
|
||||
if (!selected) {
|
||||
this.$el.removeClass('is-highlighted');
|
||||
}
|
||||
},
|
||||
|
||||
_onMouseEnter: function () {
|
||||
var selected = this.$el.attr('data-selected');
|
||||
if (!selected) {
|
||||
this.$el.addClass('is-highlighted');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
<button type="button" class="CDB-ListDecoration-itemLink u-actionTextColor
|
||||
<% if (isSelected) { %> is-selected <% } %>
|
||||
" title="<%- name %>">
|
||||
<div class="u-bSpace"><%- name %></div>
|
||||
<div class="CDB-Size-small u-altTextColor DataObservatory-description"><%- description %></div>
|
||||
</button>
|
||||
@@ -0,0 +1,38 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var template = require('./measurements-count.tpl');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'model'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var query = this.model.get('query');
|
||||
var isFiltering = this.model.get('filtering');
|
||||
var items = this.model.get('items');
|
||||
var count;
|
||||
|
||||
if (query === '' && !isFiltering) {
|
||||
count = _t('analyses.data-observatory-measure.count.suggested');
|
||||
} else {
|
||||
count = _t('analyses.data-observatory-measure.count.search', {
|
||||
items: items
|
||||
});
|
||||
}
|
||||
|
||||
this.$el.html(template({
|
||||
items: count
|
||||
}));
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this.model, 'change:items', this.render);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="DataObservatory-count CDB-Text CDB-Size-small Color-ThirdBackground">
|
||||
<%- items %>
|
||||
</div>
|
||||
@@ -0,0 +1,156 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var CustomListView = require('builder/components/custom-list/custom-view');
|
||||
var CustomListItemView = require('./measurement-list-item-view');
|
||||
var itemListTemplate = require('./measurement-list-item.tpl');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var template = require('./measurements-list.tpl');
|
||||
var SearchMeasurementView = require('./measurements-search-view');
|
||||
var CountMeasurementView = require('./measurements-count-view');
|
||||
var statusTemplate = require('./list-view-states.tpl');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'filtersCollection',
|
||||
'measurementsCollection',
|
||||
'searchMeasurements',
|
||||
'measurementModel'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
options: {
|
||||
maxItems: 100
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this.model = new Backbone.Model({
|
||||
query: '',
|
||||
items: this.options.maxItems,
|
||||
filtered: false
|
||||
});
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this.$el.html(template());
|
||||
this._createSearchView();
|
||||
this._createCountView();
|
||||
this._renderListSection();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._measurementsCollection.stateModel, 'change:state', this._renderListSection);
|
||||
this.listenTo(this.model, 'change:query', this._search);
|
||||
},
|
||||
|
||||
_createListView: function () {
|
||||
if (this._listView) {
|
||||
this._listView.clean();
|
||||
this.removeView(this._listView);
|
||||
}
|
||||
|
||||
if (!this._isSerching()) {
|
||||
this._promoteSelected();
|
||||
}
|
||||
|
||||
this.model.set({
|
||||
items: this._measurementsCollection.size(),
|
||||
filtering: this._isFiltering()
|
||||
});
|
||||
|
||||
this._listView = new CustomListView({
|
||||
typeLabel: _t('components.backbone-forms.data-observatory.dropdown.measurement.type'),
|
||||
showSearch: false,
|
||||
itemView: CustomListItemView,
|
||||
collection: this._measurementsCollection,
|
||||
itemTemplate: itemListTemplate
|
||||
});
|
||||
|
||||
this.addView(this._listView);
|
||||
this._listView.show();
|
||||
this.$('.js-list').html(this._listView.$el);
|
||||
},
|
||||
|
||||
_createStatusView: function (status) {
|
||||
var el = statusTemplate({
|
||||
status: status,
|
||||
type: _t('components.backbone-forms.data-observatory.dropdown.measurement.type')
|
||||
});
|
||||
|
||||
this.$('.js-list').html(el);
|
||||
},
|
||||
|
||||
_renderListSection: function () {
|
||||
var status = this._measurementsCollection.stateModel.get('state');
|
||||
|
||||
if (status === 'fetched') {
|
||||
this._createListView();
|
||||
} else {
|
||||
this._createStatusView(status);
|
||||
}
|
||||
},
|
||||
|
||||
_createSearchView: function () {
|
||||
this._searchView = new SearchMeasurementView({
|
||||
model: this.model,
|
||||
filtersCollection: this._filtersCollection
|
||||
});
|
||||
this.addView(this._searchView);
|
||||
this.$('.js-search').append(this._searchView.render().el);
|
||||
|
||||
this.listenTo(this._searchView, 'filters', this._onClickFilters);
|
||||
},
|
||||
|
||||
_onClickFilters: function (e) {
|
||||
this.killEvent(e);
|
||||
this.trigger('filters');
|
||||
},
|
||||
|
||||
_createCountView: function () {
|
||||
var view = new CountMeasurementView({
|
||||
model: this.model
|
||||
});
|
||||
this.addView(view);
|
||||
this.$('.js-count').append(view.render().el);
|
||||
},
|
||||
|
||||
_search: function () {
|
||||
var keyword = this.model.get('query');
|
||||
|
||||
// This function is passed from the parent
|
||||
this._searchMeasurements(keyword);
|
||||
},
|
||||
|
||||
_isSerching: function () {
|
||||
return !!this.model.get('query');
|
||||
},
|
||||
|
||||
_isFiltering: function () {
|
||||
var selectedFilters = this._filtersCollection.getSelected();
|
||||
return selectedFilters.length > 0;
|
||||
},
|
||||
|
||||
_promoteSelected: function () {
|
||||
var selected = this._measurementModel;
|
||||
if (!selected.getValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hit = this._measurementsCollection.findWhere({val: selected.getValue()});
|
||||
|
||||
if (hit) {
|
||||
this._measurementsCollection.remove(hit);
|
||||
}
|
||||
|
||||
var promoted = _.clone(selected.attributes);
|
||||
promoted.selected = true;
|
||||
|
||||
this._measurementsCollection.add(promoted, {at: 0});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="js-search"></div>
|
||||
<div class="js-count"></div>
|
||||
<div class="js-list"></div>
|
||||
@@ -0,0 +1,67 @@
|
||||
var _ = require('underscore');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var utils = require('builder/helpers/utils');
|
||||
var template = require('./measurements-search.tpl');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'model',
|
||||
'filtersCollection'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
className: 'CDB-Box-modalHeader',
|
||||
|
||||
events: {
|
||||
'input .js-input-search': '_search',
|
||||
'click .js-filters': '_onClickFilters'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var label = this._getSearchLabel();
|
||||
this.$el.html(template({
|
||||
label: label
|
||||
}));
|
||||
|
||||
// Focus the input when rendered
|
||||
setTimeout(function () {
|
||||
this._getInput().focus();
|
||||
}.bind(this), 100);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_getSearchLabel: function () {
|
||||
var selectedFilters = this._filtersCollection.getSelected().length;
|
||||
var label = _t('analyses.data-observatory-measure.filters.label');
|
||||
if (selectedFilters === 1) {
|
||||
label = _t('analyses.data-observatory-measure.filters.applied.single');
|
||||
} else if (selectedFilters > 1) {
|
||||
label = _t('analyses.data-observatory-measure.filters.applied.multiple', {
|
||||
filters: selectedFilters
|
||||
});
|
||||
}
|
||||
|
||||
return label;
|
||||
},
|
||||
|
||||
_onClickFilters: function (e) {
|
||||
this.killEvent(e);
|
||||
this.trigger('filters');
|
||||
},
|
||||
|
||||
_search: _.debounce(function (e) {
|
||||
var query = this._getInput().val();
|
||||
query = query.toLowerCase();
|
||||
query = utils.sanitizeHtml(query);
|
||||
this.model.set('query', query);
|
||||
}, 500),
|
||||
|
||||
_getInput: function () {
|
||||
return this.$('.js-input-search');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="CDB-Box-modalHeaderItem">
|
||||
<div class="u-flex u-grow">
|
||||
<div class="u-flex u-grow">
|
||||
<input type="text" name="text" autocomplete="off" placeholder="<%- _t('analyses.data-observatory-measure.search-by-name') %>" class="CDB-InputTextPlain CDB-Text js-input-search">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button class="CDB-Text CDB-Size-medium u-actionTextColor js-filters u-rSpace--m"><%- label %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,31 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
/**
|
||||
* View model of the fill dialog
|
||||
*/
|
||||
module.exports = Backbone.Model.extend({
|
||||
|
||||
defaults: {
|
||||
show: false
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.set('show', true);
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.set('show', false);
|
||||
},
|
||||
|
||||
isHidden: function () {
|
||||
return !this.get('show');
|
||||
},
|
||||
|
||||
/**
|
||||
* @override {Backbone.Model.prototype.destroy}
|
||||
*/
|
||||
destroy: function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
this.trigger.apply(this, ['destroy'].concat(args));
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var DatetimeEditorView = require('./datetime-editor-view');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
className: 'CDB-Box-modal CustomList CustomList--inputs is-visible has-visibility js-datetimePicker',
|
||||
|
||||
initialize: function () {
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
|
||||
this._datetimeEditorView = new DatetimeEditorView({
|
||||
model: this.model
|
||||
});
|
||||
this.addView(this._datetimeEditorView);
|
||||
this.$el.append(this._datetimeEditorView.render().el);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this.model, 'change:show', this._onShowChange);
|
||||
this.listenTo(this.model, 'destroy', this._onDestroy);
|
||||
},
|
||||
|
||||
_onShowChange: function (mdl, show) {
|
||||
if (show) {
|
||||
this.$el.show();
|
||||
this.$el.removeClass('is-closing').addClass('is-opening');
|
||||
} else {
|
||||
this.$el.removeClass('is-opening').addClass('is-closing');
|
||||
this.$el.hide();
|
||||
}
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.model.show();
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.model.hide();
|
||||
},
|
||||
|
||||
_onDestroy: function () {
|
||||
this.hide();
|
||||
this.clean();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
var Backbone = require('backbone');
|
||||
var Utils = require('builder/helpers/utils');
|
||||
var moment = require('moment');
|
||||
|
||||
var MONTHS = [
|
||||
{
|
||||
val: 0,
|
||||
label: _t('months.january')
|
||||
}, {
|
||||
val: 1,
|
||||
label: _t('months.february')
|
||||
}, {
|
||||
val: 2,
|
||||
label: _t('months.march')
|
||||
}, {
|
||||
val: 3,
|
||||
label: _t('months.april')
|
||||
}, {
|
||||
val: 4,
|
||||
label: _t('months.may')
|
||||
}, {
|
||||
val: 5,
|
||||
label: _t('months.june')
|
||||
}, {
|
||||
val: 6,
|
||||
label: _t('months.july')
|
||||
}, {
|
||||
val: 7,
|
||||
label: _t('months.august')
|
||||
}, {
|
||||
val: 8,
|
||||
label: _t('months.september')
|
||||
}, {
|
||||
val: 9,
|
||||
label: _t('months.october')
|
||||
}, {
|
||||
val: 10,
|
||||
label: _t('months.november')
|
||||
}, {
|
||||
val: 11,
|
||||
label: _t('months.december')
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
|
||||
initialize: function () {
|
||||
this.schema = {
|
||||
day: {
|
||||
type: 'Text',
|
||||
title: '',
|
||||
validators: [
|
||||
'required',
|
||||
/^(([0]?[1-9])|([1-2][0-9])|(3[01]))$/,
|
||||
function (value, formValues) {
|
||||
var date = moment(Utils.formatDate(formValues));
|
||||
if (!date.isValid()) {
|
||||
return {
|
||||
type: 'date',
|
||||
message: _t('components.datepicker.invalid-date')
|
||||
};
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
month: {
|
||||
type: 'Select',
|
||||
title: '',
|
||||
position: {
|
||||
'left': 0,
|
||||
'min-width': '200px'
|
||||
},
|
||||
options: MONTHS
|
||||
},
|
||||
year: {
|
||||
title: '',
|
||||
type: 'Text',
|
||||
validators: ['required', /^([0-9]{0,4})$/]
|
||||
},
|
||||
time: {
|
||||
title: '',
|
||||
type: 'Text',
|
||||
validators: ['required', /^([01]{1}[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/]
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getFormattedDate: function () {
|
||||
return Utils.formatDate(this.toJSON());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var moment = require('moment');
|
||||
var DatetimeEditorModel = require('./datetime-editor-model');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
tagName: 'div',
|
||||
className: 'Table-editorDate',
|
||||
|
||||
events: {
|
||||
'keyup': '_onKeyUp'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
var dateAttr = this.model.get('value');
|
||||
var date = {};
|
||||
|
||||
if (!dateAttr) {
|
||||
date = moment().utc();
|
||||
} else {
|
||||
date = moment(dateAttr).utc();
|
||||
}
|
||||
|
||||
this._formModel = new DatetimeEditorModel({
|
||||
day: date.date(),
|
||||
month: date.month(),
|
||||
year: date.year(),
|
||||
time: date.format('HH:mm:ss'),
|
||||
utcOffset: date.utcOffset()
|
||||
});
|
||||
this._formModel.bind('change', this._setValue, this);
|
||||
this.add_related_model(this._formModel);
|
||||
|
||||
this._setValue();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this._formView = new Backbone.Form({
|
||||
model: this._formModel
|
||||
});
|
||||
|
||||
this._formView.bind('change', function () {
|
||||
this.commit();
|
||||
});
|
||||
|
||||
this.$el.html(this._formView.render().el);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_setValue: function () {
|
||||
this.model.set('value', this._formModel.getFormattedDate());
|
||||
},
|
||||
|
||||
_onKeyUp: function () {
|
||||
this._setValue();
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._formView.remove();
|
||||
CoreView.prototype.clean.apply(this);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,120 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var moment = require('moment');
|
||||
|
||||
var DatetimeDialogModel = require('./datetime-dialog-model');
|
||||
var DatetimeDialogView = require('./datetime-dialog-view');
|
||||
var template = require('./datetime.tpl');
|
||||
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var PopupManager = require('builder/components/popup-manager');
|
||||
|
||||
Backbone.Form.editors.DateTime = Backbone.Form.editors.Base.extend({
|
||||
className: 'Editor-formInput u-flex u-alignCenter',
|
||||
|
||||
events: {
|
||||
'click .js-input': '_onInputClick'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
this.dialogMode = this.options.dialogMode || DialogConstants.Mode.DEFAULT;
|
||||
this._initBinds();
|
||||
this.render();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
Backbone.Form.editors.Base.prototype.render.apply(this, arguments);
|
||||
|
||||
this._removeDateTimeDialog();
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
value: this._getFormattedDatetime()
|
||||
})
|
||||
);
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_getFormattedDatetime: function () {
|
||||
if (this.value) {
|
||||
var value = new Date(this.value).toUTCString();
|
||||
var momentObj = moment(value).utc();
|
||||
return momentObj.format('YYYY-MM-DD') + 'T' + momentObj.format('HH:mm:ss') + 'Z';
|
||||
}
|
||||
|
||||
// for datetime type, empty '' value raises an error on Postgres
|
||||
return null;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
if (this.options.editorAttrs && this.options.editorAttrs.disabled) {
|
||||
this.$el.addClass('is-disabled');
|
||||
}
|
||||
|
||||
this._datetimeDialogModel = new DatetimeDialogModel({
|
||||
value: this._getFormattedDatetime()
|
||||
});
|
||||
this._datetimeDialogModel.bind('change:value', this._onInputChanged, this);
|
||||
|
||||
this._datetimeDialogView = new DatetimeDialogView({
|
||||
model: this._datetimeDialogModel
|
||||
});
|
||||
|
||||
this._popupManager = new PopupManager(this.cid, this.$el, this._datetimeDialogView.$el);
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var hide = function () {
|
||||
this._removeDateTimeDialog();
|
||||
}.bind(this);
|
||||
this.applyESCBind(hide);
|
||||
this.applyClickOutsideBind(hide);
|
||||
},
|
||||
|
||||
_onInputClick: function () {
|
||||
this._datetimeDialogView.render();
|
||||
this._popupManager.append(this.dialogMode);
|
||||
this._popupManager.track();
|
||||
},
|
||||
|
||||
_removeDateTimeDialog: function () {
|
||||
if (this._datetimeDialogView) {
|
||||
this._popupManager.untrack();
|
||||
this._datetimeDialogView.remove();
|
||||
}
|
||||
},
|
||||
|
||||
_onInputChanged: function (mdl) {
|
||||
var value = this._datetimeDialogModel.get('value');
|
||||
this.setValue(value);
|
||||
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_updateInput: function (value) {
|
||||
this.$('.js-input')
|
||||
.toggleClass('is-empty', !value)
|
||||
.text(value);
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
return this._datetimeDialogModel.get('value');
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
this._updateInput(value);
|
||||
this.value = value;
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._removeDateTimeDialog();
|
||||
this._popupManager && this._popupManager.destroy();
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
<button type="button" class="CDB-InputText
|
||||
<% if (!value) { %> is-empty <% } %>
|
||||
u-txt-left js-input"><%- value ? value : 'null' %></button>
|
||||
@@ -0,0 +1,175 @@
|
||||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
var template = require('./enabler-editor.tpl');
|
||||
|
||||
/**
|
||||
* Creates an element that enables another component, tested with:
|
||||
*
|
||||
* select, number and input so far.
|
||||
*/
|
||||
|
||||
Backbone.Form.editors.EnablerEditor = Backbone.Form.editors.Base.extend({
|
||||
tagName: 'div',
|
||||
className: 'Editor-checker u-flex u-alignCenter',
|
||||
|
||||
events: {
|
||||
'click .js-check': '_onCheckClicked'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
// Disable validators unless it's enabled
|
||||
this.validators = null;
|
||||
|
||||
if (!this.options.editor) {
|
||||
throw new Error('editor options is required');
|
||||
}
|
||||
|
||||
this._editorOptions = this.options.editor;
|
||||
this.value = this.model.get(opts.key);
|
||||
this._checkModel = new Backbone.Model({
|
||||
enabled: !!this.value
|
||||
});
|
||||
this.template = template;
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(
|
||||
this.template({
|
||||
label: this.options.label,
|
||||
isChecked: this._isChecked(),
|
||||
help: this.options.help || '',
|
||||
isDisabled: !!this.options.isDisabled,
|
||||
id: this.cid
|
||||
})
|
||||
);
|
||||
|
||||
if (this.options.help) {
|
||||
this._helpTooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-help'),
|
||||
gravity: 's',
|
||||
offset: 0,
|
||||
title: function () {
|
||||
return $(this).data('tooltip');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._renderComponent();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this._checkModel.bind('change:enabled', function (mdl, isEnabled) {
|
||||
this._manageValue(isEnabled);
|
||||
this._renderComponent();
|
||||
this._triggerChange();
|
||||
}, this);
|
||||
},
|
||||
|
||||
_manageValue: function (isEnabled) {
|
||||
if (isEnabled) {
|
||||
this.validators = this.options.validators;
|
||||
this.value = this.options.defaultValue || '';
|
||||
this._editorComponent.setValue(this.options.defaultValue || '');
|
||||
} else {
|
||||
this.validators = null;
|
||||
this.value = '';
|
||||
this._editorComponent.setValue('');
|
||||
}
|
||||
this.model.set(this.options.keyAttr, this.value);
|
||||
this.validate();
|
||||
},
|
||||
|
||||
_isChecked: function () {
|
||||
return this._checkModel.get('enabled');
|
||||
},
|
||||
|
||||
_renderComponent: function () {
|
||||
if (this._editorComponent) {
|
||||
this._removeComponent();
|
||||
}
|
||||
|
||||
var isDisabled = !this._isChecked() || this.options.isDisabled;
|
||||
var EditorClass = Backbone.Form.editors[this._editorOptions.type];
|
||||
var editorAttrs = _.extend(
|
||||
this._editorOptions.editorAttrs || {},
|
||||
{
|
||||
disabled: isDisabled
|
||||
}
|
||||
);
|
||||
|
||||
this._editorComponent = new EditorClass(
|
||||
_.extend(
|
||||
{
|
||||
model: this.model,
|
||||
key: this.options.keyAttr,
|
||||
editorAttrs: editorAttrs,
|
||||
trackingClass: this.options.trackingClass,
|
||||
editorType: this._editorOptions.type
|
||||
},
|
||||
_.omit(this._editorOptions, 'editorAttrs', 'type')
|
||||
)
|
||||
);
|
||||
this._editorComponent.bind('change', this._setEditorComponentValue, this);
|
||||
this.$('.js-editor').html(this._editorComponent.render().el);
|
||||
|
||||
// Not all editor implement the focus method
|
||||
try {
|
||||
this._editorComponent.focus();
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
_removeComponent: function () {
|
||||
this._editorComponent.remove();
|
||||
this._editorComponent.unbind('change', this._setEditorComponentValue, this);
|
||||
},
|
||||
|
||||
_setEditorComponentValue: function () {
|
||||
this.value = this._editorComponent.getValue();
|
||||
this._triggerChange();
|
||||
},
|
||||
|
||||
_triggerChange: function () {
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
if (this._checkModel.get('enabled')) {
|
||||
return this._editorComponent && this._editorComponent.getValue() || '';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
this._checkModel.set('enabled', !!value);
|
||||
if (this._editorComponent) {
|
||||
this._editorComponent.setValue(value);
|
||||
}
|
||||
this.value = value;
|
||||
},
|
||||
|
||||
_onCheckClicked: function (ev) {
|
||||
this._checkModel.set('enabled', $(ev.target).is(':checked'));
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
if (this._editorComponent) {
|
||||
this._removeComponent();
|
||||
}
|
||||
if (this._helpTooltip) {
|
||||
this._helpTooltip.clean();
|
||||
}
|
||||
Backbone.Form.editors.Base.prototype.remove.call(this);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="u-flex Editor-checkerInput CDB-Text CDB-Size-medium u-rSpace--xl">
|
||||
<input class="CDB-Checkbox js-check" type="checkbox" id="<%- id %>" name="" value="" <% if (isDisabled) { %>disabled<% } %> <% if (isChecked) { %>checked<% } %>>
|
||||
<span class="CDB-Checkbox-face u-rSpace--m"></span>
|
||||
<label class="CDB-Text CDB-Size-small u-ellipsis u-upperCase is-semibold u-flex u-alignCenter Editor-formLabel" for="<%- id %>">
|
||||
<span class="u-ellipsis <% if (help) { %> js-help is-underlined<% } %>" <% if (help) { %> data-tooltip="<%- help %>"<% } %> title="<%- label %>"><%- label %></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="Editor-checkerComponent js-editor"></div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<label class="CDB-Legend u-upperCase CDB-Text is-semibold CDB-Size-small">
|
||||
<div class="u-flex u-alignCenter">
|
||||
<div class="u-iBlock u-rSpace--m">
|
||||
<input class="CDB-Checkbox js-input" type="checkbox" name="" value="" <% if (checked) { %>checked<% } %> <% if (disabled) { %>disabled<% } %>>
|
||||
<span class="u-iBlock CDB-Checkbox-face"></span>
|
||||
</div>
|
||||
<span class="<% if (help) { %> js-help is-underlined<% } %>" <% if (help) { %> data-tooltip="<%- help %>"<% } %> ><%- label %></span>
|
||||
</div>
|
||||
</label>
|
||||
<div class="Editor-checkerComponent js-editor"></div>
|
||||
@@ -0,0 +1,84 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var $ = require('jquery');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
var template = require('./enabler.tpl');
|
||||
var templateReversed = require('./enabler-reversed.tpl');
|
||||
|
||||
Backbone.Form.editors.Enabler = Backbone.Form.editors.Base.extend({
|
||||
tagName: 'div',
|
||||
|
||||
events: {
|
||||
'change .js-input': '_onCheckChange',
|
||||
'focus .js-input': function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
'blur .js-input': function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
this.template = opts.schema && opts.schema.editorAttrs && opts.schema.editorAttrs.reversed ? templateReversed : (opts.template || template);
|
||||
this._initViews();
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
this.$el.html(
|
||||
this.template({
|
||||
checked: this.options.disabled ? false : this.model.get(this.options.key),
|
||||
label: this.options.label,
|
||||
id: this.options.inputId,
|
||||
title: this.options.title,
|
||||
help: this.options.help || '',
|
||||
disabled: this.options.disabled
|
||||
})
|
||||
);
|
||||
|
||||
if (this.options.help) {
|
||||
this._removeTooltip();
|
||||
|
||||
this._helpTooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-help'),
|
||||
gravity: 's',
|
||||
title: function () {
|
||||
return $(this).data('tooltip');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options.disabled) {
|
||||
this.undelegateEvents();
|
||||
}
|
||||
},
|
||||
|
||||
_onCheckChange: function () {
|
||||
var isEnabled = this.$('.js-input').is(':checked');
|
||||
this.model.set('enabler', isEnabled);
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
return this.$('.js-input').is(':checked');
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
this.$('.js-input')[value ? 'attr' : 'removeAttr']('checked', '');
|
||||
this.model.set(this.key, value);
|
||||
},
|
||||
|
||||
_removeTooltip: function () {
|
||||
if (this._helpTooltip) {
|
||||
this._helpTooltip.clean();
|
||||
}
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._removeTooltip();
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
<label class="CDB-Legend u-upperCase CDB-Text is-semibold CDB-Size-small">
|
||||
<div class="u-flex u-alignCenter">
|
||||
<div class="u-iBlock u-rSpace--m">
|
||||
<input class="CDB-Checkbox js-input" type="checkbox" name="" value="" <% if (checked) { %>checked<% } %> <% if (disabled) { %>disabled<% } %>>
|
||||
<span class="u-iBlock CDB-Checkbox-face"></span>
|
||||
</div>
|
||||
<span class="<% if (help) { %> js-help is-underlined<% } %>" <% if (help) { %> data-tooltip="<%- help %>"<% } %> ><%- title %></span>
|
||||
</div>
|
||||
</label>
|
||||
<div class="CDB-Text CDB-Size-medium Editor-formInput js-editor"></div>
|
||||
@@ -0,0 +1,67 @@
|
||||
var InputFillView = require('builder/components/input-fill/input-fill-view');
|
||||
var InputColorByValueView = require('builder/components/form-components/editors/fill-color/inputs/input-color-by-value');
|
||||
var FillConstants = require('builder/components/form-components/_constants/_fill');
|
||||
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'columns',
|
||||
'query',
|
||||
'configModel',
|
||||
'userModel',
|
||||
'editorAttrs',
|
||||
'modals',
|
||||
'dialogMode',
|
||||
'valueColorInputModel',
|
||||
'popupConfig'
|
||||
];
|
||||
|
||||
module.exports = InputFillView.extend({
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
|
||||
this._initViews();
|
||||
},
|
||||
|
||||
_initInputFields: function () {
|
||||
InputFillView.prototype._initInputFields.call(this);
|
||||
|
||||
this._initColorByValueInput();
|
||||
},
|
||||
|
||||
_initColorByValueInput: function () {
|
||||
var quantification = this._valueColorInputModel.get('quantification');
|
||||
|
||||
if (quantification && FillConstants.Quantification.REFERENCE[quantification]) {
|
||||
this._valueColorInputModel.set(
|
||||
'quantification',
|
||||
FillConstants.Quantification.REFERENCE[quantification], {
|
||||
silent: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this._valueColorInputView = new InputColorByValueView({
|
||||
model: this._valueColorInputModel,
|
||||
columns: this.options.columns,
|
||||
hideNumericColumns: this.options.hideNumericColumns,
|
||||
removeByValueCategory: this.options.removeByValueCategory,
|
||||
query: this.options.query,
|
||||
configModel: this.options.configModel,
|
||||
userModel: this.options.userModel,
|
||||
modals: this.options.modals,
|
||||
imageEnabled: this.options.imageEnabled,
|
||||
editorAttrs: this.options.editorAttrs ? this.options.editorAttrs : {},
|
||||
disabled: this.options.editorAttrs && this.options.editorAttrs.disabled
|
||||
});
|
||||
|
||||
this._valueColorInputView.bind('click', this._onInputClick, this);
|
||||
this.$('.js-content').append(this._valueColorInputView.render().$el);
|
||||
|
||||
this._inputCollection && this._inputCollection.push(this._valueColorInputModel);
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
this._valueColorInputView && this._valueColorInputView.afterRender();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
var InputFillView = require('builder/components/input-fill/input-fill-view');
|
||||
var InputColorFixedView = require('builder/components/form-components/editors/fill-color/inputs/input-color-fixed');
|
||||
var InputImageView = require('builder/components/form-components/editors/fill-color/inputs/input-image');
|
||||
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'columns',
|
||||
'editorAttrs',
|
||||
'dialogMode',
|
||||
'popupConfig',
|
||||
'fixedColorInputModel',
|
||||
'imageInputModel'
|
||||
];
|
||||
|
||||
module.exports = InputFillView.extend({
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
|
||||
if (this._editorAttrs.imageEnabled) {
|
||||
this._configModel = this.options.configModel;
|
||||
this._userModel = this.options.userModel;
|
||||
this._modals = this.options.modals;
|
||||
}
|
||||
|
||||
this._initViews();
|
||||
},
|
||||
|
||||
_initInputFields: function () {
|
||||
InputFillView.prototype._initInputFields.call(this);
|
||||
|
||||
this._initFillColorInput();
|
||||
this._initFillImageInput();
|
||||
},
|
||||
|
||||
_initFillColorInput: function () {
|
||||
this._fillInputColorView = new InputColorFixedView({
|
||||
model: this._fixedColorInputModel,
|
||||
columns: this._columns,
|
||||
configModel: this._configModel,
|
||||
userModel: this._userModel,
|
||||
modals: this._modals,
|
||||
editorAttrs: this._editorAttrs || {},
|
||||
disabled: this._editorAttrs && this._editorAttrs.disabled
|
||||
});
|
||||
|
||||
this._fillInputColorView.bind('click', this._onInputClick, this);
|
||||
this.$('.js-content').append(this._fillInputColorView.render().$el);
|
||||
|
||||
this._inputCollection && this._inputCollection.push(this._fixedColorInputModel);
|
||||
},
|
||||
|
||||
_initFillImageInput: function () {
|
||||
if (this._editorAttrs.imageEnabled) {
|
||||
this._fillInputImageView = new InputImageView({
|
||||
model: this._imageInputModel,
|
||||
columns: this._columns,
|
||||
configModel: this._configModel,
|
||||
userModel: this._userModel,
|
||||
modals: this._modals,
|
||||
query: this._query,
|
||||
editorAttrs: this._editorAttrs || {},
|
||||
disabled: this._editorAttrs && this._editorAttrs.disabled
|
||||
});
|
||||
|
||||
this._fillInputImageView.bind('click', this._onInputClick, this);
|
||||
this.$('.js-content').append(this._fillInputImageView.render().$el);
|
||||
|
||||
this._inputCollection && this._inputCollection.push(this._imageInputModel);
|
||||
}
|
||||
},
|
||||
|
||||
_unbindFormInputs: function () {
|
||||
this._fillInputColorView.unbind('click', this._onInputClick, this);
|
||||
if (this._fillInputImageView) {
|
||||
this._fillInputImageView.unbind('click', this._onInputClick, this);
|
||||
}
|
||||
this._inputCollection.unbind('onInputChanged', this._onInputChanged, this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._unbindFormInputs();
|
||||
InputFillView.prototype.clean.call(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,259 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
|
||||
var FillColorFixedView = require('builder/components/form-components/editors/fill-color/fill-color-fixed-view');
|
||||
var FillColorByValueView = require('builder/components/form-components/editors/fill-color/fill-color-by-value-view');
|
||||
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var FillConstants = require('builder/components/form-components/_constants/_fill');
|
||||
var tabPaneTemplate = require('builder/components/tab-pane/tab-pane.tpl');
|
||||
var createRadioLabelsTabPane = require('builder/components/tab-pane/create-radio-labels-tab-pane');
|
||||
|
||||
Backbone.Form.editors.FillColor = Backbone.Form.editors.Base.extend({
|
||||
className: 'Form-InputFill Editor-formInput--FillColor',
|
||||
|
||||
initialize: function (options) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, options);
|
||||
EditorHelpers.setOptions(this, options);
|
||||
|
||||
if (this.options.editorAttrs) {
|
||||
this.options = _.extend(this.options, {
|
||||
columns: this.options.options,
|
||||
query: this.options.query,
|
||||
configModel: this.options.configModel,
|
||||
userModel: this.options.userModel,
|
||||
editorAttrs: this.options.editorAttrs,
|
||||
modals: this.options.modals
|
||||
});
|
||||
|
||||
this._colorKeyAttribute = options.key;
|
||||
this._colorAttributes = this.model.get(this._colorKeyAttribute);
|
||||
this._sizeAttributes = this.model.get('fillSize');
|
||||
|
||||
this._imageInputModel = new Backbone.Model(
|
||||
_.extend({ type: 'image' }, this._colorAttributes)
|
||||
);
|
||||
|
||||
this._fixedColorInputModel = new Backbone.Model(
|
||||
_.extend({ type: 'color' }, this._colorAttributes)
|
||||
);
|
||||
|
||||
this._valueColorInputModel = new Backbone.Model(
|
||||
_.extend({ type: 'color' }, this._colorAttributes)
|
||||
);
|
||||
}
|
||||
|
||||
this._initViews();
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var self = this;
|
||||
var geometryName = this.options.editorAttrs.geometryName;
|
||||
|
||||
var fixedPane = {
|
||||
name: FillConstants.Panes.FIXED,
|
||||
label: _t('form-components.editors.fill.input-number.solid'),
|
||||
tooltip: _t('editor.style.tooltips.fill.fixed-tab', { type: geometryName }),
|
||||
tooltipGravity: 's',
|
||||
createContentView: function () {
|
||||
return self._generateFixedContentView();
|
||||
}
|
||||
};
|
||||
|
||||
var valuePane = {
|
||||
name: FillConstants.Panes.BY_VALUE,
|
||||
label: _t('form-components.editors.fill.input-number.by-value'),
|
||||
tooltip: _t('editor.style.tooltips.fill.by-value-tab', { type: geometryName }),
|
||||
tooltipGravity: 's',
|
||||
createContentView: function () {
|
||||
return self._generateValueContentView();
|
||||
}
|
||||
};
|
||||
|
||||
this._tabPaneTabs = [];
|
||||
|
||||
if (this.options.editorAttrs && this.options.editorAttrs.hidePanes) {
|
||||
var hidePanes = this.options.editorAttrs.hidePanes;
|
||||
|
||||
if (!_.contains(hidePanes, FillConstants.Panes.FIXED)) {
|
||||
this._tabPaneTabs.push(fixedPane);
|
||||
}
|
||||
|
||||
if (!_.contains(hidePanes, FillConstants.Panes.BY_VALUE)) {
|
||||
this._tabPaneTabs.push(valuePane);
|
||||
}
|
||||
} else {
|
||||
this._tabPaneTabs = [fixedPane, valuePane];
|
||||
}
|
||||
|
||||
var tabPaneOptions = {
|
||||
tabPaneOptions: {
|
||||
template: tabPaneTemplate,
|
||||
tabPaneItemOptions: {
|
||||
tagName: 'li',
|
||||
klassName: 'CDB-NavMenu-item'
|
||||
}
|
||||
},
|
||||
tabPaneItemLabelOptions: {
|
||||
tagName: 'div',
|
||||
className: 'CDB-Text CDB-Size-medium'
|
||||
}
|
||||
};
|
||||
|
||||
var selectedTabPaneIndex = this._getSelectedTabPaneIndex();
|
||||
this._tabPaneTabs[selectedTabPaneIndex].selected = true;
|
||||
|
||||
this._tabPaneView = createRadioLabelsTabPane(this._tabPaneTabs, tabPaneOptions);
|
||||
this.$el.append(this._tabPaneView.render().$el);
|
||||
},
|
||||
|
||||
_getSelectedTabPaneIndex: function () {
|
||||
var FIXED_TAB_PANE_INDEX = 0;
|
||||
var VALUE_TAB_PANE_INDEX = 1;
|
||||
|
||||
return this._colorAttributes && this._colorAttributes.range &&
|
||||
this._tabPaneTabs.length > 1
|
||||
? VALUE_TAB_PANE_INDEX
|
||||
: FIXED_TAB_PANE_INDEX;
|
||||
},
|
||||
|
||||
_removeDialogs: function () {
|
||||
this._removeFillColorFixedDialog();
|
||||
this._removeFillColorByValueDialog();
|
||||
},
|
||||
|
||||
_removeFillColorFixedDialog: function () {
|
||||
this._fillColorFixedView && this._fillColorFixedView.removeDialog();
|
||||
},
|
||||
|
||||
_removeFillColorByValueDialog: function () {
|
||||
this._fillColorByValueView && this._fillColorByValueView.removeDialog();
|
||||
},
|
||||
|
||||
_generateFixedContentView: function () {
|
||||
this._fillColorFixedView = new FillColorFixedView({
|
||||
model: this.model,
|
||||
columns: this.options.columns,
|
||||
configModel: this.options.configModel,
|
||||
userModel: this.options.userModel,
|
||||
modals: this.options.modals,
|
||||
query: this.options.query,
|
||||
editorAttrs: this.options.editorAttrs,
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
hideTabs: this.options.hideTabs,
|
||||
fixedColorInputModel: this._fixedColorInputModel,
|
||||
imageInputModel: this._imageInputModel,
|
||||
popupConfig: {
|
||||
cid: this.cid,
|
||||
$el: this.$el
|
||||
}
|
||||
});
|
||||
|
||||
this.applyESCBind(this._removeFillColorFixedDialog);
|
||||
this.applyClickOutsideBind(this._removeFillColorFixedDialog);
|
||||
|
||||
this._fillColorFixedView.on('onInputChanged', function (input) {
|
||||
this.trigger('change', input);
|
||||
}, this);
|
||||
|
||||
return this._fillColorFixedView;
|
||||
},
|
||||
|
||||
_generateValueContentView: function () {
|
||||
this._fillColorByValueView = new FillColorByValueView({
|
||||
columns: this.options.columns,
|
||||
query: this.options.query,
|
||||
configModel: this.options.configModel,
|
||||
userModel: this.options.userModel,
|
||||
editorAttrs: this.options.editorAttrs,
|
||||
model: this.model,
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
categorizeColumns: this.options.categorizeColumns,
|
||||
hideNumericColumns: this.options.hideNumericColumns,
|
||||
removeByValueCategory: this.options.removeByValueCategory,
|
||||
modals: this.options.modals,
|
||||
hideTabs: this.options.hideTabs,
|
||||
valueColorInputModel: this._valueColorInputModel,
|
||||
popupConfig: {
|
||||
cid: this.cid,
|
||||
$el: this.$el
|
||||
}
|
||||
});
|
||||
|
||||
this.applyESCBind(this._removeFillColorByValueDialog);
|
||||
this.applyClickOutsideBind(this._removeFillColorByValueDialog);
|
||||
|
||||
this._fillColorByValueView.on('onInputChanged', function (input) {
|
||||
this.trigger('change', input);
|
||||
}, this);
|
||||
|
||||
return this._fillColorByValueView;
|
||||
},
|
||||
|
||||
getValue: function (param) {
|
||||
var selectedTabPaneName = this._tabPaneView.getSelectedTabPaneName();
|
||||
|
||||
return selectedTabPaneName === FillConstants.Panes.FIXED
|
||||
? this._getFillFixedValues()
|
||||
: this._getFillByValueValues();
|
||||
},
|
||||
|
||||
_getFillFixedValues: function () {
|
||||
var color = this._fixedColorInputModel.toJSON();
|
||||
var image = this._imageInputModel.toJSON();
|
||||
|
||||
if (color.range) {
|
||||
color.fixed = color.range[0];
|
||||
color.opacity = 1;
|
||||
}
|
||||
|
||||
var colorOmitAttributes = [
|
||||
'createContentView',
|
||||
'selected',
|
||||
'type',
|
||||
'image',
|
||||
'marker',
|
||||
'range'
|
||||
];
|
||||
|
||||
var imageOmitAttributes = [
|
||||
'createContentView',
|
||||
'selected',
|
||||
'type',
|
||||
'fixed',
|
||||
'range'
|
||||
];
|
||||
|
||||
var colorAttributes = _.omit(color, colorOmitAttributes);
|
||||
var imageAttributes = _.omit(image, imageOmitAttributes);
|
||||
var values = _.extend({}, imageAttributes, colorAttributes);
|
||||
|
||||
this._fixedColorInputModel.set(values);
|
||||
this._imageInputModel.set(values);
|
||||
|
||||
return values;
|
||||
},
|
||||
|
||||
_getFillByValueValues: function () {
|
||||
var colorOmitAttributes = [
|
||||
'createContentView',
|
||||
'selected',
|
||||
'type'
|
||||
];
|
||||
|
||||
var values = _.omit(this._valueColorInputModel.toJSON(), colorOmitAttributes);
|
||||
|
||||
this._valueColorInputModel.set(values);
|
||||
|
||||
return values;
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._removeDialogs();
|
||||
this._tabPaneView.clean();
|
||||
this._fillColorByValueView && this._fillColorByValueView.clean();
|
||||
this._fillColorFixedView && this._fillColorFixedView.clean();
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,196 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
|
||||
var CoreView = require('backbone/core-view');
|
||||
|
||||
var InputColorValueContentView = require('builder/components/input-color/input-color-value-content-view');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
var InputColorTemplate = require('builder/components/form-components/editors/fill-color/inputs/input-color-by-value.tpl');
|
||||
var FillConstants = require('builder/components/form-components/_constants/_fill');
|
||||
|
||||
var Utils = require('builder/helpers/utils');
|
||||
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'columns',
|
||||
'query',
|
||||
'configModel',
|
||||
'userModel',
|
||||
'modals'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
tagName: 'li',
|
||||
className: 'CDB-OptionInput-item',
|
||||
|
||||
events: {
|
||||
'click': '_onClick'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (this.options.editorAttrs) {
|
||||
this._editorAttrs = this.options.editorAttrs;
|
||||
this._help = this._editorAttrs.help;
|
||||
this._imageEnabled = this._editorAttrs.imageEnabled;
|
||||
this._hideNumericColumns = this.options.hideNumericColumns;
|
||||
this._removeByValueCategory = this.options.removeByValueCategory;
|
||||
|
||||
if (this._editorAttrs.hidePanes && !_.contains(this._editorAttrs.hidePanes, FillConstants.Panes.BY_VALUE)) {
|
||||
if (!options.configModel) throw new Error('configModel param is required');
|
||||
if (!options.userModel) throw new Error('userModel param is required');
|
||||
if (!options.modals) throw new Error('modals param is required');
|
||||
if (!options.query) throw new Error('query param is required');
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.columns) throw new Error('columns is required');
|
||||
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this.$el.toggleClass('is-disabled', this.options.disabled);
|
||||
|
||||
this.$el.html(
|
||||
InputColorTemplate({
|
||||
value: this._getValue(),
|
||||
attribute: this.model.get('attribute'),
|
||||
opacity: this._getOpacity(),
|
||||
colorBar: this._getColorbar(),
|
||||
help: this._help.color || ''
|
||||
})
|
||||
);
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
this._openPopupForColumn();
|
||||
},
|
||||
|
||||
_openPopupForColumn: function () {
|
||||
var columnSelected = this.model.get('attribute');
|
||||
if (_.isUndefined(columnSelected)) {
|
||||
var self = this;
|
||||
setTimeout(function () { self.$el.click(); }, 200);
|
||||
}
|
||||
},
|
||||
|
||||
_getColorbar: function () {
|
||||
var colors = this._getValue();
|
||||
if (!_.isArray(colors)) return '';
|
||||
|
||||
var deltaPercent = (100.0 / colors.length);
|
||||
var previousPercent = 0;
|
||||
var self = this;
|
||||
var colorsForBar = colors.map(function (color) {
|
||||
var min = previousPercent;
|
||||
var max = previousPercent + deltaPercent;
|
||||
var entry = self._getColorStep(color, [min, max]);
|
||||
previousPercent = max;
|
||||
return entry;
|
||||
});
|
||||
|
||||
var colorBar = 'background: linear-gradient(90deg, ' + colorsForBar.join(', ') + ')';
|
||||
return colorBar;
|
||||
},
|
||||
|
||||
_getColorStep: function (color, minMax) {
|
||||
return (color + ' ' + minMax[0] + '%, ' + color + ' ' + minMax[1] + '%');
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
if (this._help.color) {
|
||||
var tooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-help'),
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return $(this).data('tooltip');
|
||||
},
|
||||
offset: 8
|
||||
});
|
||||
|
||||
this.addView(tooltip);
|
||||
}
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var self = this;
|
||||
|
||||
this.model.set('createContentView', function () {
|
||||
return self._createContentView();
|
||||
}).bind(this);
|
||||
|
||||
this.model.on('change:selected', this._onToggleSelected, this);
|
||||
this.model.on('change:opacity change:range change:attribute', this.render, this);
|
||||
},
|
||||
|
||||
_getColumn: function () {
|
||||
return _.find(this._columns, function (column) {
|
||||
return column.label === this.model.get('attribute');
|
||||
}, this);
|
||||
},
|
||||
|
||||
_iconStylingEnabled: function () {
|
||||
return this._imageEnabled;
|
||||
},
|
||||
|
||||
_createContentView: function () {
|
||||
this._inputColorValueContentView = new InputColorValueContentView({
|
||||
model: this.model,
|
||||
columns: this._columns,
|
||||
hideNumericColumns: this._hideNumericColumns,
|
||||
removeByValueCategory: this._removeByValueCategory,
|
||||
configModel: this._configModel,
|
||||
categorizeColumns: this._categorizeColumns,
|
||||
imageEnabled: this._imageEnabled,
|
||||
userModel: this._userModel,
|
||||
modals: this._modals,
|
||||
hideTabs: this._hideTabs,
|
||||
query: this._query
|
||||
});
|
||||
|
||||
this.model.on('change', this._onChangeValue, this);
|
||||
|
||||
return this._inputColorValueContentView;
|
||||
},
|
||||
|
||||
_onClick: function () {
|
||||
if (this.options.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.trigger('click', this.model);
|
||||
},
|
||||
|
||||
_getValue: function () {
|
||||
return this.model.get('range') && this.model.get('range').length
|
||||
? this._getRangeColorValues()
|
||||
: null;
|
||||
},
|
||||
|
||||
_getRangeColorValues: function () {
|
||||
return _.map(this.model.get('range'), function (color) {
|
||||
return Utils.hexToRGBA(color, this._getOpacity());
|
||||
}, this);
|
||||
},
|
||||
|
||||
_getOpacity: function () {
|
||||
return this.model.get('opacity') != null ? this.model.get('opacity') : 1;
|
||||
},
|
||||
|
||||
_onToggleSelected: function () {
|
||||
this.$el.toggleClass('is-active', this.model.get('selected'));
|
||||
},
|
||||
|
||||
_onChangeValue: function (color) {
|
||||
this.model.set({ quantification: color.get('quantification'), range: color.get('range') });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
<button type="button" class="Editor-fillContainer Editor-fillContainer--ByValue <% if (help) { %>js-help<% } %>" <% if (help) { %> data-tooltip="<%- help %>"<% } %>>
|
||||
<% if (_.isArray(value)) { %>
|
||||
<span class="Editor-fillContainer--Column u-ellipsis">
|
||||
<%- attribute %>
|
||||
</span>
|
||||
<span class="Editor-fillContainer--ColorBarContainer ColorBarContainer">
|
||||
<span class="ColorBar ColorBar-gradient" style="<%- colorBar %>"></span>
|
||||
</span>
|
||||
<% } else { %>
|
||||
<span class="u-altTextColor"><%- _t('form-components.editors.style.select-by-column') %></span>
|
||||
<% } %>
|
||||
</button>
|
||||
@@ -0,0 +1,135 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
|
||||
var CoreView = require('backbone/core-view');
|
||||
|
||||
var ColorPickerView = require('builder/components/color-picker/color-picker');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
var InputColorTemplate = require('builder/components/form-components/editors/fill-color/inputs/input-color-fixed.tpl');
|
||||
|
||||
var Utils = require('builder/helpers/utils');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
tagName: 'li',
|
||||
className: 'CDB-OptionInput-item',
|
||||
|
||||
events: {
|
||||
'click': '_onClick'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (this.options.editorAttrs) {
|
||||
this._editorAttrs = this.options.editorAttrs;
|
||||
this._help = this._editorAttrs.help;
|
||||
}
|
||||
|
||||
if (!options.columns) throw new Error('columns is required');
|
||||
|
||||
this._columns = options.columns;
|
||||
this._configModel = options.configModel;
|
||||
this._userModel = options.userModel;
|
||||
this._modals = options.modals;
|
||||
this._query = options.query;
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this.$el.toggleClass('is-disabled', this.options.disabled);
|
||||
|
||||
this.$el.html(
|
||||
InputColorTemplate({
|
||||
value: this._getValue(),
|
||||
opacity: this._getOpacity(),
|
||||
help: this._help && this._help.color || null
|
||||
})
|
||||
);
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
if (this._help && this._help.color) {
|
||||
var tooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-help'),
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return $(this).data('tooltip');
|
||||
},
|
||||
offset: 8
|
||||
});
|
||||
|
||||
this.addView(tooltip);
|
||||
}
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var self = this;
|
||||
|
||||
this.model.set('createContentView', function () {
|
||||
return self._createContentView();
|
||||
}).bind(this);
|
||||
|
||||
this.model.on('change:selected', this._onToggleSelected, this);
|
||||
this.model.on('change:opacity change:fixed', this.render, this);
|
||||
},
|
||||
|
||||
_getColumn: function () {
|
||||
return _.find(this._columns, function (column) {
|
||||
return column.label === this.model.get('attribute');
|
||||
}, this);
|
||||
},
|
||||
|
||||
_createContentView: function () {
|
||||
this._colorPickerView = new ColorPickerView({
|
||||
value: this.model.get('fixed'),
|
||||
opacity: this.model.get('opacity'),
|
||||
disableOpacity: this._disableOpacity || false
|
||||
});
|
||||
|
||||
this._initColorPickerViewBindings();
|
||||
|
||||
return this._colorPickerView;
|
||||
},
|
||||
|
||||
_initColorPickerViewBindings: function () {
|
||||
if (this._colorPickerView) {
|
||||
this._colorPickerView.bind('change', this._onChangeValue, this);
|
||||
this._colorPickerView.on('onClean', function () {
|
||||
this._colorPickerView.unbind('change', this._onChangeValue, this);
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
_onClick: function () {
|
||||
if (this.options.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.trigger('click', this.model);
|
||||
},
|
||||
|
||||
_getValue: function () {
|
||||
var value = this.model.get('fixed');
|
||||
|
||||
return value
|
||||
? Utils.hexToRGBA(value, this._getOpacity())
|
||||
: value;
|
||||
},
|
||||
|
||||
_getOpacity: function () {
|
||||
return this.model.get('opacity') != null ? this.model.get('opacity') : 1;
|
||||
},
|
||||
|
||||
_onToggleSelected: function () {
|
||||
this.$el.toggleClass('is-active', this.model.get('selected'));
|
||||
},
|
||||
|
||||
_onChangeValue: function (color) {
|
||||
this.model.set({ fixed: color.hex, opacity: color.opacity });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
<button type="button" class="Editor-fillContainer u-altTextColor <% if (help) { %>js-help<% } %>" <% if (help) { %> data-tooltip="<%- help %>"<% } %>>
|
||||
<% if (value) { %>
|
||||
<ul class="ColorBarContainer">
|
||||
<li class="ColorBar" style="background-color: <%- value %>"></li>
|
||||
</ul>
|
||||
<% } else { %>
|
||||
<%- _t('form-components.editors.fill.input-color.select-color') %>
|
||||
<% }%>
|
||||
</button>
|
||||
@@ -0,0 +1,168 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
|
||||
var CoreView = require('backbone/core-view');
|
||||
|
||||
var InputImageTemplate = require('./input-image.tpl');
|
||||
var ImageLoaderView = require('builder/components/img-loader-view');
|
||||
var InputColorFileView = require('builder/components/input-color/input-color-file-view');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
var Utils = require('builder/helpers/utils');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
tagName: 'li',
|
||||
className: 'CDB-OptionInput-item',
|
||||
|
||||
events: {
|
||||
'click': '_onClick'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (this.options.editorAttrs) {
|
||||
this._editorAttrs = this.options.editorAttrs;
|
||||
this._help = this._editorAttrs.help;
|
||||
}
|
||||
|
||||
this._imageEnabled = true;
|
||||
this._columns = options.columns;
|
||||
this._configModel = options.configModel;
|
||||
this._userModel = options.userModel;
|
||||
this._modals = options.modals;
|
||||
this._query = options.query;
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
|
||||
this.$el.html(InputImageTemplate({
|
||||
help: this._help.image || '',
|
||||
image: this.model.get('image'),
|
||||
isCustomMarker: this.model.get('kind') === 'custom-marker'
|
||||
}));
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
this.iconView = new ImageLoaderView({
|
||||
imageClass: 'Editor-fillImageAsset',
|
||||
imageUrl: this._getImageURL(),
|
||||
color: this._getColor()
|
||||
});
|
||||
|
||||
this.addView(this.iconView);
|
||||
this.$('.js-image-container').append(this.iconView.render().el);
|
||||
|
||||
if (this._help.image) {
|
||||
var tooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-help'),
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return $(this).data('tooltip');
|
||||
},
|
||||
offset: 8
|
||||
});
|
||||
|
||||
this.addView(tooltip);
|
||||
}
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var self = this;
|
||||
this.model.set('createContentView', function () {
|
||||
return self._createContentView();
|
||||
}).bind(this);
|
||||
|
||||
this.model.on('change:images change:image', this.render, this);
|
||||
this.model.on('change:opacity change:fixed', this._updateImageColor, this);
|
||||
},
|
||||
|
||||
_categoryImagesPresent: function () {
|
||||
var images = this.model.get('images');
|
||||
|
||||
images.forEach(function (image) {
|
||||
if (!_.isEmpty(image)) return true;
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_updateImageColor: function () {
|
||||
if (this.iconView) {
|
||||
this.iconView.updateImageColor(this._getColor());
|
||||
}
|
||||
},
|
||||
|
||||
_getColor: function () {
|
||||
var color = this.model.get('fixed');
|
||||
|
||||
return color
|
||||
? Utils.hexToRGBA(color, this._getOpacity())
|
||||
: color;
|
||||
},
|
||||
|
||||
_getOpacity: function () {
|
||||
return this.model.get('opacity') != null ? this.model.get('opacity') : 1;
|
||||
},
|
||||
|
||||
_onClick: function () {
|
||||
if (this.options.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.trigger('click', this.model);
|
||||
},
|
||||
|
||||
_onChangeFile: function (marker) {
|
||||
var kind, image;
|
||||
|
||||
if (marker) {
|
||||
kind = marker.kind;
|
||||
image = marker.url;
|
||||
} else {
|
||||
kind = null;
|
||||
image = null;
|
||||
}
|
||||
|
||||
this.model.set({ kind: kind, image: image });
|
||||
},
|
||||
|
||||
_createContentView: function () {
|
||||
this._inputColorFileView = new InputColorFileView({
|
||||
model: this.model,
|
||||
userModel: this._userModel,
|
||||
configModel: this._configModel,
|
||||
modals: this._modals
|
||||
});
|
||||
|
||||
this._initInputColorFileViewBindings();
|
||||
|
||||
return this._inputColorFileView;
|
||||
},
|
||||
|
||||
_initInputColorFileViewBindings: function () {
|
||||
if (this._inputColorFileView) {
|
||||
this._inputColorFileView.bind('change', this._onChangeFile, this);
|
||||
this._inputColorFileView.on('onClean', function () {
|
||||
this._inputColorFileView.unbind('change', this._onChangeFile, this);
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
_getImageURL: function () {
|
||||
return this.model.get('image') && this._isIconStylingEnabled() ? this.model.get('image') : '';
|
||||
},
|
||||
|
||||
_getKind: function () {
|
||||
return this.model.get('kind') && this._isIconStylingEnabled() ? this.model.get('kind') : '';
|
||||
},
|
||||
|
||||
_isIconStylingEnabled: function () {
|
||||
return this._imageEnabled;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<% if (image && !isCustomMarker) { %>
|
||||
<button type="button" class="Editor-fillImage <% if (help) { %> js-help<% } %>" <% if (help) { %> data-tooltip="<%- help %>"<% } %>>
|
||||
<div class="IconContainer js-image-container"></div>
|
||||
</button>
|
||||
<% } %>
|
||||
|
||||
<% if (!image || isCustomMarker) { %>
|
||||
<button type="button" class="Editor-fillImage <% if (help) { %> js-help<% } %>" <% if (help) { %> data-tooltip="<%- help %>"<% } %>>
|
||||
<div class='CDB-Text CDB-FontSize-small u-altTextColor is-semibold u-upperCase'><%= _t('form-components.editors.fill.input-color.img') %></div>
|
||||
</button>
|
||||
<% } %>
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="Editor-boxModalHeader">
|
||||
<nav class="CDB-NavMenu">
|
||||
<ul class="CDB-NavMenu-Inner CDB-NavMenu-inner--no-margin CDB-NavMenu-inner--is-dropdown CDB-Text is-semibold CDB-Size-medium js-menu"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="js-content"></div>
|
||||
@@ -0,0 +1,16 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var template = require('./fill-tab.tpl');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
initialize: function (opts) {
|
||||
if (!opts.stackLayoutModel) throw new Error('stackLayoutModel is required');
|
||||
|
||||
this._stackLayoutModel = opts.stackLayoutModel;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(template());
|
||||
return this;
|
||||
}
|
||||
|
||||
});
|
||||
223
lib/assets/javascripts/builder/components/form-components/editors/fill/fill.js
Executable file
223
lib/assets/javascripts/builder/components/form-components/editors/fill/fill.js
Executable file
@@ -0,0 +1,223 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
|
||||
var DialogModel = require('builder/components/dialog/dialog-model');
|
||||
var DialogView = require('builder/components/dialog/dialog-view');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
|
||||
var InputCollection = require('./input-collection');
|
||||
var InputNumber = require('builder/components/input-number/input-number');
|
||||
var InputColor = require('builder/components/input-color/input-color');
|
||||
|
||||
var template = require('builder/components/input-fill/input-fill.tpl');
|
||||
|
||||
var PopupManager = require('builder/components/popup-manager');
|
||||
|
||||
var INPUT_TYPE_MAP = {
|
||||
'size': InputNumber,
|
||||
'color': InputColor,
|
||||
'image': InputColor
|
||||
};
|
||||
|
||||
var QUANTIFICATION_REF = {
|
||||
'Jenks': 'jenks',
|
||||
'Equal Interval': 'equal',
|
||||
'Heads/Tails': 'headtails',
|
||||
'Quantile': 'quantiles'
|
||||
};
|
||||
|
||||
var MIN_IMAGE_SIZE = 20;
|
||||
var MIN_MARKER_SIZE = 7;
|
||||
|
||||
Backbone.Form.editors.Fill = Backbone.Form.editors.Base.extend({
|
||||
className: 'Form-InputFill CDB-OptionInput CDB-Text js-input',
|
||||
|
||||
events: {
|
||||
focus: function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
blur: function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts); // Options
|
||||
|
||||
this.options = _.extend(
|
||||
this.options,
|
||||
{
|
||||
columns: this.options.options,
|
||||
query: this.options.query,
|
||||
configModel: this.options.configModel,
|
||||
userModel: this.options.userModel,
|
||||
editorAttrs: this.options.editorAttrs,
|
||||
modals: this.options.modals
|
||||
}
|
||||
);
|
||||
|
||||
this._keyAttr = opts.key;
|
||||
this.dialogMode = this.options.dialogMode || DialogConstants.Mode.DEFAULT;
|
||||
|
||||
this._initBinds();
|
||||
this._initViews();
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
this.$el.append(template());
|
||||
|
||||
if (this.options.editorAttrs && this.options.editorAttrs.disabled) {
|
||||
this.$el.addClass('is-disabled');
|
||||
}
|
||||
|
||||
this._initFillDialog();
|
||||
this._initInputFields();
|
||||
|
||||
this._popupManager = new PopupManager(this.cid, this.$el, this._dialogView.$el);
|
||||
},
|
||||
|
||||
_initInputFields: function () {
|
||||
var inputKlass;
|
||||
|
||||
this._inputCollection = new InputCollection();
|
||||
|
||||
_.each(this.model.get(this._keyAttr), function (value, key) {
|
||||
this._inputCollection.add(_.extend({ type: key }, value));
|
||||
}, this);
|
||||
|
||||
this._inputCollection.each(function (inputModel) {
|
||||
var type = inputModel.get('type');
|
||||
|
||||
var Klass = INPUT_TYPE_MAP[type];
|
||||
|
||||
if (!Klass) {
|
||||
throw new Error(type + ' is not a valid type of constructor');
|
||||
}
|
||||
|
||||
if (inputModel.get('quantification') && QUANTIFICATION_REF[inputModel.get('quantification')]) {
|
||||
inputModel.set('quantification', QUANTIFICATION_REF[inputModel.get('quantification')], { silent: true });
|
||||
}
|
||||
|
||||
inputKlass = new Klass(({
|
||||
model: inputModel,
|
||||
columns: this.options.columns,
|
||||
query: this.options.query,
|
||||
configModel: this.options.configModel,
|
||||
userModel: this.options.userModel,
|
||||
modals: this.options.modals,
|
||||
editorAttrs: this.options.editorAttrs ? this.options.editorAttrs[type] : {},
|
||||
disabled: this.options.editorAttrs && this.options.editorAttrs.disabled
|
||||
}));
|
||||
|
||||
inputKlass.bind('click', this._onInputClick, this);
|
||||
|
||||
this.$('.js-content').append(inputKlass.render().$el);
|
||||
}, this);
|
||||
|
||||
this._inputCollection.bind('inputChanged', this._onInputChanged, this);
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.applyESCBind(function () {
|
||||
this._removeDialog();
|
||||
});
|
||||
this.applyClickOutsideBind(function () {
|
||||
this._removeDialog();
|
||||
});
|
||||
},
|
||||
|
||||
_onInputClick: function (inputModel) {
|
||||
if (inputModel.get('selected')) {
|
||||
this._removeDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
inputModel.set('selected', true);
|
||||
this._dialogView.model.set('createContentView', inputModel.get('createContentView'));
|
||||
this._dialogView.render();
|
||||
this._dialogView.show();
|
||||
|
||||
this._popupManager.append(this.dialogMode);
|
||||
this._popupManager.track();
|
||||
},
|
||||
|
||||
_onInputChanged: function (model) {
|
||||
this._adjustImageSize(model);
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_adjustImageSize: function (model) {
|
||||
if (model.get('type') !== 'color' && model.get('kind') !== 'marker') {
|
||||
return;
|
||||
}
|
||||
|
||||
var size = this._inputCollection.findWhere({ type: 'size' });
|
||||
|
||||
if (!model.get('image') && model.hasChanged('image') && model.previous('image')) {
|
||||
size.set('fixed', MIN_MARKER_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!model.get('images') && model.hasChanged('images')) {
|
||||
size.set('fixed', MIN_MARKER_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
var changedImage = model.get('image') && model.hasChanged('image') && !model.previous('image');
|
||||
var hasImages = _.isEmpty(_.compact(model.get('images')));
|
||||
var hadImages = _.isEmpty(_.compact(model.previous('images')));
|
||||
var changedImages = !hasImages && model.hasChanged('images') && hadImages;
|
||||
|
||||
if (changedImage || changedImages) {
|
||||
if (size && size.get('fixed') < MIN_IMAGE_SIZE) {
|
||||
size.set('fixed', MIN_IMAGE_SIZE);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_initFillDialog: function () {
|
||||
var dialogModel = new DialogModel();
|
||||
|
||||
this.listenToOnce(dialogModel, 'destroy', function () {
|
||||
this._dialogView = null;
|
||||
this.stopListening(dialogModel);
|
||||
});
|
||||
|
||||
this._dialogView = new DialogView({
|
||||
model: dialogModel
|
||||
});
|
||||
},
|
||||
|
||||
_removeDialog: function (dialog) {
|
||||
this._inputCollection.unselect();
|
||||
this._dialogView.clean();
|
||||
this._popupManager.untrack();
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
if (this.hasFocus) return;
|
||||
this.$('.js-fillInput').focus();
|
||||
},
|
||||
|
||||
blur: function () {
|
||||
if (!this.hasFocus) return;
|
||||
this.$('.js-fillInput').blur();
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
return this._inputCollection.getValues();
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
// TODO: add setter
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._removeDialog();
|
||||
this._popupManager.destroy();
|
||||
this._inputCollection.unbind('inputChanged', this._onInputChanged, this);
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
|
||||
var INPUT_TYPE_ORDER = ['size', 'color'];
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
constructor: function (models, options) {
|
||||
options = _.extend(options || {}, { silent: false });
|
||||
Backbone.Collection.prototype.constructor.call(this, models, options);
|
||||
},
|
||||
|
||||
comparator: function (m) {
|
||||
return INPUT_TYPE_ORDER.indexOf(m.get('type'));
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.bind('change:selected', this._onSelectedChange, this);
|
||||
this.bind('change', this._onModelsChanged, this);
|
||||
},
|
||||
|
||||
getSelected: function () {
|
||||
return this.find(function (model) {
|
||||
return model.get('selected');
|
||||
});
|
||||
},
|
||||
|
||||
unselect: function () {
|
||||
this.each(function (model) {
|
||||
model.set('selected', false);
|
||||
}, this);
|
||||
},
|
||||
|
||||
_onSelectedChange: function (itemModel, isSelected) {
|
||||
if (!isSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.each(function (model) {
|
||||
if (model !== itemModel) {
|
||||
model.set('selected', false);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
_onModelsChanged: function (mdl) {
|
||||
var mdlChanges = mdl.changed;
|
||||
|
||||
// If there is any change about selected, don't propagate it
|
||||
if (_.isEmpty(mdlChanges) || (_.size(mdlChanges) === 1 && mdl.changed.hasOwnProperty('selected'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.trigger('inputChanged', mdl, this);
|
||||
},
|
||||
|
||||
getValues: function () {
|
||||
return this.reduce(function (memo, mdl) {
|
||||
var data = {};
|
||||
var typeValues = mdl.toJSON();
|
||||
|
||||
if (typeValues.fixed) {
|
||||
data.fixed = typeValues.fixed;
|
||||
|
||||
if (typeValues.type === 'color') {
|
||||
if (typeValues.image) {
|
||||
data.image = typeValues.image;
|
||||
}
|
||||
if (typeValues.kind) {
|
||||
data.kind = typeValues.kind;
|
||||
}
|
||||
data.opacity = typeValues.opacity;
|
||||
}
|
||||
} else {
|
||||
data = _.omit(typeValues, ['createContentView', 'selected', 'type']);
|
||||
}
|
||||
|
||||
memo[typeValues.type] = data;
|
||||
|
||||
return memo;
|
||||
}, {});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
<% if (status === 'fetching') { %>
|
||||
<div class="InputColorCategory-loader js-loader">
|
||||
<div class="CDB-LoaderIcon is-dark">
|
||||
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
|
||||
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<% } else if (status === 'error') { %>
|
||||
<div class="u-flex u-alignCenter u-justifyCenter CDB-Text CDB-Size-medium u-bSpace--m u-tSpace--m u-errorTextColor"><%- _t('components.backbone-forms.data-observatory.dropdown.error', {
|
||||
type: type
|
||||
}) %></div>
|
||||
<% } else if (status === 'empty') { %>
|
||||
<div class="u-flex u-alignCenter u-justifyCenter CDB-Text CDB-Size-medium u-bSpace--m u-tSpace--m"><%- _t('components.backbone-forms.lazy-select.empty', {
|
||||
type: type
|
||||
}) %></div>
|
||||
<% } %>
|
||||
@@ -0,0 +1,124 @@
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var CustomListView = require('builder/components/custom-list/custom-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var template = require('./lazy-list.tpl');
|
||||
var SearchView = require('./lazy-search-view');
|
||||
var statusTemplate = require('./lazy-list-view-states.tpl');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'searchCollection',
|
||||
'lazySearch'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
className: 'CDB-Box-modal CustomList',
|
||||
module: 'components:lazy-load:lazy-list-view',
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
this._type = opts.type || _t('components.backbone-forms.lazy-select.type');
|
||||
this.model = new Backbone.Model({
|
||||
query: '',
|
||||
visible: false
|
||||
});
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this.$el.html(template());
|
||||
this._createSearchView();
|
||||
this._renderListSection();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._searchCollection.stateModel, 'change:state', this._renderListSection);
|
||||
this.listenTo(this.model, 'change:query', this._search);
|
||||
|
||||
this.listenTo(this.model, 'change:visible', function (mdl, isVisible) {
|
||||
isVisible ? this.render() : this.clearSubViews();
|
||||
this._toggleVisibility();
|
||||
});
|
||||
},
|
||||
|
||||
_createListView: function () {
|
||||
if (this._listView) {
|
||||
this._listView.clean();
|
||||
this.removeView(this._listView);
|
||||
}
|
||||
|
||||
this._listView = new CustomListView({
|
||||
className: '',
|
||||
typeLabel: this._type,
|
||||
showSearch: false,
|
||||
collection: this._searchCollection,
|
||||
searchPlaceholder: this.options.searchPlaceholder
|
||||
});
|
||||
|
||||
this.addView(this._listView);
|
||||
this._listView.show();
|
||||
this.$('.js-list').html(this._listView.$el);
|
||||
},
|
||||
|
||||
_createStatusView: function (status) {
|
||||
var el = statusTemplate({
|
||||
status: status,
|
||||
type: this._type
|
||||
});
|
||||
|
||||
this.$('.js-list').html(el);
|
||||
},
|
||||
|
||||
_renderListSection: function () {
|
||||
var status = this._searchCollection.stateModel.get('state');
|
||||
|
||||
if (status === 'fetched') {
|
||||
this._createListView();
|
||||
} else {
|
||||
this._createStatusView(status);
|
||||
}
|
||||
},
|
||||
|
||||
_createSearchView: function () {
|
||||
this._searchView = new SearchView({
|
||||
model: this.model
|
||||
});
|
||||
this.addView(this._searchView);
|
||||
this.$('.js-search').append(this._searchView.render().el);
|
||||
},
|
||||
|
||||
_search: function () {
|
||||
var keyword = this.model.get('query');
|
||||
|
||||
// This function is passed from the parent
|
||||
this._lazySearch(keyword);
|
||||
},
|
||||
|
||||
_isSerching: function () {
|
||||
return !!this.model.get('query');
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.model.set('visible', true);
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.model.set('visible', false);
|
||||
},
|
||||
|
||||
toggle: function () {
|
||||
this.model.set('visible', !this.model.get('visible'));
|
||||
},
|
||||
|
||||
isVisible: function () {
|
||||
return this.model.get('visible');
|
||||
},
|
||||
|
||||
_toggleVisibility: function () {
|
||||
this.$el.toggleClass('is-visible', !!this.isVisible());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
<div class="js-search"></div>
|
||||
<div class="js-list"></div>
|
||||
@@ -0,0 +1,115 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var cdb = require('internal-carto.js');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var CustomListCollection = require('builder/components/custom-list/custom-list-collection');
|
||||
var BaseModel = require('builder/components/custom-list/custom-list-item-model');
|
||||
|
||||
var queryTemplate = _.template("SELECT DISTINCT <%= column %> FROM (<%= sql %>) _table_sql WHERE <%= column %> ilike '%<%= search %>%' ORDER BY <%= column %> ASC");
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'configModel',
|
||||
'rowModel',
|
||||
'column'
|
||||
];
|
||||
|
||||
module.exports = CustomListCollection.extend({
|
||||
initialize: function (models, options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
|
||||
this._query = options.nodeDefModel.querySchemaModel.get('query');
|
||||
|
||||
var configModel = options.configModel;
|
||||
this.SQL = new cdb.SQL({
|
||||
user: configModel.get('user_name'),
|
||||
sql_api_template: configModel.get('sql_api_template'),
|
||||
api_key: configModel.get('api_key')
|
||||
});
|
||||
|
||||
this.stateModel = new Backbone.Model({
|
||||
state: _.isEmpty(models) ? 'empty' : 'fetched'
|
||||
});
|
||||
|
||||
CustomListCollection.prototype.initialize.call(this, models, options);
|
||||
},
|
||||
|
||||
model: function (attrs, opts) {
|
||||
// label and val to custom list compatibility
|
||||
var key = Object.keys(attrs)[0];
|
||||
var o = {};
|
||||
o.val = attrs[key];
|
||||
o.label = attrs[key];
|
||||
|
||||
return new BaseModel(o);
|
||||
},
|
||||
|
||||
_onSelectedChange: function (changedModel, isSelected) {
|
||||
if (this.type === 'multiple') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
this.each(function (m) {
|
||||
if (m.cid !== changedModel.cid) {
|
||||
m.set({ selected: false }, { silent: true });
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
buildQuery: function (search) {
|
||||
var column = this.options.column;
|
||||
return queryTemplate({
|
||||
sql: this._query,
|
||||
search: search,
|
||||
column: this._rowModel.get(column)
|
||||
});
|
||||
},
|
||||
|
||||
fetch: function (search) {
|
||||
this.deferred = $.Deferred();
|
||||
var sqlQuery = this.buildQuery(search);
|
||||
this.stateModel.set('state', 'fetching');
|
||||
|
||||
this.SQL.execute(sqlQuery, null, {
|
||||
extra_params: ['page', 'rows_per_page'],
|
||||
page: 0,
|
||||
rows_per_page: 40,
|
||||
success: function (data) {
|
||||
this._onFetchSuccess(data);
|
||||
this.stateModel.set('state', 'fetched');
|
||||
this.deferred.resolve();
|
||||
}.bind(this),
|
||||
error: function () {
|
||||
this.stateModel.set('state', 'error');
|
||||
this.deferred.reject();
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
return this.deferred.promise();
|
||||
},
|
||||
|
||||
_onFetchSuccess: function (data) {
|
||||
this.reset(data.rows);
|
||||
if (_.isEmpty(this.models)) {
|
||||
this.stateModel.set('state', 'empty');
|
||||
}
|
||||
},
|
||||
|
||||
isFetching: function () {
|
||||
return this.getState() === 'fetching';
|
||||
},
|
||||
|
||||
getState: function () {
|
||||
return this.stateModel.get('state');
|
||||
},
|
||||
|
||||
getItem: function (value) {
|
||||
return this.findWhere({ val: value });
|
||||
},
|
||||
|
||||
isAsync: function () {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
var _ = require('underscore');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var utils = require('builder/helpers/utils');
|
||||
var template = require('./lazy-search.tpl');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'model'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
className: 'CDB-Box-modalHeader',
|
||||
|
||||
events: {
|
||||
'input .js-input-search': '_search'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(template());
|
||||
|
||||
// Focus the input when rendered
|
||||
setTimeout(function () {
|
||||
this._getInput().focus();
|
||||
}.bind(this), 100);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_onClickFilters: function (e) {
|
||||
this.killEvent(e);
|
||||
this.trigger('filters');
|
||||
},
|
||||
|
||||
_search: _.debounce(function (e) {
|
||||
var query = this._getInput().val();
|
||||
query = query.toLowerCase();
|
||||
query = utils.sanitizeHtml(query);
|
||||
this.model.set('query', query);
|
||||
}, 500),
|
||||
|
||||
_getInput: function () {
|
||||
return this.$('.js-input-search');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="CDB-Box-modalHeaderItem">
|
||||
<div class="u-flex u-grow">
|
||||
<div class="u-flex u-grow">
|
||||
<input type="text" name="text" autocomplete="off" placeholder="<%- _t('components.backbone-forms.lazy-select.search') %>" class="CDB-InputTextPlain CDB-Text js-input-search">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
<% if (typeof isLoading != 'undefined' && isLoading) { %>
|
||||
<div class="u-flex">
|
||||
<div class="CDB-LoaderIcon CDB-LoaderIcon--small is-dark">
|
||||
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
|
||||
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="u-lSpace u-secondaryTextColor"><%- _t('components.backbone-forms.select.loading') %></span>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<%- label %>
|
||||
<% } %>
|
||||
@@ -0,0 +1,200 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var template = require('./lazy-select.tpl');
|
||||
var selectedItemTemplate = require('./lazy-select-item.tpl');
|
||||
var ListView = require('./lazy-list-view');
|
||||
var PopupManager = require('builder/components/popup-manager');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var SearchCollection = require('./lazy-search-collection');
|
||||
|
||||
var ENTER_KEY_CODE = 13;
|
||||
|
||||
Backbone.Form.editors.LazySelect = Backbone.Form.editors.Base.extend({
|
||||
|
||||
tagName: 'div',
|
||||
className: 'u-ellipsis Editor-formSelect',
|
||||
|
||||
events: {
|
||||
'click .js-button': '_onButtonClick',
|
||||
'keydown .js-button': '_onButtonKeyDown',
|
||||
'focus .js-button': function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
'blur': function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
options: {
|
||||
selectedItemTemplate: selectedItemTemplate
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, options);
|
||||
EditorHelpers.setOptions(this, options);
|
||||
|
||||
this.template = options.template || template;
|
||||
this.dialogMode = this.options.dialogMode || DialogConstants.Mode.DEFAULT;
|
||||
|
||||
var fetchOptions = {
|
||||
configModel: this.options.configModel,
|
||||
nodeDefModel: this.options.nodeDefModel,
|
||||
rowModel: this.model,
|
||||
column: this.options.column
|
||||
};
|
||||
|
||||
if (this.options.options != null) {
|
||||
this.searchCollection = new SearchCollection(this.options.options, fetchOptions);
|
||||
} else {
|
||||
this.searchCollection = this.options.collection;
|
||||
}
|
||||
|
||||
this._initBinds();
|
||||
|
||||
var value = this.model.get(this.options.keyAttr);
|
||||
if (value != null) {
|
||||
this.setValue(value);
|
||||
}
|
||||
|
||||
var lazySearch = function (search) {
|
||||
this.searchCollection.fetch(search);
|
||||
}.bind(this);
|
||||
var type = this.model.get(this.options.column);
|
||||
|
||||
this._listView = new ListView({
|
||||
configModel: this.options.configModel,
|
||||
searchCollection: this.searchCollection,
|
||||
lazySearch: lazySearch,
|
||||
type: type,
|
||||
searchPlaceholder: this.options.searchPlaceholder
|
||||
});
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var isEmpty = !this.searchCollection.length;
|
||||
var isDisabled = this.options.disabled;
|
||||
var name = this.model.get(this.options.keyAttr);
|
||||
var placeholder = this._getPlaceholder();
|
||||
var isNull = this._hasValue();
|
||||
var label = isNull ? placeholder : name;
|
||||
var title = name || '';
|
||||
|
||||
this.$el.html(
|
||||
this.template({
|
||||
title: title,
|
||||
label: label,
|
||||
keyAttr: this.options.keyAttr,
|
||||
isEmpty: isEmpty,
|
||||
isDisabled: isDisabled,
|
||||
isNull: isNull
|
||||
})
|
||||
);
|
||||
|
||||
this._popupManager = new PopupManager(this.cid, this.$el, this._listView.$el);
|
||||
this._popupManager.append(this.dialogMode);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var hide = function () {
|
||||
this._listView.hide();
|
||||
this._popupManager && this._popupManager.untrack();
|
||||
}.bind(this);
|
||||
|
||||
this.applyESCBind(hide);
|
||||
this.applyClickOutsideBind(hide);
|
||||
|
||||
this.listenTo(this.searchCollection, 'change:selected', this._onItemSelected);
|
||||
},
|
||||
|
||||
_destroyBinds: function () {
|
||||
this.stopListening(this.searchCollection);
|
||||
Backbone.Form.editors.Base.prototype._destroyBinds.call(this);
|
||||
},
|
||||
|
||||
_getPlaceholder: function (isDisabled) {
|
||||
var keyAttr = this.options.keyAttr;
|
||||
var placeholder = this.options.placeholder || _t('components.backbone-forms.select.placeholder', { keyAttr: keyAttr });
|
||||
return placeholder;
|
||||
},
|
||||
|
||||
_hasValue: function () {
|
||||
var name = this.model.get(this.options.keyAttr);
|
||||
return name == null || name === '';
|
||||
},
|
||||
|
||||
_onItemSelected: function (model) {
|
||||
this._listView.hide();
|
||||
this._popupManager.untrack();
|
||||
this._renderButton(model).focus();
|
||||
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_onButtonClick: function () {
|
||||
this._listView.toggle();
|
||||
this._listView.isVisible() ? this._popupManager.track() : this._popupManager.untrack();
|
||||
},
|
||||
|
||||
_onButtonKeyDown: function (ev) {
|
||||
if (ev.which === ENTER_KEY_CODE) {
|
||||
ev.preventDefault();
|
||||
if (!this._listView.isVisible()) {
|
||||
ev.stopPropagation();
|
||||
this._onButtonClick();
|
||||
} else {
|
||||
this._popupManager.track();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
this.$('.js-button').focus();
|
||||
},
|
||||
|
||||
blur: function () {
|
||||
this.$('.js-button').blur();
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var item = this.searchCollection.getSelectedItem();
|
||||
if (item) {
|
||||
return item.getValue();
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
var selectedModel = this.searchCollection.getSelectedItem();
|
||||
if (selectedModel) {
|
||||
this._renderButton(selectedModel);
|
||||
} else {
|
||||
this._renderButton(value);
|
||||
}
|
||||
this.value = value;
|
||||
},
|
||||
|
||||
_renderButton: function (model) {
|
||||
var button = this.$('.js-button');
|
||||
var label = model.getName && model.getName() || model;
|
||||
var $html = this.options.selectedItemTemplate({
|
||||
label: label
|
||||
});
|
||||
|
||||
button
|
||||
.toggleClass('is-empty', label === '')
|
||||
.attr('title', label)
|
||||
.html($html);
|
||||
|
||||
return button;
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._popupManager && this._popupManager.destroy();
|
||||
this._listView && this._listView.clean();
|
||||
this._destroyBinds();
|
||||
Backbone.Form.editors.Base.prototype.remove.call(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="CDB-InputText CDB-Text is-cursor js-button u-ellipsis
|
||||
<% if (isDisabled) { %> is-disabled <% } %>
|
||||
<% if (!label) { %> is-empty <% } %>
|
||||
<% if (isNull) { %> is-empty <% } %>"
|
||||
tabindex="0"
|
||||
title="<%- title %>">
|
||||
<% if (isEmpty && isNull) { %>
|
||||
<%- _t('components.backbone-forms.select.empty') %>
|
||||
<% } else { %>
|
||||
<%- label %>
|
||||
<% } %>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
|
||||
Backbone.Form.editors.List.CategoryModel = Backbone.Form.editors.NestedModel.extend({
|
||||
|
||||
initialize: function (options) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, options);
|
||||
EditorHelpers.setOptions(this, options);
|
||||
|
||||
if (!this.form) throw new Error('Missing required option "form"');
|
||||
if (!options.schema.model) throw new Error('Missing required "schema.model" option for NestedModel editor');
|
||||
},
|
||||
|
||||
render: function () {
|
||||
// Get the constructor for creating the nested form; i.e. the same constructor as used by the parent form
|
||||
var NestedForm = this.form.constructor;
|
||||
|
||||
var data = this.value || {};
|
||||
var NestedModel = this.schema.model;
|
||||
|
||||
// Wrap the data in a model if it isn't already a model instance
|
||||
var modelInstance = (data.constructor === NestedModel) ? data : new NestedModel(data, this.options);
|
||||
|
||||
this.nestedForm = new NestedForm({
|
||||
model: modelInstance,
|
||||
idPrefix: this.cid + '_',
|
||||
fieldTemplate: 'nestedField'
|
||||
});
|
||||
|
||||
this._observeFormEvents();
|
||||
|
||||
// Render form
|
||||
this.$el.html(this.nestedForm.render().el);
|
||||
|
||||
if (this.hasFocus) this.trigger('blur', this);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,169 @@
|
||||
var Backbone = require('backbone');
|
||||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
|
||||
var ENTER_KEY_CODE = 13;
|
||||
|
||||
/**
|
||||
* A single item in the list
|
||||
*
|
||||
* @param {editors.List} options.list The List editor instance this item belongs to
|
||||
* @param {Function} options.Editor Editor constructor function
|
||||
* @param {String} options.key Model key
|
||||
* @param {Mixed} options.value Value
|
||||
* @param {Object} options.schema Field schema
|
||||
*/
|
||||
Backbone.Form.editors.List.Item = Backbone.Form.editors.Base.extend({
|
||||
|
||||
events: {
|
||||
'click [data-action="remove"]': function (event) {
|
||||
event.preventDefault();
|
||||
this.list.removeItem(this);
|
||||
},
|
||||
'keydown input[type=text]': function (event) {
|
||||
if (event.keyCode !== ENTER_KEY_CODE) return;
|
||||
event.preventDefault();
|
||||
this.list.addItem(null, true);
|
||||
this.list.$list.find('input:last').focus();
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
EditorHelpers.setOptions(this, options);
|
||||
this.list = options.list;
|
||||
this.schema = options.schema || this.list.schema;
|
||||
this.Editor = options.Editor || Backbone.Form.editors.Text;
|
||||
this.template = options.template || this.schema.itemTemplate || this.constructor.template;
|
||||
this.errorClassName = options.errorClassName || this.constructor.errorClassName;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
// Create editor
|
||||
this.editor = new this.Editor({
|
||||
key: this.options.key,
|
||||
schema: this.schema,
|
||||
value: this.options.value,
|
||||
list: this.options.list,
|
||||
item: this,
|
||||
form: this.options.form,
|
||||
trackingClass: this.options.trackingClass
|
||||
}, {
|
||||
userModel: this.options.userModel,
|
||||
configModel: this.options.configModel,
|
||||
modals: this.options.modals
|
||||
}).render();
|
||||
|
||||
// Create main element
|
||||
var $el = $($.trim(this.template()));
|
||||
|
||||
$el.find('[data-editor]').append(this.editor.el);
|
||||
|
||||
var $tooltip = $el.find('.js-remove-help');
|
||||
|
||||
if ($tooltip.length) {
|
||||
this._removeTooltip();
|
||||
|
||||
this._tooltip = new TipsyTooltipView({
|
||||
el: $tooltip,
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return _t('editor.legend.tooltips.item.remove');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Replace the entire element so there isn't a wrapper tag
|
||||
this.setElement($el);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
return this.editor.getValue();
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
this.editor.setValue(value);
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
this.editor.focus();
|
||||
},
|
||||
|
||||
blur: function () {
|
||||
this.editor.blur();
|
||||
},
|
||||
|
||||
validate: function () {
|
||||
var value = this.getValue();
|
||||
var formValues = this.list.form ? this.list.form.getValue() : {};
|
||||
var validators = this.schema.validators;
|
||||
var getValidator = this.getValidator;
|
||||
|
||||
if (!validators) return null;
|
||||
|
||||
// Run through validators until an error is found
|
||||
var error = null;
|
||||
_.every(validators, function (validator) {
|
||||
error = getValidator(validator)(value, formValues);
|
||||
|
||||
return !!error;
|
||||
});
|
||||
|
||||
// Show/hide error
|
||||
if (error) {
|
||||
this.setError(error);
|
||||
} else {
|
||||
this.clearError();
|
||||
}
|
||||
|
||||
// Return error to be aggregated by list
|
||||
return error ? error : null; // eslint-disable-line
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a validation error
|
||||
*/
|
||||
setError: function (err) {
|
||||
this.$el.addClass(this.errorClassName);
|
||||
this.$el.attr('title', err.message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide validation errors
|
||||
*/
|
||||
clearError: function () {
|
||||
this.$el.removeClass(this.errorClassName);
|
||||
this.$el.attr('title', null);
|
||||
},
|
||||
|
||||
_removeTooltip: function () {
|
||||
if (this._tooltip) {
|
||||
this._tooltip.clean();
|
||||
}
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._removeTooltip();
|
||||
this.editor.remove();
|
||||
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this.$el.remove();
|
||||
}
|
||||
}, {
|
||||
|
||||
/* eslint-disable */
|
||||
template: _.template('\
|
||||
<div>\
|
||||
<span data-editor></span>\
|
||||
<button type="button" data-action="remove">×</button>\
|
||||
</div>\
|
||||
', null, Backbone.Form.templateSettings),
|
||||
errorClassName: 'error'
|
||||
/* eslint-enable */
|
||||
});
|
||||
330
lib/assets/javascripts/builder/components/form-components/editors/list/list.js
Executable file
330
lib/assets/javascripts/builder/components/form-components/editors/list/list.js
Executable file
@@ -0,0 +1,330 @@
|
||||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
|
||||
var OPTIONS = [
|
||||
'userModel',
|
||||
'configModel',
|
||||
'modals',
|
||||
'layerDefinitionModel',
|
||||
'trackingClass'
|
||||
];
|
||||
|
||||
Backbone.Form.editors.List = Backbone.Form.editors.Base.extend({
|
||||
|
||||
events: {
|
||||
'click [data-action="add"]': function (event) {
|
||||
event.preventDefault();
|
||||
if (this._canAddNewItems()) {
|
||||
this.addItem(null, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
options = options || {};
|
||||
|
||||
var editors = Backbone.Form.editors;
|
||||
|
||||
editors.Base.prototype.initialize.call(this, options);
|
||||
EditorHelpers.setOptions(this, options);
|
||||
|
||||
var schema = this.schema;
|
||||
if (!schema) throw new Error("Missing required option 'schema'");
|
||||
|
||||
this.template = options.template || schema.listTemplate || this.constructor.template;
|
||||
|
||||
// Determine the editor to use
|
||||
this.Editor = (function () {
|
||||
var type = schema.itemType;
|
||||
|
||||
// Default to Text
|
||||
if (!type) return editors.Text;
|
||||
|
||||
// Use List-specific version if available
|
||||
if (editors.List[type]) return editors.List[type];
|
||||
|
||||
// Or whichever was passed
|
||||
return editors[type];
|
||||
})();
|
||||
|
||||
_.each(OPTIONS, function (item) {
|
||||
if (this.options[item]) {
|
||||
this['_' + item] = this.options[item];
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.items = [];
|
||||
this.errors = [];
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var self = this;
|
||||
var value = this.value || [];
|
||||
|
||||
// Create main element
|
||||
var $el = $($.trim(this.template()));
|
||||
|
||||
// Store a reference to the list (item container)
|
||||
this.$list = $el.is('[data-items]') ? $el : $el.find('[data-items]');
|
||||
|
||||
this.setElement($el);
|
||||
this.$el.attr('id', this.id);
|
||||
this.$el.attr('name', this.key);
|
||||
|
||||
// Add existing items
|
||||
if (value.length) {
|
||||
_.each(value, function (itemValue) {
|
||||
self.addItem(itemValue);
|
||||
});
|
||||
} else {
|
||||
// If no existing items create an empty one, unless the editor specifies otherwise
|
||||
if (!this.Editor.isAsync) this.addItem(null, true);
|
||||
}
|
||||
|
||||
if (this.hasFocus) this.trigger('blur', this);
|
||||
|
||||
this.validate();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
if (this.options.maxItems) {
|
||||
this.listenTo(this, 'add remove', this._setAddButtonState);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new item to the list
|
||||
* @param {Mixed} [value] Value for the new item editor
|
||||
* @param {Boolean} [userInitiated] If the item was added by the user clicking 'add' or pressing `Enter`
|
||||
*/
|
||||
addItem: function (value, userInitiated) {
|
||||
var self = this;
|
||||
var editors = Backbone.Form.editors;
|
||||
|
||||
var defaultOptions = {
|
||||
list: this,
|
||||
form: this.form,
|
||||
schema: this.schema,
|
||||
value: value,
|
||||
Editor: this.Editor,
|
||||
key: this.key
|
||||
};
|
||||
|
||||
var customOptions = _.reduce(OPTIONS, function (memo, item) {
|
||||
if (this.options[item]) {
|
||||
memo[item] = this['_' + item];
|
||||
}
|
||||
return memo;
|
||||
}, {}, this);
|
||||
|
||||
var options = _.extend({}, defaultOptions, customOptions);
|
||||
|
||||
// Create the item
|
||||
var item = new editors.List.Item(options).render();
|
||||
|
||||
var _addItem = function () {
|
||||
self.items.push(item);
|
||||
self.$list.append(item.el);
|
||||
|
||||
item.editor.on('all', function (event) {
|
||||
if (event === 'change') return;
|
||||
|
||||
// args = ["key:change", itemEditor, fieldEditor]
|
||||
var args = _.toArray(arguments);
|
||||
args[0] = 'item:' + event;
|
||||
args.splice(1, 0, self);
|
||||
// args = ["item:key:change", this=listEditor, itemEditor, fieldEditor]
|
||||
|
||||
editors.List.prototype.trigger.apply(this, args);
|
||||
}, self);
|
||||
|
||||
item.editor.on('change', function () {
|
||||
if (!item.addEventTriggered) {
|
||||
item.addEventTriggered = true;
|
||||
this.trigger('add', this, item.editor);
|
||||
}
|
||||
this.trigger('item:change', this, item.editor);
|
||||
this.trigger('change', this);
|
||||
}, self);
|
||||
|
||||
item.editor.on('focus', function () {
|
||||
if (this.hasFocus) return;
|
||||
this.trigger('focus', this);
|
||||
}, self);
|
||||
item.editor.on('blur', function () {
|
||||
if (!this.hasFocus) return;
|
||||
var self = this;
|
||||
setTimeout(function () {
|
||||
if (_.find(self.items, function (item) {
|
||||
return item.editor.hasFocus;
|
||||
})) return;
|
||||
self.trigger('blur', self);
|
||||
}, 0);
|
||||
}, self);
|
||||
|
||||
if (userInitiated || value) {
|
||||
item.addEventTriggered = true;
|
||||
}
|
||||
|
||||
if (userInitiated) {
|
||||
self.trigger('add', self, item.editor);
|
||||
self.trigger('change', self);
|
||||
}
|
||||
};
|
||||
|
||||
// Check if we need to wait for the item to complete before adding to the list
|
||||
if (this.Editor.isAsync) {
|
||||
item.editor.on('readyToAdd', _addItem, this);
|
||||
} else {
|
||||
// Most editors can be added automatically
|
||||
_addItem();
|
||||
item.editor.focus();
|
||||
}
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an item from the list
|
||||
* @param {List.Item} item
|
||||
*/
|
||||
removeItem: function (item) {
|
||||
// Confirm delete
|
||||
var confirmMsg = this.schema.confirmDelete;
|
||||
if (confirmMsg && !confirm(confirmMsg)) return; // eslint-disable-line
|
||||
|
||||
var index = _.indexOf(this.items, item);
|
||||
|
||||
this.items[index].remove();
|
||||
this.items.splice(index, 1);
|
||||
|
||||
if (item.addEventTriggered) {
|
||||
this.trigger('remove', this, item.editor);
|
||||
this.trigger('change', this);
|
||||
}
|
||||
|
||||
if (!this.items.length && !this.Editor.isAsync) this.addItem();
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var values = _.map(this.items, function (item) {
|
||||
return item.getValue();
|
||||
});
|
||||
|
||||
// Filter empty items
|
||||
return _.without(values, undefined, '');
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
this.value = value;
|
||||
this.render();
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
if (this.hasFocus) return;
|
||||
|
||||
if (this.items[0]) this.items[0].editor.focus();
|
||||
},
|
||||
|
||||
blur: function () {
|
||||
if (!this.hasFocus) return;
|
||||
|
||||
var focusedItem = _.find(this.items, function (item) {
|
||||
return item.editor.hasFocus;
|
||||
});
|
||||
|
||||
if (focusedItem) focusedItem.editor.blur();
|
||||
},
|
||||
|
||||
/**
|
||||
* Override default remove function in order to remove item views
|
||||
*/
|
||||
remove: function () {
|
||||
_.invoke(this.items, 'remove');
|
||||
|
||||
Backbone.Form.editors.Base.prototype.remove.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Run validation
|
||||
*
|
||||
* @return {Object|Null}
|
||||
*/
|
||||
validate: function () {
|
||||
if (!this.validators) return null;
|
||||
|
||||
// Collect errors
|
||||
var errors = _.map(this.items, function (item) {
|
||||
return item.validate();
|
||||
});
|
||||
|
||||
// Max items Check
|
||||
if (this.options.maxItems && this.options.maxItems < this.items.length) {
|
||||
errors.push(_t('form-components.editors.list.max-items'));
|
||||
}
|
||||
|
||||
this.errors = _.compact(errors);
|
||||
// Check if any item has errors
|
||||
var hasErrors = !!this.errors.length;
|
||||
this._setAddButtonState();
|
||||
|
||||
if (!hasErrors) return null;
|
||||
},
|
||||
|
||||
_errorPresenter: function (errors) {
|
||||
return _.unique(this.errors.map(function (error) {
|
||||
return error.message;
|
||||
})).join('\n');
|
||||
},
|
||||
|
||||
_setAddButtonState: function () {
|
||||
var $button = this._getAddButtonElement();
|
||||
var hasErrors = !!this.errors.length;
|
||||
|
||||
$button.toggleClass('is-disabled', hasErrors);
|
||||
|
||||
if (hasErrors) {
|
||||
this._errorTooltip = new TipsyTooltipView({
|
||||
el: $button,
|
||||
gravity: 's',
|
||||
offset: 0,
|
||||
title: this._errorPresenter.bind(this)
|
||||
});
|
||||
} else {
|
||||
if (this._errorTooltip) {
|
||||
this._errorTooltip.clean();
|
||||
this._errorTooltip = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.maxItems) {
|
||||
$button.toggle(this.items.length < this.options.maxItems);
|
||||
}
|
||||
},
|
||||
|
||||
_getAddButtonElement: function () {
|
||||
return this.$('[data-action="add"]');
|
||||
},
|
||||
|
||||
_canAddNewItems: function () {
|
||||
var opts = this.options;
|
||||
var maxItemsReached = (opts.maxItems && opts.maxItems <= this.items.length) || false;
|
||||
return !this.errors.length && !maxItemsReached;
|
||||
}
|
||||
}, {
|
||||
|
||||
/* eslint-disable */
|
||||
template: _.template('\
|
||||
<div>\
|
||||
<div data-items></div>\
|
||||
<button type="button" data-action="add">Add</button>\
|
||||
</div>\
|
||||
', null, Backbone.Form.templateSettings)
|
||||
/* eslint-enable */
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
var CustomListItemView = require('builder/components/custom-list/custom-list-item-view');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = CustomListItemView.extend({
|
||||
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
this.clearSubViews();
|
||||
|
||||
this.$el.append(
|
||||
this.options.template(
|
||||
_.extend(
|
||||
{
|
||||
typeLabel: this.options.typeLabel,
|
||||
isSelected: this.model.get('selected'),
|
||||
isSourceType: this.model.get('isSourceType'),
|
||||
name: this.model.getName(),
|
||||
val: this.model.getValue()
|
||||
},
|
||||
this.model.attributes
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
this.$el
|
||||
.attr('data-val', this.model.getValue())
|
||||
.toggleClass('is-disabled', !!this.model.get('disabled'));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
<button type="button" class="CDB-ListDecoration-itemLink u-ellipsis u-actionTextColor" title="<%- val %>">
|
||||
<% if (typeof type != 'undefined' && type === 'node') { %>
|
||||
<div class="u-flex u-alignCenter">
|
||||
<span class="CDB-Text CDB-Size-small is-semibold u-bSpace--s u-upperCase" style="color: <%- color %>;">
|
||||
<%- val %>
|
||||
</span>
|
||||
|
||||
<% if (!isSourceType) { %>
|
||||
<span class="CDB-Text CDB-Size-small u-lSpace--s u-flex" style="color: <%- color %>;">
|
||||
<i class="CDB-IconFont CDB-Size-small CDB-IconFont-ray"></i>
|
||||
</span>
|
||||
<% } %>
|
||||
|
||||
<span class="CDB-Text CDB-Size-medium u-lSpace">
|
||||
<%= nodeTitle %>
|
||||
</span>
|
||||
|
||||
<span class="CDB-Text CDB-Size-medium u-altTextColor u-ellipsis u-lSpace" title="<%= layerName %>">
|
||||
<%= layerName %>
|
||||
</span>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<%- val %>
|
||||
<% } %>
|
||||
</button>
|
||||
@@ -0,0 +1,34 @@
|
||||
<% if (typeof isLoading != 'undefined' && isLoading) { %>
|
||||
<div class="u-flex">
|
||||
<div class="CDB-LoaderIcon CDB-LoaderIcon--small is-dark">
|
||||
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
|
||||
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="u-lSpace u-secondaryTextColor"><%- _t('components.backbone-forms.select.loading') %></span>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<% if (typeof type != 'undefined' && type === 'node') { %>
|
||||
<div class="u-flex u-alignCenter">
|
||||
<span class="CDB-Text CDB-Size-small is-semibold u-bSpace--s u-upperCase" style="color: <%- color %>;">
|
||||
<%- val %>
|
||||
</span>
|
||||
|
||||
<% if (!isSourceType) { %>
|
||||
<span class="CDB-Text CDB-Size-small u-lSpace--s u-flex" style="color: <%- color %>;">
|
||||
<i class="CDB-IconFont CDB-Size-small CDB-IconFont-ray"></i>
|
||||
</span>
|
||||
<% } %>
|
||||
|
||||
<span class="CDB-Text CDB-Size-medium u-lSpace">
|
||||
<%= nodeTitle %>
|
||||
</span>
|
||||
|
||||
<span class="CDB-Text CDB-Size-medium u-altTextColor u-ellipsis u-lSpace" title="<%= layerName %>">
|
||||
<%= layerName %>
|
||||
</span>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<%- label %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
@@ -0,0 +1,14 @@
|
||||
var Backbone = require('backbone');
|
||||
var NodeDatasetItemView = require('./node-dataset-item-view');
|
||||
var nodeDatasetSelectedTemplate = require('./node-dataset-selected.tpl');
|
||||
var nodeDatasetItemTemplate = require('./node-dataset-item.tpl');
|
||||
|
||||
Backbone.Form.editors.NodeDataset = Backbone.Form.editors.Select.extend({
|
||||
|
||||
options: {
|
||||
selectedItemTemplate: nodeDatasetSelectedTemplate,
|
||||
itemListTemplate: nodeDatasetItemTemplate,
|
||||
customListItemView: NodeDatasetItemView
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,218 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var template = require('./number.tpl');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
require('jquery');
|
||||
require('jquery-ui');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
|
||||
var UP_ARROW_KEY_CODE = 38;
|
||||
var BOTTOM_ARROW_KEY_CODE = 40;
|
||||
var SPACE_KEY_CODE = 32;
|
||||
var ENTER_KEY_CODE = 13;
|
||||
|
||||
var SMALL_INCREMENT = 1;
|
||||
var BIG_INCREMENT = 10;
|
||||
|
||||
Backbone.Form.editors.Number = Backbone.Form.editors.Base.extend({
|
||||
|
||||
tagName: 'ul',
|
||||
className: 'CDB-OptionInput-container CDB-OptionInput-container--noMargin u-grow',
|
||||
|
||||
// backbone-forms 0.14.1 has a 'number' validator:
|
||||
// https://github.com/powmedia/backbone-forms/blob/v0.14.1/src/validators.js#L64
|
||||
// We're on 0.14.0 so we have to use a 'regexp' validator
|
||||
options: {
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
showSlider: true,
|
||||
validators: [{
|
||||
type: 'regexp',
|
||||
regexp: /^[+-]?((\.\d+)|(\d+(\.\d+)?))$/,
|
||||
message: _t('editor.edit-feature.valid')
|
||||
}]
|
||||
},
|
||||
|
||||
events: {
|
||||
'keydown .js-input': '_onInputKeyDown',
|
||||
'keyup .js-input': '_onInputKeyUp',
|
||||
focus: function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
blur: function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
// Setting min, max and step from validators, if exists.
|
||||
this._validators = opts.validators || (opts.schema && opts.schema.validators);
|
||||
if (this._validators && this._validators[1]) {
|
||||
this.options.min = this._validators[1].min;
|
||||
this.options.max = this._validators[1].max;
|
||||
this.options.step = this._validators[1].step;
|
||||
}
|
||||
|
||||
if (this.options.editorAttrs && this.options.editorAttrs.help) {
|
||||
this._help = this.options.editorAttrs.help;
|
||||
}
|
||||
|
||||
this._debouncedTriggerChange = _.debounce(this._triggerChange, 333).bind(this);
|
||||
|
||||
this._initViews();
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var placeholder = (this.value === null) ? 'null' : '';
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
value: this.value,
|
||||
isDisabled: this.options.disabled,
|
||||
hasSlider: this.options.showSlider,
|
||||
isFormatted: this.options.isFormatted,
|
||||
placeholder: placeholder,
|
||||
help: this._help || ''
|
||||
})
|
||||
);
|
||||
|
||||
if (this.options.showSlider) {
|
||||
this.$('.js-slider').slider({
|
||||
range: 'min',
|
||||
value: this.value,
|
||||
min: this.options.min,
|
||||
max: this.options.max,
|
||||
step: this.options.step,
|
||||
orientation: 'horizontal',
|
||||
disabled: this.options.disabled,
|
||||
slide: this._onSlideChange.bind(this),
|
||||
stop: this._onSlideStop.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
if (this._help) {
|
||||
this._removeTooltip();
|
||||
|
||||
this._helpTooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-help'),
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return $(this).data('tooltip');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.$el.toggleClass('CDB-OptionInput-container--noSlider', !!this.options.showSlider);
|
||||
},
|
||||
|
||||
_onSlideChange: function (ev, ui) {
|
||||
this.value = ui.value;
|
||||
this.$('.js-input').val(this.value);
|
||||
},
|
||||
|
||||
_onSlideStop: function (ev, ui) {
|
||||
this._debouncedTriggerChange();
|
||||
},
|
||||
|
||||
_onInputKeyDown: function (event) {
|
||||
var $input = this.$('.js-input');
|
||||
var value = +this.getValue();
|
||||
var increment = event.shiftKey === true ? BIG_INCREMENT : SMALL_INCREMENT;
|
||||
|
||||
switch (event.which) {
|
||||
case SPACE_KEY_CODE:
|
||||
case ENTER_KEY_CODE:
|
||||
return false;
|
||||
case UP_ARROW_KEY_CODE:
|
||||
value = this._nextValue(value, increment);
|
||||
$input.val(value);
|
||||
break;
|
||||
case BOTTOM_ARROW_KEY_CODE:
|
||||
value = this._nextValue(value, increment * -1);
|
||||
$input.val(value);
|
||||
break;
|
||||
default:
|
||||
// Any other case!
|
||||
}
|
||||
},
|
||||
|
||||
_nextValue: function (value, increment) {
|
||||
var nextValue = value += increment;
|
||||
|
||||
if (nextValue < this.options.min) return this.options.min;
|
||||
if (nextValue > this.options.max) return this.options.max;
|
||||
|
||||
return nextValue;
|
||||
},
|
||||
|
||||
_hasSlider: function () {
|
||||
return this.options.showSlider && this.$('.js-slider').data('ui-slider');
|
||||
},
|
||||
|
||||
_onInputKeyUp: function () {
|
||||
var value = this.$('.js-input').val();
|
||||
if (this._hasSlider()) {
|
||||
this.$('.js-slider').slider('value', value);
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
this._debouncedTriggerChange();
|
||||
},
|
||||
|
||||
_triggerChange: function () {
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
if (this.hasFocus) return;
|
||||
this.$('.js-input').focus();
|
||||
},
|
||||
|
||||
blur: function () {
|
||||
if (!this.hasFocus) return;
|
||||
this.$('.js-input').blur();
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var val = this.$('.js-input').val();
|
||||
|
||||
return (val === '') ? null : +val;
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
if (this._hasSlider()) {
|
||||
this.$('.js-slider').slider('value', value);
|
||||
}
|
||||
|
||||
this.$('.js-input').val(value);
|
||||
this.value = value;
|
||||
},
|
||||
|
||||
_destroySlider: function () {
|
||||
if (this._hasSlider()) {
|
||||
this.$('.js-slider').slider('destroy');
|
||||
}
|
||||
},
|
||||
|
||||
_removeTooltip: function () {
|
||||
if (this._helpTooltip) {
|
||||
this._helpTooltip.clean();
|
||||
}
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._destroySlider();
|
||||
this._removeTooltip();
|
||||
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this.$el.remove();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
<% if (hasSlider) { %>
|
||||
<li class="CDB-OptionInput-item CDB-OptionInput-item--noSeparator">
|
||||
<div class="UISlider js-slider"></div>
|
||||
</li>
|
||||
<% } %>
|
||||
<li class="CDB-OptionInput-item">
|
||||
<input type="text" class="CDB-InputText <% if (isFormatted) { %>is-number<% } %> <% if (isDisabled) { %>is-disabled<% } %> js-input <% if (help) { %> js-help<% } %>" <% if (isDisabled) { %>readonly<% } %> value="<%- value %>" placeholder="<%- placeholder %>" <% if (help) { %> data-tooltip="<%- help %>"<% } %> />
|
||||
</li>
|
||||
@@ -0,0 +1,23 @@
|
||||
var CustomListCollection = require('builder/components/custom-list/custom-list-collection');
|
||||
var _ = require('underscore');
|
||||
|
||||
/*
|
||||
* Custom list collection, it parses pairs like:
|
||||
*
|
||||
* [{ val, label }]
|
||||
* ["string"]
|
||||
*/
|
||||
|
||||
module.exports = CustomListCollection.extend({
|
||||
|
||||
search: function (query) {
|
||||
query = query.toLowerCase();
|
||||
|
||||
return _(this.filter(function (model) {
|
||||
var val = model.getName().toLowerCase();
|
||||
var type = model.get('type').toLowerCase();
|
||||
return ~val.indexOf(query) && type === 'number';
|
||||
}));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
<p class="CDB-Text CDB-Size-medium u-altTextColor Editor-dropDownInfoText">
|
||||
<%- _t('components.backbone-forms.operators.count-message') %>
|
||||
</p>
|
||||
@@ -0,0 +1,25 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
|
||||
defaults: {
|
||||
visible: false,
|
||||
operator: 'count',
|
||||
attribute: ''
|
||||
},
|
||||
|
||||
isValidOperator: function () {
|
||||
var operator = this.get('operator');
|
||||
var attribute = this.get('attribute');
|
||||
|
||||
if (operator === 'count') {
|
||||
return true;
|
||||
} else {
|
||||
if (operator && attribute) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,150 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var CustomListView = require('builder/components/custom-list/custom-view');
|
||||
var DropdownOverlayView = require('builder/components/dropdown-overlay/dropdown-overlay-view');
|
||||
var OperatorsListModel = require('./operators-list-model');
|
||||
var emptyTemplate = require('./operators-list-count.tpl');
|
||||
var template = require('./operators-list.tpl');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
className: 'Editor-dropdown Editor-boxModal Editor-dropdownOperators',
|
||||
tagName: 'div',
|
||||
|
||||
events: {
|
||||
'change input[name="operator"]': '_onOperationChange'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
this.model = new OperatorsListModel({
|
||||
operator: opts.operator,
|
||||
attribute: opts.attribute,
|
||||
visible: false
|
||||
});
|
||||
|
||||
if (opts.attribute) {
|
||||
this.collection.setSelected(opts.attribute);
|
||||
}
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this.$el.append(
|
||||
template({
|
||||
operator: this.model.get('operator')
|
||||
})
|
||||
);
|
||||
this._renderList();
|
||||
|
||||
this._dropdownOverlay = new DropdownOverlayView({
|
||||
onClickAction: this.hide.bind(this),
|
||||
visible: this.model.get('visible')
|
||||
});
|
||||
this.addView(this._dropdownOverlay);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.model.bind('change:operator change:attribute', _.debounce(this._onModelChange.bind(this), 10));
|
||||
this.model.bind('change:visible', function (mdl, isVisible) {
|
||||
this._toggleVisibility();
|
||||
|
||||
if (!isVisible) {
|
||||
this._destroyList();
|
||||
this.$('.js-list').html(emptyTemplate());
|
||||
} else {
|
||||
this.render();
|
||||
}
|
||||
}, this);
|
||||
this.collection.bind('change:selected', this._onAttributeSelected, this);
|
||||
this.add_related_model(this.collection);
|
||||
},
|
||||
|
||||
_hasList: function () {
|
||||
return !!this._listView;
|
||||
},
|
||||
|
||||
_renderList: function () {
|
||||
if (this.model.get('operator') !== 'count') {
|
||||
if (this._hasList()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._listView = new CustomListView({
|
||||
className: 'CDB-Dropdown-options CDB-Text CDB-Size-medium',
|
||||
collection: this.collection,
|
||||
showSearch: true,
|
||||
typeLabel: this.options.keyAttr
|
||||
});
|
||||
this.$('.js-list').html(this._listView.render().el);
|
||||
} else {
|
||||
this._destroyList();
|
||||
this.$('.js-list').html(emptyTemplate());
|
||||
}
|
||||
},
|
||||
|
||||
_destroyList: function () {
|
||||
if (this._hasList()) {
|
||||
this.removeView(this._listView);
|
||||
this._listView.clean();
|
||||
delete this._listView;
|
||||
}
|
||||
},
|
||||
|
||||
_onModelChange: function () {
|
||||
if (this.model.isValidOperator()) {
|
||||
this.trigger('change', this.model.toJSON(), this);
|
||||
}
|
||||
},
|
||||
|
||||
_onOperationChange: function (ev) {
|
||||
var $input = $(ev.target);
|
||||
var operator = $input.val();
|
||||
|
||||
if (operator === 'count') {
|
||||
this.model.set('attribute', '');
|
||||
this.collection.removeSelected();
|
||||
}
|
||||
|
||||
this.model.set('operator', operator);
|
||||
|
||||
this._renderList();
|
||||
},
|
||||
|
||||
_onAttributeSelected: function (mdl) {
|
||||
if (mdl.get('selected')) {
|
||||
this.model.set('attribute', mdl.getValue());
|
||||
}
|
||||
},
|
||||
|
||||
_toggleVisibility: function () {
|
||||
this.$el.toggleClass('is-visible', !!this.model.get('visible'));
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.model.set('visible', true);
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.model.set('visible', false);
|
||||
},
|
||||
|
||||
toggle: function () {
|
||||
this.model.set('visible', !this.model.get('visible'));
|
||||
},
|
||||
|
||||
isVisible: function () {
|
||||
return this.model.get('visible');
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._destroyList();
|
||||
this.$el.empty();
|
||||
CoreView.prototype.remove.call(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
<ul class="Editor-dropdownCalculations CDB-Text is-semibold">
|
||||
<li class="Editor-dropdownCalculationsElement CDB-Fieldset">
|
||||
<input class="CDB-Radio" type="radio" name="operator" value="count" <% if (operator === 'count') { %>checked<% } %>>
|
||||
<span class="u-iBlock CDB-Radio-face"></span>
|
||||
<label class="u-iBlock u-lSpace"><%- _t('operators.count') %></label>
|
||||
</li>
|
||||
<li class="Editor-dropdownCalculationsElement CDB-Fieldset">
|
||||
<input class="CDB-Radio" type="radio" name="operator" value="sum" <% if (operator === 'sum') { %>checked<% } %>>
|
||||
<span class="u-iBlock CDB-Radio-face"></span>
|
||||
<label class="u-iBlock u-lSpace"><%- _t('operators.sum') %></label>
|
||||
</li>
|
||||
<li class="Editor-dropdownCalculationsElement CDB-Fieldset">
|
||||
<input class="CDB-Radio" type="radio" name="operator" value="avg" <% if (operator === 'avg') { %>checked<% } %>>
|
||||
<span class="u-iBlock CDB-Radio-face"></span>
|
||||
<label class="u-iBlock u-lSpace"><%- _t('operators.avg') %></label>
|
||||
</li>
|
||||
<li class="Editor-dropdownCalculationsElement CDB-Fieldset">
|
||||
<input class="CDB-Radio" type="radio" name="operator" value="max" <% if (operator === 'max') { %>checked<% } %>>
|
||||
<span class="u-iBlock CDB-Radio-face"></span>
|
||||
<label class="u-iBlock u-lSpace"><%- _t('operators.max') %></label>
|
||||
</li>
|
||||
<li class="Editor-dropdownCalculationsElement CDB-Fieldset">
|
||||
<input class="CDB-Radio" type="radio" name="operator" value="min" <% if (operator === 'min') { %>checked<% } %>>
|
||||
<span class="u-iBlock CDB-Radio-face"></span>
|
||||
<label class="u-iBlock u-lSpace"><%- _t('operators.min') %></label>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="js-list"></div>
|
||||
@@ -0,0 +1,165 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var OperatorListView = require('./operators-list-view');
|
||||
var OperatorListCollection = require('./operators-list-collection');
|
||||
var template = require('./operators.tpl');
|
||||
var PopupManager = require('builder/components/popup-manager');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
|
||||
var ENTER_KEY_CODE = 13;
|
||||
|
||||
Backbone.Form.editors.Operators = Backbone.Form.editors.Base.extend({
|
||||
|
||||
tagName: 'div',
|
||||
className: 'Editor-formSelect u-ellipsis',
|
||||
|
||||
events: {
|
||||
'click .js-button': '_onButtonClick',
|
||||
'keydown .js-button': '_onButtonKeyDown',
|
||||
'focus .js-button': function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
'blur': function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
this.collection = new OperatorListCollection(this.options.options);
|
||||
this.dialogMode = this.options.dialogMode || DialogConstants.Mode.DEFAULT;
|
||||
|
||||
if (this.options.editorAttrs && this.options.editorAttrs.help) {
|
||||
this._help = this.options.editorAttrs.help;
|
||||
}
|
||||
|
||||
this._initViews();
|
||||
this.setValue(this.model.get(this.options.keyAttr));
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var value = this.model.get(this.options.keyAttr);
|
||||
|
||||
this.$el.append(
|
||||
$('<div>').addClass('js-operator')
|
||||
);
|
||||
|
||||
this._setOperatorTemplate();
|
||||
|
||||
this._operatorsListView = new OperatorListView({
|
||||
operator: value.operator,
|
||||
attribute: value.attribute,
|
||||
collection: this.collection
|
||||
});
|
||||
this._operatorsListView.bind('change', this._onOperatorsChange, this);
|
||||
|
||||
this._popupManager = new PopupManager(this.cid, this.$el, this._operatorsListView.$el);
|
||||
this._popupManager.append(this.dialogMode);
|
||||
|
||||
if (this.options.disabled) {
|
||||
this.undelegateEvents();
|
||||
}
|
||||
},
|
||||
|
||||
_setOperatorTemplate: function () {
|
||||
this.$('.js-operator').html(
|
||||
template({
|
||||
name: this._getOperatorsTextValue(),
|
||||
disabled: this.options.disabled,
|
||||
keyAttr: this.options.keyAttr,
|
||||
help: this._help || ''
|
||||
})
|
||||
);
|
||||
|
||||
if (this._help) {
|
||||
this._removeTooltip();
|
||||
|
||||
this._helpTooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-help'),
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return $(this).data('tooltip');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var hide = function () {
|
||||
this._operatorsListView.hide();
|
||||
this._popupManager.untrack();
|
||||
}.bind(this);
|
||||
|
||||
this.applyESCBind(hide);
|
||||
this.applyClickOutsideBind(hide);
|
||||
},
|
||||
|
||||
_onOperatorsChange: function (data) {
|
||||
this.model.set(this.options.keyAttr, data);
|
||||
this._setOperatorTemplate();
|
||||
this.$('.js-button').focus();
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_getOperatorsTextValue: function () {
|
||||
var value = this.model.get(this.options.keyAttr);
|
||||
if (value && value.operator) {
|
||||
return value.operator.toUpperCase() + (value.attribute ? '(' + value.attribute + ')' : '');
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
_onButtonClick: function (ev) {
|
||||
this._operatorsListView.toggle();
|
||||
this._operatorsListView.isVisible() ? this._popupManager.track() : this._popupManager.untrack();
|
||||
},
|
||||
|
||||
_onButtonKeyDown: function (ev) {
|
||||
if (ev.which === ENTER_KEY_CODE) {
|
||||
ev.preventDefault();
|
||||
if (!this._operatorsListView.isVisible()) {
|
||||
ev.stopPropagation();
|
||||
this._operatorsListView.toggle();
|
||||
this._popupManager.track();
|
||||
} else {
|
||||
this._popupManager.untrack();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var data = this.model.get(this.options.keyAttr);
|
||||
return _.omit(data, 'visible');
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
var textValue = this._getOperatorsTextValue();
|
||||
this.value = textValue;
|
||||
this._setOperatorTemplate();
|
||||
},
|
||||
|
||||
_removeTooltip: function () {
|
||||
if (this._helpTooltip) {
|
||||
this._helpTooltip.clean();
|
||||
}
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._removeTooltip();
|
||||
|
||||
this._popupManager && this._popupManager.destroy();
|
||||
this._operatorsListView && this._operatorsListView.clean();
|
||||
Backbone.Form.editors.Base.prototype.remove.call(this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this.$el.remove();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="CDB-InputText CDB-Text is-cursor js-button u-ellipsis
|
||||
<% if (disabled) { %> is-disabled <% } %>
|
||||
<% if (!name) { %> is-empty <% } %>
|
||||
<% if (help) { %> js-help<% } %>"
|
||||
<% if (help) { %> data-tooltip="<%- help %>"<% } %>
|
||||
tabindex="0">
|
||||
<%- name || _t('components.backbone-forms.select.placeholder', { keyAttr: keyAttr }) %>
|
||||
</div>
|
||||
@@ -0,0 +1,91 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var template = require('./radio.tpl');
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
|
||||
Backbone.Form.editors.Radio = Backbone.Form.editors.Radio.extend({
|
||||
|
||||
className: 'CDB-Text CDB-Size-medium u-flex u-alignCenter',
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.apply(this, arguments);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.setOptions(this.schema.options); // It comes from the default Select editor (not ours)
|
||||
this._setHelp();
|
||||
return this;
|
||||
},
|
||||
|
||||
_setHelp: function () {
|
||||
var containsHelp = _.find(this.schema.options, function (option) {
|
||||
return option.help;
|
||||
});
|
||||
|
||||
if (containsHelp) {
|
||||
this._helpTooltip = this._createTooltip({
|
||||
$el: this.$('.js-help')
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_createTooltip: function (opts) {
|
||||
return new TipsyTooltipView({
|
||||
el: opts.$el || this.$el,
|
||||
gravity: opts.gravity || 's',
|
||||
className: opts.className || '',
|
||||
offset: opts.offset || 0,
|
||||
title: function () {
|
||||
return opts.msg || $(this).data('tooltip');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var value = this.$('input[type=radio]:checked').val();
|
||||
|
||||
return (value === '') ? null : value;
|
||||
},
|
||||
|
||||
_arrayToHtml: function (array) {
|
||||
var selectedVal = this.form.model.get(this.key);
|
||||
|
||||
var items = _.map(array, function (option, index) {
|
||||
var val = option.val;
|
||||
|
||||
var item = {
|
||||
name: this.getName(),
|
||||
value: (val === null) ? '' : val.toString(),
|
||||
help: option.help,
|
||||
id: this.id,
|
||||
label: option.label,
|
||||
className: option.className
|
||||
};
|
||||
|
||||
// Can't be selected and disabled simultaneously
|
||||
if (selectedVal === val) {
|
||||
item.selected = true;
|
||||
} else {
|
||||
item.disabled = option.disabled;
|
||||
}
|
||||
|
||||
return item;
|
||||
}, this);
|
||||
|
||||
return template({
|
||||
items: items
|
||||
});
|
||||
},
|
||||
|
||||
_destroyBinds: function () {},
|
||||
|
||||
remove: function () {
|
||||
if (this._helpTooltip) {
|
||||
this._helpTooltip.clean();
|
||||
}
|
||||
Backbone.Form.editors.Base.prototype.remove.call(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
<% _.each(items, function(item, index) { %>
|
||||
<li class="u-flex u-alignCenter <%- (index === (items.length - 1)) ? '' : 'u-rSpace--xl' %>">
|
||||
<input type="radio" class="CDB-Radio u-iBlock<% if (item.className) { %> <%- item.className %> <% } %>"
|
||||
name="<%- item.name %>" value="<%- item.value %>" id="<%- item.id %>-<%- index %>"
|
||||
<% if (item.selected) { %>
|
||||
checked="checked"
|
||||
<% } else if (item.disabled) { %>
|
||||
disabled="disabled"
|
||||
<% } %>
|
||||
/>
|
||||
<span class="u-rSpace CDB-Radio-face"></span>
|
||||
<% if (item.help) { %>
|
||||
<span class="js-help is-underlined u-lSpace" data-tooltip="<%- item.help %>">
|
||||
<% } %>
|
||||
<label for="<%- item.id %>-<%- index %>"><%- item.label %></label>
|
||||
<% if (item.help) { %>
|
||||
</span>
|
||||
<% } %>
|
||||
</li>
|
||||
<% }); %>
|
||||
@@ -0,0 +1,193 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var _ = require('underscore');
|
||||
var CustomListView = require('builder/components/custom-list/custom-view');
|
||||
var CustomListCollection = require('builder/components/custom-list/custom-list-multi-collection');
|
||||
var selectedItemTemplate = require('./select-item.tpl');
|
||||
var CustomListItemView = require('builder/components/custom-list/custom-list-multi-item-view');
|
||||
var itemListTemplate = require('builder/components/custom-list/custom-list-item-with-checkbox.tpl');
|
||||
var template = require('./select.tpl');
|
||||
var PopupManager = require('builder/components/popup-manager');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
|
||||
var ENTER_KEY_CODE = 13;
|
||||
|
||||
Backbone.Form.editors.MultiSelect = Backbone.Form.editors.Base.extend({
|
||||
|
||||
tagName: 'div',
|
||||
className: 'u-ellipsis Editor-formSelect',
|
||||
|
||||
events: {
|
||||
'click .js-button': '_onButtonClick',
|
||||
'keydown .js-button': '_onButtonKeyDown',
|
||||
'focus .js-button': function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
'blur': function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
options: {
|
||||
selectedItemTemplate: selectedItemTemplate,
|
||||
itemListTemplate: itemListTemplate,
|
||||
customListItemView: CustomListItemView
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
this.collection = new CustomListCollection(opts.schema.options);
|
||||
this.dialogMode = this.options.dialogMode || DialogConstants.Mode.DEFAULT;
|
||||
|
||||
this._initViews();
|
||||
this.setValue(this.model.get(opts.key));
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
_getLabel: function () {
|
||||
var itemCount = _.compact(this.collection.pluck('selected')).length;
|
||||
return _t('components.backbone-forms.select.selected', { count: itemCount });
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var isLoading = this.options.loading;
|
||||
var isEmpty = !this.collection.length;
|
||||
var isDisabled = !isEmpty ? this.options.disabled : true;
|
||||
var name = this._getLabel();
|
||||
var isNull = name === null;
|
||||
var label = isNull ? 'null' : name;
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
label: label,
|
||||
keyAttr: this.options.keyAttr,
|
||||
isDisabled: isDisabled,
|
||||
isLoading: isLoading,
|
||||
isEmpty: isEmpty,
|
||||
isNull: isNull,
|
||||
placeholder: null,
|
||||
help: this.options.help
|
||||
})
|
||||
);
|
||||
|
||||
if (isDisabled) {
|
||||
this.undelegateEvents();
|
||||
}
|
||||
|
||||
this._listView = new CustomListView({
|
||||
collection: this.collection,
|
||||
showSearch: this.options.showSearch,
|
||||
typeLabel: this.options.keyAttr,
|
||||
itemTemplate: this.options.itemListTemplate,
|
||||
itemView: this.options.customListItemView,
|
||||
actions: [{
|
||||
label: _t('components.backbone-forms.select.none'),
|
||||
action: this._deselectAll.bind(this)
|
||||
},
|
||||
{
|
||||
label: _t('components.backbone-forms.select.all'),
|
||||
action: this._selectAll.bind(this)
|
||||
}],
|
||||
searchPlaceholder: 'Search'
|
||||
});
|
||||
|
||||
this._listView.bind('hidden', this._onHide, this);
|
||||
|
||||
this._popupManager = new PopupManager(this.cid, this.$el, this._listView.$el);
|
||||
this._popupManager.append(this.dialogMode);
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var hide = function () {
|
||||
this._listView.hide();
|
||||
this._popupManager.untrack();
|
||||
}.bind(this);
|
||||
|
||||
this.collection.bind('change:selected', _.debounce(this._onItemSelected, 50), this);
|
||||
this.applyESCBind(hide);
|
||||
this.applyClickOutsideBind(hide);
|
||||
},
|
||||
|
||||
_selectAll: function () {
|
||||
this.collection.each(function (model) {
|
||||
model.set('selected', true);
|
||||
});
|
||||
},
|
||||
|
||||
_deselectAll: function () {
|
||||
this.collection.each(function (model) {
|
||||
model.set('selected', false);
|
||||
});
|
||||
},
|
||||
|
||||
_onItemSelected: function (mdl) {
|
||||
this._renderButton(mdl).focus();
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_onButtonClick: function (ev) {
|
||||
this._listView.toggle();
|
||||
this._listView.isVisible() ? this._popupManager.track() : this._popupManager.untrack();
|
||||
},
|
||||
|
||||
_onButtonKeyDown: function (ev) {
|
||||
if (ev.which === ENTER_KEY_CODE) {
|
||||
ev.preventDefault();
|
||||
if (!this._listView.isVisible()) {
|
||||
ev.stopPropagation();
|
||||
this._listView.toggle();
|
||||
this._popupManager.track();
|
||||
} else {
|
||||
this._popupManager.untrack();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getSelectedValues: function () {
|
||||
return this.collection.chain().map(function (m) { return m.get('selected') ? m.get('val') : null; }).compact().value();
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var values = this._getSelectedValues();
|
||||
if (values.length > 0) {
|
||||
return values;
|
||||
}
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
if (value) {
|
||||
var selectedModel = this.collection.setSelected(value);
|
||||
|
||||
if (selectedModel) {
|
||||
this._renderButton(selectedModel);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_renderButton: function (mdl) {
|
||||
var button = this.$('.js-button');
|
||||
var data = _.extend({}, mdl.attributes, { label: this._getLabel() });
|
||||
var $html = this.options.selectedItemTemplate(data);
|
||||
|
||||
button
|
||||
.removeClass('is-empty')
|
||||
.html($html);
|
||||
|
||||
return button;
|
||||
},
|
||||
|
||||
_onHide: function () {
|
||||
if (this._getSelectedValues().length > 0) {
|
||||
this.trigger('change', this);
|
||||
}
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._popupManager && this._popupManager.destroy();
|
||||
this._listView && this._listView.clean();
|
||||
Backbone.Form.editors.Base.prototype.remove.call(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
<% if (typeof isLoading != 'undefined' && isLoading) { %>
|
||||
<div class="u-flex">
|
||||
<div class="CDB-LoaderIcon CDB-LoaderIcon--small is-dark">
|
||||
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
|
||||
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="u-lSpace u-secondaryTextColor"><%- _t('components.backbone-forms.select.loading') %></span>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<%- label %>
|
||||
<% } %>
|
||||
@@ -0,0 +1,23 @@
|
||||
<button type="button" class="CDB-ListDecoration-itemLink
|
||||
<% if (isSelected) { %> is-selected <% } %> <% if (isDestructive) { %> u-alertTextColor <% } else { %> u-actionTextColor <% } %>"
|
||||
title="<%- nodeTitle %> - <%- layerName %>">
|
||||
<div class="u-flex u-alignCenter">
|
||||
<span class="CDB-Text CDB-Size-small is-semibold u-bSpace--s u-upperCase" style="color: <%- color %>;">
|
||||
<%- layer_id %>
|
||||
</span>
|
||||
|
||||
<% if (!isSourceType) { %>
|
||||
<span class="CDB-Text CDB-Size-small u-lSpace--s u-flex" style="color: <%- color %>;">
|
||||
<i class="CDB-IconFont CDB-Size-small CDB-IconFont-ray"></i>
|
||||
</span>
|
||||
<% } %>
|
||||
|
||||
<span class="CDB-Text CDB-Size-medium u-lSpace u-ellipsLongText">
|
||||
<%= nodeTitle %>
|
||||
</span>
|
||||
|
||||
<span class="CDB-Text CDB-Size-medium u-altTextColor u-ellipsis u-lSpace" title="<%= layerName %>">
|
||||
<%= layerName %>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
@@ -0,0 +1,55 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
options: {
|
||||
template: require('./select-layer-list-item.tpl')
|
||||
},
|
||||
|
||||
className: 'CDB-ListDecoration-item CustomList-item js-listItem',
|
||||
tagName: 'li',
|
||||
|
||||
events: {
|
||||
'mouseenter': '_onMouseEnter',
|
||||
'mouseleave': '_onMouseLeave',
|
||||
'click': '_onClick'
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
|
||||
this.$el.append(
|
||||
this.options.template(
|
||||
_.extend({
|
||||
typeLabel: this.options.typeLabel,
|
||||
isSelected: this.model.get('selected'),
|
||||
isDestructive: this.model.get('destructive'),
|
||||
layerName: this.model.get('layerName'),
|
||||
nodeTitle: this.model.get('nodeTitle'),
|
||||
color: this.model.get('color'),
|
||||
layer_id: this.model.get('layer_id'),
|
||||
isSourceType: this.model.get('isSourceType')
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
this.$el.attr('data-val', this.model.getValue());
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_onMouseLeave: function () {
|
||||
this.$el.removeClass('is-highlighted');
|
||||
},
|
||||
|
||||
_onMouseEnter: function () {
|
||||
this.$el.addClass('is-highlighted');
|
||||
},
|
||||
|
||||
_onClick: function (e) {
|
||||
e.stopPropagation();
|
||||
this.model.set('selected', true);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
<div class="u-flex u-alignCenter">
|
||||
<span class="CDB-Text CDB-Size-small is-semibold u-bSpace--s u-upperCase" style="color: <%- color %>;">
|
||||
<%- layer_id %>
|
||||
</span>
|
||||
|
||||
<% if (!isSourceType) { %>
|
||||
<span class="CDB-Text CDB-Size-small u-lSpace--s u-flex" style="color: <%- color %>;">
|
||||
<i class="CDB-IconFont CDB-Size-small CDB-IconFont-ray"></i>
|
||||
</span>
|
||||
<% } %>
|
||||
|
||||
<span class="CDB-Text CDB-Size-medium u-lSpace">
|
||||
<%= nodeTitle %>
|
||||
</span>
|
||||
|
||||
<span class="CDB-Text CDB-Size-medium u-altTextColor u-ellipsis u-lSpace" title="<%= layerName %>">
|
||||
<%= layerName %>
|
||||
</span>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
<% if (status === 'fetching') { %>
|
||||
<div class="InputColorCategory-loader CDB-Box-modal InputColorCategory-loader">
|
||||
<div class="CDB-LoaderIcon is-dark">
|
||||
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
|
||||
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<% } else if (status === 'error') { %>
|
||||
<div class="u-flex u-alignCenter u-justifyCenter CDB-Text CDB-Size-medium u-bSpace--m u-tSpace--m u-errorTextColor"><%- _t('components.backbone-forms.select.error', {
|
||||
type: type
|
||||
}) %></div>
|
||||
<% } %>
|
||||
@@ -0,0 +1,168 @@
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var CustomView = require('builder/components/custom-list/custom-view');
|
||||
var DropdownOverlayView = require('builder/components/dropdown-overlay/dropdown-overlay-view');
|
||||
var statusTemplate = require('./select-list-view-states.tpl');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'collection',
|
||||
'itemTemplate',
|
||||
'itemView'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
module: 'components:form-components:editors:select:select-list-view',
|
||||
|
||||
className: 'CustomList',
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this._selectModel = this.options.selectModel;
|
||||
this.model = new Backbone.Model({
|
||||
visible: false
|
||||
});
|
||||
|
||||
if (this.options.mouseOverAction) {
|
||||
this._mouseOverAction = this.options.mouseOverAction;
|
||||
}
|
||||
|
||||
if (this.options.mouseOutAction) {
|
||||
this._mouseOutAction = this.options.mouseOutAction;
|
||||
}
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this._renderListSection();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this.model, 'change:visible', function (mdl, isVisible) {
|
||||
isVisible ? this.render() : this.clearSubViews();
|
||||
this._toggleVisibility();
|
||||
this.trigger('change:visible');
|
||||
});
|
||||
|
||||
if (this.collection.isAsync()) {
|
||||
this.listenTo(this.collection.stateModel, 'change:state', this._renderListSection);
|
||||
}
|
||||
},
|
||||
|
||||
_createListView: function () {
|
||||
if (this._listView) {
|
||||
this._listView.clean();
|
||||
this.removeView(this._listView);
|
||||
}
|
||||
this._listView = new CustomView({
|
||||
className: 'CDB-Box-modal CustomList--inner',
|
||||
collection: this.collection,
|
||||
showSearch: this.options.showSearch,
|
||||
allowFreeTextInput: this.options.allowFreeTextInput,
|
||||
typeLabel: this.options.typeLabel,
|
||||
itemTemplate: this._itemTemplate,
|
||||
itemView: this._itemView,
|
||||
position: this.options.position,
|
||||
searchPlaceholder: this.options.searchPlaceholder,
|
||||
selectModel: this._selectModel,
|
||||
mouseOverAction: this._mouseOverAction,
|
||||
mouseOutAction: this._mouseOutAction
|
||||
});
|
||||
|
||||
this.addView(this._listView);
|
||||
this._listView.show();
|
||||
this.el.appendChild(this._listView.render().el);
|
||||
},
|
||||
|
||||
_createStatusView: function (status) {
|
||||
var el = statusTemplate({
|
||||
status: status,
|
||||
type: this.options.typeLabel
|
||||
});
|
||||
|
||||
this.$el.html(el);
|
||||
},
|
||||
|
||||
_renderListSection: function () {
|
||||
var status = this._getStatus();
|
||||
|
||||
if (status === 'fetched') {
|
||||
this._createListView();
|
||||
} else {
|
||||
this._createStatusView(status);
|
||||
}
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.model.set('visible', true);
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.model.set('visible', false);
|
||||
},
|
||||
|
||||
toggle: function () {
|
||||
this.model.set('visible', !this.model.get('visible'));
|
||||
},
|
||||
|
||||
_renderOverlay: function () {
|
||||
var closestModalDialog = this.$el.closest('.Dialog');
|
||||
|
||||
this._dropdownOverlay = new DropdownOverlayView({
|
||||
container: closestModalDialog.length ? closestModalDialog : undefined,
|
||||
onClickAction: this.hide.bind(this),
|
||||
visible: true
|
||||
});
|
||||
|
||||
this.addView(this._dropdownOverlay);
|
||||
},
|
||||
|
||||
_destroyOverlay: function () {
|
||||
this._dropdownOverlay && this._dropdownOverlay.clean() && this.removeView(this._dropdownOverlay);
|
||||
},
|
||||
|
||||
isVisible: function () {
|
||||
return this.model.get('visible');
|
||||
},
|
||||
|
||||
_toggleVisibility: function () {
|
||||
this.$el.toggleClass('is-visible', !!this.isVisible());
|
||||
|
||||
if (this.isVisible()) {
|
||||
this._renderOverlay();
|
||||
} else {
|
||||
this._destroyOverlay();
|
||||
}
|
||||
},
|
||||
|
||||
_getStatus: function () {
|
||||
if (this.collection.isAsync()) {
|
||||
return this.collection.stateModel.get('state');
|
||||
} else {
|
||||
return 'fetched';
|
||||
}
|
||||
},
|
||||
|
||||
_destroyBinds: function () {
|
||||
this.stopListening(this.collection);
|
||||
},
|
||||
|
||||
_onMouseOver: function () {
|
||||
this._mouseOverAction && this._mouseOverAction();
|
||||
},
|
||||
|
||||
_onMouseOut: function () {
|
||||
this._mouseOutAction && this._mouseOutAction();
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._listView && this._listView.clean();
|
||||
this._dropdownOverlay && this._dropdownOverlay.clean();
|
||||
this._destroyBinds();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
Backbone.Form.editors.SelectPlaceholder = Backbone.Form.editors.Select.extend({
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Select.prototype.initialize.call(this, opts);
|
||||
},
|
||||
|
||||
_renderButton: function (model) {
|
||||
Backbone.Form.editors.Select.prototype._renderButton.call(this, model);
|
||||
|
||||
if (this.options.forcePlaceholder) {
|
||||
this._getButton().addClass('is-empty');
|
||||
}
|
||||
},
|
||||
|
||||
_getLabel: function () {
|
||||
if (this.options.forcePlaceholder) {
|
||||
return this.options.placeholder;
|
||||
}
|
||||
|
||||
return Backbone.Form.editors.Select.prototype._getLabel.call(this);
|
||||
},
|
||||
|
||||
_hasValue: function () {
|
||||
if (this.options.forcePlaceholder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Backbone.Form.editors.Select.prototype._hasValue.call(this);
|
||||
},
|
||||
|
||||
_getButtonTemplateData: function (model) {
|
||||
var data = Backbone.Form.editors.Select.prototype._getButtonTemplateData.call(this, model);
|
||||
|
||||
if (this.options.forcePlaceholder) {
|
||||
data.label = this.options.placeholder;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,326 @@
|
||||
var $ = require('jquery');
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var _ = require('underscore');
|
||||
var CustomListCollection = require('builder/components/custom-list/custom-list-collection');
|
||||
var selectedItemTemplate = require('./select-item.tpl');
|
||||
var CustomListItemView = require('builder/components/custom-list/custom-list-item-view');
|
||||
var itemListTemplate = require('builder/components/custom-list/custom-list-item.tpl');
|
||||
var template = require('./select.tpl');
|
||||
var PopupManager = require('builder/components/popup-manager');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var SelectListView = require('./select-list-view');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
|
||||
var ENTER_KEY_CODE = 13;
|
||||
|
||||
Backbone.Form.editors.Select = Backbone.Form.editors.Base.extend({
|
||||
|
||||
tagName: 'div',
|
||||
className: 'u-ellipsis Editor-formSelect',
|
||||
|
||||
events: {
|
||||
'click .js-button': '_onButtonClick',
|
||||
'keydown .js-button': '_onButtonKeyDown',
|
||||
'focus .js-button': function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
'blur': function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
options: {
|
||||
selectedItemTemplate: selectedItemTemplate,
|
||||
itemListTemplate: itemListTemplate,
|
||||
customListItemView: CustomListItemView
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
this.template = opts.template || template;
|
||||
this.dialogMode = this.options.dialogMode || DialogConstants.Mode.DEFAULT;
|
||||
|
||||
if (this.options.mouseOverAction) {
|
||||
this._mouseOverAction = this.options.mouseOverAction;
|
||||
}
|
||||
|
||||
if (this.options.mouseOutAction) {
|
||||
this._mouseOutAction = this.options.mouseOutAction;
|
||||
}
|
||||
|
||||
if (this.options.editorAttrs && this.options.editorAttrs.help) {
|
||||
this._help = this.options.editorAttrs.help;
|
||||
}
|
||||
|
||||
if (this.options.options != null) {
|
||||
this.collection = new CustomListCollection(this.options.options);
|
||||
} else {
|
||||
this.collection = this.options.collection;
|
||||
}
|
||||
|
||||
if (this.collection.isAsync === undefined) {
|
||||
throw new Error('collection must implement isAsync method.');
|
||||
}
|
||||
|
||||
this._initViews();
|
||||
|
||||
this.setValue(this.model.get(this.options.keyAttr));
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var isEmpty = !this.collection.length;
|
||||
var isNull = !this._hasValue();
|
||||
this._isDisabled = !isEmpty ? this.options.disabled : true;
|
||||
var placeholder = this._getPlaceholder(this._isDisabled);
|
||||
var label = isNull ? placeholder : this._getLabel();
|
||||
var isLoading = this._isLoading();
|
||||
|
||||
this.$el.html(this.template({
|
||||
keyAttr: this.options.keyAttr,
|
||||
isEmpty: isEmpty,
|
||||
label: label,
|
||||
isDisabled: this._isDisabled,
|
||||
isNull: isNull,
|
||||
isLoading: isLoading,
|
||||
help: this._help || ''
|
||||
}));
|
||||
|
||||
// we are replacing the html, so we need to re append if nested mode
|
||||
if (this.dialogMode === DialogConstants.Mode.NESTED) {
|
||||
this._popupManager.append(this.dialogMode);
|
||||
}
|
||||
|
||||
if (!isLoading) {
|
||||
this._renderSelected();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var hide = function () {
|
||||
this._listView.hide();
|
||||
this._popupManager.untrack();
|
||||
this._onToggleSelected();
|
||||
}.bind(this);
|
||||
|
||||
this.applyESCBind(hide);
|
||||
|
||||
this.listenTo(this.collection, 'change:selected', this._onItemSelected);
|
||||
this.listenTo(this._listView, 'change:visible', this._onToggleSelected);
|
||||
|
||||
if (this.collection.isAsync()) {
|
||||
this.listenTo(this.collection.stateModel, 'change:state', this.render);
|
||||
}
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
this._listView = new SelectListView({
|
||||
collection: this.collection,
|
||||
showSearch: this.options.showSearch,
|
||||
allowFreeTextInput: this.options.allowFreeTextInput,
|
||||
typeLabel: this.options.keyAttr,
|
||||
itemTemplate: this.options.itemListTemplate,
|
||||
itemView: this.options.customListItemView,
|
||||
position: this.options.position,
|
||||
searchPlaceholder: this.options.searchPlaceholder,
|
||||
selectModel: this.options.defaultValue && this.model,
|
||||
mouseOverAction: this._mouseOverAction,
|
||||
mouseOutAction: this._mouseOutAction
|
||||
});
|
||||
|
||||
this._popupManager = new PopupManager(this.cid, this.$el, this._listView.$el);
|
||||
this._popupManager.append(this.dialogMode);
|
||||
},
|
||||
|
||||
_getPlaceholder: function (isDisabled) {
|
||||
var keyAttr = this.options.keyAttr;
|
||||
var placeholder;
|
||||
|
||||
if (isDisabled) {
|
||||
placeholder = this.options.disabledPlaceholder || _t('components.backbone-forms.select.disabled-placeholder', { keyAttr: keyAttr });
|
||||
} else {
|
||||
placeholder = this.options.placeholder || _t('components.backbone-forms.select.placeholder', { keyAttr: keyAttr });
|
||||
}
|
||||
|
||||
return placeholder;
|
||||
},
|
||||
|
||||
_hasValue: function () {
|
||||
var name = this.model.get(this.options.keyAttr);
|
||||
return name != null && name !== '';
|
||||
},
|
||||
|
||||
_getLabel: function () {
|
||||
var name = this.model.get(this.options.keyAttr);
|
||||
var mdl = this.collection.findWhere({val: name});
|
||||
return mdl && mdl.getName() || name || '';
|
||||
},
|
||||
|
||||
_isLoading: function () {
|
||||
var isLoading = this.options.loading;
|
||||
|
||||
if (this.collection.isAsync()) {
|
||||
isLoading = this.collection.stateModel.get('state') === 'fetching';
|
||||
}
|
||||
|
||||
return isLoading;
|
||||
},
|
||||
|
||||
_destroyBinds: function () {
|
||||
this.stopListening(this.collection);
|
||||
Backbone.Form.editors.Base.prototype._destroyBinds.call(this);
|
||||
},
|
||||
|
||||
_onItemSelected: function (model) {
|
||||
this._listView.hide();
|
||||
this._popupManager.untrack();
|
||||
this._renderButton(model);
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_onButtonClick: function () {
|
||||
if (this._isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._listView.toggle();
|
||||
this._listView.isVisible() ? this._popupManager.track() : this._popupManager.untrack();
|
||||
this._onToggleSelected();
|
||||
},
|
||||
|
||||
_onButtonKeyDown: function (event) {
|
||||
if (this._isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.which === ENTER_KEY_CODE) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!this._listView.isVisible()) {
|
||||
event.stopPropagation();
|
||||
this._listView.toggle();
|
||||
} else {
|
||||
this._popupManager.track();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var item = this.collection.getSelectedItem();
|
||||
if (item) {
|
||||
return item.getValue();
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
var selectedModel = this.collection.setSelected(value);
|
||||
if (selectedModel) {
|
||||
this._renderButton(selectedModel);
|
||||
} else {
|
||||
this.render();
|
||||
}
|
||||
this.value = value;
|
||||
},
|
||||
|
||||
_renderSelected: function () {
|
||||
var selectedModel = this.collection.getSelectedItem();
|
||||
if (selectedModel) {
|
||||
this._renderButton(selectedModel);
|
||||
}
|
||||
},
|
||||
|
||||
_getButton: function () {
|
||||
return this.$('.js-button');
|
||||
},
|
||||
|
||||
_getButtonTemplateData: function (model) {
|
||||
return _.extend({ isSourceType: false }, model.attributes, { label: model.getName() });
|
||||
},
|
||||
|
||||
_renderButton: function (model) {
|
||||
var button = this._getButton();
|
||||
var data = this._getButtonTemplateData(model);
|
||||
var $html = this.options.selectedItemTemplate(data);
|
||||
|
||||
button
|
||||
.removeClass('is-empty')
|
||||
.html($html);
|
||||
|
||||
this._initButtonBinds();
|
||||
|
||||
if (this._help) {
|
||||
this._removeTooltip();
|
||||
|
||||
this._helpTooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-help'),
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return $(this).data('tooltip');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return button;
|
||||
},
|
||||
|
||||
_removeTooltip: function () {
|
||||
if (this._helpTooltip) {
|
||||
this._helpTooltip.clean();
|
||||
}
|
||||
},
|
||||
|
||||
_initButtonBinds: function () {
|
||||
var button = this._getButton();
|
||||
|
||||
button
|
||||
.on('mouseover', this._onMouseOver.bind(this))
|
||||
.on('mouseout', this._onMouseOut.bind(this));
|
||||
},
|
||||
|
||||
_destroyButtonBinds: function () {
|
||||
var button = this._getButton();
|
||||
|
||||
button
|
||||
.off('mouseover', this._onMouseOver.bind(this))
|
||||
.off('mouseout', this._onMouseOut.bind(this));
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._removeTooltip();
|
||||
this._popupManager && this._popupManager.destroy();
|
||||
this._listView && this._listView.clean();
|
||||
this._destroyButtonBinds();
|
||||
this._destroyBinds();
|
||||
Backbone.Form.editors.Base.prototype.remove.call(this);
|
||||
},
|
||||
|
||||
_onToggleSelected: function () {
|
||||
var visible = this._listView.isVisible();
|
||||
|
||||
this.$el.toggleClass('is-active', visible);
|
||||
|
||||
if (!visible) {
|
||||
this._getButton().blur();
|
||||
}
|
||||
},
|
||||
|
||||
_onMouseOver: function () {
|
||||
this._mouseOverAction && this._mouseOverAction();
|
||||
},
|
||||
|
||||
_onMouseOut: function () {
|
||||
this._mouseOutAction && this._mouseOutAction();
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this.$el.remove();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
<div class="CDB-InputText CDB-Text is-cursor js-button u-ellipsis
|
||||
<% if (isDisabled) { %> is-disabled <% } %>
|
||||
<% if (!label) { %> is-empty <% } %>
|
||||
<% if (isNull) { %> is-empty <% } %>
|
||||
<% if (help) { %> js-help<% } %>"
|
||||
<% if (help) { %> data-tooltip="<%- help %>"<% } %>
|
||||
tabindex="0">
|
||||
<% if (isLoading) { %>
|
||||
<div class="CDB-LoaderIcon CDB-LoaderIcon--small is-dark">
|
||||
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
|
||||
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
|
||||
</svg>
|
||||
<span class="u-lSpace u-secondaryTextColor"><%- _t('components.backbone-forms.select.loading') %></span>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<% if (isEmpty && isNull) { %>
|
||||
<%- _t('components.backbone-forms.select.empty') %>
|
||||
<% } else { %>
|
||||
<%- label %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
@@ -0,0 +1,69 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var CustomListCollection = require('builder/components/custom-list/custom-list-collection');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var template = require('./select.tpl');
|
||||
|
||||
Backbone.Form.editors.Suggest = Backbone.Form.editors.Select.extend({
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
this.template = opts.template || template;
|
||||
this.dialogMode = this.options.dialogMode || DialogConstants.Mode.DEFAULT;
|
||||
this._setupCollection();
|
||||
this._initViews();
|
||||
|
||||
this.setValue(this.model.get(this.options.keyAttr));
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
_setupCollection: function () {
|
||||
var item = { label: this.value, val: this.value };
|
||||
this.collection = new CustomListCollection(this.options.options);
|
||||
|
||||
if (this.options.editorAttrs && this.options.editorAttrs.collectionData) {
|
||||
var categories = _.map(this.options.editorAttrs.collectionData, function (data) {
|
||||
return { label: data, val: data };
|
||||
});
|
||||
|
||||
this.collection.reset(categories);
|
||||
}
|
||||
|
||||
if (this.value && !this.collection.findWhere(item)) {
|
||||
this.collection.add(item);
|
||||
}
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
var hide = function () {
|
||||
this._listView.hide();
|
||||
this._popupManager.untrack();
|
||||
}.bind(this);
|
||||
|
||||
this.collection.bind('change:selected', this._onItemSelected, this);
|
||||
this.collection.bind('reset', this.render, this);
|
||||
this.applyESCBind(hide);
|
||||
this.applyClickOutsideBind(hide);
|
||||
},
|
||||
|
||||
_renderButton: function (model) {
|
||||
var button = this.$('.js-button');
|
||||
var name = model.getName();
|
||||
var isNull = name === null || name === 'null';
|
||||
var label = isNull ? 'null' : name;
|
||||
|
||||
var data = _.extend({}, model.attributes, { label: label });
|
||||
var $html = this.options.selectedItemTemplate(data);
|
||||
|
||||
button
|
||||
.html($html)
|
||||
.toggleClass('is-empty', isNull);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var template = require('./size-by-value-content-view.tpl');
|
||||
|
||||
var DEFAULT_RANGE = [1, 5];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
events: {
|
||||
'click .js-back': '_onClickBack',
|
||||
'click .js-bins': '_onClickBins',
|
||||
'click .js-quantification': '_onClickQuantification'
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this._removeForm();
|
||||
this.$el.empty();
|
||||
|
||||
this.$el.append(
|
||||
template({
|
||||
bins: this.model.get('bins'),
|
||||
attribute: this.model.get('attribute'),
|
||||
quantification: this.model.get('quantification')
|
||||
})
|
||||
);
|
||||
|
||||
this._initForm();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initForm: function () {
|
||||
if (this._formView) this._formView.remove();
|
||||
|
||||
var range = this._getRangeOrCalculateItIfNeeded();
|
||||
this._formView = this._createFormView(range);
|
||||
|
||||
this._renderFormView();
|
||||
},
|
||||
|
||||
_renderFormView: function () {
|
||||
this.$('.js-content').append(this._formView.render().$el);
|
||||
},
|
||||
|
||||
_getRangeOrCalculateItIfNeeded: function () {
|
||||
var min, max;
|
||||
var fixedValue = this.model.get('fixed');
|
||||
|
||||
var rangeValues = this.model.get('range');
|
||||
if (rangeValues) {
|
||||
min = +rangeValues[0];
|
||||
max = +rangeValues[1];
|
||||
}
|
||||
|
||||
/* if we come from a fixed value, we need to
|
||||
calculate the range values based on this */
|
||||
if (fixedValue) {
|
||||
var rangeFromFixedValue = this._calculateRangeFromFixed(fixedValue);
|
||||
min = rangeFromFixedValue[0];
|
||||
max = rangeFromFixedValue[1];
|
||||
this.model.set('range', rangeFromFixedValue); // changes are propagated!
|
||||
this.model.unset('fixed'); // once we have a range, everything is ready to map by value
|
||||
}
|
||||
|
||||
return [min, max];
|
||||
},
|
||||
|
||||
_createFormView: function (range) {
|
||||
var min = range[0];
|
||||
var max = range[1];
|
||||
|
||||
var formModel = new Backbone.Model({ min: min, max: max });
|
||||
|
||||
var getNumberType = this._getNumberType.bind(this);
|
||||
formModel.schema = { min: getNumberType(), max: getNumberType() };
|
||||
formModel.bind(
|
||||
'change',
|
||||
function (input) {
|
||||
this.model.set('range', [+input.get('min'), +input.get('max')]);
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
var formView = new Backbone.Form({
|
||||
className: 'Editor-boxList',
|
||||
model: formModel
|
||||
});
|
||||
formView.bind('change', function () {
|
||||
this.commit();
|
||||
});
|
||||
|
||||
return formView;
|
||||
},
|
||||
|
||||
_calculateRangeFromFixed: function (fixed, percent) {
|
||||
percent = percent || 30;
|
||||
|
||||
var span = this.options.max - this.options.min;
|
||||
var delta = fixed / span;
|
||||
var min = Math.floor(Math.max(this.options.min, fixed - percent * delta));
|
||||
var max = Math.floor(Math.min(this.options.max, fixed + percent * delta));
|
||||
|
||||
min = Math.max(min, DEFAULT_RANGE[0]);
|
||||
max = Math.max(max, DEFAULT_RANGE[1]);
|
||||
|
||||
return [min, max];
|
||||
},
|
||||
|
||||
_getNumberType: function () {
|
||||
return {
|
||||
type: 'Number',
|
||||
validators: [
|
||||
'required',
|
||||
{
|
||||
type: 'interval',
|
||||
min: this.options.min,
|
||||
max: this.options.max
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
_removeForm: function () {
|
||||
this._formView && this._formView.remove();
|
||||
},
|
||||
|
||||
_onClickBack: function (e) {
|
||||
this.killEvent(e);
|
||||
this.trigger('back', this);
|
||||
},
|
||||
|
||||
_onClickQuantification: function (e) {
|
||||
this.killEvent(e);
|
||||
this.trigger('selectQuantification', this);
|
||||
},
|
||||
|
||||
_onClickBins: function (e) {
|
||||
this.killEvent(e);
|
||||
this.trigger('selectBins', this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._removeForm();
|
||||
CoreView.prototype.clean.call(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
<div class="CDB-Box-modalHeader">
|
||||
<ul class="CDB-Box-modalHeaderItem CDB-Box-modalHeaderItem--block CDB-Box-modalHeaderItem--paddingHorizontal">
|
||||
<li class="InputColor-modalHeader CDB-ListDecoration-item CDB-ListDecoration-itemPadding--vertical CDB-Text CDB-Size-medium u-secondaryTextColor">
|
||||
<div class="u-flex u-alignStart u-ellipsis">
|
||||
<button class="u-rSpace u-actionTextColor js-back">
|
||||
<i class="CDB-IconFont CDB-IconFont-arrowPrev Size-large"></i>
|
||||
</button>
|
||||
<div class="u-ellipsis test-attribute">
|
||||
<%- attribute %>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="CDB-ListDecoration-item CDB-ListDecoration-itemPadding--vertical CDB-Text CDB-Size-medium u-secondaryTextColor">
|
||||
<ul class="u-flex u-justifySpace">
|
||||
<li class="u-flex test-bins">
|
||||
<%- bins %> <%- _t('form-components.editors.fill.input-ramp.buckets', { smart_count: bins }) %>
|
||||
<button class="CDB-Shape u-lSpace js-bins">
|
||||
<div class="CDB-Shape-threePoints is-horizontal is-blue is-small">
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
<li class="u-flex test-quantification">
|
||||
<%- _t('form-components.editors.fill.quantification.methods.' + quantification) %>
|
||||
<button class="CDB-Shape u-lSpace js-quantification">
|
||||
<div class="CDB-Shape-threePoints is-horizontal is-blue is-small">
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="js-content"></div>
|
||||
@@ -0,0 +1,304 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
|
||||
var template = require('./size-by-value-view.tpl');
|
||||
var DialogModel = require('builder/components/dialog/dialog-model');
|
||||
var DialogView = require('builder/components/dialog/dialog-view');
|
||||
|
||||
var StackLayoutView = require('builder/components/stack-layout/stack-layout-view');
|
||||
var ColumnListView = require('builder/components/custom-list/column-list/column-list-view');
|
||||
var columnListQuantificationMethodItemTemplate = require('builder/components/custom-list/column-list/column-list-quantification-method-item.tpl');
|
||||
var SizeByValueContentView = require('./size-by-value-content-view');
|
||||
|
||||
var PopupManager = require('builder/components/popup-manager');
|
||||
|
||||
var FillConstants = require('builder/components/form-components/_constants/_fill');
|
||||
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'columns',
|
||||
'popupConfig'
|
||||
];
|
||||
|
||||
var COLUMN_PANE_INDEX = 0;
|
||||
var MAIN_PANE_INDEX = 1;
|
||||
var QUANTIFICATION_PANE_INDEX = 2;
|
||||
var BINS_PANE_INDEX = 3;
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
className: 'Form-StyleByValue u-ellipsis',
|
||||
|
||||
events: {
|
||||
'click .js-button': '_showByValueDialog',
|
||||
'click .js-back': '_onClickBack'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
|
||||
this._settings = FillConstants.Settings.NUMBER;
|
||||
this._setupModel();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
this._openPopupForColumn();
|
||||
},
|
||||
|
||||
_openPopupForColumn: function () {
|
||||
var columnSelected = this.model.get('attribute');
|
||||
if (_.isUndefined(columnSelected)) {
|
||||
var self = this;
|
||||
this.timeoutId = setTimeout(function () {
|
||||
self.timeoutId = null;
|
||||
self._showByValueDialog();
|
||||
}, 200);
|
||||
}
|
||||
},
|
||||
|
||||
_setupModel: function () {
|
||||
var options = {};
|
||||
|
||||
if (!this.model.get('quantification')) {
|
||||
var quantifications = this._settings.quantifications;
|
||||
options.quantification =
|
||||
quantifications.items[quantifications.defaultIndex];
|
||||
}
|
||||
|
||||
var modelBins = this.model.get('bins');
|
||||
var defaultBins = this._settings.bins;
|
||||
|
||||
if (!modelBins) {
|
||||
options.bins = defaultBins.items[defaultBins.defaultIndex];
|
||||
}
|
||||
|
||||
if (+modelBins > +_.last(defaultBins.items)) {
|
||||
options.bins = _.last(defaultBins.items);
|
||||
}
|
||||
|
||||
this.model.set(options);
|
||||
this.listenTo(this.model, 'change:attribute change:range', this.render);
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
this._initDialog();
|
||||
this._initPopup();
|
||||
this._initInputColumn();
|
||||
},
|
||||
|
||||
_initDialog: function () {
|
||||
if (this._dialogView) return;
|
||||
|
||||
var dialogModel = new DialogModel();
|
||||
|
||||
this.listenToOnce(dialogModel, 'destroy', function () {
|
||||
this._dialogView = null;
|
||||
this.stopListening(dialogModel);
|
||||
});
|
||||
|
||||
this._dialogView = new DialogView({
|
||||
model: dialogModel
|
||||
});
|
||||
},
|
||||
|
||||
_initPopup: function () {
|
||||
this._popupManager = new PopupManager(
|
||||
this._popupConfig.cid,
|
||||
this._popupConfig.$el,
|
||||
this._dialogView.$el
|
||||
);
|
||||
},
|
||||
|
||||
_initInputColumn: function () {
|
||||
var columnSelected = this.model.get('attribute');
|
||||
var rangeSelected = this.model.get('range');
|
||||
|
||||
var labelColumn = columnSelected || _t('form-components.editors.style.select-by-column');
|
||||
var labelRange = rangeSelected ? rangeSelected[0] + ' - ' + rangeSelected[1] : '';
|
||||
|
||||
this.$el.append(template({
|
||||
columnSelected: !!columnSelected,
|
||||
label: labelColumn,
|
||||
rangeSelected: !!rangeSelected,
|
||||
range: labelRange
|
||||
}));
|
||||
},
|
||||
|
||||
removeDialog: function () {
|
||||
this._dialogView.clean();
|
||||
this._popupManager.untrack();
|
||||
},
|
||||
|
||||
removePopupManager: function () {
|
||||
this._popupManager.destroy();
|
||||
},
|
||||
|
||||
_createStackView: function () {
|
||||
var stackLayoutView = new StackLayoutView({
|
||||
collection: new Backbone.Collection([
|
||||
{ createStackView: this._createColumnsView.bind(this) },
|
||||
{ createStackView: this._createSizeValueContentView.bind(this) },
|
||||
{ createStackView: this._createQuantificationView.bind(this) },
|
||||
{ createStackView: this._createBinsView.bind(this) }
|
||||
])
|
||||
});
|
||||
stackLayoutView.model.set('position', this._getDialogStepPosition());
|
||||
return stackLayoutView;
|
||||
},
|
||||
|
||||
_showByValueDialog: function () {
|
||||
this._dialogView.model.set('createContentView', this._createStackView.bind(this));
|
||||
this._dialogView.render();
|
||||
this._dialogView.show();
|
||||
|
||||
this._popupManager.append(this._popupConfig.mode);
|
||||
this._popupManager.track();
|
||||
},
|
||||
|
||||
_getDialogStepPosition: function () {
|
||||
var position = MAIN_PANE_INDEX;
|
||||
if (!this.model.get('attribute')) {
|
||||
position = COLUMN_PANE_INDEX;
|
||||
} else if (!this.model.get('quantification')) {
|
||||
position = QUANTIFICATION_PANE_INDEX;
|
||||
}
|
||||
return position;
|
||||
},
|
||||
|
||||
_createColumnsView: function (stackLayoutModel, opts) {
|
||||
var view = new ColumnListView({
|
||||
stackLayoutModel: stackLayoutModel,
|
||||
columns: this._columns.filter(function (column) {
|
||||
return column.type === 'number';
|
||||
}),
|
||||
showSearch: true,
|
||||
typeLabel: 'column'
|
||||
});
|
||||
|
||||
view.bind(
|
||||
'selectItem',
|
||||
function (item) {
|
||||
this.model.set('attribute', item.get('val'));
|
||||
var step = MAIN_PANE_INDEX;
|
||||
if (!this.model.get('quantification')) {
|
||||
step = QUANTIFICATION_PANE_INDEX;
|
||||
}
|
||||
stackLayoutModel.goToStep(step);
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
_createSizeValueContentView: function (stackLayoutModel, opts) {
|
||||
var view = new SizeByValueContentView({
|
||||
stackLayoutModel: stackLayoutModel,
|
||||
model: this.model,
|
||||
min: this.options.min,
|
||||
max: this.options.max
|
||||
});
|
||||
|
||||
view.bind(
|
||||
'back',
|
||||
function (value) {
|
||||
stackLayoutModel.prevStep();
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
view.bind(
|
||||
'selectQuantification',
|
||||
function (value) {
|
||||
stackLayoutModel.goToStep(QUANTIFICATION_PANE_INDEX);
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
view.bind(
|
||||
'selectBins',
|
||||
function (value) {
|
||||
stackLayoutModel.goToStep(BINS_PANE_INDEX);
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
_createQuantificationView: function (stackLayoutModel, opts) {
|
||||
var view = new ColumnListView({
|
||||
headerTitle: _t('form-components.editors.fill.quantification.title'),
|
||||
stackLayoutModel: stackLayoutModel,
|
||||
columns: this._settings.quantifications.items,
|
||||
itemTemplate: columnListQuantificationMethodItemTemplate,
|
||||
showSearch: false
|
||||
});
|
||||
|
||||
view.bind(
|
||||
'selectItem',
|
||||
function (item) {
|
||||
this.model.set('quantification', item.get('val'));
|
||||
stackLayoutModel.prevStep();
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
view.bind(
|
||||
'back',
|
||||
function (value) {
|
||||
stackLayoutModel.prevStep();
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
_createBinsView: function (stackLayoutModel, opts) {
|
||||
var view = new ColumnListView({
|
||||
headerTitle: _t('form-components.editors.fill.bins'),
|
||||
stackLayoutModel: stackLayoutModel,
|
||||
columns: this._settings.bins.items
|
||||
});
|
||||
|
||||
view.bind(
|
||||
'selectItem',
|
||||
function (item) {
|
||||
this.model.set('bins', item.get('val'));
|
||||
stackLayoutModel.goToStep(MAIN_PANE_INDEX);
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
view.bind(
|
||||
'back',
|
||||
function (value) {
|
||||
stackLayoutModel.goToStep(MAIN_PANE_INDEX);
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
_onClickBack: function (e) {
|
||||
this.killEvent(e);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
if (this.timeoutId) {
|
||||
clearTimeout(this.timeoutId);
|
||||
}
|
||||
|
||||
CoreView.prototype.clean.apply(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="CDB-InputText CDB-Text is-cursor js-button u-ellipsis <% if (!columnSelected) { %> u-altTextColor <% } %>" tabindex="0">
|
||||
<ul class="Form-StyleByValue--column CDB-OptionInput-container CDB-OptionInput-container--border">
|
||||
<li class="u-ellipsis by-column-name">
|
||||
<%- label %>
|
||||
</li>
|
||||
<% if (rangeSelected) { %>
|
||||
<li class="by-column-range">
|
||||
<%- range %>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this._removeForm();
|
||||
this.$el.empty();
|
||||
this._initForm();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initForm: function () {
|
||||
this._formModel = new Backbone.Model({
|
||||
value: this.model.get('fixed')
|
||||
});
|
||||
|
||||
this._formModel.schema = {
|
||||
value: {
|
||||
type: 'Number',
|
||||
title: '',
|
||||
validators: [
|
||||
'required',
|
||||
{
|
||||
type: 'interval',
|
||||
min: this.options.min,
|
||||
max: this.options.max,
|
||||
step: this.options.step
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
this._formModel.bind(
|
||||
'change',
|
||||
function (input) {
|
||||
this.model.set('fixed', input.get('value'));
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
this._formView = new Backbone.Form({
|
||||
model: this._formModel
|
||||
});
|
||||
|
||||
this._formView.bind('change', function () {
|
||||
this.commit();
|
||||
});
|
||||
|
||||
this.$el.append(this._formView.render().$el);
|
||||
},
|
||||
|
||||
_removeForm: function () {
|
||||
this._formView && this._formView.remove();
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._removeForm();
|
||||
CoreView.prototype.clean.call(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,239 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
|
||||
var tabPaneTemplate = require('builder/components/tab-pane/tab-pane.tpl');
|
||||
var createRadioLabelsTabPane = require('builder/components/tab-pane/create-radio-labels-tab-pane');
|
||||
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
|
||||
var SizeFixedView = require('./size-fixed-view');
|
||||
var SizeByValueView = require('./size-by-value-view');
|
||||
|
||||
var FillConstants = require('builder/components/form-components/_constants/_fill');
|
||||
|
||||
Backbone.Form.editors.Size = Backbone.Form.editors.Base.extend({
|
||||
className: 'Form-InputSize CDB-Text',
|
||||
|
||||
events: {
|
||||
focus: function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
blur: function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
this._sizeModel = new Backbone.Model(this.model.get(opts.key));
|
||||
|
||||
this._initBinds();
|
||||
this._initViews();
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this._updateSizeModelOnChangeBind();
|
||||
this.applyESCBind(this._removeDialogs);
|
||||
this.applyClickOutsideBind(this._removeDialogs);
|
||||
},
|
||||
|
||||
_updateSizeModelOnChangeBind: function () {
|
||||
var self = this;
|
||||
this._sizeModel.bind('change', function () {
|
||||
self.model.set(self.key, self._sizeModel.toJSON());
|
||||
}, this);
|
||||
},
|
||||
|
||||
_removeDialogs: function () {
|
||||
this._valueView && this._valueView.removeDialog();
|
||||
},
|
||||
|
||||
_removePopupManagers: function () {
|
||||
this._valueView && this._valueView.removePopupManager();
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
this._tabPaneTabs = this._getTabPanes();
|
||||
|
||||
this._setSelectedTab();
|
||||
|
||||
this._tabPaneView = createRadioLabelsTabPane(
|
||||
this._tabPaneTabs,
|
||||
this._getTabPaneOptions()
|
||||
);
|
||||
|
||||
this.listenTo(
|
||||
this._tabPaneView.collection,
|
||||
'change:selected',
|
||||
this._onChangeTabPaneViewTab
|
||||
);
|
||||
|
||||
this.$el.append(this._tabPaneView.render().$el);
|
||||
},
|
||||
|
||||
_getTabPanes: function () {
|
||||
var tabPaneTabs = [];
|
||||
var attrs = this.options.editorAttrs;
|
||||
if (attrs && attrs.hidePanes) {
|
||||
if (!_.contains(attrs.hidePanes, FillConstants.Panes.FIXED)) tabPaneTabs.push(this._buildFixedPane());
|
||||
if (!_.contains(attrs.hidePanes, FillConstants.Panes.BY_VALUE)) tabPaneTabs.push(this._buildByValuePane());
|
||||
} else {
|
||||
tabPaneTabs = [this._buildFixedPane(), this._buildByValuePane()];
|
||||
}
|
||||
return tabPaneTabs;
|
||||
},
|
||||
|
||||
_setSelectedTab: function () {
|
||||
var selectedIndex = this._getSelectedTabPaneIndex();
|
||||
this._tabPaneTabs[selectedIndex].selected = true;
|
||||
},
|
||||
|
||||
_getTabPaneOptions: function () {
|
||||
var options = {
|
||||
tabPaneOptions: {
|
||||
template: tabPaneTemplate,
|
||||
tabPaneItemOptions: {
|
||||
tagName: 'li',
|
||||
klassName: 'CDB-NavMenu-item'
|
||||
}
|
||||
},
|
||||
tabPaneItemLabelOptions: {
|
||||
tagName: 'div',
|
||||
className: 'CDB-Text CDB-Size-medium'
|
||||
}
|
||||
};
|
||||
return options;
|
||||
},
|
||||
|
||||
_buildFixedPane: function () {
|
||||
var self = this;
|
||||
var geometryName = this.options.editorAttrs.geometryName;
|
||||
var fixedPane = {
|
||||
name: FillConstants.Panes.FIXED,
|
||||
label: _t('form-components.editors.fill.input-number.fixed'),
|
||||
tooltip: _t('editor.style.tooltips.size.fixed-tab', { type: geometryName }),
|
||||
tooltipGravity: 's',
|
||||
createContentView: function () {
|
||||
return self._generateFixedContentView();
|
||||
}
|
||||
};
|
||||
return fixedPane;
|
||||
},
|
||||
|
||||
_buildByValuePane: function () {
|
||||
var self = this;
|
||||
var geometryName = this.options.editorAttrs.geometryName;
|
||||
var valuePane = {
|
||||
name: FillConstants.Panes.BY_VALUE,
|
||||
label: _t('form-components.editors.fill.input-number.by-value'),
|
||||
tooltip: _t('editor.style.tooltips.size.by-value-tab', { type: geometryName }),
|
||||
tooltipGravity: 's',
|
||||
createContentView: function () {
|
||||
return self._generateByValueContentView();
|
||||
}
|
||||
};
|
||||
return valuePane;
|
||||
},
|
||||
|
||||
_getSelectedTabPaneIndex: function () {
|
||||
var FIXED_TAB_PANE = 0;
|
||||
var BY_VALUE_PANE = 1;
|
||||
|
||||
var hasRange = this._sizeModel.get('range');
|
||||
var thereIsByValuePane = this._tabPaneTabs.length > 1;
|
||||
|
||||
return hasRange && thereIsByValuePane ? BY_VALUE_PANE : FIXED_TAB_PANE;
|
||||
},
|
||||
|
||||
_onChangeTabPaneViewTab: function () {
|
||||
var selectedTabPaneName = this._tabPaneView.getSelectedTabPaneName();
|
||||
|
||||
if (selectedTabPaneName === FillConstants.Panes.FIXED) {
|
||||
this._updateFixedValue();
|
||||
} else {
|
||||
this._updateRangeValue();
|
||||
}
|
||||
|
||||
this.trigger('change', selectedTabPaneName, this);
|
||||
},
|
||||
|
||||
_updateFixedValue: function () {
|
||||
var range = this._sizeModel.get('range');
|
||||
if (range) {
|
||||
// when coming from range calculate the average...
|
||||
var avg = 0.5 * (+range[0] + +range[1]);
|
||||
this._sizeModel.set('fixed', avg);
|
||||
this._sizeModel.unset('range');
|
||||
}
|
||||
},
|
||||
|
||||
_updateRangeValue: function () {
|
||||
var fixed = this._sizeModel.get('fixed');
|
||||
if (
|
||||
fixed !== null &&
|
||||
!_.isUndefined(fixed) &&
|
||||
this._sizeModel.get('attribute')
|
||||
) {
|
||||
var editorAttrs = this.options.editorAttrs;
|
||||
var range = editorAttrs && editorAttrs.defaultRange || [fixed, fixed];
|
||||
this._sizeModel.set('range', range);
|
||||
this._sizeModel.unset('fixed');
|
||||
}
|
||||
},
|
||||
|
||||
_generateFixedContentView: function () {
|
||||
var editorAttrs = this.options.editorAttrs;
|
||||
this._fixedView = new SizeFixedView({
|
||||
model: this._sizeModel,
|
||||
min: editorAttrs && editorAttrs.min || FillConstants.Size.DefaultInput100.MIN,
|
||||
max: editorAttrs && editorAttrs.max || FillConstants.Size.DefaultInput100.MAX,
|
||||
step: editorAttrs && editorAttrs.step || FillConstants.Size.DefaultInput100.STEP
|
||||
});
|
||||
return this._fixedView;
|
||||
},
|
||||
|
||||
_generateByValueContentView: function () {
|
||||
var editorAttrs = this.options.editorAttrs;
|
||||
this._valueView = new SizeByValueView({
|
||||
model: this._sizeModel,
|
||||
columns: this.schema.options,
|
||||
min: editorAttrs && editorAttrs.min || FillConstants.Size.DefaultInput100.MIN,
|
||||
max: editorAttrs && editorAttrs.max || FillConstants.Size.DefaultInput100.MAX,
|
||||
popupConfig: {
|
||||
cid: this.cid,
|
||||
$el: this.$el,
|
||||
mode: this.options.dialogMode
|
||||
}
|
||||
});
|
||||
return this._valueView;
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
if (this.hasFocus) return;
|
||||
this.$('.js-menu').focus();
|
||||
},
|
||||
|
||||
blur: function () {
|
||||
if (!this.hasFocus) return;
|
||||
this.$('.js-menu').blur();
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
return this._sizeModel.toJSON();
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._removeDialogs();
|
||||
this._removePopupManagers();
|
||||
this._tabPaneView.clean();
|
||||
this._valueView && this._valueView.clean();
|
||||
this._fixedView && this._fixedView.clean();
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this.$el.remove();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,229 @@
|
||||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var template = require('./slider.tpl');
|
||||
var MutationObserver = window.MutationObserver;
|
||||
|
||||
var TICKS_PLACEHOLDER = 5;
|
||||
var HANDLE_WIDTH = 12;
|
||||
var DIMENSION = 160;
|
||||
var INITIAL = {
|
||||
HIGHEST: 'highest',
|
||||
LOWEST: 'lowest'
|
||||
};
|
||||
|
||||
Backbone.Form.editors.Slider = Backbone.Form.editors.Base.extend({
|
||||
|
||||
className: 'rangeslider--no-fill',
|
||||
|
||||
options: {
|
||||
direction: 'horizontal',
|
||||
initial: INITIAL.LOWEST
|
||||
},
|
||||
|
||||
events: {
|
||||
'change .js-slider': '_onValueChange',
|
||||
focus: function () {
|
||||
this.trigger('focus', this);
|
||||
},
|
||||
blur: function () {
|
||||
this.trigger('blur', this);
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
if (!this.options.labels) {
|
||||
throw new Error('labels is required');
|
||||
}
|
||||
|
||||
if (this.options.values && this.options.labels.length !== this.options.values.length) {
|
||||
throw new Error('values and labels should have the same length');
|
||||
}
|
||||
|
||||
if (this.options.value !== undefined) {
|
||||
this.value = this.options.value;
|
||||
}
|
||||
|
||||
this._onValueChange = this._onValueChange.bind(this);
|
||||
this._onSlideChange = this._onSlideChange.bind(this);
|
||||
|
||||
this._values = this.options.values;
|
||||
this._labels = this.options.labels;
|
||||
this._getInitialTickID();
|
||||
this._updateUI();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var isDisabled = this.options.disabled || this._labels.length <= 0;
|
||||
var max = this._labels.length === 0 ? TICKS_PLACEHOLDER - 1 : this._labels.length - 1;
|
||||
var step = this._getStepPercentage();
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
orientation: this.options.direction,
|
||||
disabled: isDisabled,
|
||||
min: 0,
|
||||
max: max,
|
||||
value: this.rangeIndex
|
||||
})
|
||||
);
|
||||
|
||||
this._renderSlider();
|
||||
this._addTicks(step);
|
||||
this._updateUI();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_getInitialTickID: function () {
|
||||
var value = this.value;
|
||||
|
||||
if (this._values && this._values.length > 0) {
|
||||
value = this._values.indexOf(this.value);
|
||||
|
||||
if (value === -1) {
|
||||
value = this.options.initial === INITIAL.LOWEST ? 0 : this._values.length - 1;
|
||||
}
|
||||
} else if (!this.value) {
|
||||
value = 0;
|
||||
}
|
||||
|
||||
this.rangeIndex = value;
|
||||
},
|
||||
|
||||
_getNumberOfValues: function () {
|
||||
return (this._values && this._values.length) || 0;
|
||||
},
|
||||
|
||||
_renderSlider: function () {
|
||||
var element;
|
||||
var field;
|
||||
var onMutationObserver;
|
||||
var observer;
|
||||
var config = { subtree: true, childList: true };
|
||||
|
||||
if (!MutationObserver) {
|
||||
this._initializeSlider();
|
||||
} else {
|
||||
element = document.body;
|
||||
field = this.$('.js-slider').get(0);
|
||||
onMutationObserver = function () {
|
||||
if (element.contains(field)) {
|
||||
this._initializeSlider();
|
||||
observer.disconnect();
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
observer = new MutationObserver(onMutationObserver);
|
||||
onMutationObserver();
|
||||
|
||||
observer.observe(element, config);
|
||||
}
|
||||
},
|
||||
|
||||
_initializeSlider: function () {
|
||||
var numberOfValues = this._getNumberOfValues();
|
||||
var init = function () {
|
||||
this.$('.js-slider').rangeslider({
|
||||
polyfill: false,
|
||||
fillClass: 'rangesliderFill',
|
||||
handleClass: 'rangesliderHandle',
|
||||
onSlideEnd: this._onSlideChange
|
||||
});
|
||||
}.bind(this);
|
||||
|
||||
if (numberOfValues !== 1) {
|
||||
!MutationObserver ? setTimeout(init, 0) : init();
|
||||
}
|
||||
|
||||
this.$('.js-slider').toggle(numberOfValues !== 1);
|
||||
},
|
||||
|
||||
_updateUI: function () {
|
||||
this._updateLabel(this.rangeIndex);
|
||||
this._highlightTick(this.rangeIndex);
|
||||
},
|
||||
|
||||
_addTicks: function (step) {
|
||||
var offset = HANDLE_WIDTH / 2;
|
||||
var numberOfValues = this._getNumberOfValues();
|
||||
var ticks = this._labels.length > 0 ? this._labels.length : TICKS_PLACEHOLDER;
|
||||
_.each(_.range(ticks), function (tick, index) {
|
||||
$('<div class="rangeslider-tick js-tick"></div>')
|
||||
.css('left', (step * index + offset) + 'px')
|
||||
.appendTo(this.$('.js-ticks'));
|
||||
}, this);
|
||||
|
||||
this.$('.js-ticks').toggle(numberOfValues !== 1);
|
||||
},
|
||||
|
||||
_highlightTick: function (tickID) {
|
||||
this.$('.js-tick').removeClass('is-highlighted');
|
||||
$(this.$('.js-tick').get(tickID)).addClass('is-highlighted');
|
||||
},
|
||||
|
||||
_updateLabel: function (tickID) {
|
||||
var label = this._labels[tickID];
|
||||
if (this._labels.length <= 0) {
|
||||
label = _t('form-components.editors.slide.no-values');
|
||||
}
|
||||
this.$('.js-label').text(label);
|
||||
},
|
||||
|
||||
_getStepPercentage: function () {
|
||||
var steps = this._getSteps();
|
||||
var rangeWidth = DIMENSION - HANDLE_WIDTH;
|
||||
|
||||
return (rangeWidth / steps);
|
||||
},
|
||||
|
||||
_getSteps: function () {
|
||||
var steps = this._labels.length - 1;
|
||||
if (steps < 0) {
|
||||
steps = TICKS_PLACEHOLDER - 1;
|
||||
}
|
||||
|
||||
return steps;
|
||||
},
|
||||
|
||||
_onValueChange: function (e) {
|
||||
var value = this.$('.js-slider').val();
|
||||
this._onSlideChange(null, value);
|
||||
},
|
||||
|
||||
_onSlideChange: function (position, value) {
|
||||
this.rangeIndex = +value;
|
||||
this.value = this._values && this._values.length > 0 ? this._values[this.rangeIndex] : this.rangeIndex;
|
||||
this._updateUI(this.rangeIndex);
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var value = this._values && this._values.length > 0 ? this._values[this.rangeIndex] : null;
|
||||
return this.value || value;
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
var index = this._values ? this._values.indexOf(value) : value;
|
||||
this.$('.js-slider').val(index).change();
|
||||
this.value = value;
|
||||
},
|
||||
|
||||
_destroySlider: function () {
|
||||
this.$('.js-slider').off('change', this._onValueChange);
|
||||
this.$('.js-slider').rangeslider('destroy');
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._destroySlider();
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this.$el.remove();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
<input
|
||||
class="js-slider"
|
||||
type="range"
|
||||
min="<%- min %>"
|
||||
max="<%- max %>"
|
||||
data-orientation="<%- orientation %>"
|
||||
<% if (disabled) { %>
|
||||
disabled="true"
|
||||
<% } %>
|
||||
value="<%- value %>"
|
||||
>
|
||||
<div class="rangeslider-ticks js-ticks"></div>
|
||||
<div class="rangeslider-label CDB-Text CDB-Size-medium js-label"></div>
|
||||
@@ -0,0 +1,63 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
require('jquery-ui');
|
||||
|
||||
Backbone.Form.editors.SortableList = Backbone.Form.editors.List.extend({
|
||||
initialize: function () {
|
||||
Backbone.Form.editors.List.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
Backbone.Form.editors.List.prototype.render.apply(this, arguments);
|
||||
this._initSortable();
|
||||
return this;
|
||||
},
|
||||
|
||||
addItem: function () {
|
||||
Backbone.Form.editors.List.prototype.addItem.apply(this, arguments);
|
||||
this._updateEditability();
|
||||
return this;
|
||||
},
|
||||
|
||||
removeItem: function () {
|
||||
Backbone.Form.editors.List.prototype.removeItem.apply(this, arguments);
|
||||
this._updateEditability();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initSortable: function () {
|
||||
this.$list.sortable({
|
||||
axis: 'y',
|
||||
items: '.js-sortable',
|
||||
tolerance: 'pointer',
|
||||
containment: this.$list,
|
||||
forceHelperSize: true,
|
||||
forcePlaceholderSize: true,
|
||||
update: this._onSortableUpdate.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
_onSortableUpdate: function (event, ui) {
|
||||
var sorted = [];
|
||||
var list = this.$list;
|
||||
var items = this.items;
|
||||
|
||||
_.each(items, function (item) {
|
||||
var index = item.$el.index(list.$el);
|
||||
sorted[index] = item;
|
||||
});
|
||||
|
||||
this.items = sorted;
|
||||
this.commit();
|
||||
},
|
||||
|
||||
_updateEditability: function () {
|
||||
if (this.items.length > 1) {
|
||||
this.$('.js-sortable').addClass('is-movable');
|
||||
this.$('.js-editable').removeClass('is-hidden');
|
||||
} else {
|
||||
this.$('.js-sortable').removeClass('is-movable');
|
||||
this.$('.js-editable').addClass('is-hidden');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
removeTag: function (label) {
|
||||
var m = this.findWhere({label: label});
|
||||
m && this.remove(m);
|
||||
},
|
||||
|
||||
addTag: function (label) {
|
||||
var m = this.findWhere({label: label});
|
||||
!m && this.add({label: label});
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
return this.map(function (mdl) {
|
||||
return mdl.get('label');
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
tagName: 'li',
|
||||
|
||||
initialize: function (opts) {
|
||||
if (!opts.label) throw new Error('label is required');
|
||||
this._label = opts.label;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.html(this._label);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var template = require('./taglist.tpl');
|
||||
var TagView = require('./taglist-item-view');
|
||||
var TagCollection = require('./taglist-collection');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
|
||||
require('jquery');
|
||||
require('jquery-ui');
|
||||
require('tagit');
|
||||
|
||||
Backbone.Form.editors.Taglist = Backbone.Form.editors.Base.extend({
|
||||
className: 'Form-tags CDB-Text',
|
||||
events: {
|
||||
'mouseover': '_onMouseOver',
|
||||
'mouseout': '_onMouseOut'
|
||||
},
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
this._tagCollection = new TagCollection(this._normalize(opts.schema.options.tags));
|
||||
|
||||
this.isEditable = opts.schema.options.isEditable || opts.schema.options.isEditable === undefined;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var self = this;
|
||||
var tagsPlaceholder = (!this.isEditable && this._tagCollection.length === 0)
|
||||
? _t('components.taglist.none')
|
||||
: _t('components.taglist.placeholder');
|
||||
|
||||
this.$el.html(template());
|
||||
|
||||
this._tagCollection.each(this._renderTag, this);
|
||||
|
||||
this.$('.js-tagsList').tagit({
|
||||
allowSpaces: true,
|
||||
caseSensitive: false,
|
||||
placeholderText: tagsPlaceholder,
|
||||
readOnly: !this.isEditable,
|
||||
onBlur: function () {
|
||||
self.isEditable && self.$el.removeClass('is-focus');
|
||||
self.trigger('blur', self);
|
||||
},
|
||||
onFocus: function () {
|
||||
self.isEditable && self.$el.removeClass('is-hover').addClass('is-focus');
|
||||
self.trigger('focus', self);
|
||||
},
|
||||
preprocessTag: tag => tag.toLowerCase(),
|
||||
afterTagAdded: self._onTagAdded.bind(self),
|
||||
afterTagRemoved: self._onTagRemoved.bind(self)
|
||||
});
|
||||
},
|
||||
|
||||
_renderTag: function (model) {
|
||||
var view = new TagView({
|
||||
label: model.get('label')
|
||||
});
|
||||
|
||||
this.$('.js-tagsList').append(view.render().el);
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
if (this.hasFocus) return;
|
||||
this.$el.focus();
|
||||
},
|
||||
|
||||
blur: function () {
|
||||
if (!this.hasFocus) return;
|
||||
this.$el.blur();
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
return this._tagCollection.getValue();
|
||||
},
|
||||
|
||||
setValue: function (tags) {
|
||||
this._tagCollection.reset(this._normalize(tags));
|
||||
this._destroyTagit();
|
||||
this.render();
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_onMouseOver: function () {
|
||||
!this.$el.hasClass('is-focus') && this.$el.addClass('is-hover');
|
||||
},
|
||||
|
||||
_onMouseOut: function () {
|
||||
!this.$el.hasClass('is-focus') && this.$el.removeClass('is-hover');
|
||||
},
|
||||
|
||||
_onTagRemoved: function (e, ui) {
|
||||
var tag = ui.tag.find('.tagit-label').text();
|
||||
this._tagCollection.removeTag(tag);
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_onTagAdded: function (e, ui) {
|
||||
var tag = ui.tag.find('.tagit-label').text();
|
||||
this._tagCollection.addTag(tag);
|
||||
this.trigger('change', this);
|
||||
},
|
||||
|
||||
_normalize: function (tags) {
|
||||
return _.map(tags, function (tag) {
|
||||
return {
|
||||
label: tag.trim()
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
_destroyTagit: function () {
|
||||
// cannot call public methods before initilization
|
||||
// checking ui-widget class does the trick
|
||||
(this.$('.ui-widget').length > 0) && this.$('.js-tagsList').tagit('destroy');
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._destroyTagit();
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this.$el.remove();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
<ul class="Form-tagsList js-tagsList"></ul>
|
||||
110
lib/assets/javascripts/builder/components/form-components/editors/text.js
Executable file
110
lib/assets/javascripts/builder/components/form-components/editors/text.js
Executable file
@@ -0,0 +1,110 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var EditorHelpers = require('builder/components/form-components/helpers/editor');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
|
||||
Backbone.Form.editors.Text = Backbone.Form.editors.Text.extend({
|
||||
className: 'CDB-InputText CDB-Text',
|
||||
|
||||
initialize: function (opts) {
|
||||
Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
|
||||
EditorHelpers.setOptions(this, opts);
|
||||
|
||||
var schema = this.schema;
|
||||
|
||||
// Allow customising text type (email, phone etc.) for HTML5 browsers
|
||||
var type = 'text';
|
||||
|
||||
if (schema && schema.editorAttrs && schema.editorAttrs.type) type = schema.editorAttrs.type;
|
||||
if (schema && schema.dataType) type = schema.dataType;
|
||||
|
||||
if (this.options.editorAttrs && this.options.editorAttrs.help) {
|
||||
this._help = this.options.editorAttrs.help;
|
||||
}
|
||||
|
||||
this.$el.attr('type', type);
|
||||
|
||||
this.determineChange = _.debounce(this.determineChange, 200);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.setValue(this.value);
|
||||
this._toggleDisableState();
|
||||
|
||||
if (this._isCopyButtonEnabled()) {
|
||||
this._toggleClipboardState();
|
||||
}
|
||||
|
||||
if (this._help) {
|
||||
this._removeTooltip();
|
||||
|
||||
if (!this.options.disabled) {
|
||||
this._helpTooltip = new TipsyTooltipView({
|
||||
el: this.$el,
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return this._help;
|
||||
}.bind(this)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var val = this.$el.val();
|
||||
|
||||
return (val === '') ? null : val;
|
||||
},
|
||||
|
||||
_toggleClipboardState: function () {
|
||||
this.$el.toggleClass('Share-input-field u-ellipsis', this._isCopyButtonEnabled());
|
||||
},
|
||||
|
||||
_togglePlaceholder: function () {
|
||||
if (this.options.placeholder) {
|
||||
this.$el.attr('placeholder', this.options.placeholder);
|
||||
} else {
|
||||
var placeholder = (this.value === null) ? 'null' : '';
|
||||
this.$el.attr('placeholder', placeholder);
|
||||
}
|
||||
},
|
||||
|
||||
_toggleDisableState: function () {
|
||||
if (this.options.disabled) {
|
||||
this.$el.attr('readonly', '');
|
||||
this.$el.attr('placeholder', '');
|
||||
|
||||
// if it's disabled AND has copy, leave just readonly
|
||||
if (this._isCopyButtonEnabled()) {
|
||||
this.$el.removeAttr('disabled');
|
||||
}
|
||||
} else {
|
||||
this.$el.removeAttr('readonly');
|
||||
this._togglePlaceholder();
|
||||
}
|
||||
|
||||
this.$el.toggleClass('is-disabled', !!this.options.disabled);
|
||||
},
|
||||
|
||||
_isCopyButtonEnabled: function () {
|
||||
return !!this.options.hasCopyButton;
|
||||
},
|
||||
|
||||
_removeTooltip: function () {
|
||||
if (this._helpTooltip) {
|
||||
this._helpTooltip.clean();
|
||||
}
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this._removeTooltip();
|
||||
|
||||
Backbone.Form.editors.Base.prototype.remove.apply(this);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this.$el.remove();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
Backbone.Form.editors.TextArea = Backbone.Form.editors.Text.extend({
|
||||
tagName: 'textarea',
|
||||
className: 'CDB-Textarea',
|
||||
|
||||
render: function () {
|
||||
this.setValue(this.value);
|
||||
this._toggleDisableState();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var val = this.$el.val();
|
||||
|
||||
return (val === '') ? null : val;
|
||||
},
|
||||
|
||||
_toggleDisableState: function () {
|
||||
if (this.options.disabled) {
|
||||
this.$el.attr('readonly', '');
|
||||
this.$el.attr('placeholder', '');
|
||||
} else {
|
||||
this.$el.removeAttr('readonly');
|
||||
this._togglePlaceholder();
|
||||
}
|
||||
|
||||
this.$el.toggleClass('is-disabled', !!this.options.disabled);
|
||||
}
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user