@jbpark/live-editor 2.1.1 → 3.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 (40) hide show
  1. package/dist/{ast-CAhr9WXv.js → ast-DRg6HV4H.js} +2 -2
  2. package/dist/ast-DRg6HV4H.js.map +1 -0
  3. package/dist/{boundary-DQiKrXhI.js → boundary-BF7iZp6e.js} +2 -2
  4. package/dist/{boundary-DQiKrXhI.js.map → boundary-BF7iZp6e.js.map} +1 -1
  5. package/dist/{context-CJ0_ptf4.js → context-DH70wj9d.js} +3 -3
  6. package/dist/{context-CJ0_ptf4.js.map → context-DH70wj9d.js.map} +1 -1
  7. package/dist/{core-D5Hn-6ow.js → core-CbuS_p_d.js} +2 -2
  8. package/dist/{core-D5Hn-6ow.js.map → core-CbuS_p_d.js.map} +1 -1
  9. package/dist/dnd/index.d.ts +2 -2
  10. package/dist/dnd/index.js +2 -2
  11. package/dist/{dnd-C1YGcp4k.js → dnd-Bl_oqayG.js} +672 -584
  12. package/dist/dnd-Bl_oqayG.js.map +1 -0
  13. package/dist/{document-CgUTKVQo.js → document-C6bNHl7l.js} +2 -2
  14. package/dist/document-C6bNHl7l.js.map +1 -0
  15. package/dist/editor/index.js +2 -2
  16. package/dist/{editor-Ds-mUYs_.js → editor-Ct9wuhLm.js} +3 -3
  17. package/dist/{editor-Ds-mUYs_.js.map → editor-Ct9wuhLm.js.map} +1 -1
  18. package/dist/error/index.js +2 -2
  19. package/dist/{error-B5MciRP7.js → error-lsJey-it.js} +2 -2
  20. package/dist/{error-B5MciRP7.js.map → error-lsJey-it.js.map} +1 -1
  21. package/dist/index-Bqc5kUAG.d.ts +162 -0
  22. package/dist/index.d.ts +8 -4
  23. package/dist/index.js +5 -5
  24. package/dist/index.js.map +1 -1
  25. package/dist/preview/index.js +1 -1
  26. package/dist/{preview-DtBbnlwy.js → preview-CNVXyDgY.js} +4 -4
  27. package/dist/{preview-DtBbnlwy.js.map → preview-CNVXyDgY.js.map} +1 -1
  28. package/dist/provider/index.js +1 -1
  29. package/dist/{use-dynamic-tailwind-cdaivJXJ.js → use-dynamic-tailwind-C0Onss1A.js} +2 -2
  30. package/dist/use-dynamic-tailwind-C0Onss1A.js.map +1 -0
  31. package/dist/utils/ast/index.js +2 -2
  32. package/dist/utils/index.js +2 -2
  33. package/dist/{utils-DbfbS1yV.js → utils-BwEgY5pe.js} +2 -2
  34. package/dist/{utils-DbfbS1yV.js.map → utils-BwEgY5pe.js.map} +1 -1
  35. package/package.json +1 -1
  36. package/dist/ast-CAhr9WXv.js.map +0 -1
  37. package/dist/dnd-C1YGcp4k.js.map +0 -1
  38. package/dist/document-CgUTKVQo.js.map +0 -1
  39. package/dist/index-9bvWkK07.d.ts +0 -98
  40. package/dist/use-dynamic-tailwind-cdaivJXJ.js.map +0 -1
@@ -1,11 +1,11 @@
1
- import { C as __toESM, b as DRAGGABLE_ITEMS, f as nanoid, g as BINDING_PROP, m as require_lib, n as createSectionPreviewCache, r as fillSectionIds, y as DEFAULT_TEMPLATE } from "./document-CgUTKVQo.js";
2
- import { d as replaceSections, o as extractSections, r as cn, s as generateSection, u as preloadScripts } from "./utils-DbfbS1yV.js";
1
+ import { C as __toESM, b as DRAGGABLE_ITEMS, f as nanoid, g as BINDING_PROP, m as require_lib, n as createSectionPreviewCache, r as fillSectionIds, y as DEFAULT_TEMPLATE } from "./document-C6bNHl7l.js";
2
+ import { d as replaceSections, o as extractSections, r as cn, s as generateSection, u as preloadScripts } from "./utils-BwEgY5pe.js";
3
3
  import { i as usePreview } from "./states-Ci1AvoQ9.js";
