@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
@@ -6,16 +6,20 @@ const childrenSearchableSelect = require('../../../components/ui-builder/interna
6
6
  const iconnameField = require('../../../components/ui-builder/internal/form-fields/iconname-field.cjs');
7
7
  const textarea = require('../../../components/textarea.cjs');
8
8
  const minimalTiptap = require('../../../components/minimal-tiptap/minimal-tiptap.cjs');
9
+ const tooltip = require('../../../components/tooltip.cjs');
10
+ const layerStore = require('../store/layer-store.cjs');
11
+ require('react');
12
+ const types = require('../../../components/ui-builder/types.cjs');
13
+ const LucideIcons = require('lucide-react');
14
+ const button = require('../../../components/button.cjs');
15
+ const dropdownMenu = require('../../../components/dropdown-menu.cjs');
9
16
  require('clsx');
10
17
  require('tailwind-merge');
11
- require('../store/layer-store.cjs');
12
- require('react');
13
- require('lucide-react');
14
- require('../../../components/button.cjs');
15
- require('../store/editor-store.cjs');
18
+ const editorStore = require('../store/editor-store.cjs');
19
+ const card = require('../../../components/card.cjs');
16
20
  const breakpointClassnameControl = require('../../../components/ui-builder/internal/form-fields/classname-control/breakpoint-classname-control.cjs');
17
- require('@radix-ui/react-label');
18
- require('../../../components/badge.cjs');
21
+ const label = require('../../../components/label.cjs');
22
+ const badge = require('../../../components/badge.cjs');
19
23
 
20
24
  const classNameFieldOverrides = (layer) => {
21
25
  return {
@@ -86,8 +90,9 @@ const iconNameFieldOverrides = (layer) => {
86
90
  )
87
91
  };
88
92
  };
89
- const childrenAsTextareaFieldOverrides = (layer) => {
93
+ const childrenAsTextareaFieldOverrides = (layer, allowVariableBinding = false) => {
90
94
  return {
95
+ renderParent: allowVariableBinding ? ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(ChildrenVariableBindingWrapper, { children }) : void 0,
91
96
  fieldType: ({
92
97
  label,
93
98
  isRequired,
@@ -103,7 +108,7 @@ const childrenAsTextareaFieldOverrides = (layer) => {
103
108
  children: /* @__PURE__ */ jsxRuntime.jsx(
104
109
  textarea.Textarea,
105
110
  {
106
- value: layer.children,
111
+ value: typeof layer.children === "string" ? layer.children : "",
107
112
  onChange: field.onChange,
108
113
  ...fieldProps
109
114
  }
@@ -112,8 +117,9 @@ const childrenAsTextareaFieldOverrides = (layer) => {
112
117
  )
113
118
  };
114
119
  };
115
- const childrenAsTipTapFieldOverrides = (layer) => {
120
+ const childrenAsTipTapFieldOverrides = (layer, allowVariableBinding = false) => {
116
121
  return {
122
+ renderParent: allowVariableBinding ? ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(ChildrenVariableBindingWrapper, { children }) : void 0,
117
123
  fieldType: ({
118
124
  label,
119
125
  isRequired,
@@ -132,7 +138,7 @@ const childrenAsTipTapFieldOverrides = (layer) => {
132
138
  immediatelyRender: false,
133
139
  output: "markdown",
134
140
  editable: true,
135
- value: layer.children,
141
+ value: typeof layer.children === "string" ? layer.children : "",
136
142
  editorClassName: "focus:outline-none px-4 py-2 h-full",
137
143
  onChange: (content) => {
138
144
  if (typeof content === "string") {
@@ -175,7 +181,100 @@ function FormFieldWrapper({
175
181
  fieldConfigItem?.description && /* @__PURE__ */ jsxRuntime.jsx(form.FormDescription, { children: fieldConfigItem.description })
176
182
  ] });
177
183
  }
184
+ function ChildrenVariableBindingWrapper({
185
+ children
186
+ }) {
187
+ const variables = layerStore.useLayerStore((state) => state.variables);
188
+ const selectedLayerId = layerStore.useLayerStore((state) => state.selectedLayerId);
189
+ const findLayerById = layerStore.useLayerStore((state) => state.findLayerById);
190
+ const isChildrenBindingImmutable = layerStore.useLayerStore((state) => state.isChildrenBindingImmutable);
191
+ const incrementRevision = editorStore.useEditorStore((state) => state.incrementRevision);
192
+ const unbindChildrenFromVariable = layerStore.useLayerStore(
193
+ (state) => state.unbindChildrenFromVariable
194
+ );
195
+ const bindChildrenToVariable = layerStore.useLayerStore((state) => state.bindChildrenToVariable);
196
+ const selectedLayer = findLayerById(selectedLayerId);
197
+ if (!selectedLayer) {
198
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
199
+ }
200
+ const currentValue = selectedLayer.children;
201
+ const isCurrentlyBound = types.isVariableReference(currentValue);
202
+ const boundVariable = isCurrentlyBound ? variables.find((v) => v.id === currentValue.__variableRef) : null;
203
+ const isImmutable = isChildrenBindingImmutable(selectedLayer.id);
204
+ const handleBindToVariable = (variableId) => {
205
+ bindChildrenToVariable(selectedLayer.id, variableId);
206
+ incrementRevision();
207
+ };
208
+ const handleUnbind = () => {
209
+ unbindChildrenFromVariable(selectedLayer.id);
210
+ incrementRevision();
211
+ };
212
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full gap-2 items-end", children: isCurrentlyBound && boundVariable ? (
213
+ // Bound state - show variable info and unbind button
214
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
215
+ /* @__PURE__ */ jsxRuntime.jsx(label.Label, { children: "Children" }),
216
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2 w-full", children: [
217
+ /* @__PURE__ */ jsxRuntime.jsx(card.Card, { className: "w-full", children: /* @__PURE__ */ jsxRuntime.jsx(card.CardContent, { className: "py-1 px-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 w-full", children: [
218
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Link, { className: "h-4 w-4 flex-shrink-0" }),
219
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
220
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 w-full", children: [
221
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: boundVariable.name }),
222
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-1.5 py-0.5 bg-muted rounded text-xs font-mono", children: boundVariable.type }),
223
+ isImmutable && /* @__PURE__ */ jsxRuntime.jsx(badge.Badge, { "data-testid": "immutable-children-badge", className: "rounded", children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.LockKeyhole, { strokeWidth: 3, className: "w-3 h-3" }) })
224
+ ] }),
225
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground truncate", children: String(boundVariable.defaultValue) })
226
+ ] })
227
+ ] }) }) }),
228
+ !isImmutable && /* @__PURE__ */ jsxRuntime.jsxs(tooltip.Tooltip, { children: [
229
+ /* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
230
+ button.Button,
231
+ {
232
+ variant: "outline",
233
+ onClick: handleUnbind,
234
+ className: "px-3 h-10",
235
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Unlink, { className: "h-4 w-4" })
236
+ }
237
+ ) }),
238
+ /* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipContent, { children: "Unbind Variable" })
239
+ ] })
240
+ ] })
241
+ ] })
242
+ ) : (
243
+ // Unbound state - show normal field with bind button
244
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
245
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children }),
246
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(dropdownMenu.DropdownMenu, { children: [
247
+ /* @__PURE__ */ jsxRuntime.jsxs(tooltip.Tooltip, { children: [
248
+ /* @__PURE__ */ jsxRuntime.jsx(dropdownMenu.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "outline", size: "sm", className: "px-3 h-10", children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Link, { className: "h-4 w-4 my-1" }) }) }) }),
249
+ /* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipContent, { children: "Bind Children to Variable" })
250
+ ] }),
251
+ /* @__PURE__ */ jsxRuntime.jsxs(dropdownMenu.DropdownMenuContent, { align: "end", className: "w-56", children: [
252
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-1.5 text-xs font-medium text-muted-foreground border-b", children: "Bind Children to Variable" }),
253
+ variables.filter((v) => v.type === "string").length > 0 ? variables.filter((v) => v.type === "string").map((variable) => /* @__PURE__ */ jsxRuntime.jsx(
254
+ dropdownMenu.DropdownMenuItem,
255
+ {
256
+ onClick: () => handleBindToVariable(variable.id),
257
+ className: "flex flex-col items-start p-3",
258
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 w-full", children: [
259
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Link, { className: "h-4 w-4 flex-shrink-0" }),
260
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
261
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 ", children: [
262
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: variable.name }),
263
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-1.5 py-0.5 bg-muted rounded text-xs font-mono", children: variable.type })
264
+ ] }),
265
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground truncate", children: String(variable.defaultValue) })
266
+ ] })
267
+ ] })
268
+ },
269
+ variable.id
270
+ )) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-muted-foreground", children: "No string variables defined" })
271
+ ] })
272
+ ] }) })
273
+ ] })
274
+ ) });
275
+ }
178
276
 
