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