@valbuild/react 0.13.0 → 0.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ValProvider-52f2fa13.esm.js +731 -0
- package/dist/ValProvider-7364ec46.cjs.dev.js +746 -0
- package/dist/ValProvider-a45a47b9.worker.esm.js +726 -0
- package/dist/ValProvider-c7a8476b.browser.esm.js +731 -0
- package/dist/ValProvider-e1d5ffbe.cjs.js +7 -0
- package/dist/ValProvider-e1d5ffbe.cjs.prod.js +746 -0
- package/dist/ValUI-0fbdafd4.cjs.prod.js +398 -0
- package/dist/ValUI-371e9bf4.cjs.dev.js +398 -0
- package/dist/ValUI-51404232.browser.esm.js +396 -0
- package/dist/ValUI-9a3eb570.esm.js +396 -0
- package/dist/declarations/src/AuthStatus.d.ts +6 -0
- package/dist/declarations/src/ShadowRoot.d.ts +4 -0
- package/dist/declarations/src/ValProvider.d.ts +1 -1
- package/dist/declarations/src/ValProviderWrapper.d.ts +3 -0
- package/dist/declarations/src/ValUI.d.ts +8 -0
- package/dist/declarations/src/index.d.ts +1 -1
- package/dist/{slicedToArray-0ead6329.cjs.dev.js → slicedToArray-0eb0bcdb.cjs.prod.js} +3 -17
- package/dist/{slicedToArray-bf9b195a.worker.esm.js → slicedToArray-1a246338.browser.esm.js} +3 -16
- package/dist/{slicedToArray-236143cd.browser.esm.js → slicedToArray-9e7d1407.worker.esm.js} +3 -16
- package/dist/{slicedToArray-57b117df.cjs.prod.js → slicedToArray-b7cf26e0.cjs.dev.js} +3 -17
- package/dist/{slicedToArray-390fde8c.esm.js → slicedToArray-d846e1d2.esm.js} +3 -16
- package/dist/unsupportedIterableToArray-51bb61c2.esm.js +16 -0
- package/dist/unsupportedIterableToArray-738344ef.worker.esm.js +16 -0
- package/dist/unsupportedIterableToArray-9e97e24a.cjs.dev.js +18 -0
- package/dist/unsupportedIterableToArray-afbea1dd.cjs.prod.js +18 -0
- package/dist/unsupportedIterableToArray-d3087ed5.browser.esm.js +16 -0
- package/dist/valbuild-react.browser.esm.js +9 -1083
- package/dist/valbuild-react.cjs.dev.js +24 -1074
- package/dist/valbuild-react.cjs.prod.js +24 -1074
- package/dist/valbuild-react.esm.js +13 -1079
- package/dist/valbuild-react.worker.esm.js +13 -1079
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.browser.esm.js +2 -1
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.cjs.dev.js +2 -1
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.cjs.prod.js +2 -1
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.esm.js +2 -1
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.worker.esm.js +2 -1
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.browser.esm.js +2 -1
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.cjs.dev.js +2 -1
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.cjs.prod.js +2 -1
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.esm.js +2 -1
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.worker.esm.js +2 -1
- package/package.json +9 -6
- package/src/AuthStatus.tsx +13 -0
- package/src/ShadowRoot.tsx +32 -0
- package/src/ValProvider.tsx +10 -383
- package/src/ValProviderWrapper.tsx +15 -0
- package/src/ValUI.tsx +353 -0
- package/src/index.ts +1 -1
package/src/ValUI.tsx
ADDED
@@ -0,0 +1,353 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2
|
+
import { useEffect, useState } from "react";
|
3
|
+
import { ValApi } from "./ValApi";
|
4
|
+
import { ValStore } from "./ValStore";
|
5
|
+
import { Inputs, Style, ValOverlay } from "@valbuild/ui";
|
6
|
+
import {
|
7
|
+
FileSource,
|
8
|
+
FILE_REF_PROP,
|
9
|
+
Internal,
|
10
|
+
RichText,
|
11
|
+
SourcePath,
|
12
|
+
VAL_EXTENSION,
|
13
|
+
} from "@valbuild/core";
|
14
|
+
import { PatchJSON } from "@valbuild/core/patch";
|
15
|
+
import { ImageMetadata } from "@valbuild/core/src/schema/image";
|
16
|
+
import { AuthStatus } from "./AuthStatus";
|
17
|
+
import { ShadowRoot } from "./ShadowRoot";
|
18
|
+
|
19
|
+
export type ValUIProps = {
|
20
|
+
valStore: ValStore;
|
21
|
+
valApi: ValApi;
|
22
|
+
};
|
23
|
+
|
24
|
+
export default function ValUI({ valApi, valStore }: ValUIProps) {
|
25
|
+
const [selectedSources, setSelectedSources] = useState<string[]>([]);
|
26
|
+
const [editMode, setEditMode] = useState(false);
|
27
|
+
const [editFormPosition, setEditFormPosition] = useState<{
|
28
|
+
left: number;
|
29
|
+
top: number;
|
30
|
+
} | null>(null);
|
31
|
+
|
32
|
+
const [authentication, setAuthentication] = useState<AuthStatus>({
|
33
|
+
status: "not-asked",
|
34
|
+
});
|
35
|
+
|
36
|
+
useEffect(() => {
|
37
|
+
if (editMode) {
|
38
|
+
valStore.updateAll();
|
39
|
+
}
|
40
|
+
}, [editMode]);
|
41
|
+
useEffect(() => {
|
42
|
+
let openValFormListener: ((e: MouseEvent) => void) | undefined = undefined;
|
43
|
+
let styleElement: HTMLStyleElement | undefined = undefined;
|
44
|
+
const editButtonClickOptions = {
|
45
|
+
capture: true,
|
46
|
+
passive: true,
|
47
|
+
};
|
48
|
+
if (editMode) {
|
49
|
+
// highlight val element by appending a new style
|
50
|
+
styleElement = document.createElement("style");
|
51
|
+
styleElement.id = "val-edit-highlight";
|
52
|
+
styleElement.innerHTML = `
|
53
|
+
.val-edit-mode >* [data-val-path] {
|
54
|
+
outline: #ffff00 solid 1px;
|
55
|
+
cursor: pointer;
|
56
|
+
}
|
57
|
+
`;
|
58
|
+
document.body.appendChild(styleElement);
|
59
|
+
|
60
|
+
// capture event clicks on data-val-path elements
|
61
|
+
openValFormListener = (e: MouseEvent) => {
|
62
|
+
if (e.target instanceof Element) {
|
63
|
+
let parent = e.target;
|
64
|
+
while (parent && parent !== document.body) {
|
65
|
+
if (parent.getAttribute("data-val-path")) {
|
66
|
+
break;
|
67
|
+
}
|
68
|
+
if (parent.parentElement) {
|
69
|
+
parent = parent.parentElement;
|
70
|
+
} else {
|
71
|
+
break;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
const valSources = parent?.getAttribute("data-val-path");
|
75
|
+
if (valSources) {
|
76
|
+
e.stopPropagation();
|
77
|
+
setSelectedSources(
|
78
|
+
valSources.split(
|
79
|
+
","
|
80
|
+
) /* TODO: just split on commas will not work if path contains , */
|
81
|
+
);
|
82
|
+
setEditFormPosition({
|
83
|
+
left: e.pageX,
|
84
|
+
top: e.pageY,
|
85
|
+
});
|
86
|
+
// } else if (!isValElement(e.target)) {
|
87
|
+
// console.log("click outside", e.target);
|
88
|
+
// setEditFormPosition(null);
|
89
|
+
// setSelectedSources([]);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
};
|
93
|
+
document.addEventListener(
|
94
|
+
"click",
|
95
|
+
openValFormListener,
|
96
|
+
editButtonClickOptions
|
97
|
+
);
|
98
|
+
}
|
99
|
+
return () => {
|
100
|
+
if (openValFormListener) {
|
101
|
+
document.removeEventListener(
|
102
|
+
"click",
|
103
|
+
openValFormListener,
|
104
|
+
editButtonClickOptions
|
105
|
+
);
|
106
|
+
}
|
107
|
+
styleElement?.remove();
|
108
|
+
};
|
109
|
+
}, [editMode]);
|
110
|
+
useEffect(() => {
|
111
|
+
if (editMode) {
|
112
|
+
document.body.classList.add("val-edit-mode");
|
113
|
+
} else {
|
114
|
+
document.body.classList.remove("val-edit-mode");
|
115
|
+
}
|
116
|
+
|
117
|
+
if (editMode) {
|
118
|
+
if (authentication.status !== "authenticated") {
|
119
|
+
valApi
|
120
|
+
.getSession()
|
121
|
+
.then(async (res) => {
|
122
|
+
if (res.status === 401) {
|
123
|
+
setAuthentication({
|
124
|
+
status: "unauthenticated",
|
125
|
+
});
|
126
|
+
} else if (res.ok) {
|
127
|
+
const data = await res.json();
|
128
|
+
if (data.mode === "local") {
|
129
|
+
setAuthentication({ status: "local" });
|
130
|
+
} else if (data.mode === "proxy") {
|
131
|
+
setAuthentication({
|
132
|
+
status: "authenticated",
|
133
|
+
});
|
134
|
+
} else {
|
135
|
+
setAuthentication({
|
136
|
+
status: "error",
|
137
|
+
message: "Unknown authentication mode",
|
138
|
+
});
|
139
|
+
}
|
140
|
+
} else {
|
141
|
+
let message = "Unknown error";
|
142
|
+
try {
|
143
|
+
message = await res.text();
|
144
|
+
} catch {
|
145
|
+
// ignore
|
146
|
+
}
|
147
|
+
setAuthentication({
|
148
|
+
status: "error",
|
149
|
+
message,
|
150
|
+
});
|
151
|
+
}
|
152
|
+
})
|
153
|
+
.catch((err) => {
|
154
|
+
console.error("Failed to fetch session", err);
|
155
|
+
setAuthentication({
|
156
|
+
status: "error",
|
157
|
+
message: "Unknown authentication mode",
|
158
|
+
});
|
159
|
+
});
|
160
|
+
}
|
161
|
+
} else {
|
162
|
+
if (authentication.status === "error") {
|
163
|
+
setAuthentication({
|
164
|
+
status: "not-asked",
|
165
|
+
});
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}, [editMode, authentication.status]);
|
169
|
+
|
170
|
+
const [showEditButton, setShowEditButton] = useState(false);
|
171
|
+
useEffect(() => {
|
172
|
+
setShowEditButton(true);
|
173
|
+
}, []);
|
174
|
+
|
175
|
+
const [inputs, setInputs] = useState<Inputs>({});
|
176
|
+
|
177
|
+
useEffect(() => {
|
178
|
+
setInputs({});
|
179
|
+
for (const path of selectedSources) {
|
180
|
+
valApi.getModule(path).then((serializedModule) => {
|
181
|
+
let input: Inputs[string] | undefined;
|
182
|
+
if (
|
183
|
+
serializedModule.schema.type === "string" &&
|
184
|
+
typeof serializedModule.source === "string"
|
185
|
+
) {
|
186
|
+
input = {
|
187
|
+
status: "completed",
|
188
|
+
type: "text",
|
189
|
+
data: serializedModule.source,
|
190
|
+
};
|
191
|
+
} else if (
|
192
|
+
serializedModule.schema.type === "richtext" &&
|
193
|
+
typeof serializedModule.source === "object"
|
194
|
+
) {
|
195
|
+
input = {
|
196
|
+
status: "completed",
|
197
|
+
type: "richtext",
|
198
|
+
data: serializedModule.source as RichText, // TODO: validate
|
199
|
+
};
|
200
|
+
} else if (
|
201
|
+
serializedModule.schema.type === "image" &&
|
202
|
+
serializedModule.source &&
|
203
|
+
typeof serializedModule.source === "object" &&
|
204
|
+
FILE_REF_PROP in serializedModule.source &&
|
205
|
+
typeof serializedModule.source[FILE_REF_PROP] === "string" &&
|
206
|
+
VAL_EXTENSION in serializedModule.source &&
|
207
|
+
typeof serializedModule.source[VAL_EXTENSION] === "string"
|
208
|
+
) {
|
209
|
+
input = {
|
210
|
+
status: "completed",
|
211
|
+
type: "image",
|
212
|
+
data: Internal.convertImageSource(
|
213
|
+
serializedModule.source as FileSource<ImageMetadata>
|
214
|
+
),
|
215
|
+
};
|
216
|
+
}
|
217
|
+
console.log("input path", path);
|
218
|
+
console.log("serialized path", serializedModule.path);
|
219
|
+
if (!input) {
|
220
|
+
throw new Error(
|
221
|
+
`Unsupported module type: ${serializedModule.schema.type}`
|
222
|
+
);
|
223
|
+
}
|
224
|
+
setInputs((inputs) => {
|
225
|
+
return {
|
226
|
+
...inputs,
|
227
|
+
[serializedModule.path]: input,
|
228
|
+
} as Inputs;
|
229
|
+
});
|
230
|
+
});
|
231
|
+
}
|
232
|
+
}, [selectedSources.join(",")]);
|
233
|
+
if (!showEditButton) {
|
234
|
+
return null;
|
235
|
+
}
|
236
|
+
return (
|
237
|
+
<ShadowRoot>
|
238
|
+
{/* TODO: */}
|
239
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
240
|
+
<link
|
241
|
+
rel="preconnect"
|
242
|
+
href="https://fonts.gstatic.com"
|
243
|
+
crossOrigin="anonymous"
|
244
|
+
/>
|
245
|
+
<link
|
246
|
+
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,400&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,400;1,700&display=swap"
|
247
|
+
rel="stylesheet"
|
248
|
+
/>
|
249
|
+
<Style />
|
250
|
+
<div data-mode="dark">
|
251
|
+
<ValOverlay
|
252
|
+
editMode={editMode}
|
253
|
+
setEditMode={setEditMode}
|
254
|
+
closeValWindow={() => {
|
255
|
+
setEditFormPosition(null);
|
256
|
+
setSelectedSources([]);
|
257
|
+
setInputs({});
|
258
|
+
}}
|
259
|
+
valWindow={
|
260
|
+
(editFormPosition && {
|
261
|
+
position: editFormPosition,
|
262
|
+
inputs,
|
263
|
+
onSubmit: (inputs) => {
|
264
|
+
Promise.all(
|
265
|
+
Object.entries(inputs).map(([path, input]) => {
|
266
|
+
if (input.status === "completed") {
|
267
|
+
const [moduleId, modulePath] =
|
268
|
+
Internal.splitModuleIdAndModulePath(path as SourcePath);
|
269
|
+
if (input.type === "text") {
|
270
|
+
const patch: PatchJSON = [
|
271
|
+
{
|
272
|
+
value: input.data,
|
273
|
+
op: "replace",
|
274
|
+
path: `/${modulePath
|
275
|
+
.split(".")
|
276
|
+
.map((p) => JSON.parse(p))
|
277
|
+
.join("/")}`,
|
278
|
+
},
|
279
|
+
];
|
280
|
+
return valApi.patchModuleContent(moduleId, patch);
|
281
|
+
} else if (input.type === "image") {
|
282
|
+
const pathParts = modulePath
|
283
|
+
.split(".")
|
284
|
+
.map((p) => JSON.parse(p));
|
285
|
+
|
286
|
+
if (!input?.data || !("src" in input.data)) {
|
287
|
+
// TODO: We probably need to have an Output type that is different from the Input: we have a union of both cases in Input right now, and we believe we do not want that
|
288
|
+
console.warn(
|
289
|
+
"No .src on input provided - this might mean no changes was made"
|
290
|
+
);
|
291
|
+
return;
|
292
|
+
}
|
293
|
+
const patch: PatchJSON = [
|
294
|
+
{
|
295
|
+
value: input.data.src,
|
296
|
+
op: "replace",
|
297
|
+
path: `/${pathParts.slice(0, -1).join("/")}/$${
|
298
|
+
pathParts[pathParts.length - 1]
|
299
|
+
}`,
|
300
|
+
},
|
301
|
+
];
|
302
|
+
if (input.data.metadata) {
|
303
|
+
if (input.data.addMetadata) {
|
304
|
+
patch.push({
|
305
|
+
value: input.data.metadata,
|
306
|
+
op: "add",
|
307
|
+
path: `/${pathParts.join("/")}/metadata`,
|
308
|
+
});
|
309
|
+
} else {
|
310
|
+
patch.push({
|
311
|
+
value: input.data.metadata,
|
312
|
+
op: "replace",
|
313
|
+
path: `/${pathParts.join("/")}/metadata`,
|
314
|
+
});
|
315
|
+
}
|
316
|
+
}
|
317
|
+
console.log("patch", patch);
|
318
|
+
return valApi.patchModuleContent(moduleId, patch);
|
319
|
+
} else if (input.type === "richtext") {
|
320
|
+
const patch: PatchJSON = [
|
321
|
+
{
|
322
|
+
value: input.data,
|
323
|
+
op: "replace",
|
324
|
+
path: `/${modulePath
|
325
|
+
.split(".")
|
326
|
+
.map((p) => JSON.parse(p))
|
327
|
+
.join("/")}`,
|
328
|
+
},
|
329
|
+
];
|
330
|
+
return valApi.patchModuleContent(moduleId, patch);
|
331
|
+
}
|
332
|
+
throw new Error(
|
333
|
+
`Unsupported input type: ${(input as any).type}`
|
334
|
+
);
|
335
|
+
} else {
|
336
|
+
console.error("Submitted incomplete input, ignoring...");
|
337
|
+
return Promise.resolve();
|
338
|
+
}
|
339
|
+
})
|
340
|
+
).then(() => {
|
341
|
+
setEditFormPosition(null);
|
342
|
+
setSelectedSources([]);
|
343
|
+
setInputs({});
|
344
|
+
});
|
345
|
+
},
|
346
|
+
}) ??
|
347
|
+
undefined
|
348
|
+
}
|
349
|
+
/>
|
350
|
+
</div>
|
351
|
+
</ShadowRoot>
|
352
|
+
);
|
353
|
+
}
|
package/src/index.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/* export { useContent } from "./useContent";
|
2
2
|
export { useText } from "./useText";
|
3
3
|
export { WithVal } from "./WithVal"; */
|
4
|
-
export { ValProvider } from "./
|
4
|
+
export { ValProviderWrapper as ValProvider } from "./ValProviderWrapper";
|
5
5
|
export { useVal } from "./hooks/useVal";
|
6
6
|
export { ValRichText } from "./ValRichText";
|