@btst/stack 1.10.0 → 1.11.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 (75) hide show
  1. package/dist/packages/ui/src/components/minimal-tiptap/utils.cjs +15 -11
  2. package/dist/packages/ui/src/components/minimal-tiptap/utils.mjs +15 -11
  3. package/dist/packages/ui/src/components/ui-builder/index.cjs +9 -7
  4. package/dist/packages/ui/src/components/ui-builder/index.mjs +9 -7
  5. package/dist/packages/ui/src/components/ui-builder/internal/canvas/auto-frame.cjs +6 -3
  6. package/dist/packages/ui/src/components/ui-builder/internal/canvas/auto-frame.mjs +6 -3
  7. package/dist/packages/ui/src/components/ui-builder/internal/components/add-component-popover.cjs +228 -48
  8. package/dist/packages/ui/src/components/ui-builder/internal/components/add-component-popover.mjs +228 -48
  9. package/dist/packages/ui/src/components/ui-builder/internal/components/element-selector.cjs +1 -1
  10. package/dist/packages/ui/src/components/ui-builder/internal/components/element-selector.mjs +1 -1
  11. package/dist/packages/ui/src/components/ui-builder/internal/components/error-fallback.cjs +4 -2
  12. package/dist/packages/ui/src/components/ui-builder/internal/components/error-fallback.mjs +4 -2
  13. package/dist/packages/ui/src/components/ui-builder/internal/components/multi-select.cjs +6 -3
  14. package/dist/packages/ui/src/components/ui-builder/internal/components/multi-select.mjs +6 -3
  15. package/dist/packages/ui/src/components/ui-builder/internal/dnd/draggable-new-component.cjs +67 -0
  16. package/dist/packages/ui/src/components/ui-builder/internal/dnd/draggable-new-component.mjs +62 -0
  17. package/dist/packages/ui/src/components/ui-builder/internal/dnd/drop-zone.cjs +181 -37
  18. package/dist/packages/ui/src/components/ui-builder/internal/dnd/drop-zone.mjs +181 -38
  19. package/dist/packages/ui/src/components/ui-builder/internal/editor-panel.cjs +1 -1
  20. package/dist/packages/ui/src/components/ui-builder/internal/editor-panel.mjs +1 -1
  21. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-group-control.cjs +1 -1
  22. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-group-control.mjs +1 -1
  23. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-item-control.cjs +9 -2
  24. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-item-control.mjs +9 -2
  25. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/iconname-field.cjs +3 -2
  26. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/iconname-field.mjs +3 -2
  27. package/dist/packages/ui/src/components/ui-builder/internal/layers-panel.cjs +1 -1
  28. package/dist/packages/ui/src/components/ui-builder/internal/layers-panel.mjs +1 -1
  29. package/dist/packages/ui/src/components/ui-builder/internal/props-panel.cjs +17 -5
  30. package/dist/packages/ui/src/components/ui-builder/internal/props-panel.mjs +17 -5
  31. package/dist/packages/ui/src/components/ui-builder/internal/utils/render-utils.cjs +70 -16
  32. package/dist/packages/ui/src/components/ui-builder/internal/utils/render-utils.mjs +73 -20
  33. package/dist/packages/ui/src/lib/ui-builder/context/dnd-context-colission-utils.cjs +14 -9
  34. package/dist/packages/ui/src/lib/ui-builder/context/dnd-context-colission-utils.mjs +14 -9
  35. package/dist/packages/ui/src/lib/ui-builder/context/dnd-context.cjs +38 -10
  36. package/dist/packages/ui/src/lib/ui-builder/context/dnd-context.mjs +35 -11
  37. package/dist/packages/ui/src/lib/ui-builder/context/dnd-contexts.cjs +1 -0
  38. package/dist/packages/ui/src/lib/ui-builder/context/dnd-contexts.mjs +1 -0
  39. package/dist/packages/ui/src/lib/ui-builder/context/drag-overlay.cjs +7 -4
  40. package/dist/packages/ui/src/lib/ui-builder/context/drag-overlay.mjs +7 -4
  41. package/dist/packages/ui/src/lib/ui-builder/hooks/use-auto-scroll.cjs +4 -4
  42. package/dist/packages/ui/src/lib/ui-builder/hooks/use-auto-scroll.mjs +4 -4
  43. package/dist/packages/ui/src/lib/ui-builder/hooks/use-dnd-event-handlers.cjs +53 -16
  44. package/dist/packages/ui/src/lib/ui-builder/hooks/use-dnd-event-handlers.mjs +53 -16
  45. package/dist/packages/ui/src/lib/ui-builder/hooks/use-drop-validation.cjs +23 -7
  46. package/dist/packages/ui/src/lib/ui-builder/hooks/use-drop-validation.mjs +23 -7
  47. package/dist/packages/ui/src/lib/ui-builder/registry/form-field-overrides.cjs +110 -11
  48. package/dist/packages/ui/src/lib/ui-builder/registry/form-field-overrides.mjs +111 -13
  49. package/dist/packages/ui/src/lib/ui-builder/store/editor-store.cjs +3 -2
  50. package/dist/packages/ui/src/lib/ui-builder/store/editor-store.mjs +3 -2
  51. package/dist/packages/ui/src/lib/ui-builder/store/layer-store.cjs +53 -7
  52. package/dist/packages/ui/src/lib/ui-builder/store/layer-store.mjs +54 -8
  53. package/dist/packages/ui/src/lib/ui-builder/store/layer-utils.cjs +4 -3
  54. package/dist/packages/ui/src/lib/ui-builder/store/layer-utils.mjs +4 -3
  55. package/dist/packages/ui/src/lib/ui-builder/utils/variable-resolver.cjs +12 -0
  56. package/dist/packages/ui/src/lib/ui-builder/utils/variable-resolver.mjs +12 -1
  57. package/dist/plugins/ui-builder/client/components/index.d.cts +1 -1
  58. package/dist/plugins/ui-builder/client/components/index.d.mts +1 -1
  59. package/dist/plugins/ui-builder/client/components/index.d.ts +1 -1
  60. package/dist/plugins/ui-builder/client/hooks/index.d.cts +2 -2
  61. package/dist/plugins/ui-builder/client/hooks/index.d.mts +2 -2
  62. package/dist/plugins/ui-builder/client/hooks/index.d.ts +2 -2
  63. package/dist/plugins/ui-builder/client/index.d.cts +17 -7
  64. package/dist/plugins/ui-builder/client/index.d.mts +17 -7
  65. package/dist/plugins/ui-builder/client/index.d.ts +17 -7
  66. package/dist/plugins/ui-builder/index.d.cts +2 -2
  67. package/dist/plugins/ui-builder/index.d.mts +2 -2
  68. package/dist/plugins/ui-builder/index.d.ts +2 -2
  69. package/dist/shared/{stack.BSM2cgoq.d.cts → stack.BYysGdHl.d.cts} +1 -1
  70. package/dist/shared/{stack.CqfZWfjJ.d.cts → stack.BdJFrdyt.d.cts} +8 -2
  71. package/dist/shared/{stack.e1FN86dE.d.mts → stack.ChVuHi5e.d.mts} +8 -2
  72. package/dist/shared/{stack.CLtOoAqF.d.mts → stack.DYCFcnkL.d.mts} +1 -1
  73. package/dist/shared/{stack.MMntCVZZ.d.ts → stack.EhM4pmtN.d.ts} +8 -2
  74. package/dist/shared/{stack.BD1m-4yB.d.ts → stack.kFbDspnF.d.ts} +1 -1
  75. package/package.json +1 -1
