@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
@@ -32,7 +32,9 @@ function ClassNameItemControl({ value, onChange }) {
32
32
  }
33
33
  }
34
34
  });
35
- if (!found) initialSelected[group.label] = group.keys[0];
35
+ if (!found && group.keys[0]) {
36
+ initialSelected[group.label] = group.keys[0];
37
+ }
36
38
  });
37
39
  const handledTokens = new Set(
38
40
  Object.values(parsedState).flatMap(
@@ -50,7 +52,9 @@ function ClassNameItemControl({ value, onChange }) {
50
52
  } else {
51
53
  const initialSelected = {};
52
54
  LAYOUT_GROUPS.forEach((group) => {
53
- initialSelected[group.label] = group.keys[0];
55
+ if (group.keys[0]) {
56
+ initialSelected[group.label] = group.keys[0];
57
+ }
54
58
  });
55
59
  return {
56
60
  parsedState: {},
@@ -172,7 +176,9 @@ function ClassNameItemControl({ value, onChange }) {
172
176
  const keys = group.keys.map(String);
173
177
  const selectedKey = selectedKeys[group.label] || keys[0];
174
178
  if (entry.isVisible && !entry.isVisible(state)) return null;
179
+ if (!selectedKey) return null;
175
180
  const groupConfig = CONFIG[selectedKey];
181
+ if (!groupConfig) return null;
176
182
  return /* @__PURE__ */ jsx("div", { className: cn("w-full", entry.className), "data-testid": `group-${group.label.toLowerCase().replace(/\s+/g, "-")}`, children: /* @__PURE__ */ jsx(
177
183
  ClassNameGroupControl,
178
184
  {
@@ -188,6 +194,7 @@ function ClassNameItemControl({ value, onChange }) {
188
194
  if (entry.isVisible && !entry.isVisible(state)) return null;
189
195
  const configKey = entry.key;
190
196
  const ungroupedConfig = CONFIG[configKey];
197
+ if (!ungroupedConfig) return null;
191
198
  return /* @__PURE__ */ jsx("div", { className: cn("w-full", entry.className), "data-testid": `item-${configKey}`, children: /* @__PURE__ */ jsx(
192
199
  ungroupedConfig.component,
193
200
  {
@@ -29,8 +29,9 @@ const IconNameField = ({
29
29
  }, []);
30
30
  const handleChange = React.useCallback(
31
31
  (values) => {
32
- if (values.length > 0) {
33
- onChange(values[0].value);
32
+ const firstValue = values[0];
33
+ if (firstValue) {
34
+ onChange(firstValue.value);
34
35
  }
35
36
  },
36
37
  [onChange]
@@ -27,8 +27,9 @@ const IconNameField = ({
27
27
  }, []);
28
28
  const handleChange = useCallback(
29
29
  (values) => {
30
- if (values.length > 0) {
31
- onChange(values[0].value);
30
+ const firstValue = values[0];
31
+ if (firstValue) {
32
+ onChange(firstValue.value);
32
33
  }
33
34
  },
34
35
  [onChange]
@@ -84,7 +84,7 @@ const LayersTree = React__default.memo(
84
84
  }
85
85
  const updatedChildren = layerUtils.hasLayerChildren(updatedPageLayer) ? updatedPageLayer.children || [] : [];
86
86
  const currentLayer = layers[0];
87
- const currentChildren = layerUtils.hasLayerChildren(currentLayer) ? currentLayer.children || [] : [];
87
+ const currentChildren = currentLayer && layerUtils.hasLayerChildren(currentLayer) ? currentLayer.children || [] : [];
88
88
  if (!index(currentChildren, updatedChildren)) {
89
89
  updateLayer(selectedPageId, {}, { children: updatedChildren });
90
90
  }
@@ -76,7 +76,7 @@ const LayersTree = React__default.memo(
76
76
  }
77
77
  const updatedChildren = hasLayerChildren(updatedPageLayer) ? updatedPageLayer.children || [] : [];
78
78
  const currentLayer = layers[0];
79
- const currentChildren = hasLayerChildren(currentLayer) ? currentLayer.children || [] : [];
79
+ const currentChildren = currentLayer && hasLayerChildren(currentLayer) ? currentLayer.children || [] : [];
80
80
  if (!isDeepEqual(currentChildren, updatedChildren)) {
81
81
  updateLayer(selectedPageId, {}, { children: updatedChildren });
82
82
  }
@@ -101,7 +101,10 @@ const ComponentPropsAutoForm = ({
101
101
  );
102
102
  const { schema } = React.useMemo(() => {
103
103
  if (selectedLayer && componentRegistry[selectedLayer.type]) {
104
- return componentRegistry[selectedLayer.type];
104
+ const registryEntry = componentRegistry[selectedLayer.type];
105
+ if (registryEntry) {
106
+ return registryEntry;
107
+ }
105
108
  }
106
109
  return { schema: EMPTY_ZOD_SCHEMA };
107
110
  }, [selectedLayer, componentRegistry]);
@@ -111,6 +114,7 @@ const ComponentPropsAutoForm = ({
111
114
  const handleDuplicateLayer = React.useCallback(() => {
112
115
  duplicateLayer(selectedLayerId);
113
116
  }, [duplicateLayer, selectedLayerId]);
117
+ const variables = layerStore.useLayerStore((state) => state.variables);
114
118
  const onParsedValuesChange = React.useCallback(
115
119
  (parsedValues) => {
116
120
  const { children, ...dataProps } = parsedValues;
@@ -123,7 +127,12 @@ const ComponentPropsAutoForm = ({
123
127
  const fieldDef = "shape" in schema && schema.shape ? schema.shape[key] : void 0;
124
128
  const baseType = fieldDef ? helpers.getBaseType(fieldDef) : void 0;
125
129
  if (types.isVariableReference(originalValue)) {
126
- preservedProps[key] = originalValue;
130
+ const variableExists = variables.some((v) => v.id === originalValue.__variableRef);
131
+ if (variableExists) {
132
+ preservedProps[key] = originalValue;
133
+ } else {
134
+ preservedProps[key] = newValue;
135
+ }
127
136
  } else {
128
137
  if (baseType === "ZodDate" && newValue instanceof Date) {
129
138
  preservedProps[key] = newValue.toISOString();
@@ -133,7 +142,11 @@ const ComponentPropsAutoForm = ({
133
142
  }
134
143
  });
135
144
  }
136
- if (typeof children === "string") {
145
+ const originalChildren = selectedLayer?.children;
146
+ const shouldPreserveChildrenBinding = types.isVariableReference(originalChildren) && variables.some((v) => v.id === originalChildren.__variableRef);
147
+ if (shouldPreserveChildrenBinding) {
148
+ updateLayer(selectedLayerId, preservedProps);
149
+ } else if (typeof children === "string") {
137
150
  updateLayer(selectedLayerId, preservedProps, { children });
138
151
  } else if (children && children.layerType) {
139
152
  updateLayer(selectedLayerId, preservedProps, {
@@ -148,9 +161,8 @@ const ComponentPropsAutoForm = ({
148
161
  updateLayer(selectedLayerId, preservedProps);
149
162
  }
150
163
  },
151
- [updateLayer, selectedLayerId, selectedLayer, addComponentLayer, schema]
164
+ [updateLayer, selectedLayerId, selectedLayer, addComponentLayer, schema, variables]
152
165
  );
153
- const variables = layerStore.useLayerStore((state) => state.variables);
154
166
  const formValues = React.useMemo(() => {
155
167
  if (!selectedLayer) return EMPTY_FORM_VALUES;
156
168
  const resolvedProps = variableResolver.resolveVariableReferences(
@@ -95,7 +95,10 @@ const ComponentPropsAutoForm = ({
95
95
  );
96
96
  const { schema } = useMemo(() => {
97
97
  if (selectedLayer && componentRegistry[selectedLayer.type]) {
98
- return componentRegistry[selectedLayer.type];
98
+ const registryEntry = componentRegistry[selectedLayer.type];
99
+ if (registryEntry) {
100
+ return registryEntry;
101
+ }
99
102
  }
100
103
  return { schema: EMPTY_ZOD_SCHEMA };
101
104
  }, [selectedLayer, componentRegistry]);
@@ -105,6 +108,7 @@ const ComponentPropsAutoForm = ({
105
108
  const handleDuplicateLayer = useCallback(() => {
106
109
  duplicateLayer(selectedLayerId);
107
110
  }, [duplicateLayer, selectedLayerId]);
111
+ const variables = useLayerStore((state) => state.variables);
108
112
  const onParsedValuesChange = useCallback(
109
113
  (parsedValues) => {
110
114
  const { children, ...dataProps } = parsedValues;
@@ -117,7 +121,12 @@ const ComponentPropsAutoForm = ({
117
121
  const fieldDef = "shape" in schema && schema.shape ? schema.shape[key] : void 0;
118
122
  const baseType = fieldDef ? getBaseType(fieldDef) : void 0;
119
123
  if (isVariableReference(originalValue)) {
120
- preservedProps[key] = originalValue;
124
+ const variableExists = variables.some((v) => v.id === originalValue.__variableRef);
125
+ if (variableExists) {
126
+ preservedProps[key] = originalValue;
127
+ } else {
128
+ preservedProps[key] = newValue;
129
+ }
121
130
  } else {
122
131
  if (baseType === "ZodDate" && newValue instanceof Date) {
123
132
  preservedProps[key] = newValue.toISOString();
@@ -127,7 +136,11 @@ const ComponentPropsAutoForm = ({
127
136
  }
128
137
  });
129
138
  }
130
- if (typeof children === "string") {
139
+ const originalChildren = selectedLayer?.children;
140
+ const shouldPreserveChildrenBinding = isVariableReference(originalChildren) && variables.some((v) => v.id === originalChildren.__variableRef);
141
+ if (shouldPreserveChildrenBinding) {
142
+ updateLayer(selectedLayerId, preservedProps);
143
+ } else if (typeof children === "string") {
131
144
  updateLayer(selectedLayerId, preservedProps, { children });
132
145
  } else if (children && children.layerType) {
133
146
  updateLayer(selectedLayerId, preservedProps, {
@@ -142,9 +155,8 @@ const ComponentPropsAutoForm = ({
142
155
  updateLayer(selectedLayerId, preservedProps);
143
156
  }
144
157
  },
145
- [updateLayer, selectedLayerId, selectedLayer, addComponentLayer, schema]
158
+ [updateLayer, selectedLayerId, selectedLayer, addComponentLayer, schema, variables]
146
159
  );
147
- const variables = useLayerStore((state) => state.variables);
148
160
  const formValues = useMemo(() => {
149
161
  if (!selectedLayer) return EMPTY_FORM_VALUES;
150
162
  const resolvedProps = resolveVariableReferences(
@@ -16,11 +16,30 @@ const reactErrorBoundary = require('react-error-boundary');
16
16
  const errorFallback = require('../components/error-fallback.cjs');
17
17
  const editorUtils = require('../../../../lib/ui-builder/store/editor-utils.cjs');
18
18
  const devProfiler = require('../components/dev-profiler.cjs');
19
+ const types = require('../../types.cjs');
19
20
  const variableResolver = require('../../../../lib/ui-builder/utils/variable-resolver.cjs');
20
21
 
22
+ const POSITION_CLASSES = ["static", "fixed", "absolute", "relative", "sticky"];
23
+ function hasPositionClass(className) {
24
+ const classes = className.split(/\s+/);
25
+ return POSITION_CLASSES.some((posClass) => classes.includes(posClass));
26
+ }
27
+ function isLayerBeingDraggedOrDescendant(layerId, activeLayerId, pages) {
28
+ if (!activeLayerId) return false;
29
+ if (layerId === activeLayerId) return true;
30
+ if (!pages || !Array.isArray(pages) || pages.length === 0) return false;
31
+ const draggedLayer = layerUtils.findLayerRecursive(pages, activeLayerId);
32
+ if (!draggedLayer) return false;
33
+ if (layerUtils.hasLayerChildren(draggedLayer)) {
34
+ const foundInDragged = layerUtils.findLayerRecursive([draggedLayer], layerId);
35
+ return !!foundInDragged;
36
+ }
37
+ return false;
38
+ }
21
39
  const RenderLayer = React.memo(
22
40
  ({ layer, componentRegistry, editorConfig, variables, variableValues }) => {
23
41
  const storeVariables = layerStore.useLayerStore((state) => state.variables);
42
+ const pages = layerStore.useLayerStore((state) => state.pages);
24
43
  const isLayerAPage = layerStore.useLayerStore((state) => state.isLayerAPage(layer.id));
25
44
  const registry = editorStore.useEditorStore((state) => state.registry);
26
45
  const dndContext = dndContexts.useDndContext();
@@ -38,15 +57,29 @@ const RenderLayer = React.memo(
38
57
  () => variableResolver.resolveVariableReferences(layer.props, effectiveVariables, variableValues),
39
58
  [layer.props, effectiveVariables, variableValues]
40
59
  );
41
- const childProps = React.useMemo(() => ({ ...resolvedProps }), [resolvedProps]);
42
60
  const childEditorConfig = React.useMemo(() => {
43
61
  return editorConfig ? { ...editorConfig, zIndex: editorConfig.zIndex + 1, parentUpdated: editorConfig.parentUpdated || !index(prevLayer.current, layer) } : void 0;
44
62
  }, [editorConfig, layer]);
63
+ const isBeingDragged = React.useMemo(
64
+ () => isLayerBeingDraggedOrDescendant(layer.id, dndContext.activeLayerId, pages),
65
+ [layer.id, dndContext.activeLayerId, pages]
66
+ );
67
+ const isRootDraggedLayer = layer.id === dndContext.activeLayerId;
45
68
  const canAcceptChildren = React.useMemo(() => layerUtils.canLayerAcceptChildren(layer, registry), [layer, registry]);
46
69
  const showDropZones = React.useMemo(
47
- () => editorConfig && dndContext.isDragging && canAcceptChildren,
48
- [editorConfig, dndContext.isDragging, canAcceptChildren]
70
+ () => editorConfig && dndContext.isDragging && canAcceptChildren && !isBeingDragged,
71
+ [editorConfig, dndContext.isDragging, canAcceptChildren, isBeingDragged]
49
72
  );
73
+ const childProps = React.useMemo(() => {
74
+ const props = { ...resolvedProps };
75
+ if (showDropZones && layerUtils.hasLayerChildren(layer)) {
76
+ const existingClassName = props.className || "";
77
+ if (!hasPositionClass(existingClassName)) {
78
+ props.className = existingClassName ? `${existingClassName} relative` : "relative";
79
+ }
80
+ }
81
+ return props;
82
+ }, [resolvedProps, showDropZones, layer]);
50
83
  if (!componentDefinition) {
51
84
  console.error(
52
85
  `[UIBuilder] Component definition not found in registry:`,
@@ -75,7 +108,7 @@ const RenderLayer = React.memo(
75
108
  child.id
76
109
  );
77
110
  if (showDropZones) {
78
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
111
+ return /* @__PURE__ */ jsxRuntime.jsxs(React.Fragment, { children: [
79
112
  /* @__PURE__ */ jsxRuntime.jsx(
80
113
  dropZone.DropPlaceholder,
81
114
  {
@@ -90,21 +123,30 @@ const RenderLayer = React.memo(
90
123
  return childElement;
91
124
  });
92
125
  if (showDropZones) {
93
- const lastDropZone = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
94
- dropZone.DropPlaceholder,
95
- {
96
- parentId: layer.id,
97
- position: layer.children.length,
98
- isActive: true
99
- }
100
- ) }, `drop-${layer.id}-${layer.children.length}`);
101
- childElements.push(lastDropZone);
126
+ childElements.push(
127
+ /* @__PURE__ */ jsxRuntime.jsx(
128
+ dropZone.DropPlaceholder,
129
+ {
130
+ parentId: layer.id,
131
+ position: layer.children.length,
132
+ isActive: true
133
+ },
134
+ `drop-${layer.id}-${layer.children.length}`
135
+ )
136
+ );
102
137
  }
103
138
  childProps.children = childElements;
139
+ } else if (types.isVariableReference(layer.children)) {
140
+ const resolvedChildren = variableResolver.resolveChildrenVariableReference(
141
+ layer.children,
142
+ effectiveVariables,
143
+ variableValues
144
+ );
145
+ childProps.children = resolvedChildren;
104
146
  } else if (typeof layer.children === "string") {
105
147
  childProps.children = layer.children;
106
148
  } else if (showDropZones && layerUtils.hasLayerChildren(layer)) {
107
- childProps.children = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative min-h-[2rem]", children: /* @__PURE__ */ jsxRuntime.jsx(
149
+ childProps.children = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-[2rem] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
108
150
  dropZone.DropPlaceholder,
109
151
  {
110
152
  parentId: layer.id,
@@ -114,8 +156,19 @@ const RenderLayer = React.memo(
114
156
  ) });
115
157
  }
116
158
  const WrappedComponent = isPrimitive ? /* @__PURE__ */ jsxRuntime.jsx(Component, { id: layer.id, "data-testid": layer.id, "data-layer-id": layer.id, ...childProps }) : /* @__PURE__ */ jsxRuntime.jsx(ErrorSuspenseWrapper, { id: layer.id, children: /* @__PURE__ */ jsxRuntime.jsx(Component, { "data-testid": layer.id, "data-layer-id": layer.id, ...childProps }) }, layer.id);
159
+ const DragFeedbackWrapper = isRootDraggedLayer ? /* @__PURE__ */ jsxRuntime.jsxs(
160
+ "div",
161
+ {
162
+ className: "relative opacity-50 pointer-events-none",
163
+ "data-dragging": "true",
164
+ children: [
165
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-primary/20 rounded pointer-events-none z-10" }),
166
+ WrappedComponent
167
+ ]
168
+ }
169
+ ) : WrappedComponent;
117
170
  if (!editorConfig) {
118
- return WrappedComponent;
171
+ return DragFeedbackWrapper;
119
172
  } else {
120
173
  const {
121
174
  zIndex,
@@ -141,7 +194,7 @@ const RenderLayer = React.memo(
141
194
  totalLayers,
142
195
  onDuplicateLayer: handleDuplicateLayer,
143
196
  onDeleteLayer: handleDeleteLayer,
144
- children: WrappedComponent
197
+ children: DragFeedbackWrapper
145
198
  },
146
199
  layer.id
147
200
  )
@@ -171,3 +224,4 @@ const ErrorSuspenseWrapper = ({ children }) => {
171
224
  const LoadingComponent = () => /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Loading..." });
172
225
 
173
226
  exports.RenderLayer = RenderLayer;
227
+ exports.hasPositionClass = hasPositionClass;
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { memo, useRef, useMemo, Suspense } from 'react';
2
+ import { memo, useRef, useMemo, Fragment, Suspense } from 'react';
3
3
  import isDeepEqual from '../../../../../../../_virtual/index.mjs';
4
4
  import { ElementSelector } from '../components/element-selector.mjs';
5
5
  import { DropPlaceholder } from '../dnd/drop-zone.mjs';
@@ -8,17 +8,36 @@ import '../../../../lib/ui-builder/context/dnd-context-colission-utils.mjs';
8
8
  import { useDndContext } from '../../../../lib/ui-builder/context/dnd-contexts.mjs';
9
9
  import 'react-dom';
10
10
  import { useLayerStore } from '../../../../lib/ui-builder/store/layer-store.mjs';
11
- import { canLayerAcceptChildren, hasLayerChildren } from '../../../../lib/ui-builder/store/layer-utils.mjs';
11
+ import { canLayerAcceptChildren, hasLayerChildren, findLayerRecursive } from '../../../../lib/ui-builder/store/layer-utils.mjs';
12
12
  import { useEditorStore } from '../../../../lib/ui-builder/store/editor-store.mjs';
13
13
  import { ErrorBoundary } from 'react-error-boundary';
14
14
  import { ErrorFallback } from '../components/error-fallback.mjs';
15
15
  import { isPrimitiveComponent } from '../../../../lib/ui-builder/store/editor-utils.mjs';
16
16
  import { DevProfiler } from '../components/dev-profiler.mjs';
17
- import { resolveVariableReferences } from '../../../../lib/ui-builder/utils/variable-resolver.mjs';
17
+ import { isVariableReference } from '../../types.mjs';
18
+ import { resolveVariableReferences, resolveChildrenVariableReference } from '../../../../lib/ui-builder/utils/variable-resolver.mjs';
18
19
 
20
+ const POSITION_CLASSES = ["static", "fixed", "absolute", "relative", "sticky"];
21
+ function hasPositionClass(className) {
22
+ const classes = className.split(/\s+/);
23
+ return POSITION_CLASSES.some((posClass) => classes.includes(posClass));
24
+ }
25
+ function isLayerBeingDraggedOrDescendant(layerId, activeLayerId, pages) {
26
+ if (!activeLayerId) return false;
27
+ if (layerId === activeLayerId) return true;
28
+ if (!pages || !Array.isArray(pages) || pages.length === 0) return false;
29
+ const draggedLayer = findLayerRecursive(pages, activeLayerId);
30
+ if (!draggedLayer) return false;
31
+ if (hasLayerChildren(draggedLayer)) {
32
+ const foundInDragged = findLayerRecursive([draggedLayer], layerId);
33
+ return !!foundInDragged;
34
+ }
35
+ return false;
36
+ }
19
37
  const RenderLayer = memo(
20
38
  ({ layer, componentRegistry, editorConfig, variables, variableValues }) => {
21
39
  const storeVariables = useLayerStore((state) => state.variables);
40
+ const pages = useLayerStore((state) => state.pages);
22
41
  const isLayerAPage = useLayerStore((state) => state.isLayerAPage(layer.id));
23
42
  const registry = useEditorStore((state) => state.registry);
24
43
  const dndContext = useDndContext();
@@ -36,15 +55,29 @@ const RenderLayer = memo(
36
55
  () => resolveVariableReferences(layer.props, effectiveVariables, variableValues),
37
56
  [layer.props, effectiveVariables, variableValues]
38
57
  );
39
- const childProps = useMemo(() => ({ ...resolvedProps }), [resolvedProps]);
40
58
  const childEditorConfig = useMemo(() => {
41
59
  return editorConfig ? { ...editorConfig, zIndex: editorConfig.zIndex + 1, parentUpdated: editorConfig.parentUpdated || !isDeepEqual(prevLayer.current, layer) } : void 0;
42
60
  }, [editorConfig, layer]);
61
+ const isBeingDragged = useMemo(
62
+ () => isLayerBeingDraggedOrDescendant(layer.id, dndContext.activeLayerId, pages),
63
+ [layer.id, dndContext.activeLayerId, pages]
64
+ );
65
+ const isRootDraggedLayer = layer.id === dndContext.activeLayerId;
43
66
  const canAcceptChildren = useMemo(() => canLayerAcceptChildren(layer, registry), [layer, registry]);
44
67
  const showDropZones = useMemo(
45
- () => editorConfig && dndContext.isDragging && canAcceptChildren,
46
- [editorConfig, dndContext.isDragging, canAcceptChildren]
68
+ () => editorConfig && dndContext.isDragging && canAcceptChildren && !isBeingDragged,
69
+ [editorConfig, dndContext.isDragging, canAcceptChildren, isBeingDragged]
47
70
  );
71
+ const childProps = useMemo(() => {
72
+ const props = { ...resolvedProps };
73
+ if (showDropZones && hasLayerChildren(layer)) {
74
+ const existingClassName = props.className || "";
75
+ if (!hasPositionClass(existingClassName)) {
76
+ props.className = existingClassName ? `${existingClassName} relative` : "relative";
77
+ }
78
+ }
79
+ return props;
80
+ }, [resolvedProps, showDropZones, layer]);
48
81
  if (!componentDefinition) {
49
82
  console.error(
50
83
  `[UIBuilder] Component definition not found in registry:`,
@@ -73,7 +106,7 @@ const RenderLayer = memo(
73
106
  child.id
74
107
  );
75
108
  if (showDropZones) {
76
- return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
109
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
77
110
  /* @__PURE__ */ jsx(
78
111
  DropPlaceholder,
79
112
  {
@@ -88,21 +121,30 @@ const RenderLayer = memo(
88
121
  return childElement;
89
122
  });
90
123
  if (showDropZones) {
91
- const lastDropZone = /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
92
- DropPlaceholder,
93
- {
94
- parentId: layer.id,
95
- position: layer.children.length,
96
- isActive: true
97
- }
98
- ) }, `drop-${layer.id}-${layer.children.length}`);
99
- childElements.push(lastDropZone);
124
+ childElements.push(
125
+ /* @__PURE__ */ jsx(
126
+ DropPlaceholder,
127
+ {
128
+ parentId: layer.id,
129
+ position: layer.children.length,
130
+ isActive: true
131
+ },
132
+ `drop-${layer.id}-${layer.children.length}`
133
+ )
134
+ );
100
135
  }
101
136
  childProps.children = childElements;
137
+ } else if (isVariableReference(layer.children)) {
138
+ const resolvedChildren = resolveChildrenVariableReference(
139
+ layer.children,
140
+ effectiveVariables,
141
+ variableValues
142
+ );
143
+ childProps.children = resolvedChildren;
102
144
  } else if (typeof layer.children === "string") {
103
145
  childProps.children = layer.children;
104
146
  } else if (showDropZones && hasLayerChildren(layer)) {
105
- childProps.children = /* @__PURE__ */ jsx("div", { className: "relative min-h-[2rem]", children: /* @__PURE__ */ jsx(
147
+ childProps.children = /* @__PURE__ */ jsx("div", { className: "min-h-[2rem] w-full", children: /* @__PURE__ */ jsx(
106
148
  DropPlaceholder,
107
149
  {
108
150
  parentId: layer.id,
@@ -112,8 +154,19 @@ const RenderLayer = memo(
112
154
  ) });
113
155
  }
114
156
  const WrappedComponent = isPrimitive ? /* @__PURE__ */ jsx(Component, { id: layer.id, "data-testid": layer.id, "data-layer-id": layer.id, ...childProps }) : /* @__PURE__ */ jsx(ErrorSuspenseWrapper, { id: layer.id, children: /* @__PURE__ */ jsx(Component, { "data-testid": layer.id, "data-layer-id": layer.id, ...childProps }) }, layer.id);
157
+ const DragFeedbackWrapper = isRootDraggedLayer ? /* @__PURE__ */ jsxs(
158
+ "div",
159
+ {
160
+ className: "relative opacity-50 pointer-events-none",
161
+ "data-dragging": "true",
162
+ children: [
163
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-primary/20 rounded pointer-events-none z-10" }),
164
+ WrappedComponent
165
+ ]
166
+ }
167
+ ) : WrappedComponent;
115
168
  if (!editorConfig) {
116
- return WrappedComponent;
169
+ return DragFeedbackWrapper;
117
170
  } else {
118
171
  const {
119
172
  zIndex,
@@ -139,7 +192,7 @@ const RenderLayer = memo(
139
192
  totalLayers,
140
193
  onDuplicateLayer: handleDuplicateLayer,
141
194
  onDeleteLayer: handleDeleteLayer,
142
- children: WrappedComponent
195
+ children: DragFeedbackWrapper
143
196
  },
144
197
  layer.id
145
198
  )
@@ -168,4 +221,4 @@ const ErrorSuspenseWrapper = ({ children }) => {
168
221
  };
169
222
  const LoadingComponent = () => /* @__PURE__ */ jsx("div", { children: "Loading..." });
170
223
 
171
- export { RenderLayer };
224
+ export { RenderLayer, hasPositionClass };
@@ -138,17 +138,22 @@ const getTransformState = () => {
138
138
  const transform = computedStyle.transform;
139
139
  if (transform && transform !== "none") {
140
140
  const matrixMatch = transform.match(/matrix\(([^)]*)\)/);
141
- if (matrixMatch) {
141
+ if (matrixMatch && matrixMatch[1]) {
142
142
  const values = matrixMatch[1].split(",").map((v) => parseFloat(v.trim()));
143
143
  if (values.length >= 6) {
144
- transformState = {
145
- scale: values[0],
146
- // scaleX
147
- positionX: values[4],
148
- // translateX
149
- positionY: values[5]
150
- // translateY
151
- };
144
+ const scale = values[0];
145
+ const positionX = values[4];
146
+ const positionY = values[5];
147
+ if (scale !== void 0 && positionX !== void 0 && positionY !== void 0) {
148
+ transformState = {
149
+ scale,
150
+ // scaleX
151
+ positionX,
152
+ // translateX
153
+ positionY
154
+ // translateY
155
+ };
156
+ }
152
157
  }
153
158
  }
154
159
  }
@@ -136,17 +136,22 @@ const getTransformState = () => {
136
136
  const transform = computedStyle.transform;
137
137
  if (transform && transform !== "none") {
138
138
  const matrixMatch = transform.match(/matrix\(([^)]*)\)/);
139
- if (matrixMatch) {
139
+ if (matrixMatch && matrixMatch[1]) {
140
140
  const values = matrixMatch[1].split(",").map((v) => parseFloat(v.trim()));
141
141
  if (values.length >= 6) {
142
- transformState = {
143
- scale: values[0],
144
- // scaleX
145
- positionX: values[4],
146
- // translateX
147
- positionY: values[5]
148
- // translateY
149
- };
142
+ const scale = values[0];
143
+ const positionX = values[4];
144
+ const positionY = values[5];
145
+ if (scale !== void 0 && positionX !== void 0 && positionY !== void 0) {
146
+ transformState = {
147
+ scale,
148
+ // scaleX
149
+ positionX,
150
+ // translateX
151
+ positionY
152
+ // translateY
153
+ };
154
+ }
150
155
  }
151
156
  }
152
157
  }