UNPKG

ng-devui

Version:

DevUI components based on Angular

615 lines 222 kB
import { __decorate, __metadata } from "tslib"; import { DOCUMENT } from '@angular/common'; import { Component, ContentChild, ElementRef, EventEmitter, HostListener, Inject, Input, Output, QueryList, TemplateRef, ViewChild, ViewChildren, } from '@angular/core'; import { I18nService } from 'ng-devui/i18n'; import { DevConfigService, WithConfig, expandCollapseForDomDestroy } from 'ng-devui/utils'; import { difference } from 'lodash-es'; import { TreeComponent } from './tree.component'; import * as i0 from "@angular/core"; import * as i1 from "ng-devui/i18n"; import * as i2 from "ng-devui/utils"; import * as i3 from "@angular/common"; import * as i4 from "@angular/forms"; import * as i5 from "ng-devui/loading"; import * as i6 from "ng-devui/checkbox"; import * as i7 from "ng-devui/popover"; import * as i8 from "./tree.component"; import * as i9 from "./tree-nodes.component"; import * as i10 from "./auto-focus.directive"; import * as i11 from "./pipe/transfer-to-array.pipe"; export class OperableTreeComponent { static { this.ID_SEED = 0; } constructor(doc, i18n, devConfigService) { this.doc = doc; this.i18n = i18n; this.devConfigService = devConfigService; this.checkable = true; this.deletable = false; this.addable = false; this.editable = false; this.draggable = false; this.dropFromOutside = false; this.checkboxInput = {}; this.canActivateNode = true; this.canActivateParentNode = true; this.canActivateMultipleNode = false; this.treeNodeTitleKey = 'title'; this.virtualScroll = false; this.virtualScrollHeight = '800px'; this.showAnimation = true; this.itemSize = 30; this.minBufferPx = 600; this.maxBufferPx = 900; this.checkableRelation = 'both'; this.indent = '16px'; /** * @deprecated */ this.canIdEmpty = true; this.operatorAlign = 'start'; this.nodeSelected = new EventEmitter(); this.nodeDblClicked = new EventEmitter(); this.nodeRightClicked = new EventEmitter(); this.nodeToggled = new EventEmitter(); this.afterTreeInit = new EventEmitter(); this.nodeDeleted = new EventEmitter(); this.nodeChecked = new EventEmitter(); this.currentNodeChecked = new EventEmitter(); this.nodeEdited = new EventEmitter(); this.editValueChange = new EventEmitter(); this.nodeDragStart = new EventEmitter(); this.nodeOnDrop = new EventEmitter(); this.dropType = { dropPrev: false, dropNext: false, dropInner: true, }; this.addingNode = false; this.mouseRightButton = 2; this.treeNodeDragoverResponder = { node: null, timeout: null, }; this.dragState = { showIndicator: true, dropType: null, draggingNode: null, indicatorTop: 0, indicatorLeft: 0, indicatorWidth: 0, }; this.afterInitAnimate = true; this.isOpenedOonDragOver = []; this.deleteNodesProxy = (event, treeNode) => { this.deleteNodes(event, treeNode); }; this.addChildNodeProxy = (event, treeNode, newNode) => { this.addChildNode(event, treeNode, newNode); }; this.editNodeProxy = (event, treeNode) => { this.editNode(event, treeNode); }; this.id = `d-operable-tree-${OperableTreeComponent.ID_SEED++}`; this.document = this.doc; } ngOnInit() { this.i18nCommonText = this.i18n.getI18nText().common; this.i18nSubscription = this.i18n.langChange().subscribe((data) => { this.i18nCommonText = data.common; }); } ngAfterViewInit() { setTimeout(() => { this.afterInitAnimate = false; }); } ngOnDestroy() { if (this.i18nSubscription) { this.i18nSubscription.unsubscribe(); } } contextmenuEvent(event, node) { this.nodeRightClicked.emit({ node: node, event: event }); } copyStyle(source, target) { ['id', 'class', 'style', 'draggable'].forEach((attr) => target.removeAttribute(attr)); const computedStyle = getComputedStyle(source); for (let i = 0; i < computedStyle.length; i++) { const key = computedStyle[i]; if (key.indexOf('transition') < 0) { target.style[key] = computedStyle[key]; } } target.style.pointerEvents = 'none'; for (let i = 0; i < source.children.length; i++) { this.copyStyle(source.children[i], target.children[i]); } } multipleDragStyle(event, nodes, target) { const num = nodes.length > 2 ? 2 : nodes.length - 1; const cloneNodes = new Array(num).fill(null); const container = this.document.createElement('div'); const cloneNode = target.cloneNode(true); this.copyStyle(target, cloneNode); cloneNode.style.position = 'absolute'; cloneNode.style.border = 'solid 1px var(--devui-connected-overlay-line, #526ecc)'; cloneNodes.push(cloneNode); cloneNodes.forEach((node, index) => { const child = node || cloneNode.cloneNode(true); child.style.left = `${8 * (num - index)}px`; child.style.top = `${4 * index}px`; container.appendChild(child); }); container.className = 'devui-tree-drag-ghost-container'; // setDragImage 只能对viewport内的 dom 起作用 this.document.body.appendChild(container); event.dataTransfer.setDragImage(container, -16, 0); setTimeout(() => container.remove()); } onDragstart(event, treeNode) { this.isOpenedOonDragOver = []; this.dragState.draggingNode = event.target; const result = { event, treeNode }; const data = { type: 'operable-tree-node', treeId: this.id, nodeId: treeNode.id, parentId: treeNode.parentId, nodeTitle: treeNode.data.title, isParent: treeNode.data.isParent, isChecked: treeNode.data.isChecked, halfChecked: treeNode.data.halfChecked, }; if (this.canActivateMultipleNode) { const activatedNodes = this.treeFactory.getActivatedNodes(); // 存在无激活项,直接拖拽单个节点情况 const availableNodes = activatedNodes.length ? activatedNodes : [treeNode]; data.multipleData = availableNodes; result.treeNodes = availableNodes; // 拖拽启用层叠样式,随拖拽个数变化 this.multipleDragStyle(event, availableNodes, event.target); } event.dataTransfer.setData('Text', JSON.stringify(data)); this.nodeDragStart.emit(result); } onDragover(event, droppable, treeNode) { if (droppable) { event.preventDefault(); event.dataTransfer.dropEffect = 'move'; if (this.dropType.dropInner && (!this.treeNodeDragoverResponder.node || (this.treeNodeDragoverResponder.node && this.treeNodeDragoverResponder.node.id !== treeNode.id))) { clearTimeout(this.treeNodeDragoverResponder.timeout); this.treeNodeDragoverResponder.node = treeNode; this.treeNodeDragoverResponder.timeout = setTimeout(() => { if (treeNode.data.isParent && !treeNode.data.isOpen) { this.isOpenedOonDragOver.push(treeNode.id); this.treeFactory.openNodesById(treeNode.id); this.nodeToggled.emit(treeNode); } }, 1000); } this.handlerDragState(event, treeNode); } } handlerDragState(event, treeNode) { let dropType; const dropPrev = this.dropType.dropPrev; const dropNext = this.dropType.dropNext; const dropInner = this.dropType.dropInner; const treePosition = this.operableTreeEle.nativeElement.getBoundingClientRect(); const nextRes = dropNext ? 0.45 : 1; const innerNextRes = dropInner ? 0.25 : nextRes; const prevPercent = dropPrev ? innerNextRes : -1; const prevRes = dropPrev ? 0.55 : 0; const innerPrevRes = dropInner ? 0.75 : prevRes; const nextPercent = dropNext ? innerPrevRes : 1; const targetPosition = event.currentTarget.getBoundingClientRect(); const dom = event.currentTarget.querySelector('.devui-tree-drag-handle') || event.currentTarget.querySelector('.devui-tree-node__title'); const treeNodePosition = dom.getBoundingClientRect(); const distance = event.clientY - targetPosition.top; if (distance < targetPosition.height * prevPercent) { dropType = 'prev'; } else if (distance > targetPosition.height * nextPercent) { dropType = 'next'; } else if (dropInner) { dropType = 'inner'; } else { dropType = 'none'; } if (dropType === 'prev') { this.dragState.indicatorTop = treeNodePosition.top - treePosition.top - 10; } if (dropType === 'next') { this.dragState.indicatorTop = treeNodePosition.bottom - treePosition.top + 10; } if (dropType === 'inner') { event.currentTarget.classList.add('devui-drop-inner'); } else { event.currentTarget.classList.remove('devui-drop-inner'); } this.dragState.indicatorLeft = treeNodePosition.left - treePosition.left; this.dragState.indicatorWidth = treePosition.width - this.dragState.indicatorLeft; this.dragState.dropType = dropType; this.dragState.showIndicator = dropType === 'prev' || dropType === 'next'; } onDragleave(event, treeNode) { this.removeDraggingStyle(event.currentTarget); if (this.treeNodeDragoverResponder.node && this.treeNodeDragoverResponder.node.id === treeNode.id) { this.treeNodeDragoverResponder.node = null; clearTimeout(this.treeNodeDragoverResponder.timeout); } } removeDraggingStyle(target) { this.dragState.showIndicator = false; target.classList.remove('devui-drop-inner'); } onDrop(event, dropNode) { this.removeDraggingStyle(event.currentTarget); if (!this.draggable && !this.dropFromOutside) { return; } event.preventDefault(); const transferDataStr = event.dataTransfer.getData('Text'); if (transferDataStr) { try { const transferData = JSON.parse(transferDataStr); if (typeof transferData === 'object' && transferData.type === 'operable-tree-node' && transferData.treeId === this.id) { let dragResult = Promise.resolve(true); const dragNodeId = transferData.nodeId; const dragNodeIds = []; const dropNodeId = dropNode.id; // 使用对象存储选择状态,因为节点id为数字,避免产生长索引数组影响性能 const dragNodesCheckStatus = {}; const multipleData = transferData.multipleData; if (this.canActivateMultipleNode && multipleData?.length > 1) { // 多选时遍历所有被激活的节点确认是否为目标节点或其父节点并储存可用节点的选择状态 // 反序遍历用于先处理子节点以更新父子同时拖拽的状态 multipleData.reverse().forEach((node) => this.reverseSelection(node, dropNodeId, dragNodeIds, dragNodesCheckStatus)); } else { const isParent = this.treeFactory.checkIsParent(dropNodeId, dragNodeId); if (dragNodeId === dropNodeId || isParent) { return; } this.treeFactory.checkNodesById(dragNodeId, false, 'upward'); dragNodesCheckStatus[dragNodeId] = { checked: transferData.isChecked, halfChecked: transferData.halfChecked }; } if (this.beforeNodeDrop) { dragResult = this.beforeNodeDrop(dragNodeId, dropNodeId, this.dragState.dropType, dragNodeIds); } dragResult .then(() => { this.setSelection(dropNode, dragNodeId, dragNodeIds, dragNodesCheckStatus); if (this.nodeOnDrop.observers.length > 0) { if (this.isOpenedOonDragOver.length) { const ids = this.treeFactory.getLineage(dropNode); this.isOpenedOonDragOver = difference(this.isOpenedOonDragOver, ids); } this.nodeOnDrop.emit({ event, treeNode: dropNode, dropType: this.dragState.dropType }); } }); } else { this.dropFormOutside(event, dropNode); } } catch (e) { this.dropFormOutside(event, dropNode); } } } dropFormOutside(event, dropNode) { if (this.dropFromOutside && this.nodeOnDrop.observers.length > 0) { this.nodeOnDrop.emit({ event, treeNode: dropNode, dropType: this.dragState.dropType, isFromOutside: true }); } } onDragend() { this.isOpenedOonDragOver.forEach((id) => this.treeFactory.closeNodesById(id)); this.isOpenedOonDragOver = []; } reverseSelection(node, dropNodeId, dragNodeIds, dragNodesCheckStatus) { const id = node.id; const isParent = this.treeFactory.checkIsParent(dropNodeId, id); if (!isParent && id !== dropNodeId) { this.treeFactory.checkNodesById(id, false, 'upward'); // 获取更新后的node状态,不使用node旧数据避免父子同时拖拽状态被覆盖 const currentNode = this.treeFactory.getNodeById(id); // 半选不保留,取最新状态,选中则保留原有状态 const checkedStatus = node.data.halfChecked ? currentNode.isChecked : node.data.isChecked; dragNodesCheckStatus[id] = { checked: checkedStatus, halfChecked: currentNode.halfChecked }; dragNodeIds.push(id); } } setSelection(dropNode, dragNodeId, dragNodeIds, dragNodesCheckStatus) { let finalDragNodeIds = [dragNodeId]; if (this.canActivateMultipleNode && dragNodeIds?.length > 1) { dragNodeIds.reverse().forEach((id) => this.handleDropNode(id, dropNode)); finalDragNodeIds = dragNodeIds; } else { this.handleDropNode(dragNodeId, dropNode); } this.treeFactory.renderFlattenTree(); // 移动结束后向上整理选择状态,恢复半选状态 finalDragNodeIds.forEach((id) => { const node = this.treeFactory.getCompleteNodeById(id); node.data.isChecked = dragNodesCheckStatus[id].checked; node.data.halfChecked = dragNodesCheckStatus[id].halfChecked; this.treeFactory.checkParentNodes(node); }); } handleDropNode(dragNodeId, dropNode) { const movingNode = this.treeFactory.nodes[dragNodeId]; const movingNodeIndex = this.treeFactory.getNodeIndex(movingNode); const dropNodeIndex = this.treeFactory.getNodeIndex(dropNode); const hasParentId = this.canIdEmpty ? movingNode.parentId : movingNode.parentId !== undefined; const originalParentNode = hasParentId ? this.treeFactory.nodes[movingNode.parentId] : this.treeFactory.treeRoot; switch (this.dragState.dropType) { case 'prev': this.handlerDropSort(movingNodeIndex, dropNodeIndex, movingNode, dropNode, originalParentNode, 'prev'); break; case 'next': this.handlerDropSort(movingNodeIndex, dropNodeIndex, movingNode, dropNode, originalParentNode, 'next'); break; case 'inner': this.handlerDropInner(movingNodeIndex, movingNode, dropNode, originalParentNode); break; default: } } handlerDropSort(oldIndex, newIndex, movingNode, dropNode, originalParentNode, type) { const dropIndex = type === 'next' ? newIndex + 1 : newIndex; movingNode.parentId = dropNode.parentId; let parentNode; if (dropNode.parentId === undefined) { parentNode = this.treeFactory.treeRoot; parentNode.splice(dropIndex, 0, movingNode); } else { parentNode = this.treeFactory.getNodeById(dropNode.parentId); parentNode.children.splice(dropIndex, 0, movingNode); } if (dropNode.parentId === originalParentNode.id && newIndex < oldIndex) { this.handlerOriginalParentNode(originalParentNode, oldIndex + 1); } else { this.handlerOriginalParentNode(originalParentNode, oldIndex); } } handlerOriginalParentNode(originalParentNode, oldIndex) { if (originalParentNode.id === undefined) { originalParentNode.splice(oldIndex, 1); } else { originalParentNode.data.children.splice(oldIndex, 1); if (!originalParentNode.data.children || !originalParentNode.data.children.length) { originalParentNode.data.isParent = false; } } } handlerDropInner(oldIndex, movingNode, dropNode, originalParentNode) { movingNode.parentId = dropNode.id; dropNode.data.isParent = true; this.treeFactory.openNodesById(dropNode.id); this.treeFactory.addChildNode(dropNode, movingNode); this.handlerOriginalParentNode(originalParentNode, oldIndex); this.nodeToggled.emit(dropNode); } selectNode(event, treeNode) { if (treeNode.data.disableSelect) { return; } if (!this.operableTree.isSelectableRegion(event.target)) { return; } let selectResult = Promise.resolve(true); if (this.beforeSelectNode) { selectResult = this.beforeSelectNode(treeNode); } selectResult.then((canSelect) => { if (!canSelect) { return; } if (!this.canActivateNode) { this.checkNode(event, treeNode); return; } if (this.canActivateParentNode || (!this.canActivateParentNode && !treeNode.data.isParent)) { const isMultiple = this.canActivateMultipleNode && (event.ctrlKey || event.shiftKey); this.treeFactory.activeNodeById(treeNode.id, isMultiple); if (isMultiple) { this.nodeSelected.emit(this.treeFactory.getActivatedNodes()); return; } } else { this.toggleNode(event, treeNode); return; } this.nodeSelected.emit(treeNode); }); } toggleNode(event, treeNode) { if (treeNode.data.disableToggle) { return; } this.treeFactory.toggleNodeById(treeNode.id); this.nodeToggled.emit(treeNode); } deleteNodes(event, treeNode) { let delResult = Promise.resolve(true); if (this.beforeDeleteNode) { delResult = this.beforeDeleteNode(treeNode); } delResult.then((canDelete) => { if (!canDelete) { return; } this.treeFactory.deleteNodeById(treeNode.id); this.nodeDeleted.emit(treeNode); }); } addChildNode(event, treeNode, newNode) { let addResult = Promise.resolve(newNode ? newNode : true); if (this.beforeAddNode) { addResult = this.beforeAddNode(treeNode); } addResult.then((nodeInfo) => { if (!nodeInfo) { return; } const node = this.treeFactory.addNode({ parentId: treeNode.id, title: nodeInfo.title ? nodeInfo.title : '新增节点', isParent: nodeInfo.isParent, id: nodeInfo.id ? nodeInfo.id : undefined, data: nodeInfo.data, }, nodeInfo.index, false); this.treeFactory.editNodeTitle(node.id); this.addingNode = true; treeNode.data.isParent = true; this.treeFactory.openNodesById(treeNode.id); return treeNode; }); } editNode(event, treeNode) { let editResult = Promise.resolve(true); if (this.beforeEditNode) { editResult = this.beforeEditNode(treeNode); } editResult.then((canEdit) => { if (!canEdit) { return; } this.treeFactory.editNodeTitle(treeNode.id); }); } checkNodeById(checked, id) { const results = this.treeFactory.checkNodesById(id, checked, this.checkableRelation); this.nodeChecked.emit(results); this.currentNodeChecked.emit({ id: id, data: this.treeFactory.getNodeById(id) }); } onBlurEdit(treeNode) { if (!treeNode.data.errTips) { treeNode.data.editable = false; return this.postEditNode(treeNode); } } onInputChange(event, treeNode) { const targe = event.target; this.editValueChange.emit({ value: targe.value, callback: (validateInfo) => { if (validateInfo && validateInfo.errTips) { treeNode.data.errTips = validateInfo.errTips; treeNode.data.errTipsPosition = validateInfo.errTipsPosition || 'top'; } else { if (treeNode.data.errTips) { delete treeNode.data.errTips; delete treeNode.data.errTipsPosition; } } }, }); } treeNodeHover(treeNode, type) { if (this.disableMouseEvent || treeNode.data.disableMouseEvent) { return; } treeNode.data.isHover = type === 'enter'; } isSelectedNode(node) { if (this.checkable) { return node.isChecked; } else { return node.isActive; } } get nodes() { return this.operableTree ? Object.keys(this.treeFactory.nodes).map((k) => this.treeFactory.nodes[k]) : []; } postEditNode(treeNode) { if (this.addingNode === true) { this.addingNode = false; if (typeof this.postAddNode === 'function') { const originalId = treeNode.id; return this.postAddNode(treeNode) .then((nodeInfo) => { // Swap id if id was modified by outer system treeNode.id = nodeInfo.id ? nodeInfo.id : originalId; delete this.treeFactory.nodes[originalId]; if (Object.prototype.hasOwnProperty.call(nodeInfo, 'data') && nodeInfo.data) { if (Object.prototype.hasOwnProperty.call(treeNode, 'data') && Object.prototype.hasOwnProperty.call(treeNode.data, 'data')) { treeNode.data.data = { ...treeNode.data.data, ...nodeInfo.data }; } else { treeNode.data = Object.assign(treeNode.data, { data: nodeInfo.data }); } } this.treeFactory.nodes[treeNode.id] = treeNode; return treeNode; }) .catch((e, reaction = 'cancel') => { switch (reaction) { case 'justify': { const parentNode = this.treeFactory.nodes[treeNode.parentId]; const title = treeNode.data.title; this.treeFactory.deleteNodeById(treeNode.id); this.addChildNode(null, parentNode, { title: title }); break; } case 'cancel': default: this.treeFactory.deleteNodeById(treeNode.id); } return Promise.reject(e); }); } } else { this.nodeEdited.emit(treeNode); } return Promise.resolve(treeNode); } 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, checkboxDisabledKey: this.checkboxDisabledKey, selectDisabledKey: this.selectDisabledKey, toggleDisabledKey: this.toggleDisabledKey, treeNodeTitleKey: this.treeNodeTitleKey, }); } get treeFactory() { return this.operableTree.treeFactory; } checkNode(checked, treeNode) { if (!treeNode.data.disabled) { treeNode.data.isChecked = !treeNode.data.isChecked; this.checkNodeById(treeNode.data.isChecked, treeNode.id); } } nodeDblClick(event, node) { this.nodeDblClicked.emit(node); } initTreeFinishEvent($event) { this.afterTreeInit.emit($event); } eventTriggerBlur(event) { event.target.blur(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OperableTreeComponent, deps: [{ token: DOCUMENT }, { token: i1.I18nService }, { token: i2.DevConfigService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: OperableTreeComponent, selector: "d-operable-tree", inputs: { tree: "tree", treeNodeIdKey: "treeNodeIdKey", treeNodeChildrenKey: "treeNodeChildrenKey", checkboxDisabledKey: "checkboxDisabledKey", selectDisabledKey: "selectDisabledKey", toggleDisabledKey: "toggleDisabledKey", iconParentOpen: "iconParentOpen", iconParentClose: "iconParentClose", iconLeaf: "iconLeaf", showLoading: "showLoading", loadingTemplateRef: "loadingTemplateRef", treeNodesRef: "treeNodesRef", checkable: "checkable", deletable: "deletable", addable: "addable", editable: "editable", draggable: "draggable", dropFromOutside: "dropFromOutside", disableMouseEvent: "disableMouseEvent", checkboxInput: "checkboxInput", beforeAddNode: "beforeAddNode", beforeDeleteNode: "beforeDeleteNode", beforeEditNode: "beforeEditNode", beforeSelectNode: "beforeSelectNode", beforeNodeDrop: "beforeNodeDrop", canActivateNode: "canActivateNode", canActivateParentNode: "canActivateParentNode", canActivateMultipleNode: "canActivateMultipleNode", treeNodeTitleKey: "treeNodeTitleKey", postAddNode: "postAddNode", iconTemplatePosition: "iconTemplatePosition", virtualScroll: "virtualScroll", virtualScrollHeight: "virtualScrollHeight", showAnimation: "showAnimation", itemSize: "itemSize", minBufferPx: "minBufferPx", maxBufferPx: "maxBufferPx", checkableRelation: "checkableRelation", indent: "indent", canIdEmpty: "canIdEmpty", operatorAlign: "operatorAlign", dropType: "dropType" }, outputs: { nodeSelected: "nodeSelected", nodeDblClicked: "nodeDblClicked", nodeRightClicked: "nodeRightClicked", nodeToggled: "nodeToggled", afterTreeInit: "afterTreeInit", nodeDeleted: "nodeDeleted", nodeChecked: "nodeChecked", currentNodeChecked: "currentNodeChecked", nodeEdited: "nodeEdited", editValueChange: "editValueChange", nodeDragStart: "nodeDragStart", nodeOnDrop: "nodeOnDrop" }, host: { listeners: { "dragend": "onDragend()" } }, queries: [{ propertyName: "iconTemplate", first: true, predicate: ["iconTemplate"], descendants: true }, { propertyName: "nodeTemplate", first: true, predicate: ["nodeTemplate"], descendants: true }, { propertyName: "operatorTemplate", first: true, predicate: ["operatorTemplate"], descendants: true }, { propertyName: "statusTemplate", first: true, predicate: ["statusTemplate"], descendants: true }], viewQueries: [{ propertyName: "operableTree", first: true, predicate: ["operableTree"], descendants: true, static: true }, { propertyName: "operableTreeEle", first: true, predicate: ["operableTreeContainer"], descendants: true, static: true }, { propertyName: "treeDropIndicator", first: true, predicate: ["treeDropIndicator"], descendants: true }, { propertyName: "treeNodeContent", predicate: ["treeNodeContent"], descendants: true }], exportAs: ["dOperableTreeComponent"], ngImport: i0, template: "<div #operableTreeContainer style=\"position: relative\">\n <d-tree\n #operableTree\n [tree]=\"tree\"\n [virtualScroll]=\"virtualScroll\"\n [treeNodeIdKey]=\"treeNodeIdKey\"\n [treeNodeChildrenKey]=\"treeNodeChildrenKey\"\n [treeNodesRef]=\"treeNodesRef ? treeNodesRef : virtualScroll ? operableTreeVirtualScrollRef : operableTreeNodeRef\"\n [iconParentOpen]=\"iconParentOpen\"\n [iconParentClose]=\"iconParentClose\"\n [iconLeaf]=\"iconLeaf\"\n [treeNodeTitleKey]=\"treeNodeTitleKey\"\n [checkboxDisabledKey]=\"checkboxDisabledKey\"\n [selectDisabledKey]=\"selectDisabledKey\"\n [toggleDisabledKey]=\"toggleDisabledKey\"\n [virtualScrollHeight]=\"virtualScrollHeight\"\n [minBufferPx]=\"minBufferPx\"\n [maxBufferPx]=\"maxBufferPx\"\n [itemSize]=\"itemSize\"\n [indent]=\"indent\"\n [canIdEmpty]=\"canIdEmpty\"\n (afterTreeInit)=\"initTreeFinishEvent($event)\"\n >\n </d-tree>\n <div\n *ngIf=\"dragState.showIndicator\"\n #treeDropIndicator\n class=\"devui-tree-indicator\"\n [style.top.px]=\"dragState.indicatorTop\"\n [style.width.px]=\"dragState.indicatorWidth\"\n [style.left.px]=\"dragState.indicatorLeft\"\n ></div>\n</div>\n<!-- TODO: \u865A\u62DF\u6EDA\u52A8\u652F\u6301\u52A8\u6548 -->\n<ng-template #operableTreeVirtualScrollRef let-treeNode=\"treeNode\" let-treeFactory=\"treeFactory\">\n <div\n *ngIf=\"!treeNode.data.isHide\"\n [id]=\"'devui-tree-node-' + treeNode.id\"\n class=\"devui-tree-node devui-operable-tree-node\"\n [style.paddingLeft.px]=\"treeNode.data.depth * 24\"\n [ngClass]=\"{\n selected: isSelectedNode(treeNode.data),\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 (mouseenter)=\"treeNodeHover(treeNode, 'enter')\"\n (mouseleave)=\"treeNodeHover(treeNode, 'leave')\"\n [draggable]=\"draggable && !treeNode?.data?.editable\"\n (dragstart)=\"onDragstart($event, treeNode)\"\n (dragover)=\"onDragover($event, draggable, treeNode)\"\n (dragleave)=\"onDragleave($event, treeNode)\"\n (drop)=\"onDrop($event, treeNode)\"\n [class.disabled]=\"treeNode.data.disabled\"\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\n class=\"devui-tree-node__folder--icon\"\n [ngStyle]=\"{\n display: iconParentClose && !treeNode.data.isOpen ? 'inline-block' : 'none'\n }\"\n [innerHTML]=\"iconParentClose | safe : 'html'\"\n ></span>\n <span\n class=\"devui-tree-node__folder--icon\"\n [ngStyle]=\"{\n display: iconParentOpen && treeNode.data.isOpen ? 'inline-block' : 'none'\n }\"\n [innerHTML]=\"iconParentOpen | safe : 'html'\"\n ></span>\n <span\n class=\"devui-tree-node__folder--icon\"\n [ngStyle]=\"{\n display: !iconParentOpen && !treeNode.data.isOpen ? 'inline-block' : 'none'\n }\"\n >\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\n class=\"devui-tree-node__folder--icon\"\n [ngStyle]=\"{\n display: !iconParentOpen && treeNode.data.isOpen ? 'inline-block' : 'none'\n }\"\n >\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 | safe : 'html'\"></span>\n </span>\n <ng-container *ngIf=\"iconTemplate && iconTemplatePosition === 'before-checkbox'\">\n <ng-template [ngTemplateOutlet]=\"iconTemplate\" [ngTemplateOutletContext]=\"{ node: treeNode.data, completeNode: treeNode }\">\n </ng-template>\n </ng-container>\n <d-checkbox\n *ngIf=\"checkable && (treeNode.data.showCheckbox === undefined ? true : treeNode.data.showCheckbox)\"\n [showAnimation]=\"treeNode.data.isHover\"\n [(ngModel)]=\"treeNode.data.isChecked\"\n (ngModelChange)=\"checkNodeById($event, treeNode.id)\"\n [halfchecked]=\"treeNode.data.halfChecked\"\n [color]=\"checkboxInput?.color\"\n [cssClass]=\"'devui-tree-node__checkbox'\"\n [disabled]=\"treeNode.data.disabled\"\n [showGlowStyle]=\"!virtualScroll\"\n >\n </d-checkbox>\n <ng-container *ngIf=\"iconTemplate && (!iconTemplatePosition || iconTemplatePosition === 'after-checkbox')\">\n <ng-template [ngTemplateOutlet]=\"iconTemplate\" [ngTemplateOutletContext]=\"{ node: treeNode.data, completeNode: treeNode }\">\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"nodeTemplate\">\n <ng-template [ngTemplateOutlet]=\"nodeTemplate\" [ngTemplateOutletContext]=\"{ node: treeNode.data, completeNode: treeNode }\">\n </ng-template>\n </ng-container>\n <span\n *ngIf=\"!nodeTemplate && !treeNode.data?.editable\"\n (dblclick)=\"nodeDblClick($event, treeNode)\"\n (contextmenu)=\"contextmenuEvent($event, treeNode)\"\n class=\"devui-tree-node__title devui-tree-drag-handle\"\n [class.select-disabled]=\"treeNode.data.disableSelect\"\n title=\"{{ treeNode.data.title }}\"\n >\n <d-highlight\n *ngIf=\"treeNode.data?.isMatch && !treeNode.data?.isCustomSearch\"\n [value]=\"treeNode.data.title\"\n [term]=\"treeFactory.searchItem\"\n >\n </d-highlight>\n <span *ngIf=\"treeNode.data?.isMatch && treeNode.data?.isCustomSearch\" class=\"devui-tree-node-highlight\">{{\n treeNode.data.title\n }}</span>\n <ng-container *ngIf=\"!treeNode.data?.isMatch\">{{ treeNode.data.title }}</ng-container>\n </span>\n <span class=\"devui-tree-node__edit\" *ngIf=\"!nodeTemplate && treeNode.data?.editable\">\n <input\n [class.error]=\"!!treeNode.data.errTips\"\n class=\"devui-form-control devui-input-sm\"\n type=\"text\"\n dTreeAutoFocus\n [(ngModel)]=\"treeNode.data.title\"\n (blur)=\"onBlurEdit(treeNode)\"\n (keyup.enter)=\"eventTriggerBlur($event)\"\n (input)=\"onInputChange($event, treeNode)\"\n dPopover\n [content]=\"treeNode.data.errTips\"\n [position]=\"treeNode.data.errTipsPosition\"\n [appendToBody]=\"true\"\n [visible]=\"!!treeNode.data.errTips\"\n [popType]=\"'error'\"\n />\n </span>\n <ng-container *ngIf=\"statusTemplate\">\n <ng-template [ngTemplateOutlet]=\"statusTemplate\" [ngTemplateOutletContext]=\"{ node: treeNode }\"> </ng-template>\n </ng-container>\n <span\n dLoading\n [showLoading]=\"treeNode.data.loading\"\n [loadingTemplateRef]=\"loadingTemplateRef ? loadingTemplateRef : defaultLoadingTmpl\"\n >\n </span>\n <div class=\"devui-tree-icons-container\">\n <span\n class=\"devui-tree-icons\"\n *ngIf=\"!treeNode.data.disableAdd && (treeNode.data.isActive || treeNode.data.isHover) && addable\"\n (click)=\"addChildNode($event, treeNode)\"\n >\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 >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <g fill-rule=\"nonzero\">\n <path\n d=\"M8,0 C12.418278,0 16,3.59071231 16,8 C16,12.418278 12.4092877,16 8,16 C3.581722,16 0,12.4092877 0,8 C0,3.581722 3.59071231,0 8,0 Z M8,1.5 C4.41577399,1.5 1.5,4.41351238 1.5,8 C1.5,11.584226 4.41351238,14.5 8,14.5 C11.584226,14.5 14.5,11.5864876 14.5,8 C14.5,4.41577399 11.5864876,1.5 8,1.5 Z M8,4.3 C8.38659932,4.3 8.7,4.61340068 8.7,5 L8.699,7.299 L11,7.3 C11.3865993,7.3 11.7,7.61340068 11.7,8 C11.7,8.38659932 11.3865993,8.7 11,8.7 L8.699,8.699 L8.7,11 C8.7,11.3865993 8.38659932,11.7 8,11.7 C7.61340068,11.7 7.3,11.3865993 7.3,11 L7.299,8.699 L5,8.7 C4.61340068,8.7 4.3,8.38659932 4.3,8 C4.3,7.61340068 4.61340068,7.3 5,7.3 L7.299,7.299 L7.3,5 C7.3,4.61340068 7.61340068,4.3 8,4.3 Z\"\n ></path>\n </g>\n </g>\n </svg>\n </span>\n <span\n class=\"devui-tree-icons\"\n *ngIf=\"!treeNode.data.disableEdit && (treeNode.data.isActive || treeNode.data.isHover) && editable\"\n (click)=\"editNode($event, treeNode)\"\n >\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 >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <path\n d=\"M12.3087741,0.74795609 L14.4300944,2.86927643 C14.9182498,3.3574318 14.9182498,4.14888802 14.4300944,4.63704339 L6.76529422,12.3018436 C6.6135568,12.453581 6.42531143,12.5637003 6.21868103,12.6215999 L3.29553945,13.4406895 C2.63078755,13.6269587 1.9408986,13.2390719 1.75462937,12.57432 C1.69358229,12.356457 1.69281139,12.1261099 1.75239888,11.9078433 L2.55548559,8.96616788 C2.61274771,8.75641893 2.7237298,8.5652334 2.87747239,8.41149081 L10.5410071,0.74795609 C11.0291625,0.259800726 11.8206187,0.259800726 12.3087741,0.74795609 Z M11.4248906,1.98539296 L3.98505387,9.42522967 L3.31563544,11.8772834 L5.75092262,11.1948948 L13.1926575,3.75315991 L11.4248906,1.98539296 Z\"\n fill-rule=\"nonzero\"\n ></path>\n <rect fill-rule=\"nonzero\" x=\"0\" y=\"14.5\" width=\"15\" height=\"1.5\" rx=\"0.75\"></rect>\n </g>\n </svg>\n </span>\n <span\n class=\"devui-tree-icons\"\n *ngIf=\"!treeNode.data.disableDelete && (treeNode.data.isActive || treeNode.data.isHover) && deletable\"\n (click)=\"deleteNodes($event, treeNode)\"\n >\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 >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <g fill-rule=\"nonzero\">\n <path\n d=\"M13.0996399,1.27743711 C13.4935963,0.882043175 14.1784277,0.912988481 14.6292951,1.34651479 C15.0800797,1.78012228 15.1261817,2.45212782 14.7322253,2.84752174 L9.59690531,7.99860255 L14.7322253,13.1524782 C15.0903675,13.5119272 15.084825,14.0999809 14.7421763,14.5297048 L14.6292951,14.6534852 C14.1784277,15.0870115 13.4935963,15.1179567 13.0996399,14.7225628 L8,9.60284628 L2.90036004,14.7225628 C2.54221791,15.082012 1.94368316,15.0891145 1.49907515,14.7617204 L1.37070487,14.6534852 C0.919920289,14.2198777 0.873818322,13.5478721 1.26777466,13.1524782 L6.40166885,7.99860255 L1.26777466,2.84752174 C0.909632535,2.48807271 0.915174959,1.90001906 1.25782365,1.47029514 L1.37070487,1.34651479 C1.82157229,0.912988481 2.50640369,0.882043175 2.90036004,1.27743711 L8,6.39575625 L13.0996399,1.27743711 Z\"\n ></path>\n </g>\n </g>\n </svg>\n </span>\n <ng-container *ngIf=\"operatorTemplate && (treeNode.data.isActive || treeNode.data.isHover)\">\n <ng-template\n [ngTemplateOutlet]=\"operatorTemplate\"\n [ngTemplateOutletContext]=\"{\n $implicit: this,\n node: treeNode,\n addNode: addChildNodeProxy,\n editNode: editNodeProxy,\n deleteNode: deleteNodesProxy\n }\"\n >\n </ng-template>\n </ng-container>\n </div>\n </div>\n </div>\n </div>\n</ng-template>\n<ng-template #operableTreeNodeRef let-treeNode=\"treeNode\" let-treeFactory=\"treeFactory\">\n <div\n class=\"devui-tree-node devui-operable-tree-node devui-tree-without-virtual-scroll\"\n [style.display]=\"treeNode.data.isHide ? 'none' : ''\"\n [ngClass]=\"{\n selected: isSelectedNode(treeNode.data),\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 (mouseenter)=\"treeNodeHover(treeNode, 'enter')\"\n (mouseleave)=\"treeNodeHover(treeNode, 'leave')\"\n [draggable]=\"draggable && !treeNode?.data?.editable\"\n (dragstart)=\"onDragstart($event, treeNode)\"\n (dragover)=\"onDragover($event, draggable, treeNode)\"\n (dragleave)=\"onDragleave($event, treeNode)\"\n (drop)=\"onDrop($event, treeNode)\"\n [class.disabled]=\"treeNode.data.disabled\"\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\n class=\"devui-tree-node__folder--icon\"\n [ngStyle]=\"{\n display: iconParentClose && !treeNode.data.isOpen ? 'inline-block' : 'none'\n }\"\n [innerHTML]=\"iconParentClose | safe : 'html'\"\n ></span>\n <span\n class=\"devui-tree-node__folder--icon\"\n [ngStyle]=\"{\n display: iconParentOpen && treeNode.data.isOpen ? 'inline-block' : 'none'\n }\"\n [innerHTML]=\"iconParentOpen | safe : 'html'\"\n ></span>\n <span\n class=\"devui-tree-node__folder--icon\"\n [ngStyle]=\"{\n display: !iconParentOpen && !treeNode.data.isOpen ? 'inline-block' : 'none'\n }\"\n >\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\n class=\"devui-tree-node__folder--icon\"\n [ngStyle]=\"{\n display: !iconParentOpen && treeNode.data.isOpen ? 'inline-block' : 'none'\n }\"\n >\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 | safe : 'html'\"></span>\n </span>\n <ng-container *ngIf=\"iconTemplate && iconTemplatePosition === 'before-checkbox'\">\n <ng-template [ngTemplateOutlet]=\"iconTemplate\" [ngTemplateOutletContext]=\"{ node: treeNode.data, completeNode: treeNode }\">\n </ng-template>\n </ng-container>\n <d-checkbox\n *ngIf=\"checkable && checkable && (treeNode.data.showCheckbox === undefined ? true : treeNode.data.showCheckbox)\"\n [(ngModel)]=\"treeNode.data.isChecked\"\n (ngModelChange)=\"checkNodeById($event, treeNode.id)\"\n [halfchecked]=\"treeNode.data.halfChecked\"\n [color]=\"checkboxInput?.color\"\n [cssClass]=\"'devui-tree-node__checkbox'\"\n [disabled]=\"treeNode.data.disabled\"\n [showGlowStyle]=\"!virtualScroll\"\n >\n </d-checkbox>\n <ng-container *ngIf=\"iconTemplate && (!iconTemplatePosition || iconTemplatePosition === 'after-checkbox')\">\n <ng-template [ngTemplateOutlet]=\"iconTemplate\" [ngTemplateOutletContext]=\"{ node: treeNode.data, completeNode: treeNode }\">\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"nodeTemplate\">\n <ng-template [ngTemplateOutlet]=\"nodeTemplate\" [ngTemplateOutletContext]=\"{ node: treeNode.data, completeNode: treeNode }\">\n </ng-template>\n </ng-container>\n <span\n *ngIf=\"!nodeTemplate && !treeNode.data?.editable\"\n (dblclick)=\"nodeDblClick($event, treeNode)\"\n (contextmenu)=\"contextmenuEvent($event, treeNode)\"\n class=\"devui-tree-node__title devui-tree-drag-handle\"\n [class.select-disabled]=\"treeNode.data.disableSelect\"\n title=\"{{ treeNode.data.title }