4
- import { A as parseValue, D as extractObjectProperties, E as extractNodeValue, M as generateCode, S as parseBinding, _ as moveSelectedIndices, a as extract, b as getCurrentValue, c as moveArrayItem, d as removeArrayItems, f as updateArrayItemProperty, g as replaceIds, h as fillIds, k as parseArrayExpression, l as moveArrayItems, o as appendArrayItem, p as updateArrayItemValue, r as update, s as duplicateArrayItems, t as validateBindingValue, v as removeIndices, x as getStructuredValue, y as findEditableChildren } from "./ast-CAhr9WXv.js";
5
- import { n as useCompiledModule, r as Frame, t as useDynamicTailwind } from "./use-dynamic-tailwind-cdaivJXJ.js";
6
- import { n as Error, t as ErrorBoundary } from "./boundary-DQiKrXhI.js";
7
- import { t as Core } from "./core-D5Hn-6ow.js";
8
- import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { A as parseValue, D as extractObjectProperties, E as extractNodeValue, M as generateCode, S as parseBinding, _ as moveSelectedIndices, a as extract, b as getCurrentValue, c as moveArrayItem, d as removeArrayItems, f as updateArrayItemProperty, g as replaceIds, h as fillIds, k as parseArrayExpression, l as moveArrayItems, o as appendArrayItem, p as updateArrayItemValue, r as update, s as duplicateArrayItems, t as validateBindingValue, v as removeIndices, x as getStructuredValue, y as findEditableChildren } from "./ast-DRg6HV4H.js";
5
+ import { n as useCompiledModule, r as Frame, t as useDynamicTailwind } from "./use-dynamic-tailwind-C0Onss1A.js";
6
+ import { t as Core } from "./core-CbuS_p_d.js";
7
+ import { n as Error$1, t as ErrorBoundary } from "./boundary-BF7iZp6e.js";
8
+ import { Children, createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
9
9
  import { Button, Card, Checkbox, ColorPicker, DatePicker, Drawer, Input, RichTextEditor, Select, Space, Splitter, Toast, Typography, Upload } from "@jbpark/ui-kit";
10
10
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
11
  import { DndContext, DragOverlay, PointerSensor, closestCenter, useDndContext, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
@@ -14,6 +14,28 @@ import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy }
14
14
  import { useDebounce, useMultiSelect, useResponsiveSize } from "@jbpark/use-hooks";
15
15
  import { AlertCircle, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bell, Bookmark, Calendar, Camera, Check, ChevronDown, ChevronRight, ChevronUp, Clock, Copy, Download, Edit, Eye, Filter, Heart, Home, Image, Info, LayoutGrid, Link, Mail, Map as Map$1, Menu, MessageCircle, Phone, Plus, Search, Settings, Share, ShoppingCart, Star, Trash, Upload as Upload$1, User, X } from "lucide-react";
16
16
  import { CSS } from "@dnd-kit/utilities";
17
+ //#region src/components/dnd/droppable.tsx
18
+ var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
19
+ const Droppable = ({ children, className }) => {
20
+ const { setNodeRef, isOver, active } = useDroppable({ id: "sortable-area" });
21
+ const { setNodeRef: setBottomRef, isOver: isBottomOver } = useDroppable({ id: "sortable-area-bottom" });
22
+ const isNewItemDragging = active?.data.current?.type === "new-item";
23
+ const shouldHighlight = isOver && isNewItemDragging;
24
+ const shouldHighlightBottom = isBottomOver && isNewItemDragging;
25
+ return /* @__PURE__ */ jsxs("div", {
26
+ ref: setNodeRef,
27
+ className: cn("min-h-full", "border-2 border-dashed p-1", shouldHighlight ? "border-blue-300 bg-blue-50" : "border-gray-200", className),
28
+ children: [children, isNewItemDragging && /* @__PURE__ */ jsx("div", {
29
+ ref: setBottomRef,
30
+ 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"),
31
+ children: /* @__PURE__ */ jsx(Typography.Text, {
32
+ className: "text-sm text-gray-500",
33
+ children: shouldHighlightBottom ? "Drop here" : "Drag here to add at bottom"
34
+ })
35
+ })]
36
+ });
37
+ };
38
+ //#endregion
17
39
  //#region src/components/dnd/draggable.tsx
18
40
  const DraggableItem = ({ item, children }) => {
19
41
  const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
@@ -45,158 +67,151 @@ const DefaultDraggableItem = ({ item, onAdd, tapToAdd = false }) => /* @__PURE__
45
67
  })
46
68
  });
47
69
  //#endregion
48
- //#region src/components/dnd/droppable.tsx
49
- const Droppable = ({ children, className }) => {
50
- const { setNodeRef, isOver, active } = useDroppable({ id: "sortable-area" });
51
- const { setNodeRef: setBottomRef, isOver: isBottomOver } = useDroppable({ id: "sortable-area-bottom" });
52
- const isNewItemDragging = active?.data.current?.type === "new-item";
53
- const shouldHighlight = isOver && isNewItemDragging;
54
- const shouldHighlightBottom = isBottomOver && isNewItemDragging;
55
- return /* @__PURE__ */ jsxs("div", {
56
- ref: setNodeRef,
57
- className: cn("min-h-full", "border-2 border-dashed p-1", shouldHighlight ? "border-blue-300 bg-blue-50" : "border-gray-200", className),
58
- children: [children, isNewItemDragging && /* @__PURE__ */ jsx("div", {
59
- ref: setBottomRef,
60
- 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"),
61
- children: /* @__PURE__ */ jsx(Typography.Text, {
62
- className: "text-sm text-gray-500",
63
- children: shouldHighlightBottom ? "Drop here" : "Drag here to add at bottom"
64
- })
65
- })]
66
- });
70
+ //#region src/components/dnd/layout-context.ts
71
+ const DndRegionContext = createContext(null);
72
+ const useDndRegions = (subject) => {
73
+ const regions = useContext(DndRegionContext);
74
+ if (!regions) throw new Error(`${subject} must be used inside <Live.Dnd>.`);
75
+ return regions;
67
76
  };
77
+ const useDndPalette = () => useDndRegions("useDndPalette()").palette;
78
+ const useDndPanel = () => useDndRegions("useDndPanel()").panel;
79
+ const useDndLayout = () => useDndRegions("useDndLayout()");
68
80
  //#endregion
69
- //#region src/components/dnd/renderer.tsx
70
- const Renderer = ({ preview, headers, modules, frame, dynamicTailwind = false, provider }) => {
71
- const module = useCompiledModule(preview, modules);
72
- const { ref: wrapperRef, css: dynamicCSS } = useDynamicTailwind(preview, dynamicTailwind);
73
- const renderProvider = (component) => {
74
- return provider ? provider(component) : component;
75
- };
76
- if (module?.error) return /* @__PURE__ */ jsx(Error, {
77
- message: module.error,
78
- title: "Compile Error"
81
+ //#region src/components/dnd/panel/use-items-editor.ts
82
+ const resolveLeaf = (render, key) => {
83
+ const leaf = render?.[key];
84
+ return leaf && "type" in leaf ? leaf : null;
85
+ };
86
+ const resolveMap = (render, key) => {
87
+ const leaf = render?.[key];
88
+ return leaf && !("type" in leaf) ? leaf : void 0;
89
+ };
90
+ const bindingsInJSX = (node) => {
91
+ const nodes = extract(generateCode(node));
92
+ const container = nodes.find((n) => n.bindings?.some((b) => b.property === "children"));
93
+ if (container) return [container];
94
+ const bindings = [];
95
+ nodes.forEach((n) => {
96
+ if (n.bindings && n.bindings.length > 0 && n.dataAttributes.some((a) => a.name === "data-id")) bindings.push(n);
97
+ bindings.push(...findEditableChildren(n));
79
98
  });
80
- const Component = module?.exports?.default;
81
- if (!Component) return null;
82
- return /* @__PURE__ */ jsx(Frame, {
83
- ...frame,
84
- autoHeight: true,
85
- children: (container) => /* @__PURE__ */ jsx("div", {
86
- ref: wrapperRef,
87
- className: "w-full overflow-x-hidden",
88
- "data-editor-mode": true,
89
- children: /* @__PURE__ */ jsx(ErrorBoundary, {
90
- resetKeys: [preview],
91
- children: renderProvider(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Component, {
92
- headers,
93
- container
94
- }), dynamicTailwind && dynamicCSS && /* @__PURE__ */ jsx("style", { children: dynamicCSS })] }))
95
- })
96
- })
99
+ return bindings;
100
+ };
101
+ const parseSource = (value) => {
102
+ const ast = parseArrayExpression(value);
103
+ if (!ast) return {
104
+ objectItems: [],
105
+ primitiveItems: [],
106
+ parseError: true
107
+ };
108
+ const objectItems = [];
109
+ const primitiveItems = [];
110
+ ast.elements.filter((element) => Boolean(element)).forEach((element, elementIndex) => {
111
+ if (!import_lib.isObjectExpression(element)) {
112
+ const extracted = extractNodeValue(element);
113
+ primitiveItems.push({
114
+ id: nanoid(6),
115
+ index: primitiveItems.length,
116
+ elementIndex,
117
+ value: extracted.value,
118
+ type: extracted.type
119
+ });
120
+ return;
121
+ }
122
+ const jsxBindings = {};
123
+ const jsxFallbacks = {};
124
+ element.properties.forEach((prop) => {
125
+ if (!import_lib.isObjectProperty(prop) || !import_lib.isIdentifier(prop.key) || !(import_lib.isJSXElement(prop.value) || import_lib.isJSXFragment(prop.value))) return;
126
+ const propertyName = prop.key.name;
127
+ try {
128
+ const found = bindingsInJSX(prop.value);
129
+ if (found.length > 0) jsxBindings[propertyName] = found;
130
+ else jsxFallbacks[propertyName] = generateCode(prop.value);
131
+ } catch (error) {
132
+ console.error(`Failed to parse JSX in property '${propertyName}':`, error);
133
+ }
134
+ });
135
+ objectItems.push({
136
+ id: nanoid(6),
137
+ index: objectItems.length,
138
+ elementIndex,
139
+ editableProperties: extractObjectProperties(element),
140
+ jsxBindings,
141
+ jsxFallbacks
142
+ });
97
143
  });
144
+ return {
145
+ objectItems,
146
+ primitiveItems,
147
+ parseError: false
148
+ };
98
149
  };
99
- var renderer_default = memo(Renderer);
100
- //#endregion
101
- //#region src/components/dnd/sortable.tsx
102
- const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy: _onCopy }) => {
103
- const { attributes, listeners, setNodeRef, transform, transition, isDragging, isOver, active } = useSortable({ id });
104
- const style = {
105
- transform: CSS.Transform.toString(transform),
106
- transition,
107
- opacity: isDragging ? .5 : 1,
108
- cursor: "grab"
150
+ const useItemsEditor = (value, { render, onChange, onNodeChange } = {}) => {
151
+ const { objectItems, primitiveItems, parseError } = useMemo(() => parseSource(value), [value]);
152
+ useEffect(() => {
153
+ if (parseError) Toast.error("Failed to parse items", { description: "Check the console for details." });
154
+ }, [parseError]);
155
+ const kind = primitiveItems.length > 0 && objectItems.length === 0 ? "primitive" : "object";
156
+ const isPrimitive = kind === "primitive";
157
+ const selection = useMultiSelect(isPrimitive ? primitiveItems.length : objectItems.length);
158
+ const commit = (next) => {
159
+ if (next === null) {
160
+ Toast.error("Failed to update this item", { description: "Check the console for details." });
161
+ return;
162
+ }
163
+ onChange?.(next);
109
164
  };
110
- const isNewItemOver = isOver && active?.data.current?.type === "new-item";
111
- const onDelete = (e) => {
112
- e.stopPropagation();
113
- _onDelete?.(id);
165
+ const elementIndicesOf = (indices) => {
166
+ return new Set((isPrimitive ? primitiveItems : objectItems).filter((item) => indices.has(item.index)).map((item) => item.elementIndex));
114
167
  };
115
- const onCopy = (e) => {
116
- e.stopPropagation();
117
- _onCopy?.(id);
168
+ const updateProperty = (elementIndex, propertyKey, next) => {
169
+ commit(updateArrayItemProperty(value, elementIndex, propertyKey, next, render));
118
170
  };
119
- return /* @__PURE__ */ jsxs("div", {
120
- ref: setNodeRef,
121
- style,
122
- ...attributes,
123
- ...listeners,
124
- onClick,
125
- className: cn("relative", selected && "z-10 outline-2 outline-offset-2 outline-blue-500", isNewItemOver && "border-t-4 border-t-green-500"),
126
- children: [
127
- /* @__PURE__ */ jsx("div", { className: cn("absolute inset-0 z-50") }),
128
- children,
129
- selected && /* @__PURE__ */ jsxs(Space, {
130
- className: cn("absolute top-1 right-1 z-60"),
131
- children: [/* @__PURE__ */ jsx(Button, {
132
- icon: /* @__PURE__ */ jsx(Copy, {}),
133
- onClick: onCopy
134
- }), /* @__PURE__ */ jsx(Button, {
135
- danger: true,
136
- icon: /* @__PURE__ */ jsx(Trash, {}),
137
- onClick: onDelete
138
- })]
139
- })
140
- ]
141
- });
142
- };
143
- //#endregion
144
- //#region src/components/dnd/overlay.tsx
145
- const Overlay = ({ sections, renderProps }) => {
146
- const { active } = useDndContext();
147
- if (!active) return null;
148
- if (active.data.current?.type === "new-item") {
149
- const item = active.data.current.item;
150
- return /* @__PURE__ */ jsx(DefaultDraggableItem, { item });
151
- }
152
- const section = sections.find((s) => s.id === active.id);
153
- if (section) {
154
- const preview = generateSection(section.code, renderProps.fullCode);
155
- return /* @__PURE__ */ jsx(Sortable, {
156
- id: section.id,
157
- name: section.name,
158
- children: /* @__PURE__ */ jsx(renderer_default, {
159
- preview,
160
- modules: renderProps.modules,
161
- frame: renderProps.frame,
162
- dynamicTailwind: renderProps.dynamicTailwind
163
- })
164
- });
165
- }
166
- return null;
167
- };
168
- //#endregion
169
- //#region src/components/dnd/panel/node.tsx
170
- var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
171
- const Node = ({ data, onChange }) => {
172
- const idAttr = data.dataAttributes.find((a) => a.name === "data-id");
173
- const bindingAttr = data.dataAttributes.find((a) => a.name === "data-binding");
174
- if (!idAttr?.value || !bindingAttr?.value) return null;
175
- const dataId = idAttr.value;
176
- const bindings = data.bindings || parseBinding(bindingAttr.value);
177
- if (!bindings.length) return null;
178
- return /* @__PURE__ */ jsx("div", {
179
- className: "space-y-2 rounded",
180
- children: /* @__PURE__ */ jsx("div", {
181
- className: "space-y-1",
182
- children: bindings.map((binding) => {
183
- const currentValue = getCurrentValue(data, binding.property);
184
- const structuredValue = getStructuredValue(data, binding.property, binding.type);
185
- return /* @__PURE__ */ jsxs("div", {
186
- className: "space-y-1",
187
- children: [/* @__PURE__ */ jsxs("label", {
188
- className: "block text-xs font-semibold text-gray-700",
189
- children: [binding.label, /* @__PURE__ */ jsxs("span", {
190
- className: "ml-1 text-gray-400",
191
- children: [
192
- "(",
193
- binding.property,
194
- ")"
195
- ]
196
- })]
197
- }), /* @__PURE__ */ jsx(Field, {
198
- binding: {
199
- id: dataId,
171
+ return {
172
+ kind,
173
+ items: isPrimitive ? primitiveItems.map((item) => ({
174
+ id: item.id,
175
+ index: item.index,
176
+ elementIndex: item.elementIndex,
177
+ properties: [],
178
+ nested: [],
179
+ value: {
180
+ id: `primitive-${item.id}`,
181
+ label: `item-${item.index}`,
182
+ property: item.type,
183
+ value: item.value ?? "",
184
+ rawValue: String(item.value ?? ""),
185
+ onChange: (next) => commit(updateArrayItemValue(value, item.elementIndex, next))
186
+ }
187
+ })) : objectItems.map((item) => {
188
+ const properties = Object.entries(item.editableProperties).map(([key, prop]) => {
189
+ const leaf = resolveLeaf(render, key);
190
+ return {
191
+ id: `item-${item.id}-${key}`,
192
+ label: key,
193
+ property: leaf ? leaf.property ?? leaf.type : key,
194
+ type: leaf?.type,
195
+ render: leaf ? leaf.render : resolveMap(render, key),
196
+ value: parseValue(String(prop.value)),
197
+ rawValue: String(prop.value),
198
+ meta: { valueType: prop.type },
199
+ onChange: (next) => updateProperty(item.elementIndex, key, next)
200
+ };
201
+ });
202
+ const nested = [...Object.entries(item.jsxBindings).map(([property, nodes]) => ({
203
+ property,
204
+ elements: nodes.flatMap((node) => {
205
+ const id = node.dataAttributes.find((a) => a.name === "data-id")?.value;
206
+ const attr = node.dataAttributes.find((a) => a.name === "data-binding")?.value;
207
+ if (!id || !attr) return [];
208
+ const parsed = node.bindings ?? parseBinding(attr);
209
+ if (!parsed.length) return [];
210
+ return [{
211
+ id,
212
+ tagName: node.tagName || "element",
213
+ bindings: parsed.map((binding) => ({
214
+ id,
200
215
  label: binding.label,
201
216
  property: binding.property,
202
217
  type: binding.type,
@@ -208,21 +223,68 @@ const Node = ({ data, onChange }) => {
208
223
  pattern: binding.pattern,
209
224
  required: binding.required,
210
225
  meta: binding.meta,
211
- value: structuredValue,
212
- rawValue: currentValue,
213
- onChange: (next) => onChange?.({
214
- id: dataId,
226
+ value: getStructuredValue(node, binding.property, binding.type),
227
+ rawValue: getCurrentValue(node, binding.property),
228
+ onChange: (next) => onNodeChange?.({
229
+ id,
215
230
  label: binding.label,
216
231
  property: binding.property,
217
232
  value: next
218
233
  })
219
- },
220
- onNodeChange: onChange
221
- })]
222
- }, binding.label);
223
- })
224
- })
225
- });
234
+ }))
235
+ }];
236
+ })
237
+ })), ...Object.entries(item.jsxFallbacks).map(([property, code]) => ({
238
+ property,
239
+ elements: [],
240
+ fallback: {
241
+ id: `item-${item.id}-${property}-jsx`,
242
+ label: property,
243
+ property,
244
+ type: "jsx",
245
+ value: code,
246
+ rawValue: code,
247
+ onChange: (next) => updateProperty(item.elementIndex, property, next)
248
+ }
249
+ }))];
250
+ return {
251
+ id: item.id,
252
+ index: item.index,
253
+ elementIndex: item.elementIndex,
254
+ properties,
255
+ nested
256
+ };
257
+ }),
258
+ selection,
259
+ actions: {
260
+ add: () => commit(appendArrayItem(value, kind)),
261
+ move: (elementIndex, toIndex) => {
262
+ const target = (isPrimitive ? primitiveItems : objectItems).find((item) => item.index === toIndex);
263
+ if (!target) return;
264
+ commit(moveArrayItem(value, elementIndex, target.elementIndex));
265
+ selection.clear();
266
+ },
267
+ remove: (elementIndex) => {
268
+ commit(removeArrayItems(value, /* @__PURE__ */ new Set([elementIndex]), kind));
269
+ selection.clear();
270
+ },
271
+ duplicateSelected: () => commit(duplicateArrayItems(value, elementIndicesOf(selection.selected))),
272
+ moveSelected: (direction) => {
273
+ const result = moveArrayItems(value, elementIndicesOf(selection.selected), direction);
274
+ if (!result) {
275
+ commit(null);
276
+ return;
277
+ }
278
+ selection.replace(result.indices);
279
+ commit(result.code);
280
+ },
281
+ removeSelected: () => {
282
+ commit(removeArrayItems(value, elementIndicesOf(selection.selected), kind));
283
+ selection.clear();
284
+ }
285
+ },
286
+ parseError
287
+ };
226
288
  };
227
289
  //#endregion
228
290
  //#region src/components/dnd/panel/items.tsx
@@ -270,143 +332,101 @@ const BulkActionsBar = ({ count, onDuplicate, onMoveUp, onMoveDown, onDelete, on
270
332
  })]
271
333
  });
272
334
  };
335
+ const NestedGroup = ({ group, onNodeChange }) => {
336
+ if (group.fallback) return /* @__PURE__ */ jsxs("div", {
337
+ className: "space-y-2",
338
+ children: [/* @__PURE__ */ jsx("div", {
339
+ className: "text-xs font-medium text-blue-700",
340
+ children: group.property
341
+ }), /* @__PURE__ */ jsx(Field, {
342
+ binding: group.fallback,
343
+ onNodeChange
344
+ })]
345
+ });
346
+ return /* @__PURE__ */ jsxs("div", {
347
+ className: "space-y-2",
348
+ children: [/* @__PURE__ */ jsxs("div", {
349
+ className: "text-xs font-medium text-blue-700",
350
+ children: [
351
+ group.property,
352
+ " Bindings (",
353
+ group.elements.length,
354
+ "):"
355
+ ]
356
+ }), group.elements.map((element) => /* @__PURE__ */ jsxs("div", {
357
+ className: "rounded border border-blue-100 bg-blue-50 p-2",
358
+ children: [/* @__PURE__ */ jsxs("div", {
359
+ className: "mb-1 text-xs text-blue-600",
360
+ children: [
361
+ "<",
362
+ element.tagName,
363
+ ">"
364
+ ]
365
+ }), /* @__PURE__ */ jsx("div", {
366
+ className: "space-y-2 rounded",
367
+ children: /* @__PURE__ */ jsx("div", {
368
+ className: "space-y-1",
369
+ children: element.bindings.map((binding) => /* @__PURE__ */ jsxs("div", {
370
+ className: "space-y-1",
371
+ children: [/* @__PURE__ */ jsxs("label", {
372
+ className: "block text-xs font-semibold text-gray-700",
373
+ children: [binding.label, /* @__PURE__ */ jsxs("span", {
374
+ className: "ml-1 text-gray-400",
375
+ children: [
376
+ "(",
377
+ binding.property,
378
+ ")"
379
+ ]
380
+ })]
381
+ }), /* @__PURE__ */ jsx(Field, {
382
+ binding,
383
+ onNodeChange
384
+ })]
385
+ }, binding.label))
386
+ })
387
+ })]
388
+ }, `${group.property}-${element.id}`))]
389
+ });
390
+ };
273
391
  const Items = ({ value, render, onChange, onChildChange }) => {
274
- const { objectItems, primitiveItems, parseError } = useMemo(() => {
275
- const ast = parseArrayExpression(value);
276
- if (!ast) return {
277
- objectItems: [],
278
- primitiveItems: [],
279
- parseError: true
280
- };
281
- const objectItems = [];
282
- const primitiveItems = [];
283
- ast.elements.filter((element) => Boolean(element)).forEach((element, elementIndex) => {
284
- if (!import_lib.isObjectExpression(element)) {
285
- const extracted = extractNodeValue(element);
286
- primitiveItems.push({
287
- id: nanoid(6),
288
- index: primitiveItems.length,
289
- elementIndex,
290
- value: extracted.value,
291
- type: extracted.type
292
- });
293
- return;
294
- }
295
- const jsxBindings = {};
296
- const jsxFallbacks = {};
297
- element.properties.forEach((prop) => {
298
- if (!import_lib.isObjectProperty(prop) || !import_lib.isIdentifier(prop.key) || !(import_lib.isJSXElement(prop.value) || import_lib.isJSXFragment(prop.value))) return;
299
- const propertyName = prop.key.name;
300
- try {
301
- const jsxCode = generateCode(prop.value);
302
- const nodes = extract(jsxCode);
303
- const bindings = [];
304
- const bindingContainer = nodes.find((node) => node.bindings?.some((b) => b.property === "children"));
305
- if (bindingContainer) bindings.push(bindingContainer);
306
- else nodes.forEach((node) => {
307
- if (node.bindings && node.bindings.length > 0 && node.dataAttributes.some((a) => a.name === "data-id")) bindings.push(node);
308
- const editableChildren = findEditableChildren(node);
309
- bindings.push(...editableChildren);
310
- });
311
- if (bindings.length > 0) jsxBindings[propertyName] = bindings;
312
- else jsxFallbacks[propertyName] = jsxCode;
313
- } catch (error) {
314
- console.error(`Failed to parse JSX in property '${propertyName}':`, error);
315
- }
316
- });
317
- objectItems.push({
318
- id: nanoid(6),
319
- index: objectItems.length,
320
- elementIndex,
321
- editableProperties: extractObjectProperties(element),
322
- jsxBindings,
323
- jsxFallbacks
324
- });
325
- });
326
- return {
327
- objectItems,
328
- primitiveItems,
329
- parseError: false
330
- };
331
- }, [value]);
332
- useEffect(() => {
333
- if (parseError) Toast.error("Failed to parse items", { description: "Check the console for details." });
334
- }, [parseError]);
335
- const isPrimitive = primitiveItems.length > 0 && objectItems.length === 0;
336
- const selection = useMultiSelect(isPrimitive ? primitiveItems.length : objectItems.length);
337
- const commit = (next) => {
338
- if (next === null) {
339
- Toast.error("Failed to update this item", { description: "Check the console for details." });
340
- return;
341
- }
342
- onChange?.(next);
343
- };
344
- const elementIndicesOf = (items, indices) => {
345
- return new Set(items.filter((item) => indices.has(item.index)).map((item) => item.elementIndex));
346
- };
347
- const updatePrimitive = (elementIndex, next) => {
348
- commit(updateArrayItemValue(value, elementIndex, next));
349
- };
350
- const movePrimitive = (from, to) => {
351
- const target = primitiveItems.find((item) => item.index === to);
352
- if (!target) return;
353
- commit(moveArrayItem(value, from, target.elementIndex));
354
- selection.clear();
355
- };
356
- const deleteSelectedPrimitives = (indices) => {
357
- commit(removeArrayItems(value, elementIndicesOf(primitiveItems, indices), "primitive"));
358
- };
359
- const deletePrimitive = (elementIndex) => {
360
- commit(removeArrayItems(value, /* @__PURE__ */ new Set([elementIndex]), "primitive"));
361
- selection.clear();
362
- };
363
- const addPrimitive = () => {
364
- commit(appendArrayItem(value, "primitive"));
365
- };
366
- const duplicateSelectedPrimitives = (indices) => {
367
- commit(duplicateArrayItems(value, elementIndicesOf(primitiveItems, indices)));
368
- };
369
- const moveSelectedPrimitives = (indices, direction) => {
370
- const result = moveArrayItems(value, elementIndicesOf(primitiveItems, indices), direction);
371
- if (!result) {
372
- commit(null);
373
- return;
374
- }
375
- selection.replace(result.indices);
376
- commit(result.code);
377
- };
378
- const moveItem = (from, to) => {
379
- const target = objectItems.find((item) => item.index === to);
380
- if (!target) return;
381
- commit(moveArrayItem(value, from, target.elementIndex));
382
- selection.clear();
383
- };
384
- const updateProperty = (elementIndex, propertyKey, next) => {
385
- commit(updateArrayItemProperty(value, elementIndex, propertyKey, next, render));
386
- };
387
- const deleteSelectedItems = (indices) => {
388
- commit(removeArrayItems(value, elementIndicesOf(objectItems, indices), "object"));
389
- };
390
- const deleteItem = (elementIndex) => {
391
- commit(removeArrayItems(value, /* @__PURE__ */ new Set([elementIndex]), "object"));
392
- selection.clear();
393
- };
394
- const addItem = () => {
395
- commit(appendArrayItem(value, "object"));
396
- };
397
- const duplicateSelectedItems = (indices) => {
398
- commit(duplicateArrayItems(value, elementIndicesOf(objectItems, indices)));
399
- };
400
- const moveSelectedItems = (indices, direction) => {
401
- const result = moveArrayItems(value, elementIndicesOf(objectItems, indices), direction);
402
- if (!result) {
403
- commit(null);
404
- return;
405
- }
406
- selection.replace(result.indices);
407
- commit(result.code);
408
- };
409
- if (isPrimitive) return /* @__PURE__ */ jsxs("div", {
392
+ const { kind, items, selection, actions } = useItemsEditor(value, {
393
+ render,
394
+ onChange,
395
+ onNodeChange: onChildChange
396
+ });
397
+ const bulkBar = /* @__PURE__ */ jsx(BulkActionsBar, {
398
+ count: selection.selected.size,
399
+ onDuplicate: actions.duplicateSelected,
400
+ onMoveUp: () => actions.moveSelected("up"),
401
+ onMoveDown: () => actions.moveSelected("down"),
402
+ onDelete: actions.removeSelected,
403
+ onClear: selection.clear
404
+ });
405
+ const itemControls = (item) => /* @__PURE__ */ jsxs("div", {
406
+ className: "flex space-x-1",
407
+ children: [
408
+ /* @__PURE__ */ jsx(Button, {
409
+ size: "small",
410
+ icon: /* @__PURE__ */ jsx(ArrowUp, {}),
411
+ disabled: item.index === 0,
412
+ onClick: () => actions.move(item.elementIndex, item.index - 1)
413
+ }),
414
+ /* @__PURE__ */ jsx(Button, {
415
+ size: "small",
416
+ icon: /* @__PURE__ */ jsx(ArrowDown, {}),
417
+ disabled: item.index === items.length - 1,
418
+ onClick: () => actions.move(item.elementIndex, item.index + 1)
419
+ }),
420
+ /* @__PURE__ */ jsx(Button, {
421
+ danger: true,
422
+ size: "small",
423
+ icon: /* @__PURE__ */ jsx(X, {}),
424
+ disabled: items.length <= 1,
425
+ onClick: () => actions.remove(item.elementIndex)
426
+ })
427
+ ]
428
+ });
429
+ if (kind === "primitive") return /* @__PURE__ */ jsxs("div", {
410
430
  className: "space-y-4",
411
431
  children: [
412
432
  /* @__PURE__ */ jsxs("div", {
@@ -415,7 +435,7 @@ const Items = ({ value, render, onChange, onChildChange }) => {
415
435
  className: "text-sm font-semibold",
416
436
  children: [
417
437
  "Items (",
418
- primitiveItems.length,
438
+ items.length,
419
439
  ")"
420
440
  ]
421
441
  }), /* @__PURE__ */ jsx(Button, {
@@ -423,22 +443,12 @@ const Items = ({ value, render, onChange, onChildChange }) => {
423
443
  icon: /* @__PURE__ */ jsx(Plus, {}),
424
444
  variant: "solid",
425
445
  color: "green",
426
- onClick: addPrimitive,
446
+ onClick: actions.add,
427
447
  children: "Add Item"
428
448
  })]
429
449
  }),
430
- /* @__PURE__ */ jsx(BulkActionsBar, {
431
- count: selection.selected.size,
432
- onDuplicate: () => duplicateSelectedPrimitives(selection.selected),
433
- onMoveUp: () => moveSelectedPrimitives(selection.selected, "up"),
434
- onMoveDown: () => moveSelectedPrimitives(selection.selected, "down"),
435
- onDelete: () => {
436
- deleteSelectedPrimitives(selection.selected);
437
- selection.clear();
438
- },
439
- onClear: selection.clear
440
- }),
441
- primitiveItems.map((item, i) => /* @__PURE__ */ jsxs("div", {
450
+ bulkBar,
451
+ items.map((item) => /* @__PURE__ */ jsxs("div", {
442
452
  className: "space-y-2 rounded border border-gray-100 bg-gray-50 p-2",
443
453
  children: [/* @__PURE__ */ jsxs("div", {
444
454
  className: "flex items-center justify-between space-x-1",
@@ -448,40 +458,10 @@ const Items = ({ value, render, onChange, onChildChange }) => {
448
458
  children: /* @__PURE__ */ jsx(Checkbox, {
449
459
  checked: selection.isSelected(item.index),
450
460
  onChange: () => {}
451
- })
452
- }), /* @__PURE__ */ jsxs("div", {
453
- className: "flex space-x-1",
454
- children: [
455
- /* @__PURE__ */ jsx(Button, {
456
- size: "small",
457
- icon: /* @__PURE__ */ jsx(ArrowUp, {}),
458
- disabled: i === 0,
459
- onClick: () => movePrimitive(item.elementIndex, item.index - 1)
460
- }),
461
- /* @__PURE__ */ jsx(Button, {
462
- size: "small",
463
- icon: /* @__PURE__ */ jsx(ArrowDown, {}),
464
- disabled: i === primitiveItems.length - 1,
465
- onClick: () => movePrimitive(item.elementIndex, item.index + 1)
466
- }),
467
- /* @__PURE__ */ jsx(Button, {
468
- danger: true,
469
- size: "small",
470
- icon: /* @__PURE__ */ jsx(X, {}),
471
- disabled: primitiveItems.length <= 1,
472
- onClick: () => deletePrimitive(item.elementIndex)
473
- })
474
- ]
475
- })]
461
+ })
462
+ }), itemControls(item)]
476
463
  }), /* @__PURE__ */ jsx(Field, {
477
- binding: {
478
- id: `primitive-${item.id}`,
479
- label: `item-${i}`,
480
- property: item.type,
481
- value: item.value ?? "",
482
- rawValue: String(item.value ?? ""),
483
- onChange: (next) => updatePrimitive(item.elementIndex, next)
484
- },
464
+ binding: item.value,
485
465
  onNodeChange: onChildChange
486
466
  })]
487
467
  }, item.id))
@@ -496,7 +476,7 @@ const Items = ({ value, render, onChange, onChildChange }) => {
496
476
  className: "text-sm font-semibold",
497
477
  children: [
498
478
  "Items (",
499
- objectItems.length,
479
+ items.length,
500
480
  ")"
501
481
  ]
502
482
  }), /* @__PURE__ */ jsx(Button, {
@@ -504,23 +484,13 @@ const Items = ({ value, render, onChange, onChildChange }) => {
504
484
  icon: /* @__PURE__ */ jsx(Plus, {}),
505
485
  variant: "solid",
506
486
  color: "green",
507
- disabled: objectItems.length === 0,
508
- onClick: addItem,
487
+ disabled: items.length === 0,
488
+ onClick: actions.add,
509
489
  children: "Add Item"
510
490
  })]
511
491
  }),
512
- /* @__PURE__ */ jsx(BulkActionsBar, {
513
- count: selection.selected.size,
514
- onDuplicate: () => duplicateSelectedItems(selection.selected),
515
- onMoveUp: () => moveSelectedItems(selection.selected, "up"),
516
- onMoveDown: () => moveSelectedItems(selection.selected, "down"),
517
- onDelete: () => {
518
- deleteSelectedItems(selection.selected);
519
- selection.clear();
520
- },
521
- onClear: selection.clear
522
- }),
523
- objectItems.map((item) => /* @__PURE__ */ jsxs("div", {
492
+ bulkBar,
493
+ items.map((item) => /* @__PURE__ */ jsxs("div", {
524
494
  className: "space-y-3 rounded border bg-gray-50 p-3",
525
495
  children: [
526
496
  /* @__PURE__ */ jsxs("div", {
@@ -538,107 +508,34 @@ const Items = ({ value, render, onChange, onChildChange }) => {
538
508
  className: "text-xs font-medium",
539
509
  children: ["Item ", item.index + 1]
540
510
  })]
541
- }), /* @__PURE__ */ jsxs("div", {
542
- className: "flex space-x-1",
543
- children: [
544
- /* @__PURE__ */ jsx(Button, {
545
- size: "small",
546
- icon: /* @__PURE__ */ jsx(ArrowUp, {}),
547
- disabled: item.index === 0,
548
- onClick: () => moveItem(item.elementIndex, item.index - 1)
549
- }),
550
- /* @__PURE__ */ jsx(Button, {
551
- size: "small",
552
- icon: /* @__PURE__ */ jsx(ArrowDown, {}),
553
- disabled: item.index === objectItems.length - 1,
554
- onClick: () => moveItem(item.elementIndex, item.index + 1)
555
- }),
556
- /* @__PURE__ */ jsx(Button, {
557
- danger: true,
558
- size: "small",
559
- icon: /* @__PURE__ */ jsx(X, {}),
560
- disabled: objectItems.length <= 1,
561
- onClick: () => deleteItem(item.elementIndex)
562
- })
563
- ]
564
- })]
511
+ }), itemControls(item)]
565
512
  }),
566
513
  /* @__PURE__ */ jsx("div", {
567
514
  className: "space-y-2",
568
- children: Object.entries(item.editableProperties).map(([key, prop]) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
515
+ children: item.properties.map((binding) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
569
516
  className: "flex flex-col space-y-2",
570
517
  children: [/* @__PURE__ */ jsx("label", {
571
518
  className: "w-20 shrink-0 text-xs font-medium",
572
- children: key
519
+ children: binding.label
573
520
  }), /* @__PURE__ */ jsx(Field, {
574
- binding: {
575
- id: `item-${item.id}-${key}`,
576
- label: key,
577
- property: render?.[key] && "type" in render[key] ? render[key].property ?? render[key].type : key,
578
- type: render?.[key] && "type" in render[key] ? render[key].type : void 0,
579
- render: render?.[key] && "type" in render[key] ? render[key].render : render?.[key] && !("type" in render[key]) ? render[key] : void 0,
580
- value: parseValue(String(prop.value)),
581
- rawValue: String(prop.value),
582
- onChange: (next) => updateProperty(item.elementIndex, key, next)
583
- },
521
+ binding,
584
522
  onNodeChange: onChildChange
585
523
  })]
586
524
  }), /* @__PURE__ */ jsxs("span", {
587
525
  className: "text-right text-xs text-gray-500",
588
526
  children: [
589
527
  "(",
590
- prop.type,
528
+ String(binding.meta?.valueType),
591
529
  ")"
592
530
  ]
593
- })] }, `${item.id}-${key}`))
531
+ })] }, `${item.id}-${binding.label}`))
594
532
  }),
