Files
decky-frontend-lib/src/utils/react.ts

96 lines
2.8 KiB
TypeScript
Raw Normal View History

2022-10-24 20:33:40 -04:00
import * as React from 'react';
2022-05-29 20:19:10 -04:00
// this shouldn't need to be redeclared but it does for some reason
declare global {
2022-10-24 20:33:40 -04:00
interface Window {
SP_REACT: typeof React;
}
2022-05-29 20:19:10 -04:00
}
2022-10-24 20:33:40 -04:00
export function fakeRenderComponent(fun: Function, customHooks: any = {}): any {
const hooks = (window.SP_REACT as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher
.current;
2022-05-29 20:19:10 -04:00
2022-10-24 20:33:40 -04:00
// TODO: add more hooks
2022-05-29 20:19:10 -04:00
2022-10-24 20:33:40 -04:00
let oldHooks = {
useContext: hooks.useContext,
useCallback: hooks.useCallback,
useLayoutEffect: hooks.useLayoutEffect,
useEffect: hooks.useEffect,
useMemo: hooks.useMemo,
useRef: hooks.useRef,
useState: hooks.useState,
};
2022-05-29 20:19:10 -04:00
2022-10-24 20:33:40 -04:00
hooks.useCallback = (cb: Function) => cb;
hooks.useContext = (cb: any) => cb._currentValue;
hooks.useLayoutEffect = (_: Function) => {}; //cb();
hooks.useMemo = (cb: Function, _: any[]) => cb;
hooks.useEffect = (_: Function) => {}; //cb();
hooks.useRef = (val: any) => ({ current: val || {} });
hooks.useState = (v: any) => {
let val = v;
2022-05-29 20:19:10 -04:00
2022-10-24 20:33:40 -04:00
return [val, (n: any) => (val = n)];
};
2022-05-29 20:19:10 -04:00
2022-10-24 20:33:40 -04:00
Object.assign(hooks, customHooks);
2022-05-29 20:19:10 -04:00
2022-10-24 20:33:40 -04:00
const res = fun(hooks);
2022-05-29 20:19:10 -04:00
2022-10-24 20:33:40 -04:00
Object.assign(hooks, oldHooks);
return res;
2022-05-29 20:19:10 -04:00
}
export function wrapReactType(node: any, prop: any = 'type') {
2022-10-24 20:33:40 -04:00
return (node[prop] = { ...node[prop] });
2022-08-17 14:45:34 -04:00
}
export function wrapReactClass(node: any, prop: any = 'type') {
2022-10-24 20:33:40 -04:00
const cls = node[prop];
const wrappedCls = class extends cls {};
return (node[prop] = wrappedCls);
}
2022-05-29 20:19:10 -04:00
export function getReactInstance(o: HTMLElement | Element | Node) {
2022-10-24 20:33:40 -04:00
return o[Object.keys(o).find((k) => k.startsWith('__reactInternalInstance')) as string];
2022-05-29 20:19:10 -04:00
}
2022-06-06 17:27:56 -04:00
// Based on https://github.com/GooseMod/GooseMod/blob/9ef146515a9e59ed4e25665ed365fd72fc0dcf23/src/util/react.js#L20
export interface findInTreeOpts {
2022-10-24 20:33:40 -04:00
walkable?: string[];
ignore?: string[];
}
2022-10-24 20:33:40 -04:00
export declare type findInTreeFilter = (element: any) => boolean;
export const findInTree = (parent: any, filter: findInTreeFilter, opts: findInTreeOpts): any => {
2022-10-24 20:33:40 -04:00
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);
};
2022-10-24 20:33:40 -04:00
export const findInReactTree = (node: any, filter: findInTreeFilter) =>
findInTree(node, filter, {
// Specialised findInTree for React nodes
walkable: ['props', 'children', 'child', 'sibling'],
});