mobx-bonsai
Version:
A fast lightweight alternative to MobX-State-Tree + Y.js two-way binding
95 lines (81 loc) • 2.32 kB
text/typescript
import { assertIsNode } from "../node"
import { getChildrenNodes } from "./getChildrenNodes"
/**
* Mode for the `walkTree` method.
*/
export enum WalkTreeMode {
/**
* The walk will be done parent (roots) first, then children.
*/
ParentFirst = "parentFirst",
/**
* The walk will be done children (leafs) first, then parents.
*/
ChildrenFirst = "childrenFirst",
}
/**
* Walks a tree, running the predicate function for each node.
* If the predicate function returns something other than undefined,
* then the walk will be stopped and the function will return the returned value.
*
* @template T Returned node type, defaults to void.
* @param root Subtree root node.
* @param visit Function that will be run for each node of the tree.
* @param mode Mode to walk the tree, as defined in `WalkTreeMode`.
* @returns The value returned by the predicate function that stopped the walk,
* or undefined if the walk was completed.
*/
export function walkTree<T = void>(
root: object,
visit: (node: object) => T | undefined,
mode: WalkTreeMode
): T | undefined {
assertIsNode(root, "root")
if (mode === WalkTreeMode.ParentFirst) {
return walkTreeParentFirst(root, visit)
} else {
return walkTreeChildrenFirst(root, visit)
}
}
function walkTreeParentFirst<T = void>(
root: object,
visit: (node: object) => T | undefined
): T | undefined {
const stack: object[] = [root]
while (stack.length > 0) {
const node = stack.pop()!
const ret = visit(node)
if (ret !== undefined) {
return ret
}
const children = getChildrenNodes(node)
stack.length += children.size
let i = stack.length - 1
const childrenIter = children.values()
let ch = childrenIter.next()
while (!ch.done) {
stack[i--] = ch.value
ch = childrenIter.next()
}
}
return undefined
}
function walkTreeChildrenFirst<T = void>(
root: object,
visit: (node: object) => T | undefined
): T | undefined {
const childrenIter = getChildrenNodes(root).values()
let ch = childrenIter.next()
while (!ch.done) {
const ret = walkTreeChildrenFirst(ch.value, visit)
if (ret !== undefined) {
return ret
}
ch = childrenIter.next()
}
const ret = visit(root)
if (ret !== undefined) {
return ret
}
return undefined
}