@tailorkit/react 0.1.0-beta.3 → 0.1.0-beta.33
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 +74 -0
- package/README.md +3 -0
- package/dist/components/context.d.ts +7 -10
- package/dist/components/index.d.ts +1 -2
- package/dist/components/render-slot.d.ts +9 -0
- package/dist/hooks/use-apps.d.ts +6 -13
- package/dist/hooks/use-query-result.d.ts +9 -0
- package/dist/hooks/use-view-context.d.ts +6 -0
- package/dist/hooks/use-views.d.ts +7 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +362 -839
- package/dist/primitives.d.ts +11 -21
- package/dist/remote-view.d.ts +12 -6
- package/dist/tailorkit.d.ts +45 -0
- package/package.json +42 -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 +0 -20
- package/dist/components/render.d.ts.map +0 -1
- package/dist/components/root.d.ts +0 -11
- 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 +0 -9
- 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/src/__tests__/node-store.test.ts +0 -206
- package/src/__tests__/primitives.test.tsx +0 -85
- package/src/__tests__/tailor-kit.test.tsx +0 -424
- package/src/__tests__/tailor-kit.types.tsx +0 -151
- package/src/components/context.ts +0 -21
- package/src/components/index.ts +0 -1
- package/src/components/render.tsx +0 -116
- package/src/components/root.tsx +0 -37
- package/src/hooks/use-apps.ts +0 -47
- package/src/hooks/use-current-screen.ts +0 -81
- package/src/index.ts +0 -8
- package/src/node-store.ts +0 -131
- package/src/primitives.tsx +0 -261
- package/src/remote-context.ts +0 -11
- package/src/remote-view.tsx +0 -232
- package/src/tailor-kit.tsx +0 -501
package/dist/index.js
CHANGED
|
@@ -1,342 +1,16 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { useMemo as useMemo2 } from "react";
|
|
6
|
-
|
|
7
|
-
// src/components/render.tsx
|
|
8
|
-
import { cloneElement, createElement, isValidElement, useMemo } from "react";
|
|
9
|
-
function useRender({
|
|
10
|
-
defaultTagName,
|
|
11
|
-
props,
|
|
12
|
-
render
|
|
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);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// src/components/context.ts
|
|
81
|
-
import { createContext, useContext } from "react";
|
|
82
|
-
var TailorRootContext = createContext(null);
|
|
83
|
-
function useTailorRootContext(component) {
|
|
84
|
-
const context = useContext(TailorRootContext);
|
|
85
|
-
if (!context) {
|
|
86
|
-
throw new Error(`${component} must be rendered inside tailor.Root.`);
|
|
87
|
-
}
|
|
88
|
-
return context;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// src/components/root.tsx
|
|
92
|
-
import { jsx } from "react/jsx-runtime";
|
|
93
|
-
var EMPTY_APPS = [];
|
|
94
|
-
function Root({ apps: appsProp, children, render, tailor, ...props }) {
|
|
95
|
-
const appsResult = tailor.useApps();
|
|
96
|
-
const apps = appsProp ?? appsResult.data ?? EMPTY_APPS;
|
|
97
|
-
const context = useMemo2(
|
|
98
|
-
() => ({
|
|
99
|
-
apps
|
|
100
|
-
}),
|
|
101
|
-
[apps]
|
|
102
|
-
);
|
|
103
|
-
const element = render ? useRender({
|
|
104
|
-
defaultTagName: "div",
|
|
105
|
-
props: mergeProps({ children }, props),
|
|
106
|
-
render
|
|
107
|
-
}) : children;
|
|
108
|
-
return /* @__PURE__ */ jsx(TailorRootContext.Provider, { value: context, children: element });
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// src/hooks/use-apps.ts
|
|
112
|
-
import { useCallback, useEffect, useSyncExternalStore } from "react";
|
|
113
|
-
function createUseApps(store) {
|
|
114
|
-
return function useApps() {
|
|
115
|
-
const snapshot = useSyncExternalStore(
|
|
116
|
-
store.subscribe,
|
|
117
|
-
store.getAppsSnapshot,
|
|
118
|
-
store.getAppsSnapshot
|
|
119
|
-
);
|
|
120
|
-
useEffect(() => {
|
|
121
|
-
void store.fetchApps();
|
|
122
|
-
}, []);
|
|
123
|
-
const refetch = useCallback(() => store.fetchApps({ force: true }), []);
|
|
124
|
-
return toUseAppsResult(snapshot, refetch);
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
function toUseAppsResult(snapshot, refetch) {
|
|
128
|
-
return {
|
|
129
|
-
data: snapshot.status === "ready" ? snapshot.apps : void 0,
|
|
130
|
-
error: snapshot.error,
|
|
131
|
-
isError: snapshot.status === "error",
|
|
132
|
-
isLoading: snapshot.status === "loading",
|
|
133
|
-
isPending: snapshot.status === "idle" || snapshot.status === "loading",
|
|
134
|
-
isSuccess: snapshot.status === "ready",
|
|
135
|
-
refetch,
|
|
136
|
-
status: snapshot.status
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// src/hooks/use-current-screen.ts
|
|
141
|
-
import { useEffect as useEffect2, useMemo as useMemo3, useRef } from "react";
|
|
142
|
-
function createUseCurrentScreen(store) {
|
|
143
|
-
return function useCurrentScreen(options) {
|
|
144
|
-
useTailorRootContext("tailor.useCurrentScreen");
|
|
145
|
-
const id = useMemo3(() => /* @__PURE__ */ Symbol("tailorkit-current-screen"), []);
|
|
146
|
-
const status = options.status ?? "ready";
|
|
147
|
-
const context = "context" in options ? options.context : void 0;
|
|
148
|
-
const contextKey = JSON.stringify(context);
|
|
149
|
-
const contextRef = useRef({ key: contextKey, value: context });
|
|
150
|
-
if (contextRef.current.key !== contextKey) {
|
|
151
|
-
contextRef.current = { key: contextKey, value: context };
|
|
152
|
-
}
|
|
153
|
-
const contextSnapshot = contextRef.current.value;
|
|
154
|
-
useEffect2(
|
|
155
|
-
() => () => {
|
|
156
|
-
store.unregisterScreen(id);
|
|
157
|
-
},
|
|
158
|
-
[id]
|
|
159
|
-
);
|
|
160
|
-
useEffect2(() => {
|
|
161
|
-
store.registerScreen({
|
|
162
|
-
context: contextSnapshot,
|
|
163
|
-
id,
|
|
164
|
-
screen: options.screen,
|
|
165
|
-
status
|
|
166
|
-
});
|
|
167
|
-
}, [contextSnapshot, id, options.screen, status]);
|
|
168
|
-
};
|
|
169
|
-
}
|
|
1
|
+
// src/components/render-slot.tsx
|
|
2
|
+
import { useStore as useStore2 } from "@nanostores/react";
|
|
3
|
+
import { createSlotRuntime, buildThemeCss } from "@tailorkit/client-core";
|
|
4
|
+
import { useEffect as useEffect2, useId as useId2, useMemo as useMemo3, useState as useState2 } from "react";
|
|
170
5
|
|
|
171
6
|
// src/primitives.tsx
|
|
172
|
-
import {
|
|
173
|
-
import {
|
|
174
|
-
import { jsx
|
|
175
|
-
var PrimitiveThemeContext =
|
|
176
|
-
var tokenGroups = [
|
|
177
|
-
"space",
|
|
178
|
-
"radius",
|
|
179
|
-
"background",
|
|
180
|
-
"border",
|
|
181
|
-
"borderColor",
|
|
182
|
-
"textColor"
|
|
183
|
-
];
|
|
184
|
-
var cssEscape = (value) => {
|
|
185
|
-
const css = globalThis.CSS;
|
|
186
|
-
if (typeof css?.escape === "function") {
|
|
187
|
-
return css.escape(value);
|
|
188
|
-
}
|
|
189
|
-
return value.replaceAll(/[^A-Za-z0-9_-]/gu, "\\$&");
|
|
190
|
-
};
|
|
191
|
-
var toCssValue = (group, value) => {
|
|
192
|
-
if (typeof value === "number") {
|
|
193
|
-
return `${value}px`;
|
|
194
|
-
}
|
|
195
|
-
if (group === "size" || group === "raw") {
|
|
196
|
-
return value;
|
|
197
|
-
}
|
|
198
|
-
return `var(--tailorkit-${group}-${cssEscape(value)})`;
|
|
199
|
-
};
|
|
200
|
-
var toResponsiveEntries = (value) => {
|
|
201
|
-
if (value === void 0) {
|
|
202
|
-
return [];
|
|
203
|
-
}
|
|
204
|
-
if (!(value && typeof value === "object") || Array.isArray(value)) {
|
|
205
|
-
return [["base", value]];
|
|
206
|
-
}
|
|
207
|
-
return Object.entries(value).filter(
|
|
208
|
-
(entry) => entry[1] !== void 0
|
|
209
|
-
);
|
|
210
|
-
};
|
|
211
|
-
var declarationsForProp = (prop, value) => {
|
|
212
|
-
if (prop === "columns") {
|
|
213
|
-
return [`grid-template-columns: repeat(${String(value)}, minmax(0, 1fr));`];
|
|
214
|
-
}
|
|
215
|
-
const tokenDeclarations = {
|
|
216
|
-
background: ["background", "background"],
|
|
217
|
-
border: ["border", "border"],
|
|
218
|
-
borderColor: ["borderColor", "border-color"],
|
|
219
|
-
textColor: ["textColor", "color"]
|
|
220
|
-
};
|
|
221
|
-
const tokenDeclaration = tokenDeclarations[prop];
|
|
222
|
-
if (tokenDeclaration) {
|
|
223
|
-
const [group, property] = tokenDeclaration;
|
|
224
|
-
if (prop === "border") {
|
|
225
|
-
return [`border-style: ${toCssValue(group, value)};`, "border-width: 1px;"];
|
|
226
|
-
}
|
|
227
|
-
return [`${property}: ${toCssValue(group, value)};`];
|
|
228
|
-
}
|
|
229
|
-
if (prop === "gap" || prop === "margin" || prop === "padding") {
|
|
230
|
-
return [`${prop}: ${toCssValue("space", value)};`];
|
|
231
|
-
}
|
|
232
|
-
if (prop === "height" || prop === "width") {
|
|
233
|
-
return [`${prop}: ${toCssValue("size", value)};`];
|
|
234
|
-
}
|
|
235
|
-
if (prop === "radius") {
|
|
236
|
-
return [`border-radius: ${toCssValue("radius", value)};`];
|
|
237
|
-
}
|
|
238
|
-
if (prop === "align") {
|
|
239
|
-
return [`align-items: ${toFlexAlignment(value)};`];
|
|
240
|
-
}
|
|
241
|
-
if (prop === "justify") {
|
|
242
|
-
return [`justify-content: ${toFlexJustification(value)};`];
|
|
243
|
-
}
|
|
244
|
-
if (prop === "direction") {
|
|
245
|
-
return [`flex-direction: ${toCssValue("raw", value)};`];
|
|
246
|
-
}
|
|
247
|
-
if (prop === "overflow") {
|
|
248
|
-
return [`overflow: ${toCssValue("raw", value)};`];
|
|
249
|
-
}
|
|
250
|
-
if (prop === "grow") {
|
|
251
|
-
return [`flex-grow: ${String(value)};`];
|
|
252
|
-
}
|
|
253
|
-
if (prop === "shrink") {
|
|
254
|
-
return [`flex-shrink: ${String(value)};`];
|
|
255
|
-
}
|
|
256
|
-
if (prop === "minHeight") {
|
|
257
|
-
return [`min-height: ${toCssValue("size", value)};`];
|
|
258
|
-
}
|
|
259
|
-
if (prop === "minWidth") {
|
|
260
|
-
return [`min-width: ${toCssValue("size", value)};`];
|
|
261
|
-
}
|
|
262
|
-
if (prop === "basis") {
|
|
263
|
-
return [`flex-basis: ${toCssValue("size", value)};`];
|
|
264
|
-
}
|
|
265
|
-
if (prop === "wrap") {
|
|
266
|
-
return [`flex-wrap: ${toCssValue("raw", value)};`];
|
|
267
|
-
}
|
|
268
|
-
return [];
|
|
269
|
-
};
|
|
270
|
-
var toFlexAlignment = (value) => {
|
|
271
|
-
if (value === "start") {
|
|
272
|
-
return "flex-start";
|
|
273
|
-
}
|
|
274
|
-
if (value === "end") {
|
|
275
|
-
return "flex-end";
|
|
276
|
-
}
|
|
277
|
-
return String(value);
|
|
278
|
-
};
|
|
279
|
-
var toFlexJustification = (value) => {
|
|
280
|
-
if (value === "start") {
|
|
281
|
-
return "flex-start";
|
|
282
|
-
}
|
|
283
|
-
if (value === "end") {
|
|
284
|
-
return "flex-end";
|
|
285
|
-
}
|
|
286
|
-
if (value === "between") {
|
|
287
|
-
return "space-between";
|
|
288
|
-
}
|
|
289
|
-
return String(value);
|
|
290
|
-
};
|
|
291
|
-
var buildPrimitiveCss = ({
|
|
292
|
-
display,
|
|
293
|
-
nodeId,
|
|
294
|
-
props,
|
|
295
|
-
screenId,
|
|
296
|
-
theme
|
|
297
|
-
}) => {
|
|
298
|
-
const selector = `[data-tailorkit-screen="${cssEscape(screenId)}"] [data-tailorkit-node="${cssEscape(
|
|
299
|
-
nodeId
|
|
300
|
-
)}"]`;
|
|
301
|
-
const base = [`display: ${display};`];
|
|
302
|
-
const byBreakpoint = /* @__PURE__ */ new Map();
|
|
303
|
-
for (const [prop, rawValue] of Object.entries(props)) {
|
|
304
|
-
for (const [breakpoint, value] of toResponsiveEntries(rawValue)) {
|
|
305
|
-
const declarations = declarationsForProp(prop, value);
|
|
306
|
-
if (declarations.length === 0) {
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
const target = breakpoint === "base" ? base : byBreakpoint.get(breakpoint) ?? [];
|
|
310
|
-
target.push(...declarations);
|
|
311
|
-
if (breakpoint !== "base") {
|
|
312
|
-
byBreakpoint.set(breakpoint, target);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
const rules = [`${selector} { ${base.join(" ")} }`];
|
|
317
|
-
for (const [breakpoint, declarations] of byBreakpoint) {
|
|
318
|
-
const minWidth = theme.breakpoints?.[breakpoint];
|
|
319
|
-
if (!minWidth) {
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
rules.push(`@media (min-width: ${minWidth}) { ${selector} { ${declarations.join(" ")} } }`);
|
|
323
|
-
}
|
|
324
|
-
return rules.join("\n");
|
|
325
|
-
};
|
|
326
|
-
var buildThemeCss = (screenId, theme) => {
|
|
327
|
-
const selector = `[data-tailorkit-screen="${cssEscape(screenId)}"]`;
|
|
328
|
-
const declarations = [];
|
|
329
|
-
const resolvedTheme = resolveTheme(theme);
|
|
330
|
-
for (const group of tokenGroups) {
|
|
331
|
-
for (const [name, value] of Object.entries(resolvedTheme.tokens?.[group] ?? {})) {
|
|
332
|
-
declarations.push(`--tailorkit-${group}-${cssEscape(name)}: ${value};`);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
return `${selector} { ${declarations.join(" ")} }`;
|
|
336
|
-
};
|
|
7
|
+
import { buildPrimitiveCss } from "@tailorkit/client-core";
|
|
8
|
+
import { createContext, useContext, useId, useMemo } from "react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
var PrimitiveThemeContext = createContext(null);
|
|
337
11
|
var usePrimitiveNodeId = () => {
|
|
338
12
|
const id = useId();
|
|
339
|
-
return
|
|
13
|
+
return useMemo(() => `tailorkit-${id.replaceAll(":", "")}`, [id]);
|
|
340
14
|
};
|
|
341
15
|
var Primitive = ({
|
|
342
16
|
children,
|
|
@@ -344,160 +18,65 @@ var Primitive = ({
|
|
|
344
18
|
element,
|
|
345
19
|
props
|
|
346
20
|
}) => {
|
|
347
|
-
const context =
|
|
21
|
+
const context = useContext(PrimitiveThemeContext);
|
|
348
22
|
const nodeId = usePrimitiveNodeId();
|
|
349
23
|
const Element = element;
|
|
350
24
|
const css = context ? buildPrimitiveCss({
|
|
351
25
|
display,
|
|
352
26
|
nodeId,
|
|
353
27
|
props,
|
|
354
|
-
|
|
355
|
-
|
|
28
|
+
theme: context.theme,
|
|
29
|
+
viewId: context.viewId
|
|
356
30
|
}) : "";
|
|
357
31
|
return /* @__PURE__ */ jsxs(Element, { "data-tailorkit-node": nodeId, children: [
|
|
358
|
-
css === "" ? null : /* @__PURE__ */
|
|
32
|
+
css === "" ? null : /* @__PURE__ */ jsx("style", { "data-tailorkit-node-style": nodeId, children: css }),
|
|
359
33
|
children
|
|
360
34
|
] });
|
|
361
35
|
};
|
|
362
36
|
var primitives = {
|
|
363
|
-
Box: ({
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
37
|
+
Box: ({
|
|
38
|
+
props,
|
|
39
|
+
children
|
|
40
|
+
}) => /* @__PURE__ */ jsx(Primitive, { display: "block", element: "div", props, children }),
|
|
41
|
+
Flex: ({
|
|
42
|
+
props,
|
|
43
|
+
children
|
|
44
|
+
}) => /* @__PURE__ */ jsx(Primitive, { display: "flex", element: "div", props, children }),
|
|
45
|
+
Grid: ({
|
|
46
|
+
props,
|
|
47
|
+
children
|
|
48
|
+
}) => /* @__PURE__ */ jsx(Primitive, { display: "grid", element: "div", props, children }),
|
|
49
|
+
Inline: ({
|
|
50
|
+
props,
|
|
51
|
+
children
|
|
52
|
+
}) => /* @__PURE__ */ jsx(Primitive, { display: "inline", element: "span", props, children })
|
|
367
53
|
};
|
|
368
54
|
|
|
369
55
|
// src/remote-view.tsx
|
|
56
|
+
import { useStore } from "@nanostores/react";
|
|
57
|
+
import {
|
|
58
|
+
createRemoteViewStore,
|
|
59
|
+
createRemoteElementProps
|
|
60
|
+
} from "@tailorkit/client-core";
|
|
370
61
|
import {
|
|
371
62
|
Component,
|
|
372
63
|
Fragment,
|
|
373
|
-
createElement
|
|
64
|
+
createElement,
|
|
65
|
+
createContext as createContext2,
|
|
374
66
|
memo,
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
useRef as useRef2,
|
|
380
|
-
useState,
|
|
381
|
-
useSyncExternalStore as useSyncExternalStore2
|
|
67
|
+
useContext as useContext2,
|
|
68
|
+
useEffect,
|
|
69
|
+
useMemo as useMemo2,
|
|
70
|
+
useState
|
|
382
71
|
} from "react";
|
|
383
|
-
import {
|
|
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
|
-
};
|
|
491
|
-
|
|
492
|
-
// src/remote-context.ts
|
|
493
|
-
import { createContext as createContext3 } from "react";
|
|
494
|
-
var RemoteUIContext = createContext3(null);
|
|
495
|
-
|
|
496
|
-
// src/remote-view.tsx
|
|
497
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
72
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
73
|
+
var RemoteUIContext = createContext2(null);
|
|
498
74
|
var formatError = (error) => error.message;
|
|
499
75
|
var RemoteErrorBoundary = class extends Component {
|
|
500
|
-
|
|
76
|
+
constructor(props) {
|
|
77
|
+
super(props);
|
|
78
|
+
this.state = { error: null };
|
|
79
|
+
}
|
|
501
80
|
static getDerivedStateFromError(error) {
|
|
502
81
|
return { error };
|
|
503
82
|
}
|
|
@@ -506,415 +85,359 @@ var RemoteErrorBoundary = class extends Component {
|
|
|
506
85
|
}
|
|
507
86
|
render() {
|
|
508
87
|
if (this.state.error) {
|
|
509
|
-
return
|
|
88
|
+
return createElement("div", null, formatError(this.state.error));
|
|
510
89
|
}
|
|
511
90
|
return this.props.children;
|
|
512
91
|
}
|
|
513
92
|
};
|
|
514
|
-
|
|
93
|
+
var RemoteRoot = ({ store }) => {
|
|
94
|
+
const rootId = useStore(store.nodes.root);
|
|
95
|
+
return rootId === null ? null : /* @__PURE__ */ jsx2(RemoteView, { nodeId: rootId });
|
|
96
|
+
};
|
|
97
|
+
var RemoteViewHost = ({
|
|
515
98
|
appUrl,
|
|
99
|
+
sourceText,
|
|
100
|
+
toolBridge,
|
|
101
|
+
getBackendSession,
|
|
516
102
|
components: components2,
|
|
517
103
|
createIframe,
|
|
518
|
-
props
|
|
519
|
-
|
|
520
|
-
}) {
|
|
104
|
+
props
|
|
105
|
+
}) => {
|
|
521
106
|
const appKey = appUrl.toString();
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
store
|
|
539
|
-
|
|
540
|
-
useEffect3(() => {
|
|
541
|
-
ctxRef.current.components = components2;
|
|
542
|
-
}, [components2]);
|
|
543
|
-
const ui = useMemo5(() => {
|
|
544
|
-
const ctx = ctxRef.current;
|
|
545
|
-
return createElement2(
|
|
546
|
-
RemoteUIContext.Provider,
|
|
547
|
-
{ value: ctx },
|
|
548
|
-
createElement2(
|
|
549
|
-
RemoteErrorBoundary,
|
|
550
|
-
{ key: appKey },
|
|
551
|
-
createElement2(RemoteRoot, { store: ctx.store })
|
|
552
|
-
)
|
|
553
|
-
);
|
|
554
|
-
}, [appKey]);
|
|
555
|
-
useEffect3(() => {
|
|
556
|
-
const host = createIframeUiHost(appUrl, {
|
|
107
|
+
const [state, setState] = useState(() => ({
|
|
108
|
+
appKey,
|
|
109
|
+
store: createRemoteViewStore({ components: components2, props })
|
|
110
|
+
}));
|
|
111
|
+
if (state.appKey !== appKey) {
|
|
112
|
+
setState({ appKey, store: createRemoteViewStore({ components: components2, props }) });
|
|
113
|
+
}
|
|
114
|
+
const { store } = state;
|
|
115
|
+
const snapshot = useStore(store.state);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
store.setComponents(components2);
|
|
118
|
+
}, [components2, store]);
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
store.setProps(props);
|
|
121
|
+
}, [props, store]);
|
|
122
|
+
useEffect(
|
|
123
|
+
() => store.mount({
|
|
124
|
+
appUrl,
|
|
557
125
|
createIframe,
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
dispatchRef.current = (payload) => host.dispatch(payload);
|
|
567
|
-
const unsubscribe = host.subscribe(() => {
|
|
568
|
-
const tree = host.getSnapshot();
|
|
569
|
-
if (tree !== null) {
|
|
570
|
-
store.setSnapshot(tree);
|
|
571
|
-
setStatus("ready");
|
|
572
|
-
}
|
|
573
|
-
});
|
|
574
|
-
setStatus("starting");
|
|
575
|
-
setError(null);
|
|
576
|
-
host.mount();
|
|
577
|
-
return () => {
|
|
578
|
-
unsubscribe();
|
|
579
|
-
dispatchRef.current = null;
|
|
580
|
-
host.destroy();
|
|
581
|
-
};
|
|
582
|
-
}, [appUrl, createIframe, props, runtimeUrl, store]);
|
|
583
|
-
if (status === "error" && error) {
|
|
584
|
-
return createElement2("div", null, formatError(error));
|
|
585
|
-
}
|
|
586
|
-
return ui;
|
|
587
|
-
}
|
|
588
|
-
function RemoteRoot({ store }) {
|
|
589
|
-
const subscribe = useCallback2((listener) => store.subscribeRoot(listener), [store]);
|
|
590
|
-
const getSnapshot = useCallback2(() => store.getRootId(), [store]);
|
|
591
|
-
const rootId = useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
|
|
592
|
-
if (rootId === null) {
|
|
593
|
-
return null;
|
|
126
|
+
getBackendSession,
|
|
127
|
+
sourceText,
|
|
128
|
+
toolBridge
|
|
129
|
+
}),
|
|
130
|
+
[appUrl, sourceText, toolBridge, getBackendSession, createIframe, store]
|
|
131
|
+
);
|
|
132
|
+
if (snapshot.status === "error" && snapshot.error) {
|
|
133
|
+
return createElement("div", null, formatError(snapshot.error));
|
|
594
134
|
}
|
|
595
|
-
return /* @__PURE__ */
|
|
596
|
-
}
|
|
597
|
-
var
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
135
|
+
return /* @__PURE__ */ jsx2(RemoteUIContext.Provider, { value: store, children: /* @__PURE__ */ jsx2(RemoteErrorBoundary, { children: /* @__PURE__ */ jsx2(RemoteRoot, { store }) }, `${appKey}:${snapshot.generation}`) });
|
|
136
|
+
};
|
|
137
|
+
var RemoteElementViewComponent = ({
|
|
138
|
+
ctx,
|
|
139
|
+
node
|
|
140
|
+
}) => {
|
|
141
|
+
const componentStore = useMemo2(
|
|
142
|
+
() => ctx.selectComponent(node.type),
|
|
143
|
+
[ctx, node.type]
|
|
144
|
+
);
|
|
145
|
+
const component = useStore(componentStore);
|
|
146
|
+
const children = node.children.map((child) => (
|
|
147
|
+
// oxlint-disable-next-line no-use-before-define -- This deferred callback runs after initialization; the definitions are mutually dependent.
|
|
148
|
+
/* @__PURE__ */ jsx2(RemoteView, { nodeId: child.id }, child.id)
|
|
149
|
+
));
|
|
601
150
|
if (component === void 0) {
|
|
602
151
|
throw new Error(`TailorKit component "${node.type}" is not registered.`);
|
|
603
152
|
}
|
|
604
|
-
const props =
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
nodeId: node.id
|
|
612
|
-
},
|
|
613
|
-
type: "dispatchCallback"
|
|
614
|
-
});
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
return createElement2(component, props, ...children);
|
|
618
|
-
});
|
|
619
|
-
var RemoteView = memo(function RemoteView2({ nodeId }) {
|
|
620
|
-
const ctx = useContext3(RemoteUIContext);
|
|
621
|
-
if (ctx === null) {
|
|
622
|
-
return null;
|
|
623
|
-
}
|
|
624
|
-
const { store } = ctx;
|
|
625
|
-
const subscribe = useCallback2(
|
|
626
|
-
(listener) => store.subscribe(nodeId, listener),
|
|
627
|
-
[store, nodeId]
|
|
153
|
+
const props = createRemoteElementProps(node, ctx.dispatch.bind(ctx));
|
|
154
|
+
return createElement(
|
|
155
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- The renderer was supplied through the typed component registry; React's createElement overload erases its heterogeneous props. */
|
|
156
|
+
component,
|
|
157
|
+
/* oxlint-enable typescript/no-unsafe-type-assertion */
|
|
158
|
+
props,
|
|
159
|
+
...children
|
|
628
160
|
);
|
|
629
|
-
|
|
630
|
-
|
|
161
|
+
};
|
|
162
|
+
var RemoteElementView = memo(RemoteElementViewComponent);
|
|
163
|
+
var RemoteNodeView = ({
|
|
164
|
+
ctx,
|
|
165
|
+
nodeId
|
|
166
|
+
}) => {
|
|
167
|
+
const nodeStore = useMemo2(() => ctx.nodes.selectNode(nodeId), [ctx, nodeId]);
|
|
168
|
+
const node = useStore(nodeStore);
|
|
631
169
|
if (node === null) {
|
|
632
170
|
return null;
|
|
633
171
|
}
|
|
634
172
|
if (node.kind === "text") {
|
|
635
173
|
return node.text;
|
|
636
174
|
}
|
|
637
|
-
const childElements = node.children.map((child) =>
|
|
175
|
+
const childElements = node.children.map((child) => (
|
|
176
|
+
// oxlint-disable-next-line no-use-before-define -- This deferred callback runs after initialization; the definitions are mutually dependent.
|
|
177
|
+
/* @__PURE__ */ jsx2(RemoteView, { nodeId: child.id }, child.id)
|
|
178
|
+
));
|
|
638
179
|
if (node.kind === "fragment") {
|
|
639
|
-
return
|
|
180
|
+
return createElement(Fragment, { key: node.id }, ...childElements);
|
|
640
181
|
}
|
|
641
|
-
return /* @__PURE__ */
|
|
642
|
-
}
|
|
182
|
+
return /* @__PURE__ */ jsx2(RemoteElementView, { node, ctx });
|
|
183
|
+
};
|
|
184
|
+
var RemoteViewComponent = ({ nodeId }) => {
|
|
185
|
+
const ctx = useContext2(RemoteUIContext);
|
|
186
|
+
return ctx === null ? null : /* @__PURE__ */ jsx2(RemoteNodeView, { ctx, nodeId });
|
|
187
|
+
};
|
|
188
|
+
var RemoteView = memo(RemoteViewComponent);
|
|
189
|
+
|
|
190
|
+
// src/components/context.ts
|
|
191
|
+
import { createContext as createContext3, useContext as useContext3 } from "react";
|
|
192
|
+
var TailorkitContext = createContext3(
|
|
193
|
+
null
|
|
194
|
+
);
|
|
195
|
+
var useTailorkitContext = (component, expectedClient) => {
|
|
196
|
+
const context = useContext3(TailorkitContext);
|
|
197
|
+
if (!context) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`${component} must be rendered inside a TailorKit Provider.`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
if (expectedClient && context.client !== expectedClient) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`${component} was created for a different TailorKit client than the surrounding Provider.`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
return context;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// src/components/render-slot.tsx
|
|
211
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
212
|
+
var SlotRenderer = (options) => {
|
|
213
|
+
const { store, client } = useTailorkitContext(
|
|
214
|
+
options.mode === "managed" ? "RenderSlot" : "RenderSlot.Controlled"
|
|
215
|
+
);
|
|
216
|
+
const reactId = useId2();
|
|
217
|
+
const [state, setState] = useState2(() => ({
|
|
218
|
+
runtime: createSlotRuntime(store, options),
|
|
219
|
+
store
|
|
220
|
+
}));
|
|
221
|
+
if (state.store !== store) {
|
|
222
|
+
setState({ runtime: createSlotRuntime(store, options), store });
|
|
223
|
+
}
|
|
224
|
+
const { runtime } = state;
|
|
225
|
+
useEffect2(() => {
|
|
226
|
+
runtime.setInput(options);
|
|
227
|
+
}, [runtime, options]);
|
|
228
|
+
const snapshot = useStore2(runtime.state);
|
|
229
|
+
const viewId = `tailorkit-view-${reactId.replaceAll(":", "")}`;
|
|
230
|
+
const themeContext = useMemo3(
|
|
231
|
+
() => ({ theme: client.theme, viewId }),
|
|
232
|
+
[client.theme, viewId]
|
|
233
|
+
);
|
|
234
|
+
if (snapshot.status === "hidden") {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
if (snapshot.status === "loading") {
|
|
238
|
+
return /* @__PURE__ */ jsx3("output", { children: snapshot.message });
|
|
239
|
+
}
|
|
240
|
+
if (snapshot.status === "error") {
|
|
241
|
+
return /* @__PURE__ */ jsx3("div", { role: "alert", children: snapshot.error.message });
|
|
242
|
+
}
|
|
243
|
+
return /* @__PURE__ */ jsx3(PrimitiveThemeContext.Provider, { value: themeContext, children: /* @__PURE__ */ jsxs2("div", { "data-tailorkit-view": viewId, children: [
|
|
244
|
+
/* @__PURE__ */ jsx3("style", { "data-tailorkit-theme-style": viewId, children: buildThemeCss(viewId, client.theme) }),
|
|
245
|
+
/* @__PURE__ */ jsx3(
|
|
246
|
+
RemoteViewHost,
|
|
247
|
+
{
|
|
248
|
+
appUrl: snapshot.appUrl,
|
|
249
|
+
sourceText: snapshot.sourceText,
|
|
250
|
+
toolBridge: snapshot.toolBridge,
|
|
251
|
+
getBackendSession: snapshot.getBackendSession,
|
|
252
|
+
components: client.components,
|
|
253
|
+
props: snapshot.props
|
|
254
|
+
},
|
|
255
|
+
snapshot.hostKey
|
|
256
|
+
)
|
|
257
|
+
] }) });
|
|
258
|
+
};
|
|
259
|
+
var ManagedRenderSlot = (props) => /* @__PURE__ */ jsx3(SlotRenderer, { mode: "managed", ...props });
|
|
260
|
+
var ControlledRenderSlot = (props) => /* @__PURE__ */ jsx3(SlotRenderer, { mode: "controlled", ...props });
|
|
261
|
+
var RenderSlot = Object.assign(
|
|
262
|
+
ManagedRenderSlot,
|
|
263
|
+
{
|
|
264
|
+
Controlled: ControlledRenderSlot
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
// src/tailorkit.tsx
|
|
269
|
+
import {
|
|
270
|
+
createTailorKitClientConfig,
|
|
271
|
+
createComponentRegistry,
|
|
272
|
+
createTailorKitStore
|
|
273
|
+
} from "@tailorkit/client-core";
|
|
274
|
+
import { createElement as createElement2, useEffect as useEffect4, useMemo as useMemo8, useState as useState3 } from "react";
|
|
275
|
+
|
|
276
|
+
// src/hooks/use-apps.ts
|
|
277
|
+
import { createValueMemo, createAppsQuery } from "@tailorkit/client-core";
|
|
278
|
+
import { useMemo as useMemo5 } from "react";
|
|
279
|
+
|
|
280
|
+
// src/hooks/use-query-result.ts
|
|
281
|
+
import { useStore as useStore3 } from "@nanostores/react";
|
|
282
|
+
import { useMemo as useMemo4 } from "react";
|
|
283
|
+
var useQueryResult = (state) => {
|
|
284
|
+
const result = useStore3(state);
|
|
285
|
+
return useMemo4(
|
|
286
|
+
() => ({
|
|
287
|
+
data: result.data,
|
|
288
|
+
error: result.error,
|
|
289
|
+
fetch: result.refetch,
|
|
290
|
+
isPending: result.isPending,
|
|
291
|
+
isRefetching: result.isFetching && !result.isPending
|
|
292
|
+
}),
|
|
293
|
+
[result]
|
|
294
|
+
);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// src/hooks/use-apps.ts
|
|
298
|
+
var useAppsStore = (store, options = {}) => {
|
|
299
|
+
const memoOptions = useMemo5(() => createValueMemo(), []);
|
|
300
|
+
const stableOptions = memoOptions({
|
|
301
|
+
appIds: options.appIds === void 0 ? void 0 : [...new Set(options.appIds)].toSorted(),
|
|
302
|
+
scopes: options.scopes === void 0 ? void 0 : [...new Set(options.scopes)].toSorted()
|
|
303
|
+
});
|
|
304
|
+
const query = useMemo5(
|
|
305
|
+
() => createAppsQuery(store, stableOptions),
|
|
306
|
+
[store, stableOptions]
|
|
307
|
+
);
|
|
308
|
+
return useQueryResult(query.state);
|
|
309
|
+
};
|
|
310
|
+
var useApps = (options = {}) => useAppsStore(useTailorkitContext("useApps").store, options);
|
|
311
|
+
|
|
312
|
+
// src/hooks/use-view-context.ts
|
|
313
|
+
import { useEffect as useEffect3, useMemo as useMemo6 } from "react";
|
|
314
|
+
var useViewContext = (view, options) => {
|
|
315
|
+
const { store } = useTailorkitContext("useViewContext");
|
|
316
|
+
const id = useMemo6(() => /* @__PURE__ */ Symbol("tailorkit-current-view"), []);
|
|
317
|
+
const { context, loading, error } = options;
|
|
318
|
+
useEffect3(
|
|
319
|
+
() => () => {
|
|
320
|
+
store.views.unregister(id);
|
|
321
|
+
},
|
|
322
|
+
[id, store]
|
|
323
|
+
);
|
|
324
|
+
useEffect3(() => {
|
|
325
|
+
store.views.register({ context, error, id, loading, view });
|
|
326
|
+
}, [id, view, context, loading, error, store]);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// src/hooks/use-views.ts
|
|
330
|
+
import { createValueMemo as createValueMemo2, createViewsQuery } from "@tailorkit/client-core";
|
|
331
|
+
import { useMemo as useMemo7 } from "react";
|
|
332
|
+
var useViews = (options) => {
|
|
333
|
+
const { store } = useTailorkitContext("useViews");
|
|
334
|
+
const memoOptions = useMemo7(() => createValueMemo2(), []);
|
|
335
|
+
const stableOptions = memoOptions({
|
|
336
|
+
appIds: options.appIds === void 0 ? void 0 : [...new Set(options.appIds)].toSorted(),
|
|
337
|
+
scopes: options.scopes === void 0 ? void 0 : [...new Set(options.scopes)].toSorted(),
|
|
338
|
+
slot: options.slot
|
|
339
|
+
});
|
|
340
|
+
const query = useMemo7(
|
|
341
|
+
() => createViewsQuery(store, stableOptions),
|
|
342
|
+
[store, stableOptions]
|
|
343
|
+
);
|
|
344
|
+
return useQueryResult(query.state);
|
|
345
|
+
};
|
|
643
346
|
|
|
644
|
-
// src/
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
function
|
|
655
|
-
const wrappedComponents = {};
|
|
656
|
-
const store = createTailorKitStore(options.baseUrl);
|
|
657
|
-
const theme = options.theme ?? {};
|
|
658
|
-
for (const [name, renderer] of Object.entries(options.components ?? {})) {
|
|
659
|
-
if (renderer) {
|
|
660
|
-
const TailorKitComponent = function TailorKitComponent2({
|
|
347
|
+
// src/tailorkit.tsx
|
|
348
|
+
var components = (_contract, customComponents) => (
|
|
349
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- The wrapper preserves the declared component or slot props and changes only the rendering adapter; the generic key-to-props relationship is erased at this boundary. */
|
|
350
|
+
customComponents
|
|
351
|
+
);
|
|
352
|
+
var createReactTailorKitClient = (options) => {
|
|
353
|
+
const wrappedComponents = createComponentRegistry(
|
|
354
|
+
options.components ?? {},
|
|
355
|
+
(renderer) => (
|
|
356
|
+
// oxlint-disable-next-line typescript/promise-function-async -- Client components must render synchronously; ReactNode also permits Promise results for server components.
|
|
357
|
+
function TailorKitComponent({
|
|
661
358
|
children,
|
|
662
359
|
...props
|
|
663
360
|
}) {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
});
|
|
668
|
-
};
|
|
669
|
-
wrappedComponents[name] = TailorKitComponent;
|
|
670
|
-
wrappedComponents[toComponentTagName(name)] = TailorKitComponent;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
const useApps = createUseApps(store);
|
|
674
|
-
const useCurrentScreen = createUseCurrentScreen(store);
|
|
675
|
-
const AppView = ({
|
|
676
|
-
app,
|
|
677
|
-
createIframe,
|
|
678
|
-
fallback = null,
|
|
679
|
-
runtimeUrl,
|
|
680
|
-
...screenProps
|
|
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
|
|
361
|
+
const rendererArgs = { props };
|
|
362
|
+
if (children !== void 0) {
|
|
363
|
+
rendererArgs.children = children;
|
|
706
364
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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
|
-
] }) });
|
|
739
|
-
};
|
|
740
|
-
const client = {
|
|
741
|
-
AppView,
|
|
742
|
-
Root: (props) => /* @__PURE__ */ jsx4(Root, { ...props, tailor: client }),
|
|
743
|
-
getApp: store.getApp,
|
|
744
|
-
getApps: store.getApps,
|
|
745
|
-
useApps,
|
|
746
|
-
useCurrentScreen
|
|
747
|
-
};
|
|
748
|
-
return {
|
|
749
|
-
...client
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
function createTailorKitStore(baseUrlInput) {
|
|
753
|
-
const baseUrl = toBaseUrl(baseUrlInput);
|
|
754
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
755
|
-
const screens = /* @__PURE__ */ new Map();
|
|
756
|
-
let appsSnapshot = {
|
|
757
|
-
apps: [],
|
|
758
|
-
error: null,
|
|
759
|
-
status: "idle"
|
|
760
|
-
};
|
|
761
|
-
let metaSnapshot = {
|
|
762
|
-
assetsBaseUrl: null,
|
|
763
|
-
error: null,
|
|
764
|
-
schema: null,
|
|
765
|
-
status: "idle"
|
|
766
|
-
};
|
|
767
|
-
let currentScreen = null;
|
|
768
|
-
let fetchAppsPromise = null;
|
|
769
|
-
let fetchMetaPromise = null;
|
|
770
|
-
let fetchAppsRequestId = 0;
|
|
771
|
-
let nextOrder = 0;
|
|
772
|
-
const emit = () => {
|
|
773
|
-
for (const listener of listeners) {
|
|
774
|
-
listener();
|
|
775
|
-
}
|
|
776
|
-
};
|
|
777
|
-
const selectCurrentScreen = () => {
|
|
778
|
-
let selected = null;
|
|
779
|
-
for (const screen of screens.values()) {
|
|
780
|
-
const depth = getScreenDepth(screen.screen);
|
|
781
|
-
const selectedDepth = selected === null ? -1 : getScreenDepth(selected.screen);
|
|
782
|
-
if (selected === null || depth > selectedDepth) {
|
|
783
|
-
selected = screen;
|
|
365
|
+
return renderer(
|
|
366
|
+
rendererArgs
|
|
367
|
+
);
|
|
784
368
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
console.warn(
|
|
795
|
-
`TailorKit found multiple active screens at the same hierarchy depth: ${screenNames}. TailorKit selected "${selected.screen}" by mount order. Only one route at a hierarchy depth should call useCurrentScreen.`
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
currentScreen = selected;
|
|
369
|
+
)
|
|
370
|
+
);
|
|
371
|
+
const clientConfig = createTailorKitClientConfig({
|
|
372
|
+
...options,
|
|
373
|
+
components: wrappedComponents
|
|
374
|
+
});
|
|
375
|
+
const ClientRenderSlot = (props) => {
|
|
376
|
+
useTailorkitContext("RenderSlot", client);
|
|
377
|
+
return createElement2(RenderSlot, props);
|
|
799
378
|
};
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
}
|
|
806
|
-
appsSnapshot = { ...appsSnapshot, status: "loading" };
|
|
807
|
-
emit();
|
|
808
|
-
const requestId = ++fetchAppsRequestId;
|
|
809
|
-
fetchAppsPromise = fetch(new URL("apps", baseUrl)).then(async (response) => {
|
|
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 = {
|
|
379
|
+
const client = {
|
|
380
|
+
...clientConfig,
|
|
381
|
+
Provider: function Provider({ children, apps }) {
|
|
382
|
+
const [store] = useState3(
|
|
383
|
+
() => createTailorKitStore({
|
|
818
384
|
apps,
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
emit();
|
|
842
|
-
fetchMetaPromise = fetch(new URL("meta", baseUrl)).then(async (response) => {
|
|
843
|
-
if (!response.ok) {
|
|
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"
|
|
852
|
-
};
|
|
853
|
-
emit();
|
|
854
|
-
}).catch((error) => {
|
|
855
|
-
metaSnapshot = {
|
|
856
|
-
...metaSnapshot,
|
|
857
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
858
|
-
status: "error"
|
|
859
|
-
};
|
|
860
|
-
emit();
|
|
861
|
-
});
|
|
862
|
-
return fetchMetaPromise;
|
|
385
|
+
assetsBaseUrl: client.assetsBaseUrl,
|
|
386
|
+
baseUrl: client.baseUrl,
|
|
387
|
+
client: clientConfig.fetchClient,
|
|
388
|
+
contract: client.contract,
|
|
389
|
+
tools: client.tools
|
|
390
|
+
})
|
|
391
|
+
);
|
|
392
|
+
useEffect4(() => {
|
|
393
|
+
store.setProvidedApps(apps);
|
|
394
|
+
}, [store, apps]);
|
|
395
|
+
useEffect4(
|
|
396
|
+
() => () => {
|
|
397
|
+
store.previews.dispose();
|
|
398
|
+
},
|
|
399
|
+
[store]
|
|
400
|
+
);
|
|
401
|
+
const context = useMemo8(() => ({ client, store }), [store]);
|
|
402
|
+
return createElement2(
|
|
403
|
+
TailorkitContext.Provider,
|
|
404
|
+
{ value: context },
|
|
405
|
+
children
|
|
406
|
+
);
|
|
863
407
|
},
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
order: existing?.order ?? nextOrder
|
|
874
|
-
});
|
|
875
|
-
if (!existing) {
|
|
876
|
-
nextOrder += 1;
|
|
408
|
+
RenderSlot: Object.assign(ClientRenderSlot, {
|
|
409
|
+
Controlled: function Controlled(props) {
|
|
410
|
+
useTailorkitContext("RenderSlot.Controlled", client);
|
|
411
|
+
return createElement2(
|
|
412
|
+
RenderSlot.Controlled,
|
|
413
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- The wrapper preserves the declared component or slot props and changes only the rendering adapter; the generic key-to-props relationship is erased at this boundary. */
|
|
414
|
+
props
|
|
415
|
+
/* oxlint-enable typescript/no-unsafe-type-assertion */
|
|
416
|
+
);
|
|
877
417
|
}
|
|
878
|
-
|
|
879
|
-
|
|
418
|
+
}),
|
|
419
|
+
useApps: function useApps2(optionsValue) {
|
|
420
|
+
useTailorkitContext("useApps", client);
|
|
421
|
+
return useApps(optionsValue);
|
|
880
422
|
},
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
listeners.delete(listener);
|
|
885
|
-
};
|
|
423
|
+
useViewContext: function useViewContext2(view, optionsValue2) {
|
|
424
|
+
useTailorkitContext("useViewContext", client);
|
|
425
|
+
useViewContext(view, optionsValue2);
|
|
886
426
|
},
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
427
|
+
useViews: function useViews2(optionsValue3) {
|
|
428
|
+
useTailorkitContext("useViews", client);
|
|
429
|
+
return (
|
|
430
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- The wrapper preserves the declared component or slot props and changes only the rendering adapter; the generic key-to-props relationship is erased at this boundary. */
|
|
431
|
+
useViews(optionsValue3)
|
|
432
|
+
);
|
|
891
433
|
}
|
|
892
434
|
};
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
}
|
|
897
|
-
function resolveAppUrl(app, baseUrl, assetsBaseUrl) {
|
|
898
|
-
if (app.clientPath) {
|
|
899
|
-
return new URL(app.clientPath, baseUrl);
|
|
900
|
-
}
|
|
901
|
-
if (!assetsBaseUrl || !app.projectId || !app.currentDeployment?.id) {
|
|
902
|
-
return null;
|
|
903
|
-
}
|
|
904
|
-
return new URL(
|
|
905
|
-
`projects/${app.projectId}/apps/${app.id}/deployments/${app.currentDeployment.id}/files/client.js`,
|
|
906
|
-
toBaseUrl(assetsBaseUrl)
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
function toBaseUrl(value) {
|
|
910
|
-
const url = value instanceof URL ? new URL(value) : new URL(value);
|
|
911
|
-
if (!url.pathname.endsWith("/")) {
|
|
912
|
-
url.pathname = `${url.pathname}/`;
|
|
913
|
-
}
|
|
914
|
-
return url;
|
|
915
|
-
}
|
|
435
|
+
return client;
|
|
436
|
+
};
|
|
437
|
+
var createClient = (options) => createReactTailorKitClient(options);
|
|
916
438
|
export {
|
|
439
|
+
RenderSlot,
|
|
917
440
|
components,
|
|
918
|
-
|
|
441
|
+
createClient,
|
|
919
442
|
primitives
|
|
920
443
|
};
|