@@ -8,25 +8,48 @@ import { useEditorStore } from '../../../../lib/ui-builder/store/editor-store.mj
8
8
  import { cn } from '../../../../lib/utils.mjs';
9
9
  import LayerRenderer from '../../layer-renderer.mjs';
10
10
  import { createComponentLayer } from '../../../../lib/ui-builder/store/layer-utils.mjs';
11
+ import { DraggableNewComponent } from '../dnd/draggable-new-component.mjs';
11
12
 
12
13
  const fallback = /* @__PURE__ */ jsx("div", { className: "w-full h-full bg-muted rounded border animate-pulse" });
13
14
  const previewLayerCache = /* @__PURE__ */ new Map();
15
+ function isValidChildOfParent(componentRegistry, componentType, parentType) {
16
+ const def = componentRegistry[componentType];
17
+ if (!def?.childOf) {
18
+ return true;
19
+ }
20
+ if (!parentType) {
21
+ return false;
22
+ }
23
+ return def.childOf.includes(parentType);
24
+ }
25
+ function isChildOnlyComponent(componentRegistry, componentType) {
26
+ const def = componentRegistry[componentType];
27
+ return Boolean(def?.childOf);
28
+ }
14
29
  function AddComponentsPopover({
15
30
  className,
16
31
  children,
17
32
  addPosition,
18
33
  parentLayerId,
19
34
  onOpenChange,
35
+ enableDragToCanvas = false,
20
36
  onChange
21
37
  }) {
22
38
  const [open, setOpen] = React__default.useState(false);
39
+ const [activeView, setActiveView] = React__default.useState("components");
23
40
  const componentRegistry = useEditorStore((state) => state.registry);
41
+ const blocks = useEditorStore((state) => state.blocks);
42
+ const findLayerById = useLayerStore((state) => state.findLayerById);
43
+ const parentLayerType = useMemo(() => {
44
+ const parentLayer = findLayerById(parentLayerId);
45
+ return parentLayer?.type;
46
+ }, [findLayerById, parentLayerId]);
24
47
  const groupedOptions = useMemo(() => {
25
- const componentOptions = Object.keys(componentRegistry).map((name) => ({
48
+ const componentOptions = Object.keys(componentRegistry).filter((name) => isValidChildOfParent(componentRegistry, name, parentLayerType)).map((name) => ({
26
49
  value: name,
27
50
  label: name,
28
51
  type: "component",
29
- from: componentRegistry[name].from
52
+ from: componentRegistry[name]?.from
30
53
  }));
31
54
  return componentOptions.reduce(
32
55
  (acc, option) => {
@@ -35,16 +58,34 @@ function AddComponentsPopover({
35
58
  if (!acc[group]) {
36
59
  acc[group] = [];
37
60
  }
38
- acc[group].push(option);
61
+ acc[group]?.push(option);
39
62
  return acc;
40
63
  },
41
64
  {}
42
65
  );
43
- }, [componentRegistry]);
66
+ }, [componentRegistry, parentLayerType]);
44
67
  const categories = useMemo(() => {
45
68
  return Object.keys(groupedOptions);
46
69
  }, [groupedOptions]);
70
+ const groupedBlocks = useMemo(() => {
71
+ if (!blocks) return {};
72
+ return Object.values(blocks).reduce(
73
+ (acc, block) => {
74
+ if (!acc[block.category]) {
75
+ acc[block.category] = [];
76
+ }
77
+ acc[block.category]?.push(block);
78
+ return acc;
79
+ },
80
+ {}
81
+ );
82
+ }, [blocks]);
83
+ const blockCategories = useMemo(() => {
84
+ return Object.keys(groupedBlocks);
85
+ }, [groupedBlocks]);
86
+ const hasBlocks = blocks && Object.keys(blocks).length > 0;
47
87
  const addComponentLayer = useLayerStore((state) => state.addComponentLayer);
88
+ const addLayerDirect = useLayerStore((state) => state.addLayerDirect);
48
89
  const handleSelect = React__default.useCallback(
49
90
  (currentValue) => {
50
91
  if (onChange) {
@@ -69,6 +110,15 @@ function AddComponentsPopover({
69
110
  onChange
70
111
  ]
71
112
  );
113
+ const handleBlockSelect = React__default.useCallback(
114
+ (block) => {
115
+ const clonedTemplate = cloneLayerWithNewIds(block.template);
116
+ addLayerDirect(clonedTemplate, parentLayerId, addPosition);
117
+ setOpen(false);
118
+ onOpenChange?.(false);
119
+ },
120
+ [addLayerDirect, parentLayerId, addPosition, onOpenChange]
121
+ );
72
122
  const handleOpenChange = useCallback(
73
123
  (open2) => {
74
124
  setOpen(open2);
@@ -76,63 +126,180 @@ function AddComponentsPopover({
76
126
  },
77
127
  [onOpenChange]
78
128
  );
129
+ const handleDragStart = useCallback(() => {
130
+ setOpen(false);
131
+ onOpenChange?.(false);
132
+ }, [onOpenChange]);
79
133
  const defaultTab = categories[0] || "";
134
+ const defaultBlockCategory = blockCategories[0] || "";
80
135
  return /* @__PURE__ */ jsx("div", { className: cn("relative flex justify-center", className), children: /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
81
136
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children }),
82
- /* @__PURE__ */ jsx(PopoverContent, { className: "w-[280px] p-0", align: "start", children: categories.length > 0 ? /* @__PURE__ */ jsxs(Tabs, { defaultValue: defaultTab, className: "w-full", children: [
83
- /* @__PURE__ */ jsx(TabsList, { className: cn(categories.length > 1 ? "h-14 w-full rounded-none border-b flex flex-row overflow-x-scroll justify-start" : "hidden"), children: categories.map((category) => /* @__PURE__ */ jsxs(TabsTrigger, { value: category, className: "flex flex-col justify-start items-start overflow-hidden px-2 py-1 min-w-24 min-h-11 flex-shrink-0", children: [
84
- /* @__PURE__ */ jsx("div", { className: "text-sm", children: formatCategoryName(category) }),
85
- /* @__PURE__ */ jsx("div", { className: "w-full min-h-[12px] text-[8px] leading-[9px] text-left text-muted-foreground text-wrap", children: category })
86
- ] }, category)) }),
87
- categories.map((category) => /* @__PURE__ */ jsx(TabsContent, { value: category, className: "m-0", children: /* @__PURE__ */ jsxs(Command, { className: "border-0", children: [
88
- /* @__PURE__ */ jsx("div", { className: "flex items-center px-3 w-full [&>div:first-child]:w-full", children: /* @__PURE__ */ jsx(
89
- CommandInput,
137
+ /* @__PURE__ */ jsxs(PopoverContent, { className: "w-[320px] p-0", align: "start", children: [
138
+ hasBlocks && /* @__PURE__ */ jsxs("div", { className: "flex border-b", children: [
139
+ /* @__PURE__ */ jsx(
140
+ "button",
141
+ {
142
+ className: cn(
143
+ "flex-1 px-4 py-2 text-sm font-medium transition-colors",
144
+ activeView === "components" ? "bg-background border-b-2 border-primary" : "bg-muted/50 text-muted-foreground hover:text-foreground"
145
+ ),
146
+ onClick: () => setActiveView("components"),
147
+ children: "Components"
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsx(
151
+ "button",
90
152
  {
91
- placeholder: "Find components",
92
- className: "border-0 focus:ring-0 w-full"
153
+ className: cn(
154
+ "flex-1 px-4 py-2 text-sm font-medium transition-colors",
155
+ activeView === "blocks" ? "bg-background border-b-2 border-primary" : "bg-muted/50 text-muted-foreground hover:text-foreground"
156
+ ),
157
+ onClick: () => setActiveView("blocks"),
158
+ children: "Blocks"
93
159
  }
94
- ) }),
95
- /* @__PURE__ */ jsxs(CommandList, { className: "max-h-[250px]", children: [
96
- /* @__PURE__ */ jsx(CommandEmpty, { children: "No components found" }),
97
- /* @__PURE__ */ jsx(CommandGroup, { children: groupedOptions[category].map((component) => /* @__PURE__ */ jsx(
98
- GroupedComponentItem,
160
+ )
161
+ ] }),
162
+ activeView === "components" && categories.length > 0 ? /* @__PURE__ */ jsxs(Tabs, { defaultValue: defaultTab, className: "w-full", children: [
163
+ /* @__PURE__ */ jsx(TabsList, { className: cn(categories.length > 1 ? "h-14 w-full rounded-none border-b flex flex-row overflow-x-scroll justify-start" : "hidden"), children: categories.map((category) => /* @__PURE__ */ jsxs(TabsTrigger, { value: category, className: "flex flex-col justify-start items-start overflow-hidden px-2 py-1 min-w-24 min-h-11 shrink-0", children: [
164
+ /* @__PURE__ */ jsx("div", { className: "text-sm", children: formatCategoryName(category) }),
165
+ /* @__PURE__ */ jsx("div", { className: "w-full min-h-[12px] text-[8px] leading-[9px] text-left text-muted-foreground text-wrap", children: category })
166
+ ] }, category)) }),
167
+ categories.map((category) => /* @__PURE__ */ jsx(TabsContent, { value: category, className: "m-0", children: /* @__PURE__ */ jsxs(Command, { className: "border-0", children: [
168
+ /* @__PURE__ */ jsx("div", { className: "flex items-center px-3 w-full [&>div:first-child]:w-full", children: /* @__PURE__ */ jsx(
169
+ CommandInput,
99
170
  {
100
- component,
101
- onClick: handleSelect
102
- },
103
- component.value
104
- )) })
105
- ] })
106
- ] }) }, category))
107
- ] }) : /* @__PURE__ */ jsxs(Command, { children: [
108
- /* @__PURE__ */ jsx(CommandInput, { placeholder: "Add component" }),
109
- /* @__PURE__ */ jsx(CommandList, { children: /* @__PURE__ */ jsx(CommandEmpty, { children: "No components found" }) })
110
- ] }) })
171
+ placeholder: "Find components",
172
+ className: "border-0 focus:ring-0 w-full"
173
+ }
174
+ ) }),
175
+ /* @__PURE__ */ jsxs(CommandList, { className: "max-h-[250px]", children: [
176
+ /* @__PURE__ */ jsx(CommandEmpty, { children: "No components found" }),
177
+ /* @__PURE__ */ jsx(CommandGroup, { children: groupedOptions[category]?.map((component) => /* @__PURE__ */ jsx(
178
+ GroupedComponentItem,
179
+ {
180
+ component,
181
+ onClick: handleSelect,
182
+ onDragStart: handleDragStart,
183
+ enableDrag: enableDragToCanvas
184
+ },
185
+ component.value
186
+ )) })
187
+ ] })
188
+ ] }) }, category))
189
+ ] }) : activeView === "components" ? /* @__PURE__ */ jsxs(Command, { children: [
190
+ /* @__PURE__ */ jsx(CommandInput, { placeholder: "Add component" }),
191
+ /* @__PURE__ */ jsx(CommandList, { children: /* @__PURE__ */ jsx(CommandEmpty, { children: "No components found" }) })
192
+ ] }) : null,
193
+ activeView === "blocks" && hasBlocks && /* @__PURE__ */ jsxs(Tabs, { defaultValue: defaultBlockCategory, className: "w-full", children: [
194
+ /* @__PURE__ */ jsx(TabsList, { className: "h-14 w-full rounded-none border-b flex flex-row overflow-x-scroll justify-start", children: blockCategories.map((category) => /* @__PURE__ */ jsxs(TabsTrigger, { value: category, className: "flex flex-col justify-start items-start overflow-hidden px-2 py-1 min-w-24 min-h-11 shrink-0", children: [
195
+ /* @__PURE__ */ jsx("div", { className: "text-sm", children: formatCategoryName(category) }),
196
+ /* @__PURE__ */ jsxs("div", { className: "w-full min-h-[12px] text-[8px] leading-[9px] text-left text-muted-foreground", children: [
197
+ groupedBlocks[category]?.length,
198
+ " blocks"
199
+ ] })
200
+ ] }, category)) }),
201
+ blockCategories.map((category) => /* @__PURE__ */ jsx(TabsContent, { value: category, className: "m-0", children: /* @__PURE__ */ jsxs(Command, { className: "border-0", children: [
202
+ /* @__PURE__ */ jsx("div", { className: "flex items-center px-3 w-full [&>div:first-child]:w-full", children: /* @__PURE__ */ jsx(
203
+ CommandInput,
204
+ {
205
+ placeholder: "Find blocks",
206
+ className: "border-0 focus:ring-0 w-full"
207
+ }
208
+ ) }),
209
+ /* @__PURE__ */ jsxs(CommandList, { className: "max-h-[250px]", children: [
210
+ /* @__PURE__ */ jsx(CommandEmpty, { children: "No blocks found" }),
211
+ /* @__PURE__ */ jsx(CommandGroup, { children: groupedBlocks[category]?.map((block) => /* @__PURE__ */ jsx(
212
+ BlockItem,
213
+ {
214
+ block,
215
+ onClick: handleBlockSelect
216
+ },
217
+ block.name
218
+ )) })
219
+ ] })
220
+ ] }) }, category))
221
+ ] })
222
+ ] })
111
223
  ] }) });
112
224
  }
225
+ function cloneLayerWithNewIds(layer) {
226
+ const newId = generateUniqueId();
227
+ let newChildren;
228
+ if (Array.isArray(layer.children)) {
229
+ newChildren = layer.children.map((child) => cloneLayerWithNewIds(child));
230
+ } else if (typeof layer.children === "string") {
231
+ newChildren = layer.children;
232
+ } else if (layer.children && typeof layer.children === "object" && "__variableRef" in layer.children) {
233
+ newChildren = layer.children;
234
+ } else {
235
+ newChildren = layer.children;
236
+ }
237
+ return {
238
+ ...layer,
239
+ id: newId,
240
+ children: newChildren
241
+ };
242
+ }
243
+ function generateUniqueId() {
244
+ return Math.random().toString(36).substring(2, 9);
245
+ }
246
+ const BlockItem = memo(({
247
+ block,
248
+ onClick
249
+ }) => {
250
+ const handleSelect = useCallback(() => {
251
+ onClick(block);
252
+ }, [onClick, block]);
253
+ return /* @__PURE__ */ jsxs(
254
+ CommandItem,
255
+ {
256
+ onSelect: handleSelect,
257
+ className: "cursor-pointer flex flex-col items-start gap-1 py-3",
258
+ children: [
259
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: formatBlockName(block.name) }),
260
+ block.description && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground line-clamp-2", children: block.description })
261
+ ]
262
+ }
263
+ );
264
+ });
265
+ BlockItem.displayName = "BlockItem";
266
+ function formatBlockName(name) {
267
+ return name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
268
+ }
113
269
  const GroupedComponentItem = memo(({
114
270
  component,
115
- onClick
271
+ onClick,
272
+ onDragStart,
273
+ enableDrag = false
116
274
  }) => {
117
275
  const handleSelect = useCallback(() => {
118
276
  onClick(component.value);
119
277
  }, [onClick, component.value]);
120
278
  const componentRegistry = useEditorStore((state) => state.registry);
121
- return /* @__PURE__ */ jsxs(
279
+ const content = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
280
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 w-10 h-6 overflow-hidden", children: /* @__PURE__ */ jsx(
281
+ LazyComponentPreview,
282
+ {
283
+ componentType: component.value,
284
+ componentRegistry
285
+ }
286
+ ) }),
287
+ /* @__PURE__ */ jsx("div", { className: "flex items-center flex-1 min-w-0", children: /* @__PURE__ */ jsx("span", { className: "truncate", children: component.label }) })
288
+ ] });
289
+ return /* @__PURE__ */ jsx(
122
290
  CommandItem,
123
291
  {
124
292
  onSelect: handleSelect,
125
- className: "cursor-pointer flex items-center gap-3 py-3",
126
- children: [
127
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 w-10 h-8 overflow-hidden", children: /* @__PURE__ */ jsx(
128
- LazyComponentPreview,
129
- {
130
- componentType: component.value,
131
- componentRegistry
132
- }
133
- ) }),
134
- /* @__PURE__ */ jsx("div", { className: "flex items-center flex-1 min-w-0", children: /* @__PURE__ */ jsx("span", { className: "truncate", children: component.label }) })
135
- ]
293
+ className: "cursor-pointer flex items-center gap-2 py-2",
294
+ children: enableDrag ? /* @__PURE__ */ jsx(
295
+ DraggableNewComponent,
296
+ {
297
+ componentType: component.value,
298
+ onDragStart,
299
+ className: "flex-1",
300
+ children: content
301
+ }
302
+ ) : /* @__PURE__ */ jsx("div", { className: "flex-1", children: content })
136
303
  },
