@tailorkit/react 0.1.0-beta.7 → 0.1.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/app-view.d.ts +3 -0
- package/dist/components/context.d.ts +4 -6
- package/dist/components/root.d.ts +3 -4
- package/dist/hooks/use-apps.d.ts +4 -2
- package/dist/hooks/use-stable-context.d.ts +2 -0
- package/dist/hooks/use-view.d.ts +24 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +470 -417
- package/dist/primitives.d.ts +10 -18
- package/dist/remote-context.d.ts +2 -2
- package/dist/remote-view.d.ts +1 -2
- package/dist/store.d.ts +33 -0
- package/dist/tailor-kit.d.ts +44 -64
- package/dist/view-registry.d.ts +14 -0
- package/package.json +3 -3
- package/dist/hooks/use-current-screen.d.ts +0 -23
package/dist/index.js
CHANGED
|
@@ -1,80 +1,205 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import { useEffect as
|
|
3
|
-
|
|
4
|
-
// src/components/root.tsx
|
|
5
|
-
import { useMemo as useMemo2 } from "react";
|
|
1
|
+
// src/components/app-view.tsx
|
|
2
|
+
import { useEffect as useEffect2, useId as useId2, useMemo as useMemo3, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
3
|
+
import { isViewAncestor as isViewAncestor2 } from "@tailorkit/core/views";
|
|
6
4
|
|
|
7
|
-
// src/
|
|
8
|
-
import {
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
// src/view-registry.ts
|
|
6
|
+
import { getViewDepth, isViewAncestor } from "@tailorkit/core/views";
|
|
7
|
+
function createViewRegistry() {
|
|
8
|
+
const entries = /* @__PURE__ */ new Map();
|
|
9
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
10
|
+
let snapshot = null;
|
|
11
|
+
let nextOrder = 0;
|
|
12
|
+
let scheduled = false;
|
|
13
|
+
const publish = () => {
|
|
14
|
+
if (scheduled) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
scheduled = true;
|
|
18
|
+
queueMicrotask(() => {
|
|
19
|
+
scheduled = false;
|
|
20
|
+
const ordered = [...entries.values()].toSorted(
|
|
21
|
+
(a, b) => getViewDepth(b.view) - getViewDepth(a.view) || b.order - a.order
|
|
22
|
+
);
|
|
23
|
+
const selected = ordered[0];
|
|
24
|
+
if (selected) {
|
|
25
|
+
const peers = ordered.filter(
|
|
26
|
+
(entry) => getViewDepth(entry.view) === getViewDepth(selected.view)
|
|
27
|
+
);
|
|
28
|
+
if (peers.length > 1) {
|
|
29
|
+
console.warn(
|
|
30
|
+
`TailorKit found multiple active views at the same hierarchy depth: ${peers.toReversed().map((entry) => `"${entry.view}"`).join(
|
|
31
|
+
", "
|
|
32
|
+
)}. TailorKit selected "${selected.view}" by mount order. Only one route at a hierarchy depth should call useView.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
snapshot = {
|
|
36
|
+
view: selected.view,
|
|
37
|
+
layers: ordered.filter((entry) => isViewAncestor(entry.view, selected.view)).toReversed().map((entry) => ({ path: entry.view, context: entry.context, status: entry.status }))
|
|
38
|
+
};
|
|
39
|
+
} else {
|
|
40
|
+
snapshot = null;
|
|
18
41
|
}
|
|
19
|
-
|
|
42
|
+
for (const listener of listeners) {
|
|
43
|
+
listener();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
getSnapshot: () => snapshot,
|
|
49
|
+
subscribe(listener) {
|
|
50
|
+
listeners.add(listener);
|
|
51
|
+
return () => {
|
|
52
|
+
listeners.delete(listener);
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
register(entry) {
|
|
56
|
+
entries.set(entry.id, { ...entry, order: entries.get(entry.id)?.order ?? nextOrder++ });
|
|
57
|
+
publish();
|
|
58
|
+
},
|
|
59
|
+
unregister(id) {
|
|
60
|
+
entries.delete(id);
|
|
61
|
+
publish();
|
|
20
62
|
}
|
|
21
|
-
|
|
22
|
-
}, [defaultTagName, props, render]);
|
|
63
|
+
};
|
|
23
64
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
65
|
+
|
|
66
|
+
// src/store.ts
|
|
67
|
+
function createTailorKitStore(baseUrlInput, initialApps) {
|
|
68
|
+
const baseUrl = toBaseUrl(baseUrlInput);
|
|
69
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
70
|
+
let providedApps = initialApps;
|
|
71
|
+
let appsSnapshot = {
|
|
72
|
+
apps: initialApps ?? [],
|
|
73
|
+
error: null,
|
|
74
|
+
status: initialApps === void 0 ? "idle" : "ready"
|
|
75
|
+
};
|
|
76
|
+
let metaSnapshot = {
|
|
77
|
+
assetsBaseUrl: null,
|
|
78
|
+
error: null,
|
|
79
|
+
schema: null,
|
|
80
|
+
status: "idle"
|
|
81
|
+
};
|
|
82
|
+
let fetchAppsPromise = null;
|
|
83
|
+
let fetchMetaPromise = null;
|
|
84
|
+
let fetchAppsRequestId = 0;
|
|
85
|
+
const emit = () => {
|
|
86
|
+
for (const listener of listeners) {
|
|
87
|
+
listener();
|
|
29
88
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
89
|
+
};
|
|
90
|
+
const store = {
|
|
91
|
+
baseUrl,
|
|
92
|
+
views: createViewRegistry(),
|
|
93
|
+
setProvidedApps(apps) {
|
|
94
|
+
if (providedApps === apps) {
|
|
95
|
+
return;
|
|
33
96
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
97
|
+
providedApps = apps;
|
|
98
|
+
fetchAppsRequestId += 1;
|
|
99
|
+
fetchAppsPromise = null;
|
|
100
|
+
appsSnapshot = {
|
|
101
|
+
apps: apps ?? [],
|
|
102
|
+
error: null,
|
|
103
|
+
status: apps === void 0 ? "idle" : "ready"
|
|
104
|
+
};
|
|
105
|
+
emit();
|
|
106
|
+
if (apps === void 0) {
|
|
107
|
+
void store.fetchApps();
|
|
38
108
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
};
|
|
44
|
-
continue;
|
|
109
|
+
},
|
|
110
|
+
fetchApps: (options = {}) => {
|
|
111
|
+
if (providedApps !== void 0) {
|
|
112
|
+
return Promise.resolve();
|
|
45
113
|
}
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
continue;
|
|
114
|
+
if (fetchAppsPromise && !options.force) {
|
|
115
|
+
return fetchAppsPromise;
|
|
49
116
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
117
|
+
appsSnapshot = { ...appsSnapshot, status: "loading" };
|
|
118
|
+
emit();
|
|
119
|
+
const requestId = ++fetchAppsRequestId;
|
|
120
|
+
fetchAppsPromise = fetch(new URL("apps", baseUrl)).then(async (response) => {
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
throw new Error(`Unable to fetch TailorKit apps from ${baseUrl.toString()}.`);
|
|
123
|
+
}
|
|
124
|
+
const apps = await response.json();
|
|
125
|
+
if (requestId !== fetchAppsRequestId) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
appsSnapshot = {
|
|
129
|
+
apps,
|
|
130
|
+
error: null,
|
|
131
|
+
status: "ready"
|
|
57
132
|
};
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
133
|
+
emit();
|
|
134
|
+
}).catch((error) => {
|
|
135
|
+
if (requestId !== fetchAppsRequestId) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
appsSnapshot = {
|
|
139
|
+
...appsSnapshot,
|
|
140
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
141
|
+
status: "error"
|
|
142
|
+
};
|
|
143
|
+
emit();
|
|
144
|
+
});
|
|
145
|
+
return fetchAppsPromise;
|
|
146
|
+
},
|
|
147
|
+
fetchMeta: () => {
|
|
148
|
+
if (fetchMetaPromise) {
|
|
149
|
+
return fetchMetaPromise;
|
|
72
150
|
}
|
|
151
|
+
metaSnapshot = { ...metaSnapshot, status: "loading" };
|
|
152
|
+
emit();
|
|
153
|
+
fetchMetaPromise = fetch(new URL("meta", baseUrl)).then(async (response) => {
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
throw new Error(`Unable to fetch TailorKit metadata from ${baseUrl.toString()}.`);
|
|
156
|
+
}
|
|
157
|
+
const meta = await response.json();
|
|
158
|
+
metaSnapshot = {
|
|
159
|
+
assetsBaseUrl: meta.assetsBaseUrl ?? null,
|
|
160
|
+
error: null,
|
|
161
|
+
schema: meta.schema,
|
|
162
|
+
status: "ready"
|
|
163
|
+
};
|
|
164
|
+
emit();
|
|
165
|
+
}).catch((error) => {
|
|
166
|
+
metaSnapshot = {
|
|
167
|
+
...metaSnapshot,
|
|
168
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
169
|
+
status: "error"
|
|
170
|
+
};
|
|
171
|
+
emit();
|
|
172
|
+
});
|
|
173
|
+
return fetchMetaPromise;
|
|
174
|
+
},
|
|
175
|
+
getAppsSnapshot: () => appsSnapshot,
|
|
176
|
+
getMetaSnapshot: () => metaSnapshot,
|
|
177
|
+
subscribe: (listener) => {
|
|
178
|
+
listeners.add(listener);
|
|
179
|
+
return () => {
|
|
180
|
+
listeners.delete(listener);
|
|
181
|
+
};
|
|
73
182
|
}
|
|
74
183
|
};
|
|
184
|
+
return store;
|
|
75
185
|
}
|
|
76
|
-
function
|
|
77
|
-
|
|
186
|
+
function toBaseUrl(value) {
|
|
187
|
+
const url = value instanceof URL ? new URL(value) : new URL(value);
|
|
188
|
+
if (!url.pathname.endsWith("/")) {
|
|
189
|
+
url.pathname = `${url.pathname}/`;
|
|
190
|
+
}
|
|
191
|
+
return url;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/hooks/use-stable-context.ts
|
|
195
|
+
import { useRef } from "react";
|
|
196
|
+
function useStableContext(value) {
|
|
197
|
+
const key = JSON.stringify(value);
|
|
198
|
+
const ref = useRef({ key, value });
|
|
199
|
+
if (ref.current.key !== key) {
|
|
200
|
+
ref.current = { key, value };
|
|
201
|
+
}
|
|
202
|
+
return ref.current.value;
|
|
78
203
|
}
|
|
79
204
|
|
|
80
205
|
// src/components/context.ts
|
|
@@ -83,95 +208,15 @@ var TailorRootContext = createContext(null);
|
|
|
83
208
|
function useTailorRootContext(component) {
|
|
84
209
|
const context = useContext(TailorRootContext);
|
|
85
210
|
if (!context) {
|
|
86
|
-
throw new Error(`${component} must be rendered inside
|
|
211
|
+
throw new Error(`${component} must be rendered inside Root.`);
|
|
87
212
|
}
|
|
88
213
|
return context;
|
|
89
214
|
}
|
|
90
215
|
|
|
91
|
-
// src/components/root.tsx
|
|
92
|
-
import { jsx } from "react/jsx-runtime";
|
|
93
|
-
var EMPTY_APPS = [];
|
|
94
|
-
function Root({ apps: appsProp, children, render, tailor, ...props }) {
|
|
95
|
-
const appsResult = tailor.useApps();
|
|
96
|
-
const apps = appsProp ?? appsResult.data ?? EMPTY_APPS;
|
|
97
|
-
const context = useMemo2(
|
|
98
|
-
() => ({
|
|
99
|
-
apps
|
|
100
|
-
}),
|
|
101
|
-
[apps]
|
|
102
|
-
);
|
|
103
|
-
const element = render ? useRender({
|
|
104
|
-
defaultTagName: "div",
|
|
105
|
-
props: mergeProps({ children }, props),
|
|
106
|
-
render
|
|
107
|
-
}) : children;
|
|
108
|
-
return /* @__PURE__ */ jsx(TailorRootContext.Provider, { value: context, children: element });
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// src/hooks/use-apps.ts
|
|
112
|
-
import { useCallback, useEffect, useSyncExternalStore } from "react";
|
|
113
|
-
function createUseApps(store) {
|
|
114
|
-
return function useApps() {
|
|
115
|
-
const snapshot = useSyncExternalStore(
|
|
116
|
-
store.subscribe,
|
|
117
|
-
store.getAppsSnapshot,
|
|
118
|
-
store.getAppsSnapshot
|
|
119
|
-
);
|
|
120
|
-
useEffect(() => {
|
|
121
|
-
void store.fetchApps();
|
|
122
|
-
}, []);
|
|
123
|
-
const refetch = useCallback(() => store.fetchApps({ force: true }), []);
|
|
124
|
-
return toUseAppsResult(snapshot, refetch);
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
function toUseAppsResult(snapshot, refetch) {
|
|
128
|
-
return {
|
|
129
|
-
data: snapshot.status === "ready" ? snapshot.apps : void 0,
|
|
130
|
-
error: snapshot.error,
|
|
131
|
-
isError: snapshot.status === "error",
|
|
132
|
-
isLoading: snapshot.status === "loading",
|
|
133
|
-
isPending: snapshot.status === "idle" || snapshot.status === "loading",
|
|
134
|
-
isSuccess: snapshot.status === "ready",
|
|
135
|
-
refetch,
|
|
136
|
-
status: snapshot.status
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// src/hooks/use-current-screen.ts
|
|
141
|
-
import { useEffect as useEffect2, useMemo as useMemo3, useRef } from "react";
|
|
142
|
-
function createUseCurrentScreen(store) {
|
|
143
|
-
return function useCurrentScreen(options) {
|
|
144
|
-
useTailorRootContext("tailor.useCurrentScreen");
|
|
145
|
-
const id = useMemo3(() => /* @__PURE__ */ Symbol("tailorkit-current-screen"), []);
|
|
146
|
-
const status = options.status ?? "ready";
|
|
147
|
-
const context = "context" in options ? options.context : void 0;
|
|
148
|
-
const contextKey = JSON.stringify(context);
|
|
149
|
-
const contextRef = useRef({ key: contextKey, value: context });
|
|
150
|
-
if (contextRef.current.key !== contextKey) {
|
|
151
|
-
contextRef.current = { key: contextKey, value: context };
|
|
152
|
-
}
|
|
153
|
-
const contextSnapshot = contextRef.current.value;
|
|
154
|
-
useEffect2(
|
|
155
|
-
() => () => {
|
|
156
|
-
store.unregisterScreen(id);
|
|
157
|
-
},
|
|
158
|
-
[id]
|
|
159
|
-
);
|
|
160
|
-
useEffect2(() => {
|
|
161
|
-
store.registerScreen({
|
|
162
|
-
context: contextSnapshot,
|
|
163
|
-
id,
|
|
164
|
-
screen: options.screen,
|
|
165
|
-
status
|
|
166
|
-
});
|
|
167
|
-
}, [contextSnapshot, id, options.screen, status]);
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
216
|
// src/primitives.tsx
|
|
172
|
-
import { createContext as createContext2, useContext as useContext2, useId, useMemo
|
|
217
|
+
import { createContext as createContext2, useContext as useContext2, useId, useMemo } from "react";
|
|
173
218
|
import { resolveTheme } from "@tailorkit/core/primitives/theme";
|
|
174
|
-
import { jsx
|
|
219
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
175
220
|
var PrimitiveThemeContext = createContext2(null);
|
|
176
221
|
var tokenGroups = [
|
|
177
222
|
"space",
|
|
@@ -292,10 +337,10 @@ var buildPrimitiveCss = ({
|
|
|
292
337
|
display,
|
|
293
338
|
nodeId,
|
|
294
339
|
props,
|
|
295
|
-
|
|
340
|
+
viewId,
|
|
296
341
|
theme
|
|
297
342
|
}) => {
|
|
298
|
-
const selector = `[data-tailorkit-
|
|
343
|
+
const selector = `[data-tailorkit-view="${cssEscape(viewId)}"] [data-tailorkit-node="${cssEscape(
|
|
299
344
|
nodeId
|
|
300
345
|
)}"]`;
|
|
301
346
|
const base = [`display: ${display};`];
|
|
@@ -323,8 +368,8 @@ var buildPrimitiveCss = ({
|
|
|
323
368
|
}
|
|
324
369
|
return rules.join("\n");
|
|
325
370
|
};
|
|
326
|
-
var buildThemeCss = (
|
|
327
|
-
const selector = `[data-tailorkit-
|
|
371
|
+
var buildThemeCss = (viewId, theme) => {
|
|
372
|
+
const selector = `[data-tailorkit-view="${cssEscape(viewId)}"]`;
|
|
328
373
|
const declarations = [];
|
|
329
374
|
const resolvedTheme = resolveTheme(theme);
|
|
330
375
|
for (const group of tokenGroups) {
|
|
@@ -336,7 +381,7 @@ var buildThemeCss = (screenId, theme) => {
|
|
|
336
381
|
};
|
|
337
382
|
var usePrimitiveNodeId = () => {
|
|
338
383
|
const id = useId();
|
|
339
|
-
return
|
|
384
|
+
return useMemo(() => `tailorkit-${id.replaceAll(":", "")}`, [id]);
|
|
340
385
|
};
|
|
341
386
|
var Primitive = ({
|
|
342
387
|
children,
|
|
@@ -351,34 +396,34 @@ var Primitive = ({
|
|
|
351
396
|
display,
|
|
352
397
|
nodeId,
|
|
353
398
|
props,
|
|
354
|
-
|
|
399
|
+
viewId: context.viewId,
|
|
355
400
|
theme: context.theme
|
|
356
401
|
}) : "";
|
|
357
402
|
return /* @__PURE__ */ jsxs(Element, { "data-tailorkit-node": nodeId, children: [
|
|
358
|
-
css === "" ? null : /* @__PURE__ */
|
|
403
|
+
css === "" ? null : /* @__PURE__ */ jsx("style", { "data-tailorkit-node-style": nodeId, children: css }),
|
|
359
404
|
children
|
|
360
405
|
] });
|
|
361
406
|
};
|
|
362
407
|
var primitives = {
|
|
363
|
-
Box: ({ props,
|
|
364
|
-
Flex: ({ props,
|
|
365
|
-
Grid: ({ props,
|
|
366
|
-
Inline: ({ props,
|
|
408
|
+
Box: ({ props, children }) => /* @__PURE__ */ jsx(Primitive, { display: "block", element: "div", props, children }),
|
|
409
|
+
Flex: ({ props, children }) => /* @__PURE__ */ jsx(Primitive, { display: "flex", element: "div", props, children }),
|
|
410
|
+
Grid: ({ props, children }) => /* @__PURE__ */ jsx(Primitive, { display: "grid", element: "div", props, children }),
|
|
411
|
+
Inline: ({ props, children }) => /* @__PURE__ */ jsx(Primitive, { display: "inline", element: "span", props, children })
|
|
367
412
|
};
|
|
368
413
|
|
|
369
414
|
// src/remote-view.tsx
|
|
370
415
|
import {
|
|
371
416
|
Component,
|
|
372
417
|
Fragment,
|
|
373
|
-
createElement
|
|
418
|
+
createElement,
|
|
374
419
|
memo,
|
|
375
|
-
useCallback
|
|
420
|
+
useCallback,
|
|
376
421
|
useContext as useContext3,
|
|
377
|
-
useEffect
|
|
378
|
-
useMemo as
|
|
422
|
+
useEffect,
|
|
423
|
+
useMemo as useMemo2,
|
|
379
424
|
useRef as useRef2,
|
|
380
425
|
useState,
|
|
381
|
-
useSyncExternalStore
|
|
426
|
+
useSyncExternalStore
|
|
382
427
|
} from "react";
|
|
383
428
|
import { createIframeUiHost } from "@tailorkit/sandbox/host";
|
|
384
429
|
|
|
@@ -494,7 +539,7 @@ import { createContext as createContext3 } from "react";
|
|
|
494
539
|
var RemoteUIContext = createContext3(null);
|
|
495
540
|
|
|
496
541
|
// src/remote-view.tsx
|
|
497
|
-
import { jsx as
|
|
542
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
498
543
|
var formatError = (error) => error.message;
|
|
499
544
|
var RemoteErrorBoundary = class extends Component {
|
|
500
545
|
state = { error: null };
|
|
@@ -506,7 +551,7 @@ var RemoteErrorBoundary = class extends Component {
|
|
|
506
551
|
}
|
|
507
552
|
render() {
|
|
508
553
|
if (this.state.error) {
|
|
509
|
-
return
|
|
554
|
+
return createElement("div", null, formatError(this.state.error));
|
|
510
555
|
}
|
|
511
556
|
return this.props.children;
|
|
512
557
|
}
|
|
@@ -515,8 +560,7 @@ function RemoteViewHost({
|
|
|
515
560
|
appUrl,
|
|
516
561
|
components: components2,
|
|
517
562
|
createIframe,
|
|
518
|
-
props
|
|
519
|
-
runtimeUrl
|
|
563
|
+
props
|
|
520
564
|
}) {
|
|
521
565
|
const appKey = appUrl.toString();
|
|
522
566
|
const storeRef = useRef2(null);
|
|
@@ -529,6 +573,12 @@ function RemoteViewHost({
|
|
|
529
573
|
appKeyRef.current = appKey;
|
|
530
574
|
store.clear();
|
|
531
575
|
}
|
|
576
|
+
const hostRef = useRef2(null);
|
|
577
|
+
const propsRef = useRef2(props);
|
|
578
|
+
useEffect(() => {
|
|
579
|
+
propsRef.current = props;
|
|
580
|
+
hostRef.current?.setProps(props);
|
|
581
|
+
}, [props]);
|
|
532
582
|
const dispatchRef = useRef2(null);
|
|
533
583
|
const [error, setError] = useState(null);
|
|
534
584
|
const [status, setStatus] = useState("starting");
|
|
@@ -537,22 +587,22 @@ function RemoteViewHost({
|
|
|
537
587
|
dispatch: (payload) => dispatchRef.current?.(payload),
|
|
538
588
|
store
|
|
539
589
|
});
|
|
540
|
-
|
|
590
|
+
useEffect(() => {
|
|
541
591
|
ctxRef.current.components = components2;
|
|
542
592
|
}, [components2]);
|
|
543
|
-
const ui =
|
|
593
|
+
const ui = useMemo2(() => {
|
|
544
594
|
const ctx = ctxRef.current;
|
|
545
|
-
return
|
|
595
|
+
return createElement(
|
|
546
596
|
RemoteUIContext.Provider,
|
|
547
597
|
{ value: ctx },
|
|
548
|
-
|
|
598
|
+
createElement(
|
|
549
599
|
RemoteErrorBoundary,
|
|
550
600
|
{ key: appKey },
|
|
551
|
-
|
|
601
|
+
createElement(RemoteRoot, { store: ctx.store })
|
|
552
602
|
)
|
|
553
603
|
);
|
|
554
604
|
}, [appKey]);
|
|
555
|
-
|
|
605
|
+
useEffect(() => {
|
|
556
606
|
const host = createIframeUiHost(appUrl, {
|
|
557
607
|
createIframe,
|
|
558
608
|
onError: (error2) => {
|
|
@@ -560,9 +610,9 @@ function RemoteViewHost({
|
|
|
560
610
|
setError(error2);
|
|
561
611
|
setStatus("error");
|
|
562
612
|
},
|
|
563
|
-
props
|
|
564
|
-
runtimeUrl
|
|
613
|
+
props: propsRef.current
|
|
565
614
|
});
|
|
615
|
+
hostRef.current = host;
|
|
566
616
|
dispatchRef.current = (payload) => host.dispatch(payload);
|
|
567
617
|
const unsubscribe = host.subscribe(() => {
|
|
568
618
|
const tree = host.getSnapshot();
|
|
@@ -577,26 +627,27 @@ function RemoteViewHost({
|
|
|
577
627
|
return () => {
|
|
578
628
|
unsubscribe();
|
|
579
629
|
dispatchRef.current = null;
|
|
630
|
+
hostRef.current = null;
|
|
580
631
|
host.destroy();
|
|
581
632
|
};
|
|
582
|
-
}, [appUrl, createIframe,
|
|
633
|
+
}, [appUrl, createIframe, store]);
|
|
583
634
|
if (status === "error" && error) {
|
|
584
|
-
return
|
|
635
|
+
return createElement("div", null, formatError(error));
|
|
585
636
|
}
|
|
586
637
|
return ui;
|
|
587
638
|
}
|
|
588
639
|
function RemoteRoot({ store }) {
|
|
589
|
-
const subscribe =
|
|
590
|
-
const getSnapshot =
|
|
591
|
-
const rootId =
|
|
640
|
+
const subscribe = useCallback((listener) => store.subscribeRoot(listener), [store]);
|
|
641
|
+
const getSnapshot = useCallback(() => store.getRootId(), [store]);
|
|
642
|
+
const rootId = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
592
643
|
if (rootId === null) {
|
|
593
644
|
return null;
|
|
594
645
|
}
|
|
595
|
-
return /* @__PURE__ */
|
|
646
|
+
return /* @__PURE__ */ jsx2(RemoteView, { nodeId: rootId });
|
|
596
647
|
}
|
|
597
648
|
var RemoteElementView = memo(({ ctx, node }) => {
|
|
598
649
|
const { dispatch, components: components2 } = ctx;
|
|
599
|
-
const children = node.children.map((child) => /* @__PURE__ */
|
|
650
|
+
const children = node.children.map((child) => /* @__PURE__ */ jsx2(RemoteView, { nodeId: child.id }, child.id));
|
|
600
651
|
const component = components2[node.type];
|
|
601
652
|
if (component === void 0) {
|
|
602
653
|
throw new Error(`TailorKit component "${node.type}" is not registered.`);
|
|
@@ -614,7 +665,7 @@ var RemoteElementView = memo(({ ctx, node }) => {
|
|
|
614
665
|
});
|
|
615
666
|
};
|
|
616
667
|
}
|
|
617
|
-
return
|
|
668
|
+
return createElement(component, props, ...children);
|
|
618
669
|
});
|
|
619
670
|
var RemoteView = memo(function RemoteView2({ nodeId }) {
|
|
620
671
|
const ctx = useContext3(RemoteUIContext);
|
|
@@ -622,27 +673,109 @@ var RemoteView = memo(function RemoteView2({ nodeId }) {
|
|
|
622
673
|
return null;
|
|
623
674
|
}
|
|
624
675
|
const { store } = ctx;
|
|
625
|
-
const subscribe =
|
|
676
|
+
const subscribe = useCallback(
|
|
626
677
|
(listener) => store.subscribe(nodeId, listener),
|
|
627
678
|
[store, nodeId]
|
|
628
679
|
);
|
|
629
|
-
const getSnapshot =
|
|
630
|
-
const node =
|
|
680
|
+
const getSnapshot = useCallback(() => store.getNode(nodeId), [store, nodeId]);
|
|
681
|
+
const node = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
631
682
|
if (node === null) {
|
|
632
683
|
return null;
|
|
633
684
|
}
|
|
634
685
|
if (node.kind === "text") {
|
|
635
686
|
return node.text;
|
|
636
687
|
}
|
|
637
|
-
const childElements = node.children.map((child) => /* @__PURE__ */
|
|
688
|
+
const childElements = node.children.map((child) => /* @__PURE__ */ jsx2(RemoteView2, { nodeId: child.id }, child.id));
|
|
638
689
|
if (node.kind === "fragment") {
|
|
639
|
-
return
|
|
690
|
+
return createElement(Fragment, { key: node.id }, ...childElements);
|
|
640
691
|
}
|
|
641
|
-
return /* @__PURE__ */
|
|
692
|
+
return /* @__PURE__ */ jsx2(RemoteElementView, { node, ctx });
|
|
642
693
|
});
|
|
643
694
|
|
|
695
|
+
// src/components/app-view.tsx
|
|
696
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
697
|
+
var AppView = ({
|
|
698
|
+
app,
|
|
699
|
+
slot,
|
|
700
|
+
createIframe,
|
|
701
|
+
fallback = null,
|
|
702
|
+
...viewProps
|
|
703
|
+
}) => {
|
|
704
|
+
const { store, client } = useTailorRootContext("AppView");
|
|
705
|
+
const { theme, components: wrappedComponents } = client;
|
|
706
|
+
const reactId = useId2();
|
|
707
|
+
const currentView = useSyncExternalStore2(
|
|
708
|
+
store.views.subscribe,
|
|
709
|
+
store.views.getSnapshot,
|
|
710
|
+
store.views.getSnapshot
|
|
711
|
+
);
|
|
712
|
+
const view = viewProps.view;
|
|
713
|
+
const suppliedContext = viewProps.context;
|
|
714
|
+
const context = useStableContext(suppliedContext);
|
|
715
|
+
const status = viewProps.status ?? "ready";
|
|
716
|
+
const props = useMemo3(() => {
|
|
717
|
+
if (view !== void 0) {
|
|
718
|
+
return {
|
|
719
|
+
slot,
|
|
720
|
+
view,
|
|
721
|
+
layers: [
|
|
722
|
+
...(currentView?.layers ?? []).filter(
|
|
723
|
+
(layer) => layer.path !== view && isViewAncestor2(layer.path, view)
|
|
724
|
+
),
|
|
725
|
+
{ path: view, context, status }
|
|
726
|
+
]
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
return currentView === null ? void 0 : { slot, ...currentView };
|
|
730
|
+
}, [context, currentView, view, status, slot]);
|
|
731
|
+
const meta = useSyncExternalStore2(store.subscribe, store.getMetaSnapshot, store.getMetaSnapshot);
|
|
732
|
+
const runtimeProps = useMemo3(
|
|
733
|
+
() => props === void 0 ? void 0 : {
|
|
734
|
+
...props,
|
|
735
|
+
declaredViews: Object.keys(meta.schema?.views ?? {}),
|
|
736
|
+
supportedViews: meta.schema?.slots[slot]?.views ?? []
|
|
737
|
+
},
|
|
738
|
+
[props, meta.schema, slot]
|
|
739
|
+
);
|
|
740
|
+
const assetsBaseUrl = app.clientPath ? null : meta.assetsBaseUrl;
|
|
741
|
+
const appUrl = useMemo3(
|
|
742
|
+
() => resolveAppUrl(app, store.baseUrl, assetsBaseUrl),
|
|
743
|
+
[app, assetsBaseUrl, store.baseUrl]
|
|
744
|
+
);
|
|
745
|
+
useEffect2(() => {
|
|
746
|
+
void store.fetchMeta();
|
|
747
|
+
}, [store]);
|
|
748
|
+
if (props === void 0 || appUrl === null || meta.schema === null) {
|
|
749
|
+
return fallback;
|
|
750
|
+
}
|
|
751
|
+
const viewId = `tailorkit-view-${reactId.replaceAll(":", "")}`;
|
|
752
|
+
return /* @__PURE__ */ jsx3(PrimitiveThemeContext.Provider, { value: { viewId, theme }, children: /* @__PURE__ */ jsxs2("div", { "data-tailorkit-view": viewId, children: [
|
|
753
|
+
/* @__PURE__ */ jsx3("style", { "data-tailorkit-theme-style": viewId, children: buildThemeCss(viewId, theme) }),
|
|
754
|
+
/* @__PURE__ */ jsx3(
|
|
755
|
+
RemoteViewHost,
|
|
756
|
+
{
|
|
757
|
+
appUrl: appUrl.toString(),
|
|
758
|
+
components: wrappedComponents,
|
|
759
|
+
createIframe,
|
|
760
|
+
props: runtimeProps
|
|
761
|
+
}
|
|
762
|
+
)
|
|
763
|
+
] }) });
|
|
764
|
+
};
|
|
765
|
+
function resolveAppUrl(app, baseUrl, assetsBaseUrl) {
|
|
766
|
+
if (app.clientPath) {
|
|
767
|
+
return new URL(app.clientPath, baseUrl);
|
|
768
|
+
}
|
|
769
|
+
if (!assetsBaseUrl || !app.projectId || !app.currentDeployment?.id) {
|
|
770
|
+
return null;
|
|
771
|
+
}
|
|
772
|
+
return new URL(
|
|
773
|
+
`projects/${app.projectId}/apps/${app.id}/deployments/${app.currentDeployment.id}/files/client.js`,
|
|
774
|
+
toBaseUrl(assetsBaseUrl)
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
|
|
644
778
|
// src/tailor-kit.tsx
|
|
645
|
-
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
646
779
|
var componentTagPrefix = "tailorkit-";
|
|
647
780
|
var toComponentTagName = (name) => `${componentTagPrefix}${name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1-$2").replaceAll(/[\s_]+/gu, "-").toLowerCase()}`;
|
|
648
781
|
function components(_schema, customComponents) {
|
|
@@ -653,7 +786,6 @@ function createTailorKitClient(options) {
|
|
|
653
786
|
}
|
|
654
787
|
function createReactTailorKitClient(options) {
|
|
655
788
|
const wrappedComponents = {};
|
|
656
|
-
const store = createTailorKitStore(options.baseUrl);
|
|
657
789
|
const theme = options.theme ?? {};
|
|
658
790
|
for (const [name, renderer] of Object.entries(options.components ?? {})) {
|
|
659
791
|
if (renderer) {
|
|
@@ -663,258 +795,179 @@ function createReactTailorKitClient(options) {
|
|
|
663
795
|
}) {
|
|
664
796
|
return renderer({
|
|
665
797
|
props,
|
|
666
|
-
|
|
798
|
+
children
|
|
667
799
|
});
|
|
668
800
|
};
|
|
669
801
|
wrappedComponents[name] = TailorKitComponent;
|
|
670
802
|
wrappedComponents[toComponentTagName(name)] = TailorKitComponent;
|
|
671
803
|
}
|
|
672
804
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
)
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
const status = screenProps.status ?? "ready";
|
|
691
|
-
const props = useMemo6(() => {
|
|
692
|
-
if (screen !== void 0) {
|
|
693
|
-
return {
|
|
694
|
-
screen: {
|
|
695
|
-
context,
|
|
696
|
-
path: screen,
|
|
697
|
-
status
|
|
698
|
-
}
|
|
699
|
-
};
|
|
805
|
+
return { baseUrl: options.baseUrl, components: wrappedComponents, theme };
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// src/components/root.tsx
|
|
809
|
+
import { useEffect as useEffect3, useMemo as useMemo5, useState as useState2 } from "react";
|
|
810
|
+
|
|
811
|
+
// src/components/render.tsx
|
|
812
|
+
import { cloneElement, createElement as createElement2, isValidElement, useMemo as useMemo4 } from "react";
|
|
813
|
+
function useRender({
|
|
814
|
+
defaultTagName,
|
|
815
|
+
props,
|
|
816
|
+
render
|
|
817
|
+
}) {
|
|
818
|
+
return useMemo4(() => {
|
|
819
|
+
if (render) {
|
|
820
|
+
if (!isValidElement(render)) {
|
|
821
|
+
throw new Error("The render prop must be a valid React element.");
|
|
700
822
|
}
|
|
701
|
-
return
|
|
702
|
-
screen: {
|
|
703
|
-
context: currentScreen.context,
|
|
704
|
-
path: currentScreen.screen,
|
|
705
|
-
status: currentScreen.status
|
|
706
|
-
}
|
|
707
|
-
};
|
|
708
|
-
}, [context, currentScreen, screen, status]);
|
|
709
|
-
const meta = useSyncExternalStore3(
|
|
710
|
-
store.subscribe,
|
|
711
|
-
store.getMetaSnapshot,
|
|
712
|
-
store.getMetaSnapshot
|
|
713
|
-
);
|
|
714
|
-
const assetsBaseUrl = app.clientPath ? null : meta.assetsBaseUrl;
|
|
715
|
-
const appUrl = useMemo6(
|
|
716
|
-
() => resolveAppUrl(app, store.baseUrl, assetsBaseUrl),
|
|
717
|
-
[app, assetsBaseUrl]
|
|
718
|
-
);
|
|
719
|
-
useEffect4(() => {
|
|
720
|
-
void store.fetchMeta();
|
|
721
|
-
}, []);
|
|
722
|
-
if (props === void 0 || appUrl === null) {
|
|
723
|
-
return fallback;
|
|
823
|
+
return cloneElement(render, mergeProps(render.props, props));
|
|
724
824
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
/* @__PURE__ */ jsx4("style", { "data-tailorkit-theme-style": screenId, children: buildThemeCss(screenId, theme) }),
|
|
728
|
-
/* @__PURE__ */ jsx4(
|
|
729
|
-
RemoteViewHost,
|
|
730
|
-
{
|
|
731
|
-
appUrl,
|
|
732
|
-
components: wrappedComponents,
|
|
733
|
-
createIframe,
|
|
734
|
-
props,
|
|
735
|
-
runtimeUrl
|
|
736
|
-
}
|
|
737
|
-
)
|
|
738
|
-
] }) });
|
|
739
|
-
};
|
|
740
|
-
const client = {
|
|
741
|
-
AppView,
|
|
742
|
-
Root: (props) => /* @__PURE__ */ jsx4(Root, { ...props, tailor: client }),
|
|
743
|
-
getApp: store.getApp,
|
|
744
|
-
getApps: store.getApps,
|
|
745
|
-
useApps,
|
|
746
|
-
useCurrentScreen
|
|
747
|
-
};
|
|
748
|
-
return {
|
|
749
|
-
...client
|
|
750
|
-
};
|
|
825
|
+
return createElement2(defaultTagName, props);
|
|
826
|
+
}, [defaultTagName, props, render]);
|
|
751
827
|
}
|
|
752
|
-
function
|
|
753
|
-
const
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
apps: [],
|
|
758
|
-
error: null,
|
|
759
|
-
status: "idle"
|
|
760
|
-
};
|
|
761
|
-
let metaSnapshot = {
|
|
762
|
-
assetsBaseUrl: null,
|
|
763
|
-
error: null,
|
|
764
|
-
schema: null,
|
|
765
|
-
status: "idle"
|
|
766
|
-
};
|
|
767
|
-
let currentScreen = null;
|
|
768
|
-
let fetchAppsPromise = null;
|
|
769
|
-
let fetchMetaPromise = null;
|
|
770
|
-
let fetchAppsRequestId = 0;
|
|
771
|
-
let nextOrder = 0;
|
|
772
|
-
const emit = () => {
|
|
773
|
-
for (const listener of listeners) {
|
|
774
|
-
listener();
|
|
828
|
+
function mergeProps(...propSets) {
|
|
829
|
+
const merged = {};
|
|
830
|
+
for (const props of propSets) {
|
|
831
|
+
if (!props) {
|
|
832
|
+
continue;
|
|
775
833
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
for (const screen of screens.values()) {
|
|
780
|
-
const depth = getScreenDepth(screen.screen);
|
|
781
|
-
const selectedDepth = selected === null ? -1 : getScreenDepth(selected.screen);
|
|
782
|
-
if (selected === null || depth > selectedDepth) {
|
|
783
|
-
selected = screen;
|
|
784
|
-
}
|
|
785
|
-
if (selected !== null && depth === selectedDepth && screen.order > selected.order) {
|
|
786
|
-
selected = screen;
|
|
834
|
+
for (const [key, value] of Object.entries(props)) {
|
|
835
|
+
if (value === void 0) {
|
|
836
|
+
continue;
|
|
787
837
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
if (typeof console !== "undefined" && selected !== null && deepestScreens.length > 1) {
|
|
793
|
-
const screenNames = deepestScreens.map((screen) => `"${screen.screen}"`).join(", ");
|
|
794
|
-
console.warn(
|
|
795
|
-
`TailorKit found multiple active screens at the same hierarchy depth: ${screenNames}. TailorKit selected "${selected.screen}" by mount order. Only one route at a hierarchy depth should call useCurrentScreen.`
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
currentScreen = selected;
|
|
799
|
-
};
|
|
800
|
-
return {
|
|
801
|
-
baseUrl,
|
|
802
|
-
fetchApps: (options = {}) => {
|
|
803
|
-
if (fetchAppsPromise && !options.force) {
|
|
804
|
-
return fetchAppsPromise;
|
|
838
|
+
const existing = merged[key];
|
|
839
|
+
if (key === "className" && existing && value) {
|
|
840
|
+
merged[key] = `${existing} ${value}`;
|
|
841
|
+
continue;
|
|
805
842
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
if (!response.ok) {
|
|
811
|
-
throw new Error(`Unable to fetch TailorKit apps from ${baseUrl.toString()}.`);
|
|
812
|
-
}
|
|
813
|
-
const apps = await response.json();
|
|
814
|
-
if (requestId !== fetchAppsRequestId) {
|
|
815
|
-
return;
|
|
816
|
-
}
|
|
817
|
-
appsSnapshot = {
|
|
818
|
-
apps,
|
|
819
|
-
error: null,
|
|
820
|
-
status: "ready"
|
|
821
|
-
};
|
|
822
|
-
emit();
|
|
823
|
-
}).catch((error) => {
|
|
824
|
-
if (requestId !== fetchAppsRequestId) {
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
appsSnapshot = {
|
|
828
|
-
...appsSnapshot,
|
|
829
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
830
|
-
status: "error"
|
|
843
|
+
if (key === "style" && existing && value) {
|
|
844
|
+
merged[key] = {
|
|
845
|
+
...existing,
|
|
846
|
+
...value
|
|
831
847
|
};
|
|
832
|
-
|
|
833
|
-
});
|
|
834
|
-
return fetchAppsPromise;
|
|
835
|
-
},
|
|
836
|
-
fetchMeta: () => {
|
|
837
|
-
if (fetchMetaPromise) {
|
|
838
|
-
return fetchMetaPromise;
|
|
848
|
+
continue;
|
|
839
849
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
status: "ready"
|
|
852
|
-
};
|
|
853
|
-
emit();
|
|
854
|
-
}).catch((error) => {
|
|
855
|
-
metaSnapshot = {
|
|
856
|
-
...metaSnapshot,
|
|
857
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
858
|
-
status: "error"
|
|
850
|
+
if (key === "ref" && existing && value) {
|
|
851
|
+
merged[key] = composeRefs(existing, value);
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
if (isEventHandler(key) && typeof existing === "function" && typeof value === "function") {
|
|
855
|
+
merged[key] = (...args) => {
|
|
856
|
+
existing(...args);
|
|
857
|
+
const event = args[0];
|
|
858
|
+
if (!event?.defaultPrevented) {
|
|
859
|
+
value(...args);
|
|
860
|
+
}
|
|
859
861
|
};
|
|
860
|
-
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
});
|
|
875
|
-
if (!existing) {
|
|
876
|
-
nextOrder += 1;
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
merged[key] = value;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return merged;
|
|
868
|
+
}
|
|
869
|
+
function composeRefs(...refs) {
|
|
870
|
+
return (node) => {
|
|
871
|
+
for (const ref of refs) {
|
|
872
|
+
if (typeof ref === "function") {
|
|
873
|
+
ref(node);
|
|
874
|
+
} else if (ref) {
|
|
875
|
+
ref.current = node;
|
|
877
876
|
}
|
|
878
|
-
selectCurrentScreen();
|
|
879
|
-
emit();
|
|
880
|
-
},
|
|
881
|
-
subscribe: (listener) => {
|
|
882
|
-
listeners.add(listener);
|
|
883
|
-
return () => {
|
|
884
|
-
listeners.delete(listener);
|
|
885
|
-
};
|
|
886
|
-
},
|
|
887
|
-
unregisterScreen: (id) => {
|
|
888
|
-
screens.delete(id);
|
|
889
|
-
selectCurrentScreen();
|
|
890
|
-
emit();
|
|
891
877
|
}
|
|
892
878
|
};
|
|
893
879
|
}
|
|
894
|
-
function
|
|
895
|
-
return
|
|
880
|
+
function isEventHandler(key) {
|
|
881
|
+
return /^on[A-Z]/u.test(key);
|
|
896
882
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
883
|
+
|
|
884
|
+
// src/components/root.tsx
|
|
885
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
886
|
+
function Root({ apps: appsProp, children, render, client, ...props }) {
|
|
887
|
+
const baseUrl = toBaseUrl(client.baseUrl).toString();
|
|
888
|
+
const [previousStore, setStore] = useState2(() => createTailorKitStore(baseUrl, appsProp));
|
|
889
|
+
let store = previousStore;
|
|
890
|
+
if (previousStore.baseUrl.toString() !== baseUrl) {
|
|
891
|
+
store = createTailorKitStore(baseUrl, appsProp);
|
|
892
|
+
setStore(store);
|
|
903
893
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
894
|
+
useEffect3(() => {
|
|
895
|
+
store.setProvidedApps(appsProp);
|
|
896
|
+
}, [store, appsProp]);
|
|
897
|
+
const context = useMemo5(
|
|
898
|
+
() => ({
|
|
899
|
+
store,
|
|
900
|
+
client
|
|
901
|
+
}),
|
|
902
|
+
[store, client]
|
|
903
|
+
);
|
|
904
|
+
const element = render ? useRender({
|
|
905
|
+
defaultTagName: "div",
|
|
906
|
+
props: mergeProps({ children }, props),
|
|
907
|
+
render
|
|
908
|
+
}) : children;
|
|
909
|
+
return /* @__PURE__ */ jsx4(TailorRootContext.Provider, { value: context, children: element });
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// src/hooks/use-apps.ts
|
|
913
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
914
|
+
function useApps() {
|
|
915
|
+
return useAppsStore(useTailorRootContext("useApps").store);
|
|
916
|
+
}
|
|
917
|
+
function useAppsStore(store) {
|
|
918
|
+
const snapshot = useSyncExternalStore3(
|
|
919
|
+
store.subscribe,
|
|
920
|
+
store.getAppsSnapshot,
|
|
921
|
+
store.getAppsSnapshot
|
|
907
922
|
);
|
|
923
|
+
useEffect4(() => {
|
|
924
|
+
void store.fetchApps();
|
|
925
|
+
}, [store]);
|
|
926
|
+
const refetch = useCallback2(() => store.fetchApps({ force: true }), [store]);
|
|
927
|
+
return toUseAppsResult(snapshot, refetch);
|
|
908
928
|
}
|
|
909
|
-
function
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
929
|
+
function toUseAppsResult(snapshot, refetch) {
|
|
930
|
+
return {
|
|
931
|
+
data: snapshot.status === "ready" ? snapshot.apps : void 0,
|
|
932
|
+
error: snapshot.error,
|
|
933
|
+
isError: snapshot.status === "error",
|
|
934
|
+
isLoading: snapshot.status === "loading",
|
|
935
|
+
isPending: snapshot.status === "idle" || snapshot.status === "loading",
|
|
936
|
+
isSuccess: snapshot.status === "ready",
|
|
937
|
+
refetch,
|
|
938
|
+
status: snapshot.status
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// src/hooks/use-view.ts
|
|
943
|
+
import { useEffect as useEffect5, useMemo as useMemo6 } from "react";
|
|
944
|
+
function useView(view, options) {
|
|
945
|
+
const { store } = useTailorRootContext("useView");
|
|
946
|
+
const id = useMemo6(() => /* @__PURE__ */ Symbol("tailorkit-current-view"), []);
|
|
947
|
+
const status = options.status ?? "ready";
|
|
948
|
+
const context = "context" in options ? options.context : void 0;
|
|
949
|
+
const contextSnapshot = useStableContext(context);
|
|
950
|
+
useEffect5(
|
|
951
|
+
() => () => {
|
|
952
|
+
store.views.unregister(id);
|
|
953
|
+
},
|
|
954
|
+
[id, store]
|
|
955
|
+
);
|
|
956
|
+
useEffect5(() => {
|
|
957
|
+
store.views.register({
|
|
958
|
+
context: contextSnapshot,
|
|
959
|
+
id,
|
|
960
|
+
view,
|
|
961
|
+
status
|
|
962
|
+
});
|
|
963
|
+
}, [contextSnapshot, id, view, status, store]);
|
|
915
964
|
}
|
|
916
965
|
export {
|
|
966
|
+
AppView,
|
|
967
|
+
Root,
|
|
917
968
|
components,
|
|
918
969
|
createTailorKitClient,
|
|
919
|
-
primitives
|
|
970
|
+
primitives,
|
|
971
|
+
useApps,
|
|
972
|
+
useView
|
|
920
973
|
};
|