All files / src/utils TreeUtils.ts

85.13% Statements 63/74
80.43% Branches 37/46
94.11% Functions 16/17
86.11% Lines 62/72

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171                67x 44x 56x 56x 56x 16x                     67x 3x 3x 6x 1x   6x 5x     3x                     67x       4x 4x 3x 2x 2x   2x 1x     3x 3x       4x     67x 2x                     2x 2x 4x 2x   2x   1x 1x     1x       1x           2x       4x   2x                 67x   16x 16x 16x 26x 13x                   67x   8x 1x   7x 7x 10x 10x 9x     7x 13x                 67x 4x 8x 2x         6x 1x         5x      
import { ExtendDataNode, RecordProps } from "@props/RecordProps";
import { DataNode } from "antd/lib/tree";
 
/**
 * Build a flat view contains all nodes for a tree
 * @param rootNodes: root nodes of the tree
 * @param current: current result (for recur call)
 */
export const buildAllNodesFlat = (rootNodes: Array<ExtendDataNode>, current: Array<ExtendDataNode>): void => {
  rootNodes.forEach(node => {
    Eif (node != null) {
      current.push(node);
      if (node.children != null && node.children?.length > 0) {
        buildAllNodesFlat(node.children as Array<ExtendDataNode>, current);
      }
    }
  });
};
 
/**
 * Remove a node from a multiple level tree
 * @param treeData: root nodes of the tree
 * @param nodeToRemove: the node to remove from the tree
 */
export const recursionRemoveNode = (treeData: Array<ExtendDataNode>, nodeToRemove: RecordProps): Array<ExtendDataNode> => {
  const nodeData: Array<ExtendDataNode> = [];
  treeData.forEach(item => {
    if(item.children){
      item.children = recursionRemoveNode(item.children, nodeToRemove);
    }
    if (item.id !== nodeToRemove.id) {
      nodeData.push(item);
    }
  });
  return nodeData;
};
 
 
/**
 * Change node title
 * @param treeData: root nodes of the tree
 * @param id: id of the node to change title
 * @param newTitle: new title of the node
 * @param newIcon: new icon of the node
*/
export const recursionChangeNodeInfo = (treeData: Array<ExtendDataNode>,
                                        id: string | number,
                                        newTitle: string,
                                        newIcon?: string): Array<ExtendDataNode> => {
  const nodeData: Array<ExtendDataNode> = [];
  treeData.forEach(item => {
    if (item.id === id) {
      Eif (item.title !== newTitle) {
        item.title = newTitle;
      }
      if (item.icon !== newIcon) {
        item.icon = newIcon;
      }
    }
    nodeData.push(item);
    Iif(item.children){
      item.children = recursionChangeNodeInfo(item.children, id, newTitle);
    }
  });
  return nodeData;
};
 
export const recursionAddNode = (treeData: Array<ExtendDataNode>, parent: ExtendDataNode | undefined, nodeToAdd: ExtendDataNode): Array<ExtendDataNode> => {
  Iif (!parent) {
    // 根据 displaySequence 找到待插入的 index, 如果所有现有节点 displaySequence 均小于插入节点的,则插入到最后
    const idxToInsert = treeData.findIndex((c: ExtendDataNode) => (c.displaySequence > nodeToAdd.displaySequence));
    if (idxToInsert === -1) {
      return [...treeData, nodeToAdd];
    } else {
      const res = [...treeData];
      res.splice(idxToInsert, 0, nodeToAdd);
      return res;
    }
  }
  const nodeData: Array<ExtendDataNode> = [];
  treeData.forEach((item: ExtendDataNode) => {
    if (item.key === parent.key) {
      if (item.children && item.children.length > 0) {
        // 如果节点有 displaySequence 属性标识要考虑其显示先后顺序
        if ('displaySequence' in nodeToAdd) {
          // 根据 displaySequence 找到待插入的 index, 如果所有现有节点 displaySequence 均小于插入节点的,则插入到最后
          const idxToInsert = item.children.findIndex((c: ExtendDataNode) => (c.displaySequence > nodeToAdd.displaySequence));
          Iif (idxToInsert === -1) {
            item.children.push(nodeToAdd);
          } else {
            item.children.splice(idxToInsert, 0, nodeToAdd);
          }
        } else {
          // 如果没有 displaySequence 属性直接加到最后
          item.children.push(nodeToAdd);
        }
      } else E{
        // 如果之前父节点没有子节点,则新创建一个数组存放新的子节点
        item.children = [nodeToAdd];
      }
    } else Iif(item.children){
      // 如果不是目标父节点,则在其 children 上递归调用,寻找待插入的父节点并处理
      item.children = recursionAddNode(item.children, parent, nodeToAdd);
    }
    nodeData.push(item);
  });
  return nodeData;
};
 
/**
 * Get a subtree from a tree strucutre
 * @param rootNodes root nodes of the tree
 * @param keyToFind key of the subtree root
 * @return the root of subtree with corresponding key or undefined if not found
 */
export const getSubTreeRootNodeWithKey = (rootNodes: Array<ExtendDataNode>, keyToFind: number):
  ExtendDataNode | undefined => {
  const flatNodes: Array<ExtendDataNode> = [];
  buildAllNodesFlat(rootNodes, flatNodes);
  return flatNodes?.find(node => {
    if (node.key === keyToFind) {
      return node;
    }
  });
};
 
/**
 * Get key of self and all children nodes of a list of keys
 * @param treeData The tree to get data
 * @param parents list of all node keys, which to get self and children nodes
 */
export const getSelfAndAllChildrenKeys = (treeData: Array<ExtendDataNode>,
  parents: Array<number>): Array<number> => {
  if (parents.length === 0) {
    return [];
  }
  const current: Array<ExtendDataNode> = [];
  parents.forEach(p => {
    const node = getSubTreeRootNodeWithKey(treeData, p);
    if (node != null) {
      buildAllNodesFlat([node], current);
    }
  });
  return (current.length > 0) ?
    current.map(c => Number.parseInt(c.key.toString())) : [] as Array<number>;
};
 
/**
 * Update list of extend data node and add children to a specific children, then return the new list
 * @param list list to update
 * @param key key to add children
 * @param children children to add to key
 */
export const mergeChildrenIntoTree = (list: DataNode[], key: React.Key, children: DataNode[]): DataNode[] => {
  return list.map(node => {
    if (node.key === key) {
      return {
        ...node,
        children,
      };
    }
    if (node.children) {
      return {
        ...node,
        children: mergeChildrenIntoTree(node.children, key, children),
      };
    }
    return node;
  });
};