@jbpark/live-editor 4.1.1 → 4.3.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.ko.md +1 -1
- package/README.md +1 -1
- package/dist/{ast-jab6hQrT.js → ast-BFbDWO-l.js} +288 -229
- package/dist/ast-BFbDWO-l.js.map +1 -0
- package/dist/{boundary-D2IFja-3.js → boundary-B17cepvm.js} +12 -9
- package/dist/boundary-B17cepvm.js.map +1 -0
- package/dist/{compile-CBOAtQMz.js → compile-0Dvp8zp5.js} +37 -5
- package/dist/compile-0Dvp8zp5.js.map +1 -0
- package/dist/{constants-BD3FZKWE.js → constants-CKvdqZtO.js} +11 -4
- package/dist/constants-CKvdqZtO.js.map +1 -0
- package/dist/{context-DQ-UtoyS.js → context-93X3gyfJ.js} +2 -2
- package/dist/{context-DQ-UtoyS.js.map → context-93X3gyfJ.js.map} +1 -1
- package/dist/{core-DaXVtvH5.js → core-B3ruccKb.js} +2 -2
- package/dist/{core-DaXVtvH5.js.map → core-B3ruccKb.js.map} +1 -1
- package/dist/{detect-typescript-b0A751v2.js → detect-typescript-5MV4mCM7.js} +2 -2
- package/dist/{detect-typescript-b0A751v2.js.map → detect-typescript-5MV4mCM7.js.map} +1 -1
- package/dist/dnd/index.d.ts +2 -2
- package/dist/dnd/index.js +2 -2
- package/dist/{dnd-CMrXfD8O.js → dnd-Z1KS16Fy.js} +969 -322
- package/dist/dnd-Z1KS16Fy.js.map +1 -0
- package/dist/{document-J9Lo9p81.d.ts → document-Ct4jW78J.d.ts} +42 -22
- package/dist/{document-C5Dypv4_.js → document-cEEdFERz.js} +138 -46
- package/dist/document-cEEdFERz.js.map +1 -0
- package/dist/editor/index.js +2 -2
- package/dist/{editor-C3tQaMlU.js → editor-DvMruljw.js} +2 -2
- package/dist/{editor-C3tQaMlU.js.map → editor-DvMruljw.js.map} +1 -1
- package/dist/error/index.d.ts +1 -1
- package/dist/error/index.js +2 -2
- package/dist/{error-KW8XCcjH.js → error-DWhxQEom.js} +2 -2
- package/dist/{error-KW8XCcjH.js.map → error-DWhxQEom.js.map} +1 -1
- package/dist/{index-DNNj_0ba.d.ts → index-C4SGQ3Ci.d.ts} +4 -3
- package/dist/{index-C4t0OawE.d.ts → index-CtlUYnWq.d.ts} +131 -34
- package/dist/{index-B_p74BRM.d.ts → index-w3kLcCXl.d.ts} +29 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/preview/index.js +2 -2
- package/dist/{preview-BwEfltgi.js → preview-C_UzpGWh.js} +3 -3
- package/dist/{preview-BwEfltgi.js.map → preview-C_UzpGWh.js.map} +1 -1
- package/dist/provider/index.js +1 -1
- package/dist/sections-GJQ63uEB.js +24 -0
- package/dist/sections-GJQ63uEB.js.map +1 -0
- package/dist/{use-dynamic-tailwind-DuGLeDR3.js → use-dynamic-tailwind-DpbjmATF.js} +20 -11
- package/dist/use-dynamic-tailwind-DpbjmATF.js.map +1 -0
- package/dist/utils/ast/index.d.ts +3 -3
- package/dist/utils/ast/index.js +3 -3
- package/dist/utils/index.d.ts +7 -6
- package/dist/utils/index.js +5 -5
- package/package.json +1 -1
- package/dist/ast-jab6hQrT.js.map +0 -1
- package/dist/boundary-D2IFja-3.js.map +0 -1
- package/dist/compile-CBOAtQMz.js.map +0 -1
- package/dist/constants-BD3FZKWE.js.map +0 -1
- package/dist/dnd-CMrXfD8O.js.map +0 -1
- package/dist/document-C5Dypv4_.js.map +0 -1
- package/dist/sections-B7dWoNVJ.js +0 -19
- package/dist/sections-B7dWoNVJ.js.map +0 -1
- package/dist/use-dynamic-tailwind-DuGLeDR3.js.map +0 -1
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { S as require_lib, _ as nanoid, c as inspectDocument, h as replaceIds, m as fillIdsFrom, n as createSectionPreviewCache, r as fillSectionIds, w as __toESM } from "./document-cEEdFERz.js";
|
|
2
|
+
import { c as RESERVED_BINDING_PROPERTIES, o as DRAGGABLE_ITEMS, r as DATA_ATTR, t as BINDING_PROP } from "./constants-CKvdqZtO.js";
|
|
3
3
|
import { i as usePreview } from "./states-Ci1AvoQ9.js";
|
|
4
|
-
import {
|
|
4
|
+
import { A as parseValue, D as extractObjectProperties, E as extractNodeValue, _ as moveSelectedIndices, a as getChildrenSignatures, b as getStructuredValue, c as appendArrayItem, d as moveArrayItems, g as canStructurallyEditArray, h as updateArrayItemValue, i as updateAll, k as parseArrayExpression, l as duplicateArrayItems, m as updateArrayItemProperty, p as removeArrayItems, s as extract, t as validateBindingValue, u as moveArrayItem, v as findEditableChildren, w as canLosslesslyEvaluateSource, x as parseBinding, y as getCurrentValue } from "./ast-BFbDWO-l.js";
|
|
5
5
|
import { t as cn } from "./cn-CAnad2vL.js";
|
|
6
|
-
import { o as preloadScripts } from "./compile-
|
|
7
|
-
import { a as Frame, i as useStableModules, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-
|
|
8
|
-
import { t as Core } from "./core-
|
|
9
|
-
import {
|
|
10
|
-
import { n as Error$1, t as ErrorBoundary } from "./boundary-
|
|
6
|
+
import { o as preloadScripts } from "./compile-0Dvp8zp5.js";
|
|
7
|
+
import { a as Frame, i as useStableModules, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-DpbjmATF.js";
|
|
8
|
+
import { t as Core } from "./core-B3ruccKb.js";
|
|
9
|
+
import { a as replaceSections, n as extractSections, r as generateSection } from "./sections-GJQ63uEB.js";
|
|
10
|
+
import { n as Error$1, t as ErrorBoundary } from "./boundary-B17cepvm.js";
|
|
11
11
|
import { Children, createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
12
12
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
-
import { DndContext, DragOverlay, PointerSensor, closestCenter, useDndContext, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
|
|
14
|
-
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
|
|
15
|
-
import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
13
|
+
import { DndContext, DragOverlay, KeyboardSensor, MouseSensor, PointerSensor, TouchSensor, closestCenter, useDndContext, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
|
|
14
|
+
import { restrictToParentElement, restrictToVerticalAxis } from "@dnd-kit/modifiers";
|
|
15
|
+
import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
16
16
|
import { Button, Card, Checkbox, ColorPicker, DatePicker, Drawer, Input, RichTextEditor, Select, Space, Splitter, Toast, Typography } from "@jbpark/ui-kit";
|
|
17
17
|
import { useDebounce, useMultiSelect, useResponsiveSize } from "@jbpark/use-hooks";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import { createPortal } from "react-dom";
|
|
19
|
+
import { ArrowDown, ArrowUp, ChevronDown, ChevronRight, ChevronUp, Copy, Crosshair, GripVertical, LayoutGrid, Plus, Trash, X } from "lucide-react";
|
|
20
|
+
import { CSS as CSS$1 } from "@dnd-kit/utilities";
|
|
20
21
|
//#region src/components/dnd/droppable.tsx
|
|
21
22
|
var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
|
|
22
23
|
const Droppable = ({ children, className }) => {
|
|
@@ -46,6 +47,64 @@ const toastEditError = (error) => {
|
|
|
46
47
|
const DndEditOptionsContext = createContext({ reportError: toastEditError });
|
|
47
48
|
const useDndEditOptions = () => useContext(DndEditOptionsContext);
|
|
48
49
|
//#endregion
|
|
50
|
+
//#region src/components/dnd/inspector.ts
|
|
51
|
+
const DndInspectorContext = createContext(null);
|
|
52
|
+
const useDndInspector = () => {
|
|
53
|
+
const inspector = useContext(DndInspectorContext);
|
|
54
|
+
if (!inspector) throw new Error("useDndInspector() must be used inside <Live.Dnd>.");
|
|
55
|
+
return inspector;
|
|
56
|
+
};
|
|
57
|
+
const outside = (root) => (element) => element.getRootNode() !== root;
|
|
58
|
+
const deepestAt = (root, x, y, skip) => {
|
|
59
|
+
const element = root.elementsFromPoint(x, y).find((candidate) => !skip(candidate));
|
|
60
|
+
if (!element) return null;
|
|
61
|
+
if (element instanceof HTMLIFrameElement) {
|
|
62
|
+
const document = element.contentDocument;
|
|
63
|
+
if (!document) return element;
|
|
64
|
+
const rect = element.getBoundingClientRect();
|
|
65
|
+
return deepestAt(document, x - rect.left, y - rect.top, outside(document)) ?? element;
|
|
66
|
+
}
|
|
67
|
+
if (element.shadowRoot && element.shadowRoot !== root) return deepestAt(element.shadowRoot, x, y, outside(element.shadowRoot)) ?? element;
|
|
68
|
+
return element;
|
|
69
|
+
};
|
|
70
|
+
const hasId = (element) => Boolean(element.getAttribute(DATA_ATTR.ID));
|
|
71
|
+
const pickElement = (section, overlay, x, y) => {
|
|
72
|
+
let element = deepestAt(section.ownerDocument, x, y, (candidate) => candidate === overlay || candidate === section || !section.contains(candidate));
|
|
73
|
+
while (element && element !== section && !hasId(element)) element = element.parentElement ?? element.parentNode?.host ?? null;
|
|
74
|
+
return element === section ? null : element;
|
|
75
|
+
};
|
|
76
|
+
const viewportRect = (element) => {
|
|
77
|
+
const rect = element.getBoundingClientRect();
|
|
78
|
+
let x = rect.left;
|
|
79
|
+
let y = rect.top;
|
|
80
|
+
let frame = element.ownerDocument.defaultView?.frameElement ?? null;
|
|
81
|
+
while (frame) {
|
|
82
|
+
const frameRect = frame.getBoundingClientRect();
|
|
83
|
+
x += frameRect.left;
|
|
84
|
+
y += frameRect.top;
|
|
85
|
+
frame = frame.ownerDocument.defaultView?.frameElement ?? null;
|
|
86
|
+
}
|
|
87
|
+
return new DOMRect(x, y, rect.width, rect.height);
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/components/dnd/inspector-highlight.tsx
|
|
91
|
+
const InspectorHighlight = ({ rect }) => rect ? createPortal(/* @__PURE__ */ jsx("div", {
|
|
92
|
+
"aria-hidden": true,
|
|
93
|
+
"data-dnd-inspector-highlight": true,
|
|
94
|
+
style: {
|
|
95
|
+
position: "fixed",
|
|
96
|
+
left: rect.left,
|
|
97
|
+
top: rect.top,
|
|
98
|
+
width: rect.width,
|
|
99
|
+
height: rect.height,
|
|
100
|
+
pointerEvents: "none",
|
|
101
|
+
outline: "2px solid #3b82f6",
|
|
102
|
+
outlineOffset: -1,
|
|
103
|
+
background: "rgba(59, 130, 246, 0.12)",
|
|
104
|
+
zIndex: 2147483647
|
|
105
|
+
}
|
|
106
|
+
}), document.body) : null;
|
|
107
|
+
//#endregion
|
|
49
108
|
//#region src/components/dnd/draggable.tsx
|
|
50
109
|
const DraggableItem = ({ item, children }) => {
|
|
51
110
|
const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
|
|
@@ -70,9 +129,16 @@ const DefaultDraggableItem = ({ item, onAdd, tapToAdd = false }) => /* @__PURE__
|
|
|
70
129
|
ref,
|
|
71
130
|
style: { opacity: isDragging ? .5 : 1 },
|
|
72
131
|
...dragProps,
|
|
73
|
-
className: cn("cursor-grab", "outline-none", "hover:border-blue-300 hover:shadow-md", isDragging && "opacity-50"),
|
|
132
|
+
className: cn("cursor-grab", "outline-none focus-visible:ring-2 focus-visible:ring-blue-500", "hover:border-blue-300 hover:shadow-md", isDragging && "opacity-50"),
|
|
74
133
|
onClick: tapToAdd && onAdd ? () => onAdd(item) : void 0,
|
|
75
134
|
onDoubleClick: onAdd ? () => onAdd(item) : void 0,
|
|
135
|
+
onKeyDown: (event) => {
|
|
136
|
+
dragProps.onKeyDown?.(event);
|
|
137
|
+
if (onAdd && event.key === "Enter" && event.target === event.currentTarget && !event.defaultPrevented) {
|
|
138
|
+
event.preventDefault();
|
|
139
|
+
onAdd(item);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
76
142
|
children: item.name
|
|
77
143
|
})
|
|
78
144
|
});
|
|
@@ -91,9 +157,11 @@ const useDndLayout = () => useDndRegions("useDndLayout()");
|
|
|
91
157
|
//#region src/components/dnd/panel-binding.ts
|
|
92
158
|
const readAttribute = (node, name) => node.dataAttributes.find((attribute) => attribute.name === name)?.value;
|
|
93
159
|
const canEditBindingValue = (node, binding) => {
|
|
160
|
+
if (RESERVED_BINDING_PROPERTIES.includes(binding.property)) return false;
|
|
94
161
|
if (binding.property === "children" || binding.property === "innerText" || binding.property === "innerHTML" || binding.property === "items" || binding.property === "data" || binding.type === "array" || binding.type === "jsx" || binding.type === "richtext") return true;
|
|
95
162
|
const attribute = node.attributes.find((candidate) => candidate.name === binding.property);
|
|
96
|
-
|
|
163
|
+
if (!attribute) return true;
|
|
164
|
+
return attribute.isStringLiteral === true || canLosslesslyEvaluateSource(attribute?.value ?? "");
|
|
97
165
|
};
|
|
98
166
|
const toBindingFields = (binding) => ({
|
|
99
167
|
label: binding.label,
|
|
@@ -124,12 +192,19 @@ const resolveRenderEntry = (render, key) => {
|
|
|
124
192
|
render: entry
|
|
125
193
|
};
|
|
126
194
|
};
|
|
195
|
+
const CONTENT_PROPERTIES = /* @__PURE__ */ new Set([
|
|
196
|
+
"innerText",
|
|
197
|
+
"innerHTML",
|
|
198
|
+
"children"
|
|
199
|
+
]);
|
|
200
|
+
const isAttributeAbsent = (node, binding) => !CONTENT_PROPERTIES.has(binding.property) && !node.attributes.some((candidate) => candidate.name === binding.property);
|
|
127
201
|
const toPanelBindingData = (node, id, binding) => ({
|
|
128
202
|
...toBindingFields(binding),
|
|
129
203
|
id,
|
|
130
204
|
value: getStructuredValue(node, binding.property, binding.type),
|
|
131
205
|
rawValue: getCurrentValue(node, binding.property),
|
|
132
|
-
...!canEditBindingValue(node, binding) && { canEditValue: false }
|
|
206
|
+
...!canEditBindingValue(node, binding) && { canEditValue: false },
|
|
207
|
+
...isAttributeAbsent(node, binding) && { present: false }
|
|
133
208
|
});
|
|
134
209
|
const resolvePanelBindings = (node) => {
|
|
135
210
|
const id = readAttribute(node, "data-id");
|
|
@@ -266,7 +341,7 @@ const useStructuralSelection = (count, revision) => {
|
|
|
266
341
|
};
|
|
267
342
|
};
|
|
268
343
|
//#endregion
|
|
269
|
-
//#region src/components/dnd/panel/use-children
|
|
344
|
+
//#region src/components/dnd/panel/use-dnd-children.ts
|
|
270
345
|
const producedBy = (expected, action) => {
|
|
271
346
|
if (action.type === "move-selected") {
|
|
272
347
|
const moved = JSON.stringify(moveSelectedIndices(expected, new Set(action.indices), action.direction).items);
|
|
@@ -278,7 +353,7 @@ const producedBy = (expected, action) => {
|
|
|
278
353
|
return next.length === expected.length + added && expected.every((signature, index) => next[index] === signature);
|
|
279
354
|
};
|
|
280
355
|
};
|
|
281
|
-
const
|
|
356
|
+
const useDndChildren = (items, { onChange } = {}) => {
|
|
282
357
|
const expected = useMemo(() => getChildrenSignatures(items), [items]);
|
|
283
358
|
const revision = JSON.stringify(expected);
|
|
284
359
|
const { selection, record } = useStructuralSelection(items.length, revision);
|
|
@@ -326,7 +401,7 @@ const useChildrenEditor = (items, { onChange } = {}) => {
|
|
|
326
401
|
//#region src/components/dnd/panel/children.tsx
|
|
327
402
|
const Children$1 = ({ value, onChange, onNodeChange }) => {
|
|
328
403
|
const items = useMemo(() => Array.isArray(value) ? value : [], [value]);
|
|
329
|
-
const { selection, actions } =
|
|
404
|
+
const { selection, actions } = useDndChildren(items, { onChange });
|
|
330
405
|
const editableChildrenMap = useMemo(() => {
|
|
331
406
|
const map = /* @__PURE__ */ new Map();
|
|
332
407
|
items.forEach((item, index) => {
|
|
@@ -420,7 +495,8 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
|
|
|
420
495
|
}), editableChildrenMap.get(itemIndex).map((editableNode, idx) => {
|
|
421
496
|
const nodeId = editableNode.dataAttributes.find((a) => a.name === "data-id")?.value;
|
|
422
497
|
return /* @__PURE__ */ jsxs("div", {
|
|
423
|
-
|
|
498
|
+
"data-node-id": nodeId,
|
|
499
|
+
className: "rounded border border-green-100 bg-white p-2\n data-[picked]:ring-2 data-[picked]:ring-blue-400",
|
|
424
500
|
children: [/* @__PURE__ */ jsx("div", {
|
|
425
501
|
className: "mb-1 text-xs text-green-600",
|
|
426
502
|
children: editableNode.tagName
|
|
@@ -450,7 +526,118 @@ const Children$1 = ({ value, onChange, onNodeChange }) => {
|
|
|
450
526
|
});
|
|
451
527
|
};
|
|
452
528
|
//#endregion
|
|
453
|
-
//#region src/components/dnd/panel/
|
|
529
|
+
//#region src/components/dnd/panel/field-kind.ts
|
|
530
|
+
const isColorProperty = (property) => property.toLowerCase().includes("color");
|
|
531
|
+
const getFieldKind = (binding) => {
|
|
532
|
+
const { property, type, value } = binding;
|
|
533
|
+
if (binding.canEditValue === false) return "readonly";
|
|
534
|
+
if (property === "items" || property === "data" || type === "array") return "items";
|
|
535
|
+
if (type === "richtext") return "richtext";
|
|
536
|
+
if (property === BINDING_PROP.INNER_HTML) return "html";
|
|
537
|
+
if (type === "jsx") return "jsx";
|
|
538
|
+
if (property === "children" && Array.isArray(value)) return "children";
|
|
539
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) return "object";
|
|
540
|
+
if (type === "boolean" || typeof value === "boolean") return "boolean";
|
|
541
|
+
if (binding.options && Array.isArray(binding.options)) return "select";
|
|
542
|
+
if (type === "color" || isColorProperty(property)) return "color";
|
|
543
|
+
if (type === "date") return "date";
|
|
544
|
+
if (type === "url") return "url";
|
|
545
|
+
if (type === "number" || typeof value === "number") return "number";
|
|
546
|
+
return "text";
|
|
547
|
+
};
|
|
548
|
+
const isStructuralFieldKind = (kind) => kind === "items" || kind === "children";
|
|
549
|
+
//#endregion
|
|
550
|
+
//#region src/components/dnd/panel/sortable-items.tsx
|
|
551
|
+
const label = (items, id) => {
|
|
552
|
+
const index = items.findIndex((item) => item.id === id);
|
|
553
|
+
return index >= 0 ? `Item ${index + 1}` : "Item";
|
|
554
|
+
};
|
|
555
|
+
const SortableItems = ({ items, disabled, onMove, children }) => {
|
|
556
|
+
const sensors = useSensors(useSensor(MouseSensor, { activationConstraint: { distance: 4 } }), useSensor(TouchSensor, { activationConstraint: {
|
|
557
|
+
delay: 250,
|
|
558
|
+
tolerance: 5
|
|
559
|
+
} }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
560
|
+
const onDragEnd = ({ active, over }) => {
|
|
561
|
+
const from = items.find((item) => item.id === active.id);
|
|
562
|
+
const to = items.find((item) => item.id === over?.id);
|
|
563
|
+
if (from && to && from !== to) onMove(from, to.index);
|
|
564
|
+
};
|
|
565
|
+
return /* @__PURE__ */ jsx(DndContext, {
|
|
566
|
+
sensors,
|
|
567
|
+
collisionDetection: closestCenter,
|
|
568
|
+
modifiers: [restrictToVerticalAxis, restrictToParentElement],
|
|
569
|
+
accessibility: { announcements: {
|
|
570
|
+
onDragStart: ({ active }) => `Picked up ${label(items, active.id)}.`,
|
|
571
|
+
onDragOver: ({ active, over }) => over ? `${label(items, active.id)} is over ${label(items, over.id)}.` : void 0,
|
|
572
|
+
onDragEnd: ({ active, over }) => over ? `${label(items, active.id)} was moved to the place of ${label(items, over.id)}.` : `${label(items, active.id)} was dropped.`,
|
|
573
|
+
onDragCancel: ({ active }) => `Moving ${label(items, active.id)} was cancelled.`
|
|
574
|
+
} },
|
|
575
|
+
onDragEnd,
|
|
576
|
+
children: /* @__PURE__ */ jsx(SortableContext, {
|
|
577
|
+
items: items.map((item) => item.id),
|
|
578
|
+
strategy: verticalListSortingStrategy,
|
|
579
|
+
disabled,
|
|
580
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
581
|
+
className: "space-y-4",
|
|
582
|
+
children: items.map(children)
|
|
583
|
+
})
|
|
584
|
+
})
|
|
585
|
+
});
|
|
586
|
+
};
|
|
587
|
+
const ItemCard = ({ id, title, disabled, expanded, onToggle, checkbox, controls, className, children }) => {
|
|
588
|
+
const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
|
|
589
|
+
id,
|
|
590
|
+
disabled
|
|
591
|
+
});
|
|
592
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
593
|
+
ref: setNodeRef,
|
|
594
|
+
style: {
|
|
595
|
+
transform: CSS$1.Translate.toString(transform),
|
|
596
|
+
transition
|
|
597
|
+
},
|
|
598
|
+
className: cn(className, isDragging && "relative z-10 shadow-md"),
|
|
599
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
600
|
+
className: "flex items-center justify-between",
|
|
601
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
602
|
+
className: "flex items-center space-x-1",
|
|
603
|
+
children: [
|
|
604
|
+
/* @__PURE__ */ jsx("button", {
|
|
605
|
+
type: "button",
|
|
606
|
+
ref: setActivatorNodeRef,
|
|
607
|
+
...attributes,
|
|
608
|
+
...listeners,
|
|
609
|
+
"aria-label": `Reorder ${title}`,
|
|
610
|
+
disabled,
|
|
611
|
+
className: "cursor-grab rounded p-0.5 text-gray-400\n hover:text-gray-700 disabled:cursor-not-allowed\n disabled:opacity-40",
|
|
612
|
+
children: /* @__PURE__ */ jsx(GripVertical, { size: 14 })
|
|
613
|
+
}),
|
|
614
|
+
checkbox,
|
|
615
|
+
/* @__PURE__ */ jsxs("button", {
|
|
616
|
+
type: "button",
|
|
617
|
+
"aria-expanded": expanded,
|
|
618
|
+
onClick: onToggle,
|
|
619
|
+
className: "inline-flex items-center space-x-1 rounded px-1 text-xs\n font-medium",
|
|
620
|
+
children: [expanded ? /* @__PURE__ */ jsx(ChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(ChevronRight, { size: 14 }), /* @__PURE__ */ jsx("span", { children: title })]
|
|
621
|
+
})
|
|
622
|
+
]
|
|
623
|
+
}), controls]
|
|
624
|
+
}), expanded && children]
|
|
625
|
+
});
|
|
626
|
+
};
|
|
627
|
+
//#endregion
|
|
628
|
+
//#region src/components/dnd/panel/use-dnd-items.ts
|
|
629
|
+
const createPendingCommit = () => {
|
|
630
|
+
let current = null;
|
|
631
|
+
return {
|
|
632
|
+
read: () => current,
|
|
633
|
+
write: (next) => {
|
|
634
|
+
current = next;
|
|
635
|
+
},
|
|
636
|
+
clear: () => {
|
|
637
|
+
current = null;
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
};
|
|
454
641
|
const bindingsInJSX = (source) => {
|
|
455
642
|
const nodes = extract(source);
|
|
456
643
|
const container = nodes.find((n) => n.bindings?.some((b) => b.property === "children"));
|
|
@@ -548,7 +735,7 @@ const moveId = (ids, from, to) => {
|
|
|
548
735
|
const removeIds = (ids, indices) => {
|
|
549
736
|
return ids.filter((_, index) => !indices.has(index));
|
|
550
737
|
};
|
|
551
|
-
const
|
|
738
|
+
const useDndItems = (value, { render, onChange, onNodeChange } = {}) => {
|
|
552
739
|
const { objectItems, primitiveItems, parseError } = useMemo(() => parseSource(value), [value]);
|
|
553
740
|
const canEditStructure = useMemo(() => canStructurallyEditArray(value), [value]);
|
|
554
741
|
const { reportError } = useDndEditOptions();
|
|
@@ -574,8 +761,35 @@ const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
|
|
|
574
761
|
rawItems,
|
|
575
762
|
value
|
|
576
763
|
]);
|
|
764
|
+
const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
|
|
577
765
|
const { selection, record } = useStructuralSelection(isPrimitive ? primitiveItems.length : objectItems.length, value);
|
|
578
|
-
const
|
|
766
|
+
const [pending] = useState(createPendingCommit);
|
|
767
|
+
useEffect(() => {
|
|
768
|
+
pending.clear();
|
|
769
|
+
});
|
|
770
|
+
const latest = () => {
|
|
771
|
+
const last = pending.read();
|
|
772
|
+
if (last?.from !== value) return {
|
|
773
|
+
value,
|
|
774
|
+
items: rawItems,
|
|
775
|
+
ids: identity.ids,
|
|
776
|
+
selected: selection.selected
|
|
777
|
+
};
|
|
778
|
+
const parsed = parseSource(last.value);
|
|
779
|
+
return {
|
|
780
|
+
value: last.value,
|
|
781
|
+
items: isPrimitive ? parsed.primitiveItems : parsed.objectItems,
|
|
782
|
+
ids: last.ids,
|
|
783
|
+
selected: last.selected
|
|
784
|
+
};
|
|
785
|
+
};
|
|
786
|
+
const locate = (snapshot, elementIndex) => {
|
|
787
|
+
const shown = rawItems.find((item) => item.elementIndex === elementIndex);
|
|
788
|
+
if (!shown) return;
|
|
789
|
+
const index = snapshot.ids.indexOf(identity.ids[shown.index]);
|
|
790
|
+
return index >= 0 ? snapshot.items[index] : null;
|
|
791
|
+
};
|
|
792
|
+
const commit = (snapshot, next, nextIds, nextSelection) => {
|
|
579
793
|
if (next === null) {
|
|
580
794
|
reportError({
|
|
581
795
|
type: "items",
|
|
@@ -586,126 +800,160 @@ const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
|
|
|
586
800
|
}
|
|
587
801
|
const parsed = parseSource(next);
|
|
588
802
|
const nextItems = kind === "primitive" ? parsed.primitiveItems : parsed.objectItems;
|
|
589
|
-
|
|
590
|
-
|
|
803
|
+
const ids = nextIds ?? snapshot.ids;
|
|
804
|
+
pending.write({
|
|
805
|
+
from: value,
|
|
806
|
+
value: next,
|
|
807
|
+
ids,
|
|
808
|
+
selected: nextSelection
|
|
809
|
+
});
|
|
810
|
+
setIdentityState(createIdentityState(next, kind, nextItems, ids));
|
|
591
811
|
record(nextSelection, (revision) => revision === next, { now: true });
|
|
592
812
|
onChange?.(next);
|
|
593
813
|
return true;
|
|
594
814
|
};
|
|
595
|
-
const elementIndicesOf = (indices) =>
|
|
596
|
-
return new Set((isPrimitive ? primitiveItems : objectItems).filter((item) => indices.has(item.index)).map((item) => item.elementIndex));
|
|
597
|
-
};
|
|
815
|
+
const elementIndicesOf = (snapshot, indices) => new Set(snapshot.items.filter((item) => indices.has(item.index)).map((item) => item.elementIndex));
|
|
598
816
|
const updateProperty = (elementIndex, propertyKey, next) => {
|
|
599
|
-
|
|
817
|
+
const snapshot = latest();
|
|
818
|
+
const item = locate(snapshot, elementIndex);
|
|
819
|
+
if (item === null) return;
|
|
820
|
+
commit(snapshot, updateArrayItemProperty(snapshot.value, item?.elementIndex ?? elementIndex, propertyKey, next, render), void 0, new Set(snapshot.selected));
|
|
600
821
|
};
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
822
|
+
const updateValue = (elementIndex, next) => {
|
|
823
|
+
const snapshot = latest();
|
|
824
|
+
const item = locate(snapshot, elementIndex);
|
|
825
|
+
if (item === null) return;
|
|
826
|
+
commit(snapshot, updateArrayItemValue(snapshot.value, item?.elementIndex ?? elementIndex, next), void 0, new Set(snapshot.selected));
|
|
827
|
+
};
|
|
828
|
+
const actions = {
|
|
829
|
+
add: () => {
|
|
830
|
+
const snapshot = latest();
|
|
831
|
+
const next = appendArrayItem(snapshot.value, kind);
|
|
832
|
+
commit(snapshot, next, next ? [...snapshot.ids, nanoid(6)] : void 0, selectionAfter({ type: "append" }, snapshot.selected, snapshot.items.length));
|
|
833
|
+
},
|
|
834
|
+
move: (elementIndex, toIndex) => {
|
|
835
|
+
const snapshot = latest();
|
|
836
|
+
const from = locate(snapshot, elementIndex);
|
|
837
|
+
const target = snapshot.items.find((item) => item.index === toIndex);
|
|
838
|
+
if (!from || !target) return;
|
|
839
|
+
commit(snapshot, moveArrayItem(snapshot.value, from.elementIndex, target.elementIndex), moveId(snapshot.ids, from.index, target.index), /* @__PURE__ */ new Set());
|
|
840
|
+
},
|
|
841
|
+
remove: (elementIndex) => {
|
|
842
|
+
const snapshot = latest();
|
|
843
|
+
const removed = locate(snapshot, elementIndex);
|
|
844
|
+
if (removed === null) return;
|
|
845
|
+
commit(snapshot, removeArrayItems(snapshot.value, /* @__PURE__ */ new Set([removed?.elementIndex ?? elementIndex]), kind), removed ? removeIds(snapshot.ids, /* @__PURE__ */ new Set([removed.index])) : void 0, /* @__PURE__ */ new Set());
|
|
846
|
+
},
|
|
847
|
+
duplicateSelected: () => {
|
|
848
|
+
const snapshot = latest();
|
|
849
|
+
const selected = elementIndicesOf(snapshot, snapshot.selected);
|
|
850
|
+
const next = duplicateArrayItems(snapshot.value, selected);
|
|
851
|
+
const copied = snapshot.items.filter((item) => selected.has(item.elementIndex)).sort((a, b) => a.elementIndex - b.elementIndex).map(() => nanoid(6));
|
|
852
|
+
commit(snapshot, next, next ? [...snapshot.ids, ...copied] : void 0, selectionAfter({
|
|
853
|
+
type: "duplicate",
|
|
854
|
+
indices: [...snapshot.selected]
|
|
855
|
+
}, snapshot.selected, snapshot.items.length));
|
|
856
|
+
},
|
|
857
|
+
moveSelected: (direction) => {
|
|
858
|
+
const snapshot = latest();
|
|
859
|
+
const nextIdentity = moveSelectedIndices(snapshot.ids, snapshot.selected, direction);
|
|
860
|
+
const result = moveArrayItems(snapshot.value, elementIndicesOf(snapshot, snapshot.selected), direction);
|
|
861
|
+
if (!result) {
|
|
862
|
+
commit(snapshot, null, void 0, snapshot.selected);
|
|
863
|
+
return;
|
|
617
864
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
})
|
|
647
|
-
})), ...Object.entries(item.jsxFallbacks).map(([property, code]) => ({
|
|
648
|
-
property,
|
|
649
|
-
elements: [],
|
|
650
|
-
fallback: {
|
|
651
|
-
id: `item-${id}-${property}-jsx`,
|
|
652
|
-
label: property,
|
|
653
|
-
property,
|
|
654
|
-
type: "jsx",
|
|
655
|
-
value: code,
|
|
656
|
-
rawValue: code,
|
|
657
|
-
canEditValue: true,
|
|
658
|
-
onChange: (next) => updateProperty(item.elementIndex, property, next)
|
|
659
|
-
}
|
|
660
|
-
}))];
|
|
865
|
+
const visible = parseArrayExpression(result.code)?.elements.flatMap((node, index) => import_lib.isExpression(node) && import_lib.isObjectExpression(node) === !isPrimitive ? [index] : []) ?? [];
|
|
866
|
+
commit(snapshot, result.code, nextIdentity.items, new Set(visible.flatMap((position, index) => result.indices.has(position) ? [index] : [])));
|
|
867
|
+
},
|
|
868
|
+
removeSelected: () => {
|
|
869
|
+
const snapshot = latest();
|
|
870
|
+
commit(snapshot, removeArrayItems(snapshot.value, elementIndicesOf(snapshot, snapshot.selected), kind), removeIds(snapshot.ids, snapshot.selected), /* @__PURE__ */ new Set());
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
const items = isPrimitive ? primitiveItems.map((item) => ({
|
|
874
|
+
id: identity.ids[item.index] ?? item.id,
|
|
875
|
+
index: item.index,
|
|
876
|
+
elementIndex: item.elementIndex,
|
|
877
|
+
properties: [],
|
|
878
|
+
nested: [],
|
|
879
|
+
value: {
|
|
880
|
+
id: `primitive-${item.id}`,
|
|
881
|
+
label: `item-${item.index}`,
|
|
882
|
+
property: item.type,
|
|
883
|
+
value: item.value ?? "",
|
|
884
|
+
rawValue: item.type === "unknown" ? item.source : String(item.value ?? ""),
|
|
885
|
+
canEditValue: canLosslesslyEvaluateSource(item.source),
|
|
886
|
+
onChange: (next) => updateValue(item.elementIndex, next)
|
|
887
|
+
}
|
|
888
|
+
})) : objectItems.map((item) => {
|
|
889
|
+
const id = identity.ids[item.index] ?? item.id;
|
|
890
|
+
const properties = Object.entries(item.editableProperties).map(([key, prop]) => {
|
|
891
|
+
const binding = resolveRenderEntry(render, key);
|
|
892
|
+
const propertySource = value.slice(prop.astNode.start, prop.astNode.end);
|
|
661
893
|
return {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
894
|
+
...toBindingFields(binding),
|
|
895
|
+
id: `item-${id}-${key}`,
|
|
896
|
+
meta: {
|
|
897
|
+
...binding.meta,
|
|
898
|
+
valueType: prop.type
|
|
899
|
+
},
|
|
900
|
+
value: parseValue(String(prop.value)),
|
|
901
|
+
rawValue: prop.type === "unknown" ? propertySource : String(prop.value),
|
|
902
|
+
canEditValue: canLosslesslyEvaluateSource(propertySource),
|
|
903
|
+
onChange: (next) => updateProperty(item.elementIndex, key, next)
|
|
667
904
|
};
|
|
668
|
-
})
|
|
905
|
+
});
|
|
906
|
+
const nested = [...Object.entries(item.jsxBindings).map(([property, nodes]) => ({
|
|
907
|
+
property,
|
|
908
|
+
elements: nodes.flatMap((node) => {
|
|
909
|
+
const source = resolvePanelBindings(node);
|
|
910
|
+
if (!source) return [];
|
|
911
|
+
return [{
|
|
912
|
+
id: source.id,
|
|
913
|
+
tagName: source.tagName,
|
|
914
|
+
bindings: withPanelCommit(source.bindings, onNodeChange)
|
|
915
|
+
}];
|
|
916
|
+
})
|
|
917
|
+
})), ...Object.entries(item.jsxFallbacks).map(([property, code]) => ({
|
|
918
|
+
property,
|
|
919
|
+
elements: [],
|
|
920
|
+
fallback: {
|
|
921
|
+
id: `item-${id}-${property}-jsx`,
|
|
922
|
+
label: property,
|
|
923
|
+
property,
|
|
924
|
+
type: "jsx",
|
|
925
|
+
value: code,
|
|
926
|
+
rawValue: code,
|
|
927
|
+
canEditValue: true,
|
|
928
|
+
onChange: (next) => updateProperty(item.elementIndex, property, next)
|
|
929
|
+
}
|
|
930
|
+
}))];
|
|
931
|
+
return {
|
|
932
|
+
id,
|
|
933
|
+
index: item.index,
|
|
934
|
+
elementIndex: item.elementIndex,
|
|
935
|
+
properties,
|
|
936
|
+
nested
|
|
937
|
+
};
|
|
938
|
+
});
|
|
939
|
+
return {
|
|
940
|
+
kind,
|
|
941
|
+
items,
|
|
669
942
|
selection,
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
},
|
|
682
|
-
remove: (elementIndex) => {
|
|
683
|
-
const removed = (isPrimitive ? primitiveItems : objectItems).find((item) => item.elementIndex === elementIndex);
|
|
684
|
-
commit(removeArrayItems(value, /* @__PURE__ */ new Set([elementIndex]), kind), removed ? removeIds(identity.ids, /* @__PURE__ */ new Set([removed.index])) : void 0, /* @__PURE__ */ new Set());
|
|
685
|
-
},
|
|
686
|
-
duplicateSelected: () => {
|
|
687
|
-
const selected = elementIndicesOf(selection.selected);
|
|
688
|
-
const next = duplicateArrayItems(value, selected);
|
|
689
|
-
const copied = rawItems.filter((item) => selected.has(item.elementIndex)).sort((a, b) => a.elementIndex - b.elementIndex).map(() => nanoid(6));
|
|
690
|
-
commit(next, next ? [...identity.ids, ...copied] : void 0, selectionAfter({
|
|
691
|
-
type: "duplicate",
|
|
692
|
-
indices: [...selection.selected]
|
|
693
|
-
}, selection.selected, rawItems.length));
|
|
694
|
-
},
|
|
695
|
-
moveSelected: (direction) => {
|
|
696
|
-
const nextIdentity = moveSelectedIndices(identity.ids, selection.selected, direction);
|
|
697
|
-
const result = moveArrayItems(value, elementIndicesOf(selection.selected), direction);
|
|
698
|
-
if (!result) {
|
|
699
|
-
commit(null, void 0, selection.selected);
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
const visible = parseArrayExpression(result.code)?.elements.flatMap((node, index) => import_lib.isExpression(node) && import_lib.isObjectExpression(node) === !isPrimitive ? [index] : []) ?? [];
|
|
703
|
-
commit(result.code, nextIdentity.items, new Set(visible.flatMap((position, index) => result.indices.has(position) ? [index] : [])));
|
|
704
|
-
},
|
|
705
|
-
removeSelected: () => {
|
|
706
|
-
commit(removeArrayItems(value, elementIndicesOf(selection.selected), kind), removeIds(identity.ids, selection.selected), /* @__PURE__ */ new Set());
|
|
943
|
+
expansion: {
|
|
944
|
+
expandedIds: items.flatMap((item) => collapsed.has(item.id) ? [] : [item.id]),
|
|
945
|
+
isExpanded: (id) => !collapsed.has(id),
|
|
946
|
+
toggle: (id) => setCollapsed((previous) => {
|
|
947
|
+
const next = new Set(previous);
|
|
948
|
+
if (!next.delete(id)) next.add(id);
|
|
949
|
+
return next;
|
|
950
|
+
}),
|
|
951
|
+
setExpanded: (ids) => {
|
|
952
|
+
const expanded = new Set(ids);
|
|
953
|
+
setCollapsed(new Set(items.flatMap((item) => expanded.has(item.id) ? [] : [item.id])));
|
|
707
954
|
}
|
|
708
955
|
},
|
|
956
|
+
actions,
|
|
709
957
|
canEditStructure,
|
|
710
958
|
parseError
|
|
711
959
|
};
|
|
@@ -734,7 +982,8 @@ const NestedGroup = ({ group, onNodeChange }) => {
|
|
|
734
982
|
"):"
|
|
735
983
|
]
|
|
736
984
|
}), group.elements.map((element) => /* @__PURE__ */ jsxs("div", {
|
|
737
|
-
|
|
985
|
+
"data-node-id": element.id,
|
|
986
|
+
className: "rounded border border-blue-100 bg-blue-50 p-2\n data-[picked]:ring-2 data-[picked]:ring-blue-400",
|
|
738
987
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
739
988
|
className: "mb-1 text-xs text-blue-600",
|
|
740
989
|
children: [
|
|
@@ -769,7 +1018,7 @@ const NestedGroup = ({ group, onNodeChange }) => {
|
|
|
769
1018
|
});
|
|
770
1019
|
};
|
|
771
1020
|
const Items = ({ value, render, onChange, onChildChange }) => {
|
|
772
|
-
const { kind, items, selection, actions, canEditStructure, parseError } =
|
|
1021
|
+
const { kind, items, selection, expansion, actions, canEditStructure, parseError } = useDndItems(value, {
|
|
773
1022
|
render,
|
|
774
1023
|
onChange,
|
|
775
1024
|
onNodeChange: onChildChange
|
|
@@ -839,105 +1088,87 @@ const Items = ({ value, render, onChange, onChildChange }) => {
|
|
|
839
1088
|
})
|
|
840
1089
|
]
|
|
841
1090
|
});
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
!canEditStructure
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
}),
|
|
851
|
-
items.map((item) => /* @__PURE__ */ jsxs("div", {
|
|
852
|
-
className: "space-y-2 rounded border border-gray-100 bg-gray-50 p-2",
|
|
853
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
854
|
-
className: "flex items-center justify-between space-x-1",
|
|
855
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
856
|
-
onClick: (e) => canEditStructure && selection.toggle(item.index, e.shiftKey),
|
|
857
|
-
className: "inline-flex",
|
|
858
|
-
children: /* @__PURE__ */ jsx(Checkbox, {
|
|
859
|
-
checked: selection.isSelected(item.index),
|
|
860
|
-
disabled: !canEditStructure,
|
|
861
|
-
onChange: () => {}
|
|
862
|
-
})
|
|
863
|
-
}), itemControls(item)]
|
|
864
|
-
}), /* @__PURE__ */ jsx(Field, {
|
|
865
|
-
binding: item.value,
|
|
866
|
-
onNodeChange: onChildChange
|
|
867
|
-
})]
|
|
868
|
-
}, item.id))
|
|
869
|
-
]
|
|
1091
|
+
const itemCheckbox = (item) => /* @__PURE__ */ jsx("div", {
|
|
1092
|
+
onClick: (e) => canEditStructure && selection.toggle(item.index, e.shiftKey),
|
|
1093
|
+
className: "inline-flex",
|
|
1094
|
+
children: /* @__PURE__ */ jsx(Checkbox, {
|
|
1095
|
+
checked: selection.isSelected(item.index),
|
|
1096
|
+
disabled: !canEditStructure,
|
|
1097
|
+
onChange: () => {}
|
|
1098
|
+
})
|
|
870
1099
|
});
|
|
1100
|
+
const objectBody = (item) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
1101
|
+
className: "space-y-2",
|
|
1102
|
+
children: item.properties.map((binding) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
|
|
1103
|
+
className: "flex flex-col space-y-2",
|
|
1104
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
1105
|
+
className: "w-20 shrink-0 text-xs font-medium",
|
|
1106
|
+
children: binding.label
|
|
1107
|
+
}), /* @__PURE__ */ jsx(Field, {
|
|
1108
|
+
binding,
|
|
1109
|
+
onNodeChange: onChildChange
|
|
1110
|
+
})]
|
|
1111
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
1112
|
+
className: "text-right text-xs text-gray-500",
|
|
1113
|
+
children: [
|
|
1114
|
+
"(",
|
|
1115
|
+
String(binding.meta?.valueType),
|
|
1116
|
+
")"
|
|
1117
|
+
]
|
|
1118
|
+
})] }, `${item.id}-${binding.property}-${binding.label}`))
|
|
1119
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1120
|
+
className: "space-y-3 border-t pt-2",
|
|
1121
|
+
children: item.nested.length ? item.nested.map((group) => /* @__PURE__ */ jsx(NestedGroup, {
|
|
1122
|
+
group,
|
|
1123
|
+
onNodeChange: onChildChange
|
|
1124
|
+
}, group.property)) : /* @__PURE__ */ jsx("div", {
|
|
1125
|
+
className: "text-xs text-gray-500",
|
|
1126
|
+
children: "✓ No JSX bindings found"
|
|
1127
|
+
})
|
|
1128
|
+
})] });
|
|
1129
|
+
const allExpanded = expansion.expandedIds.length === items.length;
|
|
871
1130
|
return /* @__PURE__ */ jsxs("div", {
|
|
872
1131
|
className: "space-y-4",
|
|
873
1132
|
children: [
|
|
874
1133
|
header,
|
|
1134
|
+
items.length > 1 && /* @__PURE__ */ jsx("div", {
|
|
1135
|
+
className: "flex justify-end",
|
|
1136
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
1137
|
+
size: "small",
|
|
1138
|
+
variant: "text",
|
|
1139
|
+
onClick: () => expansion.setExpanded(allExpanded ? [] : items.map((item) => item.id)),
|
|
1140
|
+
children: allExpanded ? "Collapse all" : "Expand all"
|
|
1141
|
+
})
|
|
1142
|
+
}),
|
|
875
1143
|
bulkBar,
|
|
876
1144
|
!canEditStructure && /* @__PURE__ */ jsx("div", {
|
|
877
1145
|
className: "rounded border border-dashed border-amber-200 p-3 text-xs\n text-amber-700",
|
|
878
1146
|
children: "Values remain editable, but moving, copying, adding, and deleting require a dense array without spreads or parenthesized top-level items. Use the code editor for those structural changes."
|
|
879
1147
|
}),
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
}), itemControls(item)]
|
|
900
|
-
}),
|
|
901
|
-
/* @__PURE__ */ jsx("div", {
|
|
902
|
-
className: "space-y-2",
|
|
903
|
-
children: item.properties.map((binding) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
|
|
904
|
-
className: "flex flex-col space-y-2",
|
|
905
|
-
children: [/* @__PURE__ */ jsx("label", {
|
|
906
|
-
className: "w-20 shrink-0 text-xs font-medium",
|
|
907
|
-
children: binding.label
|
|
908
|
-
}), /* @__PURE__ */ jsx(Field, {
|
|
909
|
-
binding,
|
|
910
|
-
onNodeChange: onChildChange
|
|
911
|
-
})]
|
|
912
|
-
}), /* @__PURE__ */ jsxs("span", {
|
|
913
|
-
className: "text-right text-xs text-gray-500",
|
|
914
|
-
children: [
|
|
915
|
-
"(",
|
|
916
|
-
String(binding.meta?.valueType),
|
|
917
|
-
")"
|
|
918
|
-
]
|
|
919
|
-
})] }, `${item.id}-${binding.property}-${binding.label}`))
|
|
920
|
-
}),
|
|
921
|
-
/* @__PURE__ */ jsx("div", {
|
|
922
|
-
className: "space-y-3 border-t pt-2",
|
|
923
|
-
children: item.nested.length ? item.nested.map((group) => /* @__PURE__ */ jsx(NestedGroup, {
|
|
924
|
-
group,
|
|
925
|
-
onNodeChange: onChildChange
|
|
926
|
-
}, group.property)) : /* @__PURE__ */ jsx("div", {
|
|
927
|
-
className: "text-xs text-gray-500",
|
|
928
|
-
children: "✓ No JSX bindings found"
|
|
929
|
-
})
|
|
930
|
-
})
|
|
931
|
-
]
|
|
932
|
-
}, item.id))
|
|
1148
|
+
/* @__PURE__ */ jsx(SortableItems, {
|
|
1149
|
+
items,
|
|
1150
|
+
disabled: !canEditStructure,
|
|
1151
|
+
onMove: (item, toIndex) => actions.move(item.elementIndex, toIndex),
|
|
1152
|
+
children: (item) => /* @__PURE__ */ jsx(ItemCard, {
|
|
1153
|
+
id: item.id,
|
|
1154
|
+
title: `Item ${item.index + 1}`,
|
|
1155
|
+
disabled: !canEditStructure,
|
|
1156
|
+
expanded: expansion.isExpanded(item.id),
|
|
1157
|
+
onToggle: () => expansion.toggle(item.id),
|
|
1158
|
+
checkbox: itemCheckbox(item),
|
|
1159
|
+
controls: itemControls(item),
|
|
1160
|
+
className: kind === "primitive" ? "space-y-2 rounded border border-gray-100 bg-gray-50 p-2" : "space-y-3 rounded border bg-gray-50 p-3",
|
|
1161
|
+
children: kind === "primitive" ? /* @__PURE__ */ jsx(Field, {
|
|
1162
|
+
binding: item.value,
|
|
1163
|
+
onNodeChange: onChildChange
|
|
1164
|
+
}) : objectBody(item)
|
|
1165
|
+
}, item.id)
|
|
1166
|
+
})
|
|
933
1167
|
]
|
|
934
1168
|
});
|
|
935
1169
|
};
|
|
936
1170
|
//#endregion
|
|
937
1171
|
//#region src/components/dnd/panel/field.tsx
|
|
938
|
-
const isColorProperty = (propertyName) => {
|
|
939
|
-
return propertyName.toLowerCase().includes("color");
|
|
940
|
-
};
|
|
941
1172
|
const normalizeToHex = (value) => {
|
|
942
1173
|
const trimmed = value.trim();
|
|
943
1174
|
if (/^#([0-9A-Fa-f]{3}){1,2}$/.test(trimmed)) return trimmed;
|
|
@@ -1024,6 +1255,10 @@ const ColorPickerField = ({ value, onChange }) => {
|
|
|
1024
1255
|
}
|
|
1025
1256
|
});
|
|
1026
1257
|
};
|
|
1258
|
+
const FieldError = ({ message }) => message ? /* @__PURE__ */ jsx("p", {
|
|
1259
|
+
className: "mt-1 mb-0 text-xs text-red-500",
|
|
1260
|
+
children: message
|
|
1261
|
+
}) : null;
|
|
1027
1262
|
const BuiltinField = ({ binding, onNodeChange }) => {
|
|
1028
1263
|
const { id, value, rawValue, onChange } = binding;
|
|
1029
1264
|
const [validationError, setValidationError] = useState(null);
|
|
@@ -1033,7 +1268,8 @@ const BuiltinField = ({ binding, onNodeChange }) => {
|
|
|
1033
1268
|
setPrevRawValue(rawValue);
|
|
1034
1269
|
setText(rawValue);
|
|
1035
1270
|
}
|
|
1036
|
-
|
|
1271
|
+
const kind = getFieldKind(binding);
|
|
1272
|
+
if (kind === "readonly") return /* @__PURE__ */ jsxs("div", {
|
|
1037
1273
|
className: "space-y-1 rounded border border-dashed border-amber-200 p-3\n text-xs text-amber-700",
|
|
1038
1274
|
children: [
|
|
1039
1275
|
/* @__PURE__ */ jsx("div", { children: "This expression is preserved but is not editable here." }),
|
|
@@ -1047,31 +1283,28 @@ const BuiltinField = ({ binding, onNodeChange }) => {
|
|
|
1047
1283
|
const commitIfChanged = (next) => {
|
|
1048
1284
|
if (next !== (typeof next === "string" ? rawValue : value)) onChange(next);
|
|
1049
1285
|
};
|
|
1050
|
-
if (
|
|
1286
|
+
if (kind === "items") return /* @__PURE__ */ jsx(Items, {
|
|
1051
1287
|
value: rawValue,
|
|
1052
1288
|
render: binding.render,
|
|
1053
1289
|
onChange,
|
|
1054
1290
|
onChildChange: onNodeChange
|
|
1055
1291
|
});
|
|
1056
|
-
if (
|
|
1292
|
+
if (kind === "richtext") return /* @__PURE__ */ jsx(RichTextEditor, {
|
|
1057
1293
|
value: rawValue,
|
|
1058
1294
|
toolbar: RICHTEXT_TOOLBAR,
|
|
1059
1295
|
onChange: commitIfChanged
|
|
1060
1296
|
});
|
|
1061
|
-
if (
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
if (binding.property === "children" && Array.isArray(value)) return /* @__PURE__ */ jsx(Children$1, {
|
|
1297
|
+
if (kind === "html" || kind === "jsx") return /* @__PURE__ */ jsx(JSXEditorField, {
|
|
1298
|
+
value: rawValue,
|
|
1299
|
+
isHTML: kind === "html",
|
|
1300
|
+
onSave: commitIfChanged
|
|
1301
|
+
});
|
|
1302
|
+
if (kind === "children") return /* @__PURE__ */ jsx(Children$1, {
|
|
1070
1303
|
value,
|
|
1071
1304
|
onChange,
|
|
1072
1305
|
onNodeChange
|
|
1073
1306
|
});
|
|
1074
|
-
if (
|
|
1307
|
+
if (kind === "object") {
|
|
1075
1308
|
const objectValue = value;
|
|
1076
1309
|
return /* @__PURE__ */ jsx("div", {
|
|
1077
1310
|
className: "space-y-2 rounded border border-gray-200 bg-gray-50 p-2",
|
|
@@ -1101,23 +1334,23 @@ const BuiltinField = ({ binding, onNodeChange }) => {
|
|
|
1101
1334
|
})
|
|
1102
1335
|
});
|
|
1103
1336
|
}
|
|
1104
|
-
if (
|
|
1337
|
+
if (kind === "boolean") return /* @__PURE__ */ jsx(Checkbox, {
|
|
1105
1338
|
checked: value === true || value === "true",
|
|
1106
1339
|
onChange: (checked) => {
|
|
1107
1340
|
onChange(checked);
|
|
1108
1341
|
}
|
|
1109
1342
|
});
|
|
1110
1343
|
const stringValue = rawValue;
|
|
1111
|
-
if (
|
|
1344
|
+
if (kind === "select") return /* @__PURE__ */ jsx(Select, {
|
|
1112
1345
|
value: stringValue,
|
|
1113
|
-
options: binding.options,
|
|
1346
|
+
options: binding.options ?? [],
|
|
1114
1347
|
onChange: commitIfChanged
|
|
1115
1348
|
});
|
|
1116
|
-
if (
|
|
1349
|
+
if (kind === "color") return /* @__PURE__ */ jsx(ColorPickerField, {
|
|
1117
1350
|
value: normalizeToHex(stringValue),
|
|
1118
1351
|
onChange: commitIfChanged
|
|
1119
1352
|
});
|
|
1120
|
-
if (
|
|
1353
|
+
if (kind === "date") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(DatePicker, {
|
|
1121
1354
|
value: parseDateValue(stringValue),
|
|
1122
1355
|
onChange: (date) => {
|
|
1123
1356
|
const next = date ? formatDateValue(date) : "";
|
|
@@ -1129,11 +1362,8 @@ const BuiltinField = ({ binding, onNodeChange }) => {
|
|
|
1129
1362
|
setValidationError(null);
|
|
1130
1363
|
commitIfChanged(next);
|
|
1131
1364
|
}
|
|
1132
|
-
}),
|
|
1133
|
-
|
|
1134
|
-
children: validationError
|
|
1135
|
-
})] });
|
|
1136
|
-
if (binding.type === "url") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
|
|
1365
|
+
}), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
|
|
1366
|
+
if (kind === "url") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
|
|
1137
1367
|
type: "url",
|
|
1138
1368
|
value: text,
|
|
1139
1369
|
onChange: (e) => setText(e.target.value),
|
|
@@ -1148,11 +1378,8 @@ const BuiltinField = ({ binding, onNodeChange }) => {
|
|
|
1148
1378
|
setValidationError(null);
|
|
1149
1379
|
commitIfChanged(next);
|
|
1150
1380
|
}
|
|
1151
|
-
}),
|
|
1152
|
-
|
|
1153
|
-
children: validationError
|
|
1154
|
-
})] });
|
|
1155
|
-
if (binding.type === "number" || typeof value === "number") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
|
|
1381
|
+
}), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
|
|
1382
|
+
if (kind === "number") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
|
|
1156
1383
|
type: "number",
|
|
1157
1384
|
value: text,
|
|
1158
1385
|
onChange: (e) => setText(e.target.value),
|
|
@@ -1168,10 +1395,7 @@ const BuiltinField = ({ binding, onNodeChange }) => {
|
|
|
1168
1395
|
setValidationError(null);
|
|
1169
1396
|
commitIfChanged(next);
|
|
1170
1397
|
}
|
|
1171
|
-
}),
|
|
1172
|
-
className: "mt-1 text-xs text-red-500",
|
|
1173
|
-
children: validationError
|
|
1174
|
-
})] });
|
|
1398
|
+
}), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
|
|
1175
1399
|
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input.TextArea, {
|
|
1176
1400
|
value: text,
|
|
1177
1401
|
onChange: (e) => setText(e.target.value),
|
|
@@ -1187,10 +1411,7 @@ const BuiltinField = ({ binding, onNodeChange }) => {
|
|
|
1187
1411
|
setValidationError(null);
|
|
1188
1412
|
commitIfChanged(next);
|
|
1189
1413
|
}
|
|
1190
|
-
}),
|
|
1191
|
-
className: "mt-1 text-xs text-red-500",
|
|
1192
|
-
children: validationError
|
|
1193
|
-
})] });
|
|
1414
|
+
}), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
|
|
1194
1415
|
};
|
|
1195
1416
|
const Field = (props) => {
|
|
1196
1417
|
const { renderField } = useDndEditOptions();
|
|
@@ -1235,13 +1456,36 @@ const groupBindingsById = (bindings) => {
|
|
|
1235
1456
|
}
|
|
1236
1457
|
return [...groups.values()];
|
|
1237
1458
|
};
|
|
1238
|
-
const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, bindings, onNodeChange }) => {
|
|
1459
|
+
const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, bindings, onNodeChange, readOnly = false }) => {
|
|
1239
1460
|
const groups = groupBindingsById(bindings);
|
|
1461
|
+
const rootRef = useRef(null);
|
|
1462
|
+
const pickedId = (useContext(DndInspectorContext)?.picked ?? null)?.id;
|
|
1463
|
+
useEffect(() => {
|
|
1464
|
+
const root = rootRef.current;
|
|
1465
|
+
if (!root || !pickedId) return;
|
|
1466
|
+
const target = root.querySelector(`[data-node-id="${CSS.escape(pickedId)}"]`);
|
|
1467
|
+
if (!target) return;
|
|
1468
|
+
target.setAttribute("data-picked", "");
|
|
1469
|
+
const until = performance.now() + 600;
|
|
1470
|
+
let frame = 0;
|
|
1471
|
+
const keepInView = () => {
|
|
1472
|
+
const box = target.getBoundingClientRect();
|
|
1473
|
+
const view = root.getBoundingClientRect();
|
|
1474
|
+
if (box.top < view.top || box.bottom > view.bottom) target.scrollIntoView?.({ block: "nearest" });
|
|
1475
|
+
if (performance.now() < until) frame = requestAnimationFrame(keepInView);
|
|
1476
|
+
};
|
|
1477
|
+
keepInView();
|
|
1478
|
+
return () => {
|
|
1479
|
+
cancelAnimationFrame(frame);
|
|
1480
|
+
target.removeAttribute("data-picked");
|
|
1481
|
+
};
|
|
1482
|
+
}, [pickedId, groups.length]);
|
|
1240
1483
|
if (!item) return /* @__PURE__ */ jsx(Typography.Paragraph, {
|
|
1241
1484
|
className: cn("p-4 text-sm text-gray-500"),
|
|
1242
1485
|
children: "Please select a section."
|
|
1243
1486
|
});
|
|
1244
1487
|
return /* @__PURE__ */ jsxs("div", {
|
|
1488
|
+
ref: rootRef,
|
|
1245
1489
|
className: cn("h-full space-y-4 p-4", "overflow-x-hidden overflow-y-auto"),
|
|
1246
1490
|
children: [
|
|
1247
1491
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -1273,13 +1517,22 @@ const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canM
|
|
|
1273
1517
|
]
|
|
1274
1518
|
})]
|
|
1275
1519
|
}),
|
|
1520
|
+
readOnly && /* @__PURE__ */ jsx(Typography.Text, {
|
|
1521
|
+
role: "status",
|
|
1522
|
+
className: "block text-xs text-amber-700",
|
|
1523
|
+
children: "The document has a syntax error. These fields are from the last version that parsed, and can't be edited until it parses again."
|
|
1524
|
+
}),
|
|
1276
1525
|
!groups.length && /* @__PURE__ */ jsx(Typography.Text, {
|
|
1277
1526
|
className: "text-xs text-gray-400",
|
|
1278
1527
|
children: "No editable elements."
|
|
1279
1528
|
}),
|
|
1280
|
-
groups.map((group) => /* @__PURE__ */ jsx(
|
|
1281
|
-
|
|
1282
|
-
|
|
1529
|
+
groups.map((group) => /* @__PURE__ */ jsx("div", {
|
|
1530
|
+
"data-node-id": group[0].id,
|
|
1531
|
+
className: "rounded data-[picked]:ring-2 data-[picked]:ring-blue-400\n data-[picked]:ring-offset-2",
|
|
1532
|
+
children: /* @__PURE__ */ jsx(FieldGroup, {
|
|
1533
|
+
bindings: group,
|
|
1534
|
+
onNodeChange
|
|
1535
|
+
})
|
|
1283
1536
|
}, group[0].id))
|
|
1284
1537
|
]
|
|
1285
1538
|
});
|
|
@@ -1299,7 +1552,7 @@ const PaletteItems = ({ subject }) => {
|
|
|
1299
1552
|
});
|
|
1300
1553
|
};
|
|
1301
1554
|
const PanelFields = ({ subject }) => {
|
|
1302
|
-
const { panel: { item, onDelete, onMoveUp, onMoveDown, canMoveUp, canMoveDown, bindings, onNodeChange } } = useDndRegions(subject);
|
|
1555
|
+
const { panel: { item, onDelete, onMoveUp, onMoveDown, canMoveUp, canMoveDown, bindings, onNodeChange, readOnly } } = useDndRegions(subject);
|
|
1303
1556
|
return /* @__PURE__ */ jsx(Panel$1, {
|
|
1304
1557
|
item,
|
|
1305
1558
|
onDelete,
|
|
@@ -1308,7 +1561,8 @@ const PanelFields = ({ subject }) => {
|
|
|
1308
1561
|
canMoveUp,
|
|
1309
1562
|
canMoveDown,
|
|
1310
1563
|
bindings,
|
|
1311
|
-
onNodeChange
|
|
1564
|
+
onNodeChange,
|
|
1565
|
+
readOnly
|
|
1312
1566
|
});
|
|
1313
1567
|
};
|
|
1314
1568
|
const Palette = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
|
|
@@ -1317,8 +1571,9 @@ const Palette = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
|
|
|
1317
1571
|
children: /* @__PURE__ */ jsx(PaletteItems, { subject: "<Live.Dnd.Palette>" })
|
|
1318
1572
|
});
|
|
1319
1573
|
const Canvas = ({ className, style, ...restProps }) => {
|
|
1320
|
-
const { canvas } = useDndRegions("<Live.Dnd.Canvas>");
|
|
1321
|
-
|
|
1574
|
+
const { canvas, documentError } = useDndRegions("<Live.Dnd.Canvas>");
|
|
1575
|
+
const inspector = useDndInspector();
|
|
1576
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1322
1577
|
className: cn("relative h-full w-full overflow-y-auto", className),
|
|
1323
1578
|
"data-frame-container": true,
|
|
1324
1579
|
style: {
|
|
@@ -1328,7 +1583,21 @@ const Canvas = ({ className, style, ...restProps }) => {
|
|
|
1328
1583
|
...style
|
|
1329
1584
|
},
|
|
1330
1585
|
...restProps,
|
|
1331
|
-
children: canvas
|
|
1586
|
+
children: [canvas, !documentError && /* @__PURE__ */ jsx("div", {
|
|
1587
|
+
className: "pointer-events-none sticky bottom-0 z-70 h-0",
|
|
1588
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1589
|
+
className: "pointer-events-auto absolute bottom-3 left-3",
|
|
1590
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
1591
|
+
icon: /* @__PURE__ */ jsx(Crosshair, {}),
|
|
1592
|
+
color: "primary",
|
|
1593
|
+
variant: inspector.active ? "solid" : "outlined",
|
|
1594
|
+
"aria-pressed": inspector.active,
|
|
1595
|
+
"aria-label": "Pick an element",
|
|
1596
|
+
title: inspector.active ? "Click an element to edit it. Esc to cancel." : "Pick an element",
|
|
1597
|
+
onClick: inspector.toggle
|
|
1598
|
+
})
|
|
1599
|
+
})
|
|
1600
|
+
})]
|
|
1332
1601
|
});
|
|
1333
1602
|
};
|
|
1334
1603
|
const Panel = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
|
|
@@ -1495,15 +1764,48 @@ const arePropsEqual = (prev, next) => {
|
|
|
1495
1764
|
var renderer_default = memo(Renderer, arePropsEqual);
|
|
1496
1765
|
//#endregion
|
|
1497
1766
|
//#region src/components/dnd/sortable.tsx
|
|
1498
|
-
const
|
|
1499
|
-
|
|
1767
|
+
const NAVIGATION_KEYS = {
|
|
1768
|
+
ArrowUp: "previous",
|
|
1769
|
+
ArrowDown: "next",
|
|
1770
|
+
Home: "first",
|
|
1771
|
+
End: "last"
|
|
1772
|
+
};
|
|
1773
|
+
const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy: _onCopy, nodeRef, onNavigate, onDeleteKey, inspecting = false, onInspectMove, onInspectPick, onInspectLeave }) => {
|
|
1774
|
+
const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging, isOver, active } = useSortable({
|
|
1775
|
+
id,
|
|
1776
|
+
disabled: inspecting
|
|
1777
|
+
});
|
|
1500
1778
|
const style = {
|
|
1501
|
-
transform: CSS.Transform.toString(transform),
|
|
1779
|
+
transform: CSS$1.Transform.toString(transform),
|
|
1502
1780
|
transition,
|
|
1503
1781
|
opacity: isDragging ? .5 : 1,
|
|
1504
|
-
cursor: "grab"
|
|
1782
|
+
cursor: inspecting ? "crosshair" : "grab"
|
|
1505
1783
|
};
|
|
1506
1784
|
const isNewItemOver = isOver && active?.data.current?.type === "new-item";
|
|
1785
|
+
const onKeyDown = (e) => {
|
|
1786
|
+
listeners?.onKeyDown?.(e);
|
|
1787
|
+
if (e.target !== e.currentTarget || active || e.defaultPrevented) return;
|
|
1788
|
+
if (e.key === "Enter") {
|
|
1789
|
+
e.preventDefault();
|
|
1790
|
+
onClick?.();
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
const to = NAVIGATION_KEYS[e.key];
|
|
1794
|
+
if (to && onNavigate) {
|
|
1795
|
+
e.preventDefault();
|
|
1796
|
+
onNavigate(to);
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
if ((e.key === "Delete" || e.key === "Backspace") && onDeleteKey) {
|
|
1800
|
+
e.preventDefault();
|
|
1801
|
+
onDeleteKey();
|
|
1802
|
+
}
|
|
1803
|
+
};
|
|
1804
|
+
const setRefs = (node) => {
|
|
1805
|
+
setNodeRef(node);
|
|
1806
|
+
setActivatorNodeRef(node);
|
|
1807
|
+
nodeRef?.(node);
|
|
1808
|
+
};
|
|
1507
1809
|
const onDelete = (e) => {
|
|
1508
1810
|
e.stopPropagation();
|
|
1509
1811
|
_onDelete?.(id);
|
|
@@ -1513,14 +1815,23 @@ const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy
|
|
|
1513
1815
|
_onCopy?.(id);
|
|
1514
1816
|
};
|
|
1515
1817
|
return /* @__PURE__ */ jsxs("div", {
|
|
1516
|
-
ref:
|
|
1818
|
+
ref: setRefs,
|
|
1517
1819
|
style,
|
|
1518
1820
|
...attributes,
|
|
1519
1821
|
...listeners,
|
|
1822
|
+
onKeyDown,
|
|
1520
1823
|
onClick,
|
|
1521
1824
|
className: cn("relative", selected && "z-10 outline-2 outline-offset-2 outline-blue-500", isNewItemOver && "border-t-4 border-t-green-500"),
|
|
1522
1825
|
children: [
|
|
1523
|
-
/* @__PURE__ */ jsx("div", {
|
|
1826
|
+
/* @__PURE__ */ jsx("div", {
|
|
1827
|
+
className: cn("absolute inset-0 z-50"),
|
|
1828
|
+
onPointerMove: inspecting ? (e) => onInspectMove?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY) : void 0,
|
|
1829
|
+
onPointerLeave: inspecting ? onInspectLeave : void 0,
|
|
1830
|
+
onClick: inspecting ? (e) => {
|
|
1831
|
+
e.stopPropagation();
|
|
1832
|
+
onInspectPick?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY);
|
|
1833
|
+
} : void 0
|
|
1834
|
+
}),
|
|
1524
1835
|
children,
|
|
1525
1836
|
selected && /* @__PURE__ */ jsxs(Space, {
|
|
1526
1837
|
className: cn("absolute top-1 right-1 z-60"),
|
|
@@ -1547,7 +1858,7 @@ const Overlay = ({ sections, isForced, renderProps }) => {
|
|
|
1547
1858
|
}
|
|
1548
1859
|
const section = sections.find((s) => s.id === active.id);
|
|
1549
1860
|
if (section) {
|
|
1550
|
-
const preview = generateSection(section.code, renderProps.fullCode);
|
|
1861
|
+
const preview = generateSection(section.code, renderProps.fullCode, { containerId: renderProps.containerId });
|
|
1551
1862
|
return /* @__PURE__ */ jsx(Sortable, {
|
|
1552
1863
|
id: section.id,
|
|
1553
1864
|
name: section.name,
|
|
@@ -1567,29 +1878,100 @@ const Overlay = ({ sections, isForced, renderProps }) => {
|
|
|
1567
1878
|
};
|
|
1568
1879
|
//#endregion
|
|
1569
1880
|
//#region src/components/dnd/use-section-document.ts
|
|
1570
|
-
const
|
|
1881
|
+
const createLastParsed = () => {
|
|
1882
|
+
let last = null;
|
|
1883
|
+
return {
|
|
1884
|
+
read: (containerId) => last && last.containerId === containerId ? last.document : void 0,
|
|
1885
|
+
write: (containerId, document) => {
|
|
1886
|
+
last = {
|
|
1887
|
+
containerId,
|
|
1888
|
+
document
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1892
|
+
};
|
|
1893
|
+
const useSectionDocument = (value, onChange, { containerId, sectionNameFallback, onBlockedEdit } = {}) => {
|
|
1571
1894
|
const { setCode } = usePreview();
|
|
1572
1895
|
const [selectedId, setSelectedId] = useState(null);
|
|
1573
|
-
const
|
|
1574
|
-
|
|
1896
|
+
const readSections = useCallback((code) => extractSections(code, {
|
|
1897
|
+
containerId,
|
|
1898
|
+
sectionNameFallback
|
|
1899
|
+
}), [containerId, sectionNameFallback]);
|
|
1900
|
+
const document = useMemo(() => fillSectionIds(value, void 0, { containerId }), [containerId, value]);
|
|
1901
|
+
const [lastParsed] = useState(createLastParsed);
|
|
1902
|
+
const view = useMemo(() => {
|
|
1903
|
+
const inspection = inspectDocument(document, { containerId });
|
|
1904
|
+
if (inspection.ok) {
|
|
1905
|
+
lastParsed.write(containerId, document);
|
|
1906
|
+
return {
|
|
1907
|
+
document,
|
|
1908
|
+
problem: null,
|
|
1909
|
+
stale: false
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
const { ok: _ok, ...problem } = inspection;
|
|
1913
|
+
const last = problem.reason === "parse-error" ? lastParsed.read(containerId) : void 0;
|
|
1914
|
+
return {
|
|
1915
|
+
document: last ?? document,
|
|
1916
|
+
problem,
|
|
1917
|
+
stale: last !== void 0
|
|
1918
|
+
};
|
|
1919
|
+
}, [
|
|
1920
|
+
containerId,
|
|
1921
|
+
document,
|
|
1922
|
+
lastParsed
|
|
1923
|
+
]);
|
|
1924
|
+
const sections = useMemo(() => readSections(view.document), [readSections, view.document]);
|
|
1575
1925
|
const [previewCache] = useState(() => createSectionPreviewCache());
|
|
1576
|
-
const previews = useMemo(() => previewCache.compute(document, sections), [
|
|
1926
|
+
const previews = useMemo(() => previewCache.compute(view.document, sections, { containerId }), [
|
|
1927
|
+
containerId,
|
|
1577
1928
|
previewCache,
|
|
1578
1929
|
sections,
|
|
1579
|
-
document
|
|
1930
|
+
view.document
|
|
1580
1931
|
]);
|
|
1581
1932
|
const selectedIndex = sections.findIndex((s) => s.id === selectedId);
|
|
1582
1933
|
const selectedItem = selectedIndex >= 0 ? sections[selectedIndex] : void 0;
|
|
1934
|
+
const pendingRef = useRef(null);
|
|
1935
|
+
useEffect(() => {
|
|
1936
|
+
pendingRef.current = null;
|
|
1937
|
+
});
|
|
1938
|
+
const latestDocument = useCallback(() => {
|
|
1939
|
+
const pending = pendingRef.current;
|
|
1940
|
+
return pending?.from === document ? pending.code : document;
|
|
1941
|
+
}, [document]);
|
|
1942
|
+
const latestSections = useCallback(() => readSections(latestDocument()), [latestDocument, readSections]);
|
|
1583
1943
|
const commit = useCallback((nextSections) => {
|
|
1584
|
-
const nextCode = fillSectionIds(replaceSections(
|
|
1944
|
+
const nextCode = fillSectionIds(replaceSections(latestDocument(), nextSections.map((s) => s.code), { containerId }), void 0, { containerId });
|
|
1945
|
+
pendingRef.current = {
|
|
1946
|
+
from: document,
|
|
1947
|
+
code: nextCode
|
|
1948
|
+
};
|
|
1585
1949
|
onChange?.(nextCode);
|
|
1586
1950
|
setCode(nextCode);
|
|
1587
|
-
return
|
|
1951
|
+
return readSections(nextCode);
|
|
1588
1952
|
}, [
|
|
1953
|
+
containerId,
|
|
1589
1954
|
document,
|
|
1955
|
+
latestDocument,
|
|
1590
1956
|
onChange,
|
|
1957
|
+
readSections,
|
|
1591
1958
|
setCode
|
|
1592
1959
|
]);
|
|
1960
|
+
const getCommittedSection = useCallback((id) => {
|
|
1961
|
+
const pending = pendingRef.current;
|
|
1962
|
+
if (pending?.from !== document) return;
|
|
1963
|
+
return readSections(pending.code).find((s) => s.id === id);
|
|
1964
|
+
}, [document, readSections]);
|
|
1965
|
+
const { problem, stale } = view;
|
|
1966
|
+
const refuse = useCallback(() => {
|
|
1967
|
+
if (!stale || !problem) return false;
|
|
1968
|
+
onBlockedEdit?.(problem);
|
|
1969
|
+
return true;
|
|
1970
|
+
}, [
|
|
1971
|
+
onBlockedEdit,
|
|
1972
|
+
problem,
|
|
1973
|
+
stale
|
|
1974
|
+
]);
|
|
1593
1975
|
return {
|
|
1594
1976
|
sections,
|
|
1595
1977
|
previews,
|
|
@@ -1599,54 +1981,91 @@ const useSectionDocument = (value, onChange) => {
|
|
|
1599
1981
|
select: useCallback((id) => {
|
|
1600
1982
|
setSelectedId((prev) => prev === id ? null : id);
|
|
1601
1983
|
}, []),
|
|
1984
|
+
selectOnly: useCallback((id) => setSelectedId(id), []),
|
|
1602
1985
|
clearSelection: useCallback(() => setSelectedId(null), []),
|
|
1603
1986
|
add: useCallback((item, atIndex) => {
|
|
1987
|
+
if (refuse()) return;
|
|
1988
|
+
const current = latestSections();
|
|
1604
1989
|
const next = { code: item.code };
|
|
1605
|
-
commit(atIndex === void 0 || atIndex < 0 ? [...
|
|
1606
|
-
...
|
|
1990
|
+
commit(atIndex === void 0 || atIndex < 0 ? [...current, next] : [
|
|
1991
|
+
...current.slice(0, atIndex),
|
|
1607
1992
|
next,
|
|
1608
|
-
...
|
|
1993
|
+
...current.slice(atIndex)
|
|
1609
1994
|
]);
|
|
1610
|
-
}, [
|
|
1995
|
+
}, [
|
|
1996
|
+
commit,
|
|
1997
|
+
refuse,
|
|
1998
|
+
latestSections
|
|
1999
|
+
]),
|
|
1611
2000
|
remove: useCallback((id) => {
|
|
1612
|
-
|
|
2001
|
+
if (refuse()) return;
|
|
2002
|
+
const current = latestSections();
|
|
2003
|
+
const index = current.findIndex((s) => s.id === id);
|
|
1613
2004
|
if (index < 0) return;
|
|
1614
2005
|
if (id === selectedId) setSelectedId(null);
|
|
1615
|
-
commit(
|
|
2006
|
+
commit(current.filter((_, i) => i !== index));
|
|
1616
2007
|
}, [
|
|
1617
2008
|
commit,
|
|
1618
|
-
|
|
2009
|
+
refuse,
|
|
2010
|
+
latestSections,
|
|
1619
2011
|
selectedId
|
|
1620
2012
|
]),
|
|
1621
2013
|
copy: useCallback((id) => {
|
|
1622
|
-
|
|
1623
|
-
const
|
|
2014
|
+
if (refuse()) return;
|
|
2015
|
+
const current = latestSections();
|
|
2016
|
+
const index = current.findIndex((s) => s.id === id);
|
|
2017
|
+
const source = current[index];
|
|
1624
2018
|
if (!source) return;
|
|
1625
2019
|
const committed = commit([
|
|
1626
|
-
...
|
|
2020
|
+
...current.slice(0, index + 1),
|
|
1627
2021
|
{ code: replaceIds(source.code) },
|
|
1628
|
-
...
|
|
2022
|
+
...current.slice(index + 1)
|
|
1629
2023
|
]);
|
|
1630
2024
|
setSelectedId(committed[index + 1]?.id ?? null);
|
|
1631
|
-
}, [
|
|
2025
|
+
}, [
|
|
2026
|
+
commit,
|
|
2027
|
+
refuse,
|
|
2028
|
+
latestSections
|
|
2029
|
+
]),
|
|
1632
2030
|
move: useCallback((id, direction) => {
|
|
1633
|
-
|
|
2031
|
+
if (refuse()) return;
|
|
2032
|
+
const current = latestSections();
|
|
2033
|
+
const index = current.findIndex((s) => s.id === id);
|
|
1634
2034
|
const targetIndex = direction === "up" ? index - 1 : index + 1;
|
|
1635
|
-
if (index < 0 || targetIndex < 0 || targetIndex >=
|
|
1636
|
-
commit(arrayMove(
|
|
1637
|
-
}, [
|
|
2035
|
+
if (index < 0 || targetIndex < 0 || targetIndex >= current.length) return;
|
|
2036
|
+
commit(arrayMove(current, index, targetIndex));
|
|
2037
|
+
}, [
|
|
2038
|
+
commit,
|
|
2039
|
+
refuse,
|
|
2040
|
+
latestSections
|
|
2041
|
+
]),
|
|
1638
2042
|
reorder: useCallback((activeId, overId) => {
|
|
1639
|
-
|
|
1640
|
-
const
|
|
2043
|
+
if (refuse()) return;
|
|
2044
|
+
const current = latestSections();
|
|
2045
|
+
const prevIndex = current.findIndex((s) => s.id === activeId);
|
|
2046
|
+
const nextIndex = current.findIndex((s) => s.id === overId);
|
|
1641
2047
|
if (prevIndex < 0 || nextIndex < 0 || prevIndex === nextIndex) return;
|
|
1642
|
-
commit(arrayMove(
|
|
1643
|
-
}, [
|
|
2048
|
+
commit(arrayMove(current, prevIndex, nextIndex));
|
|
2049
|
+
}, [
|
|
2050
|
+
commit,
|
|
2051
|
+
refuse,
|
|
2052
|
+
latestSections
|
|
2053
|
+
]),
|
|
1644
2054
|
patch: useCallback((next) => {
|
|
1645
|
-
|
|
2055
|
+
if (refuse()) return;
|
|
2056
|
+
const current = latestSections();
|
|
2057
|
+
commit(current.map((s) => s.id === next.id ? {
|
|
1646
2058
|
...s,
|
|
1647
2059
|
...next
|
|
1648
2060
|
} : s));
|
|
1649
|
-
}, [
|
|
2061
|
+
}, [
|
|
2062
|
+
commit,
|
|
2063
|
+
refuse,
|
|
2064
|
+
latestSections
|
|
2065
|
+
]),
|
|
2066
|
+
getCommittedSection,
|
|
2067
|
+
problem,
|
|
2068
|
+
stale
|
|
1650
2069
|
};
|
|
1651
2070
|
};
|
|
1652
2071
|
//#endregion
|
|
@@ -1665,6 +2084,14 @@ const describeUpdateFailure = (failure, label) => {
|
|
|
1665
2084
|
title: `Failed to update "${label}"`,
|
|
1666
2085
|
description: `${failure.count} bindings share ${failure.property ? `property "${failure.property}"` : `label "${label}"`} on this element — remove the duplicate in its data-binding.`
|
|
1667
2086
|
};
|
|
2087
|
+
case "reserved-property": return {
|
|
2088
|
+
title: `Cannot edit "${label}" in the panel`,
|
|
2089
|
+
description: `"${failure.property}" is managed by the editor, so a binding can't change it.`
|
|
2090
|
+
};
|
|
2091
|
+
case "required-property": return {
|
|
2092
|
+
title: `Cannot remove "${label}"`,
|
|
2093
|
+
description: `"${failure.property}" is marked required in its data-binding, so it can't be removed.`
|
|
2094
|
+
};
|
|
1668
2095
|
case "no-binding": return {
|
|
1669
2096
|
title: `Failed to update "${label}"`,
|
|
1670
2097
|
description: "This element has no data-binding declaration."
|
|
@@ -1684,13 +2111,48 @@ const describeUpdateFailure = (failure, label) => {
|
|
|
1684
2111
|
default: return { title: `Failed to update "${label}"` };
|
|
1685
2112
|
}
|
|
1686
2113
|
};
|
|
2114
|
+
const KEYBOARD_CODES = {
|
|
2115
|
+
start: ["Space"],
|
|
2116
|
+
cancel: ["Escape"],
|
|
2117
|
+
end: ["Space", "Enter"]
|
|
2118
|
+
};
|
|
2119
|
+
const SCREEN_READER_INSTRUCTIONS = { draggable: "On a palette item, press Enter to add it to the canvas. On a canvas section, press Enter to select it, the up and down arrow keys to go to the previous or next section, and Delete to remove it. Press Space to pick a section up, the arrow keys to move it, and Space or Enter to drop it. Press Escape to cancel." };
|
|
2120
|
+
const createNodeRegistry = () => {
|
|
2121
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
2122
|
+
const callbacks = /* @__PURE__ */ new Map();
|
|
2123
|
+
return {
|
|
2124
|
+
get: (id) => nodes.get(id),
|
|
2125
|
+
register: (id) => {
|
|
2126
|
+
let callback = callbacks.get(id);
|
|
2127
|
+
if (!callback) {
|
|
2128
|
+
callback = (node) => {
|
|
2129
|
+
if (node) nodes.set(id, node);
|
|
2130
|
+
else {
|
|
2131
|
+
nodes.delete(id);
|
|
2132
|
+
callbacks.delete(id);
|
|
2133
|
+
}
|
|
2134
|
+
};
|
|
2135
|
+
callbacks.set(id, callback);
|
|
2136
|
+
}
|
|
2137
|
+
return callback;
|
|
2138
|
+
}
|
|
2139
|
+
};
|
|
2140
|
+
};
|
|
2141
|
+
const blockedEditError = (error) => ({
|
|
2142
|
+
type: "parse",
|
|
2143
|
+
target: "document",
|
|
2144
|
+
reason: "parse-error",
|
|
2145
|
+
error,
|
|
2146
|
+
title: "The document has a syntax error",
|
|
2147
|
+
description: "The canvas shows the last version that parsed. Fix the error in the code, then edit here again."
|
|
2148
|
+
});
|
|
1687
2149
|
const conditionalModifiers = (args) => {
|
|
1688
2150
|
const { active } = args;
|
|
1689
2151
|
if (active?.data.current?.type === "new-item") return args.transform;
|
|
1690
2152
|
return restrictToVerticalAxis(args);
|
|
1691
2153
|
};
|
|
1692
2154
|
const NO_MODULES = {};
|
|
1693
|
-
const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange: _onChange, className, items = [], frame, dynamicTailwind = false, provider, renderField, onEditError, renderSectionFallback, shouldForceSectionFallback, children, ...restProps }) => {
|
|
2155
|
+
const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange: _onChange, className, items = [], frame, dynamicTailwind = false, provider, renderField, onEditError, renderSectionFallback, shouldForceSectionFallback, onBeforeDelete, sectionNameFallback, containerId, onNodePick, children, ...restProps }) => {
|
|
1694
2156
|
const modules = useStableModules(_modules);
|
|
1695
2157
|
const [mobilePaletteOpen, setMobilePaletteOpen] = useState(false);
|
|
1696
2158
|
const { breakpoint } = useResponsiveSize();
|
|
@@ -1704,9 +2166,35 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1704
2166
|
return false;
|
|
1705
2167
|
}
|
|
1706
2168
|
};
|
|
1707
|
-
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 10 } })
|
|
2169
|
+
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 10 } }), useSensor(KeyboardSensor, {
|
|
2170
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
2171
|
+
keyboardCodes: KEYBOARD_CODES
|
|
2172
|
+
}));
|
|
2173
|
+
const nameOf = (id, data) => {
|
|
2174
|
+
return (data?.current)?.item?.name ?? sections.find((section) => section.id === id)?.name ?? "section";
|
|
2175
|
+
};
|
|
2176
|
+
const positionOf = (id) => sections.findIndex((section) => section.id === id) + 1;
|
|
2177
|
+
const announcements = {
|
|
2178
|
+
onDragStart: ({ active }) => `Picked up ${nameOf(active.id, active.data)}.`,
|
|
2179
|
+
onDragOver: ({ active, over }) => over && positionOf(over.id) > 0 ? `${nameOf(active.id, active.data)} moved to position ${positionOf(over.id)} of ${sections.length}.` : `${nameOf(active.id, active.data)} is no longer over a position.`,
|
|
2180
|
+
onDragEnd: ({ active, over }) => over && positionOf(over.id) > 0 ? `${nameOf(active.id, active.data)} dropped at position ${positionOf(over.id)} of ${sections.length}.` : `${nameOf(active.id, active.data)} dropped.`,
|
|
2181
|
+
onDragCancel: ({ active }) => `Moving ${nameOf(active.id, active.data)} was cancelled.`
|
|
2182
|
+
};
|
|
1708
2183
|
const value = _value === void 0 ? code : _value;
|
|
1709
|
-
const
|
|
2184
|
+
const reportError = onEditError ?? toastEditError;
|
|
2185
|
+
const reportErrorRef = useRef(reportError);
|
|
2186
|
+
useEffect(() => {
|
|
2187
|
+
reportErrorRef.current = reportError;
|
|
2188
|
+
});
|
|
2189
|
+
const onBlockedEdit = useCallback((problem) => {
|
|
2190
|
+
if (problem.reason === "parse-error") reportErrorRef.current(blockedEditError(problem.error));
|
|
2191
|
+
}, []);
|
|
2192
|
+
const { sections, previews, selectedId, selectedItem, selectedIndex, select, selectOnly, clearSelection, add: addItem, remove, copy: onCopy, move: moveSection, reorder, patch, getCommittedSection, problem, stale } = useSectionDocument(value, _onChange, {
|
|
2193
|
+
containerId,
|
|
2194
|
+
sectionNameFallback,
|
|
2195
|
+
onBlockedEdit
|
|
2196
|
+
});
|
|
2197
|
+
const missingContainer = problem?.reason === "container-not-found" ? problem.containerId : null;
|
|
1710
2198
|
const onDragStart = (_) => {};
|
|
1711
2199
|
const onDragOver = (_e) => {};
|
|
1712
2200
|
const onDragEnd = (event) => {
|
|
@@ -1721,11 +2209,110 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1721
2209
|
if (active.id !== over.id && sections.some((s) => s.id === active.id)) reorder(String(active.id), String(over.id));
|
|
1722
2210
|
};
|
|
1723
2211
|
const onSelect = (id) => select(id);
|
|
2212
|
+
const [inspecting, setInspecting] = useState(false);
|
|
2213
|
+
const [picked, setPicked] = useState(null);
|
|
2214
|
+
const [highlight, setHighlight] = useState(null);
|
|
2215
|
+
if (picked && picked.sectionId !== selectedId) setPicked(null);
|
|
2216
|
+
const stopInspecting = useCallback(() => {
|
|
2217
|
+
setInspecting(false);
|
|
2218
|
+
setHighlight(null);
|
|
2219
|
+
}, []);
|
|
2220
|
+
useEffect(() => {
|
|
2221
|
+
if (!inspecting) return;
|
|
2222
|
+
const onKeyDown = (e) => {
|
|
2223
|
+
if (e.key === "Escape") stopInspecting();
|
|
2224
|
+
};
|
|
2225
|
+
window.addEventListener("keydown", onKeyDown);
|
|
2226
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
2227
|
+
}, [inspecting, stopInspecting]);
|
|
2228
|
+
const inspector = {
|
|
2229
|
+
active: inspecting,
|
|
2230
|
+
activate: () => setInspecting(true),
|
|
2231
|
+
deactivate: stopInspecting,
|
|
2232
|
+
toggle: () => inspecting ? stopInspecting() : setInspecting(true),
|
|
2233
|
+
picked
|
|
2234
|
+
};
|
|
2235
|
+
const onInspectMove = (section, overlay, x, y) => {
|
|
2236
|
+
const element = pickElement(section, overlay, x, y);
|
|
2237
|
+
setHighlight(element ? viewportRect(element) : null);
|
|
2238
|
+
};
|
|
2239
|
+
const onInspectPick = (sectionId, section, overlay, x, y) => {
|
|
2240
|
+
const id = pickElement(section, overlay, x, y)?.getAttribute(DATA_ATTR.ID);
|
|
2241
|
+
if (!id) return;
|
|
2242
|
+
const pick = {
|
|
2243
|
+
id,
|
|
2244
|
+
sectionId
|
|
2245
|
+
};
|
|
2246
|
+
selectOnly(sectionId);
|
|
2247
|
+
setPicked(pick);
|
|
2248
|
+
stopInspecting();
|
|
2249
|
+
onNodePick?.(pick);
|
|
2250
|
+
};
|
|
2251
|
+
const removeRef = useRef(remove);
|
|
2252
|
+
useEffect(() => {
|
|
2253
|
+
removeRef.current = remove;
|
|
2254
|
+
});
|
|
2255
|
+
const requestDelete = (id, onDeleted) => {
|
|
2256
|
+
const section = sections.find((candidate) => candidate.id === id);
|
|
2257
|
+
if (!onBeforeDelete || !section) {
|
|
2258
|
+
remove(id);
|
|
2259
|
+
onDeleted?.();
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
const decide = (allowed) => {
|
|
2263
|
+
if (allowed) {
|
|
2264
|
+
removeRef.current(id);
|
|
2265
|
+
onDeleted?.();
|
|
2266
|
+
}
|
|
2267
|
+
};
|
|
2268
|
+
try {
|
|
2269
|
+
const answer = onBeforeDelete(section);
|
|
2270
|
+
if (typeof answer === "boolean") {
|
|
2271
|
+
decide(answer);
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
answer.then(decide, (error) => {
|
|
2275
|
+
console.error("onBeforeDelete rejected; the section was kept", error);
|
|
2276
|
+
});
|
|
2277
|
+
} catch (error) {
|
|
2278
|
+
console.error("onBeforeDelete threw; the section was kept", error);
|
|
2279
|
+
}
|
|
2280
|
+
};
|
|
2281
|
+
const onDelete = (id) => requestDelete(id);
|
|
2282
|
+
const [sectionNodes] = useState(createNodeRegistry);
|
|
2283
|
+
const focusSection = (id) => sectionNodes.get(id)?.focus();
|
|
2284
|
+
const onNavigate = (fromId, to) => {
|
|
2285
|
+
const from = sections.findIndex((section) => section.id === fromId);
|
|
2286
|
+
const index = to === "first" ? 0 : to === "last" ? sections.length - 1 : from + (to === "next" ? 1 : -1);
|
|
2287
|
+
const target = sections[index];
|
|
2288
|
+
if (from < 0 || !target || target.id === fromId) return;
|
|
2289
|
+
selectOnly(target.id);
|
|
2290
|
+
focusSection(target.id);
|
|
2291
|
+
};
|
|
2292
|
+
const onDeleteKey = (id) => {
|
|
2293
|
+
const index = sections.findIndex((section) => section.id === id);
|
|
2294
|
+
const neighbor = sections[index + 1] ?? sections[index - 1];
|
|
2295
|
+
requestDelete(id, () => {
|
|
2296
|
+
if (neighbor) focusSection(neighbor.id);
|
|
2297
|
+
});
|
|
2298
|
+
};
|
|
2299
|
+
useEffect(() => {
|
|
2300
|
+
if (selectedId === null) return;
|
|
2301
|
+
const frame = requestAnimationFrame(() => {
|
|
2302
|
+
sectionNodes.get(selectedId)?.scrollIntoView?.({ block: "nearest" });
|
|
2303
|
+
});
|
|
2304
|
+
return () => cancelAnimationFrame(frame);
|
|
2305
|
+
}, [
|
|
2306
|
+
sectionNodes,
|
|
2307
|
+
selectedId,
|
|
2308
|
+
selectedIndex
|
|
2309
|
+
]);
|
|
1724
2310
|
const onChange = (next) => {
|
|
1725
2311
|
if (!next.id) return;
|
|
1726
2312
|
patch(next);
|
|
1727
2313
|
};
|
|
1728
2314
|
const selectedCode = selectedItem?.code;
|
|
2315
|
+
const selectedSectionId = selectedItem?.id;
|
|
1729
2316
|
const { fields, updatedCode, parseError } = useMemo(() => {
|
|
1730
2317
|
if (!selectedCode) return {
|
|
1731
2318
|
fields: [],
|
|
@@ -1733,9 +2320,9 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1733
2320
|
parseError: null
|
|
1734
2321
|
};
|
|
1735
2322
|
try {
|
|
1736
|
-
const updated =
|
|
2323
|
+
const updated = fillIdsFrom(selectedCode, selectedSectionId ?? "");
|
|
1737
2324
|
return {
|
|
1738
|
-
fields: extract(updated)
|
|
2325
|
+
fields: extract(updated),
|
|
1739
2326
|
updatedCode: updated !== selectedCode ? updated : selectedCode,
|
|
1740
2327
|
parseError: null
|
|
1741
2328
|
};
|
|
@@ -1747,12 +2334,17 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1747
2334
|
parseError: { error: e }
|
|
1748
2335
|
};
|
|
1749
2336
|
}
|
|
1750
|
-
}, [selectedCode]);
|
|
1751
|
-
const reportError = onEditError ?? toastEditError;
|
|
1752
|
-
const reportErrorRef = useRef(reportError);
|
|
2337
|
+
}, [selectedCode, selectedSectionId]);
|
|
1753
2338
|
useEffect(() => {
|
|
1754
|
-
reportErrorRef.current
|
|
1755
|
-
|
|
2339
|
+
if (missingContainer !== null) reportErrorRef.current({
|
|
2340
|
+
type: "parse",
|
|
2341
|
+
target: "document",
|
|
2342
|
+
reason: "container-not-found",
|
|
2343
|
+
containerId: missingContainer,
|
|
2344
|
+
title: `No #${missingContainer} element in the document`,
|
|
2345
|
+
description: `Sections are the <section> elements inside the element with id="${missingContainer}". Start from createDocument(), or set containerId to match your document.`
|
|
2346
|
+
});
|
|
2347
|
+
}, [missingContainer]);
|
|
1756
2348
|
useEffect(() => {
|
|
1757
2349
|
if (parseError) reportErrorRef.current({
|
|
1758
2350
|
type: "parse",
|
|
@@ -1762,9 +2354,21 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1762
2354
|
description: "Check the console for details."
|
|
1763
2355
|
});
|
|
1764
2356
|
}, [parseError]);
|
|
1765
|
-
const
|
|
1766
|
-
|
|
2357
|
+
const commitChanges = (changes) => {
|
|
2358
|
+
if (changes.length === 0) return;
|
|
2359
|
+
if (stale && problem?.reason === "parse-error") {
|
|
2360
|
+
reportError(blockedEditError(problem.error));
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
const committed = selectedItem && getCommittedSection(selectedItem.id)?.code;
|
|
2364
|
+
const result = updateAll(committed && committed !== selectedCode ? committed : updatedCode, changes.map(({ id, label, property, value: changeValue }) => ({
|
|
2365
|
+
dataId: id,
|
|
2366
|
+
label,
|
|
2367
|
+
property,
|
|
2368
|
+
value: changeValue
|
|
2369
|
+
})));
|
|
1767
2370
|
if (!result.success) {
|
|
2371
|
+
const { id, label, property } = changes[result.index];
|
|
1768
2372
|
reportError({
|
|
1769
2373
|
type: "update",
|
|
1770
2374
|
id,
|
|
@@ -1780,6 +2384,7 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1780
2384
|
code: result.code
|
|
1781
2385
|
});
|
|
1782
2386
|
};
|
|
2387
|
+
const onFieldChange = (change) => commitChanges([change]);
|
|
1783
2388
|
const bindingFields = useMemo(() => fields.flatMap((node) => resolvePanelBindings(node)?.bindings ?? []), [fields]);
|
|
1784
2389
|
const bindings = withPanelCommit(bindingFields, onFieldChange);
|
|
1785
2390
|
useEffect(() => {
|
|
@@ -1801,13 +2406,35 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1801
2406
|
canMoveUp: selectedIndex > 0,
|
|
1802
2407
|
canMoveDown: selectedIndex >= 0 && selectedIndex < sections.length - 1,
|
|
1803
2408
|
bindings,
|
|
1804
|
-
onNodeChange: onFieldChange
|
|
2409
|
+
onNodeChange: onFieldChange,
|
|
2410
|
+
onNodesChange: commitChanges,
|
|
2411
|
+
readOnly: stale
|
|
1805
2412
|
};
|
|
1806
|
-
const canvas = /* @__PURE__ */
|
|
2413
|
+
const canvas = /* @__PURE__ */ jsxs(Fragment, { children: [stale && /* @__PURE__ */ jsx("div", {
|
|
2414
|
+
role: "status",
|
|
2415
|
+
className: cn("sticky top-0 z-70", "border-b border-amber-200 bg-amber-50 px-3 py-2", "text-sm text-amber-900"),
|
|
2416
|
+
children: "Showing the last version that parsed. Fix the syntax error in the code to edit here again."
|
|
2417
|
+
}), /* @__PURE__ */ jsx(Droppable, {
|
|
1807
2418
|
className: cn(!sections.length && "h-full"),
|
|
1808
2419
|
children: !sections.length ? /* @__PURE__ */ jsx("div", {
|
|
1809
2420
|
className: cn("flex items-center justify-center", "h-full", "text-gray-500"),
|
|
1810
|
-
children: /* @__PURE__ */ jsxs(Space, {
|
|
2421
|
+
children: problem?.reason === "parse-error" ? /* @__PURE__ */ jsxs(Space, {
|
|
2422
|
+
orientation: "vertical",
|
|
2423
|
+
align: "center",
|
|
2424
|
+
children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: "The document has a syntax error" }), /* @__PURE__ */ jsx(Typography.Text, { children: "Fix it in the code to see its sections" })]
|
|
2425
|
+
}) : missingContainer !== null ? /* @__PURE__ */ jsxs(Space, {
|
|
2426
|
+
orientation: "vertical",
|
|
2427
|
+
align: "center",
|
|
2428
|
+
children: [/* @__PURE__ */ jsxs(Typography.Paragraph, { children: [
|
|
2429
|
+
"No #",
|
|
2430
|
+
missingContainer,
|
|
2431
|
+
" element in the document"
|
|
2432
|
+
] }), /* @__PURE__ */ jsxs(Typography.Text, { children: [
|
|
2433
|
+
"Sections go inside the element with id=\"",
|
|
2434
|
+
missingContainer,
|
|
2435
|
+
"\""
|
|
2436
|
+
] })]
|
|
2437
|
+
}) : /* @__PURE__ */ jsxs(Space, {
|
|
1811
2438
|
orientation: "vertical",
|
|
1812
2439
|
align: "center",
|
|
1813
2440
|
children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: "No sections available" }), /* @__PURE__ */ jsx(Typography.Text, { children: isMobile ? "Tap a component to add it" : "Drag a component from the left to add it" })]
|
|
@@ -1822,6 +2449,13 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1822
2449
|
onClick: () => onSelect(section.id),
|
|
1823
2450
|
onDelete,
|
|
1824
2451
|
onCopy,
|
|
2452
|
+
nodeRef: sectionNodes.register(section.id),
|
|
2453
|
+
onNavigate: (to) => onNavigate(section.id, to),
|
|
2454
|
+
onDeleteKey: () => onDeleteKey(section.id),
|
|
2455
|
+
inspecting,
|
|
2456
|
+
onInspectMove,
|
|
2457
|
+
onInspectPick: (...args) => onInspectPick(section.id, ...args),
|
|
2458
|
+
onInspectLeave: () => setHighlight(null),
|
|
1825
2459
|
children: /* @__PURE__ */ jsx(renderer_default, {
|
|
1826
2460
|
preview: previews[index],
|
|
1827
2461
|
sectionId: section.id,
|
|
@@ -1836,12 +2470,16 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1836
2470
|
})
|
|
1837
2471
|
}, section.id))
|
|
1838
2472
|
})
|
|
1839
|
-
}) });
|
|
2473
|
+
})] });
|
|
1840
2474
|
return /* @__PURE__ */ jsx(SectionFallbackContext.Provider, {
|
|
1841
2475
|
value: renderSectionFallback,
|
|
1842
2476
|
children: /* @__PURE__ */ jsxs(DndContext, {
|
|
1843
2477
|
sensors,
|
|
1844
2478
|
collisionDetection: closestCenter,
|
|
2479
|
+
accessibility: {
|
|
2480
|
+
announcements,
|
|
2481
|
+
screenReaderInstructions: SCREEN_READER_INSTRUCTIONS
|
|
2482
|
+
},
|
|
1845
2483
|
modifiers: [conditionalModifiers],
|
|
1846
2484
|
onDragStart,
|
|
1847
2485
|
onDragOver,
|
|
@@ -1849,12 +2487,12 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1849
2487
|
children: [/* @__PURE__ */ jsx("div", {
|
|
1850
2488
|
className: cn("relative flex h-full w-full", className),
|
|
1851
2489
|
...restProps,
|
|
1852
|
-
children: /* @__PURE__ */
|
|
2490
|
+
children: /* @__PURE__ */ jsxs(DndEditOptionsContext.Provider, {
|
|
1853
2491
|
value: {
|
|
1854
2492
|
renderField,
|
|
1855
2493
|
reportError
|
|
1856
2494
|
},
|
|
1857
|
-
children: /* @__PURE__ */ jsx(DndRegionContext.Provider, {
|
|
2495
|
+
children: [/* @__PURE__ */ jsx(DndRegionContext.Provider, {
|
|
1858
2496
|
value: {
|
|
1859
2497
|
palette,
|
|
1860
2498
|
panel,
|
|
@@ -1863,10 +2501,14 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1863
2501
|
selectedId,
|
|
1864
2502
|
clearSelection,
|
|
1865
2503
|
paletteOpen: mobilePaletteOpen,
|
|
1866
|
-
setPaletteOpen: setMobilePaletteOpen
|
|
2504
|
+
setPaletteOpen: setMobilePaletteOpen,
|
|
2505
|
+
documentError: problem?.reason ?? null
|
|
1867
2506
|
},
|
|
1868
|
-
children:
|
|
1869
|
-
|
|
2507
|
+
children: /* @__PURE__ */ jsx(DndInspectorContext.Provider, {
|
|
2508
|
+
value: inspector,
|
|
2509
|
+
children: Children.toArray(children).length ? children : /* @__PURE__ */ jsx(Layout, {})
|
|
2510
|
+
})
|
|
2511
|
+
}), /* @__PURE__ */ jsx(InspectorHighlight, { rect: inspecting ? highlight : null })]
|
|
1870
2512
|
})
|
|
1871
2513
|
}), /* @__PURE__ */ jsx(DragOverlay, { children: /* @__PURE__ */ jsx(Overlay, {
|
|
1872
2514
|
sections,
|
|
@@ -1876,7 +2518,8 @@ const Dnd$1 = ({ value: _value, props, modules: _modules = NO_MODULES, onChange:
|
|
|
1876
2518
|
modules,
|
|
1877
2519
|
frame,
|
|
1878
2520
|
dynamicTailwind,
|
|
1879
|
-
...props
|
|
2521
|
+
...props,
|
|
2522
|
+
containerId
|
|
1880
2523
|
}
|
|
1881
2524
|
}) })]
|
|
1882
2525
|
})
|
|
@@ -1891,7 +2534,11 @@ Dnd.Palette = Palette;
|
|
|
1891
2534
|
Dnd.Canvas = Canvas;
|
|
1892
2535
|
Dnd.Panel = Panel;
|
|
1893
2536
|
Dnd.Layout = Layout;
|
|
2537
|
+
/** @deprecated Renamed to `useDndItems`. */
|
|
2538
|
+
const useItemsEditor = useDndItems;
|
|
2539
|
+
/** @deprecated Renamed to `useDndChildren`. */
|
|
2540
|
+
const useChildrenEditor = useDndChildren;
|
|
1894
2541
|
//#endregion
|
|
1895
|
-
export {
|
|
2542
|
+
export { useDndPanel as _, Layout as a, Field as c, isStructuralFieldKind as d, useDndChildren as f, useDndPalette as g, useDndLayout as h, Canvas as i, useDndItems as l, withPanelCommit as m, useChildrenEditor as n, Palette as o, resolvePanelBindings as p, useItemsEditor as r, Panel as s, Dnd as t, getFieldKind as u, DraggableItem as v, useDndInspector as y };
|
|
1896
2543
|
|
|
1897
|
-
//# sourceMappingURL=dnd-
|
|
2544
|
+
//# sourceMappingURL=dnd-Z1KS16Fy.js.map
|