@tailorkit/react 0.1.0-beta.2 → 0.1.0-beta.21
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/LICENSE.md +201 -0
- package/README.md +3 -0
- package/dist/components/context.d.ts +5 -8
- package/dist/components/index.d.ts +1 -1
- package/dist/components/render.d.ts +0 -1
- package/dist/components/root.d.ts +3 -5
- package/dist/components/slot.d.ts +51 -0
- package/dist/hooks/use-apps.d.ts +12 -3
- package/dist/hooks/use-register-view.d.ts +25 -0
- package/dist/hooks/use-slot-instances.d.ts +13 -0
- package/dist/hooks/use-stable-context.d.ts +2 -0
- package/dist/hooks/use-views.d.ts +9 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.js +520 -518
- package/dist/primitives.d.ts +10 -19
- package/dist/remote-context.d.ts +3 -4
- package/dist/remote-view.d.ts +8 -4
- package/dist/scope-query.d.ts +1 -0
- package/dist/slot-view.d.ts +1 -0
- package/dist/tailorkit.d.ts +78 -0
- package/package.json +16 -24
- package/LICENSE +0 -674
- package/dist/components/context.d.ts.map +0 -1
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/render.d.ts.map +0 -1
- package/dist/components/root.d.ts.map +0 -1
- package/dist/hooks/use-apps.d.ts.map +0 -1
- package/dist/hooks/use-current-screen.d.ts +0 -24
- package/dist/hooks/use-current-screen.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/node-store.d.ts +0 -17
- package/dist/node-store.d.ts.map +0 -1
- package/dist/primitives.d.ts.map +0 -1
- package/dist/remote-context.d.ts.map +0 -1
- package/dist/remote-view.d.ts.map +0 -1
- package/dist/tailor-kit.d.ts +0 -117
- package/dist/tailor-kit.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,133 +1,79 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import { useEffect as useEffect4, useId as useId2, useMemo as
|
|
1
|
+
// src/components/slot.tsx
|
|
2
|
+
import { useCallback as useCallback4, useEffect as useEffect4, useId as useId2, useMemo as useMemo5, useRef as useRef3, useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
3
3
|
|
|
4
|
-
// src/
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}) {
|
|
14
|
-
return useMemo(() => {
|
|
15
|
-
if (render) {
|
|
16
|
-
if (!isValidElement(render)) {
|
|
17
|
-
throw new Error("The render prop must be a valid React element.");
|
|
18
|
-
}
|
|
19
|
-
return cloneElement(render, mergeProps(render.props, props));
|
|
20
|
-
}
|
|
21
|
-
return createElement(defaultTagName, props);
|
|
22
|
-
}, [defaultTagName, props, render]);
|
|
23
|
-
}
|
|
24
|
-
function mergeProps(...propSets) {
|
|
25
|
-
const merged = {};
|
|
26
|
-
for (const props of propSets) {
|
|
27
|
-
if (!props) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
for (const [key, value] of Object.entries(props)) {
|
|
31
|
-
if (value === void 0) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
const existing = merged[key];
|
|
35
|
-
if (key === "className" && existing && value) {
|
|
36
|
-
merged[key] = `${existing} ${value}`;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (key === "style" && existing && value) {
|
|
40
|
-
merged[key] = {
|
|
41
|
-
...existing,
|
|
42
|
-
...value
|
|
43
|
-
};
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (key === "ref" && existing && value) {
|
|
47
|
-
merged[key] = composeRefs(existing, value);
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
if (isEventHandler(key) && typeof existing === "function" && typeof value === "function") {
|
|
51
|
-
merged[key] = (...args) => {
|
|
52
|
-
existing(...args);
|
|
53
|
-
const event = args[0];
|
|
54
|
-
if (!event?.defaultPrevented) {
|
|
55
|
-
value(...args);
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
merged[key] = value;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return merged;
|
|
64
|
-
}
|
|
65
|
-
function composeRefs(...refs) {
|
|
66
|
-
return (node) => {
|
|
67
|
-
for (const ref of refs) {
|
|
68
|
-
if (typeof ref === "function") {
|
|
69
|
-
ref(node);
|
|
70
|
-
} else if (ref) {
|
|
71
|
-
ref.current = node;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function isEventHandler(key) {
|
|
77
|
-
return /^on[A-Z]/u.test(key);
|
|
4
|
+
// src/hooks/use-stable-context.ts
|
|
5
|
+
import { useRef } from "react";
|
|
6
|
+
function useStableContext(value) {
|
|
7
|
+
const key = JSON.stringify(value);
|
|
8
|
+
const ref = useRef({ key, value });
|
|
9
|
+
if (ref.current.key !== key) {
|
|
10
|
+
ref.current = { key, value };
|
|
11
|
+
}
|
|
12
|
+
return ref.current.value;
|
|
78
13
|
}
|
|
79
14
|
|
|
15
|
+
// src/hooks/use-slot-instances.ts
|
|
16
|
+
import { createSlotInstancesStore } from "@tailorkit/client-core";
|
|
17
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
18
|
+
|
|
80
19
|
// src/components/context.ts
|
|
81
20
|
import { createContext, useContext } from "react";
|
|
82
21
|
var TailorRootContext = createContext(null);
|
|
83
|
-
function useTailorRootContext(component) {
|
|
22
|
+
function useTailorRootContext(component, expectedClient) {
|
|
84
23
|
const context = useContext(TailorRootContext);
|
|
85
24
|
if (!context) {
|
|
86
|
-
throw new Error(`${component} must be rendered inside
|
|
25
|
+
throw new Error(`${component} must be rendered inside Root.`);
|
|
26
|
+
}
|
|
27
|
+
if (expectedClient && context.client !== expectedClient) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`${component} was created for a different TailorKit client than the one passed to Root.`
|
|
30
|
+
);
|
|
87
31
|
}
|
|
88
32
|
return context;
|
|
89
33
|
}
|
|
90
34
|
|
|
91
|
-
// src/
|
|
92
|
-
import {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
}
|
|
35
|
+
// src/hooks/use-apps.ts
|
|
36
|
+
import { useCallback, useEffect, useMemo, useSyncExternalStore } from "react";
|
|
37
|
+
import { matchesApp } from "@tailorkit/client-core";
|
|
38
|
+
|
|
39
|
+
// src/scope-query.ts
|
|
40
|
+
import { appendScopeSelection, normalizeScopeSelection } from "@tailorkit/client-core";
|
|
110
41
|
|
|
111
42
|
// src/hooks/use-apps.ts
|
|
112
|
-
import {
|
|
113
|
-
function
|
|
114
|
-
return
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
43
|
+
import { matchesApp as matchesApp2 } from "@tailorkit/client-core";
|
|
44
|
+
function useApps(options = {}) {
|
|
45
|
+
return useAppsStore(useTailorRootContext("useApps").store, options);
|
|
46
|
+
}
|
|
47
|
+
function useAppsStore(store, options = {}) {
|
|
48
|
+
const snapshot = useAppsSnapshot(store, options);
|
|
49
|
+
const scopes = normalizeScopeSelection(options.scopes);
|
|
50
|
+
const appIds = normalizeScopeSelection(options.appIds);
|
|
51
|
+
const data = useMemo(
|
|
52
|
+
() => snapshot.status === "ready" ? snapshot.apps.filter((app) => matchesApp(app, scopes.scopes, appIds.scopes)) : void 0,
|
|
53
|
+
[snapshot, scopes.key, appIds.key]
|
|
54
|
+
);
|
|
55
|
+
const refetch = useCallback(
|
|
56
|
+
() => store.fetchApps({ force: true, staleTime: options.staleTime }),
|
|
57
|
+
[store, options.staleTime]
|
|
58
|
+
);
|
|
59
|
+
return { ...toUseAppsResult(snapshot, refetch), data };
|
|
60
|
+
}
|
|
61
|
+
function useAppsSnapshot(store, options = {}) {
|
|
62
|
+
const snapshot = useSyncExternalStore(
|
|
63
|
+
store.subscribeApps,
|
|
64
|
+
store.getAppsSnapshot,
|
|
65
|
+
store.getAppsSnapshot
|
|
66
|
+
);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
void store.fetchApps(options);
|
|
69
|
+
}, [store, options.staleTime]);
|
|
70
|
+
return snapshot;
|
|
126
71
|
}
|
|
127
72
|
function toUseAppsResult(snapshot, refetch) {
|
|
128
73
|
return {
|
|
129
74
|
data: snapshot.status === "ready" ? snapshot.apps : void 0,
|
|
130
75
|
error: snapshot.error,
|
|
76
|
+
isFetching: snapshot.isFetching,
|
|
131
77
|
isError: snapshot.status === "error",
|
|
132
78
|
isLoading: snapshot.status === "loading",
|
|
133
79
|
isPending: snapshot.status === "idle" || snapshot.status === "loading",
|
|
@@ -137,41 +83,66 @@ function toUseAppsResult(snapshot, refetch) {
|
|
|
137
83
|
};
|
|
138
84
|
}
|
|
139
85
|
|
|
140
|
-
// src/hooks/use-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
86
|
+
// src/hooks/use-slot-instances.ts
|
|
87
|
+
function useSlotInstances({
|
|
88
|
+
slot,
|
|
89
|
+
staleTime,
|
|
90
|
+
gcTime
|
|
91
|
+
}) {
|
|
92
|
+
const apps = useApps();
|
|
93
|
+
const instances = useResolvedSlotInstances(slot, apps.data, apps.status, apps.error, {
|
|
94
|
+
staleTime,
|
|
95
|
+
gcTime
|
|
96
|
+
});
|
|
97
|
+
const refetch = useCallback2(async () => {
|
|
98
|
+
if (apps.isSuccess) await instances.refetch();
|
|
99
|
+
else await apps.refetch();
|
|
100
|
+
}, [apps.isSuccess, apps.refetch, instances.refetch]);
|
|
101
|
+
return { ...instances, isFetching: apps.isFetching || instances.isFetching, refetch };
|
|
102
|
+
}
|
|
103
|
+
function useAppSlotInstances(app, slot) {
|
|
104
|
+
return useResolvedSlotInstances(slot, [app], "ready", null);
|
|
105
|
+
}
|
|
106
|
+
function useResolvedSlotInstances(slot, apps, appsStatus, appsError, { staleTime, gcTime } = {}) {
|
|
107
|
+
const { store } = useTailorRootContext("useSlotInstances");
|
|
108
|
+
const activeView = useSyncExternalStore2(
|
|
109
|
+
store.views.subscribe,
|
|
110
|
+
store.views.getSnapshot,
|
|
111
|
+
store.views.getSnapshot
|
|
112
|
+
);
|
|
113
|
+
const options = useStableContext({ apps: apps ?? [], slot, activeView });
|
|
114
|
+
const query = useMemo2(
|
|
115
|
+
() => createSlotInstancesStore(store.client, {
|
|
116
|
+
...options,
|
|
117
|
+
appsStatus,
|
|
118
|
+
appsError,
|
|
119
|
+
staleTime,
|
|
120
|
+
gcTime
|
|
121
|
+
}),
|
|
122
|
+
[store.client, options, appsStatus, appsError, staleTime, gcTime]
|
|
123
|
+
);
|
|
124
|
+
const snapshot = useSyncExternalStore2(query.subscribe, query.getSnapshot, query.getSnapshot);
|
|
125
|
+
useEffect2(() => {
|
|
126
|
+
void query.fetch();
|
|
127
|
+
}, [query]);
|
|
128
|
+
const refetch = useCallback2(() => query.fetch({ force: true }), [query]);
|
|
129
|
+
return {
|
|
130
|
+
...snapshot,
|
|
131
|
+
isError: snapshot.status === "error",
|
|
132
|
+
isPending: snapshot.status === "idle" || snapshot.status === "loading",
|
|
133
|
+
isLoading: snapshot.status === "loading",
|
|
134
|
+
isSuccess: snapshot.status === "ready",
|
|
135
|
+
refetch
|
|
168
136
|
};
|
|
169
137
|
}
|
|
170
138
|
|
|
139
|
+
// src/slot-view.ts
|
|
140
|
+
import { resolveSlotView, selectSlotView } from "@tailorkit/client-core";
|
|
141
|
+
|
|
171
142
|
// src/primitives.tsx
|
|
172
|
-
import { createContext as createContext2, useContext as useContext2, useId, useMemo as
|
|
143
|
+
import { createContext as createContext2, useContext as useContext2, useId, useMemo as useMemo3 } from "react";
|
|
173
144
|
import { resolveTheme } from "@tailorkit/core/primitives/theme";
|
|
174
|
-
import { jsx
|
|
145
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
175
146
|
var PrimitiveThemeContext = createContext2(null);
|
|
176
147
|
var tokenGroups = [
|
|
177
148
|
"space",
|
|
@@ -292,10 +263,10 @@ var buildPrimitiveCss = ({
|
|
|
292
263
|
display,
|
|
293
264
|
nodeId,
|
|
294
265
|
props,
|
|
295
|
-
|
|
266
|
+
viewId,
|
|
296
267
|
theme
|
|
297
268
|
}) => {
|
|
298
|
-
const selector = `[data-tailorkit-
|
|
269
|
+
const selector = `[data-tailorkit-view="${cssEscape(viewId)}"] [data-tailorkit-node="${cssEscape(
|
|
299
270
|
nodeId
|
|
300
271
|
)}"]`;
|
|
301
272
|
const base = [`display: ${display};`];
|
|
@@ -323,8 +294,8 @@ var buildPrimitiveCss = ({
|
|
|
323
294
|
}
|
|
324
295
|
return rules.join("\n");
|
|
325
296
|
};
|
|
326
|
-
var buildThemeCss = (
|
|
327
|
-
const selector = `[data-tailorkit-
|
|
297
|
+
var buildThemeCss = (viewId, theme) => {
|
|
298
|
+
const selector = `[data-tailorkit-view="${cssEscape(viewId)}"]`;
|
|
328
299
|
const declarations = [];
|
|
329
300
|
const resolvedTheme = resolveTheme(theme);
|
|
330
301
|
for (const group of tokenGroups) {
|
|
@@ -336,7 +307,7 @@ var buildThemeCss = (screenId, theme) => {
|
|
|
336
307
|
};
|
|
337
308
|
var usePrimitiveNodeId = () => {
|
|
338
309
|
const id = useId();
|
|
339
|
-
return
|
|
310
|
+
return useMemo3(() => `tailorkit-${id.replaceAll(":", "")}`, [id]);
|
|
340
311
|
};
|
|
341
312
|
var Primitive = ({
|
|
342
313
|
children,
|
|
@@ -351,150 +322,44 @@ var Primitive = ({
|
|
|
351
322
|
display,
|
|
352
323
|
nodeId,
|
|
353
324
|
props,
|
|
354
|
-
|
|
325
|
+
viewId: context.viewId,
|
|
355
326
|
theme: context.theme
|
|
356
327
|
}) : "";
|
|
357
328
|
return /* @__PURE__ */ jsxs(Element, { "data-tailorkit-node": nodeId, children: [
|
|
358
|
-
css === "" ? null : /* @__PURE__ */
|
|
329
|
+
css === "" ? null : /* @__PURE__ */ jsx("style", { "data-tailorkit-node-style": nodeId, children: css }),
|
|
359
330
|
children
|
|
360
331
|
] });
|
|
361
332
|
};
|
|
362
333
|
var primitives = {
|
|
363
|
-
Box: ({ props,
|
|
364
|
-
Flex: ({ props,
|
|
365
|
-
Grid: ({ props,
|
|
366
|
-
Inline: ({ props,
|
|
334
|
+
Box: ({ props, children }) => /* @__PURE__ */ jsx(Primitive, { display: "block", element: "div", props, children }),
|
|
335
|
+
Flex: ({ props, children }) => /* @__PURE__ */ jsx(Primitive, { display: "flex", element: "div", props, children }),
|
|
336
|
+
Grid: ({ props, children }) => /* @__PURE__ */ jsx(Primitive, { display: "grid", element: "div", props, children }),
|
|
337
|
+
Inline: ({ props, children }) => /* @__PURE__ */ jsx(Primitive, { display: "inline", element: "span", props, children })
|
|
367
338
|
};
|
|
368
339
|
|
|
369
340
|
// src/remote-view.tsx
|
|
370
341
|
import {
|
|
371
342
|
Component,
|
|
372
343
|
Fragment,
|
|
373
|
-
createElement
|
|
344
|
+
createElement,
|
|
374
345
|
memo,
|
|
375
|
-
useCallback as
|
|
346
|
+
useCallback as useCallback3,
|
|
376
347
|
useContext as useContext3,
|
|
377
348
|
useEffect as useEffect3,
|
|
378
|
-
useMemo as
|
|
349
|
+
useMemo as useMemo4,
|
|
379
350
|
useRef as useRef2,
|
|
380
351
|
useState,
|
|
381
|
-
useSyncExternalStore as
|
|
352
|
+
useSyncExternalStore as useSyncExternalStore3
|
|
382
353
|
} from "react";
|
|
383
354
|
import { createIframeUiHost } from "@tailorkit/sandbox/host";
|
|
384
|
-
|
|
385
|
-
// src/node-store.ts
|
|
386
|
-
var childIds = (children) => children.map((c) => c.id).join(",");
|
|
387
|
-
var nodeSignature = (node) => {
|
|
388
|
-
if (node.kind === "text") {
|
|
389
|
-
return `text:${node.text}`;
|
|
390
|
-
}
|
|
391
|
-
if (node.kind === "fragment") {
|
|
392
|
-
return `frag:${childIds(node.children)}`;
|
|
393
|
-
}
|
|
394
|
-
const propsStr = JSON.stringify(node.props);
|
|
395
|
-
const callbacksStr = (node.callbacks ?? []).map((binding) => `${binding.callback}:${binding.event}`).join(",");
|
|
396
|
-
return `elem:${node.type}|${childIds(node.children)}|${propsStr}|${callbacksStr}`;
|
|
397
|
-
};
|
|
398
|
-
var flattenTree = (node, out) => {
|
|
399
|
-
out.set(node.id, node);
|
|
400
|
-
if (node.kind === "text") {
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
for (const child of node.children) {
|
|
404
|
-
flattenTree(child, out);
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
var NodeStore = class {
|
|
408
|
-
nodes = /* @__PURE__ */ new Map();
|
|
409
|
-
signatures = /* @__PURE__ */ new Map();
|
|
410
|
-
nodeListeners = /* @__PURE__ */ new Map();
|
|
411
|
-
rootListeners = /* @__PURE__ */ new Set();
|
|
412
|
-
rootId = null;
|
|
413
|
-
clear() {
|
|
414
|
-
const nodeIds = [...this.nodeListeners.keys()];
|
|
415
|
-
const hadRoot = this.rootId !== null;
|
|
416
|
-
this.nodes = /* @__PURE__ */ new Map();
|
|
417
|
-
this.signatures = /* @__PURE__ */ new Map();
|
|
418
|
-
this.rootId = null;
|
|
419
|
-
for (const id of nodeIds) {
|
|
420
|
-
const subs = this.nodeListeners.get(id);
|
|
421
|
-
if (subs) {
|
|
422
|
-
for (const l of subs) {
|
|
423
|
-
l();
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
if (hadRoot) {
|
|
428
|
-
for (const l of this.rootListeners) {
|
|
429
|
-
l();
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
setSnapshot(tree) {
|
|
434
|
-
const next = /* @__PURE__ */ new Map();
|
|
435
|
-
flattenTree(tree, next);
|
|
436
|
-
const changed = /* @__PURE__ */ new Set();
|
|
437
|
-
for (const [id, node] of next) {
|
|
438
|
-
const sig = nodeSignature(node);
|
|
439
|
-
if (this.signatures.get(id) !== sig) {
|
|
440
|
-
changed.add(id);
|
|
441
|
-
this.signatures.set(id, sig);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
for (const id of this.signatures.keys()) {
|
|
445
|
-
if (!next.has(id)) {
|
|
446
|
-
this.signatures.delete(id);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
this.nodes = next;
|
|
450
|
-
const prevRootId = this.rootId;
|
|
451
|
-
this.rootId = tree.id;
|
|
452
|
-
for (const id of changed) {
|
|
453
|
-
const subs = this.nodeListeners.get(id);
|
|
454
|
-
if (subs) {
|
|
455
|
-
for (const l of subs) {
|
|
456
|
-
l();
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
if (prevRootId !== this.rootId) {
|
|
461
|
-
for (const l of this.rootListeners) {
|
|
462
|
-
l();
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
getRootId() {
|
|
467
|
-
return this.rootId;
|
|
468
|
-
}
|
|
469
|
-
getNode(id) {
|
|
470
|
-
return this.nodes.get(id) ?? null;
|
|
471
|
-
}
|
|
472
|
-
subscribe(id, listener) {
|
|
473
|
-
let subs = this.nodeListeners.get(id);
|
|
474
|
-
if (!subs) {
|
|
475
|
-
subs = /* @__PURE__ */ new Set();
|
|
476
|
-
this.nodeListeners.set(id, subs);
|
|
477
|
-
}
|
|
478
|
-
subs.add(listener);
|
|
479
|
-
return () => {
|
|
480
|
-
subs.delete(listener);
|
|
481
|
-
if (subs.size === 0) {
|
|
482
|
-
this.nodeListeners.delete(id);
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
subscribeRoot(listener) {
|
|
487
|
-
this.rootListeners.add(listener);
|
|
488
|
-
return () => this.rootListeners.delete(listener);
|
|
489
|
-
}
|
|
490
|
-
};
|
|
355
|
+
import { NodeStore } from "@tailorkit/client-core";
|
|
491
356
|
|
|
492
357
|
// src/remote-context.ts
|
|
493
358
|
import { createContext as createContext3 } from "react";
|
|
494
359
|
var RemoteUIContext = createContext3(null);
|
|
495
360
|
|
|
496
361
|
// src/remote-view.tsx
|
|
497
|
-
import { jsx as
|
|
362
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
498
363
|
var formatError = (error) => error.message;
|
|
499
364
|
var RemoteErrorBoundary = class extends Component {
|
|
500
365
|
state = { error: null };
|
|
@@ -506,17 +371,18 @@ var RemoteErrorBoundary = class extends Component {
|
|
|
506
371
|
}
|
|
507
372
|
render() {
|
|
508
373
|
if (this.state.error) {
|
|
509
|
-
return
|
|
374
|
+
return createElement("div", null, formatError(this.state.error));
|
|
510
375
|
}
|
|
511
376
|
return this.props.children;
|
|
512
377
|
}
|
|
513
378
|
};
|
|
514
379
|
function RemoteViewHost({
|
|
515
380
|
appUrl,
|
|
381
|
+
sourceText,
|
|
382
|
+
getBackendSession,
|
|
516
383
|
components: components2,
|
|
517
384
|
createIframe,
|
|
518
|
-
props
|
|
519
|
-
runtimeUrl
|
|
385
|
+
props
|
|
520
386
|
}) {
|
|
521
387
|
const appKey = appUrl.toString();
|
|
522
388
|
const storeRef = useRef2(null);
|
|
@@ -529,6 +395,12 @@ function RemoteViewHost({
|
|
|
529
395
|
appKeyRef.current = appKey;
|
|
530
396
|
store.clear();
|
|
531
397
|
}
|
|
398
|
+
const hostRef = useRef2(null);
|
|
399
|
+
const propsRef = useRef2(props);
|
|
400
|
+
useEffect3(() => {
|
|
401
|
+
propsRef.current = props;
|
|
402
|
+
hostRef.current?.setProps(props);
|
|
403
|
+
}, [props]);
|
|
532
404
|
const dispatchRef = useRef2(null);
|
|
533
405
|
const [error, setError] = useState(null);
|
|
534
406
|
const [status, setStatus] = useState("starting");
|
|
@@ -540,29 +412,31 @@ function RemoteViewHost({
|
|
|
540
412
|
useEffect3(() => {
|
|
541
413
|
ctxRef.current.components = components2;
|
|
542
414
|
}, [components2]);
|
|
543
|
-
const ui =
|
|
415
|
+
const ui = useMemo4(() => {
|
|
544
416
|
const ctx = ctxRef.current;
|
|
545
|
-
return
|
|
417
|
+
return createElement(
|
|
546
418
|
RemoteUIContext.Provider,
|
|
547
419
|
{ value: ctx },
|
|
548
|
-
|
|
420
|
+
createElement(
|
|
549
421
|
RemoteErrorBoundary,
|
|
550
422
|
{ key: appKey },
|
|
551
|
-
|
|
423
|
+
createElement(RemoteRoot, { store: ctx.store })
|
|
552
424
|
)
|
|
553
425
|
);
|
|
554
426
|
}, [appKey]);
|
|
555
427
|
useEffect3(() => {
|
|
556
428
|
const host = createIframeUiHost(appUrl, {
|
|
557
429
|
createIframe,
|
|
430
|
+
sourceText,
|
|
431
|
+
getBackendSession,
|
|
558
432
|
onError: (error2) => {
|
|
559
433
|
console.error("TailorKit remote app failed", error2);
|
|
560
434
|
setError(error2);
|
|
561
435
|
setStatus("error");
|
|
562
436
|
},
|
|
563
|
-
props
|
|
564
|
-
runtimeUrl
|
|
437
|
+
props: propsRef.current
|
|
565
438
|
});
|
|
439
|
+
hostRef.current = host;
|
|
566
440
|
dispatchRef.current = (payload) => host.dispatch(payload);
|
|
567
441
|
const unsubscribe = host.subscribe(() => {
|
|
568
442
|
const tree = host.getSnapshot();
|
|
@@ -577,26 +451,27 @@ function RemoteViewHost({
|
|
|
577
451
|
return () => {
|
|
578
452
|
unsubscribe();
|
|
579
453
|
dispatchRef.current = null;
|
|
454
|
+
hostRef.current = null;
|
|
580
455
|
host.destroy();
|
|
581
456
|
};
|
|
582
|
-
}, [appUrl, createIframe,
|
|
457
|
+
}, [appUrl, createIframe, sourceText, getBackendSession, store]);
|
|
583
458
|
if (status === "error" && error) {
|
|
584
|
-
return
|
|
459
|
+
return createElement("div", null, formatError(error));
|
|
585
460
|
}
|
|
586
461
|
return ui;
|
|
587
462
|
}
|
|
588
463
|
function RemoteRoot({ store }) {
|
|
589
|
-
const subscribe =
|
|
590
|
-
const getSnapshot =
|
|
591
|
-
const rootId =
|
|
464
|
+
const subscribe = useCallback3((listener) => store.subscribeRoot(listener), [store]);
|
|
465
|
+
const getSnapshot = useCallback3(() => store.getRootId(), [store]);
|
|
466
|
+
const rootId = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
|
|
592
467
|
if (rootId === null) {
|
|
593
468
|
return null;
|
|
594
469
|
}
|
|
595
|
-
return /* @__PURE__ */
|
|
470
|
+
return /* @__PURE__ */ jsx2(RemoteView, { nodeId: rootId });
|
|
596
471
|
}
|
|
597
472
|
var RemoteElementView = memo(({ ctx, node }) => {
|
|
598
473
|
const { dispatch, components: components2 } = ctx;
|
|
599
|
-
const children = node.children.map((child) => /* @__PURE__ */
|
|
474
|
+
const children = node.children.map((child) => /* @__PURE__ */ jsx2(RemoteView, { nodeId: child.id }, child.id));
|
|
600
475
|
const component = components2[node.type];
|
|
601
476
|
if (component === void 0) {
|
|
602
477
|
throw new Error(`TailorKit component "${node.type}" is not registered.`);
|
|
@@ -614,35 +489,251 @@ var RemoteElementView = memo(({ ctx, node }) => {
|
|
|
614
489
|
});
|
|
615
490
|
};
|
|
616
491
|
}
|
|
617
|
-
return
|
|
492
|
+
return createElement(component, props, ...children);
|
|
618
493
|
});
|
|
619
|
-
|
|
494
|
+
function RemoteViewComponent({ nodeId }) {
|
|
620
495
|
const ctx = useContext3(RemoteUIContext);
|
|
621
496
|
if (ctx === null) {
|
|
622
497
|
return null;
|
|
623
498
|
}
|
|
624
499
|
const { store } = ctx;
|
|
625
|
-
const subscribe =
|
|
500
|
+
const subscribe = useCallback3(
|
|
626
501
|
(listener) => store.subscribe(nodeId, listener),
|
|
627
502
|
[store, nodeId]
|
|
628
503
|
);
|
|
629
|
-
const getSnapshot =
|
|
630
|
-
const node =
|
|
504
|
+
const getSnapshot = useCallback3(() => store.getNode(nodeId), [store, nodeId]);
|
|
505
|
+
const node = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
|
|
631
506
|
if (node === null) {
|
|
632
507
|
return null;
|
|
633
508
|
}
|
|
634
509
|
if (node.kind === "text") {
|
|
635
510
|
return node.text;
|
|
636
511
|
}
|
|
637
|
-
const childElements = node.children.map((child) => /* @__PURE__ */
|
|
512
|
+
const childElements = node.children.map((child) => /* @__PURE__ */ jsx2(RemoteView, { nodeId: child.id }, child.id));
|
|
638
513
|
if (node.kind === "fragment") {
|
|
639
|
-
return
|
|
514
|
+
return createElement(Fragment, { key: node.id }, ...childElements);
|
|
640
515
|
}
|
|
641
|
-
return /* @__PURE__ */
|
|
642
|
-
}
|
|
516
|
+
return /* @__PURE__ */ jsx2(RemoteElementView, { node, ctx });
|
|
517
|
+
}
|
|
518
|
+
var RemoteView = memo(RemoteViewComponent);
|
|
519
|
+
|
|
520
|
+
// src/components/slot.tsx
|
|
521
|
+
import { toBaseUrl } from "@tailorkit/client-core";
|
|
522
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
523
|
+
function ManagedSlot({ app, name, instanceKey }) {
|
|
524
|
+
const { store } = useTailorRootContext("Slot");
|
|
525
|
+
const state = useSyncExternalStore4(
|
|
526
|
+
store.views.subscribe,
|
|
527
|
+
store.views.getSnapshot,
|
|
528
|
+
store.views.getSnapshot
|
|
529
|
+
);
|
|
530
|
+
if (state === null) return null;
|
|
531
|
+
return instanceKey === void 0 ? /* @__PURE__ */ jsx3(SlotRenderer, { app, name, state }) : /* @__PURE__ */ jsx3(InstanceSlot, { app, name, instanceKey, state });
|
|
532
|
+
}
|
|
533
|
+
function InstanceSlot({
|
|
534
|
+
app,
|
|
535
|
+
name,
|
|
536
|
+
instanceKey,
|
|
537
|
+
state
|
|
538
|
+
}) {
|
|
539
|
+
const { store } = useTailorRootContext("Slot");
|
|
540
|
+
const instances = useAppSlotInstances(app, name);
|
|
541
|
+
const meta = useSyncExternalStore4(store.subscribe, store.getMetaSnapshot, store.getMetaSnapshot);
|
|
542
|
+
const instance = instances.data?.find((instance2) => instance2.key === instanceKey);
|
|
543
|
+
const resolved = instances.isSuccess && instance && meta.schema ? resolveSlotView(app.views ?? [], name, state, meta.schema) : null;
|
|
544
|
+
const view = resolved?.status === "ready" ? resolved.view : void 0;
|
|
545
|
+
const stableContext = useStableContext(resolved?.context);
|
|
546
|
+
const stableInstance = useStableContext(
|
|
547
|
+
instance ? { key: instance.key, metadata: instance.metadata, data: instance.data } : void 0
|
|
548
|
+
);
|
|
549
|
+
const readyState = useMemo5(
|
|
550
|
+
() => view === void 0 || !stableInstance ? null : {
|
|
551
|
+
controlled: true,
|
|
552
|
+
view,
|
|
553
|
+
context: stableContext,
|
|
554
|
+
status: "ready",
|
|
555
|
+
instance: stableInstance
|
|
556
|
+
},
|
|
557
|
+
[view, stableContext, stableInstance]
|
|
558
|
+
);
|
|
559
|
+
if (instances.isPending) return /* @__PURE__ */ jsx3("div", { role: "status", children: "Loading view\u2026" });
|
|
560
|
+
if (instances.error) return /* @__PURE__ */ jsx3("div", { role: "alert", children: instances.error.message });
|
|
561
|
+
if (meta.schema && meta.schema.slots[name]?.multiple !== true)
|
|
562
|
+
return /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
|
|
563
|
+
'Slot "',
|
|
564
|
+
name,
|
|
565
|
+
'" does not support instances.'
|
|
566
|
+
] });
|
|
567
|
+
if (!instance) return /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
|
|
568
|
+
'View instance "',
|
|
569
|
+
instanceKey,
|
|
570
|
+
'" is unavailable.'
|
|
571
|
+
] });
|
|
572
|
+
if (!readyState) return null;
|
|
573
|
+
return /* @__PURE__ */ jsx3(SlotRenderer, { app, name, state: readyState });
|
|
574
|
+
}
|
|
575
|
+
function ControlledSlot({
|
|
576
|
+
app,
|
|
577
|
+
name,
|
|
578
|
+
view,
|
|
579
|
+
context,
|
|
580
|
+
status,
|
|
581
|
+
instance
|
|
582
|
+
}) {
|
|
583
|
+
useTailorRootContext("Slot.Controlled");
|
|
584
|
+
const stableContext = useStableContext(context);
|
|
585
|
+
const stableInstance = useStableContext(instance);
|
|
586
|
+
const state = useMemo5(
|
|
587
|
+
() => ({
|
|
588
|
+
controlled: true,
|
|
589
|
+
view,
|
|
590
|
+
context: stableContext,
|
|
591
|
+
status,
|
|
592
|
+
...status === "ready" && stableInstance ? { instance: stableInstance } : {}
|
|
593
|
+
}),
|
|
594
|
+
[view, stableContext, status, stableInstance]
|
|
595
|
+
);
|
|
596
|
+
if (status === "ready" && !instance && app.views?.some(
|
|
597
|
+
(entry) => entry.slot === name && entry.path === view && entry.instances && !entry.disabled
|
|
598
|
+
)) {
|
|
599
|
+
return /* @__PURE__ */ jsx3("div", { role: "alert", children: "A view instance is required. Pass instance to Slot.Controlled." });
|
|
600
|
+
}
|
|
601
|
+
return /* @__PURE__ */ jsx3(SlotRenderer, { app, name, state });
|
|
602
|
+
}
|
|
603
|
+
var Slot = Object.assign(ManagedSlot, { Controlled: ControlledSlot });
|
|
604
|
+
function SlotRenderer({ app, name, state }) {
|
|
605
|
+
const { store, client } = useTailorRootContext("Slot");
|
|
606
|
+
const reactId = useId2();
|
|
607
|
+
const getBackendSession = useMemo5(
|
|
608
|
+
() => store.client.endpoints.getSessionProvider(app),
|
|
609
|
+
[store.client, app.id, app.currentDeployment?.id, app.preview?.sessionId]
|
|
610
|
+
);
|
|
611
|
+
const meta = useSyncExternalStore4(store.subscribe, store.getMetaSnapshot, store.getMetaSnapshot);
|
|
612
|
+
const previewSessionId = app.preview?.sessionId ?? "";
|
|
613
|
+
const appRef = useRef3(app);
|
|
614
|
+
appRef.current = app;
|
|
615
|
+
const subscribePreview = useCallback4(
|
|
616
|
+
(listener) => {
|
|
617
|
+
const currentApp = appRef.current;
|
|
618
|
+
return currentApp.preview?.sessionId === previewSessionId ? store.previews.subscribe(currentApp, listener) : () => {
|
|
619
|
+
};
|
|
620
|
+
},
|
|
621
|
+
[store, previewSessionId]
|
|
622
|
+
);
|
|
623
|
+
const getPreviewSnapshot = useCallback4(
|
|
624
|
+
() => store.previews.getSnapshot(previewSessionId),
|
|
625
|
+
[store, previewSessionId]
|
|
626
|
+
);
|
|
627
|
+
const preview = useSyncExternalStore4(subscribePreview, getPreviewSnapshot, getPreviewSnapshot);
|
|
628
|
+
const props = useMemo5(
|
|
629
|
+
() => ({
|
|
630
|
+
...state,
|
|
631
|
+
slot: name,
|
|
632
|
+
declaredViews: Object.keys(meta.schema?.views ?? {}),
|
|
633
|
+
supportedViews: meta.schema?.slots[name]?.views ?? []
|
|
634
|
+
}),
|
|
635
|
+
[state, name, meta.schema]
|
|
636
|
+
);
|
|
637
|
+
const appUrl = useMemo5(() => {
|
|
638
|
+
if (app.clientPath) return new URL(app.clientPath, store.baseUrl);
|
|
639
|
+
if (!meta.assetsBaseUrl || !app.projectId || !app.currentDeployment?.id) return null;
|
|
640
|
+
return new URL(
|
|
641
|
+
`projects/${app.projectId}/apps/${app.id}/deployments/${app.currentDeployment.id}/client/client.js`,
|
|
642
|
+
toBaseUrl(meta.assetsBaseUrl)
|
|
643
|
+
);
|
|
644
|
+
}, [app, store.baseUrl, meta.assetsBaseUrl]);
|
|
645
|
+
useEffect4(() => {
|
|
646
|
+
void store.fetchMeta();
|
|
647
|
+
}, [store]);
|
|
648
|
+
useEffect4(() => {
|
|
649
|
+
store.previews.updateApp(app);
|
|
650
|
+
}, [store, app]);
|
|
651
|
+
if (meta.schema === null || appUrl === null && preview.source === null) return null;
|
|
652
|
+
const multiple = meta.schema.slots[name]?.multiple === true;
|
|
653
|
+
if ("controlled" in state && state.status === "ready") {
|
|
654
|
+
if (multiple && !state.instance)
|
|
655
|
+
return /* @__PURE__ */ jsx3("div", { role: "alert", children: "A view instance is required. Pass instance to Slot.Controlled." });
|
|
656
|
+
if (!multiple && state.instance)
|
|
657
|
+
return /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
|
|
658
|
+
'Slot "',
|
|
659
|
+
name,
|
|
660
|
+
'" does not support instances.'
|
|
661
|
+
] });
|
|
662
|
+
}
|
|
663
|
+
if (!("controlled" in state)) {
|
|
664
|
+
const selected = selectSlotView(app.views ?? [], name, state.view, meta.schema);
|
|
665
|
+
if (selected?.instances && !multiple)
|
|
666
|
+
return /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
|
|
667
|
+
'Slot "',
|
|
668
|
+
name,
|
|
669
|
+
'" does not support instances.'
|
|
670
|
+
] });
|
|
671
|
+
if (multiple)
|
|
672
|
+
return /* @__PURE__ */ jsx3("div", { role: "alert", children: "A view instance key is required. Pass instanceKey to Slot." });
|
|
673
|
+
}
|
|
674
|
+
const viewId = `tailorkit-view-${reactId.replaceAll(":", "")}`;
|
|
675
|
+
return /* @__PURE__ */ jsx3(PrimitiveThemeContext.Provider, { value: { viewId, theme: client.theme }, children: /* @__PURE__ */ jsxs2("div", { "data-tailorkit-view": viewId, children: [
|
|
676
|
+
/* @__PURE__ */ jsx3("style", { "data-tailorkit-theme-style": viewId, children: buildThemeCss(viewId, client.theme) }),
|
|
677
|
+
/* @__PURE__ */ jsx3(
|
|
678
|
+
RemoteViewHost,
|
|
679
|
+
{
|
|
680
|
+
appUrl: (appUrl ?? new URL(`preview/${previewSessionId}/client.js`, store.baseUrl)).toString(),
|
|
681
|
+
sourceText: preview.source ?? void 0,
|
|
682
|
+
getBackendSession,
|
|
683
|
+
components: client.components,
|
|
684
|
+
props
|
|
685
|
+
},
|
|
686
|
+
preview.revision || appUrl?.toString()
|
|
687
|
+
)
|
|
688
|
+
] }) });
|
|
689
|
+
}
|
|
643
690
|
|
|
644
|
-
// src/
|
|
645
|
-
import {
|
|
691
|
+
// src/tailorkit.tsx
|
|
692
|
+
import { createTailorKitFetchClient } from "@tailorkit/client-core";
|
|
693
|
+
import { createElement as createElement2 } from "react";
|
|
694
|
+
|
|
695
|
+
// src/hooks/use-register-view.ts
|
|
696
|
+
import { useEffect as useEffect5, useMemo as useMemo6 } from "react";
|
|
697
|
+
function useRegisterView(view, options) {
|
|
698
|
+
const { store } = useTailorRootContext("useRegisterView");
|
|
699
|
+
const id = useMemo6(() => /* @__PURE__ */ Symbol("tailorkit-current-view"), []);
|
|
700
|
+
const status = options.status ?? "ready";
|
|
701
|
+
const context = "context" in options ? options.context : void 0;
|
|
702
|
+
const contextSnapshot = useStableContext(context);
|
|
703
|
+
useEffect5(
|
|
704
|
+
() => () => {
|
|
705
|
+
store.views.unregister(id);
|
|
706
|
+
},
|
|
707
|
+
[id, store]
|
|
708
|
+
);
|
|
709
|
+
useEffect5(() => {
|
|
710
|
+
store.views.register({
|
|
711
|
+
context: contextSnapshot,
|
|
712
|
+
id,
|
|
713
|
+
view,
|
|
714
|
+
status
|
|
715
|
+
});
|
|
716
|
+
}, [contextSnapshot, id, view, status, store]);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// src/hooks/use-views.ts
|
|
720
|
+
import { useCallback as useCallback5, useMemo as useMemo7 } from "react";
|
|
721
|
+
function useViews(options = {}) {
|
|
722
|
+
const { store } = useTailorRootContext("useViews");
|
|
723
|
+
const snapshot = useAppsSnapshot(store, options);
|
|
724
|
+
const scopes = normalizeScopeSelection(options.scopes);
|
|
725
|
+
const appIds = normalizeScopeSelection(options.appIds);
|
|
726
|
+
const data = useMemo7(
|
|
727
|
+
() => snapshot.status === "ready" ? snapshot.views.filter(
|
|
728
|
+
(view) => (options.slot === void 0 || view.slot === options.slot) && matchesApp2(view.app, scopes.scopes, appIds.scopes)
|
|
729
|
+
) : void 0,
|
|
730
|
+
[snapshot, scopes.key, appIds.key, options.slot]
|
|
731
|
+
);
|
|
732
|
+
const refetch = useCallback5(() => store.fetchApps({ force: true }), [store]);
|
|
733
|
+
return { ...toUseAppsResult(snapshot, refetch), data };
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// src/tailorkit.tsx
|
|
646
737
|
var componentTagPrefix = "tailorkit-";
|
|
647
738
|
var toComponentTagName = (name) => `${componentTagPrefix}${name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1-$2").replaceAll(/[\s_]+/gu, "-").toLowerCase()}`;
|
|
648
739
|
function components(_schema, customComponents) {
|
|
@@ -653,7 +744,6 @@ function createTailorKitClient(options) {
|
|
|
653
744
|
}
|
|
654
745
|
function createReactTailorKitClient(options) {
|
|
655
746
|
const wrappedComponents = {};
|
|
656
|
-
const store = createTailorKitStore(options.baseUrl);
|
|
657
747
|
const theme = options.theme ?? {};
|
|
658
748
|
for (const [name, renderer] of Object.entries(options.components ?? {})) {
|
|
659
749
|
if (renderer) {
|
|
@@ -663,257 +753,169 @@ function createReactTailorKitClient(options) {
|
|
|
663
753
|
}) {
|
|
664
754
|
return renderer({
|
|
665
755
|
props,
|
|
666
|
-
|
|
756
|
+
children
|
|
667
757
|
});
|
|
668
758
|
};
|
|
669
759
|
wrappedComponents[name] = TailorKitComponent;
|
|
670
760
|
wrappedComponents[toComponentTagName(name)] = TailorKitComponent;
|
|
671
761
|
}
|
|
672
762
|
}
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
const reactId = useId2();
|
|
683
|
-
const currentScreen = useSyncExternalStore3(
|
|
684
|
-
store.subscribe,
|
|
685
|
-
store.getCurrentScreen,
|
|
686
|
-
store.getCurrentScreen
|
|
687
|
-
);
|
|
688
|
-
const screen = screenProps.screen;
|
|
689
|
-
const context = screenProps.context;
|
|
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
|
-
};
|
|
700
|
-
}
|
|
701
|
-
return currentScreen === null ? void 0 : {
|
|
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;
|
|
724
|
-
}
|
|
725
|
-
const screenId = `tailorkit-screen-${reactId.replaceAll(":", "")}`;
|
|
726
|
-
return /* @__PURE__ */ jsx4(PrimitiveThemeContext.Provider, { value: { screenId, theme }, children: /* @__PURE__ */ jsxs2("div", { "data-tailorkit-screen": screenId, children: [
|
|
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
|
-
] }) });
|
|
763
|
+
const clientConfig = {
|
|
764
|
+
baseUrl: options.baseUrl,
|
|
765
|
+
fetchClient: createTailorKitFetchClient({
|
|
766
|
+
baseUrl: options.baseUrl,
|
|
767
|
+
cache: options.cache,
|
|
768
|
+
fetch: options.fetch
|
|
769
|
+
}),
|
|
770
|
+
components: wrappedComponents,
|
|
771
|
+
theme
|
|
739
772
|
};
|
|
740
773
|
const client = {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
let fetchMetaPromise = null;
|
|
770
|
-
let fetchAppsRequestId = 0;
|
|
771
|
-
let nextOrder = 0;
|
|
772
|
-
const emit = () => {
|
|
773
|
-
for (const listener of listeners) {
|
|
774
|
-
listener();
|
|
774
|
+
...clientConfig,
|
|
775
|
+
Slot: Object.assign(
|
|
776
|
+
function ClientSlot(props) {
|
|
777
|
+
useTailorRootContext("Slot", client);
|
|
778
|
+
return createElement2(Slot, props);
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
Controlled: function ClientControlledSlot(props) {
|
|
782
|
+
useTailorRootContext("Slot.Controlled", client);
|
|
783
|
+
return createElement2(Slot.Controlled, props);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
),
|
|
787
|
+
useApps: function useClientApps(options2) {
|
|
788
|
+
useTailorRootContext("useApps", client);
|
|
789
|
+
return useApps(options2);
|
|
790
|
+
},
|
|
791
|
+
useViews: function useClientViews(options2) {
|
|
792
|
+
useTailorRootContext("useViews", client);
|
|
793
|
+
return useViews(options2);
|
|
794
|
+
},
|
|
795
|
+
useSlotInstances: function useClientSlotInstances(options2) {
|
|
796
|
+
useTailorRootContext("useSlotInstances", client);
|
|
797
|
+
return useSlotInstances(options2);
|
|
798
|
+
},
|
|
799
|
+
useRegisterView: function useClientRegisterView(view, options2) {
|
|
800
|
+
useTailorRootContext("useRegisterView", client);
|
|
801
|
+
useRegisterView(view, options2);
|
|
775
802
|
}
|
|
776
803
|
};
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
804
|
+
return client;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// src/components/root.tsx
|
|
808
|
+
import { useEffect as useEffect6, useMemo as useMemo9, useState as useState2 } from "react";
|
|
809
|
+
import { createTailorKitStore, toBaseUrl as toBaseUrl2 } from "@tailorkit/client-core";
|
|
810
|
+
|
|
811
|
+
// src/components/render.tsx
|
|
812
|
+
import { cloneElement, createElement as createElement3, isValidElement, useMemo as useMemo8 } from "react";
|
|
813
|
+
function useRender({
|
|
814
|
+
defaultTagName,
|
|
815
|
+
props,
|
|
816
|
+
render
|
|
817
|
+
}) {
|
|
818
|
+
return useMemo8(() => {
|
|
819
|
+
if (render) {
|
|
820
|
+
if (!isValidElement(render)) {
|
|
821
|
+
throw new Error("The render prop must be a valid React element.");
|
|
787
822
|
}
|
|
823
|
+
return cloneElement(render, mergeProps(render.props, props));
|
|
788
824
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
825
|
+
return createElement3(defaultTagName, props);
|
|
826
|
+
}, [defaultTagName, props, render]);
|
|
827
|
+
}
|
|
828
|
+
function mergeProps(...propSets) {
|
|
829
|
+
const merged = {};
|
|
830
|
+
for (const props of propSets) {
|
|
831
|
+
if (!props) {
|
|
832
|
+
continue;
|
|
797
833
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
baseUrl,
|
|
802
|
-
fetchApps: (options = {}) => {
|
|
803
|
-
if (fetchAppsPromise && !options.force) {
|
|
804
|
-
return fetchAppsPromise;
|
|
834
|
+
for (const [key, value] of Object.entries(props)) {
|
|
835
|
+
if (value === void 0) {
|
|
836
|
+
continue;
|
|
805
837
|
}
|
|
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"
|
|
831
|
-
};
|
|
832
|
-
emit();
|
|
833
|
-
});
|
|
834
|
-
return fetchAppsPromise;
|
|
835
|
-
},
|
|
836
|
-
fetchMeta: () => {
|
|
837
|
-
if (fetchMetaPromise) {
|
|
838
|
-
return fetchMetaPromise;
|
|
838
|
+
const existing = merged[key];
|
|
839
|
+
if (key === "className" && existing && value) {
|
|
840
|
+
merged[key] = `${existing} ${value}`;
|
|
841
|
+
continue;
|
|
839
842
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
throw new Error(`Unable to fetch TailorKit metadata from ${baseUrl.toString()}.`);
|
|
845
|
-
}
|
|
846
|
-
const meta = await response.json();
|
|
847
|
-
metaSnapshot = {
|
|
848
|
-
assetsBaseUrl: meta.assetsBaseUrl ?? null,
|
|
849
|
-
error: null,
|
|
850
|
-
schema: meta.schema,
|
|
851
|
-
status: "ready"
|
|
843
|
+
if (key === "style" && existing && value) {
|
|
844
|
+
merged[key] = {
|
|
845
|
+
...existing,
|
|
846
|
+
...value
|
|
852
847
|
};
|
|
853
|
-
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
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
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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 = toBaseUrl2(client.baseUrl).toString();
|
|
888
|
+
const [previous, setStore] = useState2(() => ({
|
|
889
|
+
store: createTailorKitStore(baseUrl, appsProp, client.fetchClient),
|
|
890
|
+
fetchClient: client.fetchClient
|
|
891
|
+
}));
|
|
892
|
+
const previousStore = previous.store;
|
|
893
|
+
let store = previousStore;
|
|
894
|
+
if (previousStore.baseUrl.toString() !== baseUrl || previous.fetchClient !== client.fetchClient) {
|
|
895
|
+
store = createTailorKitStore(baseUrl, appsProp, client.fetchClient);
|
|
896
|
+
setStore({ store, fetchClient: client.fetchClient });
|
|
897
|
+
}
|
|
898
|
+
useEffect6(() => {
|
|
899
|
+
store.setProvidedApps(appsProp);
|
|
900
|
+
}, [store, appsProp]);
|
|
901
|
+
useEffect6(() => () => store.previews.dispose(), [store]);
|
|
902
|
+
const context = useMemo9(
|
|
903
|
+
() => ({
|
|
904
|
+
store,
|
|
905
|
+
client
|
|
906
|
+
}),
|
|
907
|
+
[store, client]
|
|
907
908
|
);
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
}
|
|
914
|
-
return url;
|
|
909
|
+
const element = render ? useRender({
|
|
910
|
+
defaultTagName: "div",
|
|
911
|
+
props: mergeProps({ children }, props),
|
|
912
|
+
render
|
|
913
|
+
}) : children;
|
|
914
|
+
return /* @__PURE__ */ jsx4(TailorRootContext.Provider, { value: context, children: element });
|
|
915
915
|
}
|
|
916
916
|
export {
|
|
917
|
+
Root,
|
|
918
|
+
Slot,
|
|
917
919
|
components,
|
|
918
920
|
createTailorKitClient,
|
|
919
921
|
primitives
|