137
304
  component.value
138
305
  );
@@ -150,7 +317,7 @@ const LazyComponentPreview = memo(({
150
317
  let timeoutId = null;
151
318
  const observer = new IntersectionObserver(
152
319
  ([entry]) => {
153
- if (entry.isIntersecting) {
320
+ if (entry?.isIntersecting) {
154
321
  timeoutId = setTimeout(() => setShouldLoad(true), 50);
155
322
  }
156
323
  },
@@ -173,12 +340,20 @@ const LazyComponentPreview = memo(({
173
340
  ) }) : /* @__PURE__ */ jsx("div", { className: "w-full h-full bg-muted rounded border" }) });
174
341
  });
175
342
  LazyComponentPreview.displayName = "LazyComponentPreview";
343
+ const ChildOnlyPlaceholder = memo(({ componentType }) => {
344
+ const initials = componentType.replace(/([A-Z])/g, " $1").trim().split(" ").map((word) => word[0]).slice(0, 2).join("");
345
+ return /* @__PURE__ */ jsx("div", { className: "w-full h-full bg-muted/50 rounded border border-dashed flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-[8px] font-medium text-muted-foreground", children: initials }) });
346
+ });
347
+ ChildOnlyPlaceholder.displayName = "ChildOnlyPlaceholder";
176
348
  const ComponentPreview = memo(({
177
349
  componentType,
178
350
  componentRegistry
179
351
  }) => {
180
- const style = { width: "200%", height: "200%" };
352
+ const isChildOnly = isChildOnlyComponent(componentRegistry, componentType);
181
353
  const previewLayer = useMemo(() => {
354
+ if (isChildOnly) {
355
+ return null;
356
+ }
182
357
  const cacheKey = `${componentType}-${JSON.stringify(componentRegistry[componentType]?.schema)}`;
183
358
  if (previewLayerCache.has(cacheKey)) {
184
359
  return previewLayerCache.get(cacheKey);
@@ -194,15 +369,20 @@ const ComponentPreview = memo(({
194
369
  console.warn(`Failed to create preview for component ${componentType}:`, error);
195
370
  return null;
196
371
  }
197
- }, [componentType, componentRegistry]);
372
+ }, [componentType, componentRegistry, isChildOnly]);
373
+ if (isChildOnly) {
374
+ return /* @__PURE__ */ jsx(ChildOnlyPlaceholder, { componentType });
375
+ }
198
376
  if (!previewLayer) {
199
377
  return /* @__PURE__ */ jsx("div", { className: "w-full h-full bg-muted rounded border" });
200
378
  }
379
+ const style = { width: "200%", height: "200%" };
201
380
  return /* @__PURE__ */ jsx(
202
381
  "div",
203
382
  {
204
- className: "w-full h-full bg-background rounded border overflow-hidden transform scale-50 origin-top-left",
383
+ className: "w-full h-full bg-background rounded border overflow-hidden transform scale-50 origin-top-left pointer-events-none",
205
384
  style,
385
+ inert: true,
206
386
  children: /* @__PURE__ */ jsx(
207
387
  LayerRenderer,
208
388
  {
@@ -218,7 +398,7 @@ ComponentPreview.displayName = "ComponentPreview";
218
398
  function formatCategoryName(name) {
219
399
  const words = name.split("/");
220
400
  const lastWord = words[words.length - 1];
221
- return lastWord.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
401
+ return lastWord?.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()) ?? "";
222
402
  }
223
403
 
224
404
  export { AddComponentsPopover };
@@ -96,7 +96,7 @@ const ElementSelector = ({
96
96
  ref: refs.setReference,
97
97
  className: utils.cn(
98
98
  "fixed box-border hover:border-blue-300 hover:border-2 hover:bg-blue-300/20 hover:shadow-md hover:shadow-blue-500/20 cursor-default",
99
- isBeingDragged ? "border-2 border-orange-500 border-dashed shadow-lg shadow-orange-500/30 opacity-70 bg-orange-50/20" : isSelected ? "border-2 border-blue-500 hover:border-blue-500 hover:bg-transparent hover:shadow-none" : ""
99
+ isBeingDragged ? "border-2 border-green-500 border-dashed shadow-lg opacity-70 bg-orange-50/20" : isSelected ? "border-2 border-blue-500 hover:border-blue-500 hover:bg-transparent hover:shadow-none" : ""
100
100
  ),
101
101
  style: overlayStyle
102
102
  }
@@ -94,7 +94,7 @@ const ElementSelector = ({
94
94
  ref: refs.setReference,
95
95
  className: cn(
96
96
  "fixed box-border hover:border-blue-300 hover:border-2 hover:bg-blue-300/20 hover:shadow-md hover:shadow-blue-500/20 cursor-default",
97
- isBeingDragged ? "border-2 border-orange-500 border-dashed shadow-lg shadow-orange-500/30 opacity-70 bg-orange-50/20" : isSelected ? "border-2 border-blue-500 hover:border-blue-500 hover:bg-transparent hover:shadow-none" : ""
97
+ isBeingDragged ? "border-2 border-green-500 border-dashed shadow-lg opacity-70 bg-orange-50/20" : isSelected ? "border-2 border-blue-500 hover:border-blue-500 hover:bg-transparent hover:shadow-none" : ""
98
98
  ),
99
99
  style: overlayStyle
100
100
  }
@@ -3,15 +3,17 @@
3
3
  const jsxRuntime = require('react/jsx-runtime');
4
4
 
5
5
  function ErrorFallback({ error }) {
6
+ const errorMessage = error == null ? void 0 : error instanceof Error ? error.message : String(error);
7
+ const errorStack = error instanceof Error ? error.stack : void 0;
6
8
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 border border-red-500 bg-red-100 text-red-700 rounded flex-grow w-full", children: [
7
9
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold mb-2", children: "Component Error" }),
8
10
  /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
9
11
  "Error: ",
10
- error?.message || "Unknown error"
12
+ errorMessage || "Unknown error"
11
13
  ] }),
12
14
  /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "mt-2", children: [
13
15
  /* @__PURE__ */ jsxRuntime.jsx("summary", { className: "cursor-pointer", children: "Stack trace" }),
14
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-2 text-xs whitespace-pre-wrap", children: error?.stack })
16
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-2 text-xs whitespace-pre-wrap", children: errorStack })
15
17
  ] })
16
18
  ] });
17
19
  }
