@tailorkit/react 0.1.0-beta.4 → 0.1.0-beta.40
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 -9
- package/dist/components/index.d.ts +1 -1
- package/dist/components/render-slot.d.ts +9 -0
- package/dist/hooks/use-apps.d.ts +6 -12
- 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 -2
- package/dist/index.js +411 -840
- package/dist/primitives.d.ts +15 -20
- package/dist/remote-view.d.ts +12 -5
- package/dist/tailorkit.d.ts +45 -0
- package/package.json +43 -13
- package/LICENSE +0 -674
- package/dist/components/render.d.ts +0 -19
- package/dist/components/root.d.ts +0 -10
- package/dist/hooks/use-current-screen.d.ts +0 -23
- package/dist/node-store.d.ts +0 -16
- package/dist/remote-context.d.ts +0 -8
- package/dist/tailor-kit.d.ts +0 -116
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 { Fragment, 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,113 @@ 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
|
};
|
|
36
|
+
var isStringValue = (value) => typeof value === "string";
|
|
37
|
+
var isPressCallback = (value) => typeof value === "function";
|
|
38
|
+
var Pressable = ({
|
|
39
|
+
props,
|
|
40
|
+
children
|
|
41
|
+
}) => {
|
|
42
|
+
const context = useContext(PrimitiveThemeContext);
|
|
43
|
+
const nodeId = usePrimitiveNodeId();
|
|
44
|
+
const displays = {
|
|
45
|
+
box: "block",
|
|
46
|
+
flex: "flex",
|
|
47
|
+
grid: "grid",
|
|
48
|
+
inline: "inline-flex"
|
|
49
|
+
};
|
|
50
|
+
const { ariaLabel, layout, onPress } = props;
|
|
51
|
+
const display = layout === "box" || layout === "flex" || layout === "grid" ? displays[layout] : displays.inline;
|
|
52
|
+
const disabled = props.disabled === true;
|
|
53
|
+
const css = context ? buildPrimitiveCss({
|
|
54
|
+
display,
|
|
55
|
+
nodeId,
|
|
56
|
+
pressable: true,
|
|
57
|
+
props,
|
|
58
|
+
theme: context.theme,
|
|
59
|
+
viewId: context.viewId
|
|
60
|
+
}) : "";
|
|
61
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
62
|
+
css === "" ? null : /* @__PURE__ */ jsx("style", { "data-tailorkit-node-style": nodeId, children: css }),
|
|
63
|
+
/* @__PURE__ */ jsx(
|
|
64
|
+
"button",
|
|
65
|
+
{
|
|
66
|
+
"aria-label": isStringValue(ariaLabel) ? ariaLabel : void 0,
|
|
67
|
+
"data-tailorkit-node": nodeId,
|
|
68
|
+
disabled,
|
|
69
|
+
onClick: () => {
|
|
70
|
+
if (!disabled && isPressCallback(onPress)) {
|
|
71
|
+
onPress();
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
type: "button",
|
|
75
|
+
children
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
] });
|
|
79
|
+
};
|
|
362
80
|
var primitives = {
|
|
363
|
-
Box: ({
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
81
|
+
Box: ({
|
|
82
|
+
props,
|
|
83
|
+
children
|
|
84
|
+
}) => /* @__PURE__ */ jsx(Primitive, { display: "block", element: "div", props, children }),
|
|
85
|
+
Flex: ({
|
|
86
|
+
props,
|
|
87
|
+
children
|
|
88
|
+
}) => /* @__PURE__ */ jsx(Primitive, { display: "flex", element: "div", props, children }),
|
|
89
|
+
Grid: ({
|
|
90
|
+
props,
|
|
91
|
+
children
|
|
92
|
+
}) => /* @__PURE__ */ jsx(Primitive, { display: "grid", element: "div", props, children }),
|
|
93
|
+
Inline: ({
|
|
94
|
+
props,
|
|
95
|
+
children
|
|
96
|
+
}) => /* @__PURE__ */ jsx(Primitive, { display: "inline", element: "span", props, children }),
|
|
97
|
+
Pressable: ({
|
|
98
|
+
props,
|
|
99
|
+
children
|
|
100
|
+
}) => /* @__PURE__ */ jsx(Pressable, { props, children })
|
|
367
101
|
};
|
|
368
102
|
|
|
369
103
|
// src/remote-view.tsx
|
|
104
|
+
import { useStore } from "@nanostores/react";
|
|
105
|
+
import {
|
|
106
|
+
createRemoteViewStore,
|
|
107
|
+
createRemoteElementProps
|
|
108
|
+
} from "@tailorkit/client-core";
|
|
370
109
|
import {
|
|
371
110
|
Component,
|
|
372
|
-
Fragment,
|
|
373
|
-
createElement
|
|
111
|
+
Fragment as Fragment2,
|
|
112
|
+
createElement,
|
|
113
|
+
createContext as createContext2,
|
|
374
114
|
memo,
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
useRef as useRef2,
|
|
380
|
-
useState,
|
|
381
|
-
useSyncExternalStore as useSyncExternalStore2
|
|
115
|
+
useContext as useContext2,
|
|
116
|
+
useEffect,
|
|
117
|
+
useMemo as useMemo2,
|
|
118
|
+
useState
|
|
382
119
|
} 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";
|
|
120
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
121
|
+
var RemoteUIContext = createContext2(null);
|
|
498
122
|
var formatError = (error) => error.message;
|
|
499
123
|
var RemoteErrorBoundary = class extends Component {
|
|
500
|
-
|
|
124
|
+
constructor(props) {
|
|
125
|
+
super(props);
|
|
126
|
+
this.state = { error: null };
|
|
127
|
+
}
|
|
501
128
|
static getDerivedStateFromError(error) {
|
|
502
129
|
return { error };
|
|
503
130
|
}
|
|
@@ -506,415 +133,359 @@ var RemoteErrorBoundary = class extends Component {
|
|
|
506
133
|
}
|
|
507
134
|
render() {
|
|
508
135
|
if (this.state.error) {
|
|
509
|
-
return
|
|
136
|
+
return createElement("div", null, formatError(this.state.error));
|
|
510
137
|
}
|
|
511
138
|
return this.props.children;
|
|
512
139
|
}
|
|
513
140
|
};
|
|
514
|
-
|
|
141
|
+
var RemoteRoot = ({ store }) => {
|
|
142
|
+
const rootId = useStore(store.nodes.root);
|
|
143
|
+
return rootId === null ? null : /* @__PURE__ */ jsx2(RemoteView, { nodeId: rootId });
|
|
144
|
+
};
|
|
145
|
+
var RemoteViewHost = ({
|
|
515
146
|
appUrl,
|
|
147
|
+
sourceText,
|
|
148
|
+
toolBridge,
|
|
149
|
+
getBackendSession,
|
|
516
150
|
components: components2,
|
|
517
151
|
createIframe,
|
|
518
|
-
props
|
|
519
|
-
|
|
520
|
-
}) {
|
|
152
|
+
props
|
|
153
|
+
}) => {
|
|
521
154
|
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, {
|
|
155
|
+
const [state, setState] = useState(() => ({
|
|
156
|
+
appKey,
|
|
157
|
+
store: createRemoteViewStore({ components: components2, props })
|
|
158
|
+
}));
|
|
159
|
+
if (state.appKey !== appKey) {
|
|
160
|
+
setState({ appKey, store: createRemoteViewStore({ components: components2, props }) });
|
|
161
|
+
}
|
|
162
|
+
const { store } = state;
|
|
163
|
+
const snapshot = useStore(store.state);
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
store.setComponents(components2);
|
|
166
|
+
}, [components2, store]);
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
store.setProps(props);
|
|
169
|
+
}, [props, store]);
|
|
170
|
+
useEffect(
|
|
171
|
+
() => store.mount({
|
|
172
|
+
appUrl,
|
|
557
173
|
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;
|
|
174
|
+
getBackendSession,
|
|
175
|
+
sourceText,
|
|
176
|
+
toolBridge
|
|
177
|
+
}),
|
|
178
|
+
[appUrl, sourceText, toolBridge, getBackendSession, createIframe, store]
|
|
179
|
+
);
|
|
180
|
+
if (snapshot.status === "error" && snapshot.error) {
|
|
181
|
+
return createElement("div", null, formatError(snapshot.error));
|
|
594
182
|
}
|
|
595
|
-
return /* @__PURE__ */
|
|
596
|
-
}
|
|
597
|
-
var
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
183
|
+
return /* @__PURE__ */ jsx2(RemoteUIContext.Provider, { value: store, children: /* @__PURE__ */ jsx2(RemoteErrorBoundary, { children: /* @__PURE__ */ jsx2(RemoteRoot, { store }) }, `${appKey}:${snapshot.generation}`) });
|
|
184
|
+
};
|
|
185
|
+
var RemoteElementViewComponent = ({
|
|
186
|
+
ctx,
|
|
187
|
+
node
|
|
188
|
+
}) => {
|
|
189
|
+
const componentStore = useMemo2(
|
|
190
|
+
() => ctx.selectComponent(node.type),
|
|
191
|
+
[ctx, node.type]
|
|
192
|
+
);
|
|
193
|
+
const component = useStore(componentStore);
|
|
194
|
+
const children = node.children.map((child) => (
|
|
195
|
+
// oxlint-disable-next-line no-use-before-define -- This deferred callback runs after initialization; the definitions are mutually dependent.
|
|
196
|
+
/* @__PURE__ */ jsx2(RemoteView, { nodeId: child.id }, child.id)
|
|
197
|
+
));
|
|
601
198
|
if (component === void 0) {
|
|
602
199
|
throw new Error(`TailorKit component "${node.type}" is not registered.`);
|
|
603
200
|
}
|
|
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]
|
|
201
|
+
const props = createRemoteElementProps(node, ctx.dispatch.bind(ctx));
|
|
202
|
+
return createElement(
|
|
203
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- The renderer was supplied through the typed component registry; React's createElement overload erases its heterogeneous props. */
|
|
204
|
+
component,
|
|
205
|
+
/* oxlint-enable typescript/no-unsafe-type-assertion */
|
|
206
|
+
props,
|
|
207
|
+
...children
|
|
628
208
|
);
|
|
629
|
-
|
|
630
|
-
|
|
209
|
+
};
|
|
210
|
+
var RemoteElementView = memo(RemoteElementViewComponent);
|
|
211
|
+
var RemoteNodeView = ({
|
|
212
|
+
ctx,
|
|
213
|
+
nodeId
|
|
214
|
+
}) => {
|
|
215
|
+
const nodeStore = useMemo2(() => ctx.nodes.selectNode(nodeId), [ctx, nodeId]);
|
|
216
|
+
const node = useStore(nodeStore);
|
|
631
217
|
if (node === null) {
|
|
632
218
|
return null;
|
|
633
219
|
}
|
|
634
220
|
if (node.kind === "text") {
|
|
635
221
|
return node.text;
|
|
636
222
|
}
|
|
637
|
-
const childElements = node.children.map((child) =>
|
|
223
|
+
const childElements = node.children.map((child) => (
|
|
224
|
+
// oxlint-disable-next-line no-use-before-define -- This deferred callback runs after initialization; the definitions are mutually dependent.
|
|
225
|
+
/* @__PURE__ */ jsx2(RemoteView, { nodeId: child.id }, child.id)
|
|
226
|
+
));
|
|
638
227
|
if (node.kind === "fragment") {
|
|
639
|
-
return
|
|
228
|
+
return createElement(Fragment2, { key: node.id }, ...childElements);
|
|
229
|
+
}
|
|
230
|
+
return /* @__PURE__ */ jsx2(RemoteElementView, { node, ctx });
|
|
231
|
+
};
|
|
232
|
+
var RemoteViewComponent = ({ nodeId }) => {
|
|
233
|
+
const ctx = useContext2(RemoteUIContext);
|
|
234
|
+
return ctx === null ? null : /* @__PURE__ */ jsx2(RemoteNodeView, { ctx, nodeId });
|
|
235
|
+
};
|
|
236
|
+
var RemoteView = memo(RemoteViewComponent);
|
|
237
|
+
|
|
238
|
+
// src/components/context.ts
|
|
239
|
+
import { createContext as createContext3, useContext as useContext3 } from "react";
|
|
240
|
+
var TailorkitContext = createContext3(
|
|
241
|
+
null
|
|
242
|
+
);
|
|
243
|
+
var useTailorkitContext = (component, expectedClient) => {
|
|
244
|
+
const context = useContext3(TailorkitContext);
|
|
245
|
+
if (!context) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
`${component} must be rendered inside a TailorKit Provider.`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
if (expectedClient && context.client !== expectedClient) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`${component} was created for a different TailorKit client than the surrounding Provider.`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return context;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/components/render-slot.tsx
|
|
259
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
260
|
+
var SlotRenderer = (options) => {
|
|
261
|
+
const { store, client } = useTailorkitContext(
|
|
262
|
+
options.mode === "managed" ? "RenderSlot" : "RenderSlot.Controlled"
|
|
263
|
+
);
|
|
264
|
+
const reactId = useId2();
|
|
265
|
+
const [state, setState] = useState2(() => ({
|
|
266
|
+
runtime: createSlotRuntime(store, options),
|
|
267
|
+
store
|
|
268
|
+
}));
|
|
269
|
+
if (state.store !== store) {
|
|
270
|
+
setState({ runtime: createSlotRuntime(store, options), store });
|
|
271
|
+
}
|
|
272
|
+
const { runtime } = state;
|
|
273
|
+
useEffect2(() => {
|
|
274
|
+
runtime.setInput(options);
|
|
275
|
+
}, [runtime, options]);
|
|
276
|
+
const snapshot = useStore2(runtime.state);
|
|
277
|
+
const viewId = `tailorkit-view-${reactId.replaceAll(":", "")}`;
|
|
278
|
+
const themeContext = useMemo3(
|
|
279
|
+
() => ({ theme: client.theme, viewId }),
|
|
280
|
+
[client.theme, viewId]
|
|
281
|
+
);
|
|
282
|
+
if (snapshot.status === "hidden") {
|
|
283
|
+
return null;
|
|
640
284
|
}
|
|
641
|
-
|
|
642
|
-
});
|
|
285
|
+
if (snapshot.status === "loading") {
|
|
286
|
+
return /* @__PURE__ */ jsx3("output", { children: snapshot.message });
|
|
287
|
+
}
|
|
288
|
+
if (snapshot.status === "error") {
|
|
289
|
+
return /* @__PURE__ */ jsx3("div", { role: "alert", children: snapshot.error.message });
|
|
290
|
+
}
|
|
291
|
+
return /* @__PURE__ */ jsx3(PrimitiveThemeContext.Provider, { value: themeContext, children: /* @__PURE__ */ jsxs2("div", { "data-tailorkit-view": viewId, children: [
|
|
292
|
+
/* @__PURE__ */ jsx3("style", { "data-tailorkit-theme-style": viewId, children: buildThemeCss(viewId, client.theme) }),
|
|
293
|
+
/* @__PURE__ */ jsx3(
|
|
294
|
+
RemoteViewHost,
|
|
295
|
+
{
|
|
296
|
+
appUrl: snapshot.appUrl,
|
|
297
|
+
sourceText: snapshot.sourceText,
|
|
298
|
+
toolBridge: snapshot.toolBridge,
|
|
299
|
+
getBackendSession: snapshot.getBackendSession,
|
|
300
|
+
components: client.components,
|
|
301
|
+
props: snapshot.props
|
|
302
|
+
},
|
|
303
|
+
snapshot.hostKey
|
|
304
|
+
)
|
|
305
|
+
] }) });
|
|
306
|
+
};
|
|
307
|
+
var ManagedRenderSlot = (props) => /* @__PURE__ */ jsx3(SlotRenderer, { mode: "managed", ...props });
|
|
308
|
+
var ControlledRenderSlot = (props) => /* @__PURE__ */ jsx3(SlotRenderer, { mode: "controlled", ...props });
|
|
309
|
+
var RenderSlot = Object.assign(
|
|
310
|
+
ManagedRenderSlot,
|
|
311
|
+
{
|
|
312
|
+
Controlled: ControlledRenderSlot
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
// src/tailorkit.tsx
|
|
317
|
+
import {
|
|
318
|
+
createTailorKitClientConfig,
|
|
319
|
+
createComponentRegistry,
|
|
320
|
+
createTailorKitStore
|
|
321
|
+
} from "@tailorkit/client-core";
|
|
322
|
+
import { createElement as createElement2, useEffect as useEffect4, useMemo as useMemo8, useState as useState3 } from "react";
|
|
643
323
|
|
|
644
|
-
// src/
|
|
645
|
-
import {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
324
|
+
// src/hooks/use-apps.ts
|
|
325
|
+
import { createValueMemo, createAppsQuery } from "@tailorkit/client-core";
|
|
326
|
+
import { useMemo as useMemo5 } from "react";
|
|
327
|
+
|
|
328
|
+
// src/hooks/use-query-result.ts
|
|
329
|
+
import { useStore as useStore3 } from "@nanostores/react";
|
|
330
|
+
import { useMemo as useMemo4 } from "react";
|
|
331
|
+
var useQueryResult = (state) => {
|
|
332
|
+
const result = useStore3(state);
|
|
333
|
+
return useMemo4(
|
|
334
|
+
() => ({
|
|
335
|
+
data: result.data,
|
|
336
|
+
error: result.error,
|
|
337
|
+
fetch: result.refetch,
|
|
338
|
+
isPending: result.isPending,
|
|
339
|
+
isRefetching: result.isFetching && !result.isPending
|
|
340
|
+
}),
|
|
341
|
+
[result]
|
|
342
|
+
);
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// src/hooks/use-apps.ts
|
|
346
|
+
var useAppsStore = (store, options = {}) => {
|
|
347
|
+
const memoOptions = useMemo5(() => createValueMemo(), []);
|
|
348
|
+
const stableOptions = memoOptions({
|
|
349
|
+
appIds: options.appIds === void 0 ? void 0 : [...new Set(options.appIds)].toSorted(),
|
|
350
|
+
scopes: options.scopes === void 0 ? void 0 : [...new Set(options.scopes)].toSorted()
|
|
351
|
+
});
|
|
352
|
+
const query = useMemo5(
|
|
353
|
+
() => createAppsQuery(store, stableOptions),
|
|
354
|
+
[store, stableOptions]
|
|
355
|
+
);
|
|
356
|
+
return useQueryResult(query.state);
|
|
357
|
+
};
|
|
358
|
+
var useApps = (options = {}) => useAppsStore(useTailorkitContext("useApps").store, options);
|
|
359
|
+
|
|
360
|
+
// src/hooks/use-view-context.ts
|
|
361
|
+
import { useEffect as useEffect3, useMemo as useMemo6 } from "react";
|
|
362
|
+
var useViewContext = (view, options) => {
|
|
363
|
+
const { store } = useTailorkitContext("useViewContext");
|
|
364
|
+
const id = useMemo6(() => /* @__PURE__ */ Symbol("tailorkit-current-view"), []);
|
|
365
|
+
const { context, loading, error } = options;
|
|
366
|
+
useEffect3(
|
|
367
|
+
() => () => {
|
|
368
|
+
store.views.unregister(id);
|
|
369
|
+
},
|
|
370
|
+
[id, store]
|
|
371
|
+
);
|
|
372
|
+
useEffect3(() => {
|
|
373
|
+
store.views.register({ context, error, id, loading, view });
|
|
374
|
+
}, [id, view, context, loading, error, store]);
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// src/hooks/use-views.ts
|
|
378
|
+
import { createValueMemo as createValueMemo2, createViewsQuery } from "@tailorkit/client-core";
|
|
379
|
+
import { useMemo as useMemo7 } from "react";
|
|
380
|
+
var useViews = (options) => {
|
|
381
|
+
const { store } = useTailorkitContext("useViews");
|
|
382
|
+
const memoOptions = useMemo7(() => createValueMemo2(), []);
|
|
383
|
+
const stableOptions = memoOptions({
|
|
384
|
+
appIds: options.appIds === void 0 ? void 0 : [...new Set(options.appIds)].toSorted(),
|
|
385
|
+
scopes: options.scopes === void 0 ? void 0 : [...new Set(options.scopes)].toSorted(),
|
|
386
|
+
slot: options.slot
|
|
387
|
+
});
|
|
388
|
+
const query = useMemo7(
|
|
389
|
+
() => createViewsQuery(store, stableOptions),
|
|
390
|
+
[store, stableOptions]
|
|
391
|
+
);
|
|
392
|
+
return useQueryResult(query.state);
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// src/tailorkit.tsx
|
|
396
|
+
var components = (_contract, customComponents) => (
|
|
397
|
+
/* 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. */
|
|
398
|
+
customComponents
|
|
399
|
+
);
|
|
400
|
+
var createReactTailorKitClient = (options) => {
|
|
401
|
+
const wrappedComponents = createComponentRegistry(
|
|
402
|
+
options.components ?? {},
|
|
403
|
+
(renderer) => (
|
|
404
|
+
// oxlint-disable-next-line typescript/promise-function-async -- Client components must render synchronously; ReactNode also permits Promise results for server components.
|
|
405
|
+
function TailorKitComponent({
|
|
661
406
|
children,
|
|
662
407
|
...props
|
|
663
408
|
}) {
|
|
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
|
|
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
|
|
409
|
+
const rendererArgs = { props };
|
|
410
|
+
if (children !== void 0) {
|
|
411
|
+
rendererArgs.children = children;
|
|
736
412
|
}
|
|
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;
|
|
784
|
-
}
|
|
785
|
-
if (selected !== null && depth === selectedDepth && screen.order > selected.order) {
|
|
786
|
-
selected = screen;
|
|
413
|
+
return renderer(
|
|
414
|
+
rendererArgs
|
|
415
|
+
);
|
|
787
416
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
currentScreen = selected;
|
|
417
|
+
)
|
|
418
|
+
);
|
|
419
|
+
const clientConfig = createTailorKitClientConfig({
|
|
420
|
+
...options,
|
|
421
|
+
components: wrappedComponents
|
|
422
|
+
});
|
|
423
|
+
const ClientRenderSlot = (props) => {
|
|
424
|
+
useTailorkitContext("RenderSlot", client);
|
|
425
|
+
return createElement2(RenderSlot, props);
|
|
799
426
|
};
|
|
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 = {
|
|
427
|
+
const client = {
|
|
428
|
+
...clientConfig,
|
|
429
|
+
Provider: function Provider({ children, apps }) {
|
|
430
|
+
const [store] = useState3(
|
|
431
|
+
() => createTailorKitStore({
|
|
818
432
|
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;
|
|
433
|
+
assetsBaseUrl: client.assetsBaseUrl,
|
|
434
|
+
baseUrl: client.baseUrl,
|
|
435
|
+
client: clientConfig.fetchClient,
|
|
436
|
+
contract: client.contract,
|
|
437
|
+
tools: client.tools
|
|
438
|
+
})
|
|
439
|
+
);
|
|
440
|
+
useEffect4(() => {
|
|
441
|
+
store.setProvidedApps(apps);
|
|
442
|
+
}, [store, apps]);
|
|
443
|
+
useEffect4(
|
|
444
|
+
() => () => {
|
|
445
|
+
store.previews.dispose();
|
|
446
|
+
},
|
|
447
|
+
[store]
|
|
448
|
+
);
|
|
449
|
+
const context = useMemo8(() => ({ client, store }), [store]);
|
|
450
|
+
return createElement2(
|
|
451
|
+
TailorkitContext.Provider,
|
|
452
|
+
{ value: context },
|
|
453
|
+
children
|
|
454
|
+
);
|
|
863
455
|
},
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
order: existing?.order ?? nextOrder
|
|
874
|
-
});
|
|
875
|
-
if (!existing) {
|
|
876
|
-
nextOrder += 1;
|
|
456
|
+
RenderSlot: Object.assign(ClientRenderSlot, {
|
|
457
|
+
Controlled: function Controlled(props) {
|
|
458
|
+
useTailorkitContext("RenderSlot.Controlled", client);
|
|
459
|
+
return createElement2(
|
|
460
|
+
RenderSlot.Controlled,
|
|
461
|
+
/* 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. */
|
|
462
|
+
props
|
|
463
|
+
/* oxlint-enable typescript/no-unsafe-type-assertion */
|
|
464
|
+
);
|
|
877
465
|
}
|
|
878
|
-
|
|
879
|
-
|
|
466
|
+
}),
|
|
467
|
+
useApps: function useApps2(optionsValue) {
|
|
468
|
+
useTailorkitContext("useApps", client);
|
|
469
|
+
return useApps(optionsValue);
|
|
880
470
|
},
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
listeners.delete(listener);
|
|
885
|
-
};
|
|
471
|
+
useViewContext: function useViewContext2(view, optionsValue2) {
|
|
472
|
+
useTailorkitContext("useViewContext", client);
|
|
473
|
+
useViewContext(view, optionsValue2);
|
|
886
474
|
},
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
475
|
+
useViews: function useViews2(optionsValue3) {
|
|
476
|
+
useTailorkitContext("useViews", client);
|
|
477
|
+
return (
|
|
478
|
+
/* 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. */
|
|
479
|
+
useViews(optionsValue3)
|
|
480
|
+
);
|
|
891
481
|
}
|
|
892
482
|
};
|
|
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
|
-
}
|
|
483
|
+
return client;
|
|
484
|
+
};
|
|
485
|
+
var createClient = (options) => createReactTailorKitClient(options);
|
|
916
486
|
export {
|
|
487
|
+
RenderSlot,
|
|
917
488
|
components,
|
|
918
|
-
|
|
489
|
+
createClient,
|
|
919
490
|
primitives
|
|
920
491
|
};
|