Skip to content

Commit a95d5c1

Browse files
committed
Apply singleton pattern for factory api to onyxia-ui
1 parent 0f87faa commit a95d5c1

2 files changed

Lines changed: 104 additions & 4 deletions

File tree

src/lib/OnyxiaUi.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import * as mui from "@mui/material/styles";
66
import type { PaletteBase, ColorUseCasesBase } from "./color";
77
import { createSplashScreen, type SplashScreenParams } from "./SplashScreen";
88
import { assert } from "tsafe/assert";
9-
import type { StatefulReadonlyEvt } from "evt";
109
import { typeGuard } from "tsafe/typeGuard";
1110
import { memoize } from "../tools/memoize";
1211
import {
@@ -24,7 +23,16 @@ import { useConstCallback } from "powerhooks/useConstCallback";
2423
import { getIsDarkModeEnabledOsDefault } from "../tools/getIsDarkModeEnabledOsDefault";
2524
import { getEvtRootFontSizePx } from "../tools/evtRootFontSizePx";
2625
import { getEvtWindowInnerSize } from "../tools/evtWindowInnerSize";
27-
import { Evt } from "evt";
26+
import { Evt, type StatefulReadonlyEvt } from "evt";
27+
import { createForwardingProxy } from "../tools/createForwardingProxy";
28+
29+
const fpOnyxiaUi = createForwardingProxy<
30+
(props: { darkMode?: boolean; children: ReactNode }) => JSX.Element
31+
>({ isFunction: true });
32+
33+
const fpEvtTheme = createForwardingProxy<StatefulReadonlyEvt<any>>({
34+
isFunction: false,
35+
});
2836

2937
/**
3038
* BASE_URL:
@@ -320,9 +328,15 @@ export function createOnyxiaUi<
320328
},
321329
]);
322330

331+
fpOnyxiaUi.updateTarget(OnyxiaUi);
332+
if (evtTheme !== undefined) {
333+
fpEvtTheme.updateTarget(evtTheme);
334+
}
335+
323336
return {
324-
OnyxiaUi,
337+
OnyxiaUi: fpOnyxiaUi.proxy,
325338
ofTypeTheme: null as any,
326-
evtTheme: evtTheme as any,
339+
evtTheme:
340+
evtTheme === undefined ? undefined : (fpEvtTheme.proxy as any),
327341
};
328342
}

src/tools/createForwardingProxy.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* Creates a proxy that forwards EVERYTHING to the current target.
3+
* The proxy has a stable identity, and you can update the target at runtime.
4+
*/
5+
export function createForwardingProxy<T extends object>(params: {
6+
accessBeforeSetErrorMessage?: string;
7+
isFunction: boolean;
8+
}) {
9+
const {
10+
accessBeforeSetErrorMessage = "Assertion error: Forwarded proxy accessed too early",
11+
isFunction,
12+
} = params;
13+
14+
const checkSet = () => {
15+
if (target === undefined) {
16+
throw new Error(accessBeforeSetErrorMessage);
17+
}
18+
};
19+
20+
let target: any = undefined;
21+
22+
const handler: ProxyHandler<any> = {
23+
get(_t, prop, receiver) {
24+
checkSet();
25+
return Reflect.get(target, prop, receiver);
26+
},
27+
set(_t, prop, value, receiver) {
28+
checkSet();
29+
return Reflect.set(target, prop, value, receiver);
30+
},
31+
has(_t, prop) {
32+
checkSet();
33+
return Reflect.has(target, prop);
34+
},
35+
deleteProperty(_t, prop) {
36+
checkSet();
37+
return Reflect.deleteProperty(target, prop);
38+
},
39+
ownKeys(_t) {
40+
checkSet();
41+
return Reflect.ownKeys(target);
42+
},
43+
getOwnPropertyDescriptor(_t, prop) {
44+
checkSet();
45+
return Reflect.getOwnPropertyDescriptor(target, prop);
46+
},
47+
defineProperty(_t, prop, descriptor) {
48+
checkSet();
49+
return Reflect.defineProperty(target, prop, descriptor);
50+
},
51+
getPrototypeOf(_t) {
52+
checkSet();
53+
return Reflect.getPrototypeOf(target);
54+
},
55+
setPrototypeOf(_t, proto) {
56+
checkSet();
57+
return Reflect.setPrototypeOf(target, proto);
58+
},
59+
isExtensible(_t) {
60+
checkSet();
61+
return Reflect.isExtensible(target);
62+
},
63+
preventExtensions(_t) {
64+
checkSet();
65+
return Reflect.preventExtensions(target);
66+
},
67+
apply(_t, thisArg, args) {
68+
checkSet();
69+
return Reflect.apply(target, thisArg, args);
70+
},
71+
construct(_t, args, newTarget) {
72+
checkSet();
73+
return Reflect.construct(target, args, newTarget);
74+
},
75+
};
76+
77+
// Use a dummy callable so proxy can stand in for both functions and objects
78+
const proxy = new Proxy(isFunction ? function () {} : {}, handler) as T;
79+
80+
return {
81+
proxy,
82+
updateTarget(newTarget: T) {
83+
target = newTarget;
84+
},
85+
};
86+
}

0 commit comments

Comments
 (0)