@@ -1,15 +1,17 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
 
3
3
  function ErrorFallback({ error }) {
4
+ const errorMessage = error == null ? void 0 : error instanceof Error ? error.message : String(error);
5
+ const errorStack = error instanceof Error ? error.stack : void 0;
4
6
  return /* @__PURE__ */ jsxs("div", { className: "p-4 border border-red-500 bg-red-100 text-red-700 rounded flex-grow w-full", children: [
5
7
  /* @__PURE__ */ jsx("h3", { className: "font-bold mb-2", children: "Component Error" }),
6
8
  /* @__PURE__ */ jsxs("p", { children: [
7
9
  "Error: ",
8
- error?.message || "Unknown error"
10
+ errorMessage || "Unknown error"
9
11
  ] }),
10
12
  /* @__PURE__ */ jsxs("details", { className: "mt-2", children: [
11
13
  /* @__PURE__ */ jsx("summary", { className: "cursor-pointer", children: "Stack trace" }),
12
- /* @__PURE__ */ jsx("pre", { className: "mt-2 text-xs whitespace-pre-wrap", children: error?.stack })
14
+ /* @__PURE__ */ jsx("pre", { className: "mt-2 text-xs whitespace-pre-wrap", children: errorStack })
13
15
  ] })
14
16
  ] });
15
17
  }
