Initial commit

This commit is contained in:
zhongjin
2020-06-15 10:58:47 +08:00
commit 4f1dfe7564
8590 changed files with 1516878 additions and 0 deletions
@@ -0,0 +1,3 @@
<div class="CDB-ListDecoration-itemLink u-flex u-justifySpace u-alignCenter u-actionTextColor <% if (isSelected) { %> is-selected <% } %>">
<%- _t('form-components.editors.fill.quantification.methods.' + name) %>
</div>
@@ -0,0 +1,63 @@
var CoreView = require('backbone/core-view');
var CustomListView = require('builder/components/custom-list/custom-view');
var CustomListCollection = require('builder/components/custom-list/custom-list-collection');
var ColumnListViewTemplate = require('builder/components/custom-list/column-list/column-list-view.tpl');
module.exports = CoreView.extend({
defaults: {
headerTitle: ''
},
events: {
'click .js-back': '_onClickBack'
},
initialize: function (opts) {
if (!opts.stackLayoutModel) throw new Error('stackLayoutModel is required');
if (!opts.columns) throw new Error('columns param is required');
this._stackLayoutModel = opts.stackLayoutModel;
this._columns = opts.columns;
this._showSearch = opts.showSearch || false;
this._typeLabel = opts.typeLabel;
this._itemTemplate = opts.itemTemplate;
this.collection = new CustomListCollection(this._columns);
this.listenTo(this.collection, 'change:selected', this._onSelectItem);
},
render: function () {
this.clearSubViews();
this.$el.empty();
this.$el.append(
ColumnListViewTemplate({
headerTitle: this.options.headerTitle
})
);
this._initViews();
return this;
},
_onClickBack: function (e) {
this.killEvent(e);
this.trigger('back', this);
},
_initViews: function () {
this._listView = new CustomListView({
collection: this.collection,
showSearch: this._showSearch,
typeLabel: this._typeLabel,
itemTemplate: this._itemTemplate
});
this.$('.js-content').append(this._listView.render().$el);
this.addView(this._listView);
},
_onSelectItem: function (item) {
this.trigger('selectItem', item, this);
}
});
@@ -0,0 +1,13 @@
<% if (headerTitle) { %>
<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,32 @@
var CoreView = require('backbone/core-view');
var template = require('./custom-list-action.tpl');
module.exports = CoreView.extend({
className: 'CDB-Text CDB-Size-small is-semibold u-upperCase u-actionTextColor u-lSpace--xl',
tagName: 'button',
events: {
'click': '_onClick'
},
initialize: function (opts) {
this.options = opts;
},
render: function () {
this.$el.empty();
this.clearSubViews();
this.$el.append(template({
label: this.options.label
}));
return this;
},
_onClick: function () {
this.options.action && this.options.action();
}
});
@@ -0,0 +1 @@
<%- label %>
@@ -0,0 +1,6 @@
<div class="CDB-ListDecoration-item CustomList-item js-listItem">
<button type="button" class="CDB-ListDecoration-itemLink CustomList--full js-add-custom-value u-ellipsis" data-val="<%- query %>" title="<%- query %>">
<p class="CDB-Text CDB-FontSize-small u-altTextColor"><%- _t('components.custom-list.add-custom-result') %></p>
“<%- query %>”
</button>
</div>
@@ -0,0 +1,122 @@
var Backbone = require('backbone');
var CustomListItemModel = require('./custom-list-item-model');
var _ = require('underscore');
/*
* Custom list collection, it parses pairs like:
*
* [{ val, label }]
* ["string"]
*/
module.exports = Backbone.Collection.extend({
sort_key: 'id', // default sort key
initialize: function (models, options) {
this.options = _.extend({ silent: true }, options);
this._initBinds();
},
comparator: function (item) {
var name = item.get(this.sort_key);
return this._nameToLowerCase(name);
},
model: function (attrs, opts) {
var d = {};
if (typeof attrs === 'string') {
d.val = attrs;
} else {
d = attrs;
}
return new CustomListItemModel(d, opts);
},
sortByKey: function (key) {
this.sort_key = key;
this.sort();
},
_initBinds: function () {
this.bind('change:selected', this._onSelectedChange, this);
},
_nameToLowerCase: function (name) {
/*
* It could still be evaluated like true if it is a boolean/number
* in that case, we convert it to a string
*/
if (_.isUndefined(name) || _.isNull(name)) return name;
return _.isString(name) ? name.toLowerCase() : name.toString().toLowerCase();
},
search: function (query) {
if (!query) return this;
query = query.toLowerCase();
return _(this.filter(function (model) {
var name = model.getName();
var val = this._nameToLowerCase(name);
return val ? ~val.indexOf(query) : -1;
}, this));
},
_onSelectedChange: function (changedModel, isSelected) {
if (isSelected) {
this.each(function (m) {
if (m.cid !== changedModel.cid) {
m.set({
selected: false
}, {
silent: this.options.silent
});
}
}, this);
}
},
getSelectedItem: function () {
return this.findWhere({ selected: true });
},
containsValue: function (value) {
return this.find(function (mdl) {
return mdl.getValue() === value;
});
},
setSelected: function (value) {
var selectedModel;
var silent = { silent: this.options.silent };
this.each(function (mdl) {
if (mdl.getValue() === value) {
mdl.set({
selected: true
}, silent);
selectedModel = mdl;
} else {
mdl.set({
selected: false
}, silent);
}
});
return selectedModel;
},
removeSelected: function () {
this.each(function (mdl) {
mdl.set({
selected: false
});
});
},
isAsync: function () {
return false;
}
});
@@ -0,0 +1,9 @@
<div class="CustomList-message">
<p class="CustomList-messageText CDB-Text CDB-Size-medium u-secondaryTextColor">
<% if (query && query.length) { %>
<%- _t('components.custom-list.no-results', { typeLabel: typeLabel, query: query }) %>
<% } else { %>
<%- _t('components.custom-list.no-items', { typeLabel: typeLabel }) %>
<% } %>
</p>
</div>
@@ -0,0 +1,5 @@
<div class="CDB-Box-modalHeader">
<div class="CDB-Box-modalHeaderItem js-header u-alignCenter u-justifySpace">
<div class="u-flex js-actions"></div>
</div>
</div>
@@ -0,0 +1,22 @@
var Backbone = require('backbone');
/*
* List item model
*
*/
module.exports = Backbone.Model.extend({
defaults: {
selected: false
},
getName: function () {
return (this.get('label') == null) ? this.getValue() : this.get('label'); // eslint-disable-line
},
getValue: function () {
return this.get('val');
}
});
@@ -0,0 +1,66 @@
var _ = require('underscore');
var CoreView = require('backbone/core-view');
module.exports = CoreView.extend({
options: {
template: require('./custom-list-item.tpl')
},
className: 'CDB-ListDecoration-item CustomList-item js-listItem',
tagName: 'li',
events: {
'mouseenter': '_onMouseEnter',
'mouseleave': '_onMouseLeave',
'click': '_onClick'
},
initialize: function (opts) {
this.options = _.extend({}, this.options, opts);
this.model.on('change', this.render, this);
},
render: function () {
this.$el.empty();
this.clearSubViews();
var name = this.model.getName() == null ? 'null' : this.model.getName();
this.$el.append(
this.options.template(
_.extend({
typeLabel: this.options.typeLabel,
isSelected: this.model.get('selected'),
isDisabled: this.model.get('disabled'),
isDestructive: this.model.get('destructive'),
name: name,
val: this.model.getValue(),
options: this.model.get('renderOptions')
})
)
);
this.$el
.attr('data-val', this.model.getValue())
.toggleClass('is-disabled', !!this.model.get('disabled'));
return this;
},
_onMouseLeave: function () {
this.$el.removeClass('is-highlighted');
},
_onMouseEnter: function () {
this.$el.addClass('is-highlighted');
},
_onClick: function (ev) {
this.killEvent(ev);
this.model.set({
selectedClass: ev.target.classList,
selected: true
});
}
});
@@ -0,0 +1,17 @@
<button type="button" class="CDB-ListDecoration-itemLink u-ellipsis
<% if (isSelected) { %> is-selected <% } %>
<% if (isDestructive) { %>
u-errorTextColor
<% } else if (isDisabled) { %>
u-hintTextColor
<% } else { %>
u-actionTextColor
<% } %>
" title="<%- name %>">
<div class="u-iBlock u-rSpace">
<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>
<%- name %>
</button>
@@ -0,0 +1,12 @@
<button type="button" class="CDB-ListDecoration-itemLink u-ellipsis
<% if (isSelected) { %> is-selected <% } %>
<% if (isDestructive) { %>
u-errorTextColor
<% } else if (isDisabled) { %>
u-hintTextColor
<% } else { %>
u-actionTextColor
<% } %>
" title="<%- name %>">
<%- name %>
</button>
@@ -0,0 +1,40 @@
var _ = require('underscore');
var CustomListCollection = require('./custom-list-collection');
module.exports = CustomListCollection.extend({
_initBinds: function () { },
setSelected: function (value) {
var selectedModel;
var silentTrue = { silent: true };
if (_.isArray(value)) {
this.each(function (mdl) {
if (_.contains(value, mdl.getValue())) {
mdl.set({
selected: true
}, silentTrue);
selectedModel = mdl;
} else {
mdl.set({
selected: false
}, silentTrue);
}
});
} else {
this.each(function (mdl) {
if (mdl.getValue() === value) {
mdl.set({
selected: true
}, silentTrue);
selectedModel = mdl;
} else {
mdl.set({
selected: false
}, silentTrue);
}
});
}
return selectedModel;
}
});
@@ -0,0 +1,8 @@
var CustomListItemView = require('./custom-list-item-view');
module.exports = CustomListItemView.extend({
_onClick: function (ev) {
this.killEvent(ev);
this.model.set('selected', !this.model.get('selected'));
}
});
@@ -0,0 +1,62 @@
var CoreView = require('backbone/core-view');
var template = require('./custom-list-search.tpl');
module.exports = CoreView.extend({
className: 'CustomList--full',
tagName: 'form',
events: {
'keyup .js-search': '_onSearchType',
'click .js-clear': '_onClickClear',
'submit': '_submit'
},
initialize: function () {
this.template = this.options.template || template;
this.searchPlaceholder = this.options.searchPlaceholder || _t('components.custom-list.placeholder', { typeLabel: this.options.typeLabel });
this._initBinds();
},
render: function () {
this.$el
.empty()
.append(
this.template({
query: this.model.get('query'),
searchPlaceholder: this.searchPlaceholder
})
);
return this;
},
_initBinds: function () {
this.model.on('change:query', this._checkButtons, this);
},
_checkButtons: function () {
var query = this.model.get('query');
this.$('.js-clear').toggleClass('u-transparent', query.length === 0);
},
_onSearchType: function (e) {
this._submit();
},
_onClickClear: function (e) {
e.stopPropagation();
this.model.set('query', '');
this.render();
this.focus();
},
focus: function () {
this.$('.js-search').focus();
},
_submit: function (ev) {
this.killEvent(ev);
var query = this.$('.js-search').val();
this.model.set('query', query);
}
});
@@ -0,0 +1,8 @@
<div class="u-flex">
<input type="text" name="text" autocomplete="off" value="<%- query %>" placeholder="<%- searchPlaceholder %>" class="CDB-InputTextPlain CDB-Text js-search">
<button type="button" class="u-lSpace--xl u-transparent js-clear">
<div class="CDB-Shape">
<div class="CDB-Shape-close is-blue is-large"></div>
</div>
</button>
</div>
@@ -0,0 +1,234 @@
var CoreView = require('backbone/core-view');
var _ = require('underscore');
var Ps = require('perfect-scrollbar');
var emptyTemplate = require('./custom-list-empty.tpl');
var addTemplate = require('./custom-list-add.tpl');
var template = require('./custom-list.tpl');
var Utils = require('builder/helpers/utils');
var ARROW_DOWN_KEY_CODE = 40;
var ARROW_UP_KEY_CODE = 38;
var ENTER_KEY_CODE = 13;
module.exports = CoreView.extend({
module: 'components:custom-list:custom-list-view',
options: {
size: 3
},
className: 'CDB-Text CDB-Size-medium CustomList-listWrapper',
tagName: 'div',
events: {
'click .js-add-custom-value': '_onClickAddCustomValue',
'mouseover': '_onMouseOver',
'mouseout': '_onMouseOut'
},
initialize: function (opts) {
this.options = _.extend({}, this.options, opts);
this._onKeyDownBinded = this._onKeyDown.bind(this);
this._needsMaxSize = true;
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._removeArrowBinds();
this._destroyCustomScroll();
this.$el.empty();
var query = this.model.get('query');
var items = this.collection.search(query);
this.$el.append(template());
var allowFreeTextInput = this.options.allowFreeTextInput;
if (allowFreeTextInput && query && !Utils.isBlank(query)) {
if (!this.collection.containsValue(query)) {
this.$el.prepend(
addTemplate({
query: query,
typeLabel: this.options.typeLabel
})
);
}
}
if (items.size() > 0) {
items.each(this._renderItem, this);
this._applyArrowBinds();
// Perfect-scroll needs to have the element in the DOM in order to
// style/positionate the scroll properly, small trick
setTimeout(this._applyCustomScroll.bind(this), 0);
} else if (!allowFreeTextInput || Utils.isBlank(query)) {
this.$el.append(
emptyTemplate({
query: query,
typeLabel: this.options.typeLabel
})
);
}
return this;
},
_renderItem: function (model) {
if (model.get('hidden')) return;
var ItemViewClass = this.options.itemView;
var itemView = new ItemViewClass({
model: model,
typeLabel: this.options.typeLabel,
template: this.options.itemTemplate
});
this.$('.js-list').append(itemView.render().el);
this.addView(itemView);
itemView.bind('customEvent', function (eventName, item) {
this.trigger('customEvent', eventName, item, this);
}, this);
},
_initBinds: function () {
this.model.bind('change:query', this._onQueryChanged, this);
},
_applyArrowBinds: function () {
document.addEventListener('keydown', this._onKeyDownBinded);
},
_removeArrowBinds: function () {
document.removeEventListener('keydown', this._onKeyDownBinded);
},
_getSelected: function () {
var selectedModel = this.collection.getSelectedItem();
var selectedValue;
if (selectedModel) {
selectedValue = selectedModel.getValue();
return this.$("[data-val='" + selectedValue + "']");
}
return null;
},
_highlightSelected: function (item) {
var itemHeight = item.outerHeight();
item.addClass('is-highlighted');
this.$('.js-list').scrollTop(item.index() * itemHeight);
},
_onClickAddCustomValue: function (event) {
this.killEvent(event);
var query = this.model.get('query');
var model = this.collection.add({
val: query,
label: '“' + query + '”',
dirty: true
});
this.collection.sortByKey('val');
model.set('selected', true);
},
_onKeyDown: function (event) {
if (this.model.get('visible') === false) {
return;
}
var key = event.which;
var $listItems = this.$('.js-listItem');
var $highlighted = $listItems.filter('.is-highlighted');
var $current;
var model;
$highlighted.removeClass('is-highlighted');
if (key === ARROW_DOWN_KEY_CODE) {
if (!$highlighted.length || $highlighted[0] === $listItems.last()[0]) {
$current = $listItems.eq(0);
} else {
$current = $highlighted.next();
}
} else if (key === ARROW_UP_KEY_CODE) {
if (!$highlighted.length || $highlighted[0] === $listItems.first()[0]) {
$current = $listItems.last();
} else {
$current = $highlighted.prev();
}
} else if (key === ENTER_KEY_CODE) {
event.preventDefault();
if ($highlighted && $highlighted.length) {
model = _.first(this.collection.where({ val: $highlighted.data('val') }));
if (model) {
model.set('selected', true);
}
return false;
}
}
$current && this._highlightSelected($current);
},
_onQueryChanged: function () {
var prevQuery = this.collection.findWhere({ val: this.model.previous('query'), dirty: true });
this.collection.remove(prevQuery);
this.render();
},
_applyCustomScroll: function () {
Ps.initialize(this._wrapperContainer().get(0), {
wheelSpeed: 2,
wheelPropagation: true,
stopPropagationOnClick: false,
minScrollbarLength: 20
});
},
_destroyCustomScroll: function () {
if (this._wrapperContainer().length > 0) {
Ps.destroy(this._wrapperContainer().get(0));
}
},
_wrapperContainer: function () {
return this.$('.js-list');
},
clean: function () {
this._removeArrowBinds();
this._destroyCustomScroll();
CoreView.prototype.clean.apply(this);
},
highlight: function () {
var selected = this._getSelected();
selected && this._highlightSelected(selected);
},
_onMouseOver: function () {
this._mouseOverAction && this._mouseOverAction();
},
_onMouseOut: function () {
this._mouseOutAction && this._mouseOutAction();
}
});
@@ -0,0 +1 @@
<ul class="CustomList-list js-list"></ul>
@@ -0,0 +1,221 @@
var _ = require('underscore');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var CustomListCollection = require('./custom-list-collection');
var SearchView = require('./custom-list-search-view');
var CustomListView = require('./custom-list-view');
var headerTemplate = require('./custom-list-header.tpl');
var CustomListAction = require('./custom-list-action-view');
var itemTemplate = require('./custom-list-item.tpl');
var CustomListItemView = require('./custom-list-item-view');
/*
* A custom list with possibility to search within values.
*
* It accepts a collection of (val, label) model attributes or a values array
* with the same content or only strings.
*
* new CustomList({
* showSearch: false,
* itemTemplate: itemTemplate,
* values: [
* {
* val: 'hello',
* label: 'hi'
* }
* ]
* });
*/
module.exports = CoreView.extend({
module: 'components:custom-list:custom-view',
options: {
showSearch: true,
allowFreeTextInput: false,
typeLabel: 'column',
itemTemplate: itemTemplate,
itemView: CustomListItemView
},
className: 'CDB-Box-modal CustomList',
tagName: 'div',
events: {
'mouseover': '_onMouseOver',
'mouseout': '_onMouseOut'
},
initialize: function (opts) {
if (!opts.collection) {
if (!opts.options) { throw new Error('options array {value, label} is required'); }
this.collection = new CustomListCollection(opts.options);
}
if (opts.position) {
this.$el.css(opts.position);
}
this.options = _.extend({}, this.options, opts);
this._selectModel = this.options.selectModel;
if (this.options.mouseOverAction) {
this._mouseOverAction = this.options.mouseOverAction;
}
if (this.options.mouseOutAction) {
this._mouseOutAction = this.options.mouseOutAction;
}
this.model = new Backbone.Model({
query: '',
visible: false
});
this._initBinds();
},
render: function () {
this.$el.empty();
this.clearSubViews();
if (this.options.showSearch || this.options.actions) {
this._renderHeader();
}
if (this.options.showSearch) {
this._renderSearch();
}
if (this.options.actions) {
this._renderActions();
}
this._renderList();
if (this.options.showSearch) {
this._focusSearch();
}
return this;
},
_initBinds: function () {
this.model.bind('change:visible', this._onVisibleChanged, this);
this.model.bind('change:query', this._setActionsVisibility, this);
},
_renderHeader: function () {
this.$el.prepend(headerTemplate());
},
_renderSearch: function () {
this._searchView = new SearchView({
template: this.options.searchTemplate,
typeLabel: this.options.typeLabel,
searchPlaceholder: this.options.searchPlaceholder,
model: this.model
});
this.$('.js-header').prepend(this._searchView.render().el);
this.addView(this._searchView);
},
_renderActions: function () {
_.each(this.options.actions, function (action) {
var view = new CustomListAction(action);
this.$('.js-actions').append(view.render().el);
this.addView(view);
}, this);
},
_setActionsVisibility: function () {
this.$('.js-actions').toggleClass('u-hide', this.model.get('query') !== '');
},
_focusSearch: function () {
setTimeout(function () {
if (this._searchView) {
this._searchView.focus();
var input = this._searchView.$('input');
var $initialVal = input.val();
input.val($initialVal + ' ');
input.val($initialVal);
}
}.bind(this), 0);
},
_renderList: function () {
this._listView = new CustomListView({
model: this.model,
allowFreeTextInput: this.options.allowFreeTextInput,
collection: this.collection,
typeLabel: this.options.typeLabel,
itemView: this.options.itemView,
itemTemplate: this.options.itemTemplate,
size: this.options.size,
mouseOverAction: this._mouseOverAction,
mouseOutAction: this._mouseOutAction
});
this.$el.append(this._listView.render().el);
this._listView.highlight();
this.addView(this._listView);
this._listView.bind('customEvent', function (eventName, item) {
this.trigger(eventName, item, this);
}, this);
},
highlight: function () {
this._listView.highlight();
},
_onVisibleChanged: function (_model, isVisible) {
this._resetQuery();
this._toggleVisibility();
isVisible ? this.render() : this.clearSubViews();
},
_resetQuery: function () {
var query = this._selectModel && this._selectModel.get(this.options.typeLabel) || '';
this.model.set('query', query);
var isInCollection = this.collection.findWhere({ val: query });
if (query && !isInCollection) {
this.collection.add({ label: query, val: query });
}
},
show: function () {
this.model.set('visible', true);
},
hide: function () {
this.trigger('hidden', this);
this.model.set('visible', false);
},
toggle: function () {
this.model.set('visible', !this.model.get('visible'));
},
_toggleVisibility: function () {
this.$el.toggleClass('is-visible', !!this.model.get('visible'));
},
isVisible: function () {
return this.model.get('visible');
},
_onMouseOver: function () {
this._mouseOverAction && this._mouseOverAction();
},
_onMouseOut: function () {
this._mouseOutAction && this._mouseOutAction();
},
remove: function () {
this._listView && this._listView.clean();
CoreView.prototype.remove.call(this);
}
});