import Node from './node'; import { getNodeKey, getPropertyFromData } from '../tool/util'; export default class TreeStore { constructor(options) { this.ready = false; this.currentNode = null; this.currentNodeKey = null; Object.assign(this, options); if (!this.key) { throw new Error('[Tree] nodeKey is required'); } this.nodesMap = {}; this.root = new Node({ data: this.data, store: this }); if (this.lazy && this.load) { const loadFn = this.load; loadFn(this.root, (data) => { this.root.doCreateChildren(data); this._initDefaultCheckedNodes(); this.ready = true; }); } else { this._initDefaultCheckedNodes(); this.ready = true; } } filter(value, data) { const filterNodeMethod = this.filterNodeMethod; const lazy = this.lazy; const _self = this; const traverse = function(node) { const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId); childNodes.forEach((child) => { if (data && typeof data === 'object') { let nodePath = _self.getNodePath(child.data); if (!nodePath.some(pathItem => pathItem[_self.key] === data[_self.key])) { child.visible = false; traverse(child); return; } } if (_self.childVisibleForFilterNode) { let parent = child.getParent(child.parentId); child.visible = filterNodeMethod.call(child, value, child.data, child) || (parent && parent.visible); } else { child.visible = filterNodeMethod.call(child, value, child.data, child); } traverse(child); }); if (!node.visible && childNodes.length) { let allHidden = true; allHidden = !childNodes.some(child => child.visible); if (node.root) { node.root.visible = allHidden === false; } else { node.visible = allHidden === false; } } if (!value) return; if (node.visible && !node.isLeaf && !lazy) node.expand(); }; traverse(this); } setData(newVal) { const instanceChanged = newVal !== this.root.data; if (instanceChanged) { this.root.setData(newVal); this._initDefaultCheckedNodes(); } else { this.root.updateChildren(); } } getNode(data) { if (data instanceof Node) return data; const key = typeof data !== 'object' ? data : getNodeKey(this.key, data); if (!key) return null; return this.nodesMap[key] || null; } insertBefore(data, refData) { const refNode = this.getNode(refData); let parent = refNode.getParent(refNode.parentId); parent.insertBefore({ data }, refNode); } insertAfter(data, refData) { const refNode = this.getNode(refData); let parent = refNode.getParent(refNode.parentId); parent.insertAfter({ data }, refNode); } remove(data) { const node = this.getNode(data); if (node && node.parentId !== null) { let parent = node.getParent(node.parentId); if (node === this.currentNode) { this.currentNode = null; } parent.removeChild(node); } } append(data, parentData) { const parentNode = parentData ? this.getNode(parentData) : this.root; if (parentNode) { parentNode.insertChild({ data }); } } _initDefaultCheckedNodes() { const defaultCheckedKeys = this.defaultCheckedKeys || []; const nodesMap = this.nodesMap; let checkedKeyfromData = []; let totalCheckedKeys = [] for (let key in nodesMap) { let checked = getPropertyFromData(nodesMap[key], 'checked') || false; checked && checkedKeyfromData.push(key); } totalCheckedKeys = Array.from(new Set([...defaultCheckedKeys, ...checkedKeyfromData])); totalCheckedKeys.forEach((checkedKey) => { const node = nodesMap[checkedKey]; if (node) { node.setChecked(true, !this.checkStrictly); } }); } _initDefaultCheckedNode(node) { const defaultCheckedKeys = this.defaultCheckedKeys || []; if (defaultCheckedKeys.indexOf(node.key) !== -1) { node.setChecked(true, !this.checkStrictly); } } toggleExpendAll(isExpandAll) { const allNodes = this._getAllNodes(); allNodes.forEach(item => { const node = this.getNode(item.key); if (node) isExpandAll ? node.expand() : node.collapse(); }); } setCheckAll(isCkeckAll) { const allNodes = this._getAllNodes(); allNodes.forEach(item => { item.setChecked(isCkeckAll, false); }); } setDefaultCheckedKey(newVal) { if (newVal !== this.defaultCheckedKeys) { this.defaultCheckedKeys = newVal; this._initDefaultCheckedNodes(); } } registerNode(node) { const key = this.key; if (!key || !node || !node.data) return; const nodeKey = node.key; if (nodeKey !== undefined) this.nodesMap[node.key] = node; } deregisterNode(node) { const key = this.key; if (!key || !node || !node.data) return; let childNodes = node.getChildNodes(node.childNodesId); childNodes.forEach(child => { this.deregisterNode(child); }); delete this.nodesMap[node.key]; } getNodePath(data) { if (!this.key) throw new Error('[Tree] nodeKey is required in getNodePath'); const node = this.getNode(data); if (!node) return []; const path = [node.data]; let parent = node.getParent(node.parentId); while (parent && parent !== this.root) { path.push(parent.data); parent = parent.getParent(parent.parentId); } return path.reverse(); } getCheckedNodes(leafOnly = false, includeHalfChecked = false) { const checkedNodes = []; const traverse = function(node) { const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId); childNodes.forEach((child) => { if ((child.checked || (includeHalfChecked && child.indeterminate)) && (!leafOnly || (leafOnly && child.isLeaf))) { checkedNodes.push(child.data); } traverse(child); }); }; traverse(this); return checkedNodes; } getCheckedKeys(leafOnly = false, includeHalfChecked = false) { return this.getCheckedNodes(leafOnly, includeHalfChecked).map((data) => (data || {})[this.key]); } getHalfCheckedNodes() { const nodes = []; const traverse = function(node) { const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(node.childNodesId); childNodes.forEach((child) => { if (child.indeterminate) { nodes.push(child.data); } traverse(child); }); }; traverse(this); return nodes; } getHalfCheckedKeys() { return this.getHalfCheckedNodes().map((data) => (data || {})[this.key]); } _getAllNodes() { const allNodes = []; const nodesMap = this.nodesMap; for (let nodeKey in nodesMap) { if (nodesMap.hasOwnProperty(nodeKey)) { allNodes.push(nodesMap[nodeKey]); } } return allNodes; } updateChildren(key, data) { const node = this.nodesMap[key]; if (!node) return; const childNodes = node.getChildNodes(node.childNodesId); for (let i = childNodes.length - 1; i >= 0; i--) { const child = childNodes[i]; this.remove(child.data); } for (let i = 0, j = data.length; i < j; i++) { const child = data[i]; this.append(child, node.data); } } _setCheckedKeys(key, leafOnly = false, checkedKeys) { const allNodes = this._getAllNodes().sort((a, b) => b.level - a.level); const cache = Object.create(null); const keys = Object.keys(checkedKeys); allNodes.forEach(node => node.setChecked(false, false)); for (let i = 0, j = allNodes.length; i < j; i++) { const node = allNodes[i]; let nodeKey = node.data[key]; if (typeof nodeKey === 'undefined') continue; nodeKey = nodeKey.toString(); let checked = keys.indexOf(nodeKey) > -1; if (!checked) { if (node.checked && !cache[nodeKey]) { node.setChecked(false, false); } continue; } let parent = node.getParent(node.parentId); while (parent && parent.level > 0) { cache[parent.data[key]] = true; parent = parent.getParent(parent.parentId); } if (node.isLeaf || this.checkStrictly) { node.setChecked(true, false); continue; } node.setChecked(true, true); if (leafOnly) { node.setChecked(false, false); const traverse = function(node) { const childNodes = node.getChildNodes(node.childNodesId); childNodes.forEach((child) => { if (!child.isLeaf) { child.setChecked(false, false); } traverse(child); }); }; traverse(node); } } } setCheckedNodes(array, leafOnly = false) { const key = this.key; const checkedKeys = {}; array.forEach((item) => { checkedKeys[(item || {})[key]] = true; }); this._setCheckedKeys(key, leafOnly, checkedKeys); } setCheckedKeys(keys, leafOnly = false) { this.defaultCheckedKeys = keys; const key = this.key; const checkedKeys = {}; keys.forEach((key) => { checkedKeys[key] = true; }); this._setCheckedKeys(key, leafOnly, checkedKeys); } setDefaultExpandedKeys(keys) { keys = keys || []; this.defaultExpandedKeys = keys; keys.forEach((key) => { const node = this.getNode(key); if (node) node.expand(null, this.autoExpandParent); }); } setChecked(data, checked, deep) { const node = this.getNode(data); if (node) { node.setChecked(!!checked, deep); } } getCurrentNode() { return this.currentNode; } setCurrentNode(currentNode) { const prevCurrentNode = this.currentNode; if (prevCurrentNode) { prevCurrentNode.isCurrent = false; } this.currentNode = currentNode; this.currentNode.isCurrent = true; this.expandCurrentNodeParent && this.currentNode.expand(null, true) } setUserCurrentNode(node) { const key = node[this.key]; const currNode = this.nodesMap[key]; this.setCurrentNode(currNode); } setCurrentNodeKey(key) { if (key === null || key === undefined) { this.currentNode && (this.currentNode.isCurrent = false); this.currentNode = null; return; } const node = this.getNode(key); if (node) { this.setCurrentNode(node); } } };