@@ -40,7 +40,10 @@ function transToGroupOption(options, groupBy) {
40
40
  if (!groupOption[key]) {
41
41
  groupOption[key] = [];
42
42
  }
43
- groupOption[key].push(option);
43
+ const group = groupOption[key];
44
+ if (group) {
45
+ group.push(option);
46
+ }
44
47
  });
45
48
  return groupOption;
46
49
  }
@@ -145,8 +148,8 @@ const MultipleSelector = React__namespace.forwardRef(
145
148
  if (e.key === "Delete" || e.key === "Backspace") {
146
149
  if (input.value === "" && selected.length > 0) {
147
150
  const lastSelectOption = selected[selected.length - 1];
148
- if (!lastSelectOption.fixed) {
149
- handleUnselect(selected[selected.length - 1]);
151
+ if (lastSelectOption && !lastSelectOption.fixed) {
152
+ handleUnselect(lastSelectOption);
150
153
  }
151
154
  }
152
155
  }
@@ -25,7 +25,10 @@ function transToGroupOption(options, groupBy) {
25
25
  if (!groupOption[key]) {
26
26
  groupOption[key] = [];
27
27
  }
28
- groupOption[key].push(option);
28
+ const group = groupOption[key];
29
+ if (group) {
30
+ group.push(option);
31
+ }
29
32
  });
30
33
  return groupOption;
31
34
  }
