camox 0.18.2 → 0.19.0
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/features/content/CamoxContent.js +131 -105
- package/dist/features/preview/components/DebouncedFieldEditor.js +47 -74
- package/dist/features/preview/components/LinkFieldEditor.js +291 -340
- package/dist/hooks/use-debounced-field.js +73 -0
- package/dist/lib/normalized-data.js +98 -44
- package/dist/studio.css +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/hooks/use-debounced-field.ts
|
|
6
|
+
/**
|
|
7
|
+
* Local state for a field that autosaves to a server value, debouncing the
|
|
8
|
+
* save and refusing to overwrite the user's in-progress edit when the server
|
|
9
|
+
* response lands.
|
|
10
|
+
*
|
|
11
|
+
* Without focus tracking, a fast typist can lose characters: their first
|
|
12
|
+
* keystroke triggers a debounced save → the mutation roundtrips → react-query
|
|
13
|
+
* pushes the (now stale) server value back through props → the input is reset
|
|
14
|
+
* to the just-saved value, dropping whatever the user typed in the meantime.
|
|
15
|
+
* While `isFocused` is true we ignore `serverValue` changes; when the user
|
|
16
|
+
* leaves the input we resync.
|
|
17
|
+
*/
|
|
18
|
+
function useDebouncedField(serverValue, onSave, delay = 500) {
|
|
19
|
+
const [value, setLocal] = useState(serverValue);
|
|
20
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
21
|
+
const saveRef = useRef(onSave);
|
|
22
|
+
saveRef.current = onSave;
|
|
23
|
+
const timerRef = useRef(null);
|
|
24
|
+
const pendingRef = useRef(null);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!isFocused) setLocal(serverValue);
|
|
27
|
+
}, [serverValue, isFocused]);
|
|
28
|
+
const flush = useCallback(() => {
|
|
29
|
+
if (timerRef.current) {
|
|
30
|
+
clearTimeout(timerRef.current);
|
|
31
|
+
timerRef.current = null;
|
|
32
|
+
}
|
|
33
|
+
if (pendingRef.current) {
|
|
34
|
+
const { value: v } = pendingRef.current;
|
|
35
|
+
pendingRef.current = null;
|
|
36
|
+
saveRef.current(v);
|
|
37
|
+
}
|
|
38
|
+
}, []);
|
|
39
|
+
const cancel = useCallback(() => {
|
|
40
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41
|
+
timerRef.current = null;
|
|
42
|
+
pendingRef.current = null;
|
|
43
|
+
}, []);
|
|
44
|
+
const setValue = useCallback((v_0) => {
|
|
45
|
+
setLocal(v_0);
|
|
46
|
+
pendingRef.current = { value: v_0 };
|
|
47
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
48
|
+
timerRef.current = window.setTimeout(() => {
|
|
49
|
+
timerRef.current = null;
|
|
50
|
+
const p = pendingRef.current;
|
|
51
|
+
pendingRef.current = null;
|
|
52
|
+
if (p) saveRef.current(p.value);
|
|
53
|
+
}, delay);
|
|
54
|
+
}, [delay]);
|
|
55
|
+
const onFocus = useCallback(() => setIsFocused(true), []);
|
|
56
|
+
const onBlur = useCallback(() => {
|
|
57
|
+
setIsFocused(false);
|
|
58
|
+
flush();
|
|
59
|
+
}, [flush]);
|
|
60
|
+
useEffect(() => () => flush(), [flush]);
|
|
61
|
+
return {
|
|
62
|
+
value,
|
|
63
|
+
setValue,
|
|
64
|
+
isFocused,
|
|
65
|
+
onFocus,
|
|
66
|
+
onBlur,
|
|
67
|
+
flush,
|
|
68
|
+
cancel
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { useDebouncedField };
|
|
@@ -11,9 +11,9 @@ const NormalizedDataContext = React.createContext({
|
|
|
11
11
|
});
|
|
12
12
|
const NormalizedDataProvider = (t0) => {
|
|
13
13
|
const $ = c(13);
|
|
14
|
-
if ($[0] !== "
|
|
14
|
+
if ($[0] !== "a0f4a795dddd4ca9e7a329b1dc44ac458e5aff0b7cf376e05306b8ccf0cac2a8") {
|
|
15
15
|
for (let $i = 0; $i < 13; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
16
|
-
$[0] = "
|
|
16
|
+
$[0] = "a0f4a795dddd4ca9e7a329b1dc44ac458e5aff0b7cf376e05306b8ccf0cac2a8";
|
|
17
17
|
}
|
|
18
18
|
const { files, repeatableItems, children } = t0;
|
|
19
19
|
let t1;
|
|
@@ -58,11 +58,25 @@ function useNormalizedData() {
|
|
|
58
58
|
return React.use(NormalizedDataContext);
|
|
59
59
|
}
|
|
60
60
|
const EMPTY_IDS = [];
|
|
61
|
+
/**
|
|
62
|
+
* Returns the previously-returned array when `next` is element-wise identical to
|
|
63
|
+
* it. `useQueries` hands back a fresh `results` array whenever *any* subscribed
|
|
64
|
+
* block refetches, which otherwise produces new `pageBlocks` / `beforeBlocks` /
|
|
65
|
+
* `afterBlocks` / layout-data references on every keystroke-triggered refetch and
|
|
66
|
+
* re-renders unrelated blocks (e.g. the layout's navbar/footer) — even with
|
|
67
|
+
* React Compiler, since its memoization keys on input identity.
|
|
68
|
+
*/
|
|
69
|
+
function useStableArray(next) {
|
|
70
|
+
const ref = React.useRef(next);
|
|
71
|
+
const prev = ref.current;
|
|
72
|
+
if (prev !== next && (prev.length !== next.length || prev.some((v, i) => v !== next[i]))) ref.current = next;
|
|
73
|
+
return ref.current;
|
|
74
|
+
}
|
|
61
75
|
function usePageBlocks(pageStructure) {
|
|
62
|
-
const $ = c(
|
|
63
|
-
if ($[0] !== "
|
|
64
|
-
for (let $i = 0; $i <
|
|
65
|
-
$[0] = "
|
|
76
|
+
const $ = c(36);
|
|
77
|
+
if ($[0] !== "a0f4a795dddd4ca9e7a329b1dc44ac458e5aff0b7cf376e05306b8ccf0cac2a8") {
|
|
78
|
+
for (let $i = 0; $i < 36; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
79
|
+
$[0] = "a0f4a795dddd4ca9e7a329b1dc44ac458e5aff0b7cf376e05306b8ccf0cac2a8";
|
|
66
80
|
}
|
|
67
81
|
const blockIds = pageStructure.page.blockIds;
|
|
68
82
|
const beforeIds = pageStructure.layout?.beforeBlockIds ?? EMPTY_IDS;
|
|
@@ -87,52 +101,92 @@ function usePageBlocks(pageStructure) {
|
|
|
87
101
|
$[6] = t1;
|
|
88
102
|
} else t1 = $[6];
|
|
89
103
|
const results = useQueries(t1);
|
|
90
|
-
let
|
|
91
|
-
if ($[7] !==
|
|
92
|
-
|
|
104
|
+
let map;
|
|
105
|
+
if ($[7] !== allIds || $[8] !== results) {
|
|
106
|
+
map = /* @__PURE__ */ new Map();
|
|
93
107
|
for (let i = 0; i < allIds.length; i++) {
|
|
94
108
|
const data = results[i]?.data;
|
|
95
|
-
if (data)
|
|
96
|
-
}
|
|
97
|
-
const layoutFiles = [];
|
|
98
|
-
const layoutItems = [];
|
|
99
|
-
let t3;
|
|
100
|
-
if ($[13] !== afterIds || $[14] !== beforeIds) {
|
|
101
|
-
t3 = [...beforeIds, ...afterIds];
|
|
102
|
-
$[13] = afterIds;
|
|
103
|
-
$[14] = beforeIds;
|
|
104
|
-
$[15] = t3;
|
|
105
|
-
} else t3 = $[15];
|
|
106
|
-
for (const id_0 of t3) {
|
|
107
|
-
const bundle = bundleMap.get(id_0);
|
|
108
|
-
if (bundle) {
|
|
109
|
-
layoutFiles.push(...bundle.files);
|
|
110
|
-
layoutItems.push(...bundle.repeatableItems);
|
|
111
|
-
}
|
|
109
|
+
if (data) map.set(allIds[i], data);
|
|
112
110
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
$[7] = allIds;
|
|
112
|
+
$[8] = results;
|
|
113
|
+
$[9] = map;
|
|
114
|
+
} else map = $[9];
|
|
115
|
+
const bundleMap = map;
|
|
116
|
+
let t2;
|
|
117
|
+
if ($[10] !== afterIds || $[11] !== beforeIds) {
|
|
118
|
+
t2 = [...beforeIds, ...afterIds];
|
|
119
|
+
$[10] = afterIds;
|
|
120
|
+
$[11] = beforeIds;
|
|
121
|
+
$[12] = t2;
|
|
122
|
+
} else t2 = $[12];
|
|
123
|
+
const layoutIds = t2;
|
|
124
|
+
let t3;
|
|
125
|
+
if ($[13] !== bundleMap) {
|
|
126
|
+
t3 = (ids) => ids.map((id_0) => bundleMap.get(id_0)?.block).filter(_temp4);
|
|
127
|
+
$[13] = bundleMap;
|
|
128
|
+
$[14] = t3;
|
|
129
|
+
} else t3 = $[14];
|
|
130
|
+
const resolveBlocks = t3;
|
|
131
|
+
let t4;
|
|
132
|
+
if ($[15] !== blockIds || $[16] !== resolveBlocks) {
|
|
133
|
+
t4 = resolveBlocks(blockIds);
|
|
134
|
+
$[15] = blockIds;
|
|
135
|
+
$[16] = resolveBlocks;
|
|
136
|
+
$[17] = t4;
|
|
137
|
+
} else t4 = $[17];
|
|
138
|
+
const pageBlocks = useStableArray(t4);
|
|
139
|
+
let t5;
|
|
140
|
+
if ($[18] !== beforeIds || $[19] !== resolveBlocks) {
|
|
141
|
+
t5 = resolveBlocks(beforeIds);
|
|
142
|
+
$[18] = beforeIds;
|
|
143
|
+
$[19] = resolveBlocks;
|
|
144
|
+
$[20] = t5;
|
|
145
|
+
} else t5 = $[20];
|
|
146
|
+
const beforeBlocks = useStableArray(t5);
|
|
147
|
+
let t6;
|
|
148
|
+
if ($[21] !== afterIds || $[22] !== resolveBlocks) {
|
|
149
|
+
t6 = resolveBlocks(afterIds);
|
|
150
|
+
$[21] = afterIds;
|
|
151
|
+
$[22] = resolveBlocks;
|
|
152
|
+
$[23] = t6;
|
|
153
|
+
} else t6 = $[23];
|
|
154
|
+
const afterBlocks = useStableArray(t6);
|
|
155
|
+
let t7;
|
|
156
|
+
if ($[24] !== bundleMap || $[25] !== layoutIds) {
|
|
157
|
+
t7 = layoutIds.flatMap((id_1) => bundleMap.get(id_1)?.files ?? []);
|
|
158
|
+
$[24] = bundleMap;
|
|
159
|
+
$[25] = layoutIds;
|
|
160
|
+
$[26] = t7;
|
|
161
|
+
} else t7 = $[26];
|
|
162
|
+
const layoutFiles = useStableArray(t7);
|
|
163
|
+
let t8;
|
|
164
|
+
if ($[27] !== bundleMap || $[28] !== layoutIds) {
|
|
165
|
+
t8 = layoutIds.flatMap((id_2) => bundleMap.get(id_2)?.repeatableItems ?? []);
|
|
166
|
+
$[27] = bundleMap;
|
|
167
|
+
$[28] = layoutIds;
|
|
168
|
+
$[29] = t8;
|
|
169
|
+
} else t8 = $[29];
|
|
170
|
+
const layoutItems = useStableArray(t8);
|
|
171
|
+
let t9;
|
|
172
|
+
if ($[30] !== afterBlocks || $[31] !== beforeBlocks || $[32] !== layoutFiles || $[33] !== layoutItems || $[34] !== pageBlocks) {
|
|
173
|
+
t9 = {
|
|
174
|
+
pageBlocks,
|
|
175
|
+
beforeBlocks,
|
|
176
|
+
afterBlocks,
|
|
117
177
|
layoutFiles,
|
|
118
178
|
layoutItems
|
|
119
179
|
};
|
|
120
|
-
$[
|
|
121
|
-
$[
|
|
122
|
-
$[
|
|
123
|
-
$[
|
|
124
|
-
$[
|
|
125
|
-
$[
|
|
126
|
-
} else
|
|
127
|
-
return
|
|
180
|
+
$[30] = afterBlocks;
|
|
181
|
+
$[31] = beforeBlocks;
|
|
182
|
+
$[32] = layoutFiles;
|
|
183
|
+
$[33] = layoutItems;
|
|
184
|
+
$[34] = pageBlocks;
|
|
185
|
+
$[35] = t9;
|
|
186
|
+
} else t9 = $[35];
|
|
187
|
+
return t9;
|
|
128
188
|
}
|
|
129
189
|
/** Check if a value is a { _fileId } marker */
|
|
130
|
-
function _temp6(b_1) {
|
|
131
|
-
return b_1 != null;
|
|
132
|
-
}
|
|
133
|
-
function _temp5(b_0) {
|
|
134
|
-
return b_0 != null;
|
|
135
|
-
}
|
|
136
190
|
function _temp4(b) {
|
|
137
191
|
return b != null;
|
|
138
192
|
}
|