@jbpark/live-editor 4.6.0 → 5.0.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.
Files changed (83) hide show
  1. package/README.ko.md +2 -2
  2. package/README.md +2 -2
  3. package/dist/{boundary-BLB80tyw.js → boundary-6_nTsWxP.js} +2 -2
  4. package/dist/{boundary-BLB80tyw.js.map → boundary-6_nTsWxP.js.map} +1 -1
  5. package/dist/{compile-Dxas-D05.js → compile-DwOyWrmI.js} +8 -7
  6. package/dist/compile-DwOyWrmI.js.map +1 -0
  7. package/dist/constants-D-XKuEY2.js +58 -0
  8. package/dist/constants-D-XKuEY2.js.map +1 -0
  9. package/dist/{context-B8MzP7_p.js → context-DDR1xVKo.js} +3 -3
  10. package/dist/{context-B8MzP7_p.js.map → context-DDR1xVKo.js.map} +1 -1
  11. package/dist/{core-Bgfek364.js → core-CKazr4tT.js} +2 -2
  12. package/dist/core-CKazr4tT.js.map +1 -0
  13. package/dist/{detect-typescript-Dr_oLniM.js → detect-typescript-CoAO-QUJ.js} +2 -2
  14. package/dist/{detect-typescript-Dr_oLniM.js.map → detect-typescript-CoAO-QUJ.js.map} +1 -1
  15. package/dist/dnd/index.d.ts +2 -2
  16. package/dist/dnd/index.js +2 -2
  17. package/dist/dnd-BlC1gqUy.js +4446 -0
  18. package/dist/dnd-BlC1gqUy.js.map +1 -0
  19. package/dist/document-BQhb7YR8.d.ts +17 -0
  20. package/dist/document-DcwIEHRV.js +34077 -0
  21. package/dist/document-DcwIEHRV.js.map +1 -0
  22. package/dist/editor/index.js +2 -2
  23. package/dist/{editor-CMElfHer.js → editor-Bty5XzpV.js} +2 -2
  24. package/dist/{editor-CMElfHer.js.map → editor-Bty5XzpV.js.map} +1 -1
  25. package/dist/error/index.d.ts +1 -1
  26. package/dist/error/index.js +2 -2
  27. package/dist/{error-CN_GCnXB.js → error-DyYx2NQ2.js} +17 -31
  28. package/dist/error-DyYx2NQ2.js.map +1 -0
  29. package/dist/{index-C69Lv-jR.d.ts → index-0VB5C3Jk.d.ts} +28 -50
  30. package/dist/{index-DpDU1IAw.d.ts → index-92L7F440.d.ts} +2 -2
  31. package/dist/index-C8h8TcIl.d.ts +15 -0
  32. package/dist/{index-DQkL37eL.d.ts → index-Cl79M7ZJ.d.ts} +6 -4
  33. package/dist/{index-CRwEhJ7w.d.ts → index-kKUjo9lr.d.ts} +2 -2
  34. package/dist/{index-DrWtSKXu.d.ts → index-tAiuHZsC.d.ts} +2 -2
  35. package/dist/index.d.ts +9 -9
  36. package/dist/index.js +6 -6
  37. package/dist/index.js.map +1 -1
  38. package/dist/{messages-CSrMI4Rm.js → messages-Bt5WXKTE.js} +5 -2
  39. package/dist/messages-Bt5WXKTE.js.map +1 -0
  40. package/dist/{messages-IBPs4TkZ.d.ts → messages-Yb3b7YJt.d.ts} +5 -2
  41. package/dist/preview/index.d.ts +1 -1
  42. package/dist/preview/index.js +2 -2
  43. package/dist/{preview-CtyG5UbH.js → preview-DC0pyOdu.js} +4 -4
  44. package/dist/preview-DC0pyOdu.js.map +1 -0
  45. package/dist/provider/index.d.ts +2 -2
  46. package/dist/provider/index.js +2 -2
  47. package/dist/{sections-BYWOBISr.js → sections-4uirsXRU.js} +4 -7
  48. package/dist/sections-4uirsXRU.js.map +1 -0
  49. package/dist/style.css +776 -346
  50. package/dist/{validate-DB59Z_Nx.d.ts → types-Czps1Txy.d.ts} +12 -28
  51. package/dist/update-2PJbwoS8.d.ts +68 -0
  52. package/dist/{items-B6OXxcLG.js → update-DNInQSM7.js} +261 -512
  53. package/dist/update-DNInQSM7.js.map +1 -0
  54. package/dist/{use-dynamic-tailwind-BP1toJkW.js → use-dynamic-tailwind-CSYKXDmX.js} +301 -273
  55. package/dist/use-dynamic-tailwind-CSYKXDmX.js.map +1 -0
  56. package/dist/utils/ast/index.d.ts +47 -4
  57. package/dist/utils/ast/index.js +15 -77
  58. package/dist/utils/ast/index.js.map +1 -1
  59. package/dist/utils/index.d.ts +13 -32
  60. package/dist/utils/index.js +4 -26
  61. package/dist/validate-CZz30Pnb.js +122 -0
  62. package/dist/validate-CZz30Pnb.js.map +1 -0
  63. package/dist/validate-CiA1LF6S.d.ts +33 -0
  64. package/package.json +13 -15
  65. package/dist/compile-Dxas-D05.js.map +0 -1
  66. package/dist/constants-DU17Tdwo.js +0 -1459
  67. package/dist/constants-DU17Tdwo.js.map +0 -1
  68. package/dist/core-Bgfek364.js.map +0 -1
  69. package/dist/dnd-UoJILA75.js +0 -2756
  70. package/dist/dnd-UoJILA75.js.map +0 -1
  71. package/dist/document-CGbe6bSV.js +0 -38667
  72. package/dist/document-CGbe6bSV.js.map +0 -1
  73. package/dist/document-Ct4jW78J.d.ts +0 -1553
  74. package/dist/error-CN_GCnXB.js.map +0 -1
  75. package/dist/index-DKYUxi-E.d.ts +0 -227
  76. package/dist/items-B6OXxcLG.js.map +0 -1
  77. package/dist/messages-CSrMI4Rm.js.map +0 -1
  78. package/dist/preview-CtyG5UbH.js.map +0 -1
  79. package/dist/sections-BYWOBISr.js.map +0 -1
  80. package/dist/use-dynamic-tailwind-BP1toJkW.js.map +0 -1
  81. package/dist/utils/index.js.map +0 -1
  82. package/dist/validate-iLo8IONY.js +0 -64
  83. package/dist/validate-iLo8IONY.js.map +0 -1
