mobx-keystone
Version:
A MobX powered state management solution based on data trees with first class support for TypeScript, snapshots, patches and much more
84 lines (75 loc) • 2.37 kB
text/typescript
import { assertTweakedObject } from "../tweaker/core"
import { fastGetParentPath, ParentPath } from "./path"
import type { Path, WritablePath } from "./pathTypes"
/**
* Iterates through all the parents (from the nearest until the root)
* until one of them matches the given predicate.
* If the predicate is matched it will return the found node.
* If none is found it will return undefined.
*
* @template T Parent object type.
* @param child Target object.
* @param predicate Function that will be run for every parent of the target object, from immediate parent to the root.
* @param maxDepth Max depth, or 0 for infinite.
* @returns
*/
export function findParent<T extends object = any>(
child: object,
predicate: (parentNode: object) => boolean,
maxDepth = 0
): T | undefined {
const foundParentPath = findParentPath(child, predicate, maxDepth)
return foundParentPath ? foundParentPath.parent : undefined
}
/**
* Result of `findParentPath`.
*/
export interface FoundParentPath<T extends object> {
/**
* Found parent object.
*/
readonly parent: T
/**
* Path from the found parent to the child.
*/
readonly path: Path
}
/**
* Iterates through all the parents (from the nearest until the root)
* until one of them matches the given predicate.
* If the predicate is matched it will return the found node plus the
* path to get from the parent to the child.
* If none is found it will return undefined.
*
* @template T Parent object type.
* @param child Target object.
* @param predicate Function that will be run for every parent of the target object, from immediate parent to the root.
* @param maxDepth Max depth, or 0 for infinite.
* @returns
*/
export function findParentPath<T extends object = any>(
child: object,
predicate: (parentNode: object) => boolean,
maxDepth = 0
): FoundParentPath<T> | undefined {
assertTweakedObject(child, "child")
const path: WritablePath = []
let current: any = child
let depth = 0
let parentPath: ParentPath<any> | undefined
while ((parentPath = fastGetParentPath(current, true))) {
path.unshift(parentPath.path)
current = parentPath.parent
if (predicate(current)) {
return {
parent: current,
path,
}
}
depth++
if (maxDepth > 0 && depth === maxDepth) {
break
}
}
return undefined
}