277
+ exports.ChildrenVariableBindingWrapper = ChildrenVariableBindingWrapper;
179
278
  exports.FormFieldWrapper = FormFieldWrapper;
180
279
  exports.childrenAsTextareaFieldOverrides = childrenAsTextareaFieldOverrides;
181
280
  exports.childrenAsTipTapFieldOverrides = childrenAsTipTapFieldOverrides;
@@ -1,19 +1,23 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { FormItem, FormLabel, FormControl, FormDescription } from '../../../components/form.mjs';
3
3
  import { ChildrenSearchableSelect } from '../../../components/ui-builder/internal/form-fields/children-searchable-select.mjs';
4
4
  import IconNameField from '../../../components/ui-builder/internal/form-fields/iconname-field.mjs';
5
5
  import { Textarea } from '../../../components/textarea.mjs';
6
6
  import { MinimalTiptapEditor } from '../../../components/minimal-tiptap/minimal-tiptap.mjs';
7
+ import { Tooltip, TooltipTrigger, TooltipContent } from '../../../components/tooltip.mjs';
8
+ import { useLayerStore } from '../store/layer-store.mjs';
9
+ import 'react';
10
+ import { isVariableReference } from '../../../components/ui-builder/types.mjs';
11
+ import { Link, LockKeyhole, Unlink } from 'lucide-react';
12
+ import { Button } from '../../../components/button.mjs';
13
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '../../../components/dropdown-menu.mjs';
7
14
  import 'clsx';
