@visual-json/react 0.1.0 → 0.2.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/README.md +0 -4
- package/dist/index.d.mts +11 -7
- package/dist/index.d.ts +11 -7
- package/dist/index.js +979 -846
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +989 -856
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -3
package/dist/index.js
CHANGED
|
@@ -25,7 +25,6 @@ __export(index_exports, {
|
|
|
25
25
|
DiffView: () => DiffView,
|
|
26
26
|
FormView: () => FormView,
|
|
27
27
|
JsonEditor: () => JsonEditor,
|
|
28
|
-
PropertyEditor: () => PropertyEditor,
|
|
29
28
|
SearchBar: () => SearchBar,
|
|
30
29
|
StudioContext: () => StudioContext,
|
|
31
30
|
TreeView: () => TreeView,
|
|
@@ -36,11 +35,11 @@ __export(index_exports, {
|
|
|
36
35
|
module.exports = __toCommonJS(index_exports);
|
|
37
36
|
|
|
38
37
|
// src/json-editor.tsx
|
|
39
|
-
var
|
|
38
|
+
var import_react10 = require("react");
|
|
40
39
|
|
|
41
40
|
// src/visual-json.tsx
|
|
42
41
|
var import_react2 = require("react");
|
|
43
|
-
var
|
|
42
|
+
var import_core2 = require("@visual-json/core");
|
|
44
43
|
|
|
45
44
|
// src/context.ts
|
|
46
45
|
var import_react = require("react");
|
|
@@ -53,6 +52,91 @@ function useStudio() {
|
|
|
53
52
|
return ctx;
|
|
54
53
|
}
|
|
55
54
|
|
|
55
|
+
// src/get-visible-nodes.ts
|
|
56
|
+
function getVisibleNodes(root, isExpanded) {
|
|
57
|
+
const result = [];
|
|
58
|
+
function walk(node) {
|
|
59
|
+
result.push(node);
|
|
60
|
+
if (isExpanded(node.id) && (node.type === "object" || node.type === "array")) {
|
|
61
|
+
for (const child of node.children) {
|
|
62
|
+
walk(child);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
walk(root);
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/selection-utils.ts
|
|
71
|
+
var import_core = require("@visual-json/core");
|
|
72
|
+
function computeSelectAllIds(tree, focusedNodeId, currentlySelected) {
|
|
73
|
+
if (!focusedNodeId) return null;
|
|
74
|
+
let node = tree.nodesById.get(focusedNodeId);
|
|
75
|
+
if (!node) return null;
|
|
76
|
+
while (node) {
|
|
77
|
+
const parent = node.parentId ? tree.nodesById.get(node.parentId) : void 0;
|
|
78
|
+
const siblings = parent ? parent.children : [tree.root];
|
|
79
|
+
const siblingIds = new Set(siblings.map((s) => s.id));
|
|
80
|
+
const allSelected = siblings.every((s) => currentlySelected.has(s.id));
|
|
81
|
+
if (!allSelected) {
|
|
82
|
+
return siblingIds;
|
|
83
|
+
}
|
|
84
|
+
if (!parent || !parent.parentId) return siblingIds;
|
|
85
|
+
node = parent;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
function computeRangeIds(visibleNodes, anchorId, targetId) {
|
|
90
|
+
const anchorIdx = visibleNodes.findIndex((n) => n.id === anchorId);
|
|
91
|
+
const targetIdx = visibleNodes.findIndex((n) => n.id === targetId);
|
|
92
|
+
if (anchorIdx === -1 || targetIdx === -1) return null;
|
|
93
|
+
const start = Math.min(anchorIdx, targetIdx);
|
|
94
|
+
const end = Math.max(anchorIdx, targetIdx);
|
|
95
|
+
const ids = /* @__PURE__ */ new Set();
|
|
96
|
+
for (let i = start; i <= end; i++) {
|
|
97
|
+
ids.add(visibleNodes[i].id);
|
|
98
|
+
}
|
|
99
|
+
return ids;
|
|
100
|
+
}
|
|
101
|
+
function deleteSelectedNodes(tree, selectedIds, visibleNodes) {
|
|
102
|
+
const idsToDelete = [...selectedIds].filter((id) => {
|
|
103
|
+
const node = tree.nodesById.get(id);
|
|
104
|
+
if (!node || node.parentId === null) return false;
|
|
105
|
+
let cur = tree.nodesById.get(node.parentId);
|
|
106
|
+
while (cur) {
|
|
107
|
+
if (selectedIds.has(cur.id)) return false;
|
|
108
|
+
cur = cur.parentId ? tree.nodesById.get(cur.parentId) : void 0;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
});
|
|
112
|
+
if (idsToDelete.length === 0) return { newTree: tree, nextFocusId: null };
|
|
113
|
+
const firstDeletedIdx = visibleNodes.findIndex((n) => selectedIds.has(n.id));
|
|
114
|
+
let newTree = tree;
|
|
115
|
+
for (const id of idsToDelete) {
|
|
116
|
+
if (newTree.nodesById.has(id)) {
|
|
117
|
+
newTree = (0, import_core.removeNode)(newTree, id);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
let nextFocusId = null;
|
|
121
|
+
for (let i = firstDeletedIdx; i < visibleNodes.length; i++) {
|
|
122
|
+
const id = visibleNodes[i].id;
|
|
123
|
+
if (!selectedIds.has(id) && newTree.nodesById.has(id)) {
|
|
124
|
+
nextFocusId = id;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!nextFocusId) {
|
|
129
|
+
for (let i = firstDeletedIdx - 1; i >= 0; i--) {
|
|
130
|
+
const id = visibleNodes[i].id;
|
|
131
|
+
if (!selectedIds.has(id) && newTree.nodesById.has(id)) {
|
|
132
|
+
nextFocusId = id;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { newTree, nextFocusId };
|
|
138
|
+
}
|
|
139
|
+
|
|
56
140
|
// src/visual-json.tsx
|
|
57
141
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
58
142
|
function collectAllIds(node) {
|
|
@@ -68,12 +152,44 @@ function VisualJson({
|
|
|
68
152
|
schema,
|
|
69
153
|
children
|
|
70
154
|
}) {
|
|
71
|
-
const [tree, setTreeState] = (0, import_react2.useState)(() => (0,
|
|
72
|
-
const [
|
|
155
|
+
const [tree, setTreeState] = (0, import_react2.useState)(() => (0, import_core2.fromJson)(value));
|
|
156
|
+
const [focusedNodeId, setFocusedNodeId] = (0, import_react2.useState)(null);
|
|
157
|
+
const [selectedNodeIds, setSelectedNodeIdsState] = (0, import_react2.useState)(
|
|
158
|
+
() => /* @__PURE__ */ new Set()
|
|
159
|
+
);
|
|
160
|
+
const selectedNodeIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
161
|
+
const setSelectedNodeIds = (0, import_react2.useCallback)((ids) => {
|
|
162
|
+
selectedNodeIdsRef.current = ids;
|
|
163
|
+
setSelectedNodeIdsState(ids);
|
|
164
|
+
}, []);
|
|
165
|
+
const anchorNodeIdRef = (0, import_react2.useRef)(null);
|
|
166
|
+
const [anchorNodeId, setAnchorNodeIdState] = (0, import_react2.useState)(null);
|
|
167
|
+
const [drillDownNodeId, setDrillDownNodeId] = (0, import_react2.useState)(null);
|
|
73
168
|
const [expandedNodeIds, setExpandedNodeIds] = (0, import_react2.useState)(
|
|
74
169
|
() => /* @__PURE__ */ new Set([tree.root.id])
|
|
75
170
|
);
|
|
76
|
-
const
|
|
171
|
+
const setAnchorNodeId = (0, import_react2.useCallback)((id) => {
|
|
172
|
+
anchorNodeIdRef.current = id;
|
|
173
|
+
setAnchorNodeIdState(id);
|
|
174
|
+
}, []);
|
|
175
|
+
const focusSelectAndDrillDown = (0, import_react2.useCallback)(
|
|
176
|
+
(nodeId) => {
|
|
177
|
+
setFocusedNodeId(nodeId);
|
|
178
|
+
setSelectedNodeIds(nodeId ? /* @__PURE__ */ new Set([nodeId]) : /* @__PURE__ */ new Set());
|
|
179
|
+
setAnchorNodeId(nodeId);
|
|
180
|
+
setDrillDownNodeId(nodeId);
|
|
181
|
+
},
|
|
182
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
183
|
+
);
|
|
184
|
+
const visibleNodes = (0, import_react2.useMemo)(
|
|
185
|
+
() => getVisibleNodes(tree.root, (id) => expandedNodeIds.has(id)),
|
|
186
|
+
[tree.root, expandedNodeIds]
|
|
187
|
+
);
|
|
188
|
+
const visibleNodesOverrideRef = (0, import_react2.useRef)(null);
|
|
189
|
+
const setVisibleNodesOverride = (0, import_react2.useCallback)((nodes) => {
|
|
190
|
+
visibleNodesOverrideRef.current = nodes;
|
|
191
|
+
}, []);
|
|
192
|
+
const historyRef = (0, import_react2.useRef)(new import_core2.History());
|
|
77
193
|
const isInternalChange = (0, import_react2.useRef)(false);
|
|
78
194
|
const hasMounted = (0, import_react2.useRef)(false);
|
|
79
195
|
const [canUndo, setCanUndo] = (0, import_react2.useState)(false);
|
|
@@ -98,11 +214,11 @@ function VisualJson({
|
|
|
98
214
|
isInternalChange.current = false;
|
|
99
215
|
return;
|
|
100
216
|
}
|
|
101
|
-
const newTree = (0,
|
|
217
|
+
const newTree = (0, import_core2.fromJson)(value);
|
|
102
218
|
setTreeState(newTree);
|
|
103
219
|
setExpandedNodeIds(/* @__PURE__ */ new Set([newTree.root.id]));
|
|
104
|
-
|
|
105
|
-
historyRef.current = new
|
|
220
|
+
focusSelectAndDrillDown(null);
|
|
221
|
+
historyRef.current = new import_core2.History();
|
|
106
222
|
historyRef.current.push(newTree);
|
|
107
223
|
setCanUndo(false);
|
|
108
224
|
setCanRedo(false);
|
|
@@ -118,7 +234,7 @@ function VisualJson({
|
|
|
118
234
|
setCanUndo(historyRef.current.canUndo);
|
|
119
235
|
setCanRedo(historyRef.current.canRedo);
|
|
120
236
|
isInternalChange.current = true;
|
|
121
|
-
onChange?.((0,
|
|
237
|
+
onChange?.((0, import_core2.toJson)(newTree.root));
|
|
122
238
|
},
|
|
123
239
|
[onChange]
|
|
124
240
|
);
|
|
@@ -129,7 +245,7 @@ function VisualJson({
|
|
|
129
245
|
setCanUndo(historyRef.current.canUndo);
|
|
130
246
|
setCanRedo(historyRef.current.canRedo);
|
|
131
247
|
isInternalChange.current = true;
|
|
132
|
-
onChange?.((0,
|
|
248
|
+
onChange?.((0, import_core2.toJson)(prev.root));
|
|
133
249
|
}
|
|
134
250
|
}, [onChange]);
|
|
135
251
|
const redo = (0, import_react2.useCallback)(() => {
|
|
@@ -139,7 +255,7 @@ function VisualJson({
|
|
|
139
255
|
setCanUndo(historyRef.current.canUndo);
|
|
140
256
|
setCanRedo(historyRef.current.canRedo);
|
|
141
257
|
isInternalChange.current = true;
|
|
142
|
-
onChange?.((0,
|
|
258
|
+
onChange?.((0, import_core2.toJson)(next.root));
|
|
143
259
|
}
|
|
144
260
|
}, [onChange]);
|
|
145
261
|
(0, import_react2.useEffect)(() => {
|
|
@@ -159,8 +275,66 @@ function VisualJson({
|
|
|
159
275
|
document.addEventListener("keydown", handleKeyDown);
|
|
160
276
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
161
277
|
}, [undo, redo]);
|
|
162
|
-
const selectNode = (0, import_react2.useCallback)(
|
|
163
|
-
|
|
278
|
+
const selectNode = (0, import_react2.useCallback)(
|
|
279
|
+
(nodeId) => {
|
|
280
|
+
setFocusedNodeId(nodeId);
|
|
281
|
+
setSelectedNodeIds(nodeId ? /* @__PURE__ */ new Set([nodeId]) : /* @__PURE__ */ new Set());
|
|
282
|
+
setAnchorNodeId(nodeId);
|
|
283
|
+
},
|
|
284
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
285
|
+
);
|
|
286
|
+
const selectAndDrillDown = focusSelectAndDrillDown;
|
|
287
|
+
const toggleNodeSelection = (0, import_react2.useCallback)(
|
|
288
|
+
(nodeId) => {
|
|
289
|
+
const next = new Set(selectedNodeIdsRef.current);
|
|
290
|
+
if (next.has(nodeId)) {
|
|
291
|
+
next.delete(nodeId);
|
|
292
|
+
} else {
|
|
293
|
+
next.add(nodeId);
|
|
294
|
+
}
|
|
295
|
+
setSelectedNodeIds(next);
|
|
296
|
+
if (next.size === 0) {
|
|
297
|
+
setFocusedNodeId(null);
|
|
298
|
+
setAnchorNodeId(null);
|
|
299
|
+
} else {
|
|
300
|
+
setFocusedNodeId(nodeId);
|
|
301
|
+
setAnchorNodeId(nodeId);
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
305
|
+
);
|
|
306
|
+
const selectNodeRange = (0, import_react2.useCallback)(
|
|
307
|
+
(toNodeId) => {
|
|
308
|
+
const nodes = visibleNodesOverrideRef.current ?? visibleNodes;
|
|
309
|
+
const anchor = anchorNodeIdRef.current;
|
|
310
|
+
if (!anchor) {
|
|
311
|
+
setFocusedNodeId(toNodeId);
|
|
312
|
+
setSelectedNodeIds(/* @__PURE__ */ new Set([toNodeId]));
|
|
313
|
+
setAnchorNodeId(toNodeId);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const rangeIds = computeRangeIds(nodes, anchor, toNodeId);
|
|
317
|
+
if (!rangeIds) {
|
|
318
|
+
setFocusedNodeId(toNodeId);
|
|
319
|
+
setSelectedNodeIds(/* @__PURE__ */ new Set([toNodeId]));
|
|
320
|
+
setAnchorNodeId(toNodeId);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
setSelectedNodeIds(rangeIds);
|
|
324
|
+
setFocusedNodeId(toNodeId);
|
|
325
|
+
},
|
|
326
|
+
[visibleNodes, setSelectedNodeIds, setAnchorNodeId]
|
|
327
|
+
);
|
|
328
|
+
const setSelection = (0, import_react2.useCallback)(
|
|
329
|
+
(focusedId, newSelectedIds, newAnchorId) => {
|
|
330
|
+
setFocusedNodeId(focusedId);
|
|
331
|
+
setSelectedNodeIds(newSelectedIds);
|
|
332
|
+
setAnchorNodeId(newAnchorId);
|
|
333
|
+
},
|
|
334
|
+
[setSelectedNodeIds, setAnchorNodeId]
|
|
335
|
+
);
|
|
336
|
+
const drillDown = (0, import_react2.useCallback)((nodeId) => {
|
|
337
|
+
setDrillDownNodeId(nodeId);
|
|
164
338
|
}, []);
|
|
165
339
|
const toggleExpand = (0, import_react2.useCallback)((nodeId) => {
|
|
166
340
|
setExpandedNodeIds((prev) => {
|
|
@@ -200,13 +374,14 @@ function VisualJson({
|
|
|
200
374
|
setSearchMatchNodeIds(/* @__PURE__ */ new Set());
|
|
201
375
|
return;
|
|
202
376
|
}
|
|
203
|
-
const matches = (0,
|
|
377
|
+
const matches = (0, import_core2.searchNodes)(tree, query);
|
|
204
378
|
setSearchMatches(matches);
|
|
205
379
|
setSearchMatchIndex(0);
|
|
206
380
|
const matchIds = new Set(matches.map((m) => m.nodeId));
|
|
207
381
|
setSearchMatchNodeIds(matchIds);
|
|
208
382
|
if (matches.length > 0) {
|
|
209
|
-
const
|
|
383
|
+
const firstId = matches[0].nodeId;
|
|
384
|
+
const ancestors = (0, import_core2.getAncestorIds)(
|
|
210
385
|
tree,
|
|
211
386
|
matches.map((m) => m.nodeId)
|
|
212
387
|
);
|
|
@@ -215,7 +390,7 @@ function VisualJson({
|
|
|
215
390
|
for (const id of ancestors) next.add(id);
|
|
216
391
|
return next;
|
|
217
392
|
});
|
|
218
|
-
|
|
393
|
+
focusSelectAndDrillDown(firstId);
|
|
219
394
|
}
|
|
220
395
|
},
|
|
221
396
|
[tree]
|
|
@@ -224,17 +399,17 @@ function VisualJson({
|
|
|
224
399
|
if (searchMatches.length === 0) return;
|
|
225
400
|
const nextIdx = (searchMatchIndex + 1) % searchMatches.length;
|
|
226
401
|
setSearchMatchIndex(nextIdx);
|
|
227
|
-
|
|
228
|
-
}, [searchMatches, searchMatchIndex]);
|
|
402
|
+
focusSelectAndDrillDown(searchMatches[nextIdx].nodeId);
|
|
403
|
+
}, [searchMatches, searchMatchIndex, focusSelectAndDrillDown]);
|
|
229
404
|
const prevSearchMatch = (0, import_react2.useCallback)(() => {
|
|
230
405
|
if (searchMatches.length === 0) return;
|
|
231
406
|
const prevIdx = (searchMatchIndex - 1 + searchMatches.length) % searchMatches.length;
|
|
232
407
|
setSearchMatchIndex(prevIdx);
|
|
233
|
-
|
|
234
|
-
}, [searchMatches, searchMatchIndex]);
|
|
408
|
+
focusSelectAndDrillDown(searchMatches[prevIdx].nodeId);
|
|
409
|
+
}, [searchMatches, searchMatchIndex, focusSelectAndDrillDown]);
|
|
235
410
|
(0, import_react2.useEffect)(() => {
|
|
236
411
|
if (!searchQuery.trim()) return;
|
|
237
|
-
const matches = (0,
|
|
412
|
+
const matches = (0, import_core2.searchNodes)(tree, searchQuery);
|
|
238
413
|
setSearchMatches(matches);
|
|
239
414
|
setSearchMatchIndex(
|
|
240
415
|
(prev) => Math.min(prev, Math.max(matches.length - 1, 0))
|
|
@@ -244,7 +419,10 @@ function VisualJson({
|
|
|
244
419
|
const state = (0, import_react2.useMemo)(
|
|
245
420
|
() => ({
|
|
246
421
|
tree,
|
|
247
|
-
|
|
422
|
+
focusedNodeId,
|
|
423
|
+
selectedNodeIds,
|
|
424
|
+
anchorNodeId,
|
|
425
|
+
drillDownNodeId,
|
|
248
426
|
expandedNodeIds,
|
|
249
427
|
schema: schema ?? null,
|
|
250
428
|
searchQuery,
|
|
@@ -254,7 +432,10 @@ function VisualJson({
|
|
|
254
432
|
}),
|
|
255
433
|
[
|
|
256
434
|
tree,
|
|
257
|
-
|
|
435
|
+
focusedNodeId,
|
|
436
|
+
selectedNodeIds,
|
|
437
|
+
anchorNodeId,
|
|
438
|
+
drillDownNodeId,
|
|
258
439
|
expandedNodeIds,
|
|
259
440
|
schema,
|
|
260
441
|
searchQuery,
|
|
@@ -267,6 +448,12 @@ function VisualJson({
|
|
|
267
448
|
() => ({
|
|
268
449
|
setTree,
|
|
269
450
|
selectNode,
|
|
451
|
+
selectAndDrillDown,
|
|
452
|
+
toggleNodeSelection,
|
|
453
|
+
selectNodeRange,
|
|
454
|
+
setSelection,
|
|
455
|
+
setVisibleNodesOverride,
|
|
456
|
+
drillDown,
|
|
270
457
|
toggleExpand,
|
|
271
458
|
expandNode,
|
|
272
459
|
collapseNode,
|
|
@@ -283,6 +470,12 @@ function VisualJson({
|
|
|
283
470
|
[
|
|
284
471
|
setTree,
|
|
285
472
|
selectNode,
|
|
473
|
+
selectAndDrillDown,
|
|
474
|
+
toggleNodeSelection,
|
|
475
|
+
selectNodeRange,
|
|
476
|
+
setSelection,
|
|
477
|
+
setVisibleNodesOverride,
|
|
478
|
+
drillDown,
|
|
286
479
|
toggleExpand,
|
|
287
480
|
expandNode,
|
|
288
481
|
collapseNode,
|
|
@@ -303,7 +496,7 @@ function VisualJson({
|
|
|
303
496
|
|
|
304
497
|
// src/tree-view.tsx
|
|
305
498
|
var import_react5 = require("react");
|
|
306
|
-
var
|
|
499
|
+
var import_core4 = require("@visual-json/core");
|
|
307
500
|
|
|
308
501
|
// src/context-menu.tsx
|
|
309
502
|
var import_react3 = require("react");
|
|
@@ -417,96 +610,194 @@ function getDisplayKey(node, state) {
|
|
|
417
610
|
return node.key;
|
|
418
611
|
}
|
|
419
612
|
|
|
420
|
-
// src/get-visible-nodes.ts
|
|
421
|
-
function getVisibleNodes(root, isExpanded) {
|
|
422
|
-
const result = [];
|
|
423
|
-
function walk(node) {
|
|
424
|
-
result.push(node);
|
|
425
|
-
if (isExpanded(node.id) && (node.type === "object" || node.type === "array")) {
|
|
426
|
-
for (const child of node.children) {
|
|
427
|
-
walk(child);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
walk(root);
|
|
432
|
-
return result;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
613
|
// src/use-drag-drop.ts
|
|
436
614
|
var import_react4 = require("react");
|
|
437
|
-
var
|
|
615
|
+
var import_core3 = require("@visual-json/core");
|
|
616
|
+
|
|
617
|
+
// src/theme.ts
|
|
618
|
+
var DEFAULT_CSS_VARS = {
|
|
619
|
+
"--vj-bg": "#1e1e1e",
|
|
620
|
+
"--vj-bg-panel": "#252526",
|
|
621
|
+
"--vj-bg-hover": "#2a2d2e",
|
|
622
|
+
"--vj-bg-selected": "#2a5a1e",
|
|
623
|
+
"--vj-bg-selected-muted": "#2a2d2e",
|
|
624
|
+
"--vj-bg-match": "#3a3520",
|
|
625
|
+
"--vj-bg-match-active": "#51502b",
|
|
626
|
+
"--vj-border": "#333333",
|
|
627
|
+
"--vj-border-subtle": "#2a2a2a",
|
|
628
|
+
"--vj-text": "#cccccc",
|
|
629
|
+
"--vj-text-muted": "#888888",
|
|
630
|
+
"--vj-text-dim": "#666666",
|
|
631
|
+
"--vj-text-dimmer": "#555555",
|
|
632
|
+
"--vj-string": "#ce9178",
|
|
633
|
+
"--vj-number": "#b5cea8",
|
|
634
|
+
"--vj-boolean": "#569cd6",
|
|
635
|
+
"--vj-accent": "#007acc",
|
|
636
|
+
"--vj-accent-muted": "#094771",
|
|
637
|
+
"--vj-input-bg": "#3c3c3c",
|
|
638
|
+
"--vj-input-border": "#555555",
|
|
639
|
+
"--vj-error": "#f48771",
|
|
640
|
+
"--vj-font": "monospace",
|
|
641
|
+
"--vj-input-font-size": "13px"
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
// src/use-drag-drop.ts
|
|
645
|
+
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
438
646
|
var INITIAL_DRAG_STATE = {
|
|
439
|
-
|
|
647
|
+
draggedNodeIds: EMPTY_SET,
|
|
440
648
|
dropTargetNodeId: null,
|
|
441
649
|
dropPosition: null
|
|
442
650
|
};
|
|
443
|
-
function
|
|
651
|
+
function sortByTreeOrder(root, ids) {
|
|
652
|
+
const result = [];
|
|
653
|
+
function walk(node) {
|
|
654
|
+
if (ids.has(node.id)) result.push(node.id);
|
|
655
|
+
for (const child of node.children) walk(child);
|
|
656
|
+
}
|
|
657
|
+
walk(root);
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
function setMultiDragImage(e, count) {
|
|
661
|
+
const ghost = document.createElement("div");
|
|
662
|
+
ghost.textContent = `${count} selected`;
|
|
663
|
+
const root = document.querySelector("[data-form-container], [role='tree']");
|
|
664
|
+
const cs = root ? getComputedStyle(root) : null;
|
|
665
|
+
const bg = cs?.getPropertyValue("--vj-bg-selected").trim() || DEFAULT_CSS_VARS["--vj-bg-selected"];
|
|
666
|
+
const fg = cs?.getPropertyValue("--vj-text-selected").trim() || cs?.getPropertyValue("--vj-text").trim() || DEFAULT_CSS_VARS["--vj-text"];
|
|
667
|
+
const font = cs?.getPropertyValue("--vj-font").trim() || DEFAULT_CSS_VARS["--vj-font"];
|
|
668
|
+
ghost.style.cssText = [
|
|
669
|
+
"position:fixed",
|
|
670
|
+
"top:-1000px",
|
|
671
|
+
"left:-1000px",
|
|
672
|
+
"padding:4px 12px",
|
|
673
|
+
`background:${bg}`,
|
|
674
|
+
`color:${fg}`,
|
|
675
|
+
`font-family:${font}`,
|
|
676
|
+
"font-size:13px",
|
|
677
|
+
"border-radius:4px",
|
|
678
|
+
"white-space:nowrap",
|
|
679
|
+
"pointer-events:none"
|
|
680
|
+
].join(";");
|
|
681
|
+
document.body.appendChild(ghost);
|
|
682
|
+
e.dataTransfer.setDragImage(ghost, 0, 14);
|
|
683
|
+
requestAnimationFrame(() => ghost.remove());
|
|
684
|
+
}
|
|
685
|
+
function useDragDrop(visibleNodes, selectedNodeIds) {
|
|
444
686
|
const { state, actions } = useStudio();
|
|
445
687
|
const [dragState, setDragState] = (0, import_react4.useState)(INITIAL_DRAG_STATE);
|
|
446
688
|
const dragStateRef = (0, import_react4.useRef)(dragState);
|
|
447
689
|
dragStateRef.current = dragState;
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
690
|
+
const visibleNodeIndexMap = (0, import_react4.useMemo)(() => {
|
|
691
|
+
const map = /* @__PURE__ */ new Map();
|
|
692
|
+
visibleNodes.forEach((n, i) => map.set(n.id, i));
|
|
693
|
+
return map;
|
|
694
|
+
}, [visibleNodes]);
|
|
695
|
+
const handleDragStart = (0, import_react4.useCallback)(
|
|
696
|
+
(nodeId) => {
|
|
697
|
+
let ids;
|
|
698
|
+
if (selectedNodeIds.size > 0 && selectedNodeIds.has(nodeId)) {
|
|
699
|
+
ids = selectedNodeIds;
|
|
700
|
+
} else {
|
|
701
|
+
ids = /* @__PURE__ */ new Set([nodeId]);
|
|
702
|
+
}
|
|
703
|
+
setDragState({
|
|
704
|
+
draggedNodeIds: ids,
|
|
705
|
+
dropTargetNodeId: null,
|
|
706
|
+
dropPosition: null
|
|
707
|
+
});
|
|
708
|
+
},
|
|
709
|
+
[selectedNodeIds]
|
|
710
|
+
);
|
|
711
|
+
const rawDragOver = (0, import_react4.useCallback)(
|
|
456
712
|
(nodeId, position) => {
|
|
713
|
+
const draggedIds = dragStateRef.current.draggedNodeIds;
|
|
714
|
+
for (const draggedId of draggedIds) {
|
|
715
|
+
if (nodeId === draggedId || (0, import_core3.isDescendant)(state.tree, nodeId, draggedId)) {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
457
719
|
setDragState((prev) => ({
|
|
458
720
|
...prev,
|
|
459
721
|
dropTargetNodeId: nodeId,
|
|
460
722
|
dropPosition: position
|
|
461
723
|
}));
|
|
462
724
|
},
|
|
463
|
-
[]
|
|
725
|
+
[state.tree]
|
|
726
|
+
);
|
|
727
|
+
const handleDragOver = (0, import_react4.useCallback)(
|
|
728
|
+
(nodeId, position) => {
|
|
729
|
+
if (position === "before") {
|
|
730
|
+
const idx = visibleNodeIndexMap.get(nodeId);
|
|
731
|
+
if (idx !== void 0 && idx > 0) {
|
|
732
|
+
rawDragOver(visibleNodes[idx - 1].id, "after");
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
rawDragOver(nodeId, position);
|
|
737
|
+
},
|
|
738
|
+
[visibleNodes, visibleNodeIndexMap, rawDragOver]
|
|
464
739
|
);
|
|
465
740
|
const handleDragEnd = (0, import_react4.useCallback)(() => {
|
|
466
741
|
setDragState(INITIAL_DRAG_STATE);
|
|
467
742
|
}, []);
|
|
468
743
|
const handleDrop = (0, import_react4.useCallback)(() => {
|
|
469
|
-
const {
|
|
470
|
-
if (
|
|
471
|
-
const draggedNode = state.tree.nodesById.get(draggedNodeId);
|
|
744
|
+
const { draggedNodeIds, dropTargetNodeId, dropPosition } = dragStateRef.current;
|
|
745
|
+
if (draggedNodeIds.size === 0 || !dropTargetNodeId || !dropPosition) return;
|
|
472
746
|
const targetNode = state.tree.nodesById.get(dropTargetNodeId);
|
|
473
|
-
if (!
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
747
|
+
if (!targetNode || !targetNode.parentId) return;
|
|
748
|
+
for (const id of draggedNodeIds) {
|
|
749
|
+
if ((0, import_core3.isDescendant)(state.tree, dropTargetNodeId, id)) return;
|
|
750
|
+
}
|
|
751
|
+
const targetParentId = targetNode.parentId;
|
|
752
|
+
const targetParent = state.tree.nodesById.get(targetParentId);
|
|
753
|
+
if (!targetParent) return;
|
|
754
|
+
const parentChildren = targetParent.children;
|
|
755
|
+
const orderedDragIds = parentChildren.filter((c) => draggedNodeIds.has(c.id)).map((c) => c.id);
|
|
756
|
+
const allSameParent = orderedDragIds.length === draggedNodeIds.size && [...draggedNodeIds].every((id) => {
|
|
757
|
+
const n = state.tree.nodesById.get(id);
|
|
758
|
+
return n?.parentId === targetParentId;
|
|
759
|
+
});
|
|
760
|
+
if (allSameParent) {
|
|
761
|
+
const newTree = (0, import_core3.reorderChildrenMulti)(
|
|
762
|
+
state.tree,
|
|
763
|
+
targetParentId,
|
|
764
|
+
orderedDragIds,
|
|
765
|
+
dropTargetNodeId,
|
|
766
|
+
dropPosition
|
|
767
|
+
);
|
|
768
|
+
actions.setTree(newTree);
|
|
769
|
+
} else {
|
|
770
|
+
const orderedIds = sortByTreeOrder(state.tree.root, draggedNodeIds);
|
|
771
|
+
const draggedNodes = orderedIds.map((id) => state.tree.nodesById.get(id)).filter((n) => !!n && n.parentId !== null).map((n) => structuredClone(n));
|
|
772
|
+
let newTree = state.tree;
|
|
773
|
+
for (const id of [...orderedIds].reverse()) {
|
|
774
|
+
if (newTree.nodesById.has(id)) {
|
|
775
|
+
newTree = (0, import_core3.removeNode)(newTree, id);
|
|
493
776
|
}
|
|
494
777
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
778
|
+
const updatedTarget = newTree.nodesById.get(dropTargetNodeId);
|
|
779
|
+
if (!updatedTarget || !updatedTarget.parentId) {
|
|
780
|
+
setDragState(INITIAL_DRAG_STATE);
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
const updatedParent = newTree.nodesById.get(updatedTarget.parentId);
|
|
784
|
+
if (!updatedParent) {
|
|
785
|
+
setDragState(INITIAL_DRAG_STATE);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
let insertIdx = updatedParent.children.findIndex(
|
|
789
|
+
(c) => c.id === dropTargetNodeId
|
|
790
|
+
);
|
|
791
|
+
if (dropPosition === "after") insertIdx++;
|
|
792
|
+
for (let i = 0; i < draggedNodes.length; i++) {
|
|
793
|
+
newTree = (0, import_core3.insertNode)(
|
|
794
|
+
newTree,
|
|
795
|
+
updatedParent.id,
|
|
796
|
+
draggedNodes[i],
|
|
797
|
+
insertIdx + i
|
|
507
798
|
);
|
|
508
|
-
actions.setTree(newTree);
|
|
509
799
|
}
|
|
800
|
+
actions.setTree(newTree);
|
|
510
801
|
}
|
|
511
802
|
setDragState(INITIAL_DRAG_STATE);
|
|
512
803
|
}, [state.tree, actions]);
|
|
@@ -528,6 +819,7 @@ function TreeNodeRow({
|
|
|
528
819
|
showValues,
|
|
529
820
|
showCounts,
|
|
530
821
|
isFocused,
|
|
822
|
+
onSelectRange,
|
|
531
823
|
onDragStart,
|
|
532
824
|
onDragOver,
|
|
533
825
|
onDragEnd,
|
|
@@ -535,19 +827,15 @@ function TreeNodeRow({
|
|
|
535
827
|
onContextMenu
|
|
536
828
|
}) {
|
|
537
829
|
const { state, actions } = useStudio();
|
|
538
|
-
const isSelected = state.
|
|
830
|
+
const isSelected = state.selectedNodeIds.has(node.id);
|
|
539
831
|
const isExpanded = state.expandedNodeIds.has(node.id);
|
|
540
832
|
const isContainer = node.type === "object" || node.type === "array";
|
|
541
833
|
const [hovered, setHovered] = (0, import_react5.useState)(false);
|
|
542
834
|
const isRoot = node.parentId === null;
|
|
543
835
|
const isSearchMatch = state.searchMatchNodeIds.has(node.id);
|
|
544
836
|
const isActiveMatch = state.searchMatches.length > 0 && state.searchMatches[state.searchMatchIndex]?.nodeId === node.id;
|
|
545
|
-
const schema = state.schema;
|
|
546
|
-
const nodeSchema = schema ? (0, import_core3.getPropertySchema)(schema, node.path) : void 0;
|
|
547
|
-
const validation = nodeSchema ? (0, import_core3.validateNode)(node, nodeSchema) : null;
|
|
548
|
-
const hasError = validation ? !validation.valid : false;
|
|
549
837
|
const isDragTarget = dragState.dropTargetNodeId === node.id;
|
|
550
|
-
const isDraggedNode = dragState.
|
|
838
|
+
const isDraggedNode = dragState.draggedNodeIds.has(node.id);
|
|
551
839
|
function displayValue() {
|
|
552
840
|
if (isContainer) {
|
|
553
841
|
return node.type === "array" ? `[${node.children.length}]` : `{${node.children.length}}`;
|
|
@@ -563,12 +851,12 @@ function TreeNodeRow({
|
|
|
563
851
|
const position = e.clientY < midY ? "before" : "after";
|
|
564
852
|
onDragOver(node.id, position);
|
|
565
853
|
}
|
|
566
|
-
let
|
|
567
|
-
let
|
|
854
|
+
let borderTopColor = "transparent";
|
|
855
|
+
let borderBottomColor = "transparent";
|
|
568
856
|
if (isDragTarget && dragState.dropPosition === "before") {
|
|
569
|
-
|
|
857
|
+
borderTopColor = "var(--vj-accent, #007acc)";
|
|
570
858
|
} else if (isDragTarget && dragState.dropPosition === "after") {
|
|
571
|
-
|
|
859
|
+
borderBottomColor = "var(--vj-accent, #007acc)";
|
|
572
860
|
}
|
|
573
861
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
574
862
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
@@ -577,13 +865,24 @@ function TreeNodeRow({
|
|
|
577
865
|
role: "treeitem",
|
|
578
866
|
"aria-selected": isSelected,
|
|
579
867
|
"aria-expanded": isContainer ? isExpanded : void 0,
|
|
580
|
-
onClick: () =>
|
|
868
|
+
onClick: (e) => {
|
|
869
|
+
if (e.shiftKey) {
|
|
870
|
+
onSelectRange(node.id);
|
|
871
|
+
} else if (e.metaKey || e.ctrlKey) {
|
|
872
|
+
actions.toggleNodeSelection(node.id);
|
|
873
|
+
} else {
|
|
874
|
+
actions.selectAndDrillDown(node.id);
|
|
875
|
+
}
|
|
876
|
+
},
|
|
581
877
|
onMouseEnter: () => setHovered(true),
|
|
582
878
|
onMouseLeave: () => setHovered(false),
|
|
583
879
|
onContextMenu: (e) => onContextMenu(e, node),
|
|
584
880
|
draggable: !isRoot,
|
|
585
881
|
onDragStart: (e) => {
|
|
586
882
|
e.dataTransfer.effectAllowed = "move";
|
|
883
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
884
|
+
setMultiDragImage(e, state.selectedNodeIds.size);
|
|
885
|
+
}
|
|
587
886
|
onDragStart(node.id);
|
|
588
887
|
},
|
|
589
888
|
onDragOver: handleDragOverEvent,
|
|
@@ -597,15 +896,16 @@ function TreeNodeRow({
|
|
|
597
896
|
display: "flex",
|
|
598
897
|
alignItems: "center",
|
|
599
898
|
gap: 6,
|
|
600
|
-
padding: "
|
|
899
|
+
padding: "1px 8px",
|
|
601
900
|
paddingLeft: 8 + depth * 16,
|
|
602
901
|
cursor: "pointer",
|
|
603
902
|
backgroundColor: isSelected ? isFocused ? "var(--vj-bg-selected, #2a5a1e)" : "var(--vj-bg-selected-muted, var(--vj-bg-hover, #2a2d2e))" : isActiveMatch ? "var(--vj-bg-match-active, #51502b)" : isSearchMatch ? "var(--vj-bg-match, #3a3520)" : hovered ? "var(--vj-bg-hover, #2a2d2e)" : "transparent",
|
|
604
903
|
minHeight: 28,
|
|
605
904
|
userSelect: "none",
|
|
606
905
|
opacity: isDraggedNode ? 0.4 : 1,
|
|
607
|
-
borderTop
|
|
608
|
-
borderBottom
|
|
906
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
907
|
+
borderBottom: `2px solid ${borderBottomColor}`,
|
|
908
|
+
boxSizing: "border-box",
|
|
609
909
|
color: isSelected && isFocused ? "var(--vj-text-selected, var(--vj-text, #cccccc))" : "var(--vj-text, #cccccc)"
|
|
610
910
|
},
|
|
611
911
|
children: [
|
|
@@ -688,6 +988,7 @@ function TreeNodeRow({
|
|
|
688
988
|
showValues,
|
|
689
989
|
showCounts,
|
|
690
990
|
isFocused,
|
|
991
|
+
onSelectRange,
|
|
691
992
|
onDragStart,
|
|
692
993
|
onDragOver,
|
|
693
994
|
onDragEnd,
|
|
@@ -705,25 +1006,34 @@ function TreeView({
|
|
|
705
1006
|
}) {
|
|
706
1007
|
const { state, actions } = useStudio();
|
|
707
1008
|
const containerRef = (0, import_react5.useRef)(null);
|
|
1009
|
+
const visibleNodes = (0, import_react5.useMemo)(
|
|
1010
|
+
() => getVisibleNodes(state.tree.root, (id) => state.expandedNodeIds.has(id)),
|
|
1011
|
+
[state.tree.root, state.expandedNodeIds]
|
|
1012
|
+
);
|
|
708
1013
|
const {
|
|
709
1014
|
dragState,
|
|
710
1015
|
handleDragStart,
|
|
711
1016
|
handleDragOver,
|
|
712
1017
|
handleDragEnd,
|
|
713
1018
|
handleDrop
|
|
714
|
-
} = useDragDrop();
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1019
|
+
} = useDragDrop(visibleNodes, state.selectedNodeIds);
|
|
1020
|
+
const handleSelectRange = (0, import_react5.useCallback)(
|
|
1021
|
+
(nodeId) => {
|
|
1022
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
1023
|
+
actions.selectNodeRange(nodeId);
|
|
1024
|
+
},
|
|
1025
|
+
[visibleNodes, actions]
|
|
719
1026
|
);
|
|
1027
|
+
const [contextMenu, setContextMenu] = (0, import_react5.useState)(null);
|
|
720
1028
|
const handleContextMenu = (0, import_react5.useCallback)(
|
|
721
1029
|
(e, node) => {
|
|
722
1030
|
e.preventDefault();
|
|
723
|
-
|
|
1031
|
+
if (!state.selectedNodeIds.has(node.id)) {
|
|
1032
|
+
actions.selectAndDrillDown(node.id);
|
|
1033
|
+
}
|
|
724
1034
|
setContextMenu({ x: e.clientX, y: e.clientY, node });
|
|
725
1035
|
},
|
|
726
|
-
[actions]
|
|
1036
|
+
[actions, state.selectedNodeIds]
|
|
727
1037
|
);
|
|
728
1038
|
const buildContextMenuItems = (0, import_react5.useCallback)(
|
|
729
1039
|
(node) => {
|
|
@@ -767,7 +1077,7 @@ function TreeView({
|
|
|
767
1077
|
items.push({
|
|
768
1078
|
label: "Copy value as JSON",
|
|
769
1079
|
action: () => {
|
|
770
|
-
const val = (0,
|
|
1080
|
+
const val = (0, import_core4.toJson)(node);
|
|
771
1081
|
const text = typeof val === "string" ? val : JSON.stringify(val, null, 2);
|
|
772
1082
|
navigator.clipboard.writeText(text).catch(() => {
|
|
773
1083
|
});
|
|
@@ -778,7 +1088,7 @@ function TreeView({
|
|
|
778
1088
|
items.push({
|
|
779
1089
|
label: "Duplicate",
|
|
780
1090
|
action: () => {
|
|
781
|
-
const newTree = (0,
|
|
1091
|
+
const newTree = (0, import_core4.duplicateNode)(state.tree, node.id);
|
|
782
1092
|
actions.setTree(newTree);
|
|
783
1093
|
}
|
|
784
1094
|
});
|
|
@@ -792,7 +1102,7 @@ function TreeView({
|
|
|
792
1102
|
].filter((t) => t !== node.type).map((t) => ({
|
|
793
1103
|
label: `Change to ${t}`,
|
|
794
1104
|
action: () => {
|
|
795
|
-
const newTree = (0,
|
|
1105
|
+
const newTree = (0, import_core4.changeType)(state.tree, node.id, t);
|
|
796
1106
|
actions.setTree(newTree);
|
|
797
1107
|
}
|
|
798
1108
|
}));
|
|
@@ -802,7 +1112,7 @@ function TreeView({
|
|
|
802
1112
|
items.push({
|
|
803
1113
|
label: "Delete",
|
|
804
1114
|
action: () => {
|
|
805
|
-
const newTree = (0,
|
|
1115
|
+
const newTree = (0, import_core4.removeNode)(state.tree, node.id);
|
|
806
1116
|
actions.setTree(newTree);
|
|
807
1117
|
}
|
|
808
1118
|
});
|
|
@@ -814,19 +1124,31 @@ function TreeView({
|
|
|
814
1124
|
const handleKeyDown = (0, import_react5.useCallback)(
|
|
815
1125
|
(e) => {
|
|
816
1126
|
const currentIndex = visibleNodes.findIndex(
|
|
817
|
-
(n) => n.id === state.
|
|
1127
|
+
(n) => n.id === state.focusedNodeId
|
|
818
1128
|
);
|
|
819
1129
|
switch (e.key) {
|
|
820
1130
|
case "ArrowDown": {
|
|
821
1131
|
e.preventDefault();
|
|
822
1132
|
const next = visibleNodes[currentIndex + 1];
|
|
823
|
-
if (next)
|
|
1133
|
+
if (next) {
|
|
1134
|
+
if (e.shiftKey) {
|
|
1135
|
+
handleSelectRange(next.id);
|
|
1136
|
+
} else {
|
|
1137
|
+
actions.selectNode(next.id);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
824
1140
|
break;
|
|
825
1141
|
}
|
|
826
1142
|
case "ArrowUp": {
|
|
827
1143
|
e.preventDefault();
|
|
828
1144
|
const prev = visibleNodes[currentIndex - 1];
|
|
829
|
-
if (prev)
|
|
1145
|
+
if (prev) {
|
|
1146
|
+
if (e.shiftKey) {
|
|
1147
|
+
handleSelectRange(prev.id);
|
|
1148
|
+
} else {
|
|
1149
|
+
actions.selectNode(prev.id);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
830
1152
|
break;
|
|
831
1153
|
}
|
|
832
1154
|
case "ArrowRight": {
|
|
@@ -853,17 +1175,47 @@ function TreeView({
|
|
|
853
1175
|
}
|
|
854
1176
|
break;
|
|
855
1177
|
}
|
|
1178
|
+
case "a": {
|
|
1179
|
+
if (e.metaKey || e.ctrlKey) {
|
|
1180
|
+
e.preventDefault();
|
|
1181
|
+
const ids = computeSelectAllIds(
|
|
1182
|
+
state.tree,
|
|
1183
|
+
state.focusedNodeId,
|
|
1184
|
+
state.selectedNodeIds
|
|
1185
|
+
);
|
|
1186
|
+
if (ids) {
|
|
1187
|
+
actions.setSelection(
|
|
1188
|
+
state.focusedNodeId,
|
|
1189
|
+
ids,
|
|
1190
|
+
state.focusedNodeId
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
break;
|
|
1195
|
+
}
|
|
1196
|
+
case "Escape": {
|
|
1197
|
+
e.preventDefault();
|
|
1198
|
+
if (state.selectedNodeIds.size > 1 && state.focusedNodeId) {
|
|
1199
|
+
actions.selectNode(state.focusedNodeId);
|
|
1200
|
+
} else {
|
|
1201
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
1202
|
+
}
|
|
1203
|
+
break;
|
|
1204
|
+
}
|
|
856
1205
|
case "Delete":
|
|
857
1206
|
case "Backspace": {
|
|
858
1207
|
e.preventDefault();
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1208
|
+
const { newTree, nextFocusId } = deleteSelectedNodes(
|
|
1209
|
+
state.tree,
|
|
1210
|
+
state.selectedNodeIds,
|
|
1211
|
+
visibleNodes
|
|
1212
|
+
);
|
|
1213
|
+
if (newTree === state.tree) break;
|
|
1214
|
+
actions.setTree(newTree);
|
|
1215
|
+
if (nextFocusId) {
|
|
1216
|
+
actions.selectNode(nextFocusId);
|
|
1217
|
+
} else {
|
|
1218
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
867
1219
|
}
|
|
868
1220
|
break;
|
|
869
1221
|
}
|
|
@@ -871,23 +1223,25 @@ function TreeView({
|
|
|
871
1223
|
},
|
|
872
1224
|
[
|
|
873
1225
|
visibleNodes,
|
|
874
|
-
state.
|
|
1226
|
+
state.focusedNodeId,
|
|
1227
|
+
state.selectedNodeIds,
|
|
875
1228
|
state.expandedNodeIds,
|
|
876
1229
|
state.tree,
|
|
877
|
-
actions
|
|
1230
|
+
actions,
|
|
1231
|
+
handleSelectRange
|
|
878
1232
|
]
|
|
879
1233
|
);
|
|
880
1234
|
const [isFocused, setIsFocused] = (0, import_react5.useState)(false);
|
|
881
1235
|
(0, import_react5.useEffect)(() => {
|
|
882
|
-
if (state.
|
|
1236
|
+
if (state.focusedNodeId && containerRef.current) {
|
|
883
1237
|
const el = containerRef.current.querySelector(
|
|
884
|
-
`[data-node-id="${state.
|
|
1238
|
+
`[data-node-id="${state.focusedNodeId}"]`
|
|
885
1239
|
);
|
|
886
1240
|
if (el) {
|
|
887
1241
|
el.scrollIntoView({ block: "nearest" });
|
|
888
1242
|
}
|
|
889
1243
|
}
|
|
890
|
-
}, [state.
|
|
1244
|
+
}, [state.focusedNodeId]);
|
|
891
1245
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
892
1246
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
893
1247
|
"div",
|
|
@@ -917,6 +1271,7 @@ function TreeView({
|
|
|
917
1271
|
showValues,
|
|
918
1272
|
showCounts,
|
|
919
1273
|
isFocused,
|
|
1274
|
+
onSelectRange: handleSelectRange,
|
|
920
1275
|
onDragStart: handleDragStart,
|
|
921
1276
|
onDragOver: handleDragOver,
|
|
922
1277
|
onDragEnd: handleDragEnd,
|
|
@@ -939,8 +1294,8 @@ function TreeView({
|
|
|
939
1294
|
}
|
|
940
1295
|
|
|
941
1296
|
// src/form-view.tsx
|
|
942
|
-
var
|
|
943
|
-
var
|
|
1297
|
+
var import_react8 = require("react");
|
|
1298
|
+
var import_core5 = require("@visual-json/core");
|
|
944
1299
|
|
|
945
1300
|
// src/breadcrumbs.tsx
|
|
946
1301
|
var import_react6 = require("react");
|
|
@@ -949,8 +1304,8 @@ var MAX_SUGGESTIONS = 20;
|
|
|
949
1304
|
var DROPDOWN_MAX_HEIGHT = 200;
|
|
950
1305
|
function Breadcrumbs({ className }) {
|
|
951
1306
|
const { state, actions } = useStudio();
|
|
952
|
-
const
|
|
953
|
-
const currentPath =
|
|
1307
|
+
const drillDownNode = state.drillDownNodeId ? state.tree.nodesById.get(state.drillDownNodeId) : null;
|
|
1308
|
+
const currentPath = drillDownNode?.path ?? "/";
|
|
954
1309
|
const [inputValue, setInputValue] = (0, import_react6.useState)(currentPath);
|
|
955
1310
|
const [open, setOpen] = (0, import_react6.useState)(false);
|
|
956
1311
|
const [highlightIndex, setHighlightIndex] = (0, import_react6.useState)(0);
|
|
@@ -980,7 +1335,7 @@ function Breadcrumbs({ className }) {
|
|
|
980
1335
|
(path) => {
|
|
981
1336
|
for (const [id, node] of state.tree.nodesById) {
|
|
982
1337
|
if (node.path === path) {
|
|
983
|
-
actions.
|
|
1338
|
+
actions.selectAndDrillDown(id);
|
|
984
1339
|
break;
|
|
985
1340
|
}
|
|
986
1341
|
}
|
|
@@ -1068,8 +1423,8 @@ function Breadcrumbs({ className }) {
|
|
|
1068
1423
|
style: {
|
|
1069
1424
|
width: "100%",
|
|
1070
1425
|
boxSizing: "border-box",
|
|
1071
|
-
padding: "
|
|
1072
|
-
fontSize:
|
|
1426
|
+
padding: "3px 0",
|
|
1427
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1073
1428
|
fontFamily: "var(--vj-font, monospace)",
|
|
1074
1429
|
color: "var(--vj-text-muted, #999999)",
|
|
1075
1430
|
background: "transparent",
|
|
@@ -1124,75 +1479,241 @@ function Breadcrumbs({ className }) {
|
|
|
1124
1479
|
);
|
|
1125
1480
|
}
|
|
1126
1481
|
|
|
1127
|
-
// src/
|
|
1482
|
+
// src/enum-input.tsx
|
|
1483
|
+
var import_react7 = require("react");
|
|
1128
1484
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
if (node.type === "boolean" || node.type === "null")
|
|
1137
|
-
return "var(--vj-boolean, #569cd6)";
|
|
1138
|
-
if (node.type === "number") return "var(--vj-number, #b5cea8)";
|
|
1139
|
-
return "var(--vj-string, #ce9178)";
|
|
1140
|
-
}
|
|
1141
|
-
function getDisplayValue(node) {
|
|
1142
|
-
if (node.type === "null") return "null";
|
|
1143
|
-
if (node.type === "boolean") return String(node.value);
|
|
1144
|
-
if (node.value === null || node.value === void 0) return "";
|
|
1145
|
-
return String(node.value);
|
|
1146
|
-
}
|
|
1147
|
-
function FormField({
|
|
1148
|
-
node,
|
|
1149
|
-
schema,
|
|
1150
|
-
rootSchema,
|
|
1151
|
-
depth,
|
|
1152
|
-
showDescriptions,
|
|
1153
|
-
showCounts,
|
|
1154
|
-
formSelectedNodeId,
|
|
1155
|
-
editingNodeId,
|
|
1156
|
-
collapsedIds,
|
|
1157
|
-
maxKeyLength,
|
|
1158
|
-
maxDepth,
|
|
1159
|
-
isFocused,
|
|
1160
|
-
dragState,
|
|
1161
|
-
onSelect,
|
|
1162
|
-
onToggleCollapse,
|
|
1163
|
-
onStartEditing,
|
|
1164
|
-
onDragStart,
|
|
1165
|
-
onDragOver,
|
|
1166
|
-
onDragEnd,
|
|
1167
|
-
onDrop
|
|
1485
|
+
var DROPDOWN_MAX_HEIGHT2 = 200;
|
|
1486
|
+
function EnumInput({
|
|
1487
|
+
enumValues,
|
|
1488
|
+
value,
|
|
1489
|
+
onValueChange,
|
|
1490
|
+
inputRef,
|
|
1491
|
+
inputStyle
|
|
1168
1492
|
}) {
|
|
1169
|
-
const
|
|
1170
|
-
const
|
|
1171
|
-
const
|
|
1172
|
-
const
|
|
1173
|
-
const
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1493
|
+
const [inputValue, setInputValue] = (0, import_react7.useState)(value);
|
|
1494
|
+
const [open, setOpen] = (0, import_react7.useState)(false);
|
|
1495
|
+
const [highlightIndex, setHighlightIndex] = (0, import_react7.useState)(0);
|
|
1496
|
+
const listRef = (0, import_react7.useRef)(null);
|
|
1497
|
+
const wrapperRef = (0, import_react7.useRef)(null);
|
|
1498
|
+
(0, import_react7.useEffect)(() => {
|
|
1499
|
+
setInputValue(value);
|
|
1500
|
+
}, [value]);
|
|
1501
|
+
const suggestions = (0, import_react7.useMemo)(
|
|
1502
|
+
() => enumValues.map((v) => String(v)),
|
|
1503
|
+
[enumValues]
|
|
1504
|
+
);
|
|
1505
|
+
(0, import_react7.useEffect)(() => {
|
|
1506
|
+
setHighlightIndex(0);
|
|
1507
|
+
}, [suggestions]);
|
|
1508
|
+
const selectValue = (0, import_react7.useCallback)(
|
|
1509
|
+
(val) => {
|
|
1510
|
+
onValueChange(val);
|
|
1511
|
+
setInputValue(val);
|
|
1512
|
+
setOpen(false);
|
|
1513
|
+
},
|
|
1514
|
+
[onValueChange]
|
|
1515
|
+
);
|
|
1516
|
+
const handleKeyDown = (0, import_react7.useCallback)(
|
|
1517
|
+
(e) => {
|
|
1518
|
+
if (!open) {
|
|
1519
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
1520
|
+
e.preventDefault();
|
|
1521
|
+
e.stopPropagation();
|
|
1522
|
+
setOpen(true);
|
|
1523
|
+
}
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
switch (e.key) {
|
|
1527
|
+
case "ArrowDown":
|
|
1528
|
+
e.preventDefault();
|
|
1529
|
+
e.stopPropagation();
|
|
1530
|
+
setHighlightIndex((i) => Math.min(i + 1, suggestions.length - 1));
|
|
1531
|
+
break;
|
|
1532
|
+
case "ArrowUp":
|
|
1533
|
+
e.preventDefault();
|
|
1534
|
+
e.stopPropagation();
|
|
1535
|
+
setHighlightIndex((i) => Math.max(i - 1, 0));
|
|
1536
|
+
break;
|
|
1537
|
+
case "Enter":
|
|
1538
|
+
e.preventDefault();
|
|
1539
|
+
e.stopPropagation();
|
|
1540
|
+
if (suggestions.length > 0 && highlightIndex < suggestions.length) {
|
|
1541
|
+
selectValue(suggestions[highlightIndex]);
|
|
1542
|
+
}
|
|
1543
|
+
break;
|
|
1544
|
+
case "Escape":
|
|
1545
|
+
e.preventDefault();
|
|
1546
|
+
e.stopPropagation();
|
|
1547
|
+
setInputValue(value);
|
|
1548
|
+
setOpen(false);
|
|
1549
|
+
break;
|
|
1550
|
+
case "Tab":
|
|
1551
|
+
setInputValue(value);
|
|
1552
|
+
setOpen(false);
|
|
1553
|
+
break;
|
|
1554
|
+
}
|
|
1555
|
+
},
|
|
1556
|
+
[open, suggestions, highlightIndex, value, selectValue]
|
|
1557
|
+
);
|
|
1558
|
+
(0, import_react7.useEffect)(() => {
|
|
1559
|
+
const el = listRef.current;
|
|
1560
|
+
if (!el || !open) return;
|
|
1561
|
+
const item = el.children[highlightIndex];
|
|
1562
|
+
item?.scrollIntoView({ block: "nearest" });
|
|
1563
|
+
}, [highlightIndex, open]);
|
|
1195
1564
|
(0, import_react7.useEffect)(() => {
|
|
1565
|
+
function handleClickOutside(e) {
|
|
1566
|
+
if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
|
|
1567
|
+
setInputValue(value);
|
|
1568
|
+
setOpen(false);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1572
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
1573
|
+
}, [value]);
|
|
1574
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1575
|
+
"div",
|
|
1576
|
+
{
|
|
1577
|
+
ref: wrapperRef,
|
|
1578
|
+
style: { position: "relative", flex: 1, minWidth: 0 },
|
|
1579
|
+
children: [
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1581
|
+
"input",
|
|
1582
|
+
{
|
|
1583
|
+
ref: inputRef,
|
|
1584
|
+
value: inputValue,
|
|
1585
|
+
onChange: (e) => {
|
|
1586
|
+
setInputValue(e.target.value);
|
|
1587
|
+
if (!open) setOpen(true);
|
|
1588
|
+
},
|
|
1589
|
+
onFocus: () => setOpen(true),
|
|
1590
|
+
onKeyDown: handleKeyDown,
|
|
1591
|
+
onClick: (e) => e.stopPropagation(),
|
|
1592
|
+
spellCheck: false,
|
|
1593
|
+
autoComplete: "off",
|
|
1594
|
+
style: inputStyle
|
|
1595
|
+
}
|
|
1596
|
+
),
|
|
1597
|
+
open && suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1598
|
+
"div",
|
|
1599
|
+
{
|
|
1600
|
+
ref: listRef,
|
|
1601
|
+
style: {
|
|
1602
|
+
position: "absolute",
|
|
1603
|
+
top: "calc(100% + 4px)",
|
|
1604
|
+
left: -32,
|
|
1605
|
+
right: 0,
|
|
1606
|
+
zIndex: 50,
|
|
1607
|
+
maxHeight: DROPDOWN_MAX_HEIGHT2,
|
|
1608
|
+
overflowY: "auto",
|
|
1609
|
+
backgroundColor: "var(--vj-bg-panel, #252526)",
|
|
1610
|
+
border: "1px solid var(--vj-border, #333333)",
|
|
1611
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.3)"
|
|
1612
|
+
},
|
|
1613
|
+
children: suggestions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1614
|
+
"div",
|
|
1615
|
+
{
|
|
1616
|
+
onMouseDown: (e) => {
|
|
1617
|
+
e.preventDefault();
|
|
1618
|
+
selectValue(s);
|
|
1619
|
+
},
|
|
1620
|
+
onMouseEnter: () => setHighlightIndex(i),
|
|
1621
|
+
style: {
|
|
1622
|
+
padding: "4px 12px",
|
|
1623
|
+
fontSize: 13,
|
|
1624
|
+
fontFamily: "var(--vj-font, monospace)",
|
|
1625
|
+
display: "flex",
|
|
1626
|
+
alignItems: "center",
|
|
1627
|
+
gap: 6,
|
|
1628
|
+
color: i === highlightIndex ? "var(--vj-text, #cccccc)" : "var(--vj-text-muted, #888888)",
|
|
1629
|
+
backgroundColor: i === highlightIndex ? "var(--vj-bg-hover, #2a2d2e)" : "transparent",
|
|
1630
|
+
cursor: "pointer",
|
|
1631
|
+
whiteSpace: "nowrap",
|
|
1632
|
+
overflow: "hidden",
|
|
1633
|
+
textOverflow: "ellipsis"
|
|
1634
|
+
},
|
|
1635
|
+
children: [
|
|
1636
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { width: 14, flexShrink: 0, fontSize: 12 }, children: s === value ? "\u2713" : "" }),
|
|
1637
|
+
s
|
|
1638
|
+
]
|
|
1639
|
+
},
|
|
1640
|
+
s
|
|
1641
|
+
))
|
|
1642
|
+
}
|
|
1643
|
+
)
|
|
1644
|
+
]
|
|
1645
|
+
}
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
// src/form-view.tsx
|
|
1650
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1651
|
+
function getResolvedSchema(schema, rootSchema, path) {
|
|
1652
|
+
if (!schema) return void 0;
|
|
1653
|
+
const raw = (0, import_core5.getPropertySchema)(schema, path, rootSchema);
|
|
1654
|
+
if (!raw) return void 0;
|
|
1655
|
+
return (0, import_core5.resolveRef)(raw, rootSchema ?? schema);
|
|
1656
|
+
}
|
|
1657
|
+
function getValueColor(node) {
|
|
1658
|
+
if (node.type === "boolean" || node.type === "null")
|
|
1659
|
+
return "var(--vj-boolean, #569cd6)";
|
|
1660
|
+
if (node.type === "number") return "var(--vj-number, #b5cea8)";
|
|
1661
|
+
return "var(--vj-string, #ce9178)";
|
|
1662
|
+
}
|
|
1663
|
+
function getDisplayValue(node) {
|
|
1664
|
+
if (node.type === "null") return "null";
|
|
1665
|
+
if (node.type === "boolean") return String(node.value);
|
|
1666
|
+
if (node.value === null || node.value === void 0) return "";
|
|
1667
|
+
return String(node.value);
|
|
1668
|
+
}
|
|
1669
|
+
function FormField({
|
|
1670
|
+
node,
|
|
1671
|
+
schema,
|
|
1672
|
+
rootSchema,
|
|
1673
|
+
depth,
|
|
1674
|
+
showDescriptions,
|
|
1675
|
+
showCounts,
|
|
1676
|
+
editingNodeId,
|
|
1677
|
+
collapsedIds,
|
|
1678
|
+
maxKeyLength,
|
|
1679
|
+
maxDepth,
|
|
1680
|
+
isFocused,
|
|
1681
|
+
dragState,
|
|
1682
|
+
onSelect,
|
|
1683
|
+
onToggleCollapse,
|
|
1684
|
+
onStartEditing,
|
|
1685
|
+
onDragStart,
|
|
1686
|
+
onDragOver,
|
|
1687
|
+
onDragEnd,
|
|
1688
|
+
onDrop
|
|
1689
|
+
}) {
|
|
1690
|
+
const { state, actions } = useStudio();
|
|
1691
|
+
const isContainer = node.type === "object" || node.type === "array";
|
|
1692
|
+
const collapsed = collapsedIds.has(node.id);
|
|
1693
|
+
const isSelected = state.selectedNodeIds.has(node.id);
|
|
1694
|
+
const isEditing = editingNodeId === node.id;
|
|
1695
|
+
const propSchema = getResolvedSchema(schema, rootSchema, node.path);
|
|
1696
|
+
const isRequired = checkRequired(node, schema, rootSchema);
|
|
1697
|
+
const [hovered, setHovered] = (0, import_react8.useState)(false);
|
|
1698
|
+
const isRoot = node.parentId === null;
|
|
1699
|
+
const isDragTarget = dragState.dropTargetNodeId === node.id;
|
|
1700
|
+
const isDraggedNode = dragState.draggedNodeIds.has(node.id);
|
|
1701
|
+
function handleDragOverEvent(e) {
|
|
1702
|
+
e.preventDefault();
|
|
1703
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
1704
|
+
const midY = rect.top + rect.height / 2;
|
|
1705
|
+
onDragOver(node.id, e.clientY < midY ? "before" : "after");
|
|
1706
|
+
}
|
|
1707
|
+
let borderTopColor = "transparent";
|
|
1708
|
+
let borderBottomColor = "transparent";
|
|
1709
|
+
if (isDragTarget && dragState.dropPosition === "before") {
|
|
1710
|
+
borderTopColor = "var(--vj-accent, #007acc)";
|
|
1711
|
+
} else if (isDragTarget && dragState.dropPosition === "after") {
|
|
1712
|
+
borderBottomColor = "var(--vj-accent, #007acc)";
|
|
1713
|
+
}
|
|
1714
|
+
const valueRef = (0, import_react8.useRef)(null);
|
|
1715
|
+
const keyRef = (0, import_react8.useRef)(null);
|
|
1716
|
+
(0, import_react8.useEffect)(() => {
|
|
1196
1717
|
if (!isEditing) return;
|
|
1197
1718
|
if (!isContainer) {
|
|
1198
1719
|
const hasValue = node.value !== null && node.value !== void 0 && node.value !== "";
|
|
@@ -1205,7 +1726,7 @@ function FormField({
|
|
|
1205
1726
|
keyRef.current.focus();
|
|
1206
1727
|
}
|
|
1207
1728
|
}, [isEditing, isContainer, node.value]);
|
|
1208
|
-
const handleValueChange = (0,
|
|
1729
|
+
const handleValueChange = (0, import_react8.useCallback)(
|
|
1209
1730
|
(newValue) => {
|
|
1210
1731
|
let parsed;
|
|
1211
1732
|
if (propSchema?.type === "boolean" || newValue === "true" || newValue === "false") {
|
|
@@ -1218,43 +1739,45 @@ function FormField({
|
|
|
1218
1739
|
} else {
|
|
1219
1740
|
parsed = newValue;
|
|
1220
1741
|
}
|
|
1221
|
-
const newTree = (0,
|
|
1742
|
+
const newTree = (0, import_core5.setValue)(state.tree, node.id, parsed);
|
|
1222
1743
|
actions.setTree(newTree);
|
|
1223
1744
|
},
|
|
1224
1745
|
[state.tree, node.id, node.type, actions, propSchema]
|
|
1225
1746
|
);
|
|
1226
|
-
const handleKeyChange = (0,
|
|
1747
|
+
const handleKeyChange = (0, import_react8.useCallback)(
|
|
1227
1748
|
(newKey) => {
|
|
1228
|
-
const newTree = (0,
|
|
1749
|
+
const newTree = (0, import_core5.setKey)(state.tree, node.id, newKey);
|
|
1229
1750
|
actions.setTree(newTree);
|
|
1230
1751
|
},
|
|
1231
1752
|
[state.tree, node.id, actions]
|
|
1232
1753
|
);
|
|
1233
|
-
const handleRemove = (0,
|
|
1234
|
-
const newTree = (0,
|
|
1754
|
+
const handleRemove = (0, import_react8.useCallback)(() => {
|
|
1755
|
+
const newTree = (0, import_core5.removeNode)(state.tree, node.id);
|
|
1235
1756
|
actions.setTree(newTree);
|
|
1236
1757
|
}, [state.tree, node.id, actions]);
|
|
1237
|
-
const handleAddChild = (0,
|
|
1758
|
+
const handleAddChild = (0, import_react8.useCallback)(() => {
|
|
1238
1759
|
const key = node.type === "array" ? String(node.children.length) : `newKey${node.children.length}`;
|
|
1239
|
-
const newTree = (0,
|
|
1760
|
+
const newTree = (0, import_core5.addProperty)(state.tree, node.id, key, "");
|
|
1240
1761
|
actions.setTree(newTree);
|
|
1241
1762
|
}, [state.tree, node.id, node.type, node.children.length, actions]);
|
|
1242
1763
|
const description = propSchema?.description;
|
|
1243
|
-
const defaultVal = propSchema?.default;
|
|
1244
1764
|
const isDeprecated = propSchema?.deprecated;
|
|
1245
1765
|
const fieldTitle = propSchema?.title;
|
|
1246
1766
|
const parentIsObject = node.parentId && state.tree.nodesById.get(node.parentId)?.type === "object";
|
|
1247
1767
|
const rowBg = isSelected ? isFocused ? "var(--vj-bg-selected, #2a5a1e)" : "var(--vj-bg-selected-muted, var(--vj-bg-hover, #2a2d2e))" : hovered ? "var(--vj-bg-hover, #2a2d2e)" : "transparent";
|
|
1248
1768
|
const rowColor = isSelected && isFocused ? "var(--vj-text-selected, var(--vj-text, #cccccc))" : "var(--vj-text, #cccccc)";
|
|
1249
1769
|
if (isContainer) {
|
|
1250
|
-
return /* @__PURE__ */ (0,
|
|
1251
|
-
/* @__PURE__ */ (0,
|
|
1770
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1771
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1252
1772
|
"div",
|
|
1253
1773
|
{
|
|
1254
1774
|
"data-form-node-id": node.id,
|
|
1255
1775
|
draggable: !isRoot,
|
|
1256
1776
|
onDragStart: (e) => {
|
|
1257
1777
|
e.dataTransfer.effectAllowed = "move";
|
|
1778
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
1779
|
+
setMultiDragImage(e, state.selectedNodeIds.size);
|
|
1780
|
+
}
|
|
1258
1781
|
onDragStart(node.id);
|
|
1259
1782
|
},
|
|
1260
1783
|
onDragOver: handleDragOverEvent,
|
|
@@ -1267,26 +1790,27 @@ function FormField({
|
|
|
1267
1790
|
display: "flex",
|
|
1268
1791
|
alignItems: "center",
|
|
1269
1792
|
gap: 6,
|
|
1270
|
-
padding: "
|
|
1793
|
+
padding: "1px 8px",
|
|
1271
1794
|
paddingLeft: 8 + depth * 16,
|
|
1272
1795
|
cursor: "pointer",
|
|
1273
1796
|
backgroundColor: rowBg,
|
|
1274
1797
|
color: rowColor,
|
|
1275
1798
|
height: 28,
|
|
1799
|
+
boxSizing: "border-box",
|
|
1276
1800
|
userSelect: "none",
|
|
1277
1801
|
opacity: isDeprecated ? 0.5 : isDraggedNode ? 0.4 : 1,
|
|
1278
|
-
borderTop
|
|
1279
|
-
borderBottom
|
|
1802
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
1803
|
+
borderBottom: `2px solid ${borderBottomColor}`
|
|
1280
1804
|
},
|
|
1281
1805
|
onClick: (e) => {
|
|
1282
1806
|
e.stopPropagation();
|
|
1283
|
-
onSelect(node.id);
|
|
1807
|
+
onSelect(node.id, e);
|
|
1284
1808
|
},
|
|
1285
1809
|
onDoubleClick: () => onToggleCollapse(node.id),
|
|
1286
1810
|
onMouseEnter: () => setHovered(true),
|
|
1287
1811
|
onMouseLeave: () => setHovered(false),
|
|
1288
1812
|
children: [
|
|
1289
|
-
/* @__PURE__ */ (0,
|
|
1813
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1290
1814
|
"button",
|
|
1291
1815
|
{
|
|
1292
1816
|
onClick: (e) => {
|
|
@@ -1311,7 +1835,7 @@ function FormField({
|
|
|
1311
1835
|
children: "\u25B6"
|
|
1312
1836
|
}
|
|
1313
1837
|
),
|
|
1314
|
-
isEditing && !isRoot ? /* @__PURE__ */ (0,
|
|
1838
|
+
isEditing && !isRoot ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1315
1839
|
"input",
|
|
1316
1840
|
{
|
|
1317
1841
|
ref: keyRef,
|
|
@@ -1323,7 +1847,7 @@ function FormField({
|
|
|
1323
1847
|
border: "none",
|
|
1324
1848
|
color: "inherit",
|
|
1325
1849
|
fontFamily: "var(--vj-font, monospace)",
|
|
1326
|
-
fontSize:
|
|
1850
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1327
1851
|
fontWeight: 500,
|
|
1328
1852
|
padding: 0,
|
|
1329
1853
|
outline: "none",
|
|
@@ -1331,12 +1855,12 @@ function FormField({
|
|
|
1331
1855
|
width: `calc(${(maxDepth - depth) * 16}px + ${maxKeyLength}ch)`
|
|
1332
1856
|
}
|
|
1333
1857
|
}
|
|
1334
|
-
) : /* @__PURE__ */ (0,
|
|
1858
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1335
1859
|
"span",
|
|
1336
1860
|
{
|
|
1337
1861
|
style: {
|
|
1338
1862
|
color: !isRoot && !parentIsObject && !isSelected ? "var(--vj-text-muted, #888888)" : "inherit",
|
|
1339
|
-
fontSize:
|
|
1863
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1340
1864
|
fontFamily: "var(--vj-font, monospace)",
|
|
1341
1865
|
fontWeight: 500,
|
|
1342
1866
|
flexShrink: 0,
|
|
@@ -1346,7 +1870,7 @@ function FormField({
|
|
|
1346
1870
|
children: isRoot ? "/" : getDisplayKey(node, state.tree)
|
|
1347
1871
|
}
|
|
1348
1872
|
),
|
|
1349
|
-
showDescriptions && fieldTitle && !isSelected && /* @__PURE__ */ (0,
|
|
1873
|
+
showDescriptions && fieldTitle && !isSelected && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1350
1874
|
"span",
|
|
1351
1875
|
{
|
|
1352
1876
|
style: {
|
|
@@ -1357,7 +1881,7 @@ function FormField({
|
|
|
1357
1881
|
children: fieldTitle
|
|
1358
1882
|
}
|
|
1359
1883
|
),
|
|
1360
|
-
hovered && /* @__PURE__ */ (0,
|
|
1884
|
+
hovered && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1361
1885
|
"button",
|
|
1362
1886
|
{
|
|
1363
1887
|
onClick: (e) => {
|
|
@@ -1379,7 +1903,7 @@ function FormField({
|
|
|
1379
1903
|
]
|
|
1380
1904
|
}
|
|
1381
1905
|
),
|
|
1382
|
-
showCounts && /* @__PURE__ */ (0,
|
|
1906
|
+
showCounts && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1383
1907
|
"span",
|
|
1384
1908
|
{
|
|
1385
1909
|
style: {
|
|
@@ -1391,7 +1915,7 @@ function FormField({
|
|
|
1391
1915
|
children: node.type === "array" ? `${node.children.length} items` : `${node.children.length} properties`
|
|
1392
1916
|
}
|
|
1393
1917
|
),
|
|
1394
|
-
!isRoot && isEditing && /* @__PURE__ */ (0,
|
|
1918
|
+
!isRoot && isEditing && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1395
1919
|
"button",
|
|
1396
1920
|
{
|
|
1397
1921
|
onClick: (e) => {
|
|
@@ -1415,7 +1939,7 @@ function FormField({
|
|
|
1415
1939
|
]
|
|
1416
1940
|
}
|
|
1417
1941
|
),
|
|
1418
|
-
showDescriptions && description && /* @__PURE__ */ (0,
|
|
1942
|
+
showDescriptions && description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1419
1943
|
"div",
|
|
1420
1944
|
{
|
|
1421
1945
|
style: {
|
|
@@ -1428,7 +1952,7 @@ function FormField({
|
|
|
1428
1952
|
children: description
|
|
1429
1953
|
}
|
|
1430
1954
|
),
|
|
1431
|
-
!collapsed && /* @__PURE__ */ (0,
|
|
1955
|
+
!collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { children: node.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1432
1956
|
FormField,
|
|
1433
1957
|
{
|
|
1434
1958
|
node: child,
|
|
@@ -1437,7 +1961,6 @@ function FormField({
|
|
|
1437
1961
|
depth: depth + 1,
|
|
1438
1962
|
showDescriptions,
|
|
1439
1963
|
showCounts,
|
|
1440
|
-
formSelectedNodeId,
|
|
1441
1964
|
editingNodeId,
|
|
1442
1965
|
collapsedIds,
|
|
1443
1966
|
maxKeyLength,
|
|
@@ -1458,13 +1981,16 @@ function FormField({
|
|
|
1458
1981
|
}
|
|
1459
1982
|
const displayValue = getDisplayValue(node);
|
|
1460
1983
|
const valueColor = getValueColor(node);
|
|
1461
|
-
return /* @__PURE__ */ (0,
|
|
1984
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1462
1985
|
"div",
|
|
1463
1986
|
{
|
|
1464
1987
|
"data-form-node-id": node.id,
|
|
1465
1988
|
draggable: !isRoot,
|
|
1466
1989
|
onDragStart: (e) => {
|
|
1467
1990
|
e.dataTransfer.effectAllowed = "move";
|
|
1991
|
+
if (state.selectedNodeIds.size > 1 && state.selectedNodeIds.has(node.id)) {
|
|
1992
|
+
setMultiDragImage(e, state.selectedNodeIds.size);
|
|
1993
|
+
}
|
|
1468
1994
|
onDragStart(node.id);
|
|
1469
1995
|
},
|
|
1470
1996
|
onDragOver: handleDragOverEvent,
|
|
@@ -1477,27 +2003,28 @@ function FormField({
|
|
|
1477
2003
|
display: "flex",
|
|
1478
2004
|
alignItems: "center",
|
|
1479
2005
|
gap: 6,
|
|
1480
|
-
padding: "
|
|
2006
|
+
padding: "1px 8px",
|
|
1481
2007
|
paddingLeft: 8 + depth * 16,
|
|
1482
2008
|
cursor: "pointer",
|
|
1483
2009
|
backgroundColor: rowBg,
|
|
1484
2010
|
color: rowColor,
|
|
1485
2011
|
height: 28,
|
|
2012
|
+
boxSizing: "border-box",
|
|
1486
2013
|
userSelect: "none",
|
|
1487
2014
|
opacity: isDeprecated ? 0.5 : isDraggedNode ? 0.4 : 1,
|
|
1488
|
-
borderTop
|
|
1489
|
-
borderBottom
|
|
2015
|
+
borderTop: `2px solid ${borderTopColor}`,
|
|
2016
|
+
borderBottom: `2px solid ${borderBottomColor}`
|
|
1490
2017
|
},
|
|
1491
2018
|
onClick: (e) => {
|
|
1492
2019
|
e.stopPropagation();
|
|
1493
|
-
onSelect(node.id);
|
|
2020
|
+
onSelect(node.id, e);
|
|
1494
2021
|
},
|
|
1495
2022
|
onDoubleClick: () => onStartEditing(node.id),
|
|
1496
2023
|
onMouseEnter: () => setHovered(true),
|
|
1497
2024
|
onMouseLeave: () => setHovered(false),
|
|
1498
2025
|
children: [
|
|
1499
|
-
/* @__PURE__ */ (0,
|
|
1500
|
-
isEditing && parentIsObject ? /* @__PURE__ */ (0,
|
|
2026
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { width: 16, flexShrink: 0 } }),
|
|
2027
|
+
isEditing && parentIsObject ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1501
2028
|
"input",
|
|
1502
2029
|
{
|
|
1503
2030
|
ref: keyRef,
|
|
@@ -1515,19 +2042,19 @@ function FormField({
|
|
|
1515
2042
|
border: "none",
|
|
1516
2043
|
color: "inherit",
|
|
1517
2044
|
fontFamily: "var(--vj-font, monospace)",
|
|
1518
|
-
fontSize:
|
|
2045
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1519
2046
|
padding: 0,
|
|
1520
2047
|
flexShrink: 0,
|
|
1521
2048
|
outline: "none",
|
|
1522
2049
|
width: `calc(${(maxDepth - depth) * 16}px + ${maxKeyLength}ch)`
|
|
1523
2050
|
}
|
|
1524
2051
|
}
|
|
1525
|
-
) : /* @__PURE__ */ (0,
|
|
2052
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1526
2053
|
"span",
|
|
1527
2054
|
{
|
|
1528
2055
|
style: {
|
|
1529
2056
|
color: !parentIsObject && !isSelected ? "var(--vj-text-muted, #888888)" : "inherit",
|
|
1530
|
-
fontSize:
|
|
2057
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1531
2058
|
fontFamily: "var(--vj-font, monospace)",
|
|
1532
2059
|
flexShrink: 0,
|
|
1533
2060
|
display: "inline-block",
|
|
@@ -1536,7 +2063,7 @@ function FormField({
|
|
|
1536
2063
|
children: getDisplayKey(node, state.tree)
|
|
1537
2064
|
}
|
|
1538
2065
|
),
|
|
1539
|
-
isRequired && !isSelected && /* @__PURE__ */ (0,
|
|
2066
|
+
isRequired && !isSelected && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1540
2067
|
"span",
|
|
1541
2068
|
{
|
|
1542
2069
|
style: {
|
|
@@ -1547,7 +2074,7 @@ function FormField({
|
|
|
1547
2074
|
children: "*"
|
|
1548
2075
|
}
|
|
1549
2076
|
),
|
|
1550
|
-
isEditing ? /* @__PURE__ */ (0,
|
|
2077
|
+
isEditing ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1551
2078
|
"div",
|
|
1552
2079
|
{
|
|
1553
2080
|
style: {
|
|
@@ -1566,12 +2093,12 @@ function FormField({
|
|
|
1566
2093
|
valueColor
|
|
1567
2094
|
)
|
|
1568
2095
|
}
|
|
1569
|
-
) : /* @__PURE__ */ (0,
|
|
2096
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1570
2097
|
"span",
|
|
1571
2098
|
{
|
|
1572
2099
|
style: {
|
|
1573
2100
|
color: valueColor,
|
|
1574
|
-
fontSize:
|
|
2101
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1575
2102
|
fontFamily: "var(--vj-font, monospace)",
|
|
1576
2103
|
overflow: "hidden",
|
|
1577
2104
|
textOverflow: "ellipsis",
|
|
@@ -1581,7 +2108,7 @@ function FormField({
|
|
|
1581
2108
|
children: displayValue
|
|
1582
2109
|
}
|
|
1583
2110
|
),
|
|
1584
|
-
isEditing && /* @__PURE__ */ (0,
|
|
2111
|
+
isEditing && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1585
2112
|
"button",
|
|
1586
2113
|
{
|
|
1587
2114
|
onClick: (e) => {
|
|
@@ -1612,54 +2139,44 @@ function renderEditInput(node, propSchema, displayValue, handleValueChange, inpu
|
|
|
1612
2139
|
background: "none",
|
|
1613
2140
|
border: "none",
|
|
1614
2141
|
fontFamily: "var(--vj-font, monospace)",
|
|
1615
|
-
fontSize:
|
|
2142
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1616
2143
|
padding: 0,
|
|
1617
2144
|
flex: 1,
|
|
1618
2145
|
outline: "none",
|
|
1619
2146
|
color: valueColor
|
|
1620
2147
|
};
|
|
1621
2148
|
if (node.type === "boolean") {
|
|
1622
|
-
return /* @__PURE__ */ (0,
|
|
1623
|
-
|
|
2149
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2150
|
+
EnumInput,
|
|
1624
2151
|
{
|
|
1625
|
-
|
|
2152
|
+
enumValues: ["true", "false"],
|
|
1626
2153
|
value: String(node.value),
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
children: [
|
|
1631
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "true", children: "true" }),
|
|
1632
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "false", children: "false" })
|
|
1633
|
-
]
|
|
2154
|
+
onValueChange: handleValueChange,
|
|
2155
|
+
inputRef,
|
|
2156
|
+
inputStyle
|
|
1634
2157
|
}
|
|
1635
2158
|
);
|
|
1636
2159
|
}
|
|
1637
2160
|
if (hasEnumValues && propSchema?.enum) {
|
|
1638
|
-
return /* @__PURE__ */ (0,
|
|
1639
|
-
|
|
2161
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2162
|
+
EnumInput,
|
|
1640
2163
|
{
|
|
1641
|
-
|
|
2164
|
+
enumValues: propSchema.enum,
|
|
1642
2165
|
value: displayValue,
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
children: [
|
|
1647
|
-
!propSchema.enum.some(
|
|
1648
|
-
(v) => JSON.stringify(v) === JSON.stringify(node.value)
|
|
1649
|
-
) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: displayValue, children: displayValue || "(empty)" }),
|
|
1650
|
-
propSchema.enum.map((v, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: String(v), children: String(v) }, i))
|
|
1651
|
-
]
|
|
2166
|
+
onValueChange: handleValueChange,
|
|
2167
|
+
inputRef,
|
|
2168
|
+
inputStyle
|
|
1652
2169
|
}
|
|
1653
2170
|
);
|
|
1654
2171
|
}
|
|
1655
2172
|
if (node.type === "null") {
|
|
1656
|
-
return /* @__PURE__ */ (0,
|
|
2173
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1657
2174
|
"span",
|
|
1658
2175
|
{
|
|
1659
2176
|
style: {
|
|
1660
2177
|
color: "var(--vj-boolean, #569cd6)",
|
|
1661
2178
|
fontFamily: "var(--vj-font, monospace)",
|
|
1662
|
-
fontSize:
|
|
2179
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
1663
2180
|
fontStyle: "italic",
|
|
1664
2181
|
flex: 1
|
|
1665
2182
|
},
|
|
@@ -1667,7 +2184,7 @@ function renderEditInput(node, propSchema, displayValue, handleValueChange, inpu
|
|
|
1667
2184
|
}
|
|
1668
2185
|
);
|
|
1669
2186
|
}
|
|
1670
|
-
return /* @__PURE__ */ (0,
|
|
2187
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1671
2188
|
"input",
|
|
1672
2189
|
{
|
|
1673
2190
|
ref: inputRef,
|
|
@@ -1692,35 +2209,35 @@ function FormView({
|
|
|
1692
2209
|
}) {
|
|
1693
2210
|
const { state, actions } = useStudio();
|
|
1694
2211
|
const rootSchema = state.schema ?? void 0;
|
|
1695
|
-
const
|
|
1696
|
-
const displayNode =
|
|
1697
|
-
const [
|
|
1698
|
-
|
|
1699
|
-
)
|
|
1700
|
-
const [editingNodeId, setEditingNodeId] = (0, import_react7.useState)(null);
|
|
1701
|
-
const preEditTreeRef = (0, import_react7.useRef)(null);
|
|
1702
|
-
const [collapsedIds, setCollapsedIds] = (0, import_react7.useState)(
|
|
2212
|
+
const drillDownNode = state.drillDownNodeId ? state.tree.nodesById.get(state.drillDownNodeId) : null;
|
|
2213
|
+
const displayNode = drillDownNode ?? state.tree.root;
|
|
2214
|
+
const [editingNodeId, setEditingNodeId] = (0, import_react8.useState)(null);
|
|
2215
|
+
const preEditTreeRef = (0, import_react8.useRef)(null);
|
|
2216
|
+
const [collapsedIds, setCollapsedIds] = (0, import_react8.useState)(
|
|
1703
2217
|
() => /* @__PURE__ */ new Set()
|
|
1704
2218
|
);
|
|
1705
|
-
const containerRef = (0,
|
|
1706
|
-
const [isFocused, setIsFocused] = (0,
|
|
1707
|
-
|
|
1708
|
-
dragState,
|
|
1709
|
-
handleDragStart,
|
|
1710
|
-
handleDragOver,
|
|
1711
|
-
handleDragEnd,
|
|
1712
|
-
handleDrop
|
|
1713
|
-
} = useDragDrop();
|
|
1714
|
-
(0, import_react7.useEffect)(() => {
|
|
1715
|
-
setFormSelectedNodeId(null);
|
|
2219
|
+
const containerRef = (0, import_react8.useRef)(null);
|
|
2220
|
+
const [isFocused, setIsFocused] = (0, import_react8.useState)(false);
|
|
2221
|
+
(0, import_react8.useEffect)(() => {
|
|
1716
2222
|
setEditingNodeId(null);
|
|
1717
2223
|
setCollapsedIds(/* @__PURE__ */ new Set());
|
|
1718
2224
|
}, [displayNode.id]);
|
|
1719
|
-
const visibleNodes = (0,
|
|
2225
|
+
const visibleNodes = (0, import_react8.useMemo)(
|
|
1720
2226
|
() => getVisibleNodes(displayNode, (id) => !collapsedIds.has(id)),
|
|
1721
2227
|
[displayNode, collapsedIds]
|
|
1722
2228
|
);
|
|
1723
|
-
const {
|
|
2229
|
+
const {
|
|
2230
|
+
dragState,
|
|
2231
|
+
handleDragStart,
|
|
2232
|
+
handleDragOver,
|
|
2233
|
+
handleDragEnd,
|
|
2234
|
+
handleDrop
|
|
2235
|
+
} = useDragDrop(visibleNodes, state.selectedNodeIds);
|
|
2236
|
+
(0, import_react8.useEffect)(() => {
|
|
2237
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2238
|
+
return () => actions.setVisibleNodesOverride(null);
|
|
2239
|
+
}, [visibleNodes, actions]);
|
|
2240
|
+
const { maxKeyLength, maxDepth } = (0, import_react8.useMemo)(() => {
|
|
1724
2241
|
let maxKey = 1;
|
|
1725
2242
|
let maxD = 0;
|
|
1726
2243
|
const baseSegments = displayNode.path === "/" ? 0 : displayNode.path.split("/").filter(Boolean).length;
|
|
@@ -1733,11 +2250,21 @@ function FormView({
|
|
|
1733
2250
|
}
|
|
1734
2251
|
return { maxKeyLength: maxKey, maxDepth: maxD };
|
|
1735
2252
|
}, [visibleNodes, displayNode.path, state.tree]);
|
|
1736
|
-
const handleSelect = (0,
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
2253
|
+
const handleSelect = (0, import_react8.useCallback)(
|
|
2254
|
+
(nodeId, e) => {
|
|
2255
|
+
setEditingNodeId(null);
|
|
2256
|
+
if (e.shiftKey) {
|
|
2257
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2258
|
+
actions.selectNodeRange(nodeId);
|
|
2259
|
+
} else if (e.metaKey || e.ctrlKey) {
|
|
2260
|
+
actions.toggleNodeSelection(nodeId);
|
|
2261
|
+
} else {
|
|
2262
|
+
actions.selectNode(nodeId);
|
|
2263
|
+
}
|
|
2264
|
+
},
|
|
2265
|
+
[actions, visibleNodes]
|
|
2266
|
+
);
|
|
2267
|
+
const handleToggleCollapse = (0, import_react8.useCallback)((nodeId) => {
|
|
1741
2268
|
setCollapsedIds((prev) => {
|
|
1742
2269
|
const next = new Set(prev);
|
|
1743
2270
|
if (next.has(nodeId)) {
|
|
@@ -1748,14 +2275,14 @@ function FormView({
|
|
|
1748
2275
|
return next;
|
|
1749
2276
|
});
|
|
1750
2277
|
}, []);
|
|
1751
|
-
const handleStartEditing = (0,
|
|
2278
|
+
const handleStartEditing = (0, import_react8.useCallback)(
|
|
1752
2279
|
(nodeId) => {
|
|
1753
2280
|
preEditTreeRef.current = state.tree;
|
|
1754
2281
|
setEditingNodeId(nodeId);
|
|
1755
2282
|
},
|
|
1756
2283
|
[state.tree]
|
|
1757
2284
|
);
|
|
1758
|
-
const scrollToNode = (0,
|
|
2285
|
+
const scrollToNode = (0, import_react8.useCallback)((nodeId) => {
|
|
1759
2286
|
requestAnimationFrame(() => {
|
|
1760
2287
|
const el = containerRef.current?.querySelector(
|
|
1761
2288
|
`[data-form-node-id="${nodeId}"]`
|
|
@@ -1763,7 +2290,7 @@ function FormView({
|
|
|
1763
2290
|
el?.scrollIntoView({ block: "nearest" });
|
|
1764
2291
|
});
|
|
1765
2292
|
}, []);
|
|
1766
|
-
const handleKeyDown = (0,
|
|
2293
|
+
const handleKeyDown = (0, import_react8.useCallback)(
|
|
1767
2294
|
(e) => {
|
|
1768
2295
|
if (editingNodeId) {
|
|
1769
2296
|
if (e.key === "Escape") {
|
|
@@ -1784,15 +2311,23 @@ function FormView({
|
|
|
1784
2311
|
}
|
|
1785
2312
|
return;
|
|
1786
2313
|
}
|
|
1787
|
-
|
|
1788
|
-
(n) => n.id ===
|
|
2314
|
+
let currentIndex = visibleNodes.findIndex(
|
|
2315
|
+
(n) => n.id === state.focusedNodeId
|
|
1789
2316
|
);
|
|
2317
|
+
if (currentIndex === -1 && visibleNodes.length > 0) {
|
|
2318
|
+
currentIndex = 0;
|
|
2319
|
+
}
|
|
1790
2320
|
switch (e.key) {
|
|
1791
2321
|
case "ArrowDown": {
|
|
1792
2322
|
e.preventDefault();
|
|
1793
2323
|
const next = visibleNodes[currentIndex + 1];
|
|
1794
2324
|
if (next) {
|
|
1795
|
-
|
|
2325
|
+
if (e.shiftKey) {
|
|
2326
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2327
|
+
actions.selectNodeRange(next.id);
|
|
2328
|
+
} else {
|
|
2329
|
+
actions.selectNode(next.id);
|
|
2330
|
+
}
|
|
1796
2331
|
scrollToNode(next.id);
|
|
1797
2332
|
}
|
|
1798
2333
|
break;
|
|
@@ -1801,7 +2336,12 @@ function FormView({
|
|
|
1801
2336
|
e.preventDefault();
|
|
1802
2337
|
const prev = visibleNodes[currentIndex - 1];
|
|
1803
2338
|
if (prev) {
|
|
1804
|
-
|
|
2339
|
+
if (e.shiftKey) {
|
|
2340
|
+
actions.setVisibleNodesOverride(visibleNodes);
|
|
2341
|
+
actions.selectNodeRange(prev.id);
|
|
2342
|
+
} else {
|
|
2343
|
+
actions.selectNode(prev.id);
|
|
2344
|
+
}
|
|
1805
2345
|
scrollToNode(prev.id);
|
|
1806
2346
|
}
|
|
1807
2347
|
break;
|
|
@@ -1817,7 +2357,7 @@ function FormView({
|
|
|
1817
2357
|
return next;
|
|
1818
2358
|
});
|
|
1819
2359
|
} else if (node.children.length > 0) {
|
|
1820
|
-
|
|
2360
|
+
actions.selectNode(node.children[0].id);
|
|
1821
2361
|
scrollToNode(node.children[0].id);
|
|
1822
2362
|
}
|
|
1823
2363
|
}
|
|
@@ -1839,7 +2379,7 @@ function FormView({
|
|
|
1839
2379
|
(n) => n.id === current.parentId
|
|
1840
2380
|
);
|
|
1841
2381
|
if (parentInVisible) {
|
|
1842
|
-
|
|
2382
|
+
actions.selectNode(parentInVisible.id);
|
|
1843
2383
|
scrollToNode(parentInVisible.id);
|
|
1844
2384
|
}
|
|
1845
2385
|
}
|
|
@@ -1847,30 +2387,54 @@ function FormView({
|
|
|
1847
2387
|
}
|
|
1848
2388
|
case "Enter": {
|
|
1849
2389
|
e.preventDefault();
|
|
1850
|
-
if (
|
|
2390
|
+
if (state.focusedNodeId) {
|
|
1851
2391
|
preEditTreeRef.current = state.tree;
|
|
1852
|
-
|
|
2392
|
+
actions.selectNode(state.focusedNodeId);
|
|
2393
|
+
setEditingNodeId(state.focusedNodeId);
|
|
2394
|
+
}
|
|
2395
|
+
break;
|
|
2396
|
+
}
|
|
2397
|
+
case "a": {
|
|
2398
|
+
if (e.metaKey || e.ctrlKey) {
|
|
2399
|
+
e.preventDefault();
|
|
2400
|
+
const ids = computeSelectAllIds(
|
|
2401
|
+
state.tree,
|
|
2402
|
+
state.focusedNodeId,
|
|
2403
|
+
state.selectedNodeIds
|
|
2404
|
+
);
|
|
2405
|
+
if (ids) {
|
|
2406
|
+
actions.setSelection(
|
|
2407
|
+
state.focusedNodeId,
|
|
2408
|
+
ids,
|
|
2409
|
+
state.focusedNodeId
|
|
2410
|
+
);
|
|
2411
|
+
}
|
|
1853
2412
|
}
|
|
1854
2413
|
break;
|
|
1855
2414
|
}
|
|
1856
2415
|
case "Escape": {
|
|
1857
2416
|
e.preventDefault();
|
|
1858
|
-
|
|
2417
|
+
if (state.selectedNodeIds.size > 1 && state.focusedNodeId) {
|
|
2418
|
+
actions.selectNode(state.focusedNodeId);
|
|
2419
|
+
} else {
|
|
2420
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
2421
|
+
}
|
|
1859
2422
|
break;
|
|
1860
2423
|
}
|
|
1861
2424
|
case "Delete":
|
|
1862
2425
|
case "Backspace": {
|
|
1863
2426
|
e.preventDefault();
|
|
1864
|
-
const
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
2427
|
+
const { newTree, nextFocusId } = deleteSelectedNodes(
|
|
2428
|
+
state.tree,
|
|
2429
|
+
state.selectedNodeIds,
|
|
2430
|
+
visibleNodes
|
|
2431
|
+
);
|
|
2432
|
+
if (newTree === state.tree) break;
|
|
2433
|
+
actions.setTree(newTree);
|
|
2434
|
+
if (nextFocusId) {
|
|
2435
|
+
actions.selectNode(nextFocusId);
|
|
2436
|
+
} else {
|
|
2437
|
+
actions.setSelection(null, /* @__PURE__ */ new Set(), null);
|
|
1874
2438
|
}
|
|
1875
2439
|
break;
|
|
1876
2440
|
}
|
|
@@ -1878,7 +2442,8 @@ function FormView({
|
|
|
1878
2442
|
},
|
|
1879
2443
|
[
|
|
1880
2444
|
visibleNodes,
|
|
1881
|
-
|
|
2445
|
+
state.focusedNodeId,
|
|
2446
|
+
state.selectedNodeIds,
|
|
1882
2447
|
editingNodeId,
|
|
1883
2448
|
collapsedIds,
|
|
1884
2449
|
scrollToNode,
|
|
@@ -1886,7 +2451,7 @@ function FormView({
|
|
|
1886
2451
|
actions
|
|
1887
2452
|
]
|
|
1888
2453
|
);
|
|
1889
|
-
return /* @__PURE__ */ (0,
|
|
2454
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1890
2455
|
"div",
|
|
1891
2456
|
{
|
|
1892
2457
|
className,
|
|
@@ -1899,19 +2464,21 @@ function FormView({
|
|
|
1899
2464
|
flexDirection: "column"
|
|
1900
2465
|
},
|
|
1901
2466
|
children: [
|
|
1902
|
-
/* @__PURE__ */ (0,
|
|
2467
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1903
2468
|
"div",
|
|
1904
2469
|
{
|
|
1905
2470
|
style: {
|
|
2471
|
+
display: "flex",
|
|
2472
|
+
alignItems: "center",
|
|
1906
2473
|
padding: "4px 8px",
|
|
1907
2474
|
borderBottom: "1px solid var(--vj-border, #333333)",
|
|
1908
|
-
backgroundColor: "var(--vj-bg
|
|
2475
|
+
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
1909
2476
|
flexShrink: 0
|
|
1910
2477
|
},
|
|
1911
|
-
children: /* @__PURE__ */ (0,
|
|
2478
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Breadcrumbs, {})
|
|
1912
2479
|
}
|
|
1913
2480
|
),
|
|
1914
|
-
/* @__PURE__ */ (0,
|
|
2481
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1915
2482
|
"div",
|
|
1916
2483
|
{
|
|
1917
2484
|
ref: containerRef,
|
|
@@ -1929,7 +2496,7 @@ function FormView({
|
|
|
1929
2496
|
overflow: "auto",
|
|
1930
2497
|
outline: "none"
|
|
1931
2498
|
},
|
|
1932
|
-
children: /* @__PURE__ */ (0,
|
|
2499
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1933
2500
|
FormField,
|
|
1934
2501
|
{
|
|
1935
2502
|
node: displayNode,
|
|
@@ -1938,7 +2505,6 @@ function FormView({
|
|
|
1938
2505
|
depth: 0,
|
|
1939
2506
|
showDescriptions,
|
|
1940
2507
|
showCounts,
|
|
1941
|
-
formSelectedNodeId,
|
|
1942
2508
|
editingNodeId,
|
|
1943
2509
|
collapsedIds,
|
|
1944
2510
|
maxKeyLength,
|
|
@@ -1962,12 +2528,12 @@ function FormView({
|
|
|
1962
2528
|
}
|
|
1963
2529
|
|
|
1964
2530
|
// src/search-bar.tsx
|
|
1965
|
-
var
|
|
1966
|
-
var
|
|
2531
|
+
var import_react9 = require("react");
|
|
2532
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1967
2533
|
function SearchBar({ className }) {
|
|
1968
2534
|
const { state, actions } = useStudio();
|
|
1969
|
-
const inputRef = (0,
|
|
1970
|
-
const handleKeyDown = (0,
|
|
2535
|
+
const inputRef = (0, import_react9.useRef)(null);
|
|
2536
|
+
const handleKeyDown = (0, import_react9.useCallback)(
|
|
1971
2537
|
(e) => {
|
|
1972
2538
|
if (e.key === "Enter") {
|
|
1973
2539
|
e.preventDefault();
|
|
@@ -1985,7 +2551,7 @@ function SearchBar({ className }) {
|
|
|
1985
2551
|
},
|
|
1986
2552
|
[actions]
|
|
1987
2553
|
);
|
|
1988
|
-
(0,
|
|
2554
|
+
(0, import_react9.useEffect)(() => {
|
|
1989
2555
|
function handleGlobalKeyDown(e) {
|
|
1990
2556
|
const mod = e.metaKey || e.ctrlKey;
|
|
1991
2557
|
if (mod && e.key === "f") {
|
|
@@ -1999,7 +2565,7 @@ function SearchBar({ className }) {
|
|
|
1999
2565
|
}, []);
|
|
2000
2566
|
const matchCount = state.searchMatches.length;
|
|
2001
2567
|
const currentMatch = matchCount > 0 ? state.searchMatchIndex + 1 : 0;
|
|
2002
|
-
return /* @__PURE__ */ (0,
|
|
2568
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2003
2569
|
"div",
|
|
2004
2570
|
{
|
|
2005
2571
|
className,
|
|
@@ -2008,11 +2574,11 @@ function SearchBar({ className }) {
|
|
|
2008
2574
|
alignItems: "center",
|
|
2009
2575
|
gap: 6,
|
|
2010
2576
|
padding: "4px 8px",
|
|
2011
|
-
backgroundColor: "var(--vj-bg
|
|
2577
|
+
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2012
2578
|
borderBottom: "1px solid var(--vj-border, #333333)"
|
|
2013
2579
|
},
|
|
2014
2580
|
children: [
|
|
2015
|
-
/* @__PURE__ */ (0,
|
|
2581
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2016
2582
|
"input",
|
|
2017
2583
|
{
|
|
2018
2584
|
ref: inputRef,
|
|
@@ -2028,14 +2594,14 @@ function SearchBar({ className }) {
|
|
|
2028
2594
|
borderRadius: 3,
|
|
2029
2595
|
color: "var(--vj-text, #cccccc)",
|
|
2030
2596
|
fontFamily: "var(--vj-font, monospace)",
|
|
2031
|
-
fontSize:
|
|
2597
|
+
fontSize: "var(--vj-input-font-size, 13px)",
|
|
2032
2598
|
padding: "3px 8px",
|
|
2033
2599
|
outline: "none",
|
|
2034
2600
|
minWidth: 0
|
|
2035
2601
|
}
|
|
2036
2602
|
}
|
|
2037
2603
|
),
|
|
2038
|
-
/* @__PURE__ */ (0,
|
|
2604
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2039
2605
|
"div",
|
|
2040
2606
|
{
|
|
2041
2607
|
style: {
|
|
@@ -2045,8 +2611,8 @@ function SearchBar({ className }) {
|
|
|
2045
2611
|
flexShrink: 0,
|
|
2046
2612
|
height: 18
|
|
2047
2613
|
},
|
|
2048
|
-
children: state.searchQuery ? /* @__PURE__ */ (0,
|
|
2049
|
-
/* @__PURE__ */ (0,
|
|
2614
|
+
children: state.searchQuery ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2615
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2050
2616
|
"span",
|
|
2051
2617
|
{
|
|
2052
2618
|
style: {
|
|
@@ -2059,7 +2625,7 @@ function SearchBar({ className }) {
|
|
|
2059
2625
|
children: matchCount > 0 ? `${currentMatch}/${matchCount}` : "0/0"
|
|
2060
2626
|
}
|
|
2061
2627
|
),
|
|
2062
|
-
/* @__PURE__ */ (0,
|
|
2628
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2063
2629
|
"button",
|
|
2064
2630
|
{
|
|
2065
2631
|
onClick: actions.prevSearchMatch,
|
|
@@ -2083,7 +2649,7 @@ function SearchBar({ className }) {
|
|
|
2083
2649
|
children: "\u25B2"
|
|
2084
2650
|
}
|
|
2085
2651
|
),
|
|
2086
|
-
/* @__PURE__ */ (0,
|
|
2652
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2087
2653
|
"button",
|
|
2088
2654
|
{
|
|
2089
2655
|
onClick: actions.nextSearchMatch,
|
|
@@ -2107,7 +2673,7 @@ function SearchBar({ className }) {
|
|
|
2107
2673
|
children: "\u25BC"
|
|
2108
2674
|
}
|
|
2109
2675
|
),
|
|
2110
|
-
/* @__PURE__ */ (0,
|
|
2676
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2111
2677
|
"button",
|
|
2112
2678
|
{
|
|
2113
2679
|
onClick: () => actions.setSearchQuery(""),
|
|
@@ -2130,8 +2696,8 @@ function SearchBar({ className }) {
|
|
|
2130
2696
|
children: "\xD7"
|
|
2131
2697
|
}
|
|
2132
2698
|
)
|
|
2133
|
-
] }) : /* @__PURE__ */ (0,
|
|
2134
|
-
/* @__PURE__ */ (0,
|
|
2699
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2700
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2135
2701
|
"button",
|
|
2136
2702
|
{
|
|
2137
2703
|
onClick: actions.expandAll,
|
|
@@ -2148,7 +2714,7 @@ function SearchBar({ className }) {
|
|
|
2148
2714
|
alignItems: "center"
|
|
2149
2715
|
},
|
|
2150
2716
|
title: "Expand all",
|
|
2151
|
-
children: /* @__PURE__ */ (0,
|
|
2717
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2152
2718
|
"svg",
|
|
2153
2719
|
{
|
|
2154
2720
|
width: "14",
|
|
@@ -2160,15 +2726,15 @@ function SearchBar({ className }) {
|
|
|
2160
2726
|
strokeLinecap: "round",
|
|
2161
2727
|
strokeLinejoin: "round",
|
|
2162
2728
|
children: [
|
|
2163
|
-
/* @__PURE__ */ (0,
|
|
2164
|
-
/* @__PURE__ */ (0,
|
|
2165
|
-
/* @__PURE__ */ (0,
|
|
2729
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M8 2v4M5 4l3-2 3 2" }),
|
|
2730
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M8 14v-4M5 12l3 2 3-2" }),
|
|
2731
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M2 8h12" })
|
|
2166
2732
|
]
|
|
2167
2733
|
}
|
|
2168
2734
|
)
|
|
2169
2735
|
}
|
|
2170
2736
|
),
|
|
2171
|
-
/* @__PURE__ */ (0,
|
|
2737
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2172
2738
|
"button",
|
|
2173
2739
|
{
|
|
2174
2740
|
onClick: actions.collapseAll,
|
|
@@ -2185,7 +2751,7 @@ function SearchBar({ className }) {
|
|
|
2185
2751
|
alignItems: "center"
|
|
2186
2752
|
},
|
|
2187
2753
|
title: "Collapse all",
|
|
2188
|
-
children: /* @__PURE__ */ (0,
|
|
2754
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2189
2755
|
"svg",
|
|
2190
2756
|
{
|
|
2191
2757
|
width: "14",
|
|
@@ -2197,9 +2763,9 @@ function SearchBar({ className }) {
|
|
|
2197
2763
|
strokeLinecap: "round",
|
|
2198
2764
|
strokeLinejoin: "round",
|
|
2199
2765
|
children: [
|
|
2200
|
-
/* @__PURE__ */ (0,
|
|
2201
|
-
/* @__PURE__ */ (0,
|
|
2202
|
-
/* @__PURE__ */ (0,
|
|
2766
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M8 5V1M5 3l3 2 3-2" }),
|
|
2767
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M8 11v4M5 13l3-2 3 2" }),
|
|
2768
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M2 8h12" })
|
|
2203
2769
|
]
|
|
2204
2770
|
}
|
|
2205
2771
|
)
|
|
@@ -2214,31 +2780,7 @@ function SearchBar({ className }) {
|
|
|
2214
2780
|
}
|
|
2215
2781
|
|
|
2216
2782
|
// src/json-editor.tsx
|
|
2217
|
-
var
|
|
2218
|
-
var DEFAULT_CSS_VARS = {
|
|
2219
|
-
"--vj-bg": "#1e1e1e",
|
|
2220
|
-
"--vj-bg-panel": "#252526",
|
|
2221
|
-
"--vj-bg-hover": "#2a2d2e",
|
|
2222
|
-
"--vj-bg-selected": "#2a5a1e",
|
|
2223
|
-
"--vj-bg-selected-muted": "#2a2d2e",
|
|
2224
|
-
"--vj-bg-match": "#3a3520",
|
|
2225
|
-
"--vj-bg-match-active": "#51502b",
|
|
2226
|
-
"--vj-border": "#333333",
|
|
2227
|
-
"--vj-border-subtle": "#2a2a2a",
|
|
2228
|
-
"--vj-text": "#cccccc",
|
|
2229
|
-
"--vj-text-muted": "#888888",
|
|
2230
|
-
"--vj-text-dim": "#666666",
|
|
2231
|
-
"--vj-text-dimmer": "#555555",
|
|
2232
|
-
"--vj-string": "#ce9178",
|
|
2233
|
-
"--vj-number": "#b5cea8",
|
|
2234
|
-
"--vj-boolean": "#569cd6",
|
|
2235
|
-
"--vj-accent": "#007acc",
|
|
2236
|
-
"--vj-accent-muted": "#094771",
|
|
2237
|
-
"--vj-input-bg": "#3c3c3c",
|
|
2238
|
-
"--vj-input-border": "#555555",
|
|
2239
|
-
"--vj-error": "#f48771",
|
|
2240
|
-
"--vj-font": "monospace"
|
|
2241
|
-
};
|
|
2783
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2242
2784
|
function JsonEditor({
|
|
2243
2785
|
value,
|
|
2244
2786
|
defaultValue,
|
|
@@ -2257,15 +2799,15 @@ function JsonEditor({
|
|
|
2257
2799
|
}) {
|
|
2258
2800
|
const isControlled = value !== void 0;
|
|
2259
2801
|
const initialValue = isControlled ? value : defaultValue ?? {};
|
|
2260
|
-
const [editorKey, setEditorKey] = (0,
|
|
2261
|
-
const valueRef = (0,
|
|
2262
|
-
(0,
|
|
2802
|
+
const [editorKey, setEditorKey] = (0, import_react10.useState)(0);
|
|
2803
|
+
const valueRef = (0, import_react10.useRef)(initialValue);
|
|
2804
|
+
(0, import_react10.useEffect)(() => {
|
|
2263
2805
|
if (isControlled && value !== valueRef.current) {
|
|
2264
2806
|
valueRef.current = value;
|
|
2265
2807
|
setEditorKey((k) => k + 1);
|
|
2266
2808
|
}
|
|
2267
2809
|
}, [value, isControlled]);
|
|
2268
|
-
const handleChange = (0,
|
|
2810
|
+
const handleChange = (0, import_react10.useCallback)(
|
|
2269
2811
|
(newValue) => {
|
|
2270
2812
|
valueRef.current = newValue;
|
|
2271
2813
|
if (!readOnly) {
|
|
@@ -2283,25 +2825,35 @@ function JsonEditor({
|
|
|
2283
2825
|
...DEFAULT_CSS_VARS,
|
|
2284
2826
|
...style
|
|
2285
2827
|
};
|
|
2286
|
-
return /* @__PURE__ */ (0,
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2293
|
-
EditorLayout,
|
|
2294
|
-
{
|
|
2295
|
-
treeShowValues,
|
|
2296
|
-
treeShowCounts,
|
|
2297
|
-
editorShowDescriptions,
|
|
2298
|
-
editorShowCounts,
|
|
2299
|
-
sidebarOpen
|
|
2828
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className, "data-vj-root": "", style: containerStyle, children: [
|
|
2829
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2830
|
+
"style",
|
|
2831
|
+
{
|
|
2832
|
+
dangerouslySetInnerHTML: {
|
|
2833
|
+
__html: `@media(pointer:coarse){[data-vj-root]{--vj-input-font-size:16px}}`
|
|
2300
2834
|
}
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2835
|
+
}
|
|
2836
|
+
),
|
|
2837
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2838
|
+
VisualJson,
|
|
2839
|
+
{
|
|
2840
|
+
value: valueRef.current,
|
|
2841
|
+
onChange: readOnly ? void 0 : handleChange,
|
|
2842
|
+
schema,
|
|
2843
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2844
|
+
EditorLayout,
|
|
2845
|
+
{
|
|
2846
|
+
treeShowValues,
|
|
2847
|
+
treeShowCounts,
|
|
2848
|
+
editorShowDescriptions,
|
|
2849
|
+
editorShowCounts,
|
|
2850
|
+
sidebarOpen
|
|
2851
|
+
}
|
|
2852
|
+
)
|
|
2853
|
+
},
|
|
2854
|
+
editorKey
|
|
2855
|
+
)
|
|
2856
|
+
] });
|
|
2305
2857
|
}
|
|
2306
2858
|
function EditorLayout({
|
|
2307
2859
|
treeShowValues,
|
|
@@ -2310,14 +2862,14 @@ function EditorLayout({
|
|
|
2310
2862
|
editorShowCounts,
|
|
2311
2863
|
sidebarOpen
|
|
2312
2864
|
}) {
|
|
2313
|
-
const [sidebarWidth, setSidebarWidth] = (0,
|
|
2314
|
-
const [isNarrow, setIsNarrow] = (0,
|
|
2315
|
-
const [activePanel, setActivePanel] = (0,
|
|
2316
|
-
const containerRef = (0,
|
|
2317
|
-
const dragging = (0,
|
|
2318
|
-
const startX = (0,
|
|
2319
|
-
const startWidth = (0,
|
|
2320
|
-
(0,
|
|
2865
|
+
const [sidebarWidth, setSidebarWidth] = (0, import_react10.useState)(280);
|
|
2866
|
+
const [isNarrow, setIsNarrow] = (0, import_react10.useState)(false);
|
|
2867
|
+
const [activePanel, setActivePanel] = (0, import_react10.useState)("tree");
|
|
2868
|
+
const containerRef = (0, import_react10.useRef)(null);
|
|
2869
|
+
const dragging = (0, import_react10.useRef)(false);
|
|
2870
|
+
const startX = (0, import_react10.useRef)(0);
|
|
2871
|
+
const startWidth = (0, import_react10.useRef)(0);
|
|
2872
|
+
(0, import_react10.useEffect)(() => {
|
|
2321
2873
|
function checkWidth() {
|
|
2322
2874
|
if (containerRef.current) {
|
|
2323
2875
|
setIsNarrow(containerRef.current.offsetWidth < 500);
|
|
@@ -2328,7 +2880,7 @@ function EditorLayout({
|
|
|
2328
2880
|
if (containerRef.current) observer.observe(containerRef.current);
|
|
2329
2881
|
return () => observer.disconnect();
|
|
2330
2882
|
}, []);
|
|
2331
|
-
const handleMouseDown = (0,
|
|
2883
|
+
const handleMouseDown = (0, import_react10.useCallback)(
|
|
2332
2884
|
(e) => {
|
|
2333
2885
|
dragging.current = true;
|
|
2334
2886
|
startX.current = e.clientX;
|
|
@@ -2358,7 +2910,7 @@ function EditorLayout({
|
|
|
2358
2910
|
);
|
|
2359
2911
|
if (isNarrow) {
|
|
2360
2912
|
if (!sidebarOpen) {
|
|
2361
|
-
return /* @__PURE__ */ (0,
|
|
2913
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2362
2914
|
"div",
|
|
2363
2915
|
{
|
|
2364
2916
|
ref: containerRef,
|
|
@@ -2368,7 +2920,7 @@ function EditorLayout({
|
|
|
2368
2920
|
flex: 1,
|
|
2369
2921
|
minHeight: 0
|
|
2370
2922
|
},
|
|
2371
|
-
children: /* @__PURE__ */ (0,
|
|
2923
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2372
2924
|
FormView,
|
|
2373
2925
|
{
|
|
2374
2926
|
showDescriptions: editorShowDescriptions,
|
|
@@ -2378,7 +2930,7 @@ function EditorLayout({
|
|
|
2378
2930
|
}
|
|
2379
2931
|
);
|
|
2380
2932
|
}
|
|
2381
|
-
return /* @__PURE__ */ (0,
|
|
2933
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2382
2934
|
"div",
|
|
2383
2935
|
{
|
|
2384
2936
|
ref: containerRef,
|
|
@@ -2389,7 +2941,7 @@ function EditorLayout({
|
|
|
2389
2941
|
minHeight: 0
|
|
2390
2942
|
},
|
|
2391
2943
|
children: [
|
|
2392
|
-
/* @__PURE__ */ (0,
|
|
2944
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2393
2945
|
"div",
|
|
2394
2946
|
{
|
|
2395
2947
|
style: {
|
|
@@ -2399,7 +2951,7 @@ function EditorLayout({
|
|
|
2399
2951
|
backgroundColor: "var(--vj-bg-panel, #252526)"
|
|
2400
2952
|
},
|
|
2401
2953
|
children: [
|
|
2402
|
-
/* @__PURE__ */ (0,
|
|
2954
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2403
2955
|
"button",
|
|
2404
2956
|
{
|
|
2405
2957
|
onClick: () => setActivePanel("tree"),
|
|
@@ -2416,7 +2968,7 @@ function EditorLayout({
|
|
|
2416
2968
|
children: "Tree"
|
|
2417
2969
|
}
|
|
2418
2970
|
),
|
|
2419
|
-
/* @__PURE__ */ (0,
|
|
2971
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2420
2972
|
"button",
|
|
2421
2973
|
{
|
|
2422
2974
|
onClick: () => setActivePanel("form"),
|
|
@@ -2436,7 +2988,7 @@ function EditorLayout({
|
|
|
2436
2988
|
]
|
|
2437
2989
|
}
|
|
2438
2990
|
),
|
|
2439
|
-
/* @__PURE__ */ (0,
|
|
2991
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { flex: 1, minHeight: 0, overflow: "hidden" }, children: activePanel === "tree" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2440
2992
|
"div",
|
|
2441
2993
|
{
|
|
2442
2994
|
style: {
|
|
@@ -2445,8 +2997,8 @@ function EditorLayout({
|
|
|
2445
2997
|
height: "100%"
|
|
2446
2998
|
},
|
|
2447
2999
|
children: [
|
|
2448
|
-
/* @__PURE__ */ (0,
|
|
2449
|
-
/* @__PURE__ */ (0,
|
|
3000
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SearchBar, {}),
|
|
3001
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2450
3002
|
TreeView,
|
|
2451
3003
|
{
|
|
2452
3004
|
showValues: treeShowValues,
|
|
@@ -2455,7 +3007,7 @@ function EditorLayout({
|
|
|
2455
3007
|
) })
|
|
2456
3008
|
]
|
|
2457
3009
|
}
|
|
2458
|
-
) : /* @__PURE__ */ (0,
|
|
3010
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2459
3011
|
"div",
|
|
2460
3012
|
{
|
|
2461
3013
|
style: {
|
|
@@ -2463,7 +3015,7 @@ function EditorLayout({
|
|
|
2463
3015
|
flexDirection: "column",
|
|
2464
3016
|
height: "100%"
|
|
2465
3017
|
},
|
|
2466
|
-
children: /* @__PURE__ */ (0,
|
|
3018
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2467
3019
|
FormView,
|
|
2468
3020
|
{
|
|
2469
3021
|
showDescriptions: editorShowDescriptions,
|
|
@@ -2476,8 +3028,8 @@ function EditorLayout({
|
|
|
2476
3028
|
}
|
|
2477
3029
|
);
|
|
2478
3030
|
}
|
|
2479
|
-
return /* @__PURE__ */ (0,
|
|
2480
|
-
/* @__PURE__ */ (0,
|
|
3031
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: containerRef, style: { display: "flex", flex: 1, minHeight: 0 }, children: [
|
|
3032
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2481
3033
|
"div",
|
|
2482
3034
|
{
|
|
2483
3035
|
style: {
|
|
@@ -2489,12 +3041,12 @@ function EditorLayout({
|
|
|
2489
3041
|
transition: "width 0.2s ease"
|
|
2490
3042
|
},
|
|
2491
3043
|
children: [
|
|
2492
|
-
/* @__PURE__ */ (0,
|
|
2493
|
-
/* @__PURE__ */ (0,
|
|
3044
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SearchBar, {}),
|
|
3045
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TreeView, { showValues: treeShowValues, showCounts: treeShowCounts }) })
|
|
2494
3046
|
]
|
|
2495
3047
|
}
|
|
2496
3048
|
),
|
|
2497
|
-
sidebarOpen && /* @__PURE__ */ (0,
|
|
3049
|
+
sidebarOpen && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2498
3050
|
"div",
|
|
2499
3051
|
{
|
|
2500
3052
|
style: {
|
|
@@ -2504,7 +3056,7 @@ function EditorLayout({
|
|
|
2504
3056
|
position: "relative",
|
|
2505
3057
|
transition: "background-color 0.15s"
|
|
2506
3058
|
},
|
|
2507
|
-
children: /* @__PURE__ */ (0,
|
|
3059
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2508
3060
|
"div",
|
|
2509
3061
|
{
|
|
2510
3062
|
onMouseDown: handleMouseDown,
|
|
@@ -2533,7 +3085,7 @@ function EditorLayout({
|
|
|
2533
3085
|
)
|
|
2534
3086
|
}
|
|
2535
3087
|
),
|
|
2536
|
-
/* @__PURE__ */ (0,
|
|
3088
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2537
3089
|
"div",
|
|
2538
3090
|
{
|
|
2539
3091
|
style: {
|
|
@@ -2543,7 +3095,7 @@ function EditorLayout({
|
|
|
2543
3095
|
minWidth: 0,
|
|
2544
3096
|
overflow: "hidden"
|
|
2545
3097
|
},
|
|
2546
|
-
children: /* @__PURE__ */ (0,
|
|
3098
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2547
3099
|
FormView,
|
|
2548
3100
|
{
|
|
2549
3101
|
showDescriptions: editorShowDescriptions,
|
|
@@ -2555,424 +3107,6 @@ function EditorLayout({
|
|
|
2555
3107
|
] });
|
|
2556
3108
|
}
|
|
2557
3109
|
|
|
2558
|
-
// src/property-editor.tsx
|
|
2559
|
-
var import_react10 = require("react");
|
|
2560
|
-
var import_core5 = require("@visual-json/core");
|
|
2561
|
-
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2562
|
-
var ALL_TYPES = [
|
|
2563
|
-
"string",
|
|
2564
|
-
"number",
|
|
2565
|
-
"boolean",
|
|
2566
|
-
"null",
|
|
2567
|
-
"object",
|
|
2568
|
-
"array"
|
|
2569
|
-
];
|
|
2570
|
-
function PropertyRow({ node, schemaProperty }) {
|
|
2571
|
-
const { state, actions } = useStudio();
|
|
2572
|
-
const isContainer = node.type === "object" || node.type === "array";
|
|
2573
|
-
const [hoveredRow, setHoveredRow] = (0, import_react10.useState)(false);
|
|
2574
|
-
function handleValueChange(newValue) {
|
|
2575
|
-
let parsed;
|
|
2576
|
-
if (newValue === "null") parsed = null;
|
|
2577
|
-
else if (newValue === "true") parsed = true;
|
|
2578
|
-
else if (newValue === "false") parsed = false;
|
|
2579
|
-
else if ((node.type === "number" || schemaProperty?.type === "number" || schemaProperty?.type === "integer") && !isNaN(Number(newValue)) && newValue.trim() !== "")
|
|
2580
|
-
parsed = Number(newValue);
|
|
2581
|
-
else parsed = newValue;
|
|
2582
|
-
const newTree = (0, import_core5.setValue)(state.tree, node.id, parsed);
|
|
2583
|
-
actions.setTree(newTree);
|
|
2584
|
-
}
|
|
2585
|
-
function handleKeyChange(newKey) {
|
|
2586
|
-
const newTree = (0, import_core5.setKey)(state.tree, node.id, newKey);
|
|
2587
|
-
actions.setTree(newTree);
|
|
2588
|
-
}
|
|
2589
|
-
function handleRemove() {
|
|
2590
|
-
const newTree = (0, import_core5.removeNode)(state.tree, node.id);
|
|
2591
|
-
actions.setTree(newTree);
|
|
2592
|
-
}
|
|
2593
|
-
function handleAddChild() {
|
|
2594
|
-
const key = node.type === "array" ? String(node.children.length) : `key${node.children.length}`;
|
|
2595
|
-
const newTree = (0, import_core5.addProperty)(state.tree, node.id, key, "");
|
|
2596
|
-
actions.setTree(newTree);
|
|
2597
|
-
}
|
|
2598
|
-
function displayValue() {
|
|
2599
|
-
if (isContainer) {
|
|
2600
|
-
return node.type === "array" ? `[${node.children.length} items]` : `{${node.children.length} keys}`;
|
|
2601
|
-
}
|
|
2602
|
-
if (node.value === null) return "";
|
|
2603
|
-
if (node.value === void 0) return "";
|
|
2604
|
-
if (typeof node.value === "string" && node.value === "") return "";
|
|
2605
|
-
return String(node.value);
|
|
2606
|
-
}
|
|
2607
|
-
const hasEnumValues = schemaProperty?.enum && schemaProperty.enum.length > 0;
|
|
2608
|
-
const description = schemaProperty?.description;
|
|
2609
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2610
|
-
"div",
|
|
2611
|
-
{
|
|
2612
|
-
onMouseEnter: () => setHoveredRow(true),
|
|
2613
|
-
onMouseLeave: () => setHoveredRow(false),
|
|
2614
|
-
children: [
|
|
2615
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2616
|
-
"div",
|
|
2617
|
-
{
|
|
2618
|
-
style: {
|
|
2619
|
-
display: "flex",
|
|
2620
|
-
alignItems: "center",
|
|
2621
|
-
gap: 8,
|
|
2622
|
-
padding: "4px 12px",
|
|
2623
|
-
borderBottom: "1px solid var(--vj-border-subtle, #2a2a2a)",
|
|
2624
|
-
minHeight: 32,
|
|
2625
|
-
backgroundColor: hoveredRow ? "var(--vj-bg-hover, #2a2d2e)" : "transparent"
|
|
2626
|
-
},
|
|
2627
|
-
children: [
|
|
2628
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2629
|
-
"input",
|
|
2630
|
-
{
|
|
2631
|
-
value: node.key,
|
|
2632
|
-
onChange: (e) => handleKeyChange(e.target.value),
|
|
2633
|
-
style: {
|
|
2634
|
-
background: "none",
|
|
2635
|
-
border: "1px solid transparent",
|
|
2636
|
-
borderRadius: 3,
|
|
2637
|
-
color: "var(--vj-text, #cccccc)",
|
|
2638
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2639
|
-
fontSize: 13,
|
|
2640
|
-
padding: "2px 6px",
|
|
2641
|
-
width: 140,
|
|
2642
|
-
flexShrink: 0
|
|
2643
|
-
}
|
|
2644
|
-
}
|
|
2645
|
-
),
|
|
2646
|
-
!isContainer ? hasEnumValues ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2647
|
-
"select",
|
|
2648
|
-
{
|
|
2649
|
-
value: displayValue(),
|
|
2650
|
-
onChange: (e) => handleValueChange(e.target.value),
|
|
2651
|
-
style: {
|
|
2652
|
-
background: "var(--vj-input-bg, #3c3c3c)",
|
|
2653
|
-
border: "1px solid var(--vj-input-border, #555555)",
|
|
2654
|
-
borderRadius: 3,
|
|
2655
|
-
color: node.type === "string" ? "var(--vj-string, #ce9178)" : "var(--vj-number, #b5cea8)",
|
|
2656
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2657
|
-
fontSize: 13,
|
|
2658
|
-
padding: "2px 6px",
|
|
2659
|
-
flex: 1,
|
|
2660
|
-
cursor: "pointer"
|
|
2661
|
-
},
|
|
2662
|
-
children: schemaProperty.enum.map((v, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: String(v), children: String(v) }, i))
|
|
2663
|
-
}
|
|
2664
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2665
|
-
"input",
|
|
2666
|
-
{
|
|
2667
|
-
value: displayValue(),
|
|
2668
|
-
onChange: (e) => handleValueChange(e.target.value),
|
|
2669
|
-
placeholder: "<value>",
|
|
2670
|
-
style: {
|
|
2671
|
-
background: "none",
|
|
2672
|
-
border: "1px solid transparent",
|
|
2673
|
-
borderRadius: 3,
|
|
2674
|
-
color: node.type === "string" ? "var(--vj-string, #ce9178)" : "var(--vj-number, #b5cea8)",
|
|
2675
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2676
|
-
fontSize: 13,
|
|
2677
|
-
padding: "2px 6px",
|
|
2678
|
-
flex: 1,
|
|
2679
|
-
textAlign: "right"
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2683
|
-
"span",
|
|
2684
|
-
{
|
|
2685
|
-
style: {
|
|
2686
|
-
color: "var(--vj-text-dim, #666666)",
|
|
2687
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2688
|
-
fontSize: 13,
|
|
2689
|
-
flex: 1,
|
|
2690
|
-
textAlign: "right"
|
|
2691
|
-
},
|
|
2692
|
-
children: displayValue()
|
|
2693
|
-
}
|
|
2694
|
-
),
|
|
2695
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2696
|
-
"div",
|
|
2697
|
-
{
|
|
2698
|
-
style: {
|
|
2699
|
-
display: "flex",
|
|
2700
|
-
gap: 2,
|
|
2701
|
-
opacity: hoveredRow ? 1 : 0,
|
|
2702
|
-
transition: "opacity 0.1s",
|
|
2703
|
-
flexShrink: 0
|
|
2704
|
-
},
|
|
2705
|
-
children: [
|
|
2706
|
-
isContainer && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2707
|
-
"button",
|
|
2708
|
-
{
|
|
2709
|
-
onClick: handleAddChild,
|
|
2710
|
-
style: {
|
|
2711
|
-
background: "none",
|
|
2712
|
-
border: "none",
|
|
2713
|
-
color: "var(--vj-text-muted, #888888)",
|
|
2714
|
-
cursor: "pointer",
|
|
2715
|
-
padding: "2px 4px",
|
|
2716
|
-
fontSize: 15,
|
|
2717
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2718
|
-
borderRadius: 3,
|
|
2719
|
-
lineHeight: 1
|
|
2720
|
-
},
|
|
2721
|
-
title: "Add child",
|
|
2722
|
-
children: "+"
|
|
2723
|
-
}
|
|
2724
|
-
),
|
|
2725
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2726
|
-
"button",
|
|
2727
|
-
{
|
|
2728
|
-
onClick: handleRemove,
|
|
2729
|
-
style: {
|
|
2730
|
-
background: "none",
|
|
2731
|
-
border: "none",
|
|
2732
|
-
color: "var(--vj-text-muted, #888888)",
|
|
2733
|
-
cursor: "pointer",
|
|
2734
|
-
padding: "2px 4px",
|
|
2735
|
-
fontSize: 15,
|
|
2736
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2737
|
-
borderRadius: 3,
|
|
2738
|
-
lineHeight: 1
|
|
2739
|
-
},
|
|
2740
|
-
title: "Remove",
|
|
2741
|
-
children: "\xD7"
|
|
2742
|
-
}
|
|
2743
|
-
)
|
|
2744
|
-
]
|
|
2745
|
-
}
|
|
2746
|
-
)
|
|
2747
|
-
]
|
|
2748
|
-
}
|
|
2749
|
-
),
|
|
2750
|
-
description && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2751
|
-
"div",
|
|
2752
|
-
{
|
|
2753
|
-
style: {
|
|
2754
|
-
padding: "2px 12px 4px 44px",
|
|
2755
|
-
fontSize: 11,
|
|
2756
|
-
color: "var(--vj-text-dim, #666666)",
|
|
2757
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2758
|
-
borderBottom: "1px solid var(--vj-border-subtle, #2a2a2a)"
|
|
2759
|
-
},
|
|
2760
|
-
children: description
|
|
2761
|
-
}
|
|
2762
|
-
)
|
|
2763
|
-
]
|
|
2764
|
-
}
|
|
2765
|
-
);
|
|
2766
|
-
}
|
|
2767
|
-
function PropertyEditor({ className }) {
|
|
2768
|
-
const { state, actions } = useStudio();
|
|
2769
|
-
const selectedNode = state.selectedNodeId ? state.tree.nodesById.get(state.selectedNodeId) : null;
|
|
2770
|
-
const handleChangeType = (0, import_react10.useCallback)(
|
|
2771
|
-
(newType) => {
|
|
2772
|
-
if (!selectedNode) return;
|
|
2773
|
-
const newTree = (0, import_core5.changeType)(state.tree, selectedNode.id, newType);
|
|
2774
|
-
actions.setTree(newTree);
|
|
2775
|
-
},
|
|
2776
|
-
[state.tree, selectedNode, actions]
|
|
2777
|
-
);
|
|
2778
|
-
const handleDuplicate = (0, import_react10.useCallback)(() => {
|
|
2779
|
-
if (!selectedNode) return;
|
|
2780
|
-
const newTree = (0, import_core5.duplicateNode)(state.tree, selectedNode.id);
|
|
2781
|
-
actions.setTree(newTree);
|
|
2782
|
-
}, [state.tree, selectedNode, actions]);
|
|
2783
|
-
const handleCopyPath = (0, import_react10.useCallback)(() => {
|
|
2784
|
-
if (!selectedNode) return;
|
|
2785
|
-
navigator.clipboard.writeText(selectedNode.path).catch(() => {
|
|
2786
|
-
});
|
|
2787
|
-
}, [selectedNode]);
|
|
2788
|
-
const handleCopyValue = (0, import_react10.useCallback)(() => {
|
|
2789
|
-
if (!selectedNode) return;
|
|
2790
|
-
const value = (0, import_core5.toJson)(selectedNode);
|
|
2791
|
-
const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
2792
|
-
navigator.clipboard.writeText(text).catch(() => {
|
|
2793
|
-
});
|
|
2794
|
-
}, [selectedNode]);
|
|
2795
|
-
if (!selectedNode) {
|
|
2796
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2797
|
-
"div",
|
|
2798
|
-
{
|
|
2799
|
-
className,
|
|
2800
|
-
style: {
|
|
2801
|
-
display: "flex",
|
|
2802
|
-
alignItems: "center",
|
|
2803
|
-
justifyContent: "center",
|
|
2804
|
-
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2805
|
-
color: "var(--vj-text-dimmer, #555555)",
|
|
2806
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2807
|
-
fontSize: 13,
|
|
2808
|
-
height: "100%"
|
|
2809
|
-
},
|
|
2810
|
-
children: "Select a node to edit"
|
|
2811
|
-
}
|
|
2812
|
-
);
|
|
2813
|
-
}
|
|
2814
|
-
const isContainer = selectedNode.type === "object" || selectedNode.type === "array";
|
|
2815
|
-
const schema = state.schema ?? void 0;
|
|
2816
|
-
const schemaTitle = schema?.title;
|
|
2817
|
-
function getChildSchema(childKey) {
|
|
2818
|
-
if (!schema || !selectedNode) return void 0;
|
|
2819
|
-
const childPath = selectedNode.path === "/" ? `/${childKey}` : `${selectedNode.path}/${childKey}`;
|
|
2820
|
-
const raw = (0, import_core5.getPropertySchema)(schema, childPath);
|
|
2821
|
-
return raw ? (0, import_core5.resolveRef)(raw, schema) : void 0;
|
|
2822
|
-
}
|
|
2823
|
-
function handleAdd() {
|
|
2824
|
-
if (!selectedNode) return;
|
|
2825
|
-
const key = selectedNode.type === "array" ? String(selectedNode.children.length) : `key${selectedNode.children.length}`;
|
|
2826
|
-
const newTree = (0, import_core5.addProperty)(state.tree, selectedNode.id, key, "");
|
|
2827
|
-
actions.setTree(newTree);
|
|
2828
|
-
}
|
|
2829
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2830
|
-
"div",
|
|
2831
|
-
{
|
|
2832
|
-
className,
|
|
2833
|
-
style: {
|
|
2834
|
-
backgroundColor: "var(--vj-bg, #1e1e1e)",
|
|
2835
|
-
color: "var(--vj-text, #cccccc)",
|
|
2836
|
-
overflow: "auto",
|
|
2837
|
-
height: "100%",
|
|
2838
|
-
display: "flex",
|
|
2839
|
-
flexDirection: "column"
|
|
2840
|
-
},
|
|
2841
|
-
children: [
|
|
2842
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2843
|
-
"div",
|
|
2844
|
-
{
|
|
2845
|
-
style: {
|
|
2846
|
-
display: "flex",
|
|
2847
|
-
alignItems: "center",
|
|
2848
|
-
justifyContent: "space-between",
|
|
2849
|
-
padding: "6px 12px",
|
|
2850
|
-
borderBottom: "1px solid var(--vj-border, #333333)",
|
|
2851
|
-
fontSize: 12,
|
|
2852
|
-
color: "var(--vj-text-muted, #999999)",
|
|
2853
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2854
|
-
flexShrink: 0,
|
|
2855
|
-
backgroundColor: "var(--vj-bg-panel, #252526)"
|
|
2856
|
-
},
|
|
2857
|
-
children: [
|
|
2858
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2859
|
-
"div",
|
|
2860
|
-
{
|
|
2861
|
-
style: {
|
|
2862
|
-
display: "flex",
|
|
2863
|
-
flexDirection: "column",
|
|
2864
|
-
gap: 2,
|
|
2865
|
-
flex: 1,
|
|
2866
|
-
minWidth: 0
|
|
2867
|
-
},
|
|
2868
|
-
children: [
|
|
2869
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Breadcrumbs, {}),
|
|
2870
|
-
schemaTitle && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2871
|
-
"span",
|
|
2872
|
-
{
|
|
2873
|
-
style: { fontSize: 10, color: "var(--vj-text-dim, #666666)" },
|
|
2874
|
-
children: schemaTitle
|
|
2875
|
-
}
|
|
2876
|
-
)
|
|
2877
|
-
]
|
|
2878
|
-
}
|
|
2879
|
-
),
|
|
2880
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2881
|
-
"div",
|
|
2882
|
-
{
|
|
2883
|
-
style: {
|
|
2884
|
-
display: "flex",
|
|
2885
|
-
alignItems: "center",
|
|
2886
|
-
gap: 4,
|
|
2887
|
-
flexShrink: 0
|
|
2888
|
-
},
|
|
2889
|
-
children: [
|
|
2890
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2891
|
-
"select",
|
|
2892
|
-
{
|
|
2893
|
-
value: selectedNode.type,
|
|
2894
|
-
onChange: (e) => handleChangeType(e.target.value),
|
|
2895
|
-
style: {
|
|
2896
|
-
background: "var(--vj-input-bg, #3c3c3c)",
|
|
2897
|
-
border: "1px solid var(--vj-input-border, #555555)",
|
|
2898
|
-
borderRadius: 3,
|
|
2899
|
-
color: "var(--vj-text, #cccccc)",
|
|
2900
|
-
fontSize: 11,
|
|
2901
|
-
fontFamily: "var(--vj-font, monospace)",
|
|
2902
|
-
padding: "1px 4px",
|
|
2903
|
-
cursor: "pointer"
|
|
2904
|
-
},
|
|
2905
|
-
title: "Change type",
|
|
2906
|
-
children: ALL_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: t, children: t }, t))
|
|
2907
|
-
}
|
|
2908
|
-
),
|
|
2909
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2910
|
-
"button",
|
|
2911
|
-
{
|
|
2912
|
-
onClick: handleCopyPath,
|
|
2913
|
-
style: actionButtonStyle,
|
|
2914
|
-
title: "Copy path",
|
|
2915
|
-
children: "path"
|
|
2916
|
-
}
|
|
2917
|
-
),
|
|
2918
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2919
|
-
"button",
|
|
2920
|
-
{
|
|
2921
|
-
onClick: handleCopyValue,
|
|
2922
|
-
style: actionButtonStyle,
|
|
2923
|
-
title: "Copy value",
|
|
2924
|
-
children: "value"
|
|
2925
|
-
}
|
|
2926
|
-
),
|
|
2927
|
-
selectedNode.parentId && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2928
|
-
"button",
|
|
2929
|
-
{
|
|
2930
|
-
onClick: handleDuplicate,
|
|
2931
|
-
style: actionButtonStyle,
|
|
2932
|
-
title: "Duplicate",
|
|
2933
|
-
children: "dup"
|
|
2934
|
-
}
|
|
2935
|
-
),
|
|
2936
|
-
isContainer && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2937
|
-
"button",
|
|
2938
|
-
{
|
|
2939
|
-
onClick: handleAdd,
|
|
2940
|
-
style: {
|
|
2941
|
-
...actionButtonStyle,
|
|
2942
|
-
border: "1px solid var(--vj-input-border, #555555)"
|
|
2943
|
-
},
|
|
2944
|
-
children: "+ Add"
|
|
2945
|
-
}
|
|
2946
|
-
)
|
|
2947
|
-
]
|
|
2948
|
-
}
|
|
2949
|
-
)
|
|
2950
|
-
]
|
|
2951
|
-
}
|
|
2952
|
-
),
|
|
2953
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: isContainer ? selectedNode.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2954
|
-
PropertyRow,
|
|
2955
|
-
{
|
|
2956
|
-
node: child,
|
|
2957
|
-
schemaProperty: getChildSchema(child.key)
|
|
2958
|
-
},
|
|
2959
|
-
child.id
|
|
2960
|
-
)) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PropertyRow, { node: selectedNode }) })
|
|
2961
|
-
]
|
|
2962
|
-
}
|
|
2963
|
-
);
|
|
2964
|
-
}
|
|
2965
|
-
var actionButtonStyle = {
|
|
2966
|
-
background: "none",
|
|
2967
|
-
border: "none",
|
|
2968
|
-
borderRadius: 3,
|
|
2969
|
-
color: "var(--vj-text-muted, #888888)",
|
|
2970
|
-
cursor: "pointer",
|
|
2971
|
-
padding: "1px 6px",
|
|
2972
|
-
fontSize: 11,
|
|
2973
|
-
fontFamily: "var(--vj-font, monospace)"
|
|
2974
|
-
};
|
|
2975
|
-
|
|
2976
3110
|
// src/diff-view.tsx
|
|
2977
3111
|
var import_react11 = require("react");
|
|
2978
3112
|
var import_core6 = require("@visual-json/core");
|
|
@@ -3133,7 +3267,6 @@ function DiffView({
|
|
|
3133
3267
|
DiffView,
|
|
3134
3268
|
FormView,
|
|
3135
3269
|
JsonEditor,
|
|
3136
|
-
PropertyEditor,
|
|
3137
3270
|
SearchBar,
|
|
3138
3271
|
StudioContext,
|
|
3139
3272
|
TreeView,
|