UNPKG

comindware.core.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.

300 lines (245 loc) • 10.3 kB
// @flow import template from './templates/contextSelectEditor.html'; import PopoutButtonView from './impl/context/views/PopoutButtonView'; import PopoutWrapperView from './impl/context/views/PopoutWrapperView'; import formRepository from '../formRepository'; import BaseEditorView from './base/BaseEditorView'; import dropdownFactory from '../../dropdown/factory'; import { objectPropertyTypes } from '../../Meta'; const defaultOptions = () => ({ recordTypeId: undefined, emptyPlaceholder: Localizer.get('CORE.COMMON.NOTSET'), context: undefined, contextModel: undefined, propertyTypes: undefined, usePropertyTypes: true, allowBlank: false, instanceRecordTypeId: undefined, isInstanceExpandable: true, instanceTypeProperties: [objectPropertyTypes.INSTANCE], instanceValueProperty: 'instanceTypeId' }); export default formRepository.editors.ContextSelect = BaseEditorView.extend({ initialize(options = {}) { this.__applyOptions(options, defaultOptions); if (this.options.contextModel) { this.listenTo(this.options.contextModel, 'change:recordTypeId', (model, value) => this.__onRecordTypeChange(value)); this.listenTo(this.options.contextModel, 'change:items', (model, value) => this.__onContextChange(value)); this.recordTypeId = this.options.contextModel.get('recordTypeId'); this.context = this.options.contextModel.get('items'); } else { this.recordTypeId = this.options.recordTypeId; this.context = this.options.context; } const model = new Backbone.Model({ instanceTypeId: this.recordTypeId, propertyTypes: this.options.propertyTypes, usePropertyTypes: this.options.usePropertyTypes, instanceRecordTypeId: this.options.instanceRecordTypeId, allowBlank: this.options.allowBlank }); this.viewModel = new Backbone.Model({ button: new Backbone.Model({ placeholder: this.__placeholderShouldBe() }), panel: model }); this.__updateDisplayValue(); }, ui: { clearButton: '.js-clear-button' }, regions: { contextPopoutRegion: '.js-context-popout-region' }, template: Handlebars.compile(template), className: 'editor context_select js-dropdown__root', events: { 'click @ui.clearButton': '__onClearClickHandler', 'dblclick @ui.clearButton': '__onClearDblclick' }, onRender() { if (!this.enabled) { this.contextPopoutRegion.show( new PopoutButtonView({ model: this.viewModel.get('button') }) ); return; } this.popoutView = dropdownFactory.createDropdown({ panelView: PopoutWrapperView, panelViewOptions: { maxWidth: 390, model: this.viewModel.get('panel') }, buttonView: PopoutButtonView, buttonViewOptions: { model: this.viewModel.get('button') }, autoOpen: false }); this.showChildView('contextPopoutRegion', this.popoutView); this.listenTo(this.popoutView, 'panel:context:selected', this.__applyContext); this.listenTo(this.popoutView, 'before:open', this.__onBeforeOpen); this.listenTo(this.popoutView, 'click', this.__onButtonClick); }, setPermissions(enabled, readonly) { BaseEditorView.prototype.setPermissions.call(this, enabled, readonly); this.viewModel.get('button').set('placeholder', this.__placeholderShouldBe()); }, setValue(value) { this.__value(value, false); }, updateContext(recordTypeId, context) { const panelModel = this.viewModel.get('panel'); this.context = context; panelModel.set('context', this.__createTreeCollection(this.context, recordTypeId)); panelModel.set('instanceTypeId', recordTypeId); this.recordTypeId = recordTypeId; if (this.__isInstanceInContext(this.value)) { this.__updateDisplayValue(); } else { this.setValue(); } this.render(); }, onFocus() { BaseEditorView.prototype.onFocus.apply(this, arguments); if (this.getReadonly()) { return; } this.popoutView.open(); }, __value(value, triggerChange, newValue) { this.value = newValue || value; if (triggerChange) { this.__triggerChange(); } this.__updateDisplayValue(); }, __getButtonText(value) { if (!value || value === 'False') { return ''; } if (typeof value === 'string') { return value; } if (!Array.isArray(value)) { return ''; } let instanceTypeId = this.recordTypeId; let text = ''; value.forEach((item, index) => { const searchItem = this.context[instanceTypeId]?.find(contextItem => contextItem.id === item || contextItem.alias === item); if (searchItem) { text += index ? ` - ${searchItem.name}` : searchItem.name; instanceTypeId = searchItem[this.options.instanceValueProperty]; } else { text += this.__getButtonText(item); } }); return text; }, __onButtonClick() { if (this.getReadonly()) { return; } this.popoutView.trigger('toggle'); }, __isInstanceInContext(item) { if (!item || item === 'False') return false; return Object.values(this.context).find(value => value.find(context => context.id === item[item.length - 1])); }, __applyContext(selected) { const propertyTypes = this.options.propertyTypes; if (this.options.usePropertyTypes && propertyTypes && propertyTypes.length && !propertyTypes.includes(selected.get('type'))) { return; } const newValue = this.__collectPropertyPath(selected); this.popoutView.close(); this.__value(selected.id, true, newValue); }, __onRecordTypeChange(newData) { this.recordTypeId = newData; }, __onContextChange(newData) { this.context = newData; this.updateContext(this.recordTypeId, newData); }, __onBeforeOpen() { const panelModel = this.viewModel.get('panel'); if (!panelModel.get('context')) { panelModel.set('context', this.__createTreeCollection(this.context, this.recordTypeId)); } }, __createTreeCollection(context, recordTypeId) { if (!context || !context[recordTypeId]) { return new Backbone.Collection(); } const propertyTypes = this.options.propertyTypes; const mappedContext = {}; Object.entries(context).forEach(([key, value]) => { const mappedProperties = value.reduce((filteredProperites, property) => { const isInstance = this.options.instanceTypeProperties.includes(property.type); if (!this.__isPropertyValid(property)) { return filteredProperites; } const propertyModel = new Backbone.Model(property); if (isInstance && this.options.isInstanceExpandable) { const linkedProperties = context[property[this.options.instanceValueProperty]]; if (linkedProperties) { propertyModel.children = new Backbone.Collection(linkedProperties.filter(p => this.__isPropertyValid(p))); propertyModel.collapsed = true; propertyModel.children.forEach(childModel => (childModel.parent = propertyModel)); } } filteredProperites.push(propertyModel); return filteredProperites; }, []); mappedContext[key] = mappedProperties; }); const collection = new Backbone.Collection(mappedContext[recordTypeId]); this.listenTo(collection, 'expand', model => this.__onExpand({ model, mappedContext })); return collection; }, __isPropertyValid(property) { const { usePropertyTypes, propertyTypes } = this.options; const isFilterEnabled = usePropertyTypes && propertyTypes?.length; const isInstance = this.options.instanceTypeProperties.includes(property.type); return !isFilterEnabled || isInstance || propertyTypes.includes(property.type); }, __onExpand({ model, mappedContext }) { if (!model.children) { return; } model.children.forEach(child => { const isInstance = this.options.instanceTypeProperties.includes(child.get('type')); if (isInstance) { const newChildren = mappedContext[child.get(this.options.instanceValueProperty)]?.map(m => m.toJSON()); if (newChildren) { child.children = new Backbone.Collection(newChildren); child.collapsed = true; child.children.forEach(childModel => (childModel.parent = child)); } } }); }, __collectPropertyPath(selectedModel, collectedPath = []) { collectedPath.unshift(selectedModel.get('id')); if (selectedModel.parent) { return this.__collectPropertyPath(selectedModel.parent, collectedPath); } return collectedPath; }, __updateDisplayValue() { this.viewModel.get('button').set('value', this.__getButtonText(this.value)); }, __onClearClick() { if (this.__isDoubleClicked) { this.__isDoubleClicked = false; return; } this.__value(null, true, null); } });