@@ -1,2756 +0,0 @@
1
- import { S as require_lib, c as inspectDocument, h as replaceIds, m as fillIdsFrom, n as createSectionPreviewCache, r as fillSectionIds, w as __toESM, x as nanoid } from "./document-CGbe6bSV.js";
2
- import { c as RESERVED_BINDING_PROPERTIES, o as PALETTE_SECTIONS, r as DATA_ATTR, t as BINDING_PROP } from "./constants-DU17Tdwo.js";
3
- import { n as validateBindingValue } from "./validate-iLo8IONY.js";
4
- import { i as useLiveMessages } from "./messages-CSrMI4Rm.js";
5
- import { i as usePreview } from "./states-Ci1AvoQ9.js";
6
- import { A as extractNodeValue, C as getStructuredValue, D as readNodeBindings, M as parseArrayExpression, N as parseValue, b as getCurrentValue, c as updateArrayItemValue, f as updateAll, g as moveSelectedIndices, h as getChildrenSignatures, i as moveArrayItems, j as extractObjectProperties, k as canLosslesslyEvaluateSource, l as canStructurallyEditArray, n as duplicateArrayItems, o as removeArrayItems, r as moveArrayItem, s as updateArrayItemProperty, t as appendArrayItem, v as extract, y as findEditableChildren } from "./items-B6OXxcLG.js";
7
- import { t as cn } from "./cn-CAnad2vL.js";
8
- import { o as preloadScripts } from "./compile-Dxas-D05.js";
9
- import { a as useStableModules, i as Frame, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-BP1toJkW.js";
10
- import { i as generateSection, o as replaceSections, r as extractSections } from "./sections-BYWOBISr.js";
11
- import { n as Error$1, t as ErrorBoundary } from "./boundary-BLB80tyw.js";
12
- import { t as Core } from "./core-Bgfek364.js";
13
- import { Children, createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
14
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
- import { DndContext, DragOverlay, KeyboardSensor, MouseSensor, PointerSensor, TouchSensor, closestCenter, useDndContext, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
16
- import { restrictToParentElement, restrictToVerticalAxis } from "@dnd-kit/modifiers";
17
- import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
18
- import { Button, Card, Checkbox, ColorPicker, DatePicker, Drawer, Input, RichTextEditor, Select, Space, Splitter, Toast, Typography } from "@jbpark/ui-kit";
19
- import { useDebounce, useMultiSelect, useResponsiveSize } from "@jbpark/use-hooks";
20
- import { CSS as CSS$1 } from "@dnd-kit/utilities";
21
- import { ArrowDown, ArrowUp, ChevronDown, ChevronRight, ChevronUp, Copy, Crosshair, GripVertical, LayoutGrid, Plus, Trash, X } from "lucide-react";
22
- //#region src/components/dnd/palette/palette-drag.ts
23
- var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
24
- const PALETTE_SECTION = "palette-section";
25
- const paletteDragData = (section) => ({
26
- type: PALETTE_SECTION,
27
- section
28
- });
29
- const paletteSectionOf = (data) => {
30
- const current = data?.current;
31
- return current?.type === PALETTE_SECTION ? current.section : void 0;
32
- };
33
- //#endregion
34
- //#region src/components/dnd/canvas/droppable.tsx
35
- const Droppable = ({ children, className }) => {
36
- const { setNodeRef, isOver, active } = useDroppable({ id: "sortable-area" });
37
- const { setNodeRef: setBottomRef, isOver: isBottomOver } = useDroppable({ id: "sortable-area-bottom" });
38
- const isPaletteSectionDragging = !!paletteSectionOf(active?.data);
39
- const shouldHighlight = isOver && isPaletteSectionDragging;
40
- const shouldHighlightBottom = isBottomOver && isPaletteSectionDragging;
41
- const messages = useLiveMessages();
42
- return /* @__PURE__ */ jsxs("div", {
43
- ref: setNodeRef,
44
- className: cn("min-h-full", "border-2 border-dashed p-1", shouldHighlight ? "border-blue-300 bg-blue-50" : "border-gray-200", className),
45
- children: [children, isPaletteSectionDragging && /* @__PURE__ */ jsx("div", {
46
- ref: setBottomRef,
47
- className: cn("mt-2 min-h-24", "rounded-lg border-2 border-dashed", "transition-all duration-200", "flex items-center justify-center", shouldHighlightBottom ? "border-blue-400 bg-blue-100" : "border-gray-300 bg-gray-50"),
48
- children: /* @__PURE__ */ jsx(Typography.Text, {
49
- className: "text-sm text-gray-500",
50
- children: shouldHighlightBottom ? messages.canvas.dropHere : messages.canvas.dropAtBottom
51
- })
52
- })]
53
- });
54
- };
55
- //#endregion
56
- //#region src/components/dnd/canvas/inspector-highlight.tsx
57
- const InspectorHighlight = ({ rect }) => {
58
- const ref = useRef(null);
59
- useLayoutEffect(() => {
60
- const box = ref.current;
61
- const container = box?.closest("[data-frame-container]");
62
- if (!box || !rect || !container) return;
63
- const outer = container.getBoundingClientRect();
64
- box.style.left = `${rect.left - outer.left - container.clientLeft + container.scrollLeft}px`;
65
- box.style.top = `${rect.top - outer.top - container.clientTop + container.scrollTop}px`;
66
- box.style.width = `${rect.width}px`;
67
- box.style.height = `${rect.height}px`;
68
- }, [rect]);
69
- return rect ? /* @__PURE__ */ jsx("div", {
70
- ref,
71
- "aria-hidden": true,
72
- "data-dnd-inspector-highlight": true,
73
- style: {
74
- position: "absolute",
75
- pointerEvents: "none",
76
- outline: "2px solid #3b82f6",
77
- outlineOffset: -1,
78
- background: "rgba(59, 130, 246, 0.12)",
79
- zIndex: 65
80
- }
81
- }) : null;
82
- };
83
- //#endregion
84
- //#region src/components/dnd/palette/draggable.tsx
85
- const DraggableItem = ({ item, children }) => {
86
- const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
87
- id: item.id,
88
- data: paletteDragData(item)
89
- });
90
- return children({
91
- ref: setNodeRef,
92
- dragProps: {
93
- ...listeners,
94
- ...attributes
95
- },
96
- isDragging
97
- });
98
- };
99
- const DefaultDraggableItem = ({ item, onAdd, tapToAdd = false }) => /* @__PURE__ */ jsx(DraggableItem, {
100
- item,
101
- children: ({ ref, dragProps, isDragging }) => /* @__PURE__ */ jsx(Card, {
102
- ref,
103
- style: { opacity: isDragging ? .5 : 1 },
104
- ...dragProps,
105
- 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"),
106
- onClick: tapToAdd && onAdd ? () => onAdd(item) : void 0,
107
- onDoubleClick: onAdd ? () => onAdd(item) : void 0,
108
- onKeyDown: (event) => {
109
- dragProps.onKeyDown?.(event);
110
- if (onAdd && event.key === "Enter" && event.target === event.currentTarget && !event.defaultPrevented) {
111
- event.preventDefault();
112
- onAdd(item);
113
- }
114
- },
115
- children: item.name
116
- })
117
- });
118
- //#endregion
119
- //#region src/components/dnd/canvas/section-fallback-context.ts
120
- const SectionFallbackContext = createContext(void 0);
121
- //#endregion
122
- //#region src/components/dnd/canvas/section-fallback.tsx
123
- const ConsumerFallback = ({ render, args, builtin }) => {
124
- const node = render(args);
125
- return node === void 0 ? builtin : /* @__PURE__ */ jsx(Fragment, { children: node });
126
- };
127
- const SectionFallback = ({ args, builtin }) => {
128
- const render = useContext(SectionFallbackContext);
129
- if (!render) return builtin;
130
- return /* @__PURE__ */ jsx(ErrorBoundary, {
131
- fallback: () => builtin,
132
- children: /* @__PURE__ */ jsx(ConsumerFallback, {
133
- render,
134
- args,
135
- builtin
136
- })
137
- });
138
- };
139
- //#endregion
140
- //#region src/components/dnd/canvas/renderer.tsx
141
- const Renderer = ({ preview, sectionId = "", sectionName = "", sectionCode = preview, forceFallback = false, headers, modules, frame, dynamicTailwind = false, provider }) => {
142
- const module = useCompiledModule(forceFallback ? "" : preview, modules);
143
- const section = {
144
- id: sectionId,
145
- name: sectionName,
146
- code: sectionCode
147
- };
148
- const messages = useLiveMessages();
149
- const { ref: wrapperRef, css: dynamicCSS } = useDynamicTailwind(preview, dynamicTailwind && !forceFallback);
150
- const renderProvider = (component) => {
151
- return provider ? provider(component) : component;
152
- };
153
- if (forceFallback) return /* @__PURE__ */ jsx(SectionFallback, {
154
- args: {
155
- section,
156
- reason: "forced"
157
- },
158
- builtin: /* @__PURE__ */ jsx(Error$1, {
159
- message: messages.errors.sectionNotRendered,
160
- title: messages.errors.sectionUnavailable
161
- })
162
- });
163
- if (module?.error) return /* @__PURE__ */ jsx(SectionFallback, {
164
- args: {
165
- section,
166
- reason: "compile",
167
- message: module.error
168
- },
169
- builtin: /* @__PURE__ */ jsx(Error$1, {
170
- message: module.error,
171
- title: messages.errors.compile
172
- })
173
- });
174
- const Component = module?.exports?.default;
175
- if (!Component) return null;
176
- return /* @__PURE__ */ jsx(Frame, {
177
- ...frame,
178
- autoHeight: true,
179
- children: (container) => /* @__PURE__ */ jsx("div", {
180
- ref: wrapperRef,
181
- className: "w-full overflow-x-hidden",
182
- "data-editor-mode": true,
183
- children: /* @__PURE__ */ jsx(ErrorBoundary, {
184
- resetKeys: [preview],
185
- fallback: (message, reset) => /* @__PURE__ */ jsx(SectionFallback, {
186
- args: {
187
- section,
188
- reason: "runtime",
189
- message
190
- },
191
- builtin: /* @__PURE__ */ jsx(Error$1, {
192
- message,
193
- onReset: reset,
194
- title: messages.errors.rendering
195
- })
196
- }),
197
- children: renderProvider(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Component, {
198
- headers,
199
- container
200
- }), dynamicTailwind && dynamicCSS && /* @__PURE__ */ jsx("style", { children: dynamicCSS })] }))
201
- })
202
- })
203
- });
204
- };
205
- const sameValue = (a, b) => {
206
- if (Object.is(a, b)) return true;
207
- if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((item, i) => Object.is(item, b[i]));
208
- if (a && b && typeof a === "object" && typeof b === "object" && Object.getPrototypeOf(a) === Object.prototype && Object.getPrototypeOf(b) === Object.prototype) {
209
- const aKeys = Object.keys(a);
210
- return aKeys.length === Object.keys(b).length && aKeys.every((key) => Object.is(a[key], b[key]));
211
- }
212
- return false;
213
- };
214
- const sameFrame = (a, b) => {
215
- if (a === b) return true;
216
- if (!a || !b) return false;
217
- return [.../* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])].every((key) => sameValue(a[key], b[key]));
218
- };
219
- const arePropsEqual = (prev, next) => {
220
- return [.../* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)])].every((key) => key === "frame" ? sameFrame(prev.frame, next.frame) : Object.is(prev[key], next[key]));
221
- };
222
- var renderer_default = memo(Renderer, arePropsEqual);
223
- //#endregion
224
- //#region src/components/dnd/canvas/sortable.tsx
225
- const NAVIGATION_KEYS = {
226
- ArrowUp: "previous",
227
- ArrowDown: "next",
228
- Home: "first",
229
- End: "last"
230
- };
231
- const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy: _onCopy, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, nodeRef, onNavigate, onDeleteKey, inspecting = false, onInspectMove, onInspectPick, onInspectLeave }) => {
232
- const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging, isOver, active } = useSortable({
233
- id,
234
- disabled: inspecting
235
- });
236
- const messages = useLiveMessages();
237
- const style = {
238
- transform: CSS$1.Transform.toString(transform),
239
- transition,
240
- opacity: isDragging ? .5 : 1,
241
- cursor: inspecting ? "crosshair" : "grab"
242
- };
243
- const isPaletteSectionOver = isOver && !!paletteSectionOf(active?.data);
244
- const onKeyDown = (e) => {
245
- listeners?.onKeyDown?.(e);
246
- if (e.target !== e.currentTarget || active || e.defaultPrevented) return;
247
- if (e.key === "Enter") {
248
- e.preventDefault();
249
- onClick?.();
250
- return;
251
- }
252
- const to = NAVIGATION_KEYS[e.key];
253
- if (to && onNavigate) {
254
- e.preventDefault();
255
- onNavigate(to);
256
- return;
257
- }
258
- if ((e.key === "Delete" || e.key === "Backspace") && onDeleteKey) {
259
- e.preventDefault();
260
- onDeleteKey();
261
- }
262
- };
263
- const setRefs = (node) => {
264
- setNodeRef(node);
265
- setActivatorNodeRef(node);
266
- nodeRef?.(node);
267
- };
268
- const onDelete = (e) => {
269
- e.stopPropagation();
270
- _onDelete?.(id);
271
- };
272
- const onCopy = (e) => {
273
- e.stopPropagation();
274
- _onCopy?.(id);
275
- };
276
- const press = (action) => (e) => {
277
- e.stopPropagation();
278
- action?.();
279
- };
280
- return /* @__PURE__ */ jsxs("div", {
281
- ref: setRefs,
282
- style,
283
- ...attributes,
284
- ...listeners,
285
- onKeyDown,
286
- onClick,
287
- className: cn("relative", selected && "z-10 outline-2 outline-offset-2 outline-blue-500", isPaletteSectionOver && "border-t-4 border-t-green-500"),
288
- children: [
289
- /* @__PURE__ */ jsx("div", {
290
- className: cn("absolute inset-0 z-50"),
291
- onPointerMove: inspecting ? (e) => onInspectMove?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY) : void 0,
292
- onPointerLeave: inspecting ? onInspectLeave : void 0,
293
- onClick: inspecting ? (e) => {
294
- e.stopPropagation();
295
- onInspectPick?.(e.currentTarget.parentElement, e.currentTarget, e.clientX, e.clientY);
296
- } : void 0
297
- }),
298
- children,
299
- selected && /* @__PURE__ */ jsxs(Space, {
300
- className: cn("absolute top-1 right-1 z-60"),
301
- children: [
302
- onMoveUp && /* @__PURE__ */ jsx(Button, {
303
- icon: /* @__PURE__ */ jsx(ChevronUp, {}),
304
- disabled: !canMoveUp,
305
- onClick: press(onMoveUp),
306
- "aria-label": messages.section.moveUp
307
- }),
308
- onMoveDown && /* @__PURE__ */ jsx(Button, {
309
- icon: /* @__PURE__ */ jsx(ChevronDown, {}),
310
- disabled: !canMoveDown,
311
- onClick: press(onMoveDown),
312
- "aria-label": messages.section.moveDown
313
- }),
314
- /* @__PURE__ */ jsx(Button, {
315
- icon: /* @__PURE__ */ jsx(Copy, {}),
316
- onClick: onCopy,
317
- "aria-label": messages.section.duplicate
318
- }),
319
- /* @__PURE__ */ jsx(Button, {
320
- danger: true,
321
- icon: /* @__PURE__ */ jsx(Trash, {}),
322
- onClick: onDelete,
323
- "aria-label": messages.section.delete
324
- })
325
- ]
326
- })
327
- ]
328
- });
329
- };
330
- //#endregion
331
- //#region src/components/dnd/canvas/overlay.tsx
332
- const Overlay = ({ sections, isForced, renderProps }) => {
333
- const { active } = useDndContext();
334
- if (!active) return null;
335
- const paletteSection = paletteSectionOf(active.data);
336
- if (paletteSection) return /* @__PURE__ */ jsx(DefaultDraggableItem, { item: paletteSection });
337
- const section = sections.find((s) => s.id === active.id);
338
- if (section) {
339
- const preview = generateSection(section.code, renderProps.fullCode, { containerId: renderProps.containerId });
340
- return /* @__PURE__ */ jsx(Sortable, {
341
- id: section.id,
342
- name: section.name,
343
- children: /* @__PURE__ */ jsx(renderer_default, {
344
- preview,
345
- sectionId: section.id,
346
- sectionName: section.name,
347
- sectionCode: section.code,
348
- forceFallback: isForced?.(section) ?? false,
349
- modules: renderProps.modules,
350
- frame: renderProps.frame,
351
- dynamicTailwind: renderProps.dynamicTailwind
352
- })
353
- });
354
- }
355
- return null;
356
- };
357
- //#endregion
358
- //#region src/components/dnd/edit-options.ts
359
- const toastEditError = (error) => {
360
- Toast.error(error.title, error.description ? { description: error.description } : void 0);
361
- };
362
- const DndEditOptionsContext = createContext({ reportError: toastEditError });
363
- const useDndEditOptions = () => useContext(DndEditOptionsContext);
364
- //#endregion
365
- //#region src/components/dnd/inspector.ts
366
- const DndInspectorContext = createContext(null);
367
- const useDndInspector = () => {
368
- const inspector = useContext(DndInspectorContext);
369
- if (!inspector) throw new Error("useDndInspector() must be used inside <Live.Dnd>.");
370
- return inspector;
371
- };
372
- const outside = (root) => (element) => element.getRootNode() !== root;
373
- const deepestAt = (root, x, y, skip) => {
374
- const element = root.elementsFromPoint(x, y).find((candidate) => !skip(candidate));
375
- if (!element) return null;
376
- if (element instanceof HTMLIFrameElement) {
377
- const document = element.contentDocument;
378
- if (!document) return element;
379
- const rect = element.getBoundingClientRect();
380
- return deepestAt(document, x - rect.left, y - rect.top, outside(document)) ?? element;
381
- }
382
- if (element.shadowRoot && element.shadowRoot !== root) return deepestAt(element.shadowRoot, x, y, outside(element.shadowRoot)) ?? element;
383
- return element;
384
- };
385
- const hasId = (element) => Boolean(element.getAttribute(DATA_ATTR.ID));
386
- const pickElement = (section, overlay, x, y) => {
387
- let element = deepestAt(section.ownerDocument, x, y, (candidate) => candidate === overlay || candidate === section || !section.contains(candidate));
388
- while (element && element !== section && !hasId(element)) element = element.parentElement ?? element.parentNode?.host ?? null;
389
- return element === section ? null : element;
390
- };
391
- const findElementById = (section, id) => {
392
- const selector = `[${DATA_ATTR.ID}="${CSS.escape(id)}"]`;
393
- const roots = [section];
394
- for (const root of roots) {
395
- const found = root.querySelector(selector);
396
- if (found) return found;
397
- root.querySelectorAll("iframe").forEach((frame) => {
398
- if (frame.contentDocument) roots.push(frame.contentDocument);
399
- });
400
- root.querySelectorAll("*").forEach((element) => {
401
- if (element.shadowRoot) roots.push(element.shadowRoot);
402
- });
403
- }
404
- return null;
405
- };
406
- const viewportRect = (element) => {
407
- const rect = element.getBoundingClientRect();
408
- let x = rect.left;
409
- let y = rect.top;
410
- let frame = element.ownerDocument.defaultView?.frameElement ?? null;
411
- while (frame) {
412
- const frameRect = frame.getBoundingClientRect();
413
- x += frameRect.left;
414
- y += frameRect.top;
415
- frame = frame.ownerDocument.defaultView?.frameElement ?? null;
416
- }
417
- return new DOMRect(x, y, rect.width, rect.height);
418
- };
419
- //#endregion
420
- //#region src/components/dnd/layout-context.ts
421
- const DndRegionContext = createContext(null);
422
- const useDndRegions = (subject) => {
423
- const regions = useContext(DndRegionContext);
424
- if (!regions) throw new Error(`${subject} must be used inside <Live.Dnd>.`);
425
- return regions;
426
- };
427
- const useDndPalette = () => useDndRegions("useDndPalette()").palette;
428
- const useDndPanel = () => useDndRegions("useDndPanel()").panel;
429
- const useDndLayout = () => useDndRegions("useDndLayout()");
430
- //#endregion
431
- //#region src/components/dnd/panel-binding.ts
432
- const readAttribute = (node, name) => node.dataAttributes.find((attribute) => attribute.name === name)?.value;
433
- const canEditBindingValue = (node, binding) => {
434
- if (RESERVED_BINDING_PROPERTIES.includes(binding.property)) return false;
435
- 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;
436
- const attribute = node.attributes.find((candidate) => candidate.name === binding.property);
437
- if (!attribute) return true;
438
- return attribute.isStringLiteral === true || canLosslesslyEvaluateSource(attribute?.value ?? "");
439
- };
440
- const toBindingFields = (binding) => ({
441
- label: binding.label,
442
- property: binding.property,
443
- type: binding.type,
444
- widget: binding.widget,
445
- options: binding.options,
446
- render: binding.render,
447
- min: binding.min,
448
- max: binding.max,
449
- pattern: binding.pattern,
450
- required: binding.required,
451
- meta: binding.meta
452
- });
453
- const resolveRenderEntry = (render, key) => {
454
- const entry = render?.[key];
455
- if (entry && "type" in entry) {
456
- const leaf = entry;
457
- return {
458
- ...leaf,
459
- label: leaf.label ?? key,
460
- property: leaf.property ?? key
461
- };
462
- }
463
- return {
464
- label: key,
465
- property: key,
466
- render: entry
467
- };
468
- };
469
- const CONTENT_PROPERTIES = /* @__PURE__ */ new Set([
470
- "innerText",
471
- "innerHTML",
472
- "children"
473
- ]);
474
- const isAttributeAbsent = (node, binding) => !CONTENT_PROPERTIES.has(binding.property) && !node.attributes.some((candidate) => candidate.name === binding.property);
475
- const toPanelBindingData = (node, id, binding) => ({
476
- ...toBindingFields(binding),
477
- id,
478
- value: getStructuredValue(node, binding.property, binding.type),
479
- rawValue: getCurrentValue(node, binding.property),
480
- ...!canEditBindingValue(node, binding) && { canEditValue: false },
481
- ...isAttributeAbsent(node, binding) && { present: false },
482
- element: {
483
- tagName: node.tagName || "element",
484
- text: node.textContent.replace(/\s+/g, " ").trim()
485
- }
486
- });
487
- const resolvePanelBindings = (node) => {
488
- const id = readAttribute(node, "data-id");
489
- if (!id) return null;
490
- const parsed = readNodeBindings(node);
491
- if (!parsed.length) return null;
492
- return {
493
- id,
494
- tagName: node.tagName || "element",
495
- bindings: parsed.map((binding) => toPanelBindingData(node, id, binding))
496
- };
497
- };
498
- const withPanelCommit = (bindings, commit) => bindings.map((binding) => ({
499
- ...binding,
500
- onChange: (value) => commit?.({
501
- id: binding.id,
502
- label: binding.label,
503
- property: binding.property,
504
- value
505
- })
506
- }));
507
- //#endregion
508
- //#region src/components/dnd/panel/bulk-actions-bar.tsx
509
- const BulkActionsBar = ({ count, disabled = false, onDuplicate, onMoveUp, onMoveDown, onDelete, onClear }) => {
510
- const messages = useLiveMessages();
511
- if (count === 0) return null;
512
- return /* @__PURE__ */ jsxs("div", {
513
- className: "flex items-center justify-between rounded border\n border-blue-200 bg-blue-50 p-2",
514
- children: [/* @__PURE__ */ jsx("div", {
515
- className: "text-xs font-medium text-blue-700",
516
- children: messages.selection.count(count)
517
- }), /* @__PURE__ */ jsxs("div", {
518
- className: "flex items-center space-x-1",
519
- children: [
520
- /* @__PURE__ */ jsx(Button, {
521
- size: "small",
522
- icon: /* @__PURE__ */ jsx(Copy, {}),
523
- title: messages.selection.duplicate,
524
- disabled,
525
- onClick: onDuplicate
526
- }),
527
- /* @__PURE__ */ jsx(Button, {
528
- size: "small",
529
- icon: /* @__PURE__ */ jsx(ArrowUp, {}),
530
- title: messages.selection.moveUp,
531
- disabled,
532
- onClick: onMoveUp
533
- }),
534
- /* @__PURE__ */ jsx(Button, {
535
- size: "small",
536
- icon: /* @__PURE__ */ jsx(ArrowDown, {}),
537
- title: messages.selection.moveDown,
538
- disabled,
539
- onClick: onMoveDown
540
- }),
541
- /* @__PURE__ */ jsx(Button, {
542
- danger: true,
543
- size: "small",
544
- icon: /* @__PURE__ */ jsx(X, {}),
545
- title: messages.selection.delete,
546
- disabled,
547
- onClick: onDelete
548
- }),
549
- /* @__PURE__ */ jsx(Button, {
550
- size: "small",
551
- onClick: onClear,
552
- children: messages.selection.clear
553
- })
554
- ]
555
- })]
556
- });
557
- };
558
- //#endregion
559
- //#region src/components/dnd/panel/node.tsx
560
- const Node = ({ data, onChange }) => {
561
- const source = resolvePanelBindings(data);
562
- if (!source) return null;
563
- return /* @__PURE__ */ jsx("div", {
564
- className: "space-y-2 rounded",
565
- children: /* @__PURE__ */ jsx("div", {
566
- className: "space-y-1",
567
- children: withPanelCommit(source.bindings, onChange).map((binding) => /* @__PURE__ */ jsxs("div", {
568
- className: "space-y-1",
569
- children: [/* @__PURE__ */ jsxs("label", {
570
- className: "block text-xs font-semibold text-gray-700",
571
- children: [binding.label, /* @__PURE__ */ jsxs("span", {
572
- className: "ml-1 text-gray-400",
573
- children: [
574
- "(",
575
- binding.property,
576
- ")"
577
- ]
578
- })]
579
- }), /* @__PURE__ */ jsx(Field, {
580
- binding,
581
- onNodeChange: onChange
582
- })]
583
- }, `${binding.id}-${binding.property}`))
584
- })
585
- });
586
- };
587
- //#endregion
588
- //#region src/components/dnd/panel/use-structural-selection.ts
589
- const selectionAfter = (command, selected, count) => {
590
- switch (command.type) {
591
- case "append":
592
- case "duplicate": return new Set(selected);
593
- case "move-selected": return moveSelectedIndices(Array.from({ length: count }, (_, index) => index), new Set(command.indices), command.direction).indices;
594
- default: return /* @__PURE__ */ new Set();
595
- }
596
- };
597
- const useStructuralSelection = (count, revision) => {
598
- const selection = useMultiSelect(count);
599
- const [seen, setSeen] = useState(revision);
600
- const [pending, setPending] = useState(null);
601
- if (revision !== seen) {
602
- setSeen(revision);
603
- setPending(null);
604
- if (pending?.matches(revision) && pending.selection.size > 0) selection.replace(pending.selection);
605
- else selection.clear();
606
- }
607
- const record = (next, matches, { now = false } = {}) => {
608
- setPending({
609
- matches,
610
- selection: next
611
- });
612
- if (!now) return;
613
- if (next.size > 0) selection.replace(next);
614
- else selection.clear();
615
- };
616
- return {
617
- selection,
618
- record
619
- };
620
- };
621
- //#endregion
622
- //#region src/components/dnd/panel/use-dnd-children.ts
623
- const producedBy = (expected, action) => {
624
- if (action.type === "move-selected") {
625
- const moved = JSON.stringify(moveSelectedIndices(expected, new Set(action.indices), action.direction).items);
626
- return (revision) => revision === moved;
627
- }
628
- const added = action.type === "append" ? 1 : action.type === "duplicate" ? action.indices.length : 0;
629
- return (revision) => {
630
- const next = JSON.parse(revision);
631
- return next.length === expected.length + added && expected.every((signature, index) => next[index] === signature);
632
- };
633
- };
634
- const useDndChildren = (items, { onChange } = {}) => {
635
- const expected = useMemo(() => getChildrenSignatures(items), [items]);
636
- const revision = JSON.stringify(expected);
637
- const { selection, record } = useStructuralSelection(items.length, revision);
638
- const commit = (action) => {
639
- const edit = {
640
- kind: "children-edit",
641
- expected,
642
- action
643
- };
644
- const next = selectionAfter(action, selection.selected, items.length);
645
- if (next.size > 0) record(next, producedBy(expected, action));
646
- onChange?.(JSON.stringify(edit));
647
- };
648
- return {
649
- items,
650
- selection,
651
- actions: {
652
- move: (from, to) => commit({
653
- type: "move",
654
- from,
655
- to
656
- }),
657
- remove: (index) => commit({
658
- type: "remove",
659
- indices: [index]
660
- }),
661
- add: () => commit({ type: "append" }),
662
- duplicateSelected: () => commit({
663
- type: "duplicate",
664
- indices: [...selection.selected]
665
- }),
666
- moveSelected: (direction) => commit({
667
- type: "move-selected",
668
- indices: [...selection.selected],
669
- direction
670
- }),
671
- removeSelected: () => commit({
672
- type: "remove",
673
- indices: [...selection.selected]
674
- })
675
- }
676
- };
677
- };
678
- //#endregion
679
- //#region src/components/dnd/panel/children.tsx
680
- const Children$1 = ({ value, onChange, onNodeChange }) => {
681
- const items = useMemo(() => Array.isArray(value) ? value : [], [value]);
682
- const { selection, actions } = useDndChildren(items, { onChange });
683
- const messages = useLiveMessages();
684
- const editableChildrenMap = useMemo(() => {
685
- const map = /* @__PURE__ */ new Map();
686
- items.forEach((item, index) => {
687
- const editableNodes = findEditableChildren(item);
688
- if (editableNodes.length > 0) map.set(index, editableNodes);
689
- });
690
- return map;
691
- }, [items]);
692
- return /* @__PURE__ */ jsxs("div", {
693
- className: "space-y-3",
694
- children: [
695
- /* @__PURE__ */ jsxs("div", {
696
- className: "flex items-center justify-between",
697
- children: [/* @__PURE__ */ jsx("div", {
698
- className: "text-sm font-semibold text-green-700",
699
- children: messages.children.heading(items.length)
700
- }), /* @__PURE__ */ jsx(Button, {
701
- size: "small",
702
- color: "green",
703
- icon: /* @__PURE__ */ jsx(Plus, {}),
704
- onClick: actions.add,
705
- children: messages.children.add
706
- })]
707
- }),
708
- /* @__PURE__ */ jsx(BulkActionsBar, {
709
- count: selection.selected.size,
710
- onDuplicate: actions.duplicateSelected,
711
- onMoveUp: () => actions.moveSelected("up"),
712
- onMoveDown: () => actions.moveSelected("down"),
713
- onDelete: actions.removeSelected,
714
- onClear: selection.clear
715
- }),
716
- items.map((item, itemIndex) => /* @__PURE__ */ jsxs("div", {
717
- className: "space-y-2 rounded border border-green-200 bg-green-50 p-3",
718
- children: [
719
- /* @__PURE__ */ jsxs("div", {
720
- className: "flex items-center justify-between",
721
- children: [/* @__PURE__ */ jsxs("div", {
722
- className: "flex items-center space-x-2",
723
- children: [/* @__PURE__ */ jsx("div", {
724
- onClick: (e) => selection.toggle(itemIndex, e.shiftKey),
725
- className: "inline-flex",
726
- children: /* @__PURE__ */ jsx(Checkbox, {
727
- checked: selection.isSelected(itemIndex),
728
- onChange: () => {}
729
- })
730
- }), /* @__PURE__ */ jsx("div", {
731
- className: "text-xs font-medium text-green-800",
732
- children: messages.children.child(itemIndex + 1, item.tagName || "fragment")
733
- })]
734
- }), /* @__PURE__ */ jsxs("div", {
735
- className: "flex space-x-1",
736
- children: [
737
- /* @__PURE__ */ jsx(Button, {
738
- size: "small",
739
- icon: /* @__PURE__ */ jsx(ArrowUp, {}),
740
- "aria-label": messages.children.moveUp,
741
- disabled: itemIndex === 0,
742
- onClick: () => actions.move(itemIndex, itemIndex - 1)
743
- }),
744
- /* @__PURE__ */ jsx(Button, {
745
- size: "small",
746
- icon: /* @__PURE__ */ jsx(ArrowDown, {}),
747
- "aria-label": messages.children.moveDown,
748
- disabled: itemIndex === items.length - 1,
749
- onClick: () => actions.move(itemIndex, itemIndex + 1)
750
- }),
751
- /* @__PURE__ */ jsx(Button, {
752
- "aria-label": messages.children.delete,
753
- title: messages.children.delete,
754
- danger: true,
755
- size: "small",
756
- icon: /* @__PURE__ */ jsx(X, {}),
757
- onClick: () => actions.remove(itemIndex)
758
- })
759
- ]
760
- })]
761
- }),
762
- editableChildrenMap.has(itemIndex) && /* @__PURE__ */ jsxs("div", {
763
- className: "space-y-2",
764
- children: [/* @__PURE__ */ jsx("div", {
765
- className: "text-xs font-medium text-green-700",
766
- children: messages.children.editableBindings
767
- }), editableChildrenMap.get(itemIndex).map((editableNode, idx) => {
768
- const nodeId = editableNode.dataAttributes.find((a) => a.name === "data-id")?.value;
769
- return /* @__PURE__ */ jsxs("div", {
770
- "data-node-id": nodeId,
771
- className: "rounded border border-green-100 bg-white p-2\n data-[picked]:ring-2 data-[picked]:ring-blue-400",
772
- children: [/* @__PURE__ */ jsx("div", {
773
- className: "mb-1 text-xs text-green-600",
774
- children: editableNode.tagName
775
- }), /* @__PURE__ */ jsx(Node, {
776
- data: editableNode,
777
- onChange: onNodeChange
778
- })]
779
- }, `editable-${nodeId || idx}`);
780
- })]
781
- }),
782
- !!item.children && !editableChildrenMap.has(itemIndex) && /* @__PURE__ */ jsxs("div", {
783
- className: "space-y-1",
784
- children: [/* @__PURE__ */ jsx("div", {
785
- className: "text-xs font-medium text-green-700",
786
- children: messages.children.childNodes
787
- }), item.children.map((node, nodeIndex) => /* @__PURE__ */ jsx("div", {
788
- className: "ml-2 rounded border border-green-100 bg-white p-2",
789
- children: /* @__PURE__ */ jsx(Node, {
790
- data: node,
791
- onChange: onNodeChange
792
- })
793
- }, `node-${itemIndex}-${nodeIndex}`))]
794
- })
795
- ]
796
- }, item.id || itemIndex))
797
- ]
798
- });
799
- };
800
- //#endregion
801
- //#region src/components/dnd/panel/field-kind.ts
802
- const isColorProperty = (property) => property.toLowerCase().includes("color");
803
- const getFieldKind = (binding) => {
804
- const { property, type, value } = binding;
805
- if (binding.canEditValue === false) return "readonly";
806
- if (property === "items" || property === "data" || type === "array") return "items";
807
- if (type === "richtext") return "richtext";
808
- if (property === BINDING_PROP.INNER_HTML) return "html";
809
- if (type === "jsx") return "jsx";
810
- if (property === "children" && Array.isArray(value)) return "children";
811
- if (typeof value === "object" && value !== null && !Array.isArray(value)) return "object";
812
- if (type === "boolean" || typeof value === "boolean") return "boolean";
813
- if (binding.options && Array.isArray(binding.options)) return "select";
814
- if (type === "color" || isColorProperty(property)) return "color";
815
- if (type === "date") return "date";
816
- if (type === "url") return "url";
817
- if (type === "number" || typeof value === "number") return "number";
818
- return "text";
819
- };
820
- const isStructuralFieldKind = (kind) => kind === "items" || kind === "children";
821
- //#endregion
822
- //#region src/components/dnd/panel/sortable-items.tsx
823
- const label = (messages, items, id) => {
824
- const index = items.findIndex((item) => item.id === id);
825
- return index >= 0 ? messages.items.item(index + 1) : String(id);
826
- };
827
- const SortableItems = ({ items, disabled, onMove, children }) => {
828
- const sensors = useSensors(useSensor(MouseSensor, { activationConstraint: { distance: 4 } }), useSensor(TouchSensor, { activationConstraint: {
829
- delay: 250,
830
- tolerance: 5
831
- } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
832
- const onDragEnd = ({ active, over }) => {
833
- const from = items.find((item) => item.id === active.id);
834
- const to = items.find((item) => item.id === over?.id);
835
- if (from && to && from !== to) onMove(from, to.index);
836
- };
837
- const messages = useLiveMessages();
838
- const { announcements: say } = messages;
839
- const name = (id) => label(messages, items, id);
840
- return /* @__PURE__ */ jsx(DndContext, {
841
- sensors,
842
- collisionDetection: closestCenter,
843
- modifiers: [restrictToVerticalAxis, restrictToParentElement],
844
- accessibility: { announcements: {
845
- onDragStart: ({ active }) => say.pickedUp(name(active.id)),
846
- onDragOver: ({ active, over }) => over ? say.itemOver(name(active.id), name(over.id)) : void 0,
847
- onDragEnd: ({ active, over }) => over ? say.itemMoved(name(active.id), name(over.id)) : say.dropped(name(active.id)),
848
- onDragCancel: ({ active }) => say.cancelled(name(active.id))
849
- } },
850
- onDragEnd,
851
- children: /* @__PURE__ */ jsx(SortableContext, {
852
- items: items.map((item) => item.id),
853
- strategy: verticalListSortingStrategy,
854
- disabled,
855
- children: /* @__PURE__ */ jsx("div", {
856
- className: "space-y-4",
857
- children: items.map(children)
858
- })
859
- })
860
- });
861
- };
862
- const ItemCard = ({ id, title, disabled, expanded, onToggle, checkbox, controls, className, children }) => {
863
- const messages = useLiveMessages();
864
- const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
865
- id,
866
- disabled
867
- });
868
- return /* @__PURE__ */ jsxs("div", {
869
- ref: setNodeRef,
870
- style: {
871
- transform: CSS$1.Translate.toString(transform),
872
- transition
873
- },
874
- className: cn(className, isDragging && "relative z-10 shadow-md"),
875
- children: [/* @__PURE__ */ jsxs("div", {
876
- className: "flex items-center justify-between",
877
- children: [/* @__PURE__ */ jsxs("div", {
878
- className: "flex items-center space-x-1",
879
- children: [
880
- /* @__PURE__ */ jsx("button", {
881
- type: "button",
882
- ref: setActivatorNodeRef,
883
- ...attributes,
884
- ...listeners,
885
- "aria-label": messages.items.reorder(title),
886
- disabled,
887
- className: "cursor-grab rounded p-0.5 text-gray-400\n hover:text-gray-700 disabled:cursor-not-allowed\n disabled:opacity-40",
888
- children: /* @__PURE__ */ jsx(GripVertical, { size: 14 })
889
- }),
890
- checkbox,
891
- /* @__PURE__ */ jsxs("button", {
892
- type: "button",
893
- "aria-expanded": expanded,
894
- onClick: onToggle,
895
- className: "inline-flex items-center space-x-1 rounded px-1 text-xs\n font-medium",
896
- children: [expanded ? /* @__PURE__ */ jsx(ChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(ChevronRight, { size: 14 }), /* @__PURE__ */ jsx("span", { children: title })]
897
- })
898
- ]
899
- }), controls]
900
- }), expanded && children]
901
- });
902
- };
903
- //#endregion
904
- //#region src/components/dnd/panel/use-dnd-items.ts
905
- const createPendingCommit = () => {
906
- let current = null;
907
- return {
908
- read: () => current,
909
- write: (next) => {
910
- current = next;
911
- },
912
- clear: () => {
913
- current = null;
914
- }
915
- };
916
- };
917
- const bindingsInJSX = (source, bindingOptions) => {
918
- const nodes = extract(source, bindingOptions);
919
- const container = nodes.find((n) => n.bindings?.some((b) => b.property === "children"));
920
- if (container) return [container];
921
- const bindings = [];
922
- nodes.forEach((n) => {
923
- if (n.bindings && n.bindings.length > 0 && n.dataAttributes.some((a) => a.name === "data-id")) bindings.push(n);
924
- bindings.push(...findEditableChildren(n));
925
- });
926
- return bindings;
927
- };
928
- const parseSource = (value, bindingOptions) => {
929
- const ast = parseArrayExpression(value);
930
- if (!ast) return {
931
- objectItems: [],
932
- primitiveItems: [],
933
- parseError: true
934
- };
935
- const objectItems = [];
936
- const primitiveItems = [];
937
- ast.elements.forEach((element, elementIndex) => {
938
- if (!import_lib.isExpression(element)) return;
939
- if (!import_lib.isObjectExpression(element)) {
940
- const extracted = extractNodeValue(element);
941
- primitiveItems.push({
942
- id: nanoid(6),
943
- index: primitiveItems.length,
944
- elementIndex,
945
- source: value.slice(element.start, element.end),
946
- value: extracted.value,
947
- type: extracted.type
948
- });
949
- return;
950
- }
951
- const jsxBindings = {};
952
- const jsxFallbacks = {};
953
- element.properties.forEach((prop) => {
954
- if (!import_lib.isObjectProperty(prop) || !import_lib.isIdentifier(prop.key) || !(import_lib.isJSXElement(prop.value) || import_lib.isJSXFragment(prop.value))) return;
955
- const propertyName = prop.key.name;
956
- try {
957
- const found = bindingsInJSX(value.slice(prop.value.start, prop.value.end), bindingOptions);
958
- if (found.length > 0) jsxBindings[propertyName] = found;
959
- else jsxFallbacks[propertyName] = value.slice(prop.value.start, prop.value.end);
960
- } catch (error) {
961
- console.error(`Failed to parse JSX in property '${propertyName}':`, error);
962
- }
963
- });
964
- const editableProperties = extractObjectProperties(element);
965
- for (const property of Object.values(editableProperties)) if (property.type === "array" || property.type === "object") property.value = value.slice(property.astNode.start, property.astNode.end);
966
- objectItems.push({
967
- id: nanoid(6),
968
- index: objectItems.length,
969
- elementIndex,
970
- source: value.slice(element.start, element.end),
971
- editableProperties,
972
- jsxBindings,
973
- jsxFallbacks
974
- });
975
- });
976
- return {
977
- objectItems,
978
- primitiveItems,
979
- parseError: false
980
- };
981
- };
982
- const createIdentityState = (value, kind, items, ids = items.map((item) => item.id)) => ({
983
- value,
984
- kind,
985
- ids,
986
- signatures: items.map((item) => item.source)
987
- });
988
- const reconcileIdentityState = (current, value, kind, items) => {
989
- if (current && current.value === value && current.kind === kind && current.ids.length === items.length) return current;
990
- if (!current || current.kind !== kind) return createIdentityState(value, kind, items);
991
- const previousCounts = /* @__PURE__ */ new Map();
992
- const nextCounts = /* @__PURE__ */ new Map();
993
- current.signatures.forEach((signature) => {
994
- previousCounts.set(signature, (previousCounts.get(signature) ?? 0) + 1);
995
- });
996
- items.forEach((item) => {
997
- nextCounts.set(item.source, (nextCounts.get(item.source) ?? 0) + 1);
998
- });
999
- const ids = items.map((item) => {
1000
- const previousIndex = current.signatures.indexOf(item.source);
1001
- return previousIndex >= 0 && previousCounts.get(item.source) === 1 && nextCounts.get(item.source) === 1 ? current.ids[previousIndex] : item.id;
1002
- });
1003
- return createIdentityState(value, kind, items, ids);
1004
- };
1005
- const moveId = (ids, from, to) => {
1006
- const next = [...ids];
1007
- const [moved] = next.splice(from, 1);
1008
- next.splice(to, 0, moved);
1009
- return next;
1010
- };
1011
- const removeIds = (ids, indices) => {
1012
- return ids.filter((_, index) => !indices.has(index));
1013
- };
1014
- const useDndItems = (value, { render, onChange, onNodeChange } = {}) => {
1015
- const { reportError, bindingOptions } = useDndEditOptions();
1016
- const messages = useLiveMessages();
1017
- const { objectItems, primitiveItems, parseError } = useMemo(() => parseSource(value, bindingOptions), [value, bindingOptions]);
1018
- const canEditStructure = useMemo(() => canStructurallyEditArray(value), [value]);
1019
- const reportErrorRef = useRef(reportError);
1020
- const messagesRef = useRef(messages);
1021
- useEffect(() => {
1022
- reportErrorRef.current = reportError;
1023
- messagesRef.current = messages;
1024
- });
1025
- useEffect(() => {
1026
- if (parseError) reportErrorRef.current({
1027
- type: "parse",
1028
- target: "items",
1029
- title: messagesRef.current.editErrors.itemsParseFailed,
1030
- description: messagesRef.current.editErrors.checkConsole
1031
- });
1032
- }, [parseError]);
1033
- const kind = primitiveItems.length > 0 && objectItems.length === 0 ? "primitive" : "object";
1034
- const isPrimitive = kind === "primitive";
1035
- const rawItems = isPrimitive ? primitiveItems : objectItems;
1036
- const [identityState, setIdentityState] = useState(() => createIdentityState(value, kind, rawItems));
1037
- const identity = useMemo(() => reconcileIdentityState(identityState, value, kind, rawItems), [
1038
- identityState,
1039
- kind,
1040
- rawItems,
1041
- value
1042
- ]);
1043
- const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
1044
- const { selection, record } = useStructuralSelection(isPrimitive ? primitiveItems.length : objectItems.length, value);
1045
- const [pending] = useState(createPendingCommit);
1046
- useEffect(() => {
1047
- pending.clear();
1048
- });
1049
- const latest = () => {
1050
- const last = pending.read();
1051
- if (last?.from !== value) return {
1052
- value,
1053
- items: rawItems,
1054
- ids: identity.ids,
1055
- selected: selection.selected
1056
- };
1057
- const parsed = parseSource(last.value);
1058
- return {
1059
- value: last.value,
1060
- items: isPrimitive ? parsed.primitiveItems : parsed.objectItems,
1061
- ids: last.ids,
1062
- selected: last.selected
1063
- };
1064
- };
1065
- const locate = (snapshot, elementIndex) => {
1066
- const shown = rawItems.find((item) => item.elementIndex === elementIndex);
1067
- if (!shown) return;
1068
- const index = snapshot.ids.indexOf(identity.ids[shown.index]);
1069
- return index >= 0 ? snapshot.items[index] : null;
1070
- };
1071
- const commit = (snapshot, next, nextIds, nextSelection) => {
1072
- if (next === null) {
1073
- reportError({
1074
- type: "items",
1075
- title: messages.editErrors.itemUpdateFailed,
1076
- description: messages.editErrors.itemUpdateFailedDetail
1077
- });
1078
- return false;
1079
- }
1080
- const parsed = parseSource(next);
1081
- const nextItems = kind === "primitive" ? parsed.primitiveItems : parsed.objectItems;
1082
- const ids = nextIds ?? snapshot.ids;
1083
- pending.write({
1084
- from: value,
1085
- value: next,
1086
- ids,
1087
- selected: nextSelection
1088
- });
1089
- setIdentityState(createIdentityState(next, kind, nextItems, ids));
1090
- record(nextSelection, (revision) => revision === next, { now: true });
1091
- onChange?.(next);
1092
- return true;
1093
- };
1094
- const elementIndicesOf = (snapshot, indices) => new Set(snapshot.items.filter((item) => indices.has(item.index)).map((item) => item.elementIndex));
1095
- const updateProperty = (elementIndex, propertyKey, next) => {
1096
- const snapshot = latest();
1097
- const item = locate(snapshot, elementIndex);
1098
- if (item === null) return;
1099
- commit(snapshot, updateArrayItemProperty(snapshot.value, item?.elementIndex ?? elementIndex, propertyKey, next, render), void 0, new Set(snapshot.selected));
1100
- };
1101
- const updateValue = (elementIndex, next) => {
1102
- const snapshot = latest();
1103
- const item = locate(snapshot, elementIndex);
1104
- if (item === null) return;
1105
- commit(snapshot, updateArrayItemValue(snapshot.value, item?.elementIndex ?? elementIndex, next), void 0, new Set(snapshot.selected));
1106
- };
1107
- const actions = {
1108
- add: () => {
1109
- const snapshot = latest();
1110
- const next = appendArrayItem(snapshot.value, kind);
1111
- commit(snapshot, next, next ? [...snapshot.ids, nanoid(6)] : void 0, selectionAfter({ type: "append" }, snapshot.selected, snapshot.items.length));
1112
- },
1113
- move: (elementIndex, toIndex) => {
1114
- const snapshot = latest();
1115
- const from = locate(snapshot, elementIndex);
1116
- const target = snapshot.items.find((item) => item.index === toIndex);
1117
- if (!from || !target) return;
1118
- commit(snapshot, moveArrayItem(snapshot.value, from.elementIndex, target.elementIndex), moveId(snapshot.ids, from.index, target.index), /* @__PURE__ */ new Set());
1119
- },
1120
- remove: (elementIndex) => {
1121
- const snapshot = latest();
1122
- const removed = locate(snapshot, elementIndex);
1123
- if (removed === null) return;
1124
- 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());
1125
- },
1126
- duplicateSelected: () => {
1127
- const snapshot = latest();
1128
- const selected = elementIndicesOf(snapshot, snapshot.selected);
1129
- const next = duplicateArrayItems(snapshot.value, selected);
1130
- const copied = snapshot.items.filter((item) => selected.has(item.elementIndex)).sort((a, b) => a.elementIndex - b.elementIndex).map(() => nanoid(6));
1131
- commit(snapshot, next, next ? [...snapshot.ids, ...copied] : void 0, selectionAfter({
1132
- type: "duplicate",
1133
- indices: [...snapshot.selected]
1134
- }, snapshot.selected, snapshot.items.length));
1135
- },
1136
- moveSelected: (direction) => {
1137
- const snapshot = latest();
1138
- const nextIdentity = moveSelectedIndices(snapshot.ids, snapshot.selected, direction);
1139
- const result = moveArrayItems(snapshot.value, elementIndicesOf(snapshot, snapshot.selected), direction);
1140
- if (!result) {
1141
- commit(snapshot, null, void 0, snapshot.selected);
1142
- return;
1143
- }
1144
- const visible = parseArrayExpression(result.code)?.elements.flatMap((node, index) => import_lib.isExpression(node) && import_lib.isObjectExpression(node) === !isPrimitive ? [index] : []) ?? [];
1145
- commit(snapshot, result.code, nextIdentity.items, new Set(visible.flatMap((position, index) => result.indices.has(position) ? [index] : [])));
1146
- },
1147
- removeSelected: () => {
1148
- const snapshot = latest();
1149
- commit(snapshot, removeArrayItems(snapshot.value, elementIndicesOf(snapshot, snapshot.selected), kind), removeIds(snapshot.ids, snapshot.selected), /* @__PURE__ */ new Set());
1150
- }
1151
- };
1152
- const items = isPrimitive ? primitiveItems.map((item) => ({
1153
- id: identity.ids[item.index] ?? item.id,
1154
- index: item.index,
1155
- elementIndex: item.elementIndex,
1156
- properties: [],
1157
- nested: [],
1158
- value: {
1159
- id: `primitive-${item.id}`,
1160
- label: `item-${item.index}`,
1161
- property: item.type,
1162
- value: item.value ?? "",
1163
- rawValue: item.type === "unknown" ? item.source : String(item.value ?? ""),
1164
- canEditValue: canLosslesslyEvaluateSource(item.source),
1165
- onChange: (next) => updateValue(item.elementIndex, next)
1166
- }
1167
- })) : objectItems.map((item) => {
1168
- const id = identity.ids[item.index] ?? item.id;
1169
- const properties = Object.entries(item.editableProperties).map(([key, prop]) => {
1170
- const binding = resolveRenderEntry(render, key);
1171
- const propertySource = value.slice(prop.astNode.start, prop.astNode.end);
1172
- return {
1173
- ...toBindingFields(binding),
1174
- id: `item-${id}-${key}`,
1175
- meta: {
1176
- ...binding.meta,
1177
- valueType: prop.type
1178
- },
1179
- value: parseValue(String(prop.value)),
1180
- rawValue: prop.type === "unknown" ? propertySource : String(prop.value),
1181
- canEditValue: canLosslesslyEvaluateSource(propertySource),
1182
- onChange: (next) => updateProperty(item.elementIndex, key, next)
1183
- };
1184
- });
1185
- const nested = [...Object.entries(item.jsxBindings).map(([property, nodes]) => ({
1186
- property,
1187
- elements: nodes.flatMap((node) => {
1188
- const source = resolvePanelBindings(node);
1189
- if (!source) return [];
1190
- return [{
1191
- id: source.id,
1192
- tagName: source.tagName,
1193
- bindings: withPanelCommit(source.bindings, onNodeChange)
1194
- }];
1195
- })
1196
- })), ...Object.entries(item.jsxFallbacks).map(([property, code]) => ({
1197
- property,
1198
- elements: [],
1199
- fallback: {
1200
- id: `item-${id}-${property}-jsx`,
1201
- label: property,
1202
- property,
1203
- type: "jsx",
1204
- value: code,
1205
- rawValue: code,
1206
- canEditValue: true,
1207
- onChange: (next) => updateProperty(item.elementIndex, property, next)
1208
- }
1209
- }))];
1210
- return {
1211
- id,
1212
- index: item.index,
1213
- elementIndex: item.elementIndex,
1214
- properties,
1215
- nested
1216
- };
1217
- });
1218
- return {
1219
- kind,
1220
- items,
1221
- selection,
1222
- expansion: {
1223
- expandedIds: items.flatMap((item) => collapsed.has(item.id) ? [] : [item.id]),
1224
- isExpanded: (id) => !collapsed.has(id),
1225
- toggle: (id) => setCollapsed((previous) => {
1226
- const next = new Set(previous);
1227
- if (!next.delete(id)) next.add(id);
1228
- return next;
1229
- }),
1230
- setExpanded: (ids) => {
1231
- const expanded = new Set(ids);
1232
- setCollapsed(new Set(items.flatMap((item) => expanded.has(item.id) ? [] : [item.id])));
1233
- }
1234
- },
1235
- actions,
1236
- canEditStructure,
1237
- parseError
1238
- };
1239
- };
1240
- //#endregion
1241
- //#region src/components/dnd/panel/items.tsx
1242
- const NestedGroup = ({ group, onNodeChange }) => {
1243
- if (group.fallback) return /* @__PURE__ */ jsxs("div", {
1244
- className: "space-y-2",
1245
- children: [/* @__PURE__ */ jsx("div", {
1246
- className: "text-xs font-medium text-blue-700",
1247
- children: group.property
1248
- }), /* @__PURE__ */ jsx(Field, {
1249
- binding: group.fallback,
1250
- onNodeChange
1251
- })]
1252
- });
1253
- return /* @__PURE__ */ jsxs("div", {
1254
- className: "space-y-2",
1255
- children: [/* @__PURE__ */ jsxs("div", {
1256
- className: "text-xs font-medium text-blue-700",
1257
- children: [
1258
- group.property,
1259
- " Bindings (",
1260
- group.elements.length,
1261
- "):"
1262
- ]
1263
- }), group.elements.map((element) => /* @__PURE__ */ jsxs("div", {
1264
- "data-node-id": element.id,
1265
- className: "rounded border border-blue-100 bg-blue-50 p-2\n data-[picked]:ring-2 data-[picked]:ring-blue-400",
1266
- children: [/* @__PURE__ */ jsxs("div", {
1267
- className: "mb-1 text-xs text-blue-600",
1268
- children: [
1269
- "<",
1270
- element.tagName,
1271
- ">"
1272
- ]
1273
- }), /* @__PURE__ */ jsx("div", {
1274
- className: "space-y-2 rounded",
1275
- children: /* @__PURE__ */ jsx("div", {
1276
- className: "space-y-1",
1277
- children: element.bindings.map((binding) => /* @__PURE__ */ jsxs("div", {
1278
- className: "space-y-1",
1279
- children: [/* @__PURE__ */ jsxs("label", {
1280
- className: "block text-xs font-semibold text-gray-700",
1281
- children: [binding.label, /* @__PURE__ */ jsxs("span", {
1282
- className: "ml-1 text-gray-400",
1283
- children: [
1284
- "(",
1285
- binding.property,
1286
- ")"
1287
- ]
1288
- })]
1289
- }), /* @__PURE__ */ jsx(Field, {
1290
- binding,
1291
- onNodeChange
1292
- })]
1293
- }, `${binding.property}-${binding.label}`))
1294
- })
1295
- })]
1296
- }, `${group.property}-${element.id}`))]
1297
- });
1298
- };
1299
- const Items = ({ value, render, onChange, onChildChange }) => {
1300
- const messages = useLiveMessages();
1301
- const { kind, items, selection, expansion, actions, canEditStructure, parseError } = useDndItems(value, {
1302
- render,
1303
- onChange,
1304
- onNodeChange: onChildChange
1305
- });
1306
- const header = /* @__PURE__ */ jsxs("div", {
1307
- className: "flex items-center justify-between",
1308
- children: [/* @__PURE__ */ jsx("div", {
1309
- className: "text-sm font-semibold",
1310
- children: messages.items.heading(items.length)
1311
- }), items.length > 0 && canEditStructure && /* @__PURE__ */ jsx(Button, {
1312
- size: "small",
1313
- icon: /* @__PURE__ */ jsx(Plus, {}),
1314
- variant: "solid",
1315
- color: "green",
1316
- onClick: actions.add,
1317
- children: messages.items.add
1318
- })]
1319
- });
1320
- if (parseError) return /* @__PURE__ */ jsxs("div", {
1321
- className: "space-y-4",
1322
- children: [header, /* @__PURE__ */ jsx("div", {
1323
- className: "rounded border border-dashed border-red-200 p-3 text-xs\n text-red-600",
1324
- children: messages.items.unreadable
1325
- })]
1326
- });
1327
- if (items.length === 0) return /* @__PURE__ */ jsxs("div", {
1328
- className: "space-y-4",
1329
- children: [header, /* @__PURE__ */ jsx("div", {
1330
- className: "rounded border border-dashed border-gray-200 p-3 text-xs\n text-gray-500",
1331
- children: messages.items.empty
1332
- })]
1333
- });
1334
- const bulkBar = /* @__PURE__ */ jsx(BulkActionsBar, {
1335
- count: selection.selected.size,
1336
- disabled: !canEditStructure,
1337
- onDuplicate: actions.duplicateSelected,
1338
- onMoveUp: () => actions.moveSelected("up"),
1339
- onMoveDown: () => actions.moveSelected("down"),
1340
- onDelete: actions.removeSelected,
1341
- onClear: selection.clear
1342
- });
1343
- const itemControls = (item) => /* @__PURE__ */ jsxs("div", {
1344
- className: "flex space-x-1",
1345
- children: [
1346
- /* @__PURE__ */ jsx(Button, {
1347
- size: "small",
1348
- icon: /* @__PURE__ */ jsx(ArrowUp, {}),
1349
- "aria-label": messages.items.moveUp,
1350
- disabled: !canEditStructure || item.index === 0,
1351
- onClick: () => actions.move(item.elementIndex, item.index - 1)
1352
- }),
1353
- /* @__PURE__ */ jsx(Button, {
1354
- size: "small",
1355
- icon: /* @__PURE__ */ jsx(ArrowDown, {}),
1356
- "aria-label": messages.items.moveDown,
1357
- disabled: !canEditStructure || item.index === items.length - 1,
1358
- onClick: () => actions.move(item.elementIndex, item.index + 1)
1359
- }),
1360
- /* @__PURE__ */ jsx(Button, {
1361
- danger: true,
1362
- size: "small",
1363
- icon: /* @__PURE__ */ jsx(X, {}),
1364
- "aria-label": messages.items.delete,
1365
- disabled: !canEditStructure || items.length <= 1,
1366
- onClick: () => actions.remove(item.elementIndex)
1367
- })
1368
- ]
1369
- });
1370
- const itemCheckbox = (item) => /* @__PURE__ */ jsx("div", {
1371
- onClick: (e) => canEditStructure && selection.toggle(item.index, e.shiftKey),
1372
- className: "inline-flex",
1373
- children: /* @__PURE__ */ jsx(Checkbox, {
1374
- checked: selection.isSelected(item.index),
1375
- disabled: !canEditStructure,
1376
- onChange: () => {}
1377
- })
1378
- });
1379
- const objectBody = (item) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
1380
- className: "space-y-2",
1381
- children: item.properties.map((binding) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
1382
- className: "flex flex-col space-y-2",
1383
- children: [/* @__PURE__ */ jsx("label", {
1384
- className: "w-20 shrink-0 text-xs font-medium",
1385
- children: binding.label
1386
- }), /* @__PURE__ */ jsx(Field, {
1387
- binding,
1388
- onNodeChange: onChildChange
1389
- })]
1390
- }), /* @__PURE__ */ jsxs("span", {
1391
- className: "text-right text-xs text-gray-500",
1392
- children: [
1393
- "(",
1394
- String(binding.meta?.valueType),
1395
- ")"
1396
- ]
1397
- })] }, `${item.id}-${binding.property}-${binding.label}`))
1398
- }), /* @__PURE__ */ jsx("div", {
1399
- className: "space-y-3 border-t pt-2",
1400
- children: item.nested.length ? item.nested.map((group) => /* @__PURE__ */ jsx(NestedGroup, {
1401
- group,
1402
- onNodeChange: onChildChange
1403
- }, group.property)) : /* @__PURE__ */ jsxs("div", {
1404
- className: "text-xs text-gray-500",
1405
- children: [
1406
- /* @__PURE__ */ jsx("span", {
1407
- "aria-hidden": "true",
1408
- children: "✓"
1409
- }),
1410
- " ",
1411
- messages.items.noNestedBindings
1412
- ]
1413
- })
1414
- })] });
1415
- const allExpanded = expansion.expandedIds.length === items.length;
1416
- return /* @__PURE__ */ jsxs("div", {
1417
- className: "space-y-4",
1418
- children: [
1419
- header,
1420
- items.length > 1 && /* @__PURE__ */ jsx("div", {
1421
- className: "flex justify-end",
1422
- children: /* @__PURE__ */ jsx(Button, {
1423
- size: "small",
1424
- variant: "text",
1425
- onClick: () => expansion.setExpanded(allExpanded ? [] : items.map((item) => item.id)),
1426
- children: allExpanded ? messages.items.collapseAll : messages.items.expandAll
1427
- })
1428
- }),
1429
- bulkBar,
1430
- !canEditStructure && /* @__PURE__ */ jsx("div", {
1431
- className: "rounded border border-dashed border-amber-200 p-3 text-xs\n text-amber-700",
1432
- children: messages.items.structureLocked
1433
- }),
1434
- /* @__PURE__ */ jsx(SortableItems, {
1435
- items,
1436
- disabled: !canEditStructure,
1437
- onMove: (item, toIndex) => actions.move(item.elementIndex, toIndex),
1438
- children: (item) => /* @__PURE__ */ jsx(ItemCard, {
1439
- id: item.id,
1440
- title: messages.items.item(item.index + 1),
1441
- disabled: !canEditStructure,
1442
- expanded: expansion.isExpanded(item.id),
1443
- onToggle: () => expansion.toggle(item.id),
1444
- checkbox: itemCheckbox(item),
1445
- controls: itemControls(item),
1446
- 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",
1447
- children: kind === "primitive" ? /* @__PURE__ */ jsx(Field, {
1448
- binding: item.value,
1449
- onNodeChange: onChildChange
1450
- }) : objectBody(item)
1451
- }, item.id)
1452
- })
1453
- ]
1454
- });
1455
- };
1456
- //#endregion
1457
- //#region src/components/dnd/panel/field.tsx
1458
- const normalizeToHex = (value) => {
1459
- const trimmed = value.trim();
1460
- if (/^#([0-9A-Fa-f]{3}){1,2}$/.test(trimmed)) return trimmed;
1461
- return "#000000";
1462
- };
1463
- const parseDateValue = (value) => {
1464
- const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
1465
- if (!match) return;
1466
- const [, year, month, day] = match;
1467
- const date = new Date(Number(year), Number(month) - 1, Number(day));
1468
- return Number.isNaN(date.getTime()) ? void 0 : date;
1469
- };
1470
- const formatDateValue = (date) => {
1471
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
1472
- };
1473
- const COLOR_COMMIT_DELAY = 75;
1474
- const RICHTEXT_TOOLBAR = [
1475
- "bold",
1476
- "italic",
1477
- "underline",
1478
- "bulletList",
1479
- "orderedList",
1480
- "link"
1481
- ];
1482
- const JSXEditorField = ({ value, isHTML, onSave }) => {
1483
- const selectionRef = useRef(null);
1484
- const focusedRef = useRef(false);
1485
- const handleUpdate = useCallback((update) => {
1486
- selectionRef.current = update.state.selection;
1487
- }, []);
1488
- const handleCreateEditor = useCallback((view) => {
1489
- if (selectionRef.current) view.dispatch({ selection: selectionRef.current });
1490
- if (focusedRef.current) view.focus();
1491
- }, []);
1492
- const handleFocus = useCallback(() => {
1493
- focusedRef.current = true;
1494
- }, []);
1495
- const handleBlur = useCallback((event) => {
1496
- if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) focusedRef.current = false;
1497
- }, []);
1498
- return /* @__PURE__ */ jsx("div", {
1499
- onFocusCapture: handleFocus,
1500
- onBlurCapture: handleBlur,
1501
- children: /* @__PURE__ */ jsx(Core, {
1502
- value,
1503
- height: "150px",
1504
- fragment: !isHTML,
1505
- raw: isHTML,
1506
- onSave,
1507
- onUpdate: handleUpdate,
1508
- onCreateEditor: handleCreateEditor
1509
- })
1510
- });
1511
- };
1512
- const ColorPickerField = ({ value, onChange }) => {
1513
- const [liveValue, setLiveValue] = useState(value);
1514
- const [prevValue, setPrevValue] = useState(value);
1515
- const lastCommittedRef = useRef(value);
1516
- if (value !== prevValue) {
1517
- setPrevValue(value);
1518
- setLiveValue(value);
1519
- }
1520
- useEffect(() => {
1521
- lastCommittedRef.current = value;
1522
- }, [value]);
1523
- const commit = (next) => {
1524
- if (next === lastCommittedRef.current) return;
1525
- lastCommittedRef.current = next;
1526
- onChange(next);
1527
- };
1528
- const debouncedCommit = useDebounce(() => commit(liveValue), {
1529
- delay: COLOR_COMMIT_DELAY,
1530
- autoInvoke: false
1531
- });
1532
- return /* @__PURE__ */ jsx(ColorPicker, {
1533
- showText: true,
1534
- value: liveValue,
1535
- onChange: (next) => {
1536
- setLiveValue(next);
1537
- debouncedCommit();
1538
- },
1539
- onOpenChange: (open) => {
1540
- if (!open) commit(liveValue);
1541
- }
1542
- });
1543
- };
1544
- const FieldError = ({ message }) => message ? /* @__PURE__ */ jsx("p", {
1545
- className: "mt-1 mb-0 text-xs text-red-500",
1546
- children: message
1547
- }) : null;
1548
- const BuiltinField = ({ binding, onNodeChange }) => {
1549
- const messages = useLiveMessages();
1550
- const validationOptions = { messages: messages.validation };
1551
- const { id, value, rawValue, onChange } = binding;
1552
- const [validationError, setValidationError] = useState(null);
1553
- const [text, setText] = useState(rawValue);
1554
- const [prevRawValue, setPrevRawValue] = useState(rawValue);
1555
- if (rawValue !== prevRawValue) {
1556
- setPrevRawValue(rawValue);
1557
- setText(rawValue);
1558
- }
1559
- const kind = getFieldKind(binding);
1560
- if (kind === "readonly") return /* @__PURE__ */ jsxs("div", {
1561
- className: "space-y-1 rounded border border-dashed border-amber-200 p-3\n text-xs text-amber-700",
1562
- children: [
1563
- /* @__PURE__ */ jsx("div", { children: messages.panel.expressionPreserved }),
1564
- /* @__PURE__ */ jsx("code", {
1565
- className: "block overflow-x-auto text-amber-800",
1566
- children: rawValue
1567
- }),
1568
- /* @__PURE__ */ jsx("div", { children: messages.panel.useCodeEditor })
1569
- ]
1570
- });
1571
- const commitIfChanged = (next) => {
1572
- if (next !== (typeof next === "string" ? rawValue : value)) onChange(next);
1573
- };
1574
- if (kind === "items") return /* @__PURE__ */ jsx(Items, {
1575
- value: rawValue,
1576
- render: binding.render,
1577
- onChange,
1578
- onChildChange: onNodeChange
1579
- });
1580
- if (kind === "richtext") return /* @__PURE__ */ jsx(RichTextEditor, {
1581
- value: rawValue,
1582
- toolbar: RICHTEXT_TOOLBAR,
1583
- onChange: commitIfChanged
1584
- });
1585
- if (kind === "html" || kind === "jsx") return /* @__PURE__ */ jsx(JSXEditorField, {
1586
- value: rawValue,
1587
- isHTML: kind === "html",
1588
- onSave: commitIfChanged
1589
- });
1590
- if (kind === "children") return /* @__PURE__ */ jsx(Children$1, {
1591
- value,
1592
- onChange,
1593
- onNodeChange
1594
- });
1595
- if (kind === "object") {
1596
- const objectValue = value;
1597
- return /* @__PURE__ */ jsx("div", {
1598
- className: "space-y-2 rounded border border-gray-200 bg-gray-50 p-2",
1599
- children: Object.entries(objectValue).map(([key, val]) => {
1600
- const nested = resolveRenderEntry(binding.render, key);
1601
- return /* @__PURE__ */ jsxs("div", {
1602
- className: "space-y-1",
1603
- children: [/* @__PURE__ */ jsx("label", {
1604
- className: "block text-xs font-medium text-gray-600",
1605
- children: nested.label
1606
- }), /* @__PURE__ */ jsx(Field, {
1607
- binding: {
1608
- ...toBindingFields(nested),
1609
- id,
1610
- value: val,
1611
- rawValue: typeof val === "object" && val !== null ? JSON.stringify(val) : String(val),
1612
- onChange: (next) => {
1613
- onChange({
1614
- ...objectValue,
1615
- [key]: next
1616
- });
1617
- }
1618
- },
1619
- onNodeChange
1620
- })]
1621
- }, key);
1622
- })
1623
- });
1624
- }
1625
- if (kind === "boolean") return /* @__PURE__ */ jsx(Checkbox, {
1626
- checked: value === true || value === "true",
1627
- onChange: (checked) => {
1628
- onChange(checked);
1629
- }
1630
- });
1631
- const stringValue = rawValue;
1632
- if (kind === "select") return /* @__PURE__ */ jsx(Select, {
1633
- value: stringValue,
1634
- options: binding.options ?? [],
1635
- onChange: commitIfChanged
1636
- });
1637
- if (kind === "color") return /* @__PURE__ */ jsx(ColorPickerField, {
1638
- value: normalizeToHex(stringValue),
1639
- onChange: commitIfChanged
1640
- });
1641
- if (kind === "date") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(DatePicker, {
1642
- value: parseDateValue(stringValue),
1643
- onChange: (date) => {
1644
- const next = date ? formatDateValue(date) : "";
1645
- const result = validateBindingValue(binding, next, validationOptions);
1646
- if (!result.valid) {
1647
- setValidationError(result.message ?? messages.panel.invalidValue);
1648
- return;
1649
- }
1650
- setValidationError(null);
1651
- commitIfChanged(next);
1652
- }
1653
- }), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
1654
- if (kind === "url") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
1655
- type: "url",
1656
- value: text,
1657
- onChange: (e) => setText(e.target.value),
1658
- placeholder: messages.panel.urlPlaceholder,
1659
- onBlur: (e) => {
1660
- const next = e.target.value.trim();
1661
- const result = validateBindingValue(binding, next, validationOptions);
1662
- if (!result.valid) {
1663
- setValidationError(result.message ?? messages.panel.invalidValue);
1664
- return;
1665
- }
1666
- setValidationError(null);
1667
- commitIfChanged(next);
1668
- }
1669
- }), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
1670
- if (kind === "number") return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
1671
- type: "number",
1672
- value: text,
1673
- onChange: (e) => setText(e.target.value),
1674
- placeholder: messages.panel.numberPlaceholder,
1675
- onBlur: (e) => {
1676
- const raw = e.target.value.trim();
1677
- const next = raw !== "" && !Number.isNaN(Number(raw)) ? Number(raw) : raw;
1678
- const result = validateBindingValue(binding, next, validationOptions);
1679
- if (!result.valid) {
1680
- setValidationError(result.message ?? messages.panel.invalidValue);
1681
- return;
1682
- }
1683
- setValidationError(null);
1684
- commitIfChanged(next);
1685
- }
1686
- }), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
1687
- return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input.TextArea, {
1688
- value: text,
1689
- onChange: (e) => setText(e.target.value),
1690
- placeholder: messages.panel.textPlaceholder,
1691
- onBlur: (e) => {
1692
- const raw = e.target.value.trim();
1693
- const next = binding.type ? raw : parseValue(raw);
1694
- const result = validateBindingValue(binding, next, validationOptions);
1695
- if (!result.valid) {
1696
- setValidationError(result.message ?? messages.panel.invalidValue);
1697
- return;
1698
- }
1699
- setValidationError(null);
1700
- commitIfChanged(next);
1701
- }
1702
- }), /* @__PURE__ */ jsx(FieldError, { message: validationError })] });
1703
- };
1704
- const Field = (props) => {
1705
- const { renderField } = useDndEditOptions();
1706
- const builtin = /* @__PURE__ */ jsx(BuiltinField, { ...props });
1707
- if (!renderField) return builtin;
1708
- const custom = renderField(props, builtin);
1709
- return custom === void 0 ? builtin : /* @__PURE__ */ jsx(Fragment, { children: custom });
1710
- };
1711
- //#endregion
1712
- //#region src/components/dnd/panel/field-group.tsx
1713
- const FieldGroup = ({ bindings, onNodeChange }) => /* @__PURE__ */ jsx("div", {
1714
- className: "space-y-2 rounded",
1715
- children: /* @__PURE__ */ jsx("div", {
1716
- className: "space-y-1",
1717
- children: bindings.map((binding) => /* @__PURE__ */ jsxs("div", {
1718
- className: "space-y-1",
1719
- children: [/* @__PURE__ */ jsxs("label", {
1720
- className: "block text-xs font-semibold text-gray-700",
1721
- children: [binding.label, /* @__PURE__ */ jsxs("span", {
1722
- className: "ml-1 text-gray-400",
1723
- children: [
1724
- "(",
1725
- binding.property,
1726
- ")"
1727
- ]
1728
- })]
1729
- }), /* @__PURE__ */ jsx(Field, {
1730
- binding,
1731
- onNodeChange
1732
- })]
1733
- }, `${binding.property}-${binding.label}`))
1734
- })
1735
- });
1736
- //#endregion
1737
- //#region src/components/dnd/panel/panel.tsx
1738
- const groupBindingsById = (bindings) => {
1739
- const groups = /* @__PURE__ */ new Map();
1740
- for (const binding of bindings) {
1741
- const group = groups.get(binding.id);
1742
- if (group) group.push(binding);
1743
- else groups.set(binding.id, [binding]);
1744
- }
1745
- return [...groups.values()];
1746
- };
1747
- const describeElement = (binding) => {
1748
- const { tagName = "element", text = "" } = binding.element ?? {};
1749
- return {
1750
- tagName,
1751
- text,
1752
- label: text ? `${tagName} "${text}"` : tagName
1753
- };
1754
- };
1755
- const nodeIdOf = (target) => target instanceof Element && target.closest("[data-node-id]")?.getAttribute("data-node-id") || null;
1756
- const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, bindings, onNodeChange, readOnly = false }) => {
1757
- const messages = useLiveMessages();
1758
- const groups = groupBindingsById(bindings);
1759
- const rootRef = useRef(null);
1760
- const inspector = useContext(DndInspectorContext);
1761
- const pickedId = (inspector?.picked ?? null)?.id;
1762
- const highlight = inspector?.highlight;
1763
- const [hoveredId, setHoveredId] = useState(null);
1764
- const [focusedId, setFocusedId] = useState(null);
1765
- const targetId = hoveredId ?? focusedId;
1766
- useEffect(() => {
1767
- if (!highlight) return;
1768
- highlight(targetId);
1769
- return () => highlight(null);
1770
- }, [highlight, targetId]);
1771
- useEffect(() => {
1772
- const root = rootRef.current;
1773
- if (!root || !pickedId) return;
1774
- const target = root.querySelector(`[data-node-id="${CSS.escape(pickedId)}"]`);
1775
- if (!target) return;
1776
- target.setAttribute("data-picked", "");
1777
- const until = performance.now() + 600;
1778
- let frame = 0;
1779
- const keepInView = () => {
1780
- const box = target.getBoundingClientRect();
1781
- const view = root.getBoundingClientRect();
1782
- if (box.top < view.top || box.bottom > view.bottom) target.scrollIntoView?.({ block: "nearest" });
1783
- if (performance.now() < until) frame = requestAnimationFrame(keepInView);
1784
- };
1785
- keepInView();
1786
- return () => {
1787
- cancelAnimationFrame(frame);
1788
- target.removeAttribute("data-picked");
1789
- };
1790
- }, [pickedId, groups.length]);
1791
- if (!item) return /* @__PURE__ */ jsx(Typography.Paragraph, {
1792
- className: cn("p-4 text-sm text-gray-500"),
1793
- children: messages.panel.selectSection
1794
- });
1795
- return /* @__PURE__ */ jsxs("div", {
1796
- ref: rootRef,
1797
- onPointerOver: (e) => setHoveredId(nodeIdOf(e.target)),
1798
- onPointerLeave: () => setHoveredId(null),
1799
- onFocus: (e) => setFocusedId(nodeIdOf(e.target)),
1800
- onBlur: (e) => {
1801
- if (!e.currentTarget.contains(e.relatedTarget)) setFocusedId(null);
1802
- },
1803
- className: cn("h-full space-y-4 p-4", "overflow-x-hidden overflow-y-auto"),
1804
- children: [
1805
- /* @__PURE__ */ jsxs("div", {
1806
- className: "flex items-center justify-between",
1807
- children: [/* @__PURE__ */ jsx(Typography.Title, {
1808
- className: "text-lg font-semibold",
1809
- children: item.name
1810
- }), /* @__PURE__ */ jsxs("div", {
1811
- className: "flex items-center gap-1",
1812
- children: [
1813
- onMoveUp && /* @__PURE__ */ jsx(Button, {
1814
- icon: /* @__PURE__ */ jsx(ChevronUp, {}),
1815
- disabled: !canMoveUp,
1816
- onClick: onMoveUp,
1817
- "aria-label": messages.section.moveUp
1818
- }),
1819
- onMoveDown && /* @__PURE__ */ jsx(Button, {
1820
- icon: /* @__PURE__ */ jsx(ChevronDown, {}),
1821
- disabled: !canMoveDown,
1822
- onClick: onMoveDown,
1823
- "aria-label": messages.section.moveDown
1824
- }),
1825
- onDelete && /* @__PURE__ */ jsx(Button, {
1826
- danger: true,
1827
- icon: /* @__PURE__ */ jsx(Trash, {}),
1828
- onClick: () => onDelete(item.id),
1829
- "aria-label": messages.section.delete
1830
- })
1831
- ]
1832
- })]
1833
- }),
1834
- readOnly && /* @__PURE__ */ jsx(Typography.Text, {
1835
- role: "status",
1836
- className: "block text-xs text-amber-700",
1837
- children: messages.panel.readOnly
1838
- }),
1839
- !groups.length && /* @__PURE__ */ jsx(Typography.Text, {
1840
- className: "text-xs text-gray-400",
1841
- children: messages.panel.noEditableElements
1842
- }),
1843
- groups.map((group) => {
1844
- const element = describeElement(group[0]);
1845
- return /* @__PURE__ */ jsxs("div", {
1846
- "data-node-id": group[0].id,
1847
- role: "group",
1848
- "aria-label": element.label,
1849
- className: "rounded data-[picked]:ring-2 data-[picked]:ring-blue-400\n data-[picked]:ring-offset-2",
1850
- children: [groups.length > 1 && /* @__PURE__ */ jsxs("div", {
1851
- className: "mb-2 flex min-w-0 items-baseline gap-1.5 border-b\n border-gray-100 pb-1 text-xs",
1852
- children: [/* @__PURE__ */ jsx("code", {
1853
- className: "shrink-0 text-gray-500",
1854
- children: element.tagName
1855
- }), element.text && /* @__PURE__ */ jsx("span", {
1856
- className: "truncate text-gray-400",
1857
- children: element.text
1858
- })]
1859
- }), /* @__PURE__ */ jsx(FieldGroup, {
1860
- bindings: group,
1861
- onNodeChange
1862
- })]
1863
- }, group[0].id);
1864
- })
1865
- ]
1866
- });
1867
- };
1868
- //#endregion
1869
- //#region src/components/dnd/layout.tsx
1870
- const PaletteItems = ({ subject }) => {
1871
- const { palette: { items, onAdd }, isMobile } = useDndRegions(subject);
1872
- return /* @__PURE__ */ jsx(Space, {
1873
- orientation: "vertical",
1874
- align: "start",
1875
- children: items.map((item) => /* @__PURE__ */ jsx(DefaultDraggableItem, {
1876
- item,
1877
- onAdd,
1878
- tapToAdd: isMobile
1879
- }, item.id))
1880
- });
1881
- };
1882
- const PanelFields = ({ subject }) => {
1883
- const { panel: { item, onDelete, onMoveUp, onMoveDown, canMoveUp, canMoveDown, bindings, onNodeChange, readOnly } } = useDndRegions(subject);
1884
- return /* @__PURE__ */ jsx(Panel$1, {
1885
- item,
1886
- onDelete,
1887
- onMoveUp,
1888
- onMoveDown,
1889
- canMoveUp,
1890
- canMoveDown,
1891
- bindings,
1892
- onNodeChange,
1893
- readOnly
1894
- });
1895
- };
1896
- const Palette = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
1897
- className: cn("h-full overflow-y-auto bg-gray-50 p-4", className),
1898
- ...restProps,
1899
- children: /* @__PURE__ */ jsx(PaletteItems, { subject: "<Live.Dnd.Palette>" })
1900
- });
1901
- const Canvas = ({ className, style, ...restProps }) => {
1902
- const { canvas, documentError } = useDndRegions("<Live.Dnd.Canvas>");
1903
- const inspector = useDndInspector();
1904
- const messages = useLiveMessages();
1905
- return /* @__PURE__ */ jsxs("div", {
1906
- className: cn("relative h-full w-full overflow-y-auto", className),
1907
- "data-frame-container": true,
1908
- style: {
1909
- isolation: "isolate",
1910
- contain: "layout style",
1911
- transform: "translateZ(0)",
1912
- ...style
1913
- },
1914
- ...restProps,
1915
- children: [canvas, !documentError && /* @__PURE__ */ jsx("div", {
1916
- className: "pointer-events-none sticky bottom-0 z-70 h-0",
1917
- children: /* @__PURE__ */ jsx("div", {
1918
- className: "pointer-events-auto absolute bottom-3 left-3",
1919
- children: /* @__PURE__ */ jsx(Button, {
1920
- icon: /* @__PURE__ */ jsx(Crosshair, {}),
1921
- color: "primary",
1922
- variant: inspector.active ? "solid" : "outlined",
1923
- "aria-pressed": inspector.active,
1924
- "aria-label": messages.canvas.pickElement,
1925
- title: inspector.active ? messages.canvas.pickElementActive : messages.canvas.pickElement,
1926
- onClick: inspector.toggle
1927
- })
1928
- })
1929
- })]
1930
- });
1931
- };
1932
- const Panel = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
1933
- className: cn("h-full", className),
1934
- ...restProps,
1935
- children: /* @__PURE__ */ jsx(PanelFields, { subject: "<Live.Dnd.Panel>" })
1936
- });
1937
- const Layout = ({ palette, panel }) => {
1938
- const { isMobile, selectedId, clearSelection, paletteOpen, setPaletteOpen } = useDndRegions("<Live.Dnd.Layout>");
1939
- const messages = useLiveMessages();
1940
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1941
- isMobile ? /* @__PURE__ */ jsx(Canvas, {}) : /* @__PURE__ */ jsxs(Splitter, {
1942
- withHandle: true,
1943
- orientation: "horizontal",
1944
- children: [
1945
- /* @__PURE__ */ jsx(Splitter.Panel, {
1946
- defaultSize: "20%",
1947
- minSize: "15%",
1948
- maxSize: "35%",
1949
- collapsible: true,
1950
- children: palette ?? /* @__PURE__ */ jsx(Palette, {})
1951
- }),
1952
- /* @__PURE__ */ jsx(Splitter.Panel, {
1953
- defaultSize: "60%",
1954
- children: /* @__PURE__ */ jsx(Canvas, {})
1955
- }),
1956
- /* @__PURE__ */ jsx(Splitter.Panel, {
1957
- defaultSize: "20%",
1958
- minSize: "15%",
1959
- maxSize: "35%",
1960
- collapsible: true,
1961
- children: panel ?? /* @__PURE__ */ jsx(Panel, {})
1962
- })
1963
- ]
1964
- }),
1965
- /* @__PURE__ */ jsx(Button, {
1966
- type: "primary",
1967
- shape: "circle",
1968
- icon: /* @__PURE__ */ jsx(LayoutGrid, {}),
1969
- "aria-label": messages.canvas.palette,
1970
- className: "fixed right-4 bottom-4 z-20 md:hidden",
1971
- onClick: () => setPaletteOpen(true)
1972
- }),
1973
- /* @__PURE__ */ jsx(Drawer, {
1974
- open: isMobile && paletteOpen,
1975
- onClose: () => setPaletteOpen(false),
1976
- direction: "bottom",
1977
- size: "large",
1978
- title: messages.canvas.palette,
1979
- children: palette ?? /* @__PURE__ */ jsx(PaletteItems, { subject: "<Live.Dnd.Layout>" })
1980
- }),
1981
- /* @__PURE__ */ jsx(Drawer, {
1982
- open: isMobile && Boolean(selectedId),
1983
- onClose: clearSelection,
1984
- direction: "bottom",
1985
- size: "large",
1986
- title: messages.canvas.properties,
1987
- children: panel ?? /* @__PURE__ */ jsx(PanelFields, { subject: "<Live.Dnd.Layout>" })
1988
- })
1989
- ] });
1990
- };
1991
- //#endregion
1992
- //#region src/components/dnd/state/use-delete-flow.ts
1993
- const useDeleteFlow = ({ sections, remove, onBeforeDelete }) => {
1994
- const removeRef = useRef(remove);
1995
- useEffect(() => {
1996
- removeRef.current = remove;
1997
- });
1998
- return (id, onDeleted) => {
1999
- const section = sections.find((candidate) => candidate.id === id);
2000
- if (!onBeforeDelete || !section) {
2001
- remove(id);
2002
- onDeleted?.();
2003
- return;
2004
- }
2005
- const decide = (allowed) => {
2006
- if (allowed) {
2007
- removeRef.current(id);
2008
- onDeleted?.();
2009
- }
2010
- };
2011
- try {
2012
- const answer = onBeforeDelete(section);
2013
- if (typeof answer === "boolean") {
2014
- decide(answer);
2015
- return;
2016
- }
2017
- answer.then(decide, (error) => {
2018
- console.error("onBeforeDelete rejected; the section was kept", error);
2019
- });
2020
- } catch (error) {
2021
- console.error("onBeforeDelete threw; the section was kept", error);
2022
- }
2023
- };
2024
- };
2025
- //#endregion
2026
- //#region src/components/dnd/state/use-dnd-keyboard.ts
2027
- const KEYBOARD_CODES = {
2028
- start: ["Space"],
2029
- cancel: ["Escape"],
2030
- end: ["Space", "Enter"]
2031
- };
2032
- const createNodeRegistry = () => {
2033
- const nodes = /* @__PURE__ */ new Map();
2034
- const callbacks = /* @__PURE__ */ new Map();
2035
- return {
2036
- get: (id) => nodes.get(id),
2037
- register: (id) => {
2038
- let callback = callbacks.get(id);
2039
- if (!callback) {
2040
- callback = (node) => {
2041
- if (node) nodes.set(id, node);
2042
- else {
2043
- nodes.delete(id);
2044
- callbacks.delete(id);
2045
- }
2046
- };
2047
- callbacks.set(id, callback);
2048
- }
2049
- return callback;
2050
- }
2051
- };
2052
- };
2053
- const useDndKeyboard = ({ sections, selectedId, selectedIndex, selectOnly, move, requestDelete }) => {
2054
- const { announcements: say } = useLiveMessages();
2055
- const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 10 } }), useSensor(KeyboardSensor, {
2056
- coordinateGetter: sortableKeyboardCoordinates,
2057
- keyboardCodes: KEYBOARD_CODES
2058
- }));
2059
- const nameOf = (id, data) => {
2060
- return paletteSectionOf(data)?.name ?? sections.find((section) => section.id === id)?.name ?? say.section;
2061
- };
2062
- const positionOf = (id) => sections.findIndex((section) => section.id === id) + 1;
2063
- const announcements = {
2064
- onDragStart: ({ active }) => say.pickedUp(nameOf(active.id, active.data)),
2065
- onDragOver: ({ active, over }) => over && positionOf(over.id) > 0 ? say.movedTo(nameOf(active.id, active.data), positionOf(over.id), sections.length) : say.notOver(nameOf(active.id, active.data)),
2066
- onDragEnd: ({ active, over }) => over && positionOf(over.id) > 0 ? say.droppedAt(nameOf(active.id, active.data), positionOf(over.id), sections.length) : say.dropped(nameOf(active.id, active.data)),
2067
- onDragCancel: ({ active }) => say.cancelled(nameOf(active.id, active.data))
2068
- };
2069
- const screenReaderInstructions = { draggable: say.instructions };
2070
- const [sectionNodes] = useState(createNodeRegistry);
2071
- const focusSection = (id) => sectionNodes.get(id)?.focus();
2072
- const onNavigate = (fromId, to) => {
2073
- const from = sections.findIndex((section) => section.id === fromId);
2074
- const target = sections[to === "first" ? 0 : to === "last" ? sections.length - 1 : from + (to === "next" ? 1 : -1)];
2075
- if (from < 0 || !target || target.id === fromId) return;
2076
- selectOnly(target.id);
2077
- focusSection(target.id);
2078
- };
2079
- const onDeleteKey = (id) => {
2080
- const index = sections.findIndex((section) => section.id === id);
2081
- const neighbor = sections[index + 1] ?? sections[index - 1];
2082
- requestDelete(id, () => {
2083
- if (neighbor) focusSection(neighbor.id);
2084
- });
2085
- };
2086
- const onMoveButton = (id, direction) => {
2087
- const to = sections.findIndex((section) => section.id === id) + (direction === "up" ? -1 : 1);
2088
- move(id, direction);
2089
- if (to === 0 || to === sections.length - 1) focusSection(id);
2090
- };
2091
- useEffect(() => {
2092
- if (selectedId === null) return;
2093
- const frame = requestAnimationFrame(() => {
2094
- sectionNodes.get(selectedId)?.scrollIntoView?.({ block: "nearest" });
2095
- });
2096
- return () => cancelAnimationFrame(frame);
2097
- }, [
2098
- sectionNodes,
2099
- selectedId,
2100
- selectedIndex
2101
- ]);
2102
- return {
2103
- sensors,
2104
- announcements,
2105
- screenReaderInstructions,
2106
- sectionNodes,
2107
- onNavigate,
2108
- onDeleteKey,
2109
- onMoveButton
2110
- };
2111
- };
2112
- //#endregion
2113
- //#region src/components/dnd/state/use-inspector-state.ts
2114
- const useInspectorState = ({ selectedId, selectOnly, sectionNodes, onNodePick }) => {
2115
- const [inspecting, setInspecting] = useState(false);
2116
- const [picked, setPicked] = useState(null);
2117
- const [hoverRect, setHoverRect] = useState(null);
2118
- const [fieldTarget, setFieldTarget] = useState(null);
2119
- const [fieldRect, setFieldRect] = useState(null);
2120
- if (picked && picked.sectionId !== selectedId) setPicked(null);
2121
- const stopInspecting = useCallback(() => {
2122
- setInspecting(false);
2123
- setHoverRect(null);
2124
- }, []);
2125
- useEffect(() => {
2126
- if (!inspecting) return;
2127
- const onKeyDown = (e) => {
2128
- if (e.key === "Escape") stopInspecting();
2129
- };
2130
- window.addEventListener("keydown", onKeyDown);
2131
- return () => window.removeEventListener("keydown", onKeyDown);
2132
- }, [inspecting, stopInspecting]);
2133
- useEffect(() => {
2134
- const section = selectedId === null ? null : sectionNodes.get(selectedId);
2135
- if (!fieldTarget || !section) return;
2136
- let frame = 0;
2137
- const measure = () => {
2138
- cancelAnimationFrame(frame);
2139
- frame = requestAnimationFrame(() => {
2140
- const element = findElementById(section, fieldTarget);
2141
- setFieldRect(element ? viewportRect(element) : null);
2142
- });
2143
- };
2144
- measure();
2145
- window.addEventListener("scroll", measure, true);
2146
- window.addEventListener("resize", measure);
2147
- return () => {
2148
- cancelAnimationFrame(frame);
2149
- window.removeEventListener("scroll", measure, true);
2150
- window.removeEventListener("resize", measure);
2151
- };
2152
- }, [
2153
- fieldTarget,
2154
- selectedId,
2155
- sectionNodes
2156
- ]);
2157
- const inspector = {
2158
- active: inspecting,
2159
- activate: () => setInspecting(true),
2160
- deactivate: stopInspecting,
2161
- toggle: () => inspecting ? stopInspecting() : setInspecting(true),
2162
- picked,
2163
- highlight: setFieldTarget
2164
- };
2165
- const onInspectMove = (section, overlay, x, y) => {
2166
- const element = pickElement(section, overlay, x, y);
2167
- setHoverRect(element ? viewportRect(element) : null);
2168
- };
2169
- const onInspectPick = (sectionId, section, overlay, x, y) => {
2170
- const id = pickElement(section, overlay, x, y)?.getAttribute(DATA_ATTR.ID);
2171
- if (!id) return;
2172
- const pick = {
2173
- id,
2174
- sectionId
2175
- };
2176
- selectOnly(sectionId);
2177
- setPicked(pick);
2178
- stopInspecting();
2179
- onNodePick?.(pick);
2180
- };
2181
- return {
2182
- inspector,
2183
- inspecting,
2184
- onInspectMove,
2185
- onInspectPick,
2186
- onInspectLeave: () => setHoverRect(null),
2187
- highlightRect: inspecting ? hoverRect : fieldTarget ? fieldRect : null
2188
- };
2189
- };
2190
- //#endregion
2191
- //#region src/components/dnd/state/use-section-document.ts
2192
- const createLastParsed = () => {
2193
- let last = null;
2194
- return {
2195
- read: (containerId) => last && last.containerId === containerId ? last.document : void 0,
2196
- write: (containerId, document) => {
2197
- last = {
2198
- containerId,
2199
- document
2200
- };
2201
- }
2202
- };
2203
- };
2204
- const useSectionDocument = (value, onChange, { containerId, sectionNameFallback, onBlockedEdit } = {}) => {
2205
- const { setCode } = usePreview();
2206
- const [selectedId, setSelectedId] = useState(null);
2207
- const readSections = useCallback((code) => extractSections(code, {
2208
- containerId,
2209
- sectionNameFallback
2210
- }), [containerId, sectionNameFallback]);
2211
- const document = useMemo(() => fillSectionIds(value, void 0, { containerId }), [containerId, value]);
2212
- const [lastParsed] = useState(createLastParsed);
2213
- const view = useMemo(() => {
2214
- const inspection = inspectDocument(document, { containerId });
2215
- if (inspection.ok) {
2216
- lastParsed.write(containerId, document);
2217
- return {
2218
- document,
2219
- problem: null,
2220
- stale: false
2221
- };
2222
- }
2223
- const { ok: _ok, ...problem } = inspection;
2224
- const last = problem.reason === "parse-error" ? lastParsed.read(containerId) : void 0;
2225
- return {
2226
- document: last ?? document,
2227
- problem,
2228
- stale: last !== void 0
2229
- };
2230
- }, [
2231
- containerId,
2232
- document,
2233
- lastParsed
2234
- ]);
2235
- const sections = useMemo(() => readSections(view.document), [readSections, view.document]);
2236
- const [previewCache] = useState(() => createSectionPreviewCache());
2237
- const previews = useMemo(() => previewCache.compute(view.document, sections, { containerId }), [
2238
- containerId,
2239
- previewCache,
2240
- sections,
2241
- view.document
2242
- ]);
2243
- const selectedIndex = sections.findIndex((s) => s.id === selectedId);
2244
- const selectedSection = selectedIndex >= 0 ? sections[selectedIndex] : void 0;
2245
- const pendingRef = useRef(null);
2246
- useEffect(() => {
2247
- pendingRef.current = null;
2248
- });
2249
- const latestDocument = useCallback(() => {
2250
- const pending = pendingRef.current;
2251
- return pending?.from === document ? pending.code : document;
2252
- }, [document]);
2253
- const latestSections = useCallback(() => readSections(latestDocument()), [latestDocument, readSections]);
2254
- const commit = useCallback((nextSections) => {
2255
- const nextCode = fillSectionIds(replaceSections(latestDocument(), nextSections.map((s) => s.code), { containerId }), void 0, { containerId });
2256
- pendingRef.current = {
2257
- from: document,
2258
- code: nextCode
2259
- };
2260
- onChange?.(nextCode);
2261
- setCode(nextCode);
2262
- return readSections(nextCode);
2263
- }, [
2264
- containerId,
2265
- document,
2266
- latestDocument,
2267
- onChange,
2268
- readSections,
2269
- setCode
2270
- ]);
2271
- const getCommittedSection = useCallback((id) => {
2272
- const pending = pendingRef.current;
2273
- if (pending?.from !== document) return;
2274
- return readSections(pending.code).find((s) => s.id === id);
2275
- }, [document, readSections]);
2276
- const { problem, stale } = view;
2277
- const refuse = useCallback(() => {
2278
- if (!stale || !problem) return false;
2279
- onBlockedEdit?.(problem);
2280
- return true;
2281
- }, [
2282
- onBlockedEdit,
2283
- problem,
2284
- stale
2285
- ]);
2286
- return {
2287
- sections,
2288
- previews,
2289
- selectedId,
2290
- selectedSection,
2291
- selectedIndex,
2292
- select: useCallback((id) => {
2293
- setSelectedId((prev) => prev === id ? null : id);
2294
- }, []),
2295
- selectOnly: useCallback((id) => setSelectedId(id), []),
2296
- clearSelection: useCallback(() => setSelectedId(null), []),
2297
- add: useCallback((section, atIndex) => {
2298
- if (refuse()) return;
2299
- const current = latestSections();
2300
- const next = { code: section.code };
2301
- commit(atIndex === void 0 || atIndex < 0 ? [...current, next] : [
2302
- ...current.slice(0, atIndex),
2303
- next,
2304
- ...current.slice(atIndex)
2305
- ]);
2306
- }, [
2307
- commit,
2308
- refuse,
2309
- latestSections
2310
- ]),
2311
- remove: useCallback((id) => {
2312
- if (refuse()) return;
2313
- const current = latestSections();
2314
- const index = current.findIndex((s) => s.id === id);
2315
- if (index < 0) return;
2316
- if (id === selectedId) setSelectedId(null);
2317
- commit(current.filter((_, i) => i !== index));
2318
- }, [
2319
- commit,
2320
- refuse,
2321
- latestSections,
2322
- selectedId
2323
- ]),
2324
- copy: useCallback((id) => {
2325
- if (refuse()) return;
2326
- const current = latestSections();
2327
- const index = current.findIndex((s) => s.id === id);
2328
- const source = current[index];
2329
- if (!source) return;
2330
- const committed = commit([
2331
- ...current.slice(0, index + 1),
2332
- { code: replaceIds(source.code) },
2333
- ...current.slice(index + 1)
2334
- ]);
2335
- setSelectedId(committed[index + 1]?.id ?? null);
2336
- }, [
2337
- commit,
2338
- refuse,
2339
- latestSections
2340
- ]),
2341
- move: useCallback((id, direction) => {
2342
- if (refuse()) return;
2343
- const current = latestSections();
2344
- const index = current.findIndex((s) => s.id === id);
2345
- const targetIndex = direction === "up" ? index - 1 : index + 1;
2346
- if (index < 0 || targetIndex < 0 || targetIndex >= current.length) return;
2347
- commit(arrayMove(current, index, targetIndex));
2348
- }, [
2349
- commit,
2350
- refuse,
2351
- latestSections
2352
- ]),
2353
- reorder: useCallback((activeId, overId) => {
2354
- if (refuse()) return;
2355
- const current = latestSections();
2356
- const prevIndex = current.findIndex((s) => s.id === activeId);
2357
- const nextIndex = current.findIndex((s) => s.id === overId);
2358
- if (prevIndex < 0 || nextIndex < 0 || prevIndex === nextIndex) return;
2359
- commit(arrayMove(current, prevIndex, nextIndex));
2360
- }, [
2361
- commit,
2362
- refuse,
2363
- latestSections
2364
- ]),
2365
- patch: useCallback((next) => {
2366
- if (refuse()) return;
2367
- const current = latestSections();
2368
- commit(current.map((s) => s.id === next.id ? {
2369
- ...s,
2370
- ...next
2371
- } : s));
2372
- }, [
2373
- commit,
2374
- refuse,
2375
- latestSections
2376
- ]),
2377
- getCommittedSection,
2378
- problem,
2379
- stale
2380
- };
2381
- };
2382
- //#endregion
2383
- //#region src/components/dnd/dnd.tsx
2384
- const describeUpdateFailure = (failure, label, { editErrors: m }) => {
2385
- switch (failure?.reason) {
2386
- case "attribute-not-found": return {
2387
- title: m.updateFailed(label),
2388
- description: m.attributeNotFound(failure.property)
2389
- };
2390
- case "binding-not-declared": return {
2391
- title: m.updateFailed(label),
2392
- description: m.bindingNotDeclared({
2393
- label,
2394
- property: failure.property
2395
- })
2396
- };
2397
- case "duplicate-binding": return {
2398
- title: m.updateFailed(label),
2399
- description: m.duplicateBinding({
2400
- count: failure.count,
2401
- label,
2402
- property: failure.property
2403
- })
2404
- };
2405
- case "reserved-property": return {
2406
- title: m.cannotEdit(label),
2407
- description: m.reservedProperty(failure.property)
2408
- };
2409
- case "required-property": return {
2410
- title: m.cannotRemove(label),
2411
- description: m.requiredProperty(failure.property)
2412
- };
2413
- case "no-binding": return {
2414
- title: m.updateFailed(label),
2415
- description: m.noBinding
2416
- };
2417
- case "element-not-found": return {
2418
- title: m.updateFailed(label),
2419
- description: m.elementNotFound
2420
- };
2421
- case "self-closing": return {
2422
- title: m.cannotEdit(label),
2423
- description: m.selfClosing(failure.property)
2424
- };
2425
- case "unsupported-syntax": return {
2426
- title: m.cannotEdit(label),
2427
- description: m.unsupportedSyntax(failure.property)
2428
- };
2429
- case "parse-error": return {
2430
- title: m.updateFailed(label),
2431
- description: failure.error instanceof Error ? failure.error.message : m.checkConsole
2432
- };
2433
- default: return { title: m.updateFailed(label) };
2434
- }
2435
- };
2436
- const blockedEditError = (error, { editErrors: m }) => ({
2437
- type: "parse",
2438
- target: "document",
2439
- reason: "parse-error",
2440
- error,
2441
- title: m.syntaxError,
2442
- description: m.syntaxErrorDetail
2443
- });
2444
- const conditionalModifiers = (args) => {
2445
- const { active } = args;
2446
- if (paletteSectionOf(active?.data)) return args.transform;
2447
- return restrictToVerticalAxis(args);
2448
- };
2449
- const NO_MODULES = {};
2450
- 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, bindings: bindingRegistry, bindingKeys, onNodePick, children, ...restProps }) => {
2451
- const modules = useStableModules(_modules);
2452
- const [mobilePaletteOpen, setMobilePaletteOpen] = useState(false);
2453
- const { breakpoint } = useResponsiveSize();
2454
- const isMobile = breakpoint.current === "xs" || breakpoint.current === "sm";
2455
- const { code } = usePreview();
2456
- const isForced = (section) => {
2457
- try {
2458
- return shouldForceSectionFallback?.(section) === true;
2459
- } catch (error) {
2460
- console.error("shouldForceSectionFallback threw", error);
2461
- return false;
2462
- }
2463
- };
2464
- const value = _value === void 0 ? code : _value;
2465
- const messages = useLiveMessages();
2466
- const reportError = onEditError ?? toastEditError;
2467
- const reportErrorRef = useRef(reportError);
2468
- const messagesRef = useRef(messages);
2469
- useEffect(() => {
2470
- reportErrorRef.current = reportError;
2471
- messagesRef.current = messages;
2472
- });
2473
- const onBlockedEdit = useCallback((problem) => {
2474
- if (problem.reason === "parse-error") reportErrorRef.current(blockedEditError(problem.error, messagesRef.current));
2475
- }, []);
2476
- const { sections, previews, selectedId, selectedSection, selectedIndex, select, selectOnly, clearSelection, add: addSection, remove, copy: onCopy, move: moveSection, reorder, patch, getCommittedSection, problem, stale } = useSectionDocument(value, _onChange, {
2477
- containerId,
2478
- sectionNameFallback,
2479
- onBlockedEdit
2480
- });
2481
- const missingContainer = problem?.reason === "container-not-found" ? problem.containerId : null;
2482
- const onDragEnd = (event) => {
2483
- const { active, over } = event;
2484
- if (!over) return;
2485
- const section = paletteSectionOf(active.data);
2486
- if (section) {
2487
- const atBottom = over.id === "sortable-area" || over.id === "sortable-area-bottom";
2488
- addSection(section, atBottom ? void 0 : sections.findIndex((s) => s.id === over.id));
2489
- return;
2490
- }
2491
- if (active.id !== over.id && sections.some((s) => s.id === active.id)) reorder(String(active.id), String(over.id));
2492
- };
2493
- const onSelect = (id) => select(id);
2494
- const requestDelete = useDeleteFlow({
2495
- sections,
2496
- remove,
2497
- onBeforeDelete
2498
- });
2499
- const onDelete = (id) => requestDelete(id);
2500
- const { sensors, announcements, screenReaderInstructions, sectionNodes, onNavigate, onDeleteKey, onMoveButton } = useDndKeyboard({
2501
- sections,
2502
- selectedId,
2503
- selectedIndex,
2504
- selectOnly,
2505
- move: moveSection,
2506
- requestDelete
2507
- });
2508
- const { inspector, inspecting, onInspectMove, onInspectPick, onInspectLeave, highlightRect } = useInspectorState({
2509
- selectedId,
2510
- selectOnly,
2511
- sectionNodes,
2512
- onNodePick
2513
- });
2514
- const onChange = (next) => {
2515
- if (!next.id) return;
2516
- patch(next);
2517
- };
2518
- const bindingOptions = useMemo(() => ({
2519
- bindings: bindingRegistry,
2520
- bindingKeys
2521
- }), [bindingRegistry, bindingKeys]);
2522
- const selectedCode = selectedSection?.code;
2523
- const selectedSectionId = selectedSection?.id;
2524
- const { fields, updatedCode, parseError } = useMemo(() => {
2525
- if (!selectedCode) return {
2526
- fields: [],
2527
- updatedCode: "",
2528
- parseError: null
2529
- };
2530
- try {
2531
- const updated = fillIdsFrom(selectedCode, selectedSectionId ?? "");
2532
- return {
2533
- fields: extract(updated, bindingOptions),
2534
- updatedCode: updated !== selectedCode ? updated : selectedCode,
2535
- parseError: null
2536
- };
2537
- } catch (e) {
2538
- console.warn("⚠️ Parsing error", e);
2539
- return {
2540
- fields: [],
2541
- updatedCode: "",
2542
- parseError: { error: e }
2543
- };
2544
- }
2545
- }, [
2546
- selectedCode,
2547
- selectedSectionId,
2548
- bindingOptions
2549
- ]);
2550
- useEffect(() => {
2551
- if (missingContainer !== null) reportErrorRef.current({
2552
- type: "parse",
2553
- target: "document",
2554
- reason: "container-not-found",
2555
- containerId: missingContainer,
2556
- title: messagesRef.current.editErrors.missingContainer(missingContainer),
2557
- description: messagesRef.current.editErrors.missingContainerDetail(missingContainer)
2558
- });
2559
- }, [missingContainer]);
2560
- useEffect(() => {
2561
- if (parseError) reportErrorRef.current({
2562
- type: "parse",
2563
- target: "section",
2564
- error: parseError.error,
2565
- title: messagesRef.current.editErrors.sectionParseFailed,
2566
- description: messagesRef.current.editErrors.checkConsole
2567
- });
2568
- }, [parseError]);
2569
- const commitChanges = (changes) => {
2570
- if (changes.length === 0) return;
2571
- if (stale && problem?.reason === "parse-error") {
2572
- reportError(blockedEditError(problem.error, messages));
2573
- return;
2574
- }
2575
- const committed = selectedSection && getCommittedSection(selectedSection.id)?.code;
2576
- const result = updateAll(committed && committed !== selectedCode ? committed : updatedCode, changes.map(({ id, label, property, value: changeValue }) => ({
2577
- dataId: id,
2578
- label,
2579
- property,
2580
- value: changeValue
2581
- })), bindingOptions);
2582
- if (!result.success) {
2583
- const { id, label, property } = changes[result.index];
2584
- reportError({
2585
- type: "update",
2586
- id,
2587
- label,
2588
- property,
2589
- failure: result.failure,
2590
- ...describeUpdateFailure(result.failure, label, messages)
2591
- });
2592
- return;
2593
- }
2594
- if (selectedSection) onChange({
2595
- ...selectedSection,
2596
- code: result.code
2597
- });
2598
- };
2599
- const onFieldChange = (change) => commitChanges([change]);
2600
- const bindingFields = useMemo(() => fields.flatMap((node) => resolvePanelBindings(node)?.bindings ?? []), [fields]);
2601
- const bindings = withPanelCommit(bindingFields, onFieldChange);
2602
- useEffect(() => {
2603
- if (frame?.scripts?.length) preloadScripts(frame.scripts);
2604
- }, [frame?.scripts]);
2605
- const palette = {
2606
- items: items?.length ? items : PALETTE_SECTIONS,
2607
- onAdd: (section) => {
2608
- addSection(section);
2609
- setMobilePaletteOpen(false);
2610
- }
2611
- };
2612
- const panel = {
2613
- item: selectedSection,
2614
- onChange,
2615
- onDelete,
2616
- onMoveUp: () => moveSection(selectedId, "up"),
2617
- onMoveDown: () => moveSection(selectedId, "down"),
2618
- canMoveUp: selectedIndex > 0,
2619
- canMoveDown: selectedIndex >= 0 && selectedIndex < sections.length - 1,
2620
- bindings,
2621
- onNodeChange: onFieldChange,
2622
- onNodesChange: commitChanges,
2623
- readOnly: stale,
2624
- bindingOptions
2625
- };
2626
- const canvas = /* @__PURE__ */ jsxs(Fragment, { children: [
2627
- stale && /* @__PURE__ */ jsx("div", {
2628
- role: "status",
2629
- className: cn("sticky top-0 z-70", "border-b border-amber-200 bg-amber-50 px-3 py-2", "text-sm text-amber-900"),
2630
- children: messages.canvas.stale
2631
- }),
2632
- /* @__PURE__ */ jsx(Droppable, {
2633
- className: cn(!sections.length && "h-full"),
2634
- children: !sections.length ? /* @__PURE__ */ jsx("div", {
2635
- className: cn("flex items-center justify-center", "h-full", "text-gray-500"),
2636
- children: problem?.reason === "parse-error" ? /* @__PURE__ */ jsxs(Space, {
2637
- orientation: "vertical",
2638
- align: "center",
2639
- children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: messages.canvas.syntaxError }), /* @__PURE__ */ jsx(Typography.Text, { children: messages.canvas.syntaxErrorDetail })]
2640
- }) : missingContainer !== null ? /* @__PURE__ */ jsxs(Space, {
2641
- orientation: "vertical",
2642
- align: "center",
2643
- children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: messages.canvas.missingContainer(missingContainer) }), /* @__PURE__ */ jsx(Typography.Text, { children: messages.canvas.missingContainerDetail(missingContainer) })]
2644
- }) : /* @__PURE__ */ jsxs(Space, {
2645
- orientation: "vertical",
2646
- align: "center",
2647
- children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: messages.canvas.empty }), /* @__PURE__ */ jsx(Typography.Text, { children: isMobile ? messages.canvas.emptyHintTouch : messages.canvas.emptyHintDrag })]
2648
- })
2649
- }) : /* @__PURE__ */ jsx(SortableContext, {
2650
- items: sections.map((s) => s.id),
2651
- strategy: verticalListSortingStrategy,
2652
- children: sections.map((section, index) => /* @__PURE__ */ jsx(Sortable, {
2653
- id: section.id,
2654
- name: section.name,
2655
- selected: selectedId === section.id,
2656
- onClick: () => onSelect(section.id),
2657
- onDelete,
2658
- onCopy,
2659
- onMoveUp: () => onMoveButton(section.id, "up"),
2660
- onMoveDown: () => onMoveButton(section.id, "down"),
2661
- canMoveUp: index > 0,
2662
- canMoveDown: index < sections.length - 1,
2663
- nodeRef: sectionNodes.register(section.id),
2664
- onNavigate: (to) => onNavigate(section.id, to),
2665
- onDeleteKey: () => onDeleteKey(section.id),
2666
- inspecting,
2667
- onInspectMove,
2668
- onInspectPick: (...args) => onInspectPick(section.id, ...args),
2669
- onInspectLeave,
2670
- children: /* @__PURE__ */ jsx(renderer_default, {
2671
- preview: previews[index],
2672
- sectionId: section.id,
2673
- sectionName: section.name,
2674
- sectionCode: section.code,
2675
- forceFallback: isForced(section),
2676
- modules,
2677
- frame,
2678
- dynamicTailwind,
2679
- provider,
2680
- ...props
2681
- })
2682
- }, section.id))
2683
- })
2684
- }),
2685
- /* @__PURE__ */ jsx(InspectorHighlight, { rect: highlightRect })
2686
- ] });
2687
- return /* @__PURE__ */ jsx(SectionFallbackContext.Provider, {
2688
- value: renderSectionFallback,
2689
- children: /* @__PURE__ */ jsxs(DndContext, {
2690
- sensors,
2691
- collisionDetection: closestCenter,
2692
- accessibility: {
2693
- announcements,
2694
- screenReaderInstructions
2695
- },
2696
- modifiers: [conditionalModifiers],
2697
- onDragEnd,
2698
- children: [/* @__PURE__ */ jsx("div", {
2699
- className: cn("relative flex h-full w-full", className),
2700
- ...restProps,
2701
- children: /* @__PURE__ */ jsx(DndEditOptionsContext.Provider, {
2702
- value: {
2703
- renderField,
2704
- reportError,
2705
- bindingOptions
2706
- },
2707
- children: /* @__PURE__ */ jsx(DndRegionContext.Provider, {
2708
- value: {
2709
- palette,
2710
- panel,
2711
- canvas,
2712
- isMobile,
2713
- selectedId,
2714
- clearSelection,
2715
- paletteOpen: mobilePaletteOpen,
2716
- setPaletteOpen: setMobilePaletteOpen,
2717
- documentError: problem?.reason ?? null
2718
- },
2719
- children: /* @__PURE__ */ jsx(DndInspectorContext.Provider, {
2720
- value: inspector,
2721
- children: Children.toArray(children).length ? children : /* @__PURE__ */ jsx(Layout, {})
2722
- })
2723
- })
2724
- })
2725
- }), /* @__PURE__ */ jsx(DragOverlay, { children: /* @__PURE__ */ jsx(Overlay, {
2726
- sections,
2727
- isForced,
2728
- renderProps: {
2729
- fullCode: value,
2730
- modules,
2731
- frame,
2732
- dynamicTailwind,
2733
- ...props,
2734
- containerId
2735
- }
2736
- }) })]
2737
- })
2738
- });
2739
- };
2740
- //#endregion
2741
- //#region src/components/dnd/index.ts
2742
- const Dnd = Dnd$1;
2743
- Dnd.DraggableItem = DraggableItem;
2744
- Dnd.Field = Field;
2745
- Dnd.Palette = Palette;
2746
- Dnd.Canvas = Canvas;
2747
- Dnd.Panel = Panel;
2748
- Dnd.Layout = Layout;
2749
- /** @deprecated Renamed to `useDndItems`. */
2750
- const useItemsEditor = useDndItems;
2751
- /** @deprecated Renamed to `useDndChildren`. */
2752
- const useChildrenEditor = useDndChildren;
2753
- //#endregion
2754
- 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, useDndInspector as v, DraggableItem as y };
2755
-
2756
- //# sourceMappingURL=dnd-UoJILA75.js.map