UNPKG

comindware.ui

Version:

Comindware Core UI provides the basic components like editors, lists, dropdowns, popups that we so desperately need while creating Marionette-based single-page applications.

256 lines (210 loc) 8.87 kB
/** * Developer: Oleg Verevkin * Date: 10/21/2015 * Copyright: 2009-2016 Comindware® * All Rights Reserved * Published under the MIT license */ 'use strict'; import { Handlebars } from 'lib'; import dropdown from 'dropdown'; import template from './templates/multiSelectEditor.hbs'; import BaseLayoutEditorView from './base/BaseLayoutEditorView'; import MultiSelectPanelView from './impl/multiSelect/views/MultiSelectPanelView'; import MultiSelectButtonView from './impl/multiSelect/views/MultiSelectButtonView'; import formRepository from '../formRepository'; const defaultOptions = { collection: null, displayAttribute: 'text', allowEmptyValue: true, explicitApply: false }; /** * @name MultiSelectEditorView * @memberof module:core.form.editors * @class Выпадающая панель с возможность выбора нескольких элементов. Поддерживаемый тип данных: массив объектов, <code>Object[]</code>. * Тип объекта в массиве должен совпадать с типом данных поля <code>id</code> элементов коллекции <code>collection</code>. * @extends module:core.form.editors.base.BaseEditorView * @param {Object} options Options object. All the properties of {@link module:core.form.editors.base.BaseEditorView BaseEditorView} class are also supported. * @param {Boolean} [options.allowEmptyValue=true] Разрешить значение <code>null</code>. * @param {Backbone.Collection|Array} options.collection Массив объектов <code>{ id, text }</code> или * Backbone коллекция моделей с такими атрибутами. Используйте свойство <code>displayAttribute</code> для отображения * текста из поля, отличного от <code>text</code>. В случае передачи Backbone.Collection, дальнейшее ее изменение * отражается в выпадающем списке. * @param {String} [options.displayAttribute='text'] Имя атрибута, используемого для отображения текста. * @param {Boolean} [options.explicitApply=false] Для изменения значения требуется явно нажать кнопку Apply в выпадающей панели. * */ formRepository.editors.MultiSelect = BaseLayoutEditorView.extend(/** @lends module:core.form.editors.MultiSelectEditorView.prototype */{ initialize(options) { if (options.schema) { _.extend(this.options, defaultOptions, _.pick(options.schema, _.keys(defaultOptions))); } else { _.extend(this.options, defaultOptions, _.pick(options || {}, _.keys(defaultOptions))); } if (_.isArray(this.options.collection)) { this.options.collection = new Backbone.Collection(this.options.collection); } this.collection = this.options.collection; const collectionChangeHandler = _.throttle(this.__onCollectionChange, 100, { leading: false }); this.listenTo(this.collection, 'add', collectionChangeHandler); this.listenTo(this.collection, 'remove', collectionChangeHandler); this.listenTo(this.collection, 'reset', collectionChangeHandler); this.listenTo(this.collection, 'select', this.__onSelect); this.listenTo(this.collection, 'deselect', this.__onDeselect); this.viewModel = new Backbone.Model({ button: new Backbone.Model({ collection: this.collection, value: this.__findModels(this.getValue()) }), panel: new Backbone.Model({ collection: this.collection }) }); }, regions: { dropdownRegion: '.js-dropdown-region' }, className: 'multiselect-wrp', template: Handlebars.compile(template), onRender() { this.dropdownView = dropdown.factory.createDropdown({ buttonView: MultiSelectButtonView, buttonViewOptions: { model: this.viewModel.get('button'), displayAttribute: this.options.displayAttribute }, panelView: MultiSelectPanelView, panelViewOptions: { model: this.viewModel.get('panel'), displayAttribute: this.options.displayAttribute, explicitApply: this.options.explicitApply }, autoOpen: false }); this.listenTo(this.dropdownView, 'close', this.onPanelClose); this.listenTo(this.dropdownView, 'open', this.onPanelOpen); this.listenTo(this.dropdownView, 'button:open:panel', this.onPanelOpenRequest); this.listenTo(this.dropdownView, 'button:value:set', this.__onValueSet); this.listenTo(this.dropdownView, 'panel:select:all', this.__selectAll); this.listenTo(this.dropdownView, 'panel:apply', this.__applyValue); this.listenTo(this.dropdownView, 'panel:close', () => this.dropdownView.close()); this.dropdownRegion.show(this.dropdownView); }, __onCollectionChange() { this.__trimValues(); this.__triggerChange(); this.viewModel.get('button').set('value', this.__findModels(this.getValue())); this.__resetValue(); }, __onValueSet(values) { this.__value(values, true); }, __onSelect(model) { this.__select(model); }, __onDeselect(model) { this.__deselect(model); if (!this.options.allowEmptyValue) { const valueModels = this.__findModels(this.tempValue) || null; if (!(valueModels && valueModels.length)) { model.trigger('select', model); } } }, __select(model) { model.selected = true; this.__setValue(model.id); }, __deselect(model) { model.selected = false; this.__unsetValue(model.id); }, __selectAll() { this.collection.each(model => { model.trigger('select', model); }); }, __deselectAll() { this.collection.each(model => { model.trigger('deselect', model); }); }, __trimValues() { let values = this.getValue(); if (values === null) { return; } _.chain(values) .reject(value => this.collection.get(value)) .each(rejectedValue => { values = _.without(values, rejectedValue); }); if (values.length) { this.setValue(values); } else { this.setValue(null); } }, __findModels(values) { return this.collection ? this.collection.filter(model => _.contains(values, model.id)) : null; }, __setValue(value) { if (_.contains(this.tempValue, value)) { return; } this.tempValue = this.tempValue.concat(value); }, __unsetValue(value) { if (!_.contains(this.tempValue, value)) { return; } this.tempValue = _.without(this.tempValue, value); }, __value(value, updateUi) { this.tempValue = value; this.__applyValue(); if (updateUi) { this.viewModel.get('button').set('value', this.__findModels(this.getValue())); } }, __applyValue() { this.setValue(this.tempValue); this.__trimValues(); this.__triggerChange(); this.dropdownView.close(); }, __resetValue() { const value = this.getValue(); this.tempValue = value === null ? [] : value; this.collection.each(model => { delete model.selected; }); const valueModels = this.__findModels(this.tempValue) || null; _.each(valueModels, valueModel => { valueModel.trigger('select', valueModel); }); }, onPanelOpenRequest() { if (this.getEnabled() && !this.getReadonly()) { this.dropdownView.open(); this.__resetValue(); } }, onPanelOpen() { this.onFocus(); }, onPanelClose() { if (!this.options.explicitApply) { this.__applyValue(); } this.viewModel.get('button').set('value', this.__findModels(this.getValue())); this.onBlur(); }, focus() { this.onPanelOpenRequest(); }, blur() { this.dropdownView.close(); } }); export default formRepository.editors.MultiSelect;