UNPKG

ng-zorro-antd

Version:

An enterprise-class UI components based on Ant Design and Angular

963 lines (954 loc) 31.4 kB
import * as i0 from '@angular/core'; import { Injectable, InjectionToken } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; /** * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ class NzTreeNode { /** * Init nzTreeNode * * @param option: user's input * @param parent * @param service: base nzTreeService */ constructor(option, parent = null, service = null) { this._title = ''; this.level = 0; // Parent Node this.parentNode = null; this._icon = ''; this._children = []; this._isLeaf = false; this._isChecked = false; this._isSelectable = false; this._isDisabled = false; this._isDisableCheckbox = false; this._isExpanded = false; this._isHalfChecked = false; this._isSelected = false; this._isLoading = false; this.canHide = false; this.isMatched = false; this.service = null; if (option instanceof NzTreeNode) { return option; } this.service = service || null; this.origin = option; this.key = option.key; this.parentNode = parent; this._title = option.title || '---'; this._icon = option.icon || ''; this._isLeaf = option.isLeaf || false; this._children = []; // option params this._isChecked = option.checked || false; this._isSelectable = option.disabled || option.selectable !== false; this._isDisabled = option.disabled || false; this._isDisableCheckbox = option.disableCheckbox || false; this._isExpanded = option.isLeaf ? false : option.expanded || false; this._isHalfChecked = false; this._isSelected = (!option.disabled && option.selected) || false; this._isLoading = false; this.isMatched = false; /** * parent's checked status will affect children while initializing */ if (parent) { this.level = parent.level + 1; } else { this.level = 0; } if (typeof option.children !== 'undefined' && option.children !== null) { option.children.forEach(nodeOptions => { const s = this.treeService; if (s && !s.isCheckStrictly && option.checked && !option.disabled && !nodeOptions.disabled && !nodeOptions.disableCheckbox) { nodeOptions.checked = option.checked; } this._children.push(new NzTreeNode(nodeOptions, this)); }); } } get treeService() { return this.service || (this.parentNode && this.parentNode.treeService); } /** * auto generate * get * set */ get title() { return this._title; } set title(value) { this._title = value; this.update(); } get icon() { return this._icon; } set icon(value) { this._icon = value; this.update(); } get children() { return this._children; } set children(value) { this._children = value; this.update(); } get isLeaf() { return this._isLeaf; } set isLeaf(value) { this._isLeaf = value; this.update(); } get isChecked() { return this._isChecked; } set isChecked(value) { this._isChecked = value; this.origin.checked = value; this.afterValueChange('isChecked'); } get isHalfChecked() { return this._isHalfChecked; } set isHalfChecked(value) { this._isHalfChecked = value; this.afterValueChange('isHalfChecked'); } get isSelectable() { return this._isSelectable; } set isSelectable(value) { this._isSelectable = value; this.update(); } get isDisabled() { return this._isDisabled; } set isDisabled(value) { this._isDisabled = value; this.update(); } get isDisableCheckbox() { return this._isDisableCheckbox; } set isDisableCheckbox(value) { this._isDisableCheckbox = value; this.update(); } get isExpanded() { return this._isExpanded; } set isExpanded(value) { this._isExpanded = value; this.origin.expanded = value; this.afterValueChange('isExpanded'); this.afterValueChange('reRender'); } get isSelected() { return this._isSelected; } set isSelected(value) { this._isSelected = value; this.origin.selected = value; this.afterValueChange('isSelected'); } get isLoading() { return this._isLoading; } set isLoading(value) { this._isLoading = value; this.update(); } setSyncChecked(checked = false, halfChecked = false) { this.setChecked(checked, halfChecked); if (this.treeService && !this.treeService.isCheckStrictly) { this.treeService.conduct(this); } } setChecked(checked = false, halfChecked = false) { this.origin.checked = checked; this.isChecked = checked; this.isHalfChecked = halfChecked; } setExpanded(value) { this._isExpanded = value; this.origin.expanded = value; this.afterValueChange('isExpanded'); } getParentNode() { return this.parentNode; } getChildren() { return this.children; } /** * Support appending child nodes by position. Leaf node cannot be appended. */ addChildren(children, childPos = -1) { if (!this.isLeaf) { children.forEach(node => { const refreshLevel = (n) => { n.getChildren().forEach(c => { c.level = c.getParentNode().level + 1; // flush origin c.origin.level = c.level; refreshLevel(c); }); }; let child = node; if (child instanceof NzTreeNode) { child.parentNode = this; } else { child = new NzTreeNode(node, this); } child.level = this.level + 1; child.origin.level = child.level; refreshLevel(child); try { childPos === -1 ? this.children.push(child) : this.children.splice(childPos, 0, child); // flush origin } catch (e) { } }); this.origin.children = this.getChildren().map(v => v.origin); // remove loading state this.isLoading = false; } this.afterValueChange('addChildren'); this.afterValueChange('reRender'); } clearChildren() { // refresh checked state this.afterValueChange('clearChildren'); this.children = []; this.origin.children = []; this.afterValueChange('reRender'); } remove() { const parentNode = this.getParentNode(); if (parentNode) { parentNode.children = parentNode.getChildren().filter(v => v.key !== this.key); parentNode.origin.children = parentNode.origin.children.filter(v => v.key !== this.key); this.afterValueChange('remove'); this.afterValueChange('reRender'); } } afterValueChange(key) { if (this.treeService) { switch (key) { case 'isChecked': this.treeService.setCheckedNodeList(this); break; case 'isHalfChecked': this.treeService.setHalfCheckedNodeList(this); break; case 'isExpanded': this.treeService.setExpandedNodeList(this); break; case 'isSelected': this.treeService.setNodeActive(this); break; case 'clearChildren': this.treeService.afterRemove(this.getChildren()); break; case 'remove': this.treeService.afterRemove([this]); break; case 'reRender': this.treeService.flattenTreeData(this.treeService.rootNodes, this.treeService.getExpandedNodeList().map(v => v.key)); break; } } this.update(); } update() { if (this.component) { this.component.markForCheck(); } } } /** * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ function isCheckDisabled(node) { const { isDisabled, isDisableCheckbox } = node; return !!(isDisabled || isDisableCheckbox); } function isInArray(needle, haystack) { return haystack.length > 0 && haystack.indexOf(needle) > -1; } function getPosition(level, index) { return `${level}-${index}`; } function getKey(key, pos) { if (key !== null && key !== undefined) { return key; } return pos; } /** * Flat nest tree data into flatten list. This is used for virtual list render. * * @param treeNodeList Origin data node list * @param expandedKeys * need expanded keys, provides `true` means all expanded (used in `rc-tree-select`). */ function flattenTreeData(treeNodeList = [], expandedKeys = []) { const expandedKeySet = new Set(expandedKeys === true ? [] : expandedKeys); const flattenList = []; function dig(list, parent = null) { return list.map((treeNode, index) => { const pos = getPosition(parent ? parent.pos : '0', index); const mergedKey = getKey(treeNode.key, pos); treeNode.isStart = [...(parent ? parent.isStart : []), index === 0]; treeNode.isEnd = [...(parent ? parent.isEnd : []), index === list.length - 1]; // Add FlattenDataNode into list // TODO: only need data here. const flattenNode = { parent, pos, children: [], data: treeNode, isStart: [...(parent ? parent.isStart : []), index === 0], isEnd: [...(parent ? parent.isEnd : []), index === list.length - 1] }; flattenList.push(flattenNode); // Loop treeNode children if (expandedKeys === true || expandedKeySet.has(mergedKey) || treeNode.isExpanded) { flattenNode.children = dig(treeNode.children || [], flattenNode); } else { flattenNode.children = []; } return flattenNode; }); } dig(treeNodeList); return flattenList; } /** * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ class NzTreeBaseService { constructor() { this.DRAG_SIDE_RANGE = 0.25; this.DRAG_MIN_GAP = 2; this.isCheckStrictly = false; this.isMultiple = false; this.rootNodes = []; this.flattenNodes$ = new BehaviorSubject([]); this.selectedNodeList = []; this.expandedNodeList = []; this.checkedNodeList = []; this.halfCheckedNodeList = []; this.matchedNodeList = []; } /** * reset tree nodes will clear default node list */ initTree(nzNodes) { this.rootNodes = nzNodes; this.expandedNodeList = []; this.selectedNodeList = []; this.halfCheckedNodeList = []; this.checkedNodeList = []; this.matchedNodeList = []; } flattenTreeData(nzNodes, expandedKeys = []) { this.flattenNodes$.next(flattenTreeData(nzNodes, expandedKeys).map(item => item.data)); } getSelectedNode() { return this.selectedNode; } /** * get some list */ getSelectedNodeList() { return this.conductNodeState('select'); } /** * return checked nodes */ getCheckedNodeList() { return this.conductNodeState('check'); } getHalfCheckedNodeList() { return this.conductNodeState('halfCheck'); } /** * return expanded nodes */ getExpandedNodeList() { return this.conductNodeState('expand'); } /** * return search matched nodes */ getMatchedNodeList() { return this.conductNodeState('match'); } isArrayOfNzTreeNode(value) { return value.every(item => item instanceof NzTreeNode); } /** * set drag node */ setSelectedNode(node) { this.selectedNode = node; } /** * set node selected status */ setNodeActive(node) { if (!this.isMultiple && node.isSelected) { this.selectedNodeList.forEach(n => { if (node.key !== n.key) { // reset other nodes n.isSelected = false; } }); // single mode: remove pre node this.selectedNodeList = []; } this.setSelectedNodeList(node, this.isMultiple); } /** * add or remove node to selectedNodeList */ setSelectedNodeList(node, isMultiple = false) { const index = this.getIndexOfArray(this.selectedNodeList, node.key); if (isMultiple) { if (node.isSelected && index === -1) { this.selectedNodeList.push(node); } } else { if (node.isSelected && index === -1) { this.selectedNodeList = [node]; } } if (!node.isSelected) { this.selectedNodeList = this.selectedNodeList.filter(n => n.key !== node.key); } } /** * merge checked nodes */ setHalfCheckedNodeList(node) { const index = this.getIndexOfArray(this.halfCheckedNodeList, node.key); if (node.isHalfChecked && index === -1) { this.halfCheckedNodeList.push(node); } else if (!node.isHalfChecked && index > -1) { this.halfCheckedNodeList = this.halfCheckedNodeList.filter(n => node.key !== n.key); } } setCheckedNodeList(node) { const index = this.getIndexOfArray(this.checkedNodeList, node.key); if (node.isChecked && index === -1) { this.checkedNodeList.push(node); } else if (!node.isChecked && index > -1) { this.checkedNodeList = this.checkedNodeList.filter(n => node.key !== n.key); } } /** * conduct checked/selected/expanded keys */ conductNodeState(type = 'check') { let resultNodesList = []; switch (type) { case 'select': resultNodesList = this.selectedNodeList; break; case 'expand': resultNodesList = this.expandedNodeList; break; case 'match': resultNodesList = this.matchedNodeList; break; case 'check': resultNodesList = this.checkedNodeList; const isIgnore = (node) => { const parentNode = node.getParentNode(); if (parentNode) { if (this.checkedNodeList.findIndex(n => n.key === parentNode.key) > -1) { return true; } else { return isIgnore(parentNode); } } return false; }; // merge checked if (!this.isCheckStrictly) { resultNodesList = this.checkedNodeList.filter(n => !isIgnore(n)); } break; case 'halfCheck': if (!this.isCheckStrictly) { resultNodesList = this.halfCheckedNodeList; } break; } return resultNodesList; } /** * set expanded nodes */ setExpandedNodeList(node) { if (node.isLeaf) { return; } const index = this.getIndexOfArray(this.expandedNodeList, node.key); if (node.isExpanded && index === -1) { this.expandedNodeList.push(node); } else if (!node.isExpanded && index > -1) { this.expandedNodeList.splice(index, 1); } } setMatchedNodeList(node) { const index = this.getIndexOfArray(this.matchedNodeList, node.key); if (node.isMatched && index === -1) { this.matchedNodeList.push(node); } else if (!node.isMatched && index > -1) { this.matchedNodeList.splice(index, 1); } } /** * check state * * @param isCheckStrictly */ refreshCheckState(isCheckStrictly = false) { if (isCheckStrictly) { return; } this.checkedNodeList.forEach(node => { this.conduct(node, isCheckStrictly); }); } // reset other node checked state based current node conduct(node, isCheckStrictly = false) { const isChecked = node.isChecked; if (node && !isCheckStrictly) { this.conductUp(node); this.conductDown(node, isChecked); } } /** * 1、children half checked * 2、children all checked, parent checked * 3、no children checked */ conductUp(node) { const parentNode = node.getParentNode(); if (parentNode) { if (!isCheckDisabled(parentNode)) { if (parentNode.children.every(child => isCheckDisabled(child) || (!child.isHalfChecked && child.isChecked))) { parentNode.isChecked = true; parentNode.isHalfChecked = false; } else if (parentNode.children.some(child => child.isHalfChecked || child.isChecked)) { parentNode.isChecked = false; parentNode.isHalfChecked = true; } else { parentNode.isChecked = false; parentNode.isHalfChecked = false; } } this.setCheckedNodeList(parentNode); this.setHalfCheckedNodeList(parentNode); this.conductUp(parentNode); } } /** * reset child check state */ conductDown(node, value) { if (!isCheckDisabled(node)) { node.isChecked = value; node.isHalfChecked = false; this.setCheckedNodeList(node); this.setHalfCheckedNodeList(node); node.children.forEach(n => { this.conductDown(n, value); }); } } /** * flush after delete node */ afterRemove(nodes) { // to reset selectedNodeList & expandedNodeList const loopNode = (node) => { // remove selected node this.selectedNodeList = this.selectedNodeList.filter(n => n.key !== node.key); // remove expanded node this.expandedNodeList = this.expandedNodeList.filter(n => n.key !== node.key); // remove checked node this.checkedNodeList = this.checkedNodeList.filter(n => n.key !== node.key); if (node.children) { node.children.forEach(child => { loopNode(child); }); } }; nodes.forEach(n => { loopNode(n); }); this.refreshCheckState(this.isCheckStrictly); } /** * drag event */ refreshDragNode(node) { if (node.children.length === 0) { // until root this.conductUp(node); } else { node.children.forEach(child => { this.refreshDragNode(child); }); } } // reset node level resetNodeLevel(node) { const parentNode = node.getParentNode(); if (parentNode) { node.level = parentNode.level + 1; } else { node.level = 0; } for (const child of node.children) { this.resetNodeLevel(child); } } calcDropPosition(event) { const { clientY } = event; // to fix firefox undefined const { top, bottom, height } = event.target.getBoundingClientRect(); const des = Math.max(height * this.DRAG_SIDE_RANGE, this.DRAG_MIN_GAP); if (clientY <= top + des) { return -1; } else if (clientY >= bottom - des) { return 1; } return 0; } /** * drop * 0: inner -1: pre 1: next */ dropAndApply(targetNode, dragPos = -1) { if (!targetNode || dragPos > 1) { return; } const treeService = targetNode.treeService; const targetParent = targetNode.getParentNode(); const isSelectedRootNode = this.selectedNode.getParentNode(); // remove the dragNode if (isSelectedRootNode) { isSelectedRootNode.children = isSelectedRootNode.children.filter(n => n.key !== this.selectedNode.key); } else { this.rootNodes = this.rootNodes.filter(n => n.key !== this.selectedNode.key); } switch (dragPos) { case 0: targetNode.addChildren([this.selectedNode]); this.resetNodeLevel(targetNode); break; case -1: case 1: const tIndex = dragPos === 1 ? 1 : 0; if (targetParent) { targetParent.addChildren([this.selectedNode], targetParent.children.indexOf(targetNode) + tIndex); const parentNode = this.selectedNode.getParentNode(); if (parentNode) { this.resetNodeLevel(parentNode); } } else { const targetIndex = this.rootNodes.indexOf(targetNode) + tIndex; // Insert root node. this.rootNodes.splice(targetIndex, 0, this.selectedNode); this.rootNodes[targetIndex].parentNode = null; this.resetNodeLevel(this.rootNodes[targetIndex]); } break; } // flush all nodes this.rootNodes.forEach(child => { if (!child.treeService) { child.service = treeService; } this.refreshDragNode(child); }); } /** * emit Structure * eventName * node * event: MouseEvent / DragEvent * dragNode */ formatEvent(eventName, node, event) { const emitStructure = { eventName, node, event }; switch (eventName) { case 'dragstart': case 'dragenter': case 'dragover': case 'dragleave': case 'drop': case 'dragend': Object.assign(emitStructure, { dragNode: this.getSelectedNode() }); break; case 'click': case 'dblclick': Object.assign(emitStructure, { selectedKeys: this.selectedNodeList }); Object.assign(emitStructure, { nodes: this.selectedNodeList }); Object.assign(emitStructure, { keys: this.selectedNodeList.map(n => n.key) }); break; case 'check': const checkedNodeList = this.getCheckedNodeList(); Object.assign(emitStructure, { checkedKeys: checkedNodeList }); Object.assign(emitStructure, { nodes: checkedNodeList }); Object.assign(emitStructure, { keys: checkedNodeList.map(n => n.key) }); break; case 'search': Object.assign(emitStructure, { matchedKeys: this.getMatchedNodeList() }); Object.assign(emitStructure, { nodes: this.getMatchedNodeList() }); Object.assign(emitStructure, { keys: this.getMatchedNodeList().map(n => n.key) }); break; case 'expand': Object.assign(emitStructure, { nodes: this.expandedNodeList }); Object.assign(emitStructure, { keys: this.expandedNodeList.map(n => n.key) }); break; } return emitStructure; } /** * New functions for flatten nodes */ getIndexOfArray(list, key) { return list.findIndex(v => v.key === key); } /** * Render by nzCheckedKeys * When keys equals null, just render with checkStrictly * * @param keys * @param checkStrictly */ conductCheck(keys, checkStrictly) { this.checkedNodeList = []; this.halfCheckedNodeList = []; const calc = (nodes) => { nodes.forEach(node => { if (keys === null) { // render tree if no default checked keys found node.isChecked = !!node.origin.checked; } else { if (isInArray(node.key, keys || [])) { node.isChecked = true; node.isHalfChecked = false; } else { node.isChecked = false; node.isHalfChecked = false; } } if (node.children.length > 0) { calc(node.children); } }); }; calc(this.rootNodes); this.refreshCheckState(checkStrictly); } conductExpandedKeys(keys = []) { const expandedKeySet = new Set(keys === true ? [] : keys); this.expandedNodeList = []; const calc = (nodes) => { nodes.forEach(node => { node.setExpanded(keys === true || expandedKeySet.has(node.key) || node.isExpanded === true); if (node.isExpanded) { this.setExpandedNodeList(node); } if (node.children.length > 0) { calc(node.children); } }); }; calc(this.rootNodes); } conductSelectedKeys(keys, isMulti) { this.selectedNodeList.forEach(node => (node.isSelected = false)); this.selectedNodeList = []; const calc = (nodes) => nodes.every(node => { if (isInArray(node.key, keys)) { node.isSelected = true; this.setSelectedNodeList(node); if (!isMulti) { // if not support multi select return false; } } else { node.isSelected = false; } if (node.children.length > 0) { // Recursion return calc(node.children); } return true; }); calc(this.rootNodes); } /** * Expand parent nodes by child node * * @param node */ expandNodeAllParentBySearch(node) { const calc = (n) => { if (n) { n.canHide = false; n.setExpanded(true); this.setExpandedNodeList(n); if (n.getParentNode()) { return calc(n.getParentNode()); } } }; calc(node.getParentNode()); } } NzTreeBaseService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.5", ngImport: i0, type: NzTreeBaseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); NzTreeBaseService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.5", ngImport: i0, type: NzTreeBaseService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.5", ngImport: i0, type: NzTreeBaseService, decorators: [{ type: Injectable }] }); /** * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ const NzTreeHigherOrderServiceToken = new InjectionToken('NzTreeHigherOrder'); /** * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ class NzTreeBase { constructor(nzTreeService) { this.nzTreeService = nzTreeService; } /** * Coerces a value({@link any[]}) to a TreeNodes({@link NzTreeNode[]}) */ coerceTreeNodes(value) { let nodes = []; if (!this.nzTreeService.isArrayOfNzTreeNode(value)) { // has not been new NzTreeNode nodes = value.map(item => new NzTreeNode(item, null, this.nzTreeService)); } else { nodes = value.map((item) => { item.service = this.nzTreeService; return item; }); } return nodes; } /** * Get all nodes({@link NzTreeNode}) */ getTreeNodes() { return this.nzTreeService.rootNodes; } /** * Get {@link NzTreeNode} with key */ getTreeNodeByKey(key) { // flat tree nodes const nodes = []; const getNode = (node) => { nodes.push(node); node.getChildren().forEach(n => { getNode(n); }); }; this.getTreeNodes().forEach(n => { getNode(n); }); return nodes.find(n => n.key === key) || null; } /** * Get checked nodes(merged) */ getCheckedNodeList() { return this.nzTreeService.getCheckedNodeList(); } /** * Get selected nodes */ getSelectedNodeList() { return this.nzTreeService.getSelectedNodeList(); } /** * Get half checked nodes */ getHalfCheckedNodeList() { return this.nzTreeService.getHalfCheckedNodeList(); } /** * Get expanded nodes */ getExpandedNodeList() { return this.nzTreeService.getExpandedNodeList(); } /** * Get matched nodes(if nzSearchValue is not null) */ getMatchedNodeList() { return this.nzTreeService.getMatchedNodeList(); } } /** * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ /** * Generated bundle index. Do not edit. */ export { NzTreeBase, NzTreeBaseService, NzTreeHigherOrderServiceToken, NzTreeNode, flattenTreeData, getKey, getPosition, isCheckDisabled, isInArray }; //# sourceMappingURL=ng-zorro-antd-core-tree.mjs.map