UNPKG

ng-devui

Version:

DevUI components based on Angular

765 lines (760 loc) 184 kB
import * as i3 from '@angular/common'; import { DOCUMENT, CommonModule } from '@angular/common'; import * as i0 from '@angular/core'; import { Pipe, Component, Input, EventEmitter, Output, ViewChildren, ViewChild, Directive, Inject, ContentChild, HostListener, NgModule } from '@angular/core'; import * as i4$1 from '@angular/forms'; import { FormsModule } from '@angular/forms'; import * as i6 from 'ng-devui/checkbox'; import { CheckBoxModule } from 'ng-devui/checkbox'; import * as i4 from 'ng-devui/loading'; import { LoadingModule } from 'ng-devui/loading'; import { __decorate, __metadata } from 'tslib'; import * as i1 from 'ng-devui/i18n'; import * as i2 from 'ng-devui/utils'; import { expandCollapseForDomDestroy, WithConfig, SafePipeModule, HighlightModule } from 'ng-devui/utils'; import { forEach, isUndefined, omitBy, reduce, pickBy, values, trim, difference } from 'lodash-es'; import * as i5 from '@angular/cdk/scrolling'; import { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling'; import { BehaviorSubject, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import * as i7 from 'ng-devui/popover'; import { PopoverModule } from 'ng-devui/popover'; class TreeNode { constructor(id, parentId, data) { this.id = id; this.parentId = parentId; this.data = data; } } class TreeFactory { static create(isVirtualScroll) { return new TreeFactory(isVirtualScroll); } // tree model with items static fromTree({ treeItems, isVirtualScroll = false, treeNodeChildrenKey = 'items', treeNodeIdKey = 'id', checkboxDisabledKey = 'disabled', selectDisabledKey = 'disabled', // 默认值与checkboxDisabledKey相同,为了兼容以前tree的disable情况 toggleDisabledKey = 'disabledToggle', treeNodeTitleKey = 'title', }) { const treeFactory = TreeFactory.create(isVirtualScroll); treeFactory.mapTreeItems({ treeItems, parentId: undefined, treeNodeChildrenKey, treeNodeIdKey, checkboxDisabledKey, selectDisabledKey, toggleDisabledKey, treeNodeTitleKey, }, false); return treeFactory; } constructor(isVirtualScroll) { this.isVirtualScroll = isVirtualScroll; this._checked = new Set(); this._treeRoot = []; this.flattenNodes = new BehaviorSubject([]); this.canIdEmpty = true; this.mapTreeItems = ({ treeItems, parentId, treeNodeChildrenKey = 'items', treeNodeIdKey = 'id', checkboxDisabledKey = 'disabled', selectDisabledKey = 'disableSelect', toggleDisabledKey = 'disableToggle', treeNodeTitleKey = 'title', }, renderTree = true) => { forEach(treeItems, (item) => { const node = this.addNode({ id: item[treeNodeIdKey], parentId, title: item[treeNodeTitleKey], isOpen: !!item.open, data: item.data || {}, originItem: item, isParent: !!item.isParent || !!(item[treeNodeChildrenKey] && item[treeNodeChildrenKey].length > 0), loading: !!item.loading, isMatch: !!item.isMatch, isHide: !!item.isHide, isChecked: !!item.isChecked, halfChecked: !!item.halfChecked, isActive: !!item.isActive, disabled: !!item[checkboxDisabledKey], disableSelect: !!item[selectDisabledKey], disableToggle: !!item[toggleDisabledKey], disableAdd: !!item.disableAdd, disableEdit: !!item.disableEdit, disableDelete: !!item.disableDelete, children: [], showCheckbox: item.showCheckbox, }, undefined, renderTree); if (item.isChecked) { this._checked.add(node); } this.mapTreeItems({ treeItems: item[treeNodeChildrenKey] || [], parentId: node.id, treeNodeChildrenKey, treeNodeIdKey, checkboxDisabledKey, selectDisabledKey, toggleDisabledKey, treeNodeTitleKey, }, renderTree); }); return this; }; this.virtualScroll = isVirtualScroll; this.idx = 0; this.nodes = {}; } addNode({ id, parentId, ...data }, index, renderTree = true) { let newId = id; if (isUndefined(id)) { this.idx++; newId = this.idx; } const treeNode = new TreeNode(newId, parentId, data); if (Object.prototype.hasOwnProperty.call(this.nodes, treeNode.id)) { throw new Error(`Duplicated id: ${treeNode.id} detected, please specify unique ids in the tree.`); } this.nodes[treeNode.id] = treeNode; this.addChildNode(this.nodes[parentId], treeNode, index); // 兼容当前用户外部直接调用addNode方法创建节点 if (renderTree) { this.renderFlattenTree(); } return treeNode; } editNodeTitle(id) { if (!this.nodes[id]) { return; } this.nodes[id].data.editable = true; } deleteNodeById(id, renderTree = true) { const node = this.nodes[id]; if (!node) { return; } const parentNode = this.nodes[node.parentId]; this.removeChildNode(parentNode, node); const deleteItems = (nodeId) => { this.maintainCheckedNodeList(this.nodes[nodeId], false); const children = this.getChildrenById(nodeId); this.nodes = omitBy(this.nodes, (_node) => { return _node.id === nodeId; }); forEach(children, (child) => { deleteItems(child.id); }); }; deleteItems(id); if (parentNode && (!parentNode.data.children || !parentNode.data.children.length)) { parentNode.data.isParent = false; } if (renderTree) { this.renderFlattenTree(); } return this; } toggleNodeById(id) { if (!this.nodes[id]) { return; } this.nodes[id].data.isOpen = !this.nodes[id].data.isOpen; this.renderFlattenTree(); return this; } openNodesById(id) { if (!this.nodes[id]) { return; } this.nodes[id].data.isOpen = true; if (this.nodes[id].parentId !== undefined) { this.openNodesById(this.nodes[id].parentId); } this.renderFlattenTree(); return this; } closeNodesById(id, closeChildren = false) { if (!this.nodes[id]) { return; } this.nodes[id].data.isOpen = false; if (closeChildren) { if (this.nodes[id] && this.nodes[id].data.children) { this.nodes[id].data.children.forEach((node) => { this.closeNodesById(node.id); }); } } this.renderFlattenTree(); return this; } disabledNodesById(id) { if (!this.nodes[id]) { return; } this.nodes[id].data.disabled = true; const parentId = this.nodes[id].parentId; this._disabledParentNodes(parentId); const disabledNodes = (nodeId) => { const children = this.getChildrenById(nodeId); if (children.length > 0) { children.forEach((child) => { this.nodes[child.id].data.disabled = true; disabledNodes(child.id); }); } }; disabledNodes(id); return this; } _disabledParentNodes(parentId) { const children = this.getChildrenById(parentId); if (children.length < 1) { return; } const result = reduce(children, (status, child) => { return status && child.data.disabled; }, true); if (this.nodes[parentId]) { this.nodes[parentId].data.disabled = result; } } checkNodesById(id, checked, checkableRelation = 'both') { if (!this.nodes[id]) { return; } this.nodes[id].data.halfChecked = false; this.nodes[id].data.isChecked = checked; switch (checkableRelation) { case 'upward': this.checkParentNodes(this.nodes[id]); break; case 'downward': this.checkChildNodes(this.nodes[id], checked, this.nodes[id].data.isHide); break; case 'both': this.checkParentNodes(this.nodes[id]); this.checkChildNodes(this.nodes[id], checked, this.nodes[id].data.isHide); break; case 'none': break; default: } this.maintainCheckedNodeList(this.nodes[id], checked); return this.getCheckedNodes(); } checkParentNodes(node) { const { parentId } = node; const parentNode = this.nodes[parentId]; if (parentNode) { const childrenNode = this.getChildrenById(parentId); if (childrenNode.every((childNode) => childNode.data.isChecked && !childNode.data.halfChecked)) { parentNode.data.isChecked = true; parentNode.data.halfChecked = false; } else if (childrenNode.some((childNode) => childNode.data.halfChecked || childNode.data.isChecked)) { parentNode.data.isChecked = true; parentNode.data.halfChecked = true; } else { parentNode.data.isChecked = false; parentNode.data.halfChecked = false; } this.maintainCheckedNodeList(parentNode, parentNode.data.isChecked); this.checkParentNodes(parentNode); } } checkChildNodes(node, checked, hasHiddenAncestor = undefined) { const { id } = node; const childrenNode = this.getChildrenById(id); if (childrenNode.length > 0) { childrenNode.forEach((childNode) => { const { id: childId } = childNode; const { data: nodeData } = this.nodes[childId]; if (!nodeData.disabled) { nodeData.isChecked = checked; nodeData.halfChecked = false; nodeData.hasHiddenAncestor = hasHiddenAncestor; this.maintainCheckedNodeList(childNode, checked); } this.checkChildNodes(childNode, checked, nodeData.isHide); }); const childrenFullCheckedCount = childrenNode.filter(({ data: nodeData }) => nodeData.isChecked).length; const childrenCheckedCount = childrenNode.filter(({ data: nodeData }) => nodeData.isChecked || nodeData.halfChecked).length; node.data.halfChecked = childrenCheckedCount > 0 && childrenNode.length > childrenFullCheckedCount; } } getLineage(node) { const { parentId } = node; if (parentId) { const parentNode = this.nodes[parentId]; return [node.id, ...this.getLineage(parentNode)]; } else { return [node.id]; } } getCheckedNodes() { return Array.from(this._checked); } getCheckedNodesWithoutHide(hideInVirtualScroll = false) { return Array.from(this._checked).filter((item) => !((hideInVirtualScroll ? item.data.hideInVirtualScroll : item.data.isHide) || item.data.hasHiddenAncestor)); } getActivatedNodes() { const results = pickBy(this.nodes, (node) => node.data.isActive === true); return values(results); } getDisabledNodes() { const results = pickBy(this.nodes, (node) => node.data.disabled === true); return values(results); } activeNodeById(id, isMultiple) { if (!this.nodes[id]) { return; } if (!isMultiple) { this.deactivateAllNodes(); } this.nodes[id].data.isActive = !this.nodes[id].data.isActive; } getChildrenById(id) { if (this.nodes[id]) { return this.nodes[id].data.children || []; } else if (id === undefined) { return this._treeRoot; } return []; } startLoading(id) { if (!this.nodes[id]) { return; } this.nodes[id].data.loading = true; } endLoading(id) { if (!this.nodes[id]) { return; } this.nodes[id].data.loading = false; } getNodeById(id) { if (!this.nodes[id]) { return; } return this.nodes[id].data; } getCompleteNodeById(id) { return this.nodes[id]; } hideNodeById(id, hide) { if (!this.nodes[id]) { return; } this.nodes[id].data.isHide = hide; this.renderFlattenTree(); return this; } maintainCheckedNodeList(node, checked) { if (checked && !node.data.halfChecked) { this._checked.add(node); } else { this._checked.delete(node); } } dfs(target, tree, hideUnmatched, keyword, pattern) { if (!tree) { return false; } if (!target) { return false; } if (Array.isArray(tree)) { return tree.map((treeNode) => { return this.dfs(target, treeNode, hideUnmatched, keyword, pattern); }); } else { const treeNode = tree; const treeChildren = this.getChildrenById(treeNode.id); const key = keyword ? treeNode.data.originItem[keyword] : treeNode.data.title; const selfMatched = pattern ? pattern.test(key) : key.toLowerCase().includes(target); if (selfMatched) { treeNode.data.isMatch = true; treeNode.data.isCustomSearch = keyword; } // Test if children matches target recursively, do not hide children if parent is matched. const childrenMatched = this.dfs(target, treeChildren, hideUnmatched && !selfMatched, keyword, pattern).some((_) => !!_); if (selfMatched || childrenMatched) { if (childrenMatched && treeChildren.length > 0) { this.openNodesById(treeNode.id); } return true; } else { treeNode.data.isHide = hideUnmatched; return false; } } } addChildNode(parentNode, childNode, index) { if (parentNode) { if (Array.isArray(parentNode.data.children)) { if (index !== undefined) { parentNode.data.children.splice(index, 0, childNode); } else { parentNode.data.children.push(childNode); } } else { parentNode.data.children = [childNode]; } } else { index !== undefined ? this._treeRoot.splice(index, 0, childNode) : this._treeRoot.push(childNode); } this.nodes[childNode.id] = childNode; } removeChildNode(parentNode, childNode) { if (parentNode) { parentNode.data.children = parentNode.data.children.filter((node) => node.id !== childNode.id); } else { this._treeRoot = this._treeRoot.filter((node) => node.id !== childNode.id); } } resetSearchResults() { Object.keys(this.nodes).forEach((key) => { const treeNode = this.nodes[key]; treeNode.data.isMatch = false; treeNode.data.isHide = false; treeNode.data.isCustomSearch = false; }); } searchTree(target, hideUnmatched = false, keyword, pattern) { this.searchItem = target; const TrimmedTarget = trim(target); this.resetSearchResults(); return this.dfs(TrimmedTarget.toLowerCase(), this._treeRoot, hideUnmatched, keyword, pattern); } get treeRoot() { return this._treeRoot; } deactivateAllNodes() { for (const id of Object.keys(this.nodes)) { this.nodes[id].data.isActive = false; } } checkAllNodes(checked) { for (const id of Object.keys(this.nodes)) { if (!this.nodes[id].data.disabled) { this.nodes[id].data.halfChecked = false; this.nodes[id].data.isChecked = checked; } this.maintainCheckedNodeList(this.nodes[id], this.nodes[id].data.isChecked); } } getNodeIndex(node) { let parentNode; let children; if (node.parentId !== undefined) { parentNode = this.getNodeById(node.parentId); children = parentNode.children; } else { children = this.treeRoot; } for (let i = 0; i < children.length; i++) { if (children[i].id === node.id) { return i; } } return -1; } checkIsParent(childNodeId, parentNodeId) { const realParentId = this.nodes[childNodeId].parentId; if (realParentId === parentNodeId) { return true; } else if (realParentId !== undefined) { return this.checkIsParent(realParentId, parentNodeId); } else { return false; } } getFlattenNodes() { this.flattenNodes.next(this.flattenTree()); } flattenTree() { const flattenTree = []; const flatTree = (nodes) => { for (let i = 0; i < nodes.length; i++) { const hasParentId = this.canIdEmpty ? nodes[i].parentId : nodes[i].parentId !== undefined; nodes[i].data.depth = hasParentId ? this.nodes[nodes[i].parentId].data.depth + 1 : 0; nodes[i].data.hideInVirtualScroll = nodes[i].data.isHide || (hasParentId ? this.nodes[nodes[i].parentId].data.hideInVirtualScroll || !this.nodes[nodes[i].parentId].data.isOpen : false); nodes[i].data.isLast = i === nodes.length - 1; flattenTree.push(nodes[i]); if (nodes[i].data.children) { flatTree(nodes[i].data.children); } } }; flatTree(this.treeRoot); return flattenTree; } mergeTreeNodes(targetNode = this.treeRoot) { const mergeToNode = (node) => { if (!node) { return; } if (node.data.children?.length === 1 && node.data.children[0]?.data?.children?.length !== 0) { node.data.title = node.data.title + ' / ' + node.data.children[0]?.data?.title; node.data.children = node.data.children[0]?.data?.children; node.data.children.forEach((child) => { child.parentId = node.id; }); mergeToNode(node); } if (node.data.children?.length > 1) { node.data.children.forEach((element) => { mergeToNode(element); }); } }; if (targetNode === this.treeRoot) { this.treeRoot.forEach((element) => { mergeToNode(element); }); } else { mergeToNode(targetNode); } } renderFlattenTree() { if (!this.virtualScroll) { return; } this.getFlattenNodes(); } disableAllNodesChecked(disabled = true) { for (const id of Object.keys(this.nodes)) { this.nodes[id].data.disabled = disabled; } } disableAllNodesSelected(disabled = true) { for (const id of Object.keys(this.nodes)) { this.nodes[id].data.disableSelect = disabled; } } disableAllNodesToggled(disabled = true) { for (const id of Object.keys(this.nodes)) { this.nodes[id].data.disableToggle = disabled; } } toggleAllNodes(toggle = true) { for (const id of Object.keys(this.nodes)) { this.nodes[id].data.isOpen = toggle; } if (this.isVirtualScroll) { this.renderFlattenTree(); } } transferToTreeNode(originNode, parentId, treeNodeChildrenKey = 'items', treeNodeIdKey = 'id', checkboxDisabledKey = 'disabled', selectDisabledKey = 'disableSelect', toggleDisabledKey = 'disableToggle', treeNodeTitleKey = 'title') { const node = { id: originNode[treeNodeIdKey], parentId, title: originNode[treeNodeTitleKey], isOpen: !!originNode.open, data: originNode.data || {}, originItem: originNode, isParent: !!originNode.isParent || !!(originNode[treeNodeChildrenKey] && originNode[treeNodeChildrenKey].length > 0), loading: !!originNode.loading, isMatch: !!originNode.isMatch, isHide: !!originNode.isHide, isChecked: !!originNode.isChecked, halfChecked: !!originNode.halfChecked, isActive: !!originNode.isActive, disabled: !!originNode[checkboxDisabledKey], disableSelect: !!originNode[selectDisabledKey], disableToggle: !!originNode[toggleDisabledKey], disableAdd: !!originNode.disableAdd, disableEdit: !!originNode.disableEdit, disableDelete: !!originNode.disableDelete, children: [], }; return new TreeNode(node.id, node.parentId, { ...node }); } } class FilterNodesPipe { constructor() { } transform(nodes, key) { return nodes.filter(item => !item.data[key]); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FilterNodesPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: FilterNodesPipe, name: "filterNodesPipe" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FilterNodesPipe, decorators: [{ type: Pipe, args: [{ name: 'filterNodesPipe' }] }], ctorParameters: () => [] }); class TreeNodesComponent { constructor() { this.virtualScroll = false; } trackByFn(index, item) { return index; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TreeNodesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TreeNodesComponent, selector: "d-tree-nodes", inputs: { treeList: "treeList", treeNodesRef: "treeNodesRef", treeFactory: "treeFactory", virtualScroll: "virtualScroll" }, ngImport: i0, template: "<ng-container *ngIf=\"virtualScroll\">\n <ng-template\n *cdkVirtualFor=\"let treeNode of treeList | filterNodesPipe: 'hideInVirtualScroll'; trackBy: trackByFn\"\n [ngTemplateOutlet]=\"treeNodesRef\"\n [ngTemplateOutletContext]=\"{\n $implicit: this,\n treeNode: treeNode,\n treeFactory: treeFactory\n }\"\n >\n </ng-template>\n</ng-container>\n<ng-container *ngIf=\"!virtualScroll\">\n <ng-template\n *ngFor=\"let treeNode of treeList; trackBy: trackByFn\"\n [ngTemplateOutlet]=\"treeNodesRef\"\n [ngTemplateOutletContext]=\"{\n $implicit: this,\n treeNode: treeNode,\n treeFactory: treeFactory\n }\"\n >\n </ng-template>\n</ng-container>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i5.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "pipe", type: FilterNodesPipe, name: "filterNodesPipe" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TreeNodesComponent, decorators: [{ type: Component, args: [{ selector: 'd-tree-nodes', preserveWhitespaces: false, template: "<ng-container *ngIf=\"virtualScroll\">\n <ng-template\n *cdkVirtualFor=\"let treeNode of treeList | filterNodesPipe: 'hideInVirtualScroll'; trackBy: trackByFn\"\n [ngTemplateOutlet]=\"treeNodesRef\"\n [ngTemplateOutletContext]=\"{\n $implicit: this,\n treeNode: treeNode,\n treeFactory: treeFactory\n }\"\n >\n </ng-template>\n</ng-container>\n<ng-container *ngIf=\"!virtualScroll\">\n <ng-template\n *ngFor=\"let treeNode of treeList; trackBy: trackByFn\"\n [ngTemplateOutlet]=\"treeNodesRef\"\n [ngTemplateOutletContext]=\"{\n $implicit: this,\n treeNode: treeNode,\n treeFactory: treeFactory\n }\"\n >\n </ng-template>\n</ng-container>\n", styles: [":host{display:block}\n"] }] }], propDecorators: { treeList: [{ type: Input }], treeNodesRef: [{ type: Input }], treeFactory: [{ type: Input }], virtualScroll: [{ type: Input }] } }); class TransferToArrayPipe { constructor() { } transform(number) { return Array(number).fill(0); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TransferToArrayPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: TransferToArrayPipe, name: "transferToArrayPipe" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TransferToArrayPipe, decorators: [{ type: Pipe, args: [{ name: 'transferToArrayPipe' }] }], ctorParameters: () => [] }); class TreeComponent { constructor(i18n, devConfigService) { this.i18n = i18n; this.devConfigService = devConfigService; this.treeNodeTitleKey = 'title'; this.checkboxDisabledKey = 'disabled'; this.selectDisabledKey = 'disabled'; this.toggleDisabledKey = 'disableToggle'; this.virtualScroll = false; this.virtualScrollHeight = '800px'; this.showAnimation = true; this.minBufferPx = 600; this.maxBufferPx = 900; this.itemSize = 30; this.indent = '16px'; /** * 默认不需要判断parentId是否undefined,有业务使用了空字符串作为非根目录的id,导致必须判断来区分,当业务整改后移除该判断 * @deprecated */ this.canIdEmpty = true; this.nodeSelected = new EventEmitter(); this.nodeDblClicked = new EventEmitter(); this.nodeRightClicked = new EventEmitter(); this.nodeToggled = new EventEmitter(); this.afterTreeInit = new EventEmitter(); this.treeNodes = []; this.destroy$ = new Subject(); this.afterInitAnimate = true; } ngOnInit() { this.initTree(); this.i18nCommonText = this.i18n.getI18nText().common; this.i18nSubscription = this.i18n.langChange().subscribe((data) => { this.i18nCommonText = data.common; }); } ngOnChanges(changes) { if (changes && changes.tree && !changes.tree.isFirstChange()) { this.initTree(); } } initTree() { this.treeFactory = TreeFactory.fromTree({ treeItems: this.tree, isVirtualScroll: this.virtualScroll, treeNodeChildrenKey: this.treeNodeChildrenKey, treeNodeIdKey: this.treeNodeIdKey, treeNodeTitleKey: this.treeNodeTitleKey, checkboxDisabledKey: this.checkboxDisabledKey, selectDisabledKey: this.selectDisabledKey, toggleDisabledKey: this.toggleDisabledKey, }); this.treeFactory.canIdEmpty = this.canIdEmpty; if (this.virtualScroll) { this.treeFactory.flattenNodes.pipe(takeUntil(this.destroy$)).subscribe((data) => { this.treeNodes = data; }); this.treeFactory.getFlattenNodes(); } this.afterTreeInit.emit(this.treeFactory.nodes); } ngAfterViewInit() { setTimeout(() => { this.afterInitAnimate = false; }); } contextmenuEvent(event, node) { this.nodeRightClicked.emit({ node: node, event: event }); } selectNode(event, treeNode) { if (treeNode.data.disableSelect) { return; } if (!this.isSelectableRegion(event.target)) { return; } this.nodeSelected.emit(treeNode); this.treeFactory.activeNodeById(treeNode.id); } toggleNode(event, treeNode) { if (treeNode.data.disableToggle) { return; } this.treeFactory.toggleNodeById(treeNode.id); this.nodeToggled.emit(treeNode); } scrollToIndex(index) { this.viewPort.scrollToIndex(index, 'smooth'); } appendTreeItems(treeItems, parentId) { if (!this.treeFactory.nodes[parentId]) { throw new Error('parent node does not exist.'); } this.treeFactory.mapTreeItems({ treeItems: treeItems, parentId: parentId, treeNodeChildrenKey: this.treeNodeChildrenKey, treeNodeIdKey: this.treeNodeIdKey, treeNodeTitleKey: this.treeNodeTitleKey, checkboxDisabledKey: this.checkboxDisabledKey, selectDisabledKey: this.selectDisabledKey, toggleDisabledKey: this.toggleDisabledKey, }); } nodeDblClick(event, node) { this.nodeDblClicked.emit(node); } isSelectableRegion(ele) { if (ele && !ele.classList.contains('devui-tree-node__content--value-wrapper') && !ele.classList.contains('devui-tree-node__content') && !ele.classList.contains('devui-tree-node__title') && !ele.classList.contains('devui-tree-node-highlight') && ele.tagName !== 'D-HIGHLIGHT' && ele.parentNode?.tagName !== 'D-HIGHLIGHT') { return false; } return true; } ngOnDestroy() { if (this.i18nSubscription) { this.i18nSubscription.unsubscribe(); } this.destroy$.next(); this.destroy$.complete(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TreeComponent, deps: [{ token: i1.I18nService }, { token: i2.DevConfigService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TreeComponent, selector: "d-tree", inputs: { tree: "tree", treeNodesRef: "treeNodesRef", treeNodeIdKey: "treeNodeIdKey", treeNodeChildrenKey: "treeNodeChildrenKey", iconParentOpen: "iconParentOpen", iconParentClose: "iconParentClose", iconLeaf: "iconLeaf", loadingTemplateRef: "loadingTemplateRef", treeNodeTitleKey: "treeNodeTitleKey", checkboxDisabledKey: "checkboxDisabledKey", selectDisabledKey: "selectDisabledKey", toggleDisabledKey: "toggleDisabledKey", virtualScroll: "virtualScroll", virtualScrollHeight: "virtualScrollHeight", showAnimation: "showAnimation", minBufferPx: "minBufferPx", maxBufferPx: "maxBufferPx", itemSize: "itemSize", indent: "indent", canIdEmpty: "canIdEmpty" }, outputs: { nodeSelected: "nodeSelected", nodeDblClicked: "nodeDblClicked", nodeRightClicked: "nodeRightClicked", nodeToggled: "nodeToggled", afterTreeInit: "afterTreeInit" }, viewQueries: [{ propertyName: "viewPort", first: true, predicate: CdkVirtualScrollViewport, descendants: true }, { propertyName: "treeNodeContent", predicate: ["treeNodeContent"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<cdk-virtual-scroll-viewport\n *ngIf=\"virtualScroll\"\n class=\"devui-scrollbar devui-scroll-overlay\"\n [itemSize]=\"itemSize\"\n [minBufferPx]=\"minBufferPx\"\n [maxBufferPx]=\"maxBufferPx\"\n [style.height]=\"virtualScrollHeight\"\n>\n <d-tree-nodes\n [virtualScroll]=\"true\"\n [treeList]=\"treeNodes\"\n [treeNodesRef]=\"treeNodesRef ? treeNodesRef : virtualScrollRef\"\n [treeFactory]=\"treeFactory\"\n >\n </d-tree-nodes>\n</cdk-virtual-scroll-viewport>\n\n<d-tree-nodes\n *ngIf=\"!virtualScroll\"\n [treeList]=\"treeFactory.treeRoot\"\n [treeNodesRef]=\"treeNodesRef ? treeNodesRef : default\"\n [treeFactory]=\"treeFactory\"\n>\n</d-tree-nodes>\n<!-- TODO: \u865A\u62DF\u6EDA\u52A8\u652F\u6301\u52A8\u6548 -->\n<ng-template #virtualScrollRef let-treeNode=\"treeNode\" let-treeFactory=\"treeFactory\">\n <div\n class=\"devui-tree-node\"\n [style.paddingLeft.px]=\"treeNode.data.depth * 24\"\n [ngClass]=\"{\n 'devui-tree-node__open': treeNode.data.isOpen,\n 'devui-tree-node__customIcon': iconParentClose\n }\"\n #treeNodeContent\n >\n <div\n class=\"devui-tree-vertical-line\"\n *ngFor=\"let item of treeNode.data.depth | transferToArrayPipe; let i = index\"\n [style.marginLeft.px]=\"i === 0 ? -16 : -16 - 24 * i\"\n [ngStyle]=\"{ height: i === 0 && treeNode.data.isLast && !treeNode.data.isOpen ? '15px' : '30px' }\"\n ></div>\n <div\n *ngIf=\"treeNode.data.depth\"\n [ngStyle]=\"{ width: treeNode.data.isParent ? '8px' : '16px' }\"\n class=\"devui-tree-horizontal-line\"\n ></div>\n <div\n class=\"devui-tree-node__content\"\n [class.active]=\"treeNode.data.isActive\"\n [class.devui-tree-node--parent]=\"(treeNode.data.children || []).length > 0\"\n (click)=\"selectNode($event, treeNode)\"\n >\n <div class=\"devui-tree-node__content--value-wrapper\" [class.isMatch]=\"treeNode.data.isMatch\">\n <span\n (click)=\"toggleNode($event, treeNode)\"\n *ngIf=\"(treeNode.data.children || []).length > 0 || treeNode.data.isParent\"\n class=\"devui-tree-node__folder\"\n [class.toggle-disabled]=\"treeNode.data.disableToggle\"\n >\n <span class=\"devui-tree-node__folder--icon\" *ngIf=\"iconParentClose && !treeNode.data.isOpen\" [innerHTML]=\"iconParentClose\"></span>\n <span class=\"devui-tree-node__folder--icon\" *ngIf=\"iconParentOpen && treeNode.data.isOpen\" [innerHTML]=\"iconParentOpen\"></span>\n <span class=\"devui-tree-node__folder--icon\" *ngIf=\"!iconParentClose && !treeNode.data.isOpen\">\n <svg\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n class=\"svg-icon\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect x=\"0.5\" y=\"0.5\" width=\"15\" height=\"15\" rx=\"2\"></rect>\n <path\n d=\"M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z\"\n ></path>\n </g>\n </svg>\n </span>\n <span class=\"devui-tree-node__folder--icon\" *ngIf=\"!iconParentOpen && treeNode.data.isOpen\">\n <svg\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n class=\"svg-icon svg-icon-close\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect x=\"0.5\" y=\"0.5\" width=\"15\" height=\"15\" rx=\"2\"></rect>\n <rect x=\"4\" y=\"7\" width=\"8\" height=\"2\"></rect>\n </g>\n </svg>\n </span>\n </span>\n <span class=\"devui-tree-node__leaf\" *ngIf=\"(treeNode.data.children || []).length === 0 && !treeNode.data.isParent\">\n <span *ngIf=\"!iconLeaf\" class=\"devui-leaf-icon-none\" [ngStyle]=\"{ width: indent }\"></span>\n <span *ngIf=\"iconLeaf\" [innerHTML]=\"iconLeaf\"></span>\n </span>\n <span\n (dblclick)=\"nodeDblClick($event, treeNode)\"\n (contextmenu)=\"contextmenuEvent($event, treeNode)\"\n class=\"devui-tree-node__title\"\n [class.select-disabled]=\"treeNode.data.disableSelect\"\n title=\"{{ treeNode.data.title }}\"\n >{{ treeNode.data.title }}</span\n >\n <span\n dLoading\n [showLoading]=\"treeNode.data.loading\"\n [loadingTemplateRef]=\"loadingTemplateRef ? loadingTemplateRef : defaultLoadingTmpl\"\n >\n </span>\n </div>\n </div>\n </div>\n</ng-template>\n<ng-template #default let-treeNode=\"treeNode\" let-treeFactory=\"treeFactory\">\n <div\n class=\"devui-tree-node devui-tree-without-virtual-scroll\"\n [ngClass]=\"{\n 'devui-tree-node__open': treeNode.data.isOpen,\n 'devui-tree-node__customIcon': iconParentClose\n }\"\n #treeNodeContent\n >\n <div\n class=\"devui-tree-node__content\"\n [class.active]=\"treeNode.data.isActive\"\n [class.devui-tree-node--parent]=\"(treeNode.data.children || []).length > 0\"\n (click)=\"selectNode($event, treeNode)\"\n >\n <div class=\"devui-tree-node__content--value-wrapper\" [class.isMatch]=\"treeNode.data.isMatch\">\n <span\n (click)=\"toggleNode($event, treeNode)\"\n *ngIf=\"(treeNode.data.children || []).length > 0 || treeNode.data.isParent\"\n class=\"devui-tree-node__folder\"\n [class.toggle-disabled]=\"treeNode.data.disableToggle\"\n >\n <span class=\"devui-tree-node__folder--icon\" *ngIf=\"iconParentClose && !treeNode.data.isOpen\" [innerHTML]=\"iconParentClose\"></span>\n <span class=\"devui-tree-node__folder--icon\" *ngIf=\"iconParentOpen && treeNode.data.isOpen\" [innerHTML]=\"iconParentOpen\"></span>\n <span class=\"devui-tree-node__folder--icon\" *ngIf=\"!iconParentClose && !treeNode.data.isOpen\">\n <svg\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n class=\"svg-icon\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect x=\"0.5\" y=\"0.5\" width=\"15\" height=\"15\" rx=\"2\"></rect>\n <path\n d=\"M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z\"\n ></path>\n </g>\n </svg>\n </span>\n <span class=\"devui-tree-node__folder--icon\" *ngIf=\"!iconParentOpen && treeNode.data.isOpen\">\n <svg\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n class=\"svg-icon svg-icon-close\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect x=\"0.5\" y=\"0.5\" width=\"15\" height=\"15\" rx=\"2\"></rect>\n <rect x=\"4\" y=\"7\" width=\"8\" height=\"2\"></rect>\n </g>\n </svg>\n </span>\n </span>\n <span class=\"devui-tree-node__leaf\" *ngIf=\"(treeNode.data.children || []).length === 0 && !treeNode.data.isParent\">\n <span *ngIf=\"!iconLeaf\" class=\"devui-leaf-icon-none\" [ngStyle]=\"{ width: indent }\"></span>\n <span *ngIf=\"iconLeaf\" [innerHTML]=\"iconLeaf\"></span>\n </span>\n <span\n (dblclick)=\"nodeDblClick($event, treeNode)\"\n (contextmenu)=\"contextmenuEvent($event, treeNode)\"\n class=\"devui-tree-node__title\"\n [class.select-disabled]=\"treeNode.data.disableSelect\"\n title=\"{{ treeNode.data.title }}\"\n >{{ treeNode.data.title }}</span\n >\n <span\n dLoading\n [showLoading]=\"treeNode.data.loading\"\n [loadingTemplateRef]=\"loadingTemplateRef ? loadingTemplateRef : defaultLoadingTmpl\"\n >\n </span>\n </div>\n </div>\n <div\n *ngIf=\"treeNode.data.isOpen\"\n class=\"devui-tree-node__children\"\n @collapseForDomDestroy\n [@.disabled]=\"afterInitAnimate || !showAnimation\"\n >\n <d-tree-nodes [treeList]=\"treeNode.data.children || []\" [treeNodesRef]=\"default\" [treeFactory]=\"treeFactory\"> </d-tree-nodes>\n </div>\n </div>\n</ng-template>\n\n<ng-template #defaultLoadingTmpl>\n <span class=\"devui-loading-children\">{{ i18nCommonText?.loading }}</span>\n</ng-template>\n", styles: ["@charset \"UTF-8\";.devui-font-size-base{font-size:var(--devui-font-size, 12px)}.devui-font-base{font-size:var(--devui-font-size, 12px);font-weight:var(--devui-font-content-weight, normal);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-modal-title{font-size:var(--devui-font-size-modal-title, 18px)}.devui-font-modal-title{font-size:var(--devui-font-size-modal-title, 18px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-page-title{font-size:var(--devui-font-size-page-title, 16px)}.devui-font-page-title{font-size:var(--devui-font-size-page-title, 16px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}.devui-font-size-secondary-title{font-size:var(--devui-font-size-card-title, 14px)}.devui-font-secondary-title{font-size:var(--devui-font-size-card-title, 14px);font-weight:var(--devui-font-title-weight, bold);line-height:var(--devui-line-height-base, 1.5)}:host{display:block}.devui-text-ellipsis,.devui-tree-node .devui-tree-node__title{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.devui-tree-node{color:var(--devui-text, #191919);line-height:1.5;white-space:nowrap;position:relative}.devui-tree-node .devui-tree-node__content{display:inline-flex;align-items:center;font-size:var(--devui-font-size, 12px);padding-right:10px;width:100%;border-radius:var(--devui-border-radius, 2px);padding-left:6px}.devui-tree-node .devui-tree-node__content.active{background-color:var(--devui-list-item-selected-bg, #cedefd);text-decoration:none;border-color:transparent}.devui-tree-node .devui-tree-node__content.active .devui-tree-node__title{color:var(--devui-text, #191919);font-weight:700}.devui-tree-node .devui-tree-node__content:not(.active):hover{background-color:var(--devui-list-item-hover-bg, #f2f2f3)}.devui-tree-node .devui-tree-node__content--value-wrapper{display:inline-flex;align-items:center;height:30px;width:100%}.devui-tree-node .devui-tree-node__children{padding-left:10px}.devui-tree-node .devui-tree-node__children:first-child{border-left-color:transparent}.devui-tree-node .devui-tree-node__children .devui-tree-node{margin-left:8px;content:\"\";position:relative}.devui-tree-node .devui-tree-node__children .devui-tree-node:last-child{border-left-color:transparent}.devui-tree-node .devui-tree-node__title{margin-left:4px;display:inline-block;border:1px dashed transparent;border-radius:var(--devui-border-radius, 2px);max-width:100%}.devui-tree-node .devui-tree-node__title:not(.disabled){cursor:pointer}.devui-tree-node .devui-tree-node__edit{margin-left:.4em;padding:.1em}.devui-tree-node .devui-tree-node__edit>.devui-input-sm{height:26px}.devui-tree-node .devui-tree-node__edit>.devui-input-sm.error,.devui-tree-node .devui-tree-node__edit>.devui-input-sm.error:hover,.devui-tree-node .devui-tree-node__edit>.devui-input-sm.error:focus{border-color:var(--devui-danger, #e02128)}.devui-tree-node .devui-tree-node__leaf:not(.disabled){cursor:default}.devui-tree-node .devui-tree-node__leaf .devui-tree-node__leaf--default{color:#f2a71f}.devui-tree-node .devui-tree-node__leaf .devui-leaf-icon-none{display:inline-block;height:16px}.devui-tree-node .devui-tree-node__folder{display:inline-block;vertical-align:middle;-webkit-user-select:none;user-select:none;font-size:var(--devui-font-size-icon, 16px);height:16px;line-height:16px}.devui-tree-node .devui-tree-node__folder .devui-tree-node__folder--icon{display:inline-block;height:16px;line-height:16px}.devui-tree-node .devui-tree-node__folder .devui-tree-node__folder--icon:hover svg g path{fill:var(--devui-icon-fill-hover, #191919)}.devui-tree-node .devui-tree-node__folder .devui-tree-node__folder--icon:hover svg g rect{stroke:var(--devui-icon-fill-hover, #191919)}.devui-tree-node .devui-tree-node__folder:not(.disabled){cursor:pointer}.devui-tree-node .devui-tree-node__folder .devui-tree-node__folder--default{color:#f2b806}.devui-tree-node .devui-loading-children{display:inline-block;vertical-align:middle;margin-left:.5em;margin-top:.15em;color:var(--devui-info, #0a59f7);font-style:italic;font-size:1em;animation-name:devui-loading-children;animation-duration:2s;animation-timing-function:ease-in-out;animation-iteration-count:infinite}@keyframes devui-loading-children{0%{color:#627fe1}12.5%{color:#627fe1}25%{color:#617fe1}37.5%{color:#617ee1}50%{color:#607ee0}62.5%{color:#607ee0}75%{color:#5f7de0}87.5%{color:#5e7ce0}to{color:#5e7ce0}}.devui-tree-node svg.svg-icon path{fill:var(--devui-icon-fill, #595959)}.devui-tree-node svg.svg-icon rect{stroke:var(--devui-icon-fill, #595959)}.devui-tree-node.devui-tree-node__open:not(.devui-tree-node__customIcon)>.devui-tree-node__content svg.svg-icon path{fill:var(--devui-icon-fill-active, #191919)}.devui-tree-node.devui-tree-node__open:not(.devui-tree-node__customIcon)>.devui-tree-node__content svg.svg-icon rect{stroke:var(--devui-icon-fill-active, #191919)}.devui-tree-node.devui-tree-node__open:not(.devui-tree-node__customIcon)>.devui-tree-node__content svg.svg-icon.svg-icon-close rect:last-child{stroke:none;fill:var(--devui-icon-fill-active, #191919)}.devui-tree-node svg.svg-icon.svg-icon-close rect:last-child{stroke:none;fill:var(--devui-icon-text, #808080)}::ng-deep .devui-tree-mask{background:var(--devui-list-item-hover-bg, #f2f2f3)}.devui-tree-node.devui-tree-without-virtual-scroll.devui-tree-node__open>.devui-tree-node__content{position:relative}.devui-tree-node.devui-tree-without-virtual-scroll>.devui-tree-node__children{position:relative}.devui-tree-node.devui-tree-without-virtual-scroll>.devui-tree-node__children:before{content:\"\";width:1px;height:calc(100% - 15px);background-color:var(--devui-dividing-line, #f0f0f0);position:absolute;left:9px;top:0}.devui-tree-node.devui-tree-without-virtual-scroll>.devui-tree-node__children .devui-tree-node__content{position:relative}.devui-tree-node.devui-tree-without-virtual-scroll>.devui-tree-node__children .devui-tree-node__content:before{content:\"\";width:8px;height:1px;background-color:var(--devui-dividing-line, #f0f0f0);position:absolute;left:-9px;top:50%}.devui-tree-vertical-line{width:1px;background-color:var(--devui-dividing-line, #f0f0f0);position:absolute}.devui-tree-horizontal-line{height:1px;background-color:var(--devui-dividing-line, #f0f0f0);position:absolute;top:50%;margin-left:-16px}.toggle-disabled{cursor:not-allowed!important}.toggle-disabled svg.svg-icon rect{stroke:var(--devui-disabled-text, #c2c2c2)!important}.toggle-disabled svg.svg-icon.svg-icon-close rect:last-child{stroke:none!important;fill:var(--devui-disabled-text, #c2c2c2)!important}.toggle-disabled svg.svg-icon path{fill:var(--devui-disabled-text, #c2c2c2)!important}.select-disabled{color:var(--devui-disabled-text, #c2c2c2)!important;cursor:not-allowed!important;background-color:transparent!important}.devui-tree-node__content{transition:color var(--devui-animation-duration-fast, .1s) var(--devui-animation-ease-in-out-smooth, cubic-bezier(.645, .045, .355, 1)),background-color var(--devui-animation-duration-fast, .1s) var(--devui-animation-ease-in-out-smooth, cubic-bezier(.645, .045, .355, 1))}::ng-deep d-tree .cdk-virtual-scroll-content-wrapper{width:100%}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i4.LoadingDirective, selector: "[dLoading]", inputs: ["backdrop", "message", "positionType", "showLoading", "view", "zIndex", "loading", "loadingStyle", "loadingTemplateRef"], exportAs: ["dLoading"] }, { kind: "directive", type: i5.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scrol