UNPKG

@eclipse-scout/core

Version:
230 lines (198 loc) 6.95 kB
/* * Copyright (c) 2010, 2025 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ import {arrays, InitModelOf, LookupBox, LookupResult, LookupRow, ObjectOrChildModel, objects, scout, Tree, TreeBoxLayout, TreeBoxModel, TreeModel, TreeNode, TreeNodesCheckedEvent, TreeNodeUncheckOptions, Widget} from '../../../index'; import $ from 'jquery'; export class TreeBox<TValue> extends LookupBox<TValue> implements TreeBoxModel<TValue> { tree: Tree; protected _populating: boolean; constructor() { super(); this.tree = null; this._populating = false; this._addWidgetProperties(['tree', 'filterBox']); } protected override _init(model: InitModelOf<this>) { super._init(model); this.tree.on('nodesChecked', this._onTreeNodesChecked.bind(this)); this.tree.setScrollTop(this.scrollTop); } protected _initStructure(value: TValue[]) { if (!this.tree) { this.tree = this._createDefaultTreeBoxTree(); } } protected override _render() { super._render(); this.$container.addClass('tree-box'); } protected _createFieldContainerLayout(): TreeBoxLayout { return new TreeBoxLayout(this, this.tree, this.filterBox); } protected _renderStructure() { this.tree.render(this.$fieldContainer); this.addField(this.tree.$container); } protected _onTreeNodesChecked(event: TreeNodesCheckedEvent) { if (this._populating) { return; } this._syncTreeToValue(); } protected _syncTreeToValue() { if (!this.lookupCall || this._valueSyncing) { return; } this._valueSyncing = true; let valueArray = objects.values(this.tree.nodesMap) .filter(node => node.checked) .map((node: TreeBoxTreeNode<TValue>) => node.lookupRow.key); this.setValue(valueArray); this._valueSyncing = false; } protected override _valueChanged() { super._valueChanged(); this._syncValueToTree(this.value); } protected _syncValueToTree(newValue: TValue[]) { if (!this.lookupCall || this._valueSyncing || !this.initialized) { return; } this._valueSyncing = true; let opts = { checkOnlyEnabled: false }; try { if (arrays.empty(newValue)) { this.uncheckAll(opts); } else { // if lookup was not executed yet: do it now. let lookupScheduled = this._ensureLookupCallExecuted(); if (lookupScheduled) { return; // was the first lookup: tree has no nodes yet. cancel sync. Will be executed again after lookup execution. } this.uncheckAll(opts); objects.values(this.tree.nodesMap).forEach((node: TreeBoxTreeNode<TValue>) => { if (arrays.contains(newValue, node.lookupRow.key)) { this.tree.checkNode(node, true, opts); } }, this); } this._updateDisplayText(); } finally { this._valueSyncing = false; } if (this.tree.autoCheckChildren) { // The value can change because of parent nodes checking their children this._syncTreeToValue(); } } uncheckAll(options: TreeNodeUncheckOptions) { for (let nodeId in this.tree.nodesMap) { if (this.tree.nodesMap.hasOwnProperty(nodeId)) { this.tree.uncheckNode(this.tree.nodesMap[nodeId], options); } } } protected override _lookupByAllDone(result: LookupResult<TValue>) { super._lookupByAllDone(result); this._populateTree(result); } protected _populateTree(result: LookupResult<TValue>) { let topLevelNodes = []; this._populating = true; this._populateTreeRecursive(null, topLevelNodes, result.lookupRows); this.tree.deleteAllNodes(); this.tree.insertNodes(topLevelNodes); this._populating = false; this._syncValueToTree(this.value); } protected _populateTreeRecursive(parentKey: TValue, nodesArray: TreeNode[], lookupRows: LookupRow<TValue>[]) { let node: TreeBoxTreeNode<TValue>; lookupRows.forEach(function(lookupRow) { if (lookupRow.parentKey === parentKey) { node = this._createNode(lookupRow); this._populateTreeRecursive(node.lookupRow.key, node.childNodes, lookupRows); node.leaf = !node.childNodes.length; nodesArray.push(node); } }, this); } /** * Returns a lookup row for each node currently checked. */ getCheckedLookupRows(): LookupRow<TValue>[] { if (this.value === null || arrays.empty(this.value) || this.tree.nodes.length === 0) { return []; } return objects.values(this.tree.nodesMap) .filter(node => node.checked) .map((node: TreeBoxTreeNode<TValue>) => node.lookupRow); } protected _createNode(lookupRow: LookupRow<TValue>): TreeBoxTreeNode<TValue> { let node = scout.create(TreeNode, { parent: this.tree, text: lookupRow.text, lookupRow: lookupRow }) as TreeBoxTreeNode<TValue>; if (lookupRow.iconId) { node.iconId = lookupRow.iconId; } if (lookupRow.tooltipText) { node.tooltipText = lookupRow.tooltipText; } if (lookupRow.backgroundColor) { node.backgroundColor = lookupRow.backgroundColor; } if (lookupRow.foregroundColor) { node.foregroundColor = lookupRow.foregroundColor; } if (lookupRow.font) { node.font = lookupRow.font; } if (lookupRow.enabled === false) { node.enabled = false; } if (lookupRow.cssClass) { node.cssClass = lookupRow.cssClass; } if (lookupRow.active === false) { node.active = false; node.cssClass = (node.cssClass ? (node.cssClass + ' ') : '') + 'inactive'; } return node; } protected override _prepareWidgetProperty(propertyName: string, models: ObjectOrChildModel<Widget>): Widget; protected override _prepareWidgetProperty(propertyName: string, models: ObjectOrChildModel<Widget>[]): Widget[]; protected override _prepareWidgetProperty(propertyName: string, models: ObjectOrChildModel<Widget> | ObjectOrChildModel<Widget>[]): Widget | Widget[] { if (propertyName === 'tree' && objects.isPojo(models)) { // Enhance given model with tree box specific defaults models = $.extend(this._createDefaultTreeBoxTreeModel(), models); } return super._prepareWidgetProperty(propertyName, models as ObjectOrChildModel<Widget>); } protected _createDefaultTreeBoxTree(): Tree { return scout.create(Tree, { parent: this, ...this._createDefaultTreeBoxTreeModel() }); } protected _createDefaultTreeBoxTreeModel(): TreeModel { return { checkable: true }; } override getDelegateScrollable(): Widget { return this.tree; } } export type TreeBoxTreeNode<TValue> = TreeNode & { lookupRow: LookupRow<TValue>; active?: boolean; };