| 53 | * @returns Array of flattened nodes |
| 54 | */ |
| 55 | export function flattenTree<T>( |
| 56 | data: T | readonly T[], |
| 57 | getKey: (node: T) => string, |
| 58 | getChildren: ((node: T) => readonly T[] | undefined) | undefined, |
| 59 | hasChildrenFn: ((node: T) => boolean) | undefined, |
| 60 | expanded: readonly string[], |
| 61 | expandedSet?: ReadonlySet<string>, |
| 62 | ): readonly FlattenedNode<T>[] { |
| 63 | const result: FlattenedNode<T>[] = []; |
| 64 | const expandedLookup = expandedSet ?? new Set(expanded); |
| 65 | |
| 66 | // Normalize to array of roots |
| 67 | const roots: readonly T[] = Array.isArray(data) ? (data as readonly T[]) : [data as T]; |
| 68 | |
| 69 | function traverse( |
| 70 | nodes: readonly T[], |
| 71 | depth: number, |
| 72 | parentKey: string | null, |
| 73 | ancestorIsLast: readonly boolean[], |
| 74 | ): void { |
| 75 | const siblingCount = nodes.length; |
| 76 | |
| 77 | for (let i = 0; i < siblingCount; i++) { |
| 78 | const node = nodes[i]; |
| 79 | if (node === undefined) continue; |
| 80 | |
| 81 | const key = getKey(node); |
| 82 | const children = getChildren?.(node); |
| 83 | const hasChildren = hasChildrenFn ? hasChildrenFn(node) : (children?.length ?? 0) > 0; |
| 84 | const isLast = i === siblingCount - 1; |
| 85 | |
| 86 | const flatNode: FlattenedNode<T> = Object.freeze({ |
| 87 | node, |
| 88 | depth, |
| 89 | siblingIndex: i, |
| 90 | siblingCount, |
| 91 | key, |
| 92 | parentKey, |
| 93 | hasChildren, |
| 94 | ancestorIsLast: Object.freeze([...ancestorIsLast, isLast]), |
| 95 | }); |
| 96 | |
| 97 | result.push(flatNode); |
| 98 | |
| 99 | // Recurse into children if expanded |
| 100 | if (hasChildren && expandedLookup.has(key) && children && children.length > 0) { |
| 101 | traverse(children, depth + 1, key, flatNode.ancestorIsLast); |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | traverse(roots, 0, null, []); |
| 107 | |
| 108 | return Object.freeze(result); |
| 109 | } |
| 110 | |
| 111 | /* ========== NodeState Computation ========== */ |
| 112 | |