react-native-tree-multi-select
Version:
Super-fast, customizable tree view component for React Native with drag-and-drop reordering, multi-selection, and search filtering.
96 lines (82 loc) • 3.13 kB
text/typescript
import type { TreeNode } from "../types/treeView.types";
import { getTreeViewStore } from "../store/treeView.store";
/**
* Initialize the maps for tracking tree nodes and their parent-child relationships.
*
* This function is intended to be called once, during component initialization,
* with the initial tree data and any preselected node IDs.
*
* @param storeId - Identifier for the specific tree view store.
* @param initialData - An array of TreeNode objects that represent the initial tree structure.
*/
export function initializeNodeMaps<ID>(storeId: string, initialData: TreeNode<ID>[]) {
const treeViewStore = getTreeViewStore<ID>(storeId);
const { updateNodeMap, updateChildToParentMap } = treeViewStore.getState();
const nodeMap = new Map<ID, TreeNode<ID>>();
const childToParentMap = new Map<ID, ID>();
// Use an iterative approach with a stack instead of recursion
const stack: Array<{ nodes: TreeNode<ID>[], parentId: ID | null; }> = [
{ nodes: initialData, parentId: null }
];
while (stack.length > 0) {
const { nodes, parentId } = stack.pop()!;
for (const node of nodes) {
nodeMap.set(node.id, node);
if (parentId !== null) {
childToParentMap.set(node.id, parentId);
}
if (node.children && node.children.length > 0) {
stack.push({ nodes: node.children, parentId: node.id });
}
}
}
// Batch update the store with both maps at once
updateNodeMap(nodeMap);
updateChildToParentMap(childToParentMap);
}
/**
* Compute the maximum depth of a node's subtree (0 for a leaf node).
* Uses an iterative stack-based DFS to avoid call-stack limits on deep trees.
*
* @param nodeMap - Map of node ID to node (from `initializeNodeMaps`).
* @param nodeId - The root of the subtree to measure.
*/
export function getSubtreeDepthFromMap<ID>(
nodeMap: Map<ID, TreeNode<ID>>,
nodeId: ID
): number {
const root = nodeMap.get(nodeId);
if (!root?.children?.length) return 0;
let maxDepth = 0;
const stack: Array<{ node: TreeNode<ID>; depth: number; }> = [
{ node: root, depth: 0 }
];
while (stack.length > 0) {
const { node, depth } = stack.pop()!;
if (node.children?.length) {
for (const child of node.children) {
if (depth + 1 > maxDepth) maxDepth = depth + 1;
stack.push({ node: child, depth: depth + 1 });
}
}
}
return maxDepth;
}
/**
* Compute a node's depth (its level, 0 for root) by walking the child-to-parent map.
*
* @param childToParentMap - Map of child ID to parent ID (from `initializeNodeMaps`).
* @param nodeId - The node whose depth to compute.
*/
export function getNodeDepthFromParentMap<ID>(
childToParentMap: Map<ID, ID>,
nodeId: ID
): number {
let depth = 0;
let currentId: ID | undefined = nodeId;
while (currentId !== undefined && childToParentMap.has(currentId)) {
currentId = childToParentMap.get(currentId);
depth++;
}
return depth;
}