@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.
- package/dist/packages/ui/src/components/minimal-tiptap/utils.cjs +15 -11
- package/dist/packages/ui/src/components/minimal-tiptap/utils.mjs +15 -11
- package/dist/packages/ui/src/components/ui-builder/index.cjs +9 -7
- package/dist/packages/ui/src/components/ui-builder/index.mjs +9 -7
- package/dist/packages/ui/src/components/ui-builder/internal/canvas/auto-frame.cjs +6 -3
- package/dist/packages/ui/src/components/ui-builder/internal/canvas/auto-frame.mjs +6 -3
- package/dist/packages/ui/src/components/ui-builder/internal/components/add-component-popover.cjs +228 -48
- package/dist/packages/ui/src/components/ui-builder/internal/components/add-component-popover.mjs +228 -48
- package/dist/packages/ui/src/components/ui-builder/internal/components/element-selector.cjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/components/element-selector.mjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/components/error-fallback.cjs +4 -2
- package/dist/packages/ui/src/components/ui-builder/internal/components/error-fallback.mjs +4 -2
- package/dist/packages/ui/src/components/ui-builder/internal/components/multi-select.cjs +6 -3
- package/dist/packages/ui/src/components/ui-builder/internal/components/multi-select.mjs +6 -3
- package/dist/packages/ui/src/components/ui-builder/internal/dnd/draggable-new-component.cjs +67 -0
- package/dist/packages/ui/src/components/ui-builder/internal/dnd/draggable-new-component.mjs +62 -0
- package/dist/packages/ui/src/components/ui-builder/internal/dnd/drop-zone.cjs +181 -37
- package/dist/packages/ui/src/components/ui-builder/internal/dnd/drop-zone.mjs +181 -38
- package/dist/packages/ui/src/components/ui-builder/internal/editor-panel.cjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/editor-panel.mjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-group-control.cjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-group-control.mjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-item-control.cjs +9 -2
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-item-control.mjs +9 -2
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/iconname-field.cjs +3 -2
- package/dist/packages/ui/src/components/ui-builder/internal/form-fields/iconname-field.mjs +3 -2
- package/dist/packages/ui/src/components/ui-builder/internal/layers-panel.cjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/layers-panel.mjs +1 -1
- package/dist/packages/ui/src/components/ui-builder/internal/props-panel.cjs +17 -5
- package/dist/packages/ui/src/components/ui-builder/internal/props-panel.mjs +17 -5
- package/dist/packages/ui/src/components/ui-builder/internal/utils/render-utils.cjs +70 -16
- package/dist/packages/ui/src/components/ui-builder/internal/utils/render-utils.mjs +73 -20
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-context-colission-utils.cjs +14 -9
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-context-colission-utils.mjs +14 -9
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-context.cjs +38 -10
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-context.mjs +35 -11
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-contexts.cjs +1 -0
- package/dist/packages/ui/src/lib/ui-builder/context/dnd-contexts.mjs +1 -0
- package/dist/packages/ui/src/lib/ui-builder/context/drag-overlay.cjs +7 -4
- package/dist/packages/ui/src/lib/ui-builder/context/drag-overlay.mjs +7 -4
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-auto-scroll.cjs +4 -4
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-auto-scroll.mjs +4 -4
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-dnd-event-handlers.cjs +53 -16
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-dnd-event-handlers.mjs +53 -16
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-drop-validation.cjs +23 -7
- package/dist/packages/ui/src/lib/ui-builder/hooks/use-drop-validation.mjs +23 -7
- package/dist/packages/ui/src/lib/ui-builder/registry/form-field-overrides.cjs +110 -11
- package/dist/packages/ui/src/lib/ui-builder/registry/form-field-overrides.mjs +111 -13
- package/dist/packages/ui/src/lib/ui-builder/store/editor-store.cjs +3 -2
- package/dist/packages/ui/src/lib/ui-builder/store/editor-store.mjs +3 -2
- package/dist/packages/ui/src/lib/ui-builder/store/layer-store.cjs +53 -7
- package/dist/packages/ui/src/lib/ui-builder/store/layer-store.mjs +54 -8
- package/dist/packages/ui/src/lib/ui-builder/store/layer-utils.cjs +4 -3
- package/dist/packages/ui/src/lib/ui-builder/store/layer-utils.mjs +4 -3
- package/dist/packages/ui/src/lib/ui-builder/utils/variable-resolver.cjs +12 -0
- package/dist/packages/ui/src/lib/ui-builder/utils/variable-resolver.mjs +12 -1
- package/dist/plugins/ui-builder/client/components/index.d.cts +1 -1
- package/dist/plugins/ui-builder/client/components/index.d.mts +1 -1
- package/dist/plugins/ui-builder/client/components/index.d.ts +1 -1
- package/dist/plugins/ui-builder/client/hooks/index.d.cts +2 -2
- package/dist/plugins/ui-builder/client/hooks/index.d.mts +2 -2
- package/dist/plugins/ui-builder/client/hooks/index.d.ts +2 -2
- package/dist/plugins/ui-builder/client/index.d.cts +17 -7
- package/dist/plugins/ui-builder/client/index.d.mts +17 -7
- package/dist/plugins/ui-builder/client/index.d.ts +17 -7
- package/dist/plugins/ui-builder/index.d.cts +2 -2
- package/dist/plugins/ui-builder/index.d.mts +2 -2
- package/dist/plugins/ui-builder/index.d.ts +2 -2
- package/dist/shared/{stack.BSM2cgoq.d.cts → stack.BYysGdHl.d.cts} +1 -1
- package/dist/shared/{stack.CqfZWfjJ.d.cts → stack.BdJFrdyt.d.cts} +8 -2
- package/dist/shared/{stack.e1FN86dE.d.mts → stack.ChVuHi5e.d.mts} +8 -2
- package/dist/shared/{stack.CLtOoAqF.d.mts → stack.DYCFcnkL.d.mts} +1 -1
- package/dist/shared/{stack.MMntCVZZ.d.ts → stack.EhM4pmtN.d.ts} +8 -2
- package/dist/shared/{stack.BD1m-4yB.d.ts → stack.kFbDspnF.d.ts} +1 -1
- 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/
|
|
12
|
-
require('
|
|
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('
|
|
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/
|
|
10
|
-
import '
|
|
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 '
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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]
|
|
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
|
|
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]
|
|
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:
|
|
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
|
-
|
|
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]
|
|
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,
|
|
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]
|
|
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
|
|
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]
|
|
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:
|
|
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
|
-
|
|
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]
|
|
358
|
+
const bindings = state.immutableBindings[layerId];
|
|
359
|
+
if (bindings) {
|
|
360
|
+
bindings[propName] = isImmutable;
|
|
361
|
+
}
|
|
316
362
|
}));
|
|
317
363
|
},
|
|
318
364
|
moveLayer: (sourceLayerId, targetParentId, targetPosition) => {
|