595
533
  /* @__PURE__ */ jsx("div", {
596
534
  className: "space-y-3 border-t pt-2",
597
- children: Object.entries(item.jsxBindings).length > 0 || Object.entries(item.jsxFallbacks).length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [Object.entries(item.jsxBindings).map(([propertyName, bindings]) => /* @__PURE__ */ jsxs("div", {
598
- className: "space-y-2",
599
- children: [/* @__PURE__ */ jsxs("div", {
600
- className: "text-xs font-medium text-blue-700",
601
- children: [
602
- propertyName,
603
- " Bindings (",
604
- bindings.length,
605
- "):"
606
- ]
607
- }), bindings.map((bindingNode, idx) => {
608
- const nodeId = bindingNode.dataAttributes.find((a) => a.name === "data-id")?.value;
609
- return /* @__PURE__ */ jsxs("div", {
610
- className: "rounded border border-blue-100 bg-blue-50\n p-2",
611
- children: [/* @__PURE__ */ jsxs("div", {
612
- className: "mb-1 text-xs text-blue-600",
613
- children: [
614
- "<",
615
- bindingNode.tagName || "element",
616
- ">"
617
- ]
618
- }), /* @__PURE__ */ jsx(Node, {
619
- data: bindingNode,
620
- onChange: onChildChange
621
- })]
622
- }, `binding-${item.id}-${propertyName}-${nodeId || idx}`);
623
- })]
624
- }, propertyName)), Object.entries(item.jsxFallbacks).map(([propertyName, code]) => /* @__PURE__ */ jsxs("div", {
625
- className: "space-y-2",
626
- children: [/* @__PURE__ */ jsx("div", {
627
- className: "text-xs font-medium text-blue-700",
628
- children: propertyName
629
- }), /* @__PURE__ */ jsx(Field, {
630
- binding: {
631
- id: `item-${item.id}-${propertyName}-jsx`,
632
- label: propertyName,
633
- property: propertyName,
634
- type: "jsx",
635
- value: code,
636
- rawValue: code,
637
- onChange: (next) => updateProperty(item.elementIndex, propertyName, next)
638
- },
639
- onNodeChange: onChildChange
640
- })]
641
- }, propertyName))] }) : /* @__PURE__ */ jsx("div", {
535
+ children: item.nested.length ? item.nested.map((group) => /* @__PURE__ */ jsx(NestedGroup, {
536
+ group,
537
+ onNodeChange: onChildChange
538
+ }, group.property)) : /* @__PURE__ */ jsx("div", {
642
539
  className: "text-xs text-gray-500",
643
540
  children: "✓ No JSX bindings found"
644
541
  })
@@ -649,8 +546,66 @@ const Items = ({ value, render, onChange, onChildChange }) => {
649
546
  });
650
547
  };
