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