@@ -130,8 +133,8 @@ const MultipleSelector = React.forwardRef(
130
133
  if (e.key === "Delete" || e.key === "Backspace") {
131
134
  if (input.value === "" && selected.length > 0) {
132
135
  const lastSelectOption = selected[selected.length - 1];
133
- if (!lastSelectOption.fixed) {
134
- handleUnselect(selected[selected.length - 1]);
136
+ if (lastSelectOption && !lastSelectOption.fixed) {
137
+ handleUnselect(lastSelectOption);
135
138
  }
136
139
  }
137
140
  }
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const jsxRuntime = require('react/jsx-runtime');
6
+ const React = require('react');
7
+ const core_esm = require('../../../../../../../node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@dnd-kit/core/dist/core.esm.cjs');
8
+ const LucideIcons = require('lucide-react');
9
+ const utils = require('../../../../lib/utils.cjs');
10
+
11
+ const DraggableNewComponent = ({
12
+ componentType,
13
+ children,
14
+ onDragStart,
15
+ className
16
+ }) => {
17
+ const {
18
+ attributes,
19
+ listeners,
20
+ setNodeRef,
21
+ setActivatorNodeRef,
22
+ isDragging
23
+ } = core_esm.useDraggable({
24
+ id: `new-component-${componentType}`,
25
+ data: {
26
+ type: "new-component",
27
+ componentType
28
+ }
29
+ });
30
+ React.useEffect(() => {
31
+ if (isDragging && onDragStart) {
32
+ onDragStart();
33
+ }
34
+ }, [isDragging, onDragStart]);
35
+ return /* @__PURE__ */ jsxRuntime.jsxs(
36
+ "div",
37
+ {
38
+ ref: setNodeRef,
39
+ className: utils.cn(
40
+ "flex items-center gap-1",
41
+ isDragging && "opacity-50",
42
+ className
43
+ ),
44
+ ...attributes,
45
+ children: [
46
+ /* @__PURE__ */ jsxRuntime.jsx(
47
+ "div",
48
+ {
49
+ ref: setActivatorNodeRef,
50
+ ...listeners,
51
+ className: utils.cn(
52
+ "flex-shrink-0 cursor-grab active:cursor-grabbing p-1 -ml-1",
53
+ "text-muted-foreground hover:text-foreground transition-colors",
54
+ "rounded hover:bg-muted/50"
55
+ ),
56
+ "aria-label": `Drag ${componentType} to canvas`,
57
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.GripVertical, { className: "h-4 w-4" })
58
+ }
59
+ ),
60
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children })
61
+ ]
62
+ }
63
+ );
64
+ };
65
+
66
+ exports.DraggableNewComponent = DraggableNewComponent;
67
+ exports.default = DraggableNewComponent;
@@ -0,0 +1,62 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useEffect } from 'react';
3
+ import { useDraggable } from '../../../../../../../node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@dnd-kit/core/dist/core.esm.mjs';
4
+ import { GripVertical } from 'lucide-react';
5
+ import { cn } from '../../../../lib/utils.mjs';
6
+
7
+ const DraggableNewComponent = ({
8
+ componentType,
9
+ children,
10
+ onDragStart,
11
+ className
12
+ }) => {
13
+ const {
14
+ attributes,
15
+ listeners,
16
+ setNodeRef,
17
+ setActivatorNodeRef,
18
+ isDragging
19
+ } = useDraggable({
20
+ id: `new-component-${componentType}`,
21
+ data: {
22
+ type: "new-component",
23
+ componentType
24
+ }
25
+ });
26
+ useEffect(() => {
27
+ if (isDragging && onDragStart) {
28
+ onDragStart();
29
+ }
30
+ }, [isDragging, onDragStart]);
31
+ return /* @__PURE__ */ jsxs(
32
+ "div",
33
+ {
34
+ ref: setNodeRef,
35
+ className: cn(
36
+ "flex items-center gap-1",
37
+ isDragging && "opacity-50",
38
+ className
39
+ ),
40
+ ...attributes,
41
+ children: [
42
+ /* @__PURE__ */ jsx(
43
+ "div",
44
+ {
45
+ ref: setActivatorNodeRef,
46
+ ...listeners,
47
+ className: cn(
48
+ "flex-shrink-0 cursor-grab active:cursor-grabbing p-1 -ml-1",
49
+ "text-muted-foreground hover:text-foreground transition-colors",
50
+ "rounded hover:bg-muted/50"
51
+ ),
52
+ "aria-label": `Drag ${componentType} to canvas`,
53
+ children: /* @__PURE__ */ jsx(GripVertical, { className: "h-4 w-4" })
54
+ }
55
+ ),
56
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children })
57
+ ]
58
+ }
59
+ );
60
+ };
61
+
62
+ export { DraggableNewComponent, DraggableNewComponent as default };