651
548
  //#endregion
549
+ //#region src/components/dnd/panel/node.tsx
550
+ const Node = ({ data, onChange }) => {
551
+ const idAttr = data.dataAttributes.find((a) => a.name === "data-id");
552
+ const bindingAttr = data.dataAttributes.find((a) => a.name === "data-binding");
553
+ if (!idAttr?.value || !bindingAttr?.value) return null;
554
+ const dataId = idAttr.value;
555
+ const bindings = data.bindings || parseBinding(bindingAttr.value);
556
+ if (!bindings.length) return null;
557
+ return /* @__PURE__ */ jsx("div", {
558
+ className: "space-y-2 rounded",
559
+ children: /* @__PURE__ */ jsx("div", {
560
+ className: "space-y-1",
561
+ children: bindings.map((binding) => {
562
+ const currentValue = getCurrentValue(data, binding.property);
563
+ const structuredValue = getStructuredValue(data, binding.property, binding.type);
564
+ return /* @__PURE__ */ jsxs("div", {
565
+ className: "space-y-1",
566
+ children: [/* @__PURE__ */ jsxs("label", {
567
+ className: "block text-xs font-semibold text-gray-700",
568
+ children: [binding.label, /* @__PURE__ */ jsxs("span", {
569
+ className: "ml-1 text-gray-400",
570
+ children: [
571
+ "(",
572
+ binding.property,
573
+ ")"
574
+ ]
575
+ })]
576
+ }), /* @__PURE__ */ jsx(Field, {
577
+ binding: {
578
+ id: dataId,
579
+ label: binding.label,
580
+ property: binding.property,
581
+ type: binding.type,
582
+ widget: binding.widget,
583
+ options: binding.options,
584
+ render: binding.render,
585
+ min: binding.min,
586
+ max: binding.max,
587
+ pattern: binding.pattern,
588
+ required: binding.required,
589
+ meta: binding.meta,
590
+ value: structuredValue,
591
+ rawValue: currentValue,
592
+ onChange: (next) => onChange?.({
593
+ id: dataId,
594
+ label: binding.label,
595
+ property: binding.property,
596
+ value: next
597
+ })
598
+ },
599
+ onNodeChange: onChange
600
+ })]
601
+ }, binding.label);
602
+ })
603
+ })
604
+ });
605
+ };
606
+ //#endregion
652
607
  //#region src/components/dnd/panel/children.tsx
