From b21dfcdb661fd7ad43213756dadb6cfdf0ac1e94 Mon Sep 17 00:00:00 2001 From: AAGaming Date: Mon, 8 Aug 2022 19:30:08 -0400 Subject: [PATCH] feat(utils): add findInTree and findInReactTree --- src/utils.ts | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index a495ddc..3dcb8e4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -92,4 +92,33 @@ export function joinClassNames(...classes: string[]): string { export function sleep(ms: number) { return new Promise(res => setTimeout(res, ms)); -} \ No newline at end of file +} + +// Based on https://github.com/GooseMod/GooseMod/blob/9ef146515a9e59ed4e25665ed365fd72fc0dcf23/src/util/react.js#L20 +export interface findInTreeOpts { + walkable?: string[], + ignore?: string[] +} + +export declare type findInTreeFilter = (element: any) => boolean + +export const findInTree = (parent: any, filter: findInTreeFilter, opts: findInTreeOpts): any => { + const { walkable = null, ignore = [] } = opts ?? {}; + + if (!parent || typeof parent !== 'object') { // Parent is invalid to search through + return null; + } + + if (filter(parent)) return parent; // Parent matches, just return + + if (Array.isArray(parent)) { // Parent is an array, go through values + return parent.map((x) => findInTree(x, filter, opts)).find((x) => x); + } + + // Parent is an object, go through values (or option to only use certain keys) + return (walkable || Object.keys(parent)).map((x) => !ignore.includes(x) && findInTree(parent[x], filter, opts)).find((x: any) => x); +}; + +export const findInReactTree = (node: any, filter: findInTreeFilter) => findInTree(node, filter, { // Specialised findInTree for React nodes + walkable: [ 'props', 'children', 'child', 'sibling' ] +}); \ No newline at end of file