@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.
Files changed (48) hide show
  1. package/dist/ValProvider-52f2fa13.esm.js +731 -0
  2. package/dist/ValProvider-7364ec46.cjs.dev.js +746 -0
  3. package/dist/ValProvider-a45a47b9.worker.esm.js +726 -0
  4. package/dist/ValProvider-c7a8476b.browser.esm.js +731 -0
  5. package/dist/ValProvider-e1d5ffbe.cjs.js +7 -0
  6. package/dist/ValProvider-e1d5ffbe.cjs.prod.js +746 -0
  7. package/dist/ValUI-0fbdafd4.cjs.prod.js +398 -0
  8. package/dist/ValUI-371e9bf4.cjs.dev.js +398 -0
  9. package/dist/ValUI-51404232.browser.esm.js +396 -0
  10. package/dist/ValUI-9a3eb570.esm.js +396 -0
  11. package/dist/declarations/src/AuthStatus.d.ts +6 -0
  12. package/dist/declarations/src/ShadowRoot.d.ts +4 -0
  13. package/dist/declarations/src/ValProvider.d.ts +1 -1
  14. package/dist/declarations/src/ValProviderWrapper.d.ts +3 -0
  15. package/dist/declarations/src/ValUI.d.ts +8 -0
  16. package/dist/declarations/src/index.d.ts +1 -1
  17. package/dist/{slicedToArray-0ead6329.cjs.dev.js → slicedToArray-0eb0bcdb.cjs.prod.js} +3 -17
  18. package/dist/{slicedToArray-bf9b195a.worker.esm.js → slicedToArray-1a246338.browser.esm.js} +3 -16
  19. package/dist/{slicedToArray-236143cd.browser.esm.js → slicedToArray-9e7d1407.worker.esm.js} +3 -16
  20. package/dist/{slicedToArray-57b117df.cjs.prod.js → slicedToArray-b7cf26e0.cjs.dev.js} +3 -17
  21. package/dist/{slicedToArray-390fde8c.esm.js → slicedToArray-d846e1d2.esm.js} +3 -16
  22. package/dist/unsupportedIterableToArray-51bb61c2.esm.js +16 -0
  23. package/dist/unsupportedIterableToArray-738344ef.worker.esm.js +16 -0
  24. package/dist/unsupportedIterableToArray-9e97e24a.cjs.dev.js +18 -0
  25. package/dist/unsupportedIterableToArray-afbea1dd.cjs.prod.js +18 -0
  26. package/dist/unsupportedIterableToArray-d3087ed5.browser.esm.js +16 -0
  27. package/dist/valbuild-react.browser.esm.js +9 -1083
  28. package/dist/valbuild-react.cjs.dev.js +24 -1074
  29. package/dist/valbuild-react.cjs.prod.js +24 -1074
  30. package/dist/valbuild-react.esm.js +13 -1079
  31. package/dist/valbuild-react.worker.esm.js +13 -1079
  32. package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.browser.esm.js +2 -1
  33. package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.cjs.dev.js +2 -1
  34. package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.cjs.prod.js +2 -1
  35. package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.esm.js +2 -1
  36. package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.worker.esm.js +2 -1
  37. package/jsx-runtime/dist/valbuild-react-jsx-runtime.browser.esm.js +2 -1
  38. package/jsx-runtime/dist/valbuild-react-jsx-runtime.cjs.dev.js +2 -1
  39. package/jsx-runtime/dist/valbuild-react-jsx-runtime.cjs.prod.js +2 -1
  40. package/jsx-runtime/dist/valbuild-react-jsx-runtime.esm.js +2 -1
  41. package/jsx-runtime/dist/valbuild-react-jsx-runtime.worker.esm.js +2 -1
  42. package/package.json +9 -6
  43. package/src/AuthStatus.tsx +13 -0
  44. package/src/ShadowRoot.tsx +32 -0
  45. package/src/ValProvider.tsx +10 -383
  46. package/src/ValProviderWrapper.tsx +15 -0
  47. package/src/ValUI.tsx +353 -0
  48. 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 "./ValProvider";
4
+ export { ValProviderWrapper as ValProvider } from "./ValProviderWrapper";
5
5
  export { useVal } from "./hooks/useVal";
6
6
  export { ValRichText } from "./ValRichText";