653
- const Children = ({ value, onChange, onNodeChange }) => {
608
+ const Children$1 = ({ value, onChange, onNodeChange }) => {
654
609
  const items = useMemo(() => Array.isArray(value) ? value : [], [value]);
655
610
  const selection = useMultiSelect(items.length);
656
611
  const editableChildrenMap = useMemo(() => {
@@ -984,7 +939,7 @@ const Field = ({ binding, onNodeChange }) => {
984
939
  }
985
940
  });
986
941
  }
987
- if (binding.property === "children" && Array.isArray(value)) return /* @__PURE__ */ jsx(Children, {
942
+ if (binding.property === "children" && Array.isArray(value)) return /* @__PURE__ */ jsx(Children$1, {
988
943
  value,
989
944
  onChange,
990
945
  onNodeChange
@@ -1205,7 +1160,7 @@ const groupBindingsById = (bindings) => {
1205
1160
  }
1206
1161
  return [...groups.values()];
1207
1162
  };
1208
- const Panel = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, bindings, onNodeChange }) => {
1163
+ const Panel$1 = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canMoveDown = false, bindings, onNodeChange }) => {
1209
1164
  const groups = useMemo(() => groupBindingsById(bindings), [bindings]);
1210
1165
  if (!item) return /* @__PURE__ */ jsx(Typography.Paragraph, {
1211
1166
  className: cn("p-4 text-sm text-gray-500"),
@@ -1255,6 +1210,211 @@ const Panel = ({ item, onDelete, onMoveUp, onMoveDown, canMoveUp = false, canMov
1255
1210
  });
1256
1211
  };
1257
1212
  //#endregion
1213
+ //#region src/components/dnd/layout.tsx
1214
+ const PaletteItems = ({ subject }) => {
1215
+ const { palette: { items, onAdd }, isMobile } = useDndRegions(subject);
1216
+ return /* @__PURE__ */ jsx(Space, {
1217
+ orientation: "vertical",
1218
+ align: "start",
1219
+ children: items.map((item) => /* @__PURE__ */ jsx(DefaultDraggableItem, {
1220
+ item,
1221
+ onAdd,
1222
+ tapToAdd: isMobile
1223
+ }, item.id))
1224
+ });
1225
+ };
1226
+ const PanelFields = ({ subject }) => {
1227
+ const { panel: { item, onDelete, onMoveUp, onMoveDown, canMoveUp, canMoveDown, bindings, onNodeChange } } = useDndRegions(subject);
1228
+ return /* @__PURE__ */ jsx(Panel$1, {
1229
+ item,
1230
+ onDelete,
1231
+ onMoveUp,
1232
+ onMoveDown,
1233
+ canMoveUp,
1234
+ canMoveDown,
1235
+ bindings,
1236
+ onNodeChange
1237
+ });
1238
+ };
1239
+ const Palette = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
1240
+ className: cn("h-full overflow-y-auto bg-gray-50 p-4", className),
1241
+ ...restProps,
1242
+ children: /* @__PURE__ */ jsx(PaletteItems, { subject: "<Live.Dnd.Palette>" })
1243
+ });
1244
+ const Canvas = ({ className, style, ...restProps }) => {
1245
+ const { canvas } = useDndRegions("<Live.Dnd.Canvas>");
1246
+ return /* @__PURE__ */ jsx("div", {
1247
+ className: cn("relative h-full w-full overflow-y-auto", className),
1248
+ "data-frame-container": true,
1249
+ style: {
1250
+ isolation: "isolate",
1251
+ contain: "layout style",
1252
+ transform: "translateZ(0)",
1253
+ ...style
1254
+ },
1255
+ ...restProps,
1256
+ children: canvas
1257
+ });
1258
+ };
1259
+ const Panel = ({ className, ...restProps }) => /* @__PURE__ */ jsx("div", {
1260
+ className: cn("h-full", className),
1261
+ ...restProps,
1262
+ children: /* @__PURE__ */ jsx(PanelFields, { subject: "<Live.Dnd.Panel>" })
1263
+ });
1264
+ const Layout = ({ palette, panel }) => {
1265
+ const { isMobile, selectedId, clearSelection, paletteOpen, setPaletteOpen } = useDndRegions("<Live.Dnd.Layout>");
1266
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1267
+ isMobile ? /* @__PURE__ */ jsx(Canvas, {}) : /* @__PURE__ */ jsxs(Splitter, {
1268
+ withHandle: true,
1269
+ orientation: "horizontal",
1270
+ children: [
1271
+ /* @__PURE__ */ jsx(Splitter.Panel, {
1272
+ defaultSize: "20%",
1273
+ minSize: "15%",
1274
+ maxSize: "35%",
1275
+ collapsible: true,
1276
+ children: palette ?? /* @__PURE__ */ jsx(Palette, {})
1277
+ }),
1278
+ /* @__PURE__ */ jsx(Splitter.Panel, {
1279
+ defaultSize: "60%",
1280
+ children: /* @__PURE__ */ jsx(Canvas, {})
1281
+ }),
1282
+ /* @__PURE__ */ jsx(Splitter.Panel, {
1283
+ defaultSize: "20%",
1284
+ minSize: "15%",
1285
+ maxSize: "35%",
1286
+ collapsible: true,
1287
+ children: panel ?? /* @__PURE__ */ jsx(Panel, {})
1288
+ })
1289
+ ]
1290
+ }),
1291
+ /* @__PURE__ */ jsx(Button, {
1292
+ type: "primary",
1293
+ shape: "circle",
1294
+ icon: /* @__PURE__ */ jsx(LayoutGrid, {}),
1295
+ "aria-label": "Components",
1296
+ className: "fixed right-4 bottom-4 z-20 md:hidden",
1297
+ onClick: () => setPaletteOpen(true)
1298
+ }),
1299
+ /* @__PURE__ */ jsx(Drawer, {
1300
+ open: isMobile && paletteOpen,
1301
+ onClose: () => setPaletteOpen(false),
1302
+ direction: "bottom",
1303
+ size: "large",
1304
+ title: "Components",
1305
+ children: palette ?? /* @__PURE__ */ jsx(PaletteItems, { subject: "<Live.Dnd.Layout>" })
1306
+ }),
1307
+ /* @__PURE__ */ jsx(Drawer, {
1308
+ open: isMobile && Boolean(selectedId),
1309
+ onClose: clearSelection,
1310
+ direction: "bottom",
1311
+ size: "large",
1312
+ title: "Properties",
1313
+ children: panel ?? /* @__PURE__ */ jsx(PanelFields, { subject: "<Live.Dnd.Layout>" })
1314
+ })
1315
+ ] });
1316
+ };
1317
+ //#endregion
1318
+ //#region src/components/dnd/renderer.tsx
1319
+ const Renderer = ({ preview, headers, modules, frame, dynamicTailwind = false, provider }) => {
1320
+ const module = useCompiledModule(preview, modules);
1321
+ const { ref: wrapperRef, css: dynamicCSS } = useDynamicTailwind(preview, dynamicTailwind);
1322
+ const renderProvider = (component) => {
1323
+ return provider ? provider(component) : component;
1324
+ };
1325
+ if (module?.error) return /* @__PURE__ */ jsx(Error$1, {
1326
+ message: module.error,
1327
+ title: "Compile Error"
1328
+ });
1329
+ const Component = module?.exports?.default;
1330
+ if (!Component) return null;
1331
+ return /* @__PURE__ */ jsx(Frame, {
1332
+ ...frame,
1333
+ autoHeight: true,
1334
+ children: (container) => /* @__PURE__ */ jsx("div", {
1335
+ ref: wrapperRef,
1336
+ className: "w-full overflow-x-hidden",
1337
+ "data-editor-mode": true,
1338
+ children: /* @__PURE__ */ jsx(ErrorBoundary, {
1339
+ resetKeys: [preview],
1340
+ children: renderProvider(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Component, {
1341
+ headers,
1342
+ container
1343
+ }), dynamicTailwind && dynamicCSS && /* @__PURE__ */ jsx("style", { children: dynamicCSS })] }))
1344
+ })
1345
+ })
1346
+ });
1347
+ };
1348
+ var renderer_default = memo(Renderer);
1349
+ //#endregion
1350
+ //#region src/components/dnd/sortable.tsx
1351
+ const Sortable = ({ id, children, selected, onClick, onDelete: _onDelete, onCopy: _onCopy }) => {
1352
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging, isOver, active } = useSortable({ id });
1353
+ const style = {
1354
+ transform: CSS.Transform.toString(transform),
1355
+ transition,
1356
+ opacity: isDragging ? .5 : 1,
1357
+ cursor: "grab"
1358
+ };
1359
+ const isNewItemOver = isOver && active?.data.current?.type === "new-item";
1360
+ const onDelete = (e) => {
1361
+ e.stopPropagation();
1362
+ _onDelete?.(id);
1363
+ };
1364
+ const onCopy = (e) => {
1365
+ e.stopPropagation();
1366
+ _onCopy?.(id);
1367
+ };
1368
+ return /* @__PURE__ */ jsxs("div", {
1369
+ ref: setNodeRef,
1370
+ style,
1371
+ ...attributes,
1372
+ ...listeners,
1373
+ onClick,
1374
+ className: cn("relative", selected && "z-10 outline-2 outline-offset-2 outline-blue-500", isNewItemOver && "border-t-4 border-t-green-500"),
1375
+ children: [
1376
+ /* @__PURE__ */ jsx("div", { className: cn("absolute inset-0 z-50") }),
1377
+ children,
1378
+ selected && /* @__PURE__ */ jsxs(Space, {
1379
+ className: cn("absolute top-1 right-1 z-60"),
1380
+ children: [/* @__PURE__ */ jsx(Button, {
1381
+ icon: /* @__PURE__ */ jsx(Copy, {}),
1382
+ onClick: onCopy
1383
+ }), /* @__PURE__ */ jsx(Button, {
1384
+ danger: true,
1385
+ icon: /* @__PURE__ */ jsx(Trash, {}),
1386
+ onClick: onDelete
1387
+ })]
1388
+ })
1389
+ ]
1390
+ });
1391
+ };
1392
+ //#endregion
1393
+ //#region src/components/dnd/overlay.tsx
1394
+ const Overlay = ({ sections, renderProps }) => {
1395
+ const { active } = useDndContext();
1396
+ if (!active) return null;
1397
+ if (active.data.current?.type === "new-item") {
1398
+ const item = active.data.current.item;
1399
+ return /* @__PURE__ */ jsx(DefaultDraggableItem, { item });
1400
+ }
1401
+ const section = sections.find((s) => s.id === active.id);
1402
+ if (section) {
1403
+ const preview = generateSection(section.code, renderProps.fullCode);
1404
+ return /* @__PURE__ */ jsx(Sortable, {
1405
+ id: section.id,
1406
+ name: section.name,
1407
+ children: /* @__PURE__ */ jsx(renderer_default, {
1408
+ preview,
1409
+ modules: renderProps.modules,
1410
+ frame: renderProps.frame,
1411
+ dynamicTailwind: renderProps.dynamicTailwind
1412
+ })
1413
+ });
1414
+ }
1415
+ return null;
1416
+ };
1417
+ //#endregion
1258
1418
  //#region src/components/dnd/use-section-document.ts
1259
1419
  const useSectionDocument = (value, onChange) => {
1260
1420
  const { setCode } = usePreview();
@@ -1374,7 +1534,7 @@ const conditionalModifiers = (args) => {
1374
1534
  if (active?.data.current?.type === "new-item") return args.transform;
1375
1535
  return restrictToVerticalAxis(args);
1376
1536
  };
1377
- const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, className, items = [], frame, dynamicTailwind = false, provider, renderPalette, renderPanel, ...restProps }) => {
1537
+ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, className, items = [], frame, dynamicTailwind = false, provider, children, ...restProps }) => {
1378
1538
  const [mobilePaletteOpen, setMobilePaletteOpen] = useState(false);
1379
1539
  const { breakpoint } = useResponsiveSize();
1380
1540
  const isMobile = breakpoint.current === "xs" || breakpoint.current === "sm";
@@ -1470,87 +1630,54 @@ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, classN
1470
1630
  useEffect(() => {
1471
1631
  if (frame?.scripts?.length) preloadScripts(frame.scripts);
1472
1632
  }, [frame?.scripts]);
1473
- const renderPaletteItems = (onAdd, forMobileDrawer = false) => {
1474
- const paletteItems = items?.length ? items : DRAGGABLE_ITEMS;
1475
- if (renderPalette) return renderPalette({
1476
- items: paletteItems,
1477
- onAdd,
1478
- DraggableItem,
1479
- isMobile: forMobileDrawer
1480
- });
1481
- return /* @__PURE__ */ jsx(Space, {
1482
- orientation: "vertical",
1483
- align: "start",
1484
- children: paletteItems.map((item) => /* @__PURE__ */ jsx(DefaultDraggableItem, {
1485
- item,
1486
- onAdd,
1487
- tapToAdd: forMobileDrawer
1488
- }, item.id))
1489
- });
1633
+ const palette = {
1634
+ items: items?.length ? items : DRAGGABLE_ITEMS,
1635
+ onAdd: (item) => {
1636
+ addItem(item);
1637
+ setMobilePaletteOpen(false);
1638
+ }
1490
1639
  };
1491
- const renderPanelContent = () => {
1492
- const canMoveUp = selectedIndex > 0;
1493
- const canMoveDown = selectedIndex >= 0 && selectedIndex < sections.length - 1;
1494
- if (renderPanel) return renderPanel({
1495
- item: selectedItem,
1496
- onChange,
1497
- onDelete,
1498
- onMoveUp: () => moveSection(selectedId, "up"),
1499
- onMoveDown: () => moveSection(selectedId, "down"),
1500
- canMoveUp,
1501
- canMoveDown,
1502
- bindings
1503
- });
1504
- return /* @__PURE__ */ jsx(Panel, {
1505
- item: selectedItem,
1506
- onDelete,
1507
- onMoveUp: () => moveSection(selectedId, "up"),
1508
- onMoveDown: () => moveSection(selectedId, "down"),
1509
- canMoveUp,
1510
- canMoveDown,
1511
- bindings,
1512
- onNodeChange: onFieldChange
1513
- });
1640
+ const panel = {
1641
+ item: selectedItem,
1642
+ onChange,
1643
+ onDelete,
1644
+ onMoveUp: () => moveSection(selectedId, "up"),
1645
+ onMoveDown: () => moveSection(selectedId, "down"),
1646
+ canMoveUp: selectedIndex > 0,
1647
+ canMoveDown: selectedIndex >= 0 && selectedIndex < sections.length - 1,
1648
+ bindings,
1649
+ onNodeChange: onFieldChange
1514
1650
  };
1515
- const canvas = /* @__PURE__ */ jsx("div", {
1516
- className: "relative h-full w-full overflow-y-auto",
1517
- "data-frame-container": true,
1518
- style: {
1519
- isolation: "isolate",
1520
- contain: "layout style",
1521
- transform: "translateZ(0)"
1522
- },
1523
- children: /* @__PURE__ */ jsx(Droppable, {
1524
- className: cn(!sections.length && "h-full"),
1525
- children: !sections.length ? /* @__PURE__ */ jsx("div", {
1526
- className: cn("flex items-center justify-center", "h-full", "text-gray-500"),
1527
- children: /* @__PURE__ */ jsxs(Space, {
1528
- orientation: "vertical",
1529
- align: "center",
1530
- children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: "No sections available" }), /* @__PURE__ */ jsx(Typography.Text, { children: isMobile ? "Tap a component to add it" : "Drag a component from the left to add it" })]
1531
- })
1532
- }) : /* @__PURE__ */ jsx(SortableContext, {
1533
- items: sections.map((s) => s.id),
1534
- strategy: verticalListSortingStrategy,
1535
- children: sections.map((section, index) => /* @__PURE__ */ jsx(Sortable, {
1536
- id: section.id,
1537
- name: section.name,
1538
- selected: selectedId === section.id,
1539
- onClick: () => onSelect(section.id),
1540
- onDelete,
1541
- onCopy,
1542
- children: /* @__PURE__ */ jsx(renderer_default, {
1543
- preview: previews[index],
1544
- modules,
1545
- frame,
1546
- dynamicTailwind,
1547
- provider,
1548
- ...props
1549
- })
1550
- }, section.id))
1651
+ const canvas = /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Droppable, {
1652
+ className: cn(!sections.length && "h-full"),
1653
+ children: !sections.length ? /* @__PURE__ */ jsx("div", {
1654
+ className: cn("flex items-center justify-center", "h-full", "text-gray-500"),
1655
+ children: /* @__PURE__ */ jsxs(Space, {
1656
+ orientation: "vertical",
1657
+ align: "center",
1658
+ children: [/* @__PURE__ */ jsx(Typography.Paragraph, { children: "No sections available" }), /* @__PURE__ */ jsx(Typography.Text, { children: isMobile ? "Tap a component to add it" : "Drag a component from the left to add it" })]
1551
1659
  })
1660
+ }) : /* @__PURE__ */ jsx(SortableContext, {
1661
+ items: sections.map((s) => s.id),
1662
+ strategy: verticalListSortingStrategy,
1663
+ children: sections.map((section, index) => /* @__PURE__ */ jsx(Sortable, {
1664
+ id: section.id,
1665
+ name: section.name,
1666
+ selected: selectedId === section.id,
1667
+ onClick: () => onSelect(section.id),
1668
+ onDelete,
1669
+ onCopy,
1670
+ children: /* @__PURE__ */ jsx(renderer_default, {
1671
+ preview: previews[index],
1672
+ modules,
1673
+ frame,
1674
+ dynamicTailwind,
1675
+ provider,
1676
+ ...props
1677
+ })
1678
+ }, section.id))
1552
1679
  })
1553
- });
1680
+ }) });
1554
1681
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(DndContext, {
1555
1682
  sensors,
1556
1683
  collisionDetection: closestCenter,
@@ -1558,65 +1685,22 @@ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, classN
1558
1685
  onDragStart,
1559
1686
  onDragOver,
1560
1687
  onDragEnd,
1561
- children: [/* @__PURE__ */ jsxs("div", {
1688
+ children: [/* @__PURE__ */ jsx("div", {
1562
1689
  className: cn("relative flex h-full w-full", className),
1563
1690
  ...restProps,
1564
- children: [
1565
- isMobile ? canvas : /* @__PURE__ */ jsxs(Splitter, {
1566
- withHandle: true,
1567
- orientation: "horizontal",
1568
- children: [
1569
- /* @__PURE__ */ jsx(Splitter.Panel, {
1570
- defaultSize: "20%",
1571
- minSize: "15%",
1572
- maxSize: "35%",
1573
- collapsible: true,
1574
- children: /* @__PURE__ */ jsx("div", {
1575
- className: "h-full overflow-y-auto bg-gray-50 p-4",
1576
- children: renderPaletteItems(addItem)
1577
- })
1578
- }),
1579
- /* @__PURE__ */ jsx(Splitter.Panel, {
1580
- defaultSize: "60%",
1581
- children: canvas
1582
- }),
1583
- /* @__PURE__ */ jsx(Splitter.Panel, {
1584
- defaultSize: "20%",
1585
- minSize: "15%",
1586
- maxSize: "35%",
1587
- collapsible: true,
1588
- children: renderPanelContent()
1589
- })
1590
- ]
1591
- }),
1592
- /* @__PURE__ */ jsx(Button, {
1593
- type: "primary",
1594
- shape: "circle",
1595
- icon: /* @__PURE__ */ jsx(LayoutGrid, {}),
1596
- "aria-label": "Components",
1597
- className: "fixed right-4 bottom-4 z-20 md:hidden",
1598
- onClick: () => setMobilePaletteOpen(true)
1599
- }),
1600
- /* @__PURE__ */ jsx(Drawer, {
1601
- open: isMobile && mobilePaletteOpen,
1602
- onClose: () => setMobilePaletteOpen(false),
1603
- direction: "bottom",
1604
- size: "large",
1605
- title: "Components",
1606
- children: renderPaletteItems((item) => {
1607
- addItem(item);
1608
- setMobilePaletteOpen(false);
1609
- }, true)
1610
- }),
1611
- /* @__PURE__ */ jsx(Drawer, {
1612
- open: isMobile && Boolean(selectedId),
1613
- onClose: clearSelection,
1614
- direction: "bottom",
1615
- size: "large",
1616
- title: "Properties",
1617
- children: renderPanelContent()
1618
- })
1619
- ]
1691
+ children: /* @__PURE__ */ jsx(DndRegionContext.Provider, {
1692
+ value: {
1693
+ palette,
1694
+ panel,
1695
+ canvas,
1696
+ isMobile,
1697
+ selectedId,
1698
+ clearSelection,
1699
+ paletteOpen: mobilePaletteOpen,
1700
+ setPaletteOpen: setMobilePaletteOpen
1701
+ },
1702
+ children: Children.toArray(children).length ? children : /* @__PURE__ */ jsx(Layout, {})
1703
+ })
1620
1704
  }), /* @__PURE__ */ jsx(DragOverlay, { children: /* @__PURE__ */ jsx(Overlay, {
1621
1705
  sections,
1622
1706
  renderProps: {
@@ -1633,8 +1717,12 @@ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, classN
1633
1717
  //#region src/components/dnd/index.ts
1634
1718
  const Dnd = Dnd$1;
1635
1719
  Dnd.DraggableItem = DraggableItem;
1636
- Dnd.DefaultPanel = Panel;
1720
+ Dnd.Field = Field;
1721
+ Dnd.Palette = Palette;
1722
+ Dnd.Canvas = Canvas;
1723
+ Dnd.Panel = Panel;
1724
+ Dnd.Layout = Layout;
1637
1725
  //#endregion
1638
- export { DraggableItem as a, ICON_OPTIONS as i, Panel as n, ICON_MAP as r, Dnd as t };
1726
+ export { Panel as a, ICON_OPTIONS as c, useDndPalette as d, useDndPanel as f, Palette as i, useItemsEditor as l, Canvas as n, Field as o, DraggableItem as p, Layout as r, ICON_MAP as s, Dnd as t, useDndLayout as u };
1639
1727
 
1640
- //# sourceMappingURL=dnd-C1YGcp4k.js.map
1728
+ //# sourceMappingURL=dnd-Bl_oqayG.js.map