8
15
  import 'tailwind-merge';
9
- import '../store/layer-store.mjs';
10
- import 'react';
11
- import 'lucide-react';
12
- import '../../../components/button.mjs';
13
- import '../store/editor-store.mjs';
16
+ import { useEditorStore } from '../store/editor-store.mjs';
17
+ import { Card, CardContent } from '../../../components/card.mjs';
14
18
  import { BreakpointClassNameControl } from '../../../components/ui-builder/internal/form-fields/classname-control/breakpoint-classname-control.mjs';
15
- import '@radix-ui/react-label';
16
- import '../../../components/badge.mjs';
19
+ import { Label } from '../../../components/label.mjs';
20
+ import { Badge } from '../../../components/badge.mjs';
17
21
 
18
22
  const classNameFieldOverrides = (layer) => {
19
23
  return {
@@ -84,8 +88,9 @@ const iconNameFieldOverrides = (layer) => {
84
88
  )
85
89
  };
86
90
  };
87
- const childrenAsTextareaFieldOverrides = (layer) => {
91
+ const childrenAsTextareaFieldOverrides = (layer, allowVariableBinding = false) => {
88
92
  return {
93
+ renderParent: allowVariableBinding ? ({ children }) => /* @__PURE__ */ jsx(ChildrenVariableBindingWrapper, { children }) : void 0,
89
94
  fieldType: ({
90
95
  label,
91
96
  isRequired,
@@ -101,7 +106,7 @@ const childrenAsTextareaFieldOverrides = (layer) => {
101
106
  children: /* @__PURE__ */ jsx(
102
107
  Textarea,
103
108
  {
104
- value: layer.children,
109
+ value: typeof layer.children === "string" ? layer.children : "",
105
110
  onChange: field.onChange,
106
111
  ...fieldProps
107
112
  }
@@ -110,8 +115,9 @@ const childrenAsTextareaFieldOverrides = (layer) => {
110
115
  )
111
116
  };
112
117
  };
113
- const childrenAsTipTapFieldOverrides = (layer) => {
118
+ const childrenAsTipTapFieldOverrides = (layer, allowVariableBinding = false) => {
114
119
  return {
120
+ renderParent: allowVariableBinding ? ({ children }) => /* @__PURE__ */ jsx(ChildrenVariableBindingWrapper, { children }) : void 0,
115
121
  fieldType: ({
116
122
  label,
117
123
  isRequired,
@@ -130,7 +136,7 @@ const childrenAsTipTapFieldOverrides = (layer) => {
130
136
  immediatelyRender: false,
131
137
  output: "markdown",
132
138
  editable: true,
133
- value: layer.children,
139
+ value: typeof layer.children === "string" ? layer.children : "",
134
140
  editorClassName: "focus:outline-none px-4 py-2 h-full",
135
141
  onChange: (content) => {
136
142
  if (typeof content === "string") {
@@ -173,5 +179,97 @@ function FormFieldWrapper({
173
179
  fieldConfigItem?.description && /* @__PURE__ */ jsx(FormDescription, { children: fieldConfigItem.description })
174
180
  ] });
175
181
  }
182
+ function ChildrenVariableBindingWrapper({
183
+ children
184
+ }) {
185
+ const variables = useLayerStore((state) => state.variables);
186
+ const selectedLayerId = useLayerStore((state) => state.selectedLayerId);
187
+ const findLayerById = useLayerStore((state) => state.findLayerById);
188
+ const isChildrenBindingImmutable = useLayerStore((state) => state.isChildrenBindingImmutable);
189
+ const incrementRevision = useEditorStore((state) => state.incrementRevision);
190
+ const unbindChildrenFromVariable = useLayerStore(
191
+ (state) => state.unbindChildrenFromVariable
192
+ );
193
+ const bindChildrenToVariable = useLayerStore((state) => state.bindChildrenToVariable);
194
+ const selectedLayer = findLayerById(selectedLayerId);
195
+ if (!selectedLayer) {
196
+ return /* @__PURE__ */ jsx(Fragment, { children });
197
+ }
198
+ const currentValue = selectedLayer.children;
199
+ const isCurrentlyBound = isVariableReference(currentValue);
200
+ const boundVariable = isCurrentlyBound ? variables.find((v) => v.id === currentValue.__variableRef) : null;
201
+ const isImmutable = isChildrenBindingImmutable(selectedLayer.id);
202
+ const handleBindToVariable = (variableId) => {
203
+ bindChildrenToVariable(selectedLayer.id, variableId);
204
+ incrementRevision();
205
+ };
206
+ const handleUnbind = () => {
207
+ unbindChildrenFromVariable(selectedLayer.id);
208
+ incrementRevision();
209
+ };
210
+ return /* @__PURE__ */ jsx("div", { className: "flex w-full gap-2 items-end", children: isCurrentlyBound && boundVariable ? (
211
+ // Bound state - show variable info and unbind button
212
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
213
+ /* @__PURE__ */ jsx(Label, { children: "Children" }),
214
+ /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2 w-full", children: [
215
+ /* @__PURE__ */ jsx(Card, { className: "w-full", children: /* @__PURE__ */ jsx(CardContent, { className: "py-1 px-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 w-full", children: [
216
+ /* @__PURE__ */ jsx(Link, { className: "h-4 w-4 flex-shrink-0" }),
217
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
218
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 w-full", children: [
219
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: boundVariable.name }),
220
+ /* @__PURE__ */ jsx("span", { className: "px-1.5 py-0.5 bg-muted rounded text-xs font-mono", children: boundVariable.type }),
221
+ isImmutable && /* @__PURE__ */ jsx(Badge, { "data-testid": "immutable-children-badge", className: "rounded", children: /* @__PURE__ */ jsx(LockKeyhole, { strokeWidth: 3, className: "w-3 h-3" }) })
222
+ ] }),
223
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground truncate", children: String(boundVariable.defaultValue) })
224
+ ] })
225
+ ] }) }) }),
226
+ !isImmutable && /* @__PURE__ */ jsxs(Tooltip, { children: [
227
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
228
+ Button,
229
+ {
230
+ variant: "outline",
231
+ onClick: handleUnbind,
232
+ className: "px-3 h-10",
233
+ children: /* @__PURE__ */ jsx(Unlink, { className: "h-4 w-4" })
234
+ }
235
+ ) }),
236
+ /* @__PURE__ */ jsx(TooltipContent, { children: "Unbind Variable" })
237
+ ] })
238
+ ] })
239
+ ] })
240
+ ) : (
241
+ // Unbound state - show normal field with bind button
242
+ /* @__PURE__ */ jsxs(Fragment, { children: [
243
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children }),
244
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
245
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
246
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "px-3 h-10", children: /* @__PURE__ */ jsx(Link, { className: "h-4 w-4 my-1" }) }) }) }),
247
+ /* @__PURE__ */ jsx(TooltipContent, { children: "Bind Children to Variable" })
248
+ ] }),
249
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
250
+ /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs font-medium text-muted-foreground border-b", children: "Bind Children to Variable" }),
251
+ variables.filter((v) => v.type === "string").length > 0 ? variables.filter((v) => v.type === "string").map((variable) => /* @__PURE__ */ jsx(
252
+ DropdownMenuItem,
253
+ {
254
+ onClick: () => handleBindToVariable(variable.id),
255
+ className: "flex flex-col items-start p-3",
256
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 w-full", children: [
257
+ /* @__PURE__ */ jsx(Link, { className: "h-4 w-4 flex-shrink-0" }),
258
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
259
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 ", children: [
260
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: variable.name }),
261
+ /* @__PURE__ */ jsx("span", { className: "px-1.5 py-0.5 bg-muted rounded text-xs font-mono", children: variable.type })
262
+ ] }),
263
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground truncate", children: String(variable.defaultValue) })
264
+ ] })
265
+ ] })
266
+ },
267
+ variable.id
268
+ )) : /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-muted-foreground", children: "No string variables defined" })
269
+ ] })
270
+ ] }) })
271
+ ] })
272
+ ) });
273
+ }
176
274
 
