Initial commit
31
lib/assets/javascripts/builder/editor/widgets/delete-widget-confirmation.tpl
Executable file
@@ -0,0 +1,31 @@
|
||||
<div class="u-flex u-justifyCenter">
|
||||
<div class="Modal-inner Modal-inner--grid u-flex u-justifyCenter">
|
||||
<div class="Modal-icon">
|
||||
<svg width="24px" height="25px" viewbox="521 436 24 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path d="M524.5,440 L540.5,440 L540.5,460 L524.5,460 L524.5,440 Z M528.5,437 L536.5,437 L536.5,440 L528.5,440 L528.5,437 Z M522,440 L544,440 L522,440 Z M528.5,443.5 L528.5,455.5 L528.5,443.5 Z M532.5,443.5 L532.5,455.5 L532.5,443.5 Z M536.5,443.5 L536.5,455.5 L536.5,443.5 Z" id="Shape" stroke="#F19243" stroke-width="1" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="CDB-Text CDB-Size-huge is-light u-bSpace--xl">
|
||||
<%- _t('editor.widgets.delete.title', { name: name }) %>
|
||||
</h2>
|
||||
<p class="CDB-Text CDB-Size-large u-altTextColor"><%- _t('editor.widgets.delete.desc') %></p>
|
||||
<ul class="Modal-listActions u-flex u-alignCenter">
|
||||
<li class="Modal-listActionsitem">
|
||||
<button class="CDB-Button CDB-Button--secondary CDB-Button--big js-cancel">
|
||||
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-medium u-upperCase">
|
||||
<%- _t('editor.widgets.delete.cancel') %>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="Modal-listActionsitem">
|
||||
<button class="CDB-Button CDB-Button--primary CDB-Button--big js-confirm">
|
||||
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-medium u-upperCase">
|
||||
<%- _t('editor.widgets.delete.confirm') %>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
4
lib/assets/javascripts/builder/editor/widgets/inline-editor.tpl
Executable file
@@ -0,0 +1,4 @@
|
||||
<h2 class="Inline-editor">
|
||||
<div class="CDB-Text CDB-Size-large u-ellipsis js-title Inline-editor-text"><%- title %></div>
|
||||
<input type="text" name="text" class="Inline-editor-input Inline-editor-input--small CDB-Text CDB-InputText js-input" value="<%- title %>" readonly>
|
||||
</h2>
|
||||
@@ -0,0 +1,164 @@
|
||||
var Backbone = require('backbone');
|
||||
var cdb = require('internal-carto.js');
|
||||
var _ = require('underscore');
|
||||
var moment = require('moment');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var QUERY_TEMPLATE = _.template('SELECT MAX(<%= column %>), MIN(<%= column %>) FROM (<%= table %>) __wrapped');
|
||||
|
||||
var STATUS = {
|
||||
unavailable: 'unavailable',
|
||||
unfetched: 'unfetched',
|
||||
fetching: 'fetching',
|
||||
fetched: 'fetched'
|
||||
};
|
||||
|
||||
var DEFAULT_MAX_BUCKETS = 367;
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'configModel',
|
||||
'querySchemaModel'
|
||||
];
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
defaults: {
|
||||
status: STATUS.unfetched,
|
||||
buckets: []
|
||||
},
|
||||
|
||||
initialize: function (attrs, opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
if (!_.has(attrs, 'column')) {
|
||||
throw new Error('Column is required');
|
||||
}
|
||||
|
||||
this._SQL = new cdb.SQL({
|
||||
user: this._configModel.get('user_name'),
|
||||
sql_api_template: this._configModel.get('sql_api_template'),
|
||||
api_key: this._configModel.get('api_key')
|
||||
});
|
||||
|
||||
this._initBinds();
|
||||
|
||||
if (this._querySchemaModel.get('status') === STATUS.fetched) {
|
||||
this._queryData();
|
||||
}
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.on('change:column', this._queryData, this);
|
||||
this.listenTo(this._querySchemaModel, 'change:status', this._onQuerySchemaStatusChanged);
|
||||
},
|
||||
|
||||
_onQuerySchemaStatusChanged: function () {
|
||||
if (this._querySchemaModel.get('status') === STATUS.fetched) {
|
||||
this._queryData();
|
||||
}
|
||||
},
|
||||
|
||||
_queryData: function () {
|
||||
if (this._querySchemaModel.getColumnType(this.get('column')) !== 'date') {
|
||||
return;
|
||||
}
|
||||
|
||||
var query = QUERY_TEMPLATE({
|
||||
column: this.get('column'),
|
||||
table: this._querySchemaModel.get('query')
|
||||
});
|
||||
|
||||
var callback = {
|
||||
success: this._onQuerySuccess.bind(this),
|
||||
error: this._onQueryError.bind(this)
|
||||
};
|
||||
|
||||
this.set('status', STATUS.fetching);
|
||||
this._SQL.execute(query, null, callback);
|
||||
},
|
||||
|
||||
_onQuerySuccess: function (data) {
|
||||
this.set('status', STATUS.fetched);
|
||||
var row = data.rows && data.rows[0];
|
||||
var max = row && row.max ? row.max : 0;
|
||||
var min = row && row.min ? row.min : 0;
|
||||
this._calculateBuckets(max, min);
|
||||
},
|
||||
|
||||
_onQueryError: function () {
|
||||
this.set('status', STATUS.unavailable);
|
||||
},
|
||||
|
||||
_calculateDecadesDiff: function (start, end) {
|
||||
var startYear = start.year();
|
||||
var endYear = end.year();
|
||||
var startDecade = Math.floor(startYear / 10);
|
||||
var endDecade = Math.floor((endYear + 10) / 10);
|
||||
|
||||
return endDecade - startDecade;
|
||||
},
|
||||
|
||||
_calculateBuckets: function (max, min) {
|
||||
var end = moment(max).utc();
|
||||
var start = moment(min).utc();
|
||||
var BUCKET_INCREMENT = 1;
|
||||
|
||||
var buckets = [{
|
||||
bins: end.diff(start, 'minutes', true),
|
||||
val: 'minute',
|
||||
label: 'Minutes'
|
||||
}, {
|
||||
bins: end.diff(start, 'hours', true),
|
||||
val: 'hour',
|
||||
label: 'Hours'
|
||||
}, {
|
||||
bins: end.diff(start, 'days', true),
|
||||
val: 'day',
|
||||
label: 'Days'
|
||||
}, {
|
||||
bins: end.diff(start, 'weeks', true),
|
||||
val: 'week',
|
||||
label: 'Weeks'
|
||||
}, {
|
||||
bins: end.diff(start, 'months', true),
|
||||
val: 'month',
|
||||
label: 'Months'
|
||||
}, {
|
||||
bins: end.diff(start, 'quarters', true),
|
||||
val: 'quarter',
|
||||
label: 'Quarters'
|
||||
}, {
|
||||
bins: end.diff(start, 'years', true),
|
||||
val: 'year',
|
||||
label: 'Years'
|
||||
}, {
|
||||
bins: this._calculateDecadesDiff(start, end),
|
||||
val: 'decade',
|
||||
label: 'Decades'
|
||||
}];
|
||||
|
||||
var incrementedBuckets = _.map(buckets, function (bucket) {
|
||||
var increment = bucket.val === 'decade'
|
||||
? 0
|
||||
: BUCKET_INCREMENT;
|
||||
|
||||
return _.extend(_.clone(bucket), {
|
||||
bins: Math.ceil(bucket.bins) + increment
|
||||
});
|
||||
});
|
||||
|
||||
this.set('buckets', incrementedBuckets);
|
||||
},
|
||||
|
||||
getFilteredBuckets: function (max) {
|
||||
var limit = max || DEFAULT_MAX_BUCKETS;
|
||||
var buckets = _.filter(this.get('buckets'), function (bucket) {
|
||||
return bucket.bins <= limit;
|
||||
});
|
||||
|
||||
return buckets;
|
||||
},
|
||||
|
||||
getPreferredBucket: function (max) {
|
||||
var sortedBuckets = _.sortBy(this.getFilteredBuckets(max), 'bins');
|
||||
return sortedBuckets.length > 0 ? sortedBuckets[sortedBuckets.length - 1] : {};
|
||||
}
|
||||
});
|
||||
11
lib/assets/javascripts/builder/editor/widgets/widget-icon-category.tpl
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg width="56px" height="32px" viewBox="498 425 56 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Bolean" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(498.000000, 425.000000)">
|
||||
<rect id="Rectangle-17" fill="#FFFFFF" x="0" y="0" width="56" height="32" rx="4"></rect>
|
||||
<path d="M1,2.00174332 L1,2.00174332 L1,29.9982567 C1,30.5564876 1.4426521,31 1.99306965,31 L54.0069304,31 C54.5486548,31 55,30.5482532 55,29.9982567 L55,2.00174332 C55,1.44351239 54.5573479,1 54.0069304,1 L1.99306965,1 C1.45134516,1 1,1.4517468 1,2.00174332 L1,2.00174332 Z M0,2.00174332 C0,0.89621101 0.902308181,0 1.99306965,0 L54.0069304,0 C55.1076723,0 56,0.889261723 56,2.00174332 L56,29.9982567 C56,31.103789 55.0976918,32 54.0069304,32 L1.99306965,32 C0.892327676,32 0,31.1107383 0,29.9982567 L0,2.00174332 L0,2.00174332 Z" id="Rectangle-774" fill="#EEEEEE"></path>
|
||||
<rect id="Rectangle-1876" fill="#EEEEEE" x="8" y="6" width="16" height="3"></rect>
|
||||
<rect id="Rectangle-1876" fill="#EEEEEE" x="8" y="15" width="40" height="2" rx="1"></rect>
|
||||
<rect id="Rectangle-1876-Copy" fill="#9DE0AD" x="8" y="15" width="31" height="2" rx="1"></rect>
|
||||
<rect id="Rectangle-1876" fill="#EEEEEE" x="8" y="22" width="40" height="2" rx="1"></rect>
|
||||
<rect id="Rectangle-1876-Copy" fill="#9DE0AD" x="8" y="22" width="11" height="2" rx="1"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
9
lib/assets/javascripts/builder/editor/widgets/widget-icon-formula.tpl
Executable file
@@ -0,0 +1,9 @@
|
||||
<svg width="56px" height="32px" viewBox="0 0 56 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Graf" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect id="Rectangle-17" fill="#FFFFFF" x="0" y="0" width="56" height="32" rx="4"></rect>
|
||||
<path d="M1,2.00174332 L1,2.00174332 L1,29.9982567 C1,30.5564876 1.4426521,31 1.99306965,31 L54.0069304,31 C54.5486548,31 55,30.5482532 55,29.9982567 L55,2.00174332 C55,1.44351239 54.5573479,1 54.0069304,1 L1.99306965,1 C1.45134516,1 1,1.4517468 1,2.00174332 L1,2.00174332 Z M0,2.00174332 C0,0.89621101 0.902308181,0 1.99306965,0 L54.0069304,0 C55.1076723,0 56,0.889261723 56,2.00174332 L56,29.9982567 C56,31.103789 55.0976918,32 54.0069304,32 L1.99306965,32 C0.892327676,32 0,31.1107383 0,29.9982567 L0,2.00174332 L0,2.00174332 Z" id="Rectangle-774" fill="#EEEEEE"></path>
|
||||
<rect id="Rectangle-1876" fill="#EEEEEE" x="8" y="6" width="16" height="3"></rect>
|
||||
<rect id="Rectangle-53" fill="#9DE0AD" x="8" y="19" width="40" height="7"></rect>
|
||||
<rect id="Rectangle-1876" fill="#EEEEEE" x="8" y="14" width="16" height="2"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
21
lib/assets/javascripts/builder/editor/widgets/widget-icon-histogram.tpl
Executable file
@@ -0,0 +1,21 @@
|
||||
<svg width="56px" height="32px" viewBox="498 425 56 32" version="1.1" mlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Bars" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(498.000000, 425.000000)">
|
||||
<rect id="Rectangle-17" fill="#FFFFFF" x="0" y="0" width="56" height="32" rx="4"></rect>
|
||||
<path d="M1,2.00174332 L1,2.00174332 L1,29.9982567 C1,30.5564876 1.4426521,31 1.99306965,31 L54.0069304,31 C54.5486548,31 55,30.5482532 55,29.9982567 L55,2.00174332 C55,1.44351239 54.5573479,1 54.0069304,1 L1.99306965,1 C1.45134516,1 1,1.4517468 1,2.00174332 L1,2.00174332 Z M0,2.00174332 C0,0.89621101 0.902308181,0 1.99306965,0 L54.0069304,0 C55.1076723,0 56,0.889261723 56,2.00174332 L56,29.9982567 C56,31.103789 55.0976918,32 54.0069304,32 L1.99306965,32 C0.892327676,32 0,31.1107383 0,29.9982567 L0,2.00174332 L0,2.00174332 Z" id="Rectangle-774" fill="#EEEEEE"></path>
|
||||
<rect id="Bar" fill="#9DE0AD" transform="translate(36.000000, 23.000000) scale(-1, 1) translate(-36.000000, -23.000000) " x="35" y="22" width="2" height="2"></rect>
|
||||
<rect id="Bar-Copy-6" fill="#9DE0AD" transform="translate(39.000000, 22.000000) scale(-1, 1) translate(-39.000000, -22.000000) " x="38" y="20" width="2" height="4"></rect>
|
||||
<rect id="Bar-Copy-7" fill="#9DE0AD" transform="translate(42.000000, 21.500000) scale(-1, 1) translate(-42.000000, -21.500000) " x="41" y="19" width="2" height="5"></rect>
|
||||
<rect id="Bar-Copy-8" fill="#9DE0AD" transform="translate(45.000000, 20.000000) scale(-1, 1) translate(-45.000000, -20.000000) " x="44" y="16" width="2" height="8"></rect>
|
||||
<rect id="Bar-Copy-9" fill="#9DE0AD" transform="translate(48.000000, 21.500000) scale(-1, 1) translate(-48.000000, -21.500000) " x="47" y="19" width="2" height="5"></rect>
|
||||
<rect id="Bar-Copy-2" fill="#9DE0AD" transform="translate(33.000000, 21.000000) scale(-1, 1) translate(-33.000000, -21.000000) " x="32" y="18" width="2" height="6"></rect>
|
||||
<rect id="Bar-Copy-3" fill="#9DE0AD" transform="translate(30.000000, 22.500000) scale(-1, 1) translate(-30.000000, -22.500000) " x="29" y="21" width="2" height="3"></rect>
|
||||
<rect id="Bar-Copy-4" fill="#9DE0AD" transform="translate(27.000000, 22.000000) scale(-1, 1) translate(-27.000000, -22.000000) " x="26" y="20" width="2" height="4"></rect>
|
||||
<polygon id="Bar-Copy-5" fill="#9DE0AD" transform="translate(24.000000, 20.500000) scale(-1, 1) translate(-24.000000, -20.500000) " points="23 17 25 17 25 24 23 24"></polygon>
|
||||
<rect id="Bar" fill="#9DE0AD" x="8" y="21" width="2" height="3"></rect>
|
||||
<rect id="Bar-Copy-2" fill="#9DE0AD" x="11" y="15" width="2" height="9"></rect>
|
||||
<rect id="Bar-Copy-3" fill="#9DE0AD" x="14" y="16" width="2" height="8"></rect>
|
||||
<rect id="Bar-Copy-4" fill="#9DE0AD" x="17" y="19" width="2" height="5"></rect>
|
||||
<polygon id="Bar-Copy-5" fill="#9DE0AD" points="20 16 22 16 22 24 20 24"></polygon>
|
||||
<rect id="Rectangle-1876" fill="#EEEEEE" x="7" y="7" width="16" height="3"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
13
lib/assets/javascripts/builder/editor/widgets/widget-icon-layer-category.tpl
Executable file
@@ -0,0 +1,13 @@
|
||||
<svg width="20px" height="18px" viewBox="10 11 20 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Group" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(10.000000, 11.000000)">
|
||||
<rect id="Bar" fill="#A4DAB1" x="0" y="2" width="13" height="2"></rect>
|
||||
<rect id="Bar" fill="#A4DAB1" x="0" y="9" width="10" height="2"></rect>
|
||||
<rect id="Bar" fill="#A4DAB1" x="0" y="16" width="12" height="2"></rect>
|
||||
<rect id="Bar" fill="#DDDDDD" x="0" y="7" width="7" height="1"></rect>
|
||||
<rect id="Bar" fill="#DDDDDD" x="0" y="14" width="5" height="1"></rect>
|
||||
<rect id="Bar" fill="#EEEEEE" x="13" y="2" width="7" height="2"></rect>
|
||||
<rect id="Bar" fill="#EEEEEE" x="10" y="9" width="10" height="2"></rect>
|
||||
<rect id="Bar" fill="#EEEEEE" x="12" y="16" width="8" height="2"></rect>
|
||||
<rect id="Bar" fill="#DDDDDD" x="0" y="0" width="5" height="1"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1009 B |
@@ -0,0 +1,6 @@
|
||||
<svg width="20px" height="8px" viewBox="0 0 20 8" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Group" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect id="Bar-Copy-2" fill="#9DE0AD" x="0" y="4" width="20" height="4"></rect>
|
||||
<rect id="Bar-Copy-3" fill="#DDDDDD" x="0" y="0" width="9" height="2"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 413 B |
@@ -0,0 +1,11 @@
|
||||
<svg width="20px" height="18px" viewBox="10 11 20 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Bars" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(10.000000, 11.000000)">
|
||||
<rect id="Bar" fill="#A4DAB1" x="0" y="10" width="2" height="8"></rect>
|
||||
<rect id="Bar" fill="#A4DAB1" x="3" y="6" width="2" height="12"></rect>
|
||||
<rect id="Bar" fill="#A4DAB1" x="6" y="2" width="2" height="16"></rect>
|
||||
<rect id="Bar" fill="#A4DAB1" x="9" y="0" width="2" height="18"></rect>
|
||||
<rect id="Bar" fill="#A4DAB1" x="12" y="2" width="2" height="16"></rect>
|
||||
<rect id="Bar" fill="#A4DAB1" x="15" y="5" width="2" height="13"></rect>
|
||||
<rect id="Bar" fill="#A4DAB1" x="18" y="8" width="2" height="10"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 850 B |
@@ -0,0 +1,19 @@
|
||||
<svg width="21px" height="18px" viewBox="10 11 21 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Group-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(10.000000, 11.000000)">
|
||||
<g id="Bars" transform="translate(0.000000, 8.000000)">
|
||||
<rect id="Bar" fill="#F2CC8F" x="4" y="0" width="1" height="10"></rect>
|
||||
<rect id="Bar" fill="#F2CC8F" x="6" y="1" width="1" height="9"></rect>
|
||||
<rect id="Bar-Copy" fill="#F2CC8F" x="0" y="5" width="1" height="5"></rect>
|
||||
<rect id="Bar" fill="#F2CC8F" x="8" y="2" width="1" height="8"></rect>
|
||||
<rect id="Bar" fill="#F2CC8F" x="10" y="3" width="1" height="7"></rect>
|
||||
<rect id="Bar" fill="#EEEEEE" x="12" y="4" width="1" height="6"></rect>
|
||||
<rect id="Bar" fill="#EEEEEE" x="14" y="5" width="1" height="5"></rect>
|
||||
<rect id="Bar" fill="#EEEEEE" x="16" y="6" width="1" height="4"></rect>
|
||||
<rect id="Bar-Copy" fill="#F2CC8F" x="2" y="3" width="1" height="7"></rect>
|
||||
<rect id="Bar" fill="#EEEEEE" x="18" y="7" width="1" height="3"></rect>
|
||||
<rect id="Bar" fill="#EEEEEE" x="20" y="7" width="1" height="3"></rect>
|
||||
</g>
|
||||
<polygon id="Rectangle-316" fill="#979EA1" points="0 5 0 0 4 2.5"></polygon>
|
||||
<rect id="Rectangle-1876" fill="#DDDDDD" x="8" y="1" width="12" height="3"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
21
lib/assets/javascripts/builder/editor/widgets/widget-icon-timeSeries.tpl
Executable file
@@ -0,0 +1,21 @@
|
||||
<svg width="56px" height="32px" viewBox="539 370 56 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Time-Series" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(539.000000, 370.000000)">
|
||||
<rect id="Rectangle-17" fill="#FFFFFF" x="0" y="0" width="56" height="32" rx="4"></rect>
|
||||
<path d="M1,2.00174332 L1,2.00174332 L1,29.9982567 C1,30.5564876 1.4426521,31 1.99306965,31 L54.0069304,31 C54.5486548,31 55,30.5482532 55,29.9982567 L55,2.00174332 C55,1.44351239 54.5573479,1 54.0069304,1 L1.99306965,1 C1.45134516,1 1,1.4517468 1,2.00174332 L1,2.00174332 Z M0,2.00174332 C0,0.89621101 0.902308181,0 1.99306965,0 L54.0069304,0 C55.1076723,0 56,0.889261723 56,2.00174332 L56,29.9982567 C56,31.103789 55.0976918,32 54.0069304,32 L1.99306965,32 C0.892327676,32 0,31.1107383 0,29.9982567 L0,2.00174332 L0,2.00174332 Z" id="Rectangle-774" fill="#EEEEEE"></path>
|
||||
<rect id="Bar" fill="#DDDDDD" opacity="0.5" transform="translate(34.000000, 23.500000) scale(-1, 1) translate(-34.000000, -23.500000) " x="33" y="22" width="2" height="3"></rect>
|
||||
<rect id="Bar-Copy-6" fill="#DDDDDD" opacity="0.5" transform="translate(37.000000, 24.000000) scale(-1, 1) translate(-37.000000, -24.000000) " x="36" y="23" width="2" height="2"></rect>
|
||||
<rect id="Bar-Copy-7" fill="#DDDDDD" opacity="0.5" transform="translate(40.000000, 24.000000) scale(-1, 1) translate(-40.000000, -24.000000) " x="39" y="23" width="2" height="2"></rect>
|
||||
<rect id="Bar-Copy-8" fill="#DDDDDD" opacity="0.5" transform="translate(43.000000, 24.500000) scale(-1, 1) translate(-43.000000, -24.500000) " x="42" y="24" width="2" height="1"></rect>
|
||||
<rect id="Bar-Copy-9" fill="#DDDDDD" opacity="0.5" transform="translate(46.000000, 24.500000) scale(-1, 1) translate(-46.000000, -24.500000) " x="45" y="24" width="2" height="1"></rect>
|
||||
<rect id="Bar-Copy-2" fill="#DDDDDD" opacity="0.5" transform="translate(31.000000, 23.500000) scale(-1, 1) translate(-31.000000, -23.500000) " x="30" y="22" width="2" height="3"></rect>
|
||||
<rect id="Bar-Copy-3" fill="#DDDDDD" opacity="0.5" transform="translate(28.000000, 23.000000) scale(-1, 1) translate(-28.000000, -23.000000) " x="27" y="21" width="2" height="4"></rect>
|
||||
<rect id="Bar-Copy-4" fill="#F2CC8F" transform="translate(25.000000, 23.000000) scale(-1, 1) translate(-25.000000, -23.000000) " x="24" y="21" width="2" height="4"></rect>
|
||||
<polygon id="Bar-Copy-5" fill="#F2CC8F" transform="translate(22.000000, 22.500000) scale(-1, 1) translate(-22.000000, -22.500000) " points="21 20 23 20 23 25 21 25"></polygon>
|
||||
<rect id="Bar-Copy-2" fill="#F2CC8F" x="9" y="24" width="2" height="1"></rect>
|
||||
<rect id="Bar-Copy-3" fill="#F2CC8F" x="12" y="21" width="2" height="4"></rect>
|
||||
<rect id="Bar-Copy-4" fill="#F2CC8F" x="15" y="18" width="2" height="7"></rect>
|
||||
<polygon id="Bar-Copy-5" fill="#F2CC8F" points="18 19 20 19 20 25 18 25"></polygon>
|
||||
<polygon id="Rectangle-316" fill="#979EA1" points="9 10 9 5 13 7.5"></polygon>
|
||||
<rect id="Rectangle-1876" fill="#EEEEEE" x="16" y="6" width="16" height="3"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
143
lib/assets/javascripts/builder/editor/widgets/widget-view.js
Executable file
@@ -0,0 +1,143 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var template = require('./widget-view.tpl');
|
||||
var ContextMenuFactory = require('builder/components/context-menu-factory-view');
|
||||
var InlineEditorView = require('builder/components/inline-editor/inline-editor-view');
|
||||
var templateInlineEditor = require('./inline-editor.tpl');
|
||||
var WidgetsService = require('./widgets-service');
|
||||
var Analyses = require('builder/data/analyses');
|
||||
var Router = require('builder/routes/router');
|
||||
|
||||
var widgetIconTemplateMap = {
|
||||
category: require('./widget-icon-layer-category.tpl'),
|
||||
histogram: require('./widget-icon-layer-histogram.tpl'),
|
||||
formula: require('./widget-icon-layer-formula.tpl'),
|
||||
'time-series': require('./widget-icon-layer-timeSeries.tpl')
|
||||
};
|
||||
|
||||
/**
|
||||
* View for an individual widget definition model.
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
module: 'editor:widgets:widget-view',
|
||||
|
||||
tagName: 'li',
|
||||
|
||||
className: 'BlockList-item js-widgetItem',
|
||||
|
||||
events: {
|
||||
'click': '_onEditWidget'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
if (!opts.layer) throw new Error('layer is required');
|
||||
if (!opts.userActions) throw new Error('userActions is required');
|
||||
|
||||
this.layer = opts.layer;
|
||||
this._userActions = opts.userActions;
|
||||
this.stackLayoutModel = opts.stackLayoutModel;
|
||||
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
this.listenToOnce(this.model, 'destroy', this._onDestroy);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
|
||||
var widgetType = this.model.get('type');
|
||||
var source = this.model.get('source');
|
||||
var analysisNode = this.layer.findAnalysisDefinitionNodeModel(source);
|
||||
var layerName = analysisNode.isSourceType()
|
||||
? this.layer.getTableName()
|
||||
: this.layer.getName();
|
||||
|
||||
this.$el.html(template({
|
||||
widgetType: widgetType,
|
||||
layerName: layerName,
|
||||
sourceId: source,
|
||||
sourceColor: analysisNode.getColor(),
|
||||
sourceType: Analyses.short_title(analysisNode.get('type')),
|
||||
isSourceType: analysisNode.isSourceType()
|
||||
}));
|
||||
this.$el.attr('data-model-cid', this.model.cid);
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var widgetType = this.model.get('type');
|
||||
|
||||
this._inlineEditor = new InlineEditorView({
|
||||
template: templateInlineEditor,
|
||||
renderOptions: {
|
||||
title: this.model.get('title')
|
||||
},
|
||||
onClick: this._onEditWidget.bind(this),
|
||||
onEdit: this._renameWidget.bind(this)
|
||||
});
|
||||
|
||||
this.$('.js-header').append(this._inlineEditor.render().el);
|
||||
this.addView(this._inlineEditor);
|
||||
|
||||
var iconTemplate = widgetIconTemplateMap[widgetType];
|
||||
|
||||
if (!iconTemplate) {
|
||||
console.log(widgetType + ' widget template not defined');
|
||||
} else {
|
||||
this.$('.js-widgetIcon').append(iconTemplate());
|
||||
}
|
||||
|
||||
var menuItems = [{
|
||||
label: _t('editor.widgets.options.rename'),
|
||||
val: 'rename-widget',
|
||||
action: this._onRenameWidget.bind(this)
|
||||
}, {
|
||||
label: _t('editor.widgets.options.edit'),
|
||||
val: 'edit-widget',
|
||||
action: this._onEditWidget.bind(this)
|
||||
}, {
|
||||
label: _t('editor.widgets.options.remove'),
|
||||
val: 'delete-widget',
|
||||
destructive: true,
|
||||
action: this._confirmDeleteWidget.bind(this)
|
||||
}];
|
||||
|
||||
this._contextMenuFactory = new ContextMenuFactory({
|
||||
menuItems: menuItems
|
||||
});
|
||||
|
||||
this.$('.js-context-menu').append(this._contextMenuFactory.render().el);
|
||||
this.addView(this._contextMenuFactory);
|
||||
},
|
||||
|
||||
_onRenameWidget: function () {
|
||||
this._inlineEditor.edit();
|
||||
},
|
||||
|
||||
_renameWidget: function () {
|
||||
var newName = this._inlineEditor.getValue();
|
||||
|
||||
if (newName !== '' && newName !== this.model.get('title')) {
|
||||
this.$('.js-title').text(newName).show();
|
||||
this._inlineEditor.hide();
|
||||
this.model.set({title: newName});
|
||||
this._userActions.saveWidget(this.model);
|
||||
}
|
||||
},
|
||||
|
||||
_confirmDeleteWidget: function () {
|
||||
WidgetsService.removeWidget(this.model);
|
||||
},
|
||||
|
||||
_onEditWidget: function (event) {
|
||||
event && event.stopPropagation();
|
||||
WidgetsService.editWidget(this.model);
|
||||
},
|
||||
|
||||
_onDestroy: function () {
|
||||
Router.goToWidgetList();
|
||||
this.clean();
|
||||
}
|
||||
});
|
||||
35
lib/assets/javascripts/builder/editor/widgets/widget-view.tpl
Executable file
@@ -0,0 +1,35 @@
|
||||
<div class="BlockList-dragIcon">
|
||||
<div class="CDB-Shape">
|
||||
<div class="CDB-Shape-rectsHandle is-small">
|
||||
<div class="CDB-Shape-rectsHandleItem CDB-Shape-rectsHandleItem--grey is-first"></div>
|
||||
<div class="CDB-Shape-rectsHandleItem CDB-Shape-rectsHandleItem--grey is-second"></div>
|
||||
<div class="CDB-Shape-rectsHandleItem CDB-Shape-rectsHandleItem--grey is-third"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="BlockList-media u-rSpace--m js-widgetIcon">
|
||||
</div>
|
||||
<div class="BlockList-inner u-ellipsis">
|
||||
<div class="BlockList-title u-bSpace js-context-menu">
|
||||
<div class="BlockList-titleText js-header"></div>
|
||||
</div>
|
||||
<div class="u-flex u-alignCenter">
|
||||
<span class="CDB-Text CDB-Size-small is-semibold u-upperCase" style="color: <%- sourceColor %>;">
|
||||
<%- sourceId %>
|
||||
</span>
|
||||
|
||||
<% if (!isSourceType) { %>
|
||||
<span class="CDB-Text CDB-Size-small u-lSpace--s u-flex" style="color: <%- sourceColor %>;">
|
||||
<i class="CDB-IconFont CDB-Size-small CDB-IconFont-ray"></i>
|
||||
</span>
|
||||
<% } %>
|
||||
|
||||
<span class="CDB-Text CDB-Size-small u-mainTextColor u-lSpace">
|
||||
<%= sourceType %>
|
||||
</span>
|
||||
|
||||
<span class="CDB-Text CDB-Size-small u-altTextColor u-ellipsis u-lSpace" title="<%= layerName %>">
|
||||
<%= layerName %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
3
lib/assets/javascripts/builder/editor/widgets/widgets-content-error.tpl
Executable file
@@ -0,0 +1,3 @@
|
||||
<h2 class="CDB-Text CDB-Size-huge is-light u-secondaryTextColor">
|
||||
<%= body %>
|
||||
</h2>
|
||||
35
lib/assets/javascripts/builder/editor/widgets/widgets-content-not-ready.tpl
Executable file
@@ -0,0 +1,35 @@
|
||||
<div class="FormPlaceholder-widget u-tSpace-m u-flex">
|
||||
<div class="FormPlaceholder-widgetIcon u-rSpace--m"></div>
|
||||
<div class="FormPlaceholder-widgetInner">
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--long"></span>
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--long"></span>
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--med u-tSpace-xl"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="FormPlaceholder-widget u-tSpace-m u-flex">
|
||||
<div class="FormPlaceholder-widgetIcon u-rSpace--m"></div>
|
||||
<div class="FormPlaceholder-widgetInner">
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--med"></span>
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--med"></span>
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--long u-tSpace-xl"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="FormPlaceholder-widget u-tSpace-m u-flex">
|
||||
<div class="FormPlaceholder-widgetIcon u-rSpace--m"></div>
|
||||
<div class="FormPlaceholder-widgetInner">
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--med"></span>
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--med"></span>
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--short u-tSpace-xl"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="FormPlaceholder-widget u-tSpace-m u-flex">
|
||||
<div class="FormPlaceholder-widgetIcon u-rSpace--m"></div>
|
||||
<div class="FormPlaceholder-widgetInner">
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--long"></span>
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--long"></span>
|
||||
<span class="FormPlaceholder-title FormPlaceholder-size--short u-tSpace-xl"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<h2 class="Inline-editor">
|
||||
<div class="CDB-Text CDB-Size-huge is-light u-ellipsis js-title Inline-editor-text"><%- title %></div>
|
||||
<input type="text" name="text" class="Inline-editor-input Inline-editor-input--small CDB-Text CDB-InputText js-input" value="<%- title %>" readonly>
|
||||
</h2>
|
||||
@@ -0,0 +1,123 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var FillConstants = require('builder/components/form-components/_constants/_fill');
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
|
||||
initialize: function (attrs, options) {
|
||||
var o = [
|
||||
{
|
||||
val: true,
|
||||
label: _t('editor.widgets.widgets-form.style.yes')
|
||||
}, {
|
||||
val: false,
|
||||
label: _t('editor.widgets.widgets-form.style.no')
|
||||
}
|
||||
];
|
||||
this.schema = {
|
||||
sync_on_bbox_change: {
|
||||
type: 'Radio',
|
||||
title: _t('editor.widgets.widgets-form.style.sync_on_bbox_change'),
|
||||
options: o
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_addAllStyleSchemaAttributes: function () {
|
||||
var customType = this.get('type') === 'category' ? 'categories' : 'ramp';
|
||||
var styleAttrs = {
|
||||
widget_style_definition: {
|
||||
type: 'Fill',
|
||||
title: _t('editor.widgets.widgets-form.style.fill'),
|
||||
options: [],
|
||||
configModel: this._configModel,
|
||||
modals: this._modals,
|
||||
userModel: this._userModel,
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
editorAttrs: {
|
||||
color: {
|
||||
hidePanes: [FillConstants.Panes.BY_VALUE],
|
||||
disableOpacity: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (this.get('auto_style_allowed')) {
|
||||
var editorAttrs = {
|
||||
color: {
|
||||
hidePanes: [FillConstants.Panes.FIXED]
|
||||
}
|
||||
};
|
||||
|
||||
if (customType === 'categories') {
|
||||
editorAttrs.color.hideTabs = [
|
||||
FillConstants.Tabs.BINS,
|
||||
FillConstants.Tabs.QUANTIFICATION
|
||||
];
|
||||
}
|
||||
|
||||
styleAttrs = _.extend(styleAttrs, {
|
||||
auto_style_definition: {
|
||||
type: 'EnablerEditor',
|
||||
title: '',
|
||||
label: _t('editor.widgets.widgets-form.style.custom-colors'),
|
||||
help: _t('editor.widgets.widgets-form.style.custom-help'),
|
||||
editor: {
|
||||
type: 'Fill',
|
||||
title: '',
|
||||
options: [this.get('column')],
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
query: 'query',
|
||||
configModel: this._configModel,
|
||||
modals: this._modals,
|
||||
userModel: this._userModel,
|
||||
editorAttrs: editorAttrs
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
styleAttrs = _.extend(styleAttrs, {
|
||||
auto_style_definition: {
|
||||
type: 'Text',
|
||||
title: _t('editor.widgets.widgets-form.style.custom-colors'),
|
||||
help: _t('editor.widgets.widgets-form.style.custom-disabled'),
|
||||
disabled: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.schema = _.extend(this.schema, styleAttrs);
|
||||
},
|
||||
|
||||
parse: function (r) {
|
||||
var attrs = _.defaults(
|
||||
{
|
||||
sync_on_bbox_change: r.sync_on_bbox_change ? 'true' : 'false'
|
||||
},
|
||||
r
|
||||
);
|
||||
return attrs;
|
||||
},
|
||||
|
||||
changeWidgetDefinitionModel: function (widgetDefinitionModel) {
|
||||
var attrs = _.defaults(
|
||||
{
|
||||
sync_on_bbox_change: this.get('sync_on_bbox_change') === 'true'
|
||||
},
|
||||
this._prepareAttributesForWidgetDefinition()
|
||||
);
|
||||
|
||||
if (attrs.auto_style_definition !== '' && _.isEmpty(attrs.auto_style_definition)) {
|
||||
attrs.auto_style_definition = WidgetDefinitionModel.getDefaultAutoStyle(widgetDefinitionModel.get('type'), widgetDefinitionModel.get('column'));
|
||||
}
|
||||
|
||||
widgetDefinitionModel.set(attrs);
|
||||
},
|
||||
|
||||
_prepareAttributesForWidgetDefinition: function () {
|
||||
return this.attributes;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,122 @@
|
||||
var _ = require('underscore');
|
||||
var WidgetsFormBaseSchema = require('./widgets-form-base-schema-model');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'columnOptionsFactory',
|
||||
'modals',
|
||||
'configModel',
|
||||
'userModel'
|
||||
];
|
||||
|
||||
module.exports = WidgetsFormBaseSchema.extend({
|
||||
|
||||
defaults: {
|
||||
schema: {},
|
||||
aggregate: {
|
||||
attribute: '',
|
||||
operator: 'count'
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (attrs, opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this.on('change:aggregate', this._updateAggregation, this);
|
||||
this.on('change:column', this.updateSchema, this);
|
||||
|
||||
this._updateAggregation();
|
||||
|
||||
WidgetsFormBaseSchema.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
getFields: function () {
|
||||
var fields = ['column', 'aggregate', 'prefix', 'suffix'];
|
||||
|
||||
return {
|
||||
data: fields.join(','),
|
||||
style: ['sync_on_bbox_change', 'widget_style_definition', 'auto_style_definition']
|
||||
};
|
||||
},
|
||||
|
||||
_updateAggregation: function () {
|
||||
var aggregate = this.get('aggregate');
|
||||
if (aggregate === undefined) {
|
||||
this.set({
|
||||
aggregation: 'count',
|
||||
aggregation_column: ''
|
||||
});
|
||||
} else {
|
||||
this.set({
|
||||
aggregation_column: aggregate.attribute,
|
||||
aggregation: aggregate.operator
|
||||
});
|
||||
}
|
||||
|
||||
this.updateSchema();
|
||||
},
|
||||
|
||||
updateSchema: function () {
|
||||
var columnOptions = this._columnOptionsFactory.create(this.get('column'));
|
||||
var helpMsg = this._columnOptionsFactory.unavailableColumnsHelpMessage();
|
||||
var aggregationOptions = _.filter(columnOptions, function (column) {
|
||||
return column.type === 'number';
|
||||
});
|
||||
|
||||
this.schema = _.extend(this.schema, {
|
||||
column: {
|
||||
type: 'Select',
|
||||
title: _t('editor.widgets.widgets-form.data.aggregate-by'),
|
||||
options: columnOptions,
|
||||
help: helpMsg,
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
editorAttrs: {
|
||||
disabled: this._columnOptionsFactory.areColumnsUnavailable()
|
||||
}
|
||||
},
|
||||
aggregate: {
|
||||
type: 'Operators',
|
||||
title: _t('editor.widgets.widgets-form.data.operation'),
|
||||
options: aggregationOptions,
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
editorAttrs: {
|
||||
showSearch: false
|
||||
}
|
||||
},
|
||||
suffix: {
|
||||
type: 'EnablerEditor',
|
||||
title: '',
|
||||
label: _t('editor.widgets.widgets-form.data.suffix'),
|
||||
editor: {
|
||||
type: 'Text'
|
||||
}
|
||||
},
|
||||
prefix: {
|
||||
type: 'EnablerEditor',
|
||||
title: '',
|
||||
label: _t('editor.widgets.widgets-form.data.prefix'),
|
||||
editor: {
|
||||
type: 'Text'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._addAllStyleSchemaAttributes();
|
||||
},
|
||||
|
||||
canSave: function () {
|
||||
var aggregation = this.get('aggregation');
|
||||
var aggregationColumn = this.get('aggregation_column');
|
||||
var canSave = false;
|
||||
|
||||
if (aggregation === 'count') {
|
||||
canSave = true;
|
||||
} else {
|
||||
canSave = !!(aggregation && aggregationColumn);
|
||||
}
|
||||
|
||||
return canSave;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
var _ = require('underscore');
|
||||
var WidgetsFormBaseSchema = require('./widgets-form-base-schema-model');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
|
||||
module.exports = WidgetsFormBaseSchema.extend({
|
||||
|
||||
defaults: {
|
||||
schema: {},
|
||||
aggregate: {
|
||||
attribute: '',
|
||||
operator: 'count'
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function (attrs, opts) {
|
||||
if (!opts.columnOptionsFactory) throw new Error('columnOptionsFactory is required');
|
||||
this._columnOptionsFactory = opts.columnOptionsFactory;
|
||||
|
||||
this.listenTo(this, 'change:aggregate', this._updateAggregate);
|
||||
|
||||
this._updateAggregate();
|
||||
|
||||
WidgetsFormBaseSchema.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
parse: function (r) {
|
||||
r.aggregate = {
|
||||
attribute: r.column,
|
||||
operator: r.operation
|
||||
};
|
||||
|
||||
return r;
|
||||
},
|
||||
|
||||
getFields: function () {
|
||||
return {
|
||||
data: 'aggregate,prefix,suffix,description',
|
||||
style: 'sync_on_bbox_change'
|
||||
};
|
||||
},
|
||||
|
||||
_updateAggregate: function () {
|
||||
var aggregate = this.get('aggregate');
|
||||
this.set({
|
||||
column: aggregate.attribute,
|
||||
operation: aggregate.operator
|
||||
});
|
||||
|
||||
this.updateSchema();
|
||||
},
|
||||
|
||||
updateSchema: function () {
|
||||
var columnOptions = this._columnOptionsFactory.create(this.get('column'), this._isNumberType);
|
||||
|
||||
this.schema = _.extend(this.schema, {
|
||||
aggregate: {
|
||||
type: 'Operators',
|
||||
title: _t('editor.widgets.widgets-form.data.operation'),
|
||||
options: columnOptions,
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
editorAttrs: {
|
||||
showSearch: false
|
||||
}
|
||||
},
|
||||
suffix: {
|
||||
type: 'EnablerEditor',
|
||||
title: '',
|
||||
label: _t('editor.widgets.widgets-form.data.suffix'),
|
||||
editor: {
|
||||
type: 'Text'
|
||||
}
|
||||
},
|
||||
prefix: {
|
||||
type: 'EnablerEditor',
|
||||
title: '',
|
||||
label: _t('editor.widgets.widgets-form.data.prefix'),
|
||||
editor: {
|
||||
type: 'Text'
|
||||
}
|
||||
},
|
||||
description: {
|
||||
type: 'EnablerEditor',
|
||||
title: '',
|
||||
label: _t('editor.widgets.widgets-form.style.description'),
|
||||
editor: {
|
||||
type: 'TextArea'
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
canSave: function () {
|
||||
var column = this.get('column');
|
||||
var operation = this.get('operation');
|
||||
|
||||
return operation === 'count' || !!column;
|
||||
},
|
||||
|
||||
_isNumberType: function (m) {
|
||||
return m.get('type') === 'number';
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
var _ = require('underscore');
|
||||
var WidgetsFormBaseSchema = require('./widgets-form-base-schema-model');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
|
||||
var NUMBER_TYPE = 'number';
|
||||
var REQUIRED_OPTS = [
|
||||
'columnOptionsFactory',
|
||||
'modals',
|
||||
'configModel',
|
||||
'userModel'
|
||||
];
|
||||
|
||||
module.exports = WidgetsFormBaseSchema.extend({
|
||||
initialize: function (attrs, opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
WidgetsFormBaseSchema.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
getFields: function () {
|
||||
return {
|
||||
data: ['column', 'bins'],
|
||||
style: ['sync_on_bbox_change', 'widget_style_definition', 'auto_style_definition']
|
||||
};
|
||||
},
|
||||
|
||||
updateSchema: function () {
|
||||
var columnOptions = this._columnOptionsFactory.create(this.get('column'), this._isNumberType);
|
||||
var helpMsg = this._columnOptionsFactory.unavailableColumnsHelpMessage();
|
||||
|
||||
this.schema = _.extend(this.schema, {
|
||||
column: {
|
||||
title: _t('editor.widgets.widgets-form.data.column'),
|
||||
type: 'Select',
|
||||
help: helpMsg,
|
||||
options: columnOptions,
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
editorAttrs: {
|
||||
disabled: this._columnOptionsFactory.areColumnsUnavailable()
|
||||
},
|
||||
validators: [{
|
||||
type: 'columnType',
|
||||
columnsCollection: this._columnOptionsFactory._querySchemaModel.columnsCollection,
|
||||
columnType: 'number'
|
||||
}]
|
||||
},
|
||||
bins: {
|
||||
title: _t('editor.widgets.widgets-form.data.bins'),
|
||||
type: 'Number',
|
||||
validators: ['required', {
|
||||
type: 'interval',
|
||||
min: 2,
|
||||
max: 30
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
this._addAllStyleSchemaAttributes();
|
||||
},
|
||||
|
||||
canSave: function () {
|
||||
return !!this.get('column');
|
||||
},
|
||||
|
||||
_isNumberType: function (m) {
|
||||
return m.get('type') === NUMBER_TYPE;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,206 @@
|
||||
var _ = require('underscore');
|
||||
|
||||
var WidgetsFormBaseSchema = require('./widgets-form-base-schema-model');
|
||||
var TimeSeriesQueryModel = require('builder/editor/widgets/time-series-query-model');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var FillConstants = require('builder/components/form-components/_constants/_fill');
|
||||
|
||||
var timezones = require('builder/data/timezones');
|
||||
var moment = require('moment');
|
||||
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
require('moment-timezone');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'columnOptionsFactory',
|
||||
'configModel',
|
||||
'querySchemaModel'
|
||||
];
|
||||
|
||||
module.exports = WidgetsFormBaseSchema.extend({
|
||||
defaults: {
|
||||
schema: {},
|
||||
bins: 48,
|
||||
timezone: '',
|
||||
offset: 0
|
||||
},
|
||||
|
||||
initialize: function (attrs, opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
WidgetsFormBaseSchema.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this._timeSeriesQueryModel = new TimeSeriesQueryModel({
|
||||
column: this.get('column')
|
||||
}, {
|
||||
configModel: this._configModel,
|
||||
querySchemaModel: this._querySchemaModel
|
||||
});
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.on('change:column', this._onColumnChanged, this);
|
||||
this.listenTo(this._timeSeriesQueryModel, 'change:buckets', this.updateSchema);
|
||||
},
|
||||
|
||||
getFields: function () {
|
||||
var columnType = this._getColumnType();
|
||||
var data = ['column'];
|
||||
|
||||
if (columnType === 'date') {
|
||||
data.push('timezone', 'aggregation');
|
||||
} else {
|
||||
data.push('bins');
|
||||
}
|
||||
|
||||
return {
|
||||
data: data,
|
||||
style: ['sync_on_bbox_change', 'widget_style_definition']
|
||||
};
|
||||
},
|
||||
|
||||
updateSchema: function () {
|
||||
var columnOptions = this._columnOptionsFactory.create(this.get('column'), this._isNumberOrDateType.bind(this));
|
||||
var helpMsg = this._columnOptionsFactory.unavailableColumnsHelpMessage();
|
||||
|
||||
this.schema = _.extend(this.schema, {
|
||||
column: {
|
||||
title: _t('editor.widgets.widgets-form.data.column'),
|
||||
type: 'Select',
|
||||
help: helpMsg,
|
||||
options: columnOptions,
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
editorAttrs: {
|
||||
disabled: this._columnOptionsFactory.areColumnsUnavailable()
|
||||
}
|
||||
},
|
||||
widget_style_definition: {
|
||||
type: 'Fill',
|
||||
title: _t('editor.widgets.widgets-form.style.fill'),
|
||||
options: [],
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
editorAttrs: {
|
||||
color: {
|
||||
hidePanes: [FillConstants.Panes.BY_VALUE],
|
||||
disableOpacity: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var columnType = this._getColumnType();
|
||||
if (columnType === 'date') {
|
||||
var aggregationOptions = this._timeSeriesQueryModel.getFilteredBuckets();
|
||||
var sortedTimezoneOptions = this._getSortedTimezoneOptions(timezones);
|
||||
|
||||
this.schema = _.extend(this.schema, {
|
||||
aggregation: {
|
||||
title: _t('editor.widgets.widgets-form.data.bins'),
|
||||
type: 'Select',
|
||||
placeholder: _t('editor.widgets.widgets-form.data.select-bucket'),
|
||||
searchPlaceholder: _t('editor.widgets.widgets-form.data.search-by-bucket'),
|
||||
options: aggregationOptions,
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
loading: _.isEmpty(aggregationOptions)
|
||||
},
|
||||
timezone: {
|
||||
title: _t('editor.widgets.widgets-form.data.timezone'),
|
||||
type: 'Select',
|
||||
options: sortedTimezoneOptions,
|
||||
dialogMode: DialogConstants.Mode.FLOAT
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.schema = _.extend(this.schema, {
|
||||
bins: {
|
||||
title: _t('editor.widgets.widgets-form.data.bins'),
|
||||
type: 'Number',
|
||||
validators: ['required', {
|
||||
type: 'interval',
|
||||
min: 0,
|
||||
max: 256
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.trigger('changeSchema');
|
||||
},
|
||||
|
||||
canSave: function () {
|
||||
return this.get('column');
|
||||
},
|
||||
|
||||
_isDateType: function (model) {
|
||||
return model.get('type') === 'date';
|
||||
},
|
||||
|
||||
_isNumberType: function (model) {
|
||||
return model.get('type') === 'number';
|
||||
},
|
||||
|
||||
_isNumberOrDateType: function (model) {
|
||||
return this._isDateType(model) || this._isNumberType(model);
|
||||
},
|
||||
|
||||
_onColumnChanged: function () {
|
||||
this.set({
|
||||
aggregation: undefined
|
||||
}, { silent: true });
|
||||
this._timeSeriesQueryModel.set('column', this.get('column'));
|
||||
this.set('column_type', this._getColumnType());
|
||||
this.updateSchema();
|
||||
},
|
||||
|
||||
_getColumnType: function () {
|
||||
var column;
|
||||
if (this._querySchemaModel.isFetched()) {
|
||||
column = this._querySchemaModel.columnsCollection.findWhere({ name: this.get('column') });
|
||||
}
|
||||
return column && column.get('type');
|
||||
},
|
||||
|
||||
_prepareAttributesForWidgetDefinition: function () {
|
||||
var attrs = this.toJSON();
|
||||
|
||||
if (this._getColumnType() === 'date') {
|
||||
attrs.bins = undefined;
|
||||
attrs.offset = moment.tz(attrs.timezone).utcOffset() * 60;
|
||||
} else {
|
||||
attrs.aggregation = undefined;
|
||||
attrs.timezone = undefined;
|
||||
attrs.offset = undefined;
|
||||
}
|
||||
|
||||
return attrs;
|
||||
},
|
||||
|
||||
_getSortedTimezoneOptions: function (timezones) {
|
||||
return _.chain(timezones)
|
||||
.reduce(function (memo, tz) {
|
||||
var name = tz.name;
|
||||
|
||||
memo.push({
|
||||
label: tz.label,
|
||||
name: name,
|
||||
offset: moment.tz(name).utcOffset()
|
||||
});
|
||||
|
||||
return memo;
|
||||
}, [])
|
||||
.sortBy('offset')
|
||||
.reduce(function (memo, tz) {
|
||||
var name = tz.name;
|
||||
var timezone = tz.offset ? moment.tz(name).format('Z') : '';
|
||||
|
||||
memo.push({
|
||||
label: '(GMT' + timezone + ') ' + tz.label,
|
||||
val: name
|
||||
});
|
||||
|
||||
return memo;
|
||||
}, [])
|
||||
.value();
|
||||
}
|
||||
});
|
||||
133
lib/assets/javascripts/builder/editor/widgets/widgets-form/widget-header.js
Executable file
@@ -0,0 +1,133 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var template = require('./widget-header.tpl');
|
||||
var InlineEditorView = require('builder/components/inline-editor/inline-editor-view');
|
||||
var VisTableModel = require('builder/data/visualization-table-model');
|
||||
var templateInlineEditor = require('./inline-editor.tpl');
|
||||
var ContextMenuFactory = require('builder/components/context-menu-factory-view');
|
||||
var WidgetsService = require('builder/editor/widgets/widgets-service');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'layerDefinitionModel',
|
||||
'userActions',
|
||||
'stackLayoutModel',
|
||||
'configModel'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
events: {
|
||||
'click .js-toggle-menu': '_onToggleContextMenuClicked'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
if (!this.model) throw new Error('model is required');
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this._sourceNode = this._getSourceNode();
|
||||
|
||||
if (this._sourceNode) {
|
||||
var tableName = this._sourceNode.get('table_name');
|
||||
this._visTableModel = new VisTableModel({
|
||||
id: tableName,
|
||||
table: {
|
||||
name: tableName
|
||||
}
|
||||
}, {
|
||||
configModel: this._configModel
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var widgetTitle = this.model.get('title');
|
||||
var source = this.model.get('source');
|
||||
var analysisNode = this._layerDefinitionModel.findAnalysisDefinitionNodeModel(source);
|
||||
var layerName = analysisNode.isSourceType()
|
||||
? this._layerDefinitionModel.getTableName()
|
||||
: this._layerDefinitionModel.getName();
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
title: widgetTitle,
|
||||
source: source,
|
||||
color: this._layerDefinitionModel.get('color'),
|
||||
layerName: layerName,
|
||||
nodeTitle: analyses.short_title(analysisNode),
|
||||
isSourceType: analysisNode.isSourceType(),
|
||||
url: this._visTableModel ? this._visTableModel.datasetURL() : ''
|
||||
})
|
||||
);
|
||||
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var widgetTitle = this.model.get('title');
|
||||
|
||||
this._inlineEditor = new InlineEditorView({
|
||||
template: templateInlineEditor,
|
||||
renderOptions: {
|
||||
title: widgetTitle
|
||||
},
|
||||
onEdit: this._renameWidget.bind(this)
|
||||
});
|
||||
|
||||
this.$('.js-header').append(this._inlineEditor.render().el);
|
||||
this.addView(this._inlineEditor);
|
||||
|
||||
var menuItems = [{
|
||||
label: _t('editor.widgets.options.rename'),
|
||||
val: 'rename-widget',
|
||||
action: this._onRenameWidget.bind(this)
|
||||
}, {
|
||||
label: _t('editor.widgets.options.remove'),
|
||||
val: 'delete-widget',
|
||||
destructive: true,
|
||||
action: this._confirmDeleteWidget.bind(this)
|
||||
}];
|
||||
|
||||
this._contextMenuFactory = new ContextMenuFactory({
|
||||
menuItems: menuItems
|
||||
});
|
||||
|
||||
this.$('.js-context-menu').append(this._contextMenuFactory.render().el);
|
||||
this.addView(this._contextMenuFactory);
|
||||
},
|
||||
|
||||
_getSourceNode: function () {
|
||||
var nodeModel = this._layerDefinitionModel.getAnalysisDefinitionNodeModel();
|
||||
|
||||
var source;
|
||||
if (nodeModel.get('type') === 'source') {
|
||||
source = nodeModel;
|
||||
} else {
|
||||
var primarySource = nodeModel.getPrimarySource();
|
||||
if (primarySource && primarySource.get('type') === 'source') {
|
||||
source = primarySource;
|
||||
}
|
||||
}
|
||||
|
||||
return source;
|
||||
},
|
||||
|
||||
_onRenameWidget: function () {
|
||||
this._inlineEditor.edit();
|
||||
},
|
||||
|
||||
_renameWidget: function () {
|
||||
var newName = this._inlineEditor.getValue();
|
||||
|
||||
if (newName !== '' && newName !== this.model.get('title')) {
|
||||
this.model.set({title: newName});
|
||||
this._userActions.saveWidget(this.model);
|
||||
this.$('.js-title').text(newName).show();
|
||||
this._inlineEditor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_confirmDeleteWidget: function () {
|
||||
WidgetsService.removeWidget(this.model);
|
||||
}
|
||||
});
|
||||
39
lib/assets/javascripts/builder/editor/widgets/widgets-form/widget-header.tpl
Executable file
@@ -0,0 +1,39 @@
|
||||
<ul class="Editor-breadcrumb">
|
||||
<li class="Editor-breadcrumbItem CDB-Text CDB-Size-medium u-actionTextColor">
|
||||
<button class="js-back">
|
||||
<i class="CDB-IconFont CDB-IconFont-arrowPrev Size-large u-rSpace"></i>
|
||||
|
||||
<span class="Editor-breadcrumbLink"><%- _t('back') %></span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<li class="Editor-breadcrumbItem CDB-Text CDB-Size-medium"><span class="Editor-breadcrumbSep"> / </span> <%- _t('editor.widgets.breadcrumb.widget-options') %></li>
|
||||
</ul>
|
||||
|
||||
<div class="Editor-HeaderInfoEditor">
|
||||
<div class="Editor-HeaderInfo-inner Editor-HeaderInfo-inner--wide">
|
||||
<div class="Editor-HeaderInfo-title js-context-menu">
|
||||
<div class="Editor-HeaderInfo-titleText js-header"></div>
|
||||
</div>
|
||||
|
||||
<div class="Editor-HeaderInfo u-flex u-alignCenter">
|
||||
<span class="CDB-Text CDB-Size-small is-semibold u-bSpace--s u-upperCase" style="color: <%- color %>;">
|
||||
<%- source %>
|
||||
</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-small u-lSpace">
|
||||
<%= nodeTitle %>
|
||||
</span>
|
||||
|
||||
<span class="CDB-Text CDB-Size-small u-altTextColor u-ellipsis u-lSpace" title="<%= layerName %>">
|
||||
<%= layerName %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Object to generate column options for a current state of a query schema model
|
||||
*/
|
||||
var F = function (querySchemaModel) {
|
||||
this._querySchemaModel = querySchemaModel;
|
||||
};
|
||||
|
||||
F.prototype.areColumnsUnavailable = function () {
|
||||
var status = this._querySchemaModel.get('status');
|
||||
return status === 'fetching' || status === 'unavailable';
|
||||
};
|
||||
|
||||
F.prototype.unavailableColumnsHelpMessage = function () {
|
||||
if (this._querySchemaModel.get('status') === 'unavailable') {
|
||||
return _t('editor.widgets.widgets-form.data.columns-unavailable');
|
||||
}
|
||||
};
|
||||
|
||||
F.prototype.create = function (currentVal, columnFilter) {
|
||||
columnFilter = columnFilter || function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
switch (this._querySchemaModel.get('status')) {
|
||||
case 'fetching':
|
||||
return [{val: _t('editor.widgets.widgets-form.data.loading')}];
|
||||
case 'unavailable':
|
||||
return [{val: currentVal}];
|
||||
default:
|
||||
return this._querySchemaModel
|
||||
.columnsCollection
|
||||
.filter(columnFilter)
|
||||
.map(function (m) {
|
||||
var columnName = m.get('name');
|
||||
return {
|
||||
val: columnName,
|
||||
label: columnName,
|
||||
type: m.get('type')
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = F;
|
||||
@@ -0,0 +1,77 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var WidgetsFormView = require('./widgets-form-view');
|
||||
var WidgetHeaderView = require('./widget-header.js');
|
||||
var ScrollView = require('builder/components/scroll/scroll-view');
|
||||
var Router = require('builder/routes/router');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'userActions',
|
||||
'widgetDefinitionModel',
|
||||
'modals',
|
||||
'analysisDefinitionNodesCollection',
|
||||
'layerDefinitionsCollection',
|
||||
'stackLayoutModel',
|
||||
'configModel',
|
||||
'userModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* View to render all necessary for the widget form
|
||||
*/
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
events: {
|
||||
'click .js-back': '_goBack'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var self = this;
|
||||
var nodeId = self._widgetDefinitionModel.get('source');
|
||||
var analysisDefinitionNodeModel = self._analysisDefinitionNodesCollection.get(nodeId);
|
||||
|
||||
var header = new WidgetHeaderView({
|
||||
layerDefinitionModel: this._layerDefinitionsCollection.get(this._widgetDefinitionModel.get('layer_id')),
|
||||
model: this._widgetDefinitionModel,
|
||||
modals: this._modals,
|
||||
userActions: this._userActions,
|
||||
stackLayoutModel: this._stackLayoutModel,
|
||||
configModel: this._configModel
|
||||
});
|
||||
this.$el.append(header.render().$el);
|
||||
this.addView(header);
|
||||
|
||||
var view = new ScrollView({
|
||||
createContentView: function () {
|
||||
return new WidgetsFormView({
|
||||
userActions: self._userActions,
|
||||
widgetDefinitionModel: self._widgetDefinitionModel,
|
||||
querySchemaModel: analysisDefinitionNodeModel.querySchemaModel,
|
||||
modals: self._modals,
|
||||
configModel: self._configModel,
|
||||
userModel: self._userModel
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.$el.append(view.render().$el);
|
||||
this.addView(view);
|
||||
},
|
||||
|
||||
_goBack: function () {
|
||||
Router.goToPreviousRoute({
|
||||
fallback: 'widgets'
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
var _ = require('underscore');
|
||||
var WidgetsFormColumnOptionsFactory = require('./widgets-form-column-options-factory');
|
||||
|
||||
var dataMap = {
|
||||
category: {
|
||||
labelTranslationKey: 'editor.widgets.widgets-form.type.category',
|
||||
iconTemplate: require('builder/editor/widgets/widget-icon-category.tpl'),
|
||||
Class: require('./schema/widgets-form-category-schema-model')
|
||||
},
|
||||
formula: {
|
||||
labelTranslationKey: 'editor.widgets.widgets-form.type.formula',
|
||||
iconTemplate: require('builder/editor/widgets/widget-icon-formula.tpl'),
|
||||
Class: require('./schema/widgets-form-formula-schema-model'),
|
||||
checkIfValid: function (querySchemaModel) {
|
||||
return querySchemaModel.columnsCollection.any(function (m) {
|
||||
return m.get('type') === 'number';
|
||||
});
|
||||
}
|
||||
},
|
||||
histogram: {
|
||||
labelTranslationKey: 'editor.widgets.widgets-form.type.histogram',
|
||||
iconTemplate: require('builder/editor/widgets/widget-icon-histogram.tpl'),
|
||||
Class: require('./schema/widgets-form-histogram-schema-model'),
|
||||
checkIfValid: function (querySchemaModel) {
|
||||
return querySchemaModel.columnsCollection.any(function (m) {
|
||||
return m.get('type') === 'number';
|
||||
});
|
||||
}
|
||||
},
|
||||
'time-series': {
|
||||
labelTranslationKey: 'editor.widgets.widgets-form.type.time_series',
|
||||
iconTemplate: require('builder/editor/widgets/widget-icon-timeSeries.tpl'),
|
||||
Class: require('./schema/widgets-form-time-series-schema-model'),
|
||||
checkIfValid: function (querySchemaModel) {
|
||||
return querySchemaModel.columnsCollection.any(function (m) {
|
||||
return m.get('type') === 'date' || m.get('type') === 'number';
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
createWidgetFormModel: function (options) {
|
||||
var widgetDefinitionModel = options.widgetDefinitionModel;
|
||||
var widgetType = widgetDefinitionModel.get('type');
|
||||
var Klass = dataMap[widgetType].Class;
|
||||
return new Klass(widgetDefinitionModel.attributes, {
|
||||
parse: true, // in case the raw attributes needs to be adapted to the expected form types, e.g. timestamp => Date
|
||||
columnOptionsFactory: new WidgetsFormColumnOptionsFactory(options.querySchemaModel),
|
||||
userModel: options.userModel,
|
||||
modals: options.modals,
|
||||
configModel: options.configModel,
|
||||
querySchemaModel: options.querySchemaModel
|
||||
});
|
||||
},
|
||||
|
||||
getDataTypes: function (querySchemaModel) {
|
||||
return _.reduce(dataMap, function (memo, val, key) {
|
||||
if (val.checkIfValid ? val.checkIfValid(querySchemaModel) : true) {
|
||||
memo.push({
|
||||
iconTemplate: val.iconTemplate,
|
||||
value: key,
|
||||
label: _t(val.labelTranslationKey)
|
||||
});
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var WidgetFormFactory = require('./widgets-form-factory');
|
||||
var template = require('./widgets-form-fields.tpl');
|
||||
require('builder/components/form-components/index');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'userActions',
|
||||
'widgetDefinitionModel',
|
||||
'querySchemaModel',
|
||||
'modals',
|
||||
'configModel',
|
||||
'userModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* View of form to edit a widget definition's data
|
||||
*
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
var widgetOptions = {
|
||||
widgetDefinitionModel: this._widgetDefinitionModel,
|
||||
querySchemaModel: this._querySchemaModel,
|
||||
modals: this._modals,
|
||||
userModel: this._userModel,
|
||||
configModel: this._configModel
|
||||
};
|
||||
|
||||
this._widgetFormModel = WidgetFormFactory.createWidgetFormModel(widgetOptions);
|
||||
this._widgetFormModel.updateSchema();
|
||||
|
||||
this._debounceSaveWidget = _.debounce(this._saveWidget.bind(this), 500);
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this._removeForm();
|
||||
this.$el.empty();
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this._widgetFormModel.bind('change:column change:aggregation changeSchema', this.render, this);
|
||||
this._widgetFormModel.bind('change', this._onFormChange, this);
|
||||
this.add_related_model(this._widgetFormModel);
|
||||
|
||||
this._widgetDefinitionModel.on('change:title', function (model, title) {
|
||||
this._widgetFormModel.set({title: title}, {silent: true}); // silent to avoid sending the form
|
||||
}, this);
|
||||
this._widgetDefinitionModel.bind('change:auto_style_definition', this._onAutoStyleChanged, this);
|
||||
this.add_related_model(this._widgetDefinitionModel);
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var model = this._widgetFormModel;
|
||||
var fields = model.getFields();
|
||||
|
||||
this._widgetFormView = new Backbone.Form({
|
||||
template: template,
|
||||
templateData: {
|
||||
dataFields: fields.data,
|
||||
styleFields: fields.style
|
||||
},
|
||||
model: model
|
||||
});
|
||||
|
||||
this._widgetFormView.bind('change', function () {
|
||||
this.commit();
|
||||
});
|
||||
|
||||
this.$el.append(this._widgetFormView.render().$el);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
validateForm: function () {
|
||||
return this._widgetFormView.validate();
|
||||
},
|
||||
|
||||
_removeForm: function () {
|
||||
// Backbone.Form removes the view with the following method
|
||||
this._widgetFormView && this._widgetFormView.remove();
|
||||
},
|
||||
|
||||
_onFormChange: function () {
|
||||
if (this._widgetFormModel.canSave()) {
|
||||
this._widgetFormModel.changeWidgetDefinitionModel(this._widgetDefinitionModel);
|
||||
this._debounceSaveWidget();
|
||||
}
|
||||
},
|
||||
|
||||
_onAutoStyleChanged: function (widgetDefModel, changedAttrs) {
|
||||
var previousAutoStyleDefinition = widgetDefModel.previous('auto_style_definition');
|
||||
if (previousAutoStyleDefinition === '' && _.isEmpty(previousAutoStyleDefinition)) {
|
||||
this._widgetFormModel.set({
|
||||
auto_style_definition: widgetDefModel.get('auto_style_definition')
|
||||
}, {
|
||||
silent: true
|
||||
});
|
||||
|
||||
if (!_.isEmpty(changedAttrs)) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_saveWidget: function () {
|
||||
this._userActions.saveWidget(this._widgetDefinitionModel);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._removeForm();
|
||||
CoreView.prototype.clean.call(this);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
<div class="Editor-HeaderInfo">
|
||||
<div class="Editor-HeaderNumeration CDB-Text is-semibold u-rSpace--m">2</div>
|
||||
|
||||
<div class="Editor-HeaderInfo-inner CDB-Text">
|
||||
<div class="Editor-HeaderInfo-title u-bSpace--m">
|
||||
<h2 class="CDB-Text CDB-HeaderInfo-titleText CDB-Size-large"><%- _t('editor.widgets.widgets-form.data.title-label') %></h2>
|
||||
</div>
|
||||
<p class="CDB-Text u-upperCase CDB-FontSize-small u-altTextColor u-bSpace--m"><%- _t('editor.widgets.widgets-form.data.description') %></p>
|
||||
|
||||
<div data-fields="<%- dataFields %>"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="CDB-HeaderInfo">
|
||||
<div class="CDB-HeaderNumeration CDB-Text is-semibold u-rSpace--m">3</div>
|
||||
|
||||
<div class="Editor-HeaderInfo-inner CDB-Text">
|
||||
<div class="Editor-HeaderInfo-title u-bSpace--m">
|
||||
<h2 class="CDB-Text CDB-HeaderInfo-TitleText CDB-Size-large"><%- _t('editor.widgets.widgets-form.style.title-label') %></h2>
|
||||
</div>
|
||||
<p class="CDB-Text u-upperCase CDB-FontSize-small u-altTextColor u-bSpace--m"><%- _t('editor.widgets.widgets-form.style.define') %></p>
|
||||
|
||||
<div data-fields="<%- styleFields %>"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
<div class="Editor-HeaderInfo">
|
||||
<div class="Editor-HeaderNumeration CDB-Text is-semibold u-rSpace--m">1</div>
|
||||
<div class="Editor-HeaderInfo-inner CDB-Text js-selector">
|
||||
<div class="Editor-HeaderInfo-title u-bSpace--m">
|
||||
<h2 class="CDB-Text CDB-HeaderInfo-titleText CDB-Size-large"><%- _t('editor.widgets.widgets-form.type.title-label') %></h2>
|
||||
</div>
|
||||
<p class="CDB-Text u-upperCase CDB-FontSize-small u-altTextColor u-bSpace--m js-highlight"><%- _t('editor.widgets.widgets-form.type.description') %></p>
|
||||
</div>
|
||||
</div>
|
||||
125
lib/assets/javascripts/builder/editor/widgets/widgets-form/widgets-form-view.js
Executable file
@@ -0,0 +1,125 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var _ = require('underscore');
|
||||
var CarouselFormView = require('builder/components/carousel-form-view');
|
||||
var CarouselCollection = require('builder/components/custom-carousel/custom-carousel-collection');
|
||||
var WidgetFormFactory = require('./widgets-form-factory');
|
||||
var WidgetsFormFieldsView = require('./widgets-form-fields-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var loadingTemplate = require('builder/editor/layers/panel-loading-template.tpl');
|
||||
|
||||
var TIME_SERIES_TYPE = 'time-series';
|
||||
var REQUIRED_OPTS = [
|
||||
'userActions',
|
||||
'widgetDefinitionModel',
|
||||
'modals',
|
||||
'querySchemaModel',
|
||||
'configModel',
|
||||
'userModel'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
this._initBinds();
|
||||
|
||||
this._shouldFetchQuery();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
|
||||
if (!this._querySchemaModel.isFetched()) {
|
||||
this.$el.html(loadingTemplate());
|
||||
} else {
|
||||
this._renderCarousel();
|
||||
this._renderForm();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._widgetDefinitionModel, 'change:type', this._renderFormAndValidate);
|
||||
this.listenTo(this._querySchemaModel, 'change:status', this.render);
|
||||
this.listenTo(this._querySchemaModel, 'change:query', this._shouldFetchQuery);
|
||||
},
|
||||
|
||||
_shouldFetchQuery: function () {
|
||||
if (this._querySchemaModel.shouldFetch()) {
|
||||
this._querySchemaModel.fetch();
|
||||
}
|
||||
},
|
||||
|
||||
_renderCarousel: function () {
|
||||
var filteredDataTypes = this._getFilteredDataTypes();
|
||||
var carouselCollection = new CarouselCollection(
|
||||
_.map(filteredDataTypes, function (type) {
|
||||
return {
|
||||
selected: this._widgetDefinitionModel.get('type') === type.value,
|
||||
val: type.value,
|
||||
label: type.label,
|
||||
template: function () {
|
||||
return (type.iconTemplate && type.iconTemplate({ makeItBig: true })) || type.value;
|
||||
}
|
||||
};
|
||||
}, this)
|
||||
);
|
||||
|
||||
carouselCollection.bind('change:selected', function (mdl) {
|
||||
if (mdl.get('selected')) {
|
||||
this._widgetDefinitionModel.changeType(mdl.getValue());
|
||||
}
|
||||
}, this);
|
||||
|
||||
var view = new CarouselFormView({
|
||||
collection: carouselCollection,
|
||||
template: require('./widgets-form-types.tpl')
|
||||
});
|
||||
this.addView(view);
|
||||
this.$el.append(view.render().el);
|
||||
},
|
||||
|
||||
_getFilteredDataTypes: function () {
|
||||
var containsTimeSeries = false;
|
||||
var modelCollection = this._widgetDefinitionModel.collection;
|
||||
if (modelCollection) {
|
||||
containsTimeSeries = modelCollection.any(function (model) {
|
||||
return model.get('type') === TIME_SERIES_TYPE;
|
||||
});
|
||||
}
|
||||
var filteredDataTypes = _.filter(WidgetFormFactory.getDataTypes(this._querySchemaModel), function (type) {
|
||||
// Do not allow to change the widget to time-series if there is already a time-series widget
|
||||
return (!containsTimeSeries ||
|
||||
type.value !== TIME_SERIES_TYPE ||
|
||||
this._widgetDefinitionModel.get('type') === TIME_SERIES_TYPE);
|
||||
}, this);
|
||||
return filteredDataTypes;
|
||||
},
|
||||
|
||||
_renderFormAndValidate: function () {
|
||||
this._renderForm();
|
||||
|
||||
if (this._formView.validateForm() === null) {
|
||||
this._userActions.saveWidget(this._widgetDefinitionModel);
|
||||
}
|
||||
},
|
||||
|
||||
_renderForm: function () {
|
||||
if (this._formView) {
|
||||
this.removeView(this._formView);
|
||||
this._formView.clean();
|
||||
}
|
||||
this._formView = new WidgetsFormFieldsView({
|
||||
userActions: this._userActions,
|
||||
widgetDefinitionModel: this._widgetDefinitionModel,
|
||||
querySchemaModel: this._querySchemaModel,
|
||||
modals: this._modals,
|
||||
userModel: this._userModel,
|
||||
configModel: this._configModel
|
||||
});
|
||||
this.addView(this._formView);
|
||||
this.$el.append(this._formView.render().el);
|
||||
}
|
||||
});
|
||||
5
lib/assets/javascripts/builder/editor/widgets/widgets-placeholder.tpl
Executable file
@@ -0,0 +1,5 @@
|
||||
<h2 class="CDB-Text CDB-Size-huge is-light u-secondaryTextColor u-bSpace--xl"><%- _t('editor.widgets.widgets-form.placeholder-text') %></h2>
|
||||
<button class="CDB-Button CDB-Button--dashed CDB-Button--wide CDB-Button-Text CDB-Text is-semibold CDB-Size-small u-upperCase u-flex u-justifyCenter u-alignCenter js-add">
|
||||
<span class="js-plus-icon u-flex u-justifyCenter u-alignCenter u-rSpace--m"></span>
|
||||
<%- _t('editor.widgets.add-widget.label') %>
|
||||
</button>
|
||||
@@ -0,0 +1,54 @@
|
||||
var deleteWidgetConfirmationTemplate = require('./delete-widget-confirmation.tpl');
|
||||
var ConfirmationView = require('builder/components/modals/confirmation/modal-confirmation-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var Router = require('builder/routes/router');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'analysisDefinitionNodesCollection',
|
||||
'editorModel',
|
||||
'layerDefinitionsCollection',
|
||||
'modals',
|
||||
'userActions',
|
||||
'widgetDefinitionsCollection'
|
||||
];
|
||||
|
||||
var service = (function () {
|
||||
return {
|
||||
init: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
},
|
||||
|
||||
removeWidget: function (widgetDefinitionModel) {
|
||||
if (widgetDefinitionModel) {
|
||||
var self = this;
|
||||
var widgetName = widgetDefinitionModel.get('title');
|
||||
|
||||
this._modalView = this._modals.create(function (modalModel) {
|
||||
return new ConfirmationView({
|
||||
modalModel: modalModel,
|
||||
template: deleteWidgetConfirmationTemplate,
|
||||
loadingTitle: _t('editor.widgets.delete.loading', { name: widgetName }),
|
||||
renderOpts: {
|
||||
name: widgetName
|
||||
},
|
||||
runAction: function () {
|
||||
modalModel.destroy();
|
||||
widgetDefinitionModel.destroy();
|
||||
self._modalView.clean();
|
||||
Router.goToWidgetList();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
editWidget: function (model) {
|
||||
this._editorModel.set('edition', false);
|
||||
this._editorModel.trigger('cancelPreviousEditions');
|
||||
|
||||
Router.goToWidget(model.get('id'));
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
module.exports = service;
|
||||
218
lib/assets/javascripts/builder/editor/widgets/widgets-view.js
Executable file
@@ -0,0 +1,218 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var EditorWidgetView = require('./widget-view');
|
||||
var IconView = require('builder/components/icon/icon-view');
|
||||
var template = require('./widgets-view.tpl');
|
||||
var widgetPlaceholderTemplate = require('./widgets-placeholder.tpl');
|
||||
var widgetsErrorTemplate = require('./widgets-content-error.tpl');
|
||||
var widgetsNotReadyTemplate = require('./widgets-content-not-ready.tpl');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var AddWidgetsView = require('builder/components/modals/add-widgets/add-widgets-view');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
|
||||
require('jquery-ui');
|
||||
|
||||
var STATES = {
|
||||
loading: 'loading',
|
||||
ready: 'ready'
|
||||
};
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'userActions',
|
||||
'analysisDefinitionNodesCollection',
|
||||
'layerDefinitionsCollection',
|
||||
'widgetDefinitionsCollection',
|
||||
'userModel',
|
||||
'stackLayoutModel',
|
||||
'configModel',
|
||||
'modals'
|
||||
];
|
||||
|
||||
/**
|
||||
* View to render widgets definitions overview
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
module: 'editor:widgets:widgets-view',
|
||||
|
||||
events: {
|
||||
'click .js-add': '_addWidget'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this.viewModel = new Backbone.Model({
|
||||
state: STATES.loading
|
||||
});
|
||||
|
||||
this._initViewState();
|
||||
this._initBinds();
|
||||
|
||||
var callback = this._onAllQueryGeometryLoaded.bind(this);
|
||||
this._layerDefinitionsCollection.loadAllQueryGeometryModels(callback);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this._destroySortable();
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViewState: function () {
|
||||
this._viewState = new Backbone.Model({
|
||||
anyGeometryData: true
|
||||
});
|
||||
|
||||
this._setViewState();
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
if (this._isLoading()) {
|
||||
this.$el.append(widgetsNotReadyTemplate());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._viewState.get('anyGeometryData')) {
|
||||
this._showNoGeometryData();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._widgetDefinitionsCollection.size() > 0) {
|
||||
this.$el.append(template);
|
||||
_.each(this._widgetDefinitionsCollection.sortBy('order'), this._addWidgetItem, this);
|
||||
this._initSortable();
|
||||
this._addTooltip();
|
||||
this._renderPlusIcon();
|
||||
return;
|
||||
}
|
||||
|
||||
this.$el.append(widgetPlaceholderTemplate());
|
||||
this._addTooltip();
|
||||
this._renderPlusIcon();
|
||||
},
|
||||
|
||||
_renderPlusIcon: function () {
|
||||
var plusIcon = new IconView({
|
||||
placeholder: this.$el.find('.js-plus-icon'),
|
||||
icon: 'plus'
|
||||
});
|
||||
plusIcon.render();
|
||||
this.addView(plusIcon);
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._widgetDefinitionsCollection, 'destroy successAdd', this.render, this);
|
||||
this.listenTo(this.viewModel, 'change:state', this.render, this);
|
||||
this.listenTo(this._viewState, 'change', this.render);
|
||||
},
|
||||
|
||||
_showNoGeometryData: function () {
|
||||
this.$el.append(
|
||||
widgetsErrorTemplate({
|
||||
body: _t('editor.widgets.no-geometry-data')
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
_onAllQueryGeometryLoaded: function () {
|
||||
this.viewModel.set('state', STATES.ready);
|
||||
},
|
||||
|
||||
_addTooltip: function () {
|
||||
var tooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-add'),
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return _t('editor.widgets.add-widget.tooltip');
|
||||
},
|
||||
offset: 8
|
||||
});
|
||||
|
||||
this.addView(tooltip);
|
||||
},
|
||||
|
||||
_isLoading: function () {
|
||||
return this.viewModel.get('state') === STATES.loading;
|
||||
},
|
||||
|
||||
_isReady: function () {
|
||||
return this.viewModel.get('state') === STATES.ready;
|
||||
},
|
||||
|
||||
_setViewState: function () {
|
||||
this._layerDefinitionsCollection.isThereAnyGeometryData()
|
||||
.then(function (anyGeometry) {
|
||||
this._viewState.set('anyGeometryData', anyGeometry);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_initSortable: function () {
|
||||
this.$('.js-widgets').sortable({
|
||||
axis: 'y',
|
||||
items: '> li.BlockList-item',
|
||||
opacity: 0.8,
|
||||
update: this._onSortableFinish.bind(this),
|
||||
forcePlaceholderSize: false
|
||||
}).disableSelection();
|
||||
},
|
||||
|
||||
_destroySortable: function () {
|
||||
if (this.$('.js-widgets').data('ui-sortable')) {
|
||||
this.$('.js-widgets').sortable('destroy');
|
||||
}
|
||||
},
|
||||
|
||||
_onSortableFinish: function () {
|
||||
var self = this;
|
||||
this.$('.js-widgets > .js-widgetItem').each(function (index, item) {
|
||||
var modelCid = $(item).data('model-cid');
|
||||
var widgetDefModel = self._widgetDefinitionsCollection.get(modelCid);
|
||||
widgetDefModel.set('order', index);
|
||||
self._userActions.saveWidget(widgetDefModel);
|
||||
});
|
||||
},
|
||||
|
||||
_addWidget: function () {
|
||||
if (this.$('.js-add').hasClass('is-disabled')) return;
|
||||
|
||||
var self = this;
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new AddWidgetsView({
|
||||
modalModel: modalModel,
|
||||
userModel: self._userModel,
|
||||
userActions: self._userActions,
|
||||
configModel: self._configModel,
|
||||
analysisDefinitionNodesCollection: self._analysisDefinitionNodesCollection,
|
||||
layerDefinitionsCollection: self._layerDefinitionsCollection,
|
||||
widgetDefinitionsCollection: self._widgetDefinitionsCollection
|
||||
});
|
||||
}, {
|
||||
breadcrumbsEnabled: true
|
||||
});
|
||||
},
|
||||
|
||||
_addWidgetItem: function (model) {
|
||||
var view = new EditorWidgetView({
|
||||
model: model,
|
||||
layer: this._layerDefinitionsCollection.get(model.get('layer_id')),
|
||||
modals: this._modals,
|
||||
userActions: this._userActions,
|
||||
stackLayoutModel: this._stackLayoutModel
|
||||
});
|
||||
this.addView(view);
|
||||
this.$('.js-widgets').append(view.render().el);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._destroySortable();
|
||||
CoreView.prototype.clean.apply(this);
|
||||
}
|
||||
});
|
||||
5
lib/assets/javascripts/builder/editor/widgets/widgets-view.tpl
Executable file
@@ -0,0 +1,5 @@
|
||||
<button class="CDB-Button CDB-Button--dashed CDB-Button--wide CDB-Button-Text CDB-Text is-semibold CDB-Size-small u-upperCase u-bSpace--m u-flex u-justifyCenter u-alignCenter js-add">
|
||||
<span class="js-plus-icon u-flex u-justifyCenter u-alignCenter u-rSpace--m"></span>
|
||||
<%- _t('editor.widgets.add-widget.label') %>
|
||||
</button>
|
||||
<ul class="BlockList js-widgets"></ul>
|
||||