177
- export { FormFieldWrapper, childrenAsTextareaFieldOverrides, childrenAsTipTapFieldOverrides, childrenFieldOverrides, classNameFieldOverrides, commonFieldOverrides, iconNameFieldOverrides };
275
+ export { ChildrenVariableBindingWrapper, FormFieldWrapper, childrenAsTextareaFieldOverrides, childrenAsTipTapFieldOverrides, childrenFieldOverrides, classNameFieldOverrides, commonFieldOverrides, iconNameFieldOverrides };
@@ -6,8 +6,9 @@ const store = (set, get) => ({
6
6
  previewMode: "responsive",
7
7
  setPreviewMode: (mode) => set({ previewMode: mode }),
8
8
  registry: {},
9
- initialize: (registry, persistLayerStoreConfig, allowPagesCreation, allowPagesDeletion, allowVariableEditing) => {
10
- set((state) => ({ ...state, registry, persistLayerStoreConfig, allowPagesCreation, allowPagesDeletion, allowVariableEditing }));
9
+ blocks: void 0,
10
+ initialize: (registry, persistLayerStoreConfig, allowPagesCreation, allowPagesDeletion, allowVariableEditing, blocks) => {
11
+ set((state) => ({ ...state, registry, persistLayerStoreConfig, allowPagesCreation, allowPagesDeletion, allowVariableEditing, blocks }));
11
12
  },
12
13
  getComponentDefinition: (type) => {
13
14
  const { registry } = get();
@@ -4,8 +4,9 @@ const store = (set, get) => ({
4
4
  previewMode: "responsive",
5
5
  setPreviewMode: (mode) => set({ previewMode: mode }),
6
6
  registry: {},
7
- initialize: (registry, persistLayerStoreConfig, allowPagesCreation, allowPagesDeletion, allowVariableEditing) => {
8
- set((state) => ({ ...state, registry, persistLayerStoreConfig, allowPagesCreation, allowPagesDeletion, allowVariableEditing }));
7
+ blocks: void 0,
8
+ initialize: (registry, persistLayerStoreConfig, allowPagesCreation, allowPagesDeletion, allowVariableEditing, blocks) => {
9
+ set((state) => ({ ...state, registry, persistLayerStoreConfig, allowPagesCreation, allowPagesDeletion, allowVariableEditing, blocks }));
9
10
  },
10
11
  getComponentDefinition: (type) => {
11
12
  const { registry } = get();
@@ -33,7 +33,7 @@ const store = (set, get) => ({
33
33
  initialize: (pages, selectedPageId, selectedLayerId, variables) => {
34
34
  set(immer.produce((state) => {
35
35
  state.pages = pages;
36
- state.selectedPageId = selectedPageId || (pages.length > 0 ? pages[0].id : "");
36
+ state.selectedPageId = selectedPageId || (pages.length > 0 && pages[0] ? pages[0].id : "");
37
37
  state.selectedLayerId = selectedLayerId || null;
38
38
  state.variables = variables || [];
39
39
  const { registry } = editorStore.useEditorStore.getState();
@@ -47,7 +47,10 @@ const store = (set, get) => ({
47
47
  if (!state.immutableBindings[layer.id]) {
48
48
  state.immutableBindings[layer.id] = {};
49
49
  }
50
- state.immutableBindings[layer.id][binding.propName] = true;
50
+ const bindings = state.immutableBindings[layer.id];
51
+ if (bindings) {
52
+ bindings[binding.propName] = true;
53
+ }
51
54
  }
52
55
  }
53
56
  }
@@ -86,20 +89,29 @@ const store = (set, get) => ({
86
89
  applyVariableBindings: true,
87
90
  variables: state.variables
88
91
  });
89
- const defaultVariableBindings = registry[layerType].defaultVariableBindings || [];
92
+ const registryEntry = registry[layerType];
93
+ const defaultVariableBindings = registryEntry?.defaultVariableBindings || [];
90
94
  for (const binding of defaultVariableBindings) {
91
95
  const variable = state.variables.find((v) => v.id === binding.variableId);
92
96
  if (variable && binding.immutable) {
93
97
  if (!state.immutableBindings[newLayer.id]) {
94
98
  state.immutableBindings[newLayer.id] = {};
95
99
  }
96
- state.immutableBindings[newLayer.id][binding.propName] = true;
100
+ const bindings = state.immutableBindings[newLayer.id];
101
+ if (bindings) {
102
+ bindings[binding.propName] = true;
103
+ }
97
104
  }
98
105
  }
99
106
  const updatedPages = layerUtils.addLayer(state.pages, newLayer, parentId, parentPosition);
100
107
  state.pages = updatedPages;
101
108
  state.selectedLayerId = newLayer.id;
102
109
  })),
110
+ addLayerDirect: (layer, parentId, parentPosition) => set(immer.produce((state) => {
111
+ const updatedPages = layerUtils.addLayer(state.pages, layer, parentId, parentPosition);
112
+ state.pages = updatedPages;
113
+ state.selectedLayerId = layer.id;
114
+ })),
103
115
  addPageLayer: (pageName) => set(immer.produce((state) => {
104
116
  const newPage = {
105
117
  id: layerUtils.createId(),
@@ -155,10 +167,11 @@ const store = (set, get) => ({
155
167
  const isPage = state.pages.some((page) => page.id === layerId);
156
168
  if (isPage && pages.length > 1) {
157
169
  const newPages = state.pages.filter((page) => page.id !== layerId);
170
+ const firstPage = newPages[0];
158
171
  return {
159
172
  ...state,
160
173
  pages: newPages,
161
- selectedPageId: newPages[0].id
174
+ selectedPageId: firstPage ? firstPage.id : ""
162
175
  };
163
176
  }
164
177
  const updatedPages = pages.map(
@@ -256,6 +269,7 @@ const store = (set, get) => ({
256
269
  const cleanVariableReferences = (layer) => {
257
270
  const updatedProps = { ...layer.props };
258
271
  let hasChanges = false;
272
+ let updatedChildren = layer.children;
259
273
  Object.entries(updatedProps).forEach(([propName, propValue]) => {
260
274
  if (types.isVariableReference(propValue) && propValue.__variableRef === variableId) {
261
275
  const layerSchema = registry[layer.type]?.schema;
@@ -269,7 +283,14 @@ const store = (set, get) => ({
269
283
  }
270
284
  }
271
285
  });
272
- return hasChanges ? { ...layer, props: updatedProps } : layer;
286
+ if (types.isVariableReference(layer.children) && layer.children.__variableRef === variableId) {
287
+ updatedChildren = "";
288
+ hasChanges = true;
289
+ }
290
+ if (hasChanges) {
291
+ return { ...layer, props: updatedProps, children: updatedChildren };
292
+ }
293
+ return layer;
273
294
  };
274
295
  state.pages = state.pages.map(
275
296
  (page) => layerUtils.visitLayer(page, null, cleanVariableReferences)
@@ -302,18 +323,43 @@ const store = (set, get) => ({
302
323
  }
303
324
  get().updateLayer(layerId, { [propName]: defaultValue });
304
325
  },
326
+ // Bind layer children to a variable reference
327
+ bindChildrenToVariable: (layerId, variableId) => {
328
+ get().updateLayer(layerId, {}, { children: { __variableRef: variableId } });
329
+ },
330
+ // Unbind layer children from a variable reference and reset to empty string
331
+ unbindChildrenFromVariable: (layerId) => {
332
+ if (get().isChildrenBindingImmutable(layerId)) {
333
+ console.warn(`Cannot unbind immutable children variable binding on layer ${layerId}`);
334
+ return;
335
+ }
336
+ const layer = get().findLayerById(layerId);
337
+ if (!layer) {
338
+ console.warn(`Layer with ID ${layerId} not found.`);
339
+ return;
340
+ }
341
+ get().updateLayer(layerId, {}, { children: "" });
342
+ },
305
343
  // Check if a binding is immutable
306
344
  isBindingImmutable: (layerId, propName) => {
307
345
  const { immutableBindings } = get();
308
346
  return immutableBindings[layerId]?.[propName] === true;
309
347
  },
348
+ // Check if children binding is immutable (uses special key '__children__')
349
+ isChildrenBindingImmutable: (layerId) => {
350
+ const { immutableBindings } = get();
351
+ return immutableBindings[layerId]?.["__children__"] === true;
352
+ },
310
353
  // Test helper
311
354
  setImmutableBinding: (layerId, propName, isImmutable) => {
312
355
  set(immer.produce((state) => {
313
356
  if (!state.immutableBindings[layerId]) {
314
357
  state.immutableBindings[layerId] = {};
315
358
  }
316
- state.immutableBindings[layerId][propName] = isImmutable;
359
+ const bindings = state.immutableBindings[layerId];
360
+ if (bindings) {
361
+ bindings[propName] = isImmutable;
362
+ }
317
363
  }));
318
364
  },
319
365
  moveLayer: (sourceLayerId, targetParentId, targetPosition) => {
@@ -3,7 +3,7 @@ import { persist, createJSONStorage } from '../../../../../../node_modules/.pnpm
3
3
  import { produce } from '../../../../../../node_modules/.pnpm/immer@11.1.3/node_modules/immer/dist/immer.mjs';
4
4
  import { temporal } from '../../../../../../node_modules/.pnpm/zundo@2.3.0_zustand@4.5.5_@types_react@19.2.6_immer@11.1.3_react@19.2.0_/node_modules/zundo/dist/index.mjs';
5
5
  import isDeepEqual from '../../../../../../_virtual/index.mjs';
6
- import { migrateV1ToV2, migrateV2ToV3, migrateV5ToV6, moveLayer, visitLayer, createId, findLayerRecursive, createComponentLayer, addLayer, hasLayerChildren, duplicateWithNewIdsAndName } from './layer-utils.mjs';
6
+ import { migrateV1ToV2, migrateV2ToV3, migrateV5ToV6, moveLayer, visitLayer, createId, findLayerRecursive, addLayer, createComponentLayer, hasLayerChildren, duplicateWithNewIdsAndName } from './layer-utils.mjs';
7
7
  export { countLayers, findAllParentLayersRecursive } from './layer-utils.mjs';
8
8
  import { getDefaultProps } from './schema-utils.mjs';
9
9
  import { useEditorStore } from './editor-store.mjs';
@@ -32,7 +32,7 @@ const store = (set, get) => ({
32
32
  initialize: (pages, selectedPageId, selectedLayerId, variables) => {
33
33
  set(produce((state) => {
34
34
  state.pages = pages;
35
- state.selectedPageId = selectedPageId || (pages.length > 0 ? pages[0].id : "");
35
+ state.selectedPageId = selectedPageId || (pages.length > 0 && pages[0] ? pages[0].id : "");
36
36
  state.selectedLayerId = selectedLayerId || null;
37
37
  state.variables = variables || [];
38
38
  const { registry } = useEditorStore.getState();
@@ -46,7 +46,10 @@ const store = (set, get) => ({
46
46
  if (!state.immutableBindings[layer.id]) {
47
47
  state.immutableBindings[layer.id] = {};
48
48
  }
49
- state.immutableBindings[layer.id][binding.propName] = true;
49
+ const bindings = state.immutableBindings[layer.id];
50
+ if (bindings) {
51
+ bindings[binding.propName] = true;
52
+ }
50
53
  }
51
54
  }
52
55
  }
@@ -85,20 +88,29 @@ const store = (set, get) => ({
85
88
  applyVariableBindings: true,
86
89
  variables: state.variables
87
90
  });
88
- const defaultVariableBindings = registry[layerType].defaultVariableBindings || [];
91
+ const registryEntry = registry[layerType];
92
+ const defaultVariableBindings = registryEntry?.defaultVariableBindings || [];
89
93
  for (const binding of defaultVariableBindings) {
90
94
  const variable = state.variables.find((v) => v.id === binding.variableId);
91
95
  if (variable && binding.immutable) {
92
96
  if (!state.immutableBindings[newLayer.id]) {
93
97
  state.immutableBindings[newLayer.id] = {};
94
98
  }
95
- state.immutableBindings[newLayer.id][binding.propName] = true;
99
+ const bindings = state.immutableBindings[newLayer.id];
100
+ if (bindings) {
101
+ bindings[binding.propName] = true;
102
+ }
96
103
  }
97
104
  }
98
105
  const updatedPages = addLayer(state.pages, newLayer, parentId, parentPosition);
99
106
  state.pages = updatedPages;
100
107
  state.selectedLayerId = newLayer.id;
101
108
  })),
109
+ addLayerDirect: (layer, parentId, parentPosition) => set(produce((state) => {
110
+ const updatedPages = addLayer(state.pages, layer, parentId, parentPosition);
111
+ state.pages = updatedPages;
112
+ state.selectedLayerId = layer.id;
113
+ })),
102
114
  addPageLayer: (pageName) => set(produce((state) => {
103
115
  const newPage = {
104
116
  id: createId(),
@@ -154,10 +166,11 @@ const store = (set, get) => ({
154
166
  const isPage = state.pages.some((page) => page.id === layerId);
155
167
  if (isPage && pages.length > 1) {
156
168
  const newPages = state.pages.filter((page) => page.id !== layerId);
169
+ const firstPage = newPages[0];
157
170
  return {
158
171
  ...state,
159
172
  pages: newPages,
160
- selectedPageId: newPages[0].id
173
+ selectedPageId: firstPage ? firstPage.id : ""
161
174
  };
162
175
  }
163
176
  const updatedPages = pages.map(
@@ -255,6 +268,7 @@ const store = (set, get) => ({
255
268
  const cleanVariableReferences = (layer) => {
256
269
  const updatedProps = { ...layer.props };
257
270
  let hasChanges = false;
271
+ let updatedChildren = layer.children;
258
272
  Object.entries(updatedProps).forEach(([propName, propValue]) => {
259
273
  if (isVariableReference(propValue) && propValue.__variableRef === variableId) {
260
274
  const layerSchema = registry[layer.type]?.schema;
@@ -268,7 +282,14 @@ const store = (set, get) => ({
268
282
  }
269
283
  }
270
284
  });
271
- return hasChanges ? { ...layer, props: updatedProps } : layer;
285
+ if (isVariableReference(layer.children) && layer.children.__variableRef === variableId) {
286
+ updatedChildren = "";
287
+ hasChanges = true;
288
+ }
289
+ if (hasChanges) {
290
+ return { ...layer, props: updatedProps, children: updatedChildren };
291
+ }
292
+ return layer;
272
293
  };
273
294
  state.pages = state.pages.map(
274
295
  (page) => visitLayer(page, null, cleanVariableReferences)
@@ -301,18 +322,43 @@ const store = (set, get) => ({
301
322
  }
302
323
  get().updateLayer(layerId, { [propName]: defaultValue });
303
324
  },
325
+ // Bind layer children to a variable reference
326
+ bindChildrenToVariable: (layerId, variableId) => {
327
+ get().updateLayer(layerId, {}, { children: { __variableRef: variableId } });
328
+ },
329
+ // Unbind layer children from a variable reference and reset to empty string
330
+ unbindChildrenFromVariable: (layerId) => {
331
+ if (get().isChildrenBindingImmutable(layerId)) {
332
+ console.warn(`Cannot unbind immutable children variable binding on layer ${layerId}`);
333
+ return;
334
+ }
335
+ const layer = get().findLayerById(layerId);
336
+ if (!layer) {
337
+ console.warn(`Layer with ID ${layerId} not found.`);
338
+ return;
339
+ }
340
+ get().updateLayer(layerId, {}, { children: "" });
341
+ },
304
342
  // Check if a binding is immutable
305
343
  isBindingImmutable: (layerId, propName) => {
306
344
  const { immutableBindings } = get();
307
345
  return immutableBindings[layerId]?.[propName] === true;
308
346
  },
347
+ // Check if children binding is immutable (uses special key '__children__')
348
+ isChildrenBindingImmutable: (layerId) => {
349
+ const { immutableBindings } = get();
350
+ return immutableBindings[layerId]?.["__children__"] === true;
351
+ },
309
352
  // Test helper
310
353
  setImmutableBinding: (layerId, propName, isImmutable) => {
311
354
  set(produce((state) => {
312
355
  if (!state.immutableBindings[layerId]) {
313
356
  state.immutableBindings[layerId] = {};
314
357
  }
315
- state.immutableBindings[layerId][propName] = isImmutable;
358
+ const bindings = state.immutableBindings[layerId];
359
+ if (bindings) {
360
+ bindings[propName] = isImmutable;
361
+ }
316
362
  }));
317
363
  },
318
364
  moveLayer: (sourceLayerId, targetParentId, targetPosition) => {