@vectoriox/iox-builder 8.0.0 → 8.1.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.
@@ -1,10 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, InjectionToken, inject, NgZone, EventEmitter, createComponent, Output, Input, Inject, Directive, ChangeDetectionStrategy, Component, ViewChild, effect, Optional, ContentChildren, NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
2
+ import { Injectable, InjectionToken, Optional, Inject, inject, NgZone, EventEmitter, createComponent, Output, Input, Directive, ChangeDetectionStrategy, Component, ViewChild, effect, ContentChildren, NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
3
3
  import * as i2 from '@angular/common';
4
4
  import { DOCUMENT, CommonModule } from '@angular/common';
5
5
  import * as i16 from '@angular/forms';
6
6
  import { FormsModule } from '@angular/forms';
7
- import * as i4 from '@angular/cdk/drag-drop';
7
+ import * as i5 from '@angular/cdk/drag-drop';
8
8
  import { DragDropModule } from '@angular/cdk/drag-drop';
9
9
  import { AccordionModule } from 'primeng/accordion';
10
10
  import * as i17 from 'primeng/button';
@@ -784,6 +784,24 @@ function reissueIdentityTraits(node) {
784
784
  trait.default = `${prefix}-${freshId()}`;
785
785
  }
786
786
  }
787
+ /**
788
+ * A NEW node is born with its identity: an empty identity trait (a palette Form's `formId`) is issued,
789
+ * one already set (a preset's `contact-…`) is kept. A Form inserted from the palette used to keep
790
+ * `formId: ''` — its submissions posted to `forms//submit` and nothing was ever stored.
791
+ */
792
+ function issueIdentityTraits(root) {
793
+ for (const trait of (root.traits ?? [])) {
794
+ if (!trait?.name || !REGENERATED_TRAITS.has(trait.name))
795
+ continue;
796
+ if (trait.default === undefined || trait.default === '')
797
+ trait.default = `${IDENTITY_PREFIX[trait.name] ?? trait.name}-${freshId()}`;
798
+ }
799
+ for (const child of root.children ?? [])
800
+ if (child)
801
+ issueIdentityTraits(child);
802
+ }
803
+ /** The readable prefix a freshly issued identity carries (`form-a1b2c3`). */
804
+ const IDENTITY_PREFIX = { formId: 'form' };
787
805
  function regenerateNodeIds(root, opts = {}) {
788
806
  const idMap = new Map();
789
807
  const assign = (node) => {
@@ -1601,6 +1619,8 @@ const BUILDER_CHROME_EN = Object.freeze({
1601
1619
  'builder.chrome.save': 'Save',
1602
1620
  'builder.chrome.preview': 'Preview',
1603
1621
  'builder.chrome.addImage': 'Add image',
1622
+ 'builder.chrome.refuseNestedForm': 'A form can’t go inside another form. Place it outside the form.',
1623
+ 'builder.chrome.refuseStateBlock': 'Success and error messages belong to their form. They can’t be placed on their own.',
1604
1624
  });
1605
1625
  const CANVAS_EMPTY_IMAGE_LABEL_KEY = 'builder.chrome.addImage';
1606
1626
  const CANVAS_EMPTY_IMAGE_LABEL_EN = BUILDER_CHROME_EN[CANVAS_EMPTY_IMAGE_LABEL_KEY];
@@ -1651,6 +1671,7 @@ class ComponentRegistryService {
1651
1671
  if (!config)
1652
1672
  return null;
1653
1673
  wearDefaultStyles(config);
1674
+ issueIdentityTraits(config);
1654
1675
  return config;
1655
1676
  }
1656
1677
  /**
@@ -1921,6 +1942,93 @@ function resolveDragTarget(el, selectedElement, isDragHandle) {
1921
1942
  return (isDragHandle || isDescendantOfSelected) ? selectedElement : el;
1922
1943
  }
1923
1944
 
1945
+ /**
1946
+ * What may NOT be placed where — the ONE rule every insert path asks (drag, click-to-insert, quick-add,
1947
+ * the Form presets, paste, duplicate). Everything not refused here is accepted: any container-capable
1948
+ * node takes any child, a Form included.
1949
+ */
1950
+ var InsertRefusal;
1951
+ (function (InsertRefusal) {
1952
+ /** A form inside a form — fields belong to their NEAREST form (forms plan D1), and HTML forbids it. */
1953
+ InsertRefusal["NestedForm"] = "nestedForm";
1954
+ /** A Success / Error block is part of its form's state machine; it is never placed on its own. */
1955
+ InsertRefusal["StateBlockAlone"] = "stateBlockAlone";
1956
+ })(InsertRefusal || (InsertRefusal = {}));
1957
+ const FORM_STATE_BLOCK_TYPES = new Set(['FormSuccess', 'FormError']);
1958
+ /** The builder-chrome label that says each refusal on screen (English in `BUILDER_CHROME_EN`). */
1959
+ const INSERT_REFUSAL_LABEL_KEYS = Object.freeze({
1960
+ [InsertRefusal.NestedForm]: 'builder.chrome.refuseNestedForm',
1961
+ [InsertRefusal.StateBlockAlone]: 'builder.chrome.refuseStateBlock',
1962
+ });
1963
+ /** The refusal in the host's language (the library's English when no host resolver is provided). */
1964
+ function insertRefusalLabel(reason, labels = defaultBuilderLabels) {
1965
+ const key = INSERT_REFUSAL_LABEL_KEYS[reason];
1966
+ return labels(key, BUILDER_CHROME_EN[key]);
1967
+ }
1968
+ function containsType(node, type) {
1969
+ if (node.type === type)
1970
+ return true;
1971
+ return (node.children ?? []).some(child => containsType(child, type));
1972
+ }
1973
+ /**
1974
+ * Why `node` may not be inserted under `ancestors` (outermost first, the new parent last; empty = the page),
1975
+ * or null when it may. `node` is the whole subtree being placed — a Container holding a Form is refused
1976
+ * inside a Form just as a bare Form is. Reordering a node inside its CURRENT parent is not an insert and
1977
+ * never asks.
1978
+ */
1979
+ function insertRefusal(node, ancestors) {
1980
+ if (!node)
1981
+ return null;
1982
+ if (node.type && FORM_STATE_BLOCK_TYPES.has(node.type))
1983
+ return InsertRefusal.StateBlockAlone;
1984
+ if (ancestors.some(a => a?.type === FORM_TYPE) && containsType(node, FORM_TYPE))
1985
+ return InsertRefusal.NestedForm;
1986
+ return null;
1987
+ }
1988
+
1989
+ /**
1990
+ * ROOT ESCAPE — near the top or bottom edge of a ROOT-level container the drop goes to the PAGE, between
1991
+ * root containers, instead of inside the container. This file is the decision; DragEngineService only
1992
+ * measures and applies it.
1993
+ *
1994
+ * ⚠️ The bug this replaced (2026-10-19, "a Form cannot be inserted into a Section / Container / Linked
1995
+ * Container"): escape switched on when the pointer was in the edge band and the PREVIOUS target was the
1996
+ * same container — which is also true on the second sample after ENTERING through the band — and it only
1997
+ * switched off once the pointer LEFT the container. So approaching a root container from above or below
1998
+ * locked every drop inside it to the page, even at its centre; an empty (short) container was almost
1999
+ * always entered that way. Now escape needs a move OUTWARD — from the container's inner region to its
2000
+ * edge — and it ends as soon as the pointer is back in the inner region.
2001
+ */
2002
+ /** The edge band on a tall container, in screen px. */
2003
+ const ROOT_ESCAPE_PX = 14;
2004
+ var RootZone;
2005
+ (function (RootZone) {
2006
+ RootZone["Outside"] = "outside";
2007
+ RootZone["Edge"] = "edge";
2008
+ RootZone["Inner"] = "inner";
2009
+ })(RootZone || (RootZone = {}));
2010
+ /** The band shrinks on a short container (a quarter of its height), so its middle stays a drop target. */
2011
+ function rootEscapeBand(height) {
2012
+ return Math.min(ROOT_ESCAPE_PX, Math.max(0, height) / 4);
2013
+ }
2014
+ /** Where the pointer's y sits relative to a root container's vertical extent. */
2015
+ function rootZone(py, top, bottom) {
2016
+ if (py < top || py > bottom)
2017
+ return RootZone.Outside;
2018
+ const band = rootEscapeBand(bottom - top);
2019
+ return py <= top + band || py >= bottom - band ? RootZone.Edge : RootZone.Inner;
2020
+ }
2021
+ /**
2022
+ * Whether this sample escapes `rootId` to the page: the pointer is on its edge band AND it either was
2023
+ * already escaping it or has just moved out to the edge from its inner region. Entering from outside
2024
+ * through the band never escapes.
2025
+ */
2026
+ function escapesRoot(rootId, zone, memory) {
2027
+ if (zone !== RootZone.Edge)
2028
+ return false;
2029
+ return memory.escapingFor === rootId || memory.innerOf === rootId;
2030
+ }
2031
+
1924
2032
  /**
1925
2033
  * CSS container name of the builder canvas frame (and of the global-elements overlay). Breakpoint
1926
2034
  * rules are emitted as `@container iox-canvas (max-width: …)`: the frame's layout width IS the
@@ -2462,6 +2570,11 @@ const MOTION_EASE = 'cubic-bezier(0.22, 1, 0.36, 1)';
2462
2570
  // Lower = more lag/weight, higher = snappier. 0.35 catches up to the pointer
2463
2571
  // within ~5-6 frames (~80-100ms) — reads as "responsive but weighted".
2464
2572
  const GHOST_FOLLOW_SMOOTHING = 0.35;
2573
+ const TARGET_CHIP_BG = '#6366f1';
2574
+ // The admin chrome's danger fill (toolbar.component.scss's unpublish hover reads the same token).
2575
+ const REFUSED_CHIP_BG = 'var(--iox-danger-text, #8E211A)';
2576
+ /** On the drop-target overlay while the target refuses the dragged payload (styled by the host). */
2577
+ const DROP_REFUSED_CLASS = 'iox-drop-target-overlay--refused';
2465
2578
  function deepCloneNode(node) {
2466
2579
  const clone = JSON.parse(JSON.stringify(node));
2467
2580
  // Fresh ids + remap intra-subtree references (stateTriggers, interaction targets) so a cloned
@@ -2470,10 +2583,12 @@ function deepCloneNode(node) {
2470
2583
  return clone;
2471
2584
  }
2472
2585
  class DragEngineService {
2473
- constructor(registry, symbolRegistry, overlayService) {
2586
+ constructor(registry, symbolRegistry, overlayService, labels) {
2474
2587
  this.registry = registry;
2475
2588
  this.symbolRegistry = symbolRegistry;
2476
2589
  this.overlayService = overlayService;
2590
+ /** Every insert the builder refused, for the host to say on screen (a toast). */
2591
+ this.insertRefused$ = new Subject();
2477
2592
  // ── Drop-zone registry ────────────────────────────────────
2478
2593
  this.ids = [];
2479
2594
  this.elMap = new Map();
@@ -2524,9 +2639,13 @@ class DragEngineService {
2524
2639
  this._scale = 1;
2525
2640
  // ── Dropzone rect cache — snapshotted at drag start and on scroll ─────────
2526
2641
  this._dropzoneRectCache = new Map();
2527
- // ── Root-escape ───────────────────────────────────────────
2528
- this.ROOT_ESCAPE_PX = 14;
2642
+ // ── Root-escape (decision: utils/root-escape.util.ts) ─────
2529
2643
  this._rootEscapeActiveFor = null;
2644
+ /** The root container whose inner region held the previous sample. */
2645
+ this._innerRootId = null;
2646
+ // ── Drop refusal (utils/insert-rules.util.ts) ─────────────
2647
+ /** Why the current target would refuse the dragged payload; null = it accepts. */
2648
+ this._targetRefusal = null;
2530
2649
  // ─────────────────────────────────────────────────────────
2531
2650
  // Draggable registration
2532
2651
  // ─────────────────────────────────────────────────────────
@@ -2544,10 +2663,47 @@ class DragEngineService {
2544
2663
  this._overlay.style.top = this._ghostY + 'px';
2545
2664
  this._ghostRaf = requestAnimationFrame(this._ghostFollowStep);
2546
2665
  };
2666
+ this.labels = labels ?? defaultBuilderLabels;
2667
+ }
2668
+ /**
2669
+ * The ONE acceptance check for every insert path: null when `data` (a panel payload or a node) may go
2670
+ * under `ancestors`; otherwise the refusal, already announced on `insertRefused$`.
2671
+ */
2672
+ refuseInsert(data, ancestors) {
2673
+ const reason = insertRefusal(this.payloadNode(data), ancestors);
2674
+ if (reason)
2675
+ this.insertRefused$.next({ reason, message: insertRefusalLabel(reason, this.labels) });
2676
+ return reason;
2677
+ }
2678
+ /** The node a payload WOULD insert, without building it: a type string, a block/symbol, or a node. */
2679
+ payloadNode(data) {
2680
+ if (typeof data === 'string')
2681
+ return { type: data };
2682
+ if (!data || typeof data !== 'object')
2683
+ return null;
2684
+ const blk = data;
2685
+ const symbolNode = blk.symbolId ? this.symbolRegistry.getSymbol(blk.symbolId)?.node : null;
2686
+ return (symbolNode ?? blk.node ?? blk);
2687
+ }
2688
+ /** The nodes a dropzone sits in, outermost first, its own node last — read from the DOM, which is
2689
+ * where a dropzone id lives. Empty for the page itself. */
2690
+ dropzoneAncestors(id) {
2691
+ const out = [];
2692
+ for (let el = this._getEl(id); el && el.id !== 'canvas-preview'; el = el.parentElement) {
2693
+ const match = this.overlayService.findNodeByElement(el);
2694
+ if (match)
2695
+ out.unshift(match.node);
2696
+ }
2697
+ return out;
2547
2698
  }
2548
2699
  // ── Shared drop handler — used by all layout components ───────────────────
2549
2700
  handleDrop(children, event, dropListId, cdr, afterDrop) {
2550
2701
  const { payload, insertIndex } = event;
2702
+ const changesParent = payload.type === 'external' || payload.sourceId !== dropListId;
2703
+ if (changesParent && this.refuseInsert(payload.data, this.dropzoneAncestors(dropListId))) {
2704
+ afterDrop?.();
2705
+ return;
2706
+ }
2551
2707
  if (payload.type === 'external') {
2552
2708
  const node = this.materialize(payload.data);
2553
2709
  if (node) {
@@ -2621,7 +2777,9 @@ class DragEngineService {
2621
2777
  * `dropComplete$` exactly like a drop, so the builder selects the new node and records one undo
2622
2778
  * step through the same path. Returns the inserted node, or null if the payload was unusable.
2623
2779
  */
2624
- insertPayload(children, index, data) {
2780
+ insertPayload(children, index, data, ancestors = []) {
2781
+ if (this.refuseInsert(data, ancestors))
2782
+ return null;
2625
2783
  const node = this.materialize(data);
2626
2784
  if (!node)
2627
2785
  return null;
@@ -2667,7 +2825,7 @@ class DragEngineService {
2667
2825
  registerDropzone(id, el, data, cdr, dropCb, postDrop) {
2668
2826
  if (!id)
2669
2827
  return;
2670
- // Stamp id on element so DOM-walk helpers (_isRootContainer) find it.
2828
+ // Stamp id on element so DOM-walk helpers (_rootContainerOf, dropzoneAncestors) find it.
2671
2829
  el.id = id;
2672
2830
  if (!this.ids.includes(id)) {
2673
2831
  this.ids = [...this.ids, id];
@@ -2773,6 +2931,8 @@ class DragEngineService {
2773
2931
  this._sourceEl = el;
2774
2932
  this._isDragging = true;
2775
2933
  this._rootEscapeActiveFor = null;
2934
+ this._innerRootId = null;
2935
+ this._targetRefusal = null;
2776
2936
  // Suppress native text selection for the duration of the drag — without this,
2777
2937
  // dragging the pointer across any text (canvas or sidebar) selects/highlights it.
2778
2938
  document.body.classList.add('iox-dragging');
@@ -2887,6 +3047,8 @@ class DragEngineService {
2887
3047
  this._removeTargetOverlay();
2888
3048
  document.querySelectorAll('.iox-drag-over').forEach(el => el.classList.remove('iox-drag-over'));
2889
3049
  this._rootEscapeActiveFor = null;
3050
+ this._innerRootId = null;
3051
+ this._targetRefusal = null;
2890
3052
  this._isDragging = false;
2891
3053
  this._payload = null;
2892
3054
  this._sourceEl = null;
@@ -2974,55 +3136,14 @@ class DragEngineService {
2974
3136
  }
2975
3137
  }
2976
3138
  }
2977
- // Root-escape with hysteresis.
2978
- // Purpose: when the cursor is near the top/bottom edge of a root-level
2979
- // section, treat the target as canvas-preview so the user can insert
2980
- // between root sections.
2981
- //
2982
- // IMPORTANT: only activate if the cursor was ALREADY inside this section
2983
- // (i.e. deepTarget is the section itself or a nested child of it).
2984
- // Without this guard, crossing from Section A into the top 14px of
2985
- // Section C immediately locks the target to canvas-preview and prevents
2986
- // the user from ever reaching Section C's nested containers.
2987
- if (this._rootEscapeActiveFor) {
2988
- const escEl = this._getEl(this._rootEscapeActiveFor);
2989
- if (escEl) {
2990
- const r = this._dropzoneRectCache.get(this._rootEscapeActiveFor) ?? escEl.getBoundingClientRect();
2991
- const inside = px >= r.left && px <= r.right && py >= r.top && py <= r.bottom;
2992
- if (inside) {
2993
- bestId = 'canvas-preview';
2994
- }
2995
- else {
2996
- this._rootEscapeActiveFor = null;
2997
- }
2998
- }
2999
- else {
3000
- this._rootEscapeActiveFor = null;
3001
- }
3002
- }
3003
- if (!this._rootEscapeActiveFor &&
3004
- bestId && bestId !== 'canvas-preview' && this._isRootContainer(bestId)) {
3005
- const el = this._getEl(bestId);
3006
- if (el) {
3007
- const rect = this._dropzoneRectCache.get(bestId) ?? el.getBoundingClientRect();
3008
- const nearEdge = py <= rect.top + this.ROOT_ESCAPE_PX ||
3009
- py >= rect.bottom - this.ROOT_ESCAPE_PX;
3010
- // Only escape if we were already targeting this section or
3011
- // something nested inside it — not when crossing into it fresh.
3012
- const wasInsideThisSection = this._deepTargetId === bestId ||
3013
- this._isDescendantDropzone(this._deepTargetId, bestId);
3014
- if (nearEdge && wasInsideThisSection) {
3015
- this._rootEscapeActiveFor = bestId;
3016
- bestId = 'canvas-preview';
3017
- }
3018
- }
3019
- }
3139
+ bestId = this._applyRootEscape(bestId, py);
3020
3140
  if (bestId !== this._deepTargetId) {
3021
3141
  if (this._deepTargetId) {
3022
3142
  const old = this._getEl(this._deepTargetId);
3023
3143
  old?.classList.remove('iox-drag-over');
3024
3144
  }
3025
3145
  this._deepTargetId = bestId;
3146
+ this._targetRefusal = this._refusalAt(bestId);
3026
3147
  if (bestId) {
3027
3148
  this._getEl(bestId)?.classList.add('iox-drag-over');
3028
3149
  }
@@ -3034,6 +3155,50 @@ class DragEngineService {
3034
3155
  this._indicatorContainerId = null;
3035
3156
  }
3036
3157
  }
3158
+ /** Near a ROOT container's edge, after moving there from its inner region, the drop goes to the page. */
3159
+ _applyRootEscape(bestId, py) {
3160
+ const rootId = bestId && bestId !== 'canvas-preview' ? this._rootContainerOf(bestId) : null;
3161
+ const rootEl = rootId ? this._getEl(rootId) : null;
3162
+ const rect = rootId && rootEl ? (this._dropzoneRectCache.get(rootId) ?? rootEl.getBoundingClientRect()) : null;
3163
+ const zone = rect ? rootZone(py, rect.top, rect.bottom) : RootZone.Outside;
3164
+ const escape = !!rootId && rootId === bestId &&
3165
+ escapesRoot(rootId, zone, { escapingFor: this._rootEscapeActiveFor, innerOf: this._innerRootId });
3166
+ this._rootEscapeActiveFor = escape ? rootId : null;
3167
+ this._innerRootId = zone === RootZone.Inner ? rootId : null;
3168
+ return escape ? 'canvas-preview' : bestId;
3169
+ }
3170
+ /** The ROOT-level dropzone (a direct child zone of the page) that holds dropzone `id`, or null when it
3171
+ * is not on the page canvas (the global-elements layer). */
3172
+ _rootContainerOf(id) {
3173
+ let root = null;
3174
+ for (let el = this._getEl(id); el; el = el.parentElement) {
3175
+ if (el.id === 'canvas-preview')
3176
+ return root;
3177
+ if (el.id && this.elMap.has(el.id))
3178
+ root = el.id;
3179
+ }
3180
+ return null;
3181
+ }
3182
+ /** Why the dragged payload may not land in `targetId` (null = it may). Reordering inside the source
3183
+ * container is not an insert. */
3184
+ _refusalAt(targetId) {
3185
+ if (!targetId || !this._payload)
3186
+ return null;
3187
+ if (this._payload.type === 'internal' && this._payload.sourceId === targetId)
3188
+ return null;
3189
+ const ancestors = targetId === 'canvas-preview' ? [] : this.dropzoneAncestors(targetId);
3190
+ return insertRefusal(this.payloadNode(this._payload.data), ancestors);
3191
+ }
3192
+ /** The node a dropzone element belongs to — the element itself or the nearest registered ancestor
3193
+ * (a LinkedContainer, a Dialog, a Repeater or a slide registers an INNER element as its zone). */
3194
+ _ownerNode(el) {
3195
+ for (let cur = el; cur && cur.id !== 'canvas-preview'; cur = cur.parentElement) {
3196
+ const match = this.overlayService.findNodeByElement(cur);
3197
+ if (match)
3198
+ return match.node;
3199
+ }
3200
+ return undefined;
3201
+ }
3037
3202
  // ─────────────────────────────────────────────────────────
3038
3203
  // Target overlay — fixed to <body>, sized from getBoundingClientRect
3039
3204
  // so it is always above all canvas content regardless of stacking
@@ -3067,7 +3232,7 @@ class DragEngineService {
3067
3232
  this._targetChip.style.cssText = [
3068
3233
  'position:absolute', 'left:0', 'top:-18px', 'max-width:100%', 'overflow:hidden',
3069
3234
  'white-space:nowrap', 'text-overflow:ellipsis', 'padding:1px 6px',
3070
- 'border-radius:4px 4px 0 0', 'background:#6366f1', 'color:#fff',
3235
+ 'border-radius:4px 4px 0 0', `background:${TARGET_CHIP_BG}`, 'color:#fff',
3071
3236
  'font:500 10px/1.5 system-ui,sans-serif', 'pointer-events:none',
3072
3237
  ].join(';');
3073
3238
  this._targetOverlay.appendChild(this._targetChip);
@@ -3095,40 +3260,19 @@ class DragEngineService {
3095
3260
  _updateTargetChip(targetId, el) {
3096
3261
  if (!this._targetChip)
3097
3262
  return;
3098
- const name = this.overlayService.findNodeByElement(el)?.node;
3263
+ const refused = this._targetRefusal;
3264
+ this._targetOverlay?.classList.toggle(DROP_REFUSED_CLASS, !!refused);
3265
+ this._targetChip.style.background = refused ? REFUSED_CHIP_BG : TARGET_CHIP_BG;
3266
+ if (refused) {
3267
+ this._targetChip.textContent = insertRefusalLabel(refused, this.labels);
3268
+ return;
3269
+ }
3270
+ const name = this._ownerNode(el);
3099
3271
  const childCount = this.getData(targetId)?.length ?? 0;
3100
3272
  const index = this._indicatorContainerId === targetId && this._indicatorIndex >= 0
3101
3273
  ? this._indicatorIndex : childCount;
3102
3274
  this._targetChip.textContent = dropTargetLabel(name?.label || name?.type, index, childCount);
3103
3275
  }
3104
- _isRootContainer(id) {
3105
- const el = this._getEl(id);
3106
- if (!el)
3107
- return false;
3108
- let parent = el.parentElement;
3109
- while (parent) {
3110
- if (parent.id === 'canvas-preview')
3111
- return true;
3112
- if (parent.id && this.ids.includes(parent.id))
3113
- return false;
3114
- parent = parent.parentElement;
3115
- }
3116
- return false;
3117
- }
3118
- /**
3119
- * Returns true if the dropzone with `childId` is a DOM descendant of the
3120
- * dropzone with `parentId`. Used to decide whether the cursor was already
3121
- * "inside" a root section before triggering root-escape.
3122
- */
3123
- _isDescendantDropzone(childId, parentId) {
3124
- if (!childId)
3125
- return false;
3126
- const childEl = this._getEl(childId);
3127
- const parentEl = this._getEl(parentId);
3128
- if (!childEl || !parentEl)
3129
- return false;
3130
- return parentEl.contains(childEl);
3131
- }
3132
3276
  // ─────────────────────────────────────────────────────────
3133
3277
  // Insertion indicator (animates open so siblings shift smoothly)
3134
3278
  // ─────────────────────────────────────────────────────────
@@ -3281,12 +3425,17 @@ class DragEngineService {
3281
3425
  }
3282
3426
  return children.length;
3283
3427
  }
3284
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: DragEngineService, deps: [{ token: ComponentRegistryService }, { token: SymbolRegistryService }, { token: OverlayService }], target: i0.ɵɵFactoryTarget.Injectable }); }
3428
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: DragEngineService, deps: [{ token: ComponentRegistryService }, { token: SymbolRegistryService }, { token: OverlayService }, { token: IOX_BUILDER_LABELS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
3285
3429
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: DragEngineService }); }
3286
3430
  }
3287
3431
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: DragEngineService, decorators: [{
3288
3432
  type: Injectable
3289
- }], ctorParameters: () => [{ type: ComponentRegistryService }, { type: SymbolRegistryService }, { type: OverlayService }] });
3433
+ }], ctorParameters: () => [{ type: ComponentRegistryService }, { type: SymbolRegistryService }, { type: OverlayService }, { type: undefined, decorators: [{
3434
+ type: Optional
3435
+ }, {
3436
+ type: Inject,
3437
+ args: [IOX_BUILDER_LABELS]
3438
+ }] }] });
3290
3439
 
3291
3440
  /**
3292
3441
  * Page-scoped registry that holds the current page's data sources.
@@ -5992,9 +6141,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
5992
6141
  }] } });
5993
6142
 
5994
6143
  class LayerTreeComponent {
5995
- constructor(registry, overlayService) {
6144
+ constructor(registry, overlayService, dragEngine) {
5996
6145
  this.registry = registry;
5997
6146
  this.overlayService = overlayService;
6147
+ this.dragEngine = dragEngine;
5998
6148
  this.layout = [];
5999
6149
  this.globalElements = [];
6000
6150
  this.nodeSelect = new EventEmitter();
@@ -6102,6 +6252,13 @@ class LayerTreeComponent {
6102
6252
  const targetTreeNode = this.flatTree[event.currentIndex];
6103
6253
  if (!targetTreeNode)
6104
6254
  return;
6255
+ // Landing beside the target means landing in the target's parent — the same acceptance rule
6256
+ // every canvas insert asks (a form never goes inside a form).
6257
+ if (targetTreeNode.parentArray !== draggedTreeNode.parentArray) {
6258
+ const ancestors = (nodeAncestorPathInEither(this.layout, this.globalElements, targetTreeNode.node) ?? []).slice(0, -1);
6259
+ if (this.dragEngine.refuseInsert(draggedTreeNode.node, ancestors))
6260
+ return;
6261
+ }
6105
6262
  // Remove from source parent
6106
6263
  const srcArray = draggedTreeNode.parentArray;
6107
6264
  const srcIdx = draggedTreeNode.indexInParent;
@@ -6165,13 +6322,13 @@ class LayerTreeComponent {
6165
6322
  }
6166
6323
  }
6167
6324
  }
6168
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: LayerTreeComponent, deps: [{ token: ComponentRegistryService }, { token: OverlayService }], target: i0.ɵɵFactoryTarget.Component }); }
6169
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.31", type: LayerTreeComponent, isStandalone: false, selector: "app-layer-tree", inputs: { layout: "layout", globalElements: "globalElements" }, outputs: { nodeSelect: "nodeSelect", nodeAction: "nodeAction", nodeMove: "nodeMove" }, ngImport: i0, template: "<div class=\"layer-tree\">\n <div class=\"tree-header\">\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'page'\" (click)=\"activeSection = 'page'\">\n <i class=\"ph-thin ph-stack\"></i> Page\n </button>\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'global'\" (click)=\"activeSection = 'global'\">\n <i class=\"ph-thin ph-globe\"></i> Global\n <span *ngIf=\"flatGlobalTree.length\" class=\"section-tab-badge\">{{ flatGlobalTree.length }}</span>\n </button>\n </div>\n\n <!-- \u2500\u2500 Page elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div *ngIf=\"activeSection === 'page'\"\n class=\"tree-body\"\n cdkDropList\n [cdkDropListData]=\"flatTree\"\n (cdkDropListDropped)=\"onTreeDrop($event)\">\n <div *ngIf=\"!flatTree.length\" class=\"tree-empty\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n <span>No layers yet</span>\n </div>\n\n <div *ngFor=\"let tn of flatTree\"\n class=\"tree-row\"\n cdkDrag\n [cdkDragData]=\"tn\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <i class=\"ph-thin ph-dots-six-vertical drag-handle\" cdkDragHandle></i>\n\n <button *ngIf=\"tn.hasChildren\" class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n <span *ngIf=\"!tn.hasChildren\" class=\"expand-spacer\"></span>\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <ng-container *ngIf=\"editingNodeId === tn.node.id; else staticLabel\">\n <input class=\"node-rename-input\"\n [value]=\"editingValue\"\n (input)=\"editingValue = $any($event.target).value\"\n (keydown.enter)=\"saveRename(tn)\"\n (keydown.escape)=\"cancelRename()\"\n (blur)=\"saveRename(tn)\"\n (click)=\"$event.stopPropagation()\"\n autofocus>\n </ng-container>\n <ng-template #staticLabel>\n <span class=\"node-label\" [class.node-label--named]=\"tn.node.label\">{{ tn.label }}</span>\n </ng-template>\n\n <span class=\"node-actions\">\n <button class=\"action-btn\" title=\"Rename\" (click)=\"startRename(tn, $event)\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n </button>\n <button class=\"action-btn\" title=\"Duplicate\" (click)=\"onDuplicateNode(tn, $event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n </div>\n\n <!-- \u2500\u2500 Global elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div *ngIf=\"activeSection === 'global'\" class=\"tree-body\">\n <div *ngIf=\"!flatGlobalTree.length\" class=\"tree-empty\">\n <i class=\"ph-thin ph-globe\"></i>\n <span>No global elements</span>\n </div>\n\n <div *ngFor=\"let tn of flatGlobalTree\"\n class=\"tree-row tree-row--global\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <button *ngIf=\"tn.hasChildren\" class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n <span *ngIf=\"!tn.hasChildren\" class=\"expand-spacer\"></span>\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <span class=\"node-label\">{{ tn.label }}</span>\n <span class=\"node-position-badge\">{{ tn.node.globalPosition }}</span>\n\n <span class=\"node-actions\">\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n </div>\n</div>\n", styles: [".layer-tree{display:flex;flex-direction:column;height:100%;overflow:hidden}.layer-tree .tree-header{display:flex;gap:2px;padding:6px 8px;border-bottom:1px solid var(--p-surface-200);flex-shrink:0}.layer-tree .section-tab{display:inline-flex;align-items:center;gap:4px;flex:1;justify-content:center;padding:4px 8px;font-size:11px;font-weight:500;border:1px solid var(--p-surface-200);border-radius:4px;background:transparent;color:var(--p-text-muted-color);cursor:pointer;transition:background .12s,color .12s,border-color .12s;white-space:nowrap}.layer-tree .section-tab i{font-size:11px}.layer-tree .section-tab:hover{background:var(--p-surface-100);color:var(--p-text-color)}.layer-tree .section-tab--active{background:#cb909026;border-color:#cb9090;color:#cb9090}.layer-tree .section-tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;font-size:10px;font-weight:600;background:#cb9090;color:#fff;border-radius:8px}.layer-tree .tree-body{flex:1 1 0;overflow-y:auto;overflow-x:hidden;padding-block:4px}.layer-tree .tree-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:2rem 1rem;color:var(--p-text-muted-color);font-size:12px}.layer-tree .tree-empty i{font-size:22px}.layer-tree .tree-row{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;cursor:pointer;font-size:12px;color:var(--p-text-color);border-inline-start:2px solid transparent;transition:background .12s,border-color .12s;-webkit-user-select:none;user-select:none}.layer-tree .tree-row:hover,.layer-tree .tree-row.is-hovered{background:var(--p-surface-100)}.layer-tree .tree-row.is-selected{background:#cb90901a;border-inline-start-color:#cb9090;font-weight:500}.layer-tree .drag-handle{font-size:10px;color:var(--p-text-muted-color);opacity:0;cursor:grab;flex-shrink:0;transition:opacity .12s}.layer-tree .tree-row:hover .drag-handle{opacity:.5}.layer-tree .drag-handle:hover{opacity:1!important}.layer-tree .cdk-drag-preview{box-sizing:border-box;display:flex;align-items:center;gap:6px;height:28px;padding:0 8px 0 12px;border-radius:6px;background:var(--p-surface-0);border:1px solid #cb9090;box-shadow:0 4px 12px #0000001f;font-size:12px}.layer-tree .cdk-drag-placeholder{height:2px;background:#cb9090;border-radius:1px;opacity:.6}.layer-tree .cdk-drag-placeholder>*{visibility:hidden}.layer-tree .cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.layer-tree .expand-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;flex-shrink:0;border-radius:2px}.layer-tree .expand-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .expand-btn i{font-size:10px}.layer-tree .expand-spacer{width:16px;flex-shrink:0}.layer-tree .node-icon{font-size:14px;color:var(--p-text-muted-color);flex-shrink:0}.layer-tree .node-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.layer-tree .node-label.node-label--named{color:var(--p-primary-color, #cb9090);font-weight:500}.layer-tree .node-rename-input{flex:1;min-width:0;border:1px solid var(--p-primary-color, #cb9090);border-radius:4px;background:var(--p-surface-0, #fff);color:var(--p-text-color);font-family:inherit;font-size:.72rem;padding:1px 4px;outline:none}.layer-tree .node-actions{display:flex;gap:2px;margin-inline-start:auto;opacity:0;transition:opacity .12s;flex-shrink:0}.layer-tree .tree-row:hover .node-actions,.layer-tree .tree-row.is-selected .node-actions{opacity:1}.layer-tree .tree-section-header{display:flex;align-items:center;gap:6px;padding:6px 12px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color);border-top:1px solid var(--p-surface-200);margin-top:4px}.layer-tree .tree-section-header i{font-size:12px}.layer-tree .tree-body--global{flex:0 0 auto}.layer-tree .tree-row--global{border-inline-start-color:transparent}.layer-tree .tree-row--global.is-selected{background:#638fcb1a;border-inline-start-color:#638fcb}.layer-tree .node-position-badge{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#638fcb;background:#638fcb1f;padding:1px 5px;border-radius:3px;flex-shrink:0}.layer-tree .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:4px;font-size:12px;transition:background .12s,color .12s}.layer-tree .action-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .action-btn--delete:hover{background:#dc26261a;color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i4.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i4.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }] }); }
6325
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: LayerTreeComponent, deps: [{ token: ComponentRegistryService }, { token: OverlayService }, { token: DragEngineService }], target: i0.ɵɵFactoryTarget.Component }); }
6326
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.31", type: LayerTreeComponent, isStandalone: false, selector: "app-layer-tree", inputs: { layout: "layout", globalElements: "globalElements" }, outputs: { nodeSelect: "nodeSelect", nodeAction: "nodeAction", nodeMove: "nodeMove" }, ngImport: i0, template: "<div class=\"layer-tree\">\n <div class=\"tree-header\">\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'page'\" (click)=\"activeSection = 'page'\">\n <i class=\"ph-thin ph-stack\"></i> Page\n </button>\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'global'\" (click)=\"activeSection = 'global'\">\n <i class=\"ph-thin ph-globe\"></i> Global\n <span *ngIf=\"flatGlobalTree.length\" class=\"section-tab-badge\">{{ flatGlobalTree.length }}</span>\n </button>\n </div>\n\n <!-- \u2500\u2500 Page elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div *ngIf=\"activeSection === 'page'\"\n class=\"tree-body\"\n cdkDropList\n [cdkDropListData]=\"flatTree\"\n (cdkDropListDropped)=\"onTreeDrop($event)\">\n <div *ngIf=\"!flatTree.length\" class=\"tree-empty\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n <span>No layers yet</span>\n </div>\n\n <div *ngFor=\"let tn of flatTree\"\n class=\"tree-row\"\n cdkDrag\n [cdkDragData]=\"tn\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <i class=\"ph-thin ph-dots-six-vertical drag-handle\" cdkDragHandle></i>\n\n <button *ngIf=\"tn.hasChildren\" class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n <span *ngIf=\"!tn.hasChildren\" class=\"expand-spacer\"></span>\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <ng-container *ngIf=\"editingNodeId === tn.node.id; else staticLabel\">\n <input class=\"node-rename-input\"\n [value]=\"editingValue\"\n (input)=\"editingValue = $any($event.target).value\"\n (keydown.enter)=\"saveRename(tn)\"\n (keydown.escape)=\"cancelRename()\"\n (blur)=\"saveRename(tn)\"\n (click)=\"$event.stopPropagation()\"\n autofocus>\n </ng-container>\n <ng-template #staticLabel>\n <span class=\"node-label\" [class.node-label--named]=\"tn.node.label\">{{ tn.label }}</span>\n </ng-template>\n\n <span class=\"node-actions\">\n <button class=\"action-btn\" title=\"Rename\" (click)=\"startRename(tn, $event)\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n </button>\n <button class=\"action-btn\" title=\"Duplicate\" (click)=\"onDuplicateNode(tn, $event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n </div>\n\n <!-- \u2500\u2500 Global elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div *ngIf=\"activeSection === 'global'\" class=\"tree-body\">\n <div *ngIf=\"!flatGlobalTree.length\" class=\"tree-empty\">\n <i class=\"ph-thin ph-globe\"></i>\n <span>No global elements</span>\n </div>\n\n <div *ngFor=\"let tn of flatGlobalTree\"\n class=\"tree-row tree-row--global\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <button *ngIf=\"tn.hasChildren\" class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n <span *ngIf=\"!tn.hasChildren\" class=\"expand-spacer\"></span>\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <span class=\"node-label\">{{ tn.label }}</span>\n <span class=\"node-position-badge\">{{ tn.node.globalPosition }}</span>\n\n <span class=\"node-actions\">\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n </div>\n</div>\n", styles: [".layer-tree{display:flex;flex-direction:column;height:100%;overflow:hidden}.layer-tree .tree-header{display:flex;gap:2px;padding:6px 8px;border-bottom:1px solid var(--p-surface-200);flex-shrink:0}.layer-tree .section-tab{display:inline-flex;align-items:center;gap:4px;flex:1;justify-content:center;padding:4px 8px;font-size:11px;font-weight:500;border:1px solid var(--p-surface-200);border-radius:4px;background:transparent;color:var(--p-text-muted-color);cursor:pointer;transition:background .12s,color .12s,border-color .12s;white-space:nowrap}.layer-tree .section-tab i{font-size:11px}.layer-tree .section-tab:hover{background:var(--p-surface-100);color:var(--p-text-color)}.layer-tree .section-tab--active{background:#cb909026;border-color:#cb9090;color:#cb9090}.layer-tree .section-tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;font-size:10px;font-weight:600;background:#cb9090;color:#fff;border-radius:8px}.layer-tree .tree-body{flex:1 1 0;overflow-y:auto;overflow-x:hidden;padding-block:4px}.layer-tree .tree-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:2rem 1rem;color:var(--p-text-muted-color);font-size:12px}.layer-tree .tree-empty i{font-size:22px}.layer-tree .tree-row{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;cursor:pointer;font-size:12px;color:var(--p-text-color);border-inline-start:2px solid transparent;transition:background .12s,border-color .12s;-webkit-user-select:none;user-select:none}.layer-tree .tree-row:hover,.layer-tree .tree-row.is-hovered{background:var(--p-surface-100)}.layer-tree .tree-row.is-selected{background:#cb90901a;border-inline-start-color:#cb9090;font-weight:500}.layer-tree .drag-handle{font-size:10px;color:var(--p-text-muted-color);opacity:0;cursor:grab;flex-shrink:0;transition:opacity .12s}.layer-tree .tree-row:hover .drag-handle{opacity:.5}.layer-tree .drag-handle:hover{opacity:1!important}.layer-tree .cdk-drag-preview{box-sizing:border-box;display:flex;align-items:center;gap:6px;height:28px;padding:0 8px 0 12px;border-radius:6px;background:var(--p-surface-0);border:1px solid #cb9090;box-shadow:0 4px 12px #0000001f;font-size:12px}.layer-tree .cdk-drag-placeholder{height:2px;background:#cb9090;border-radius:1px;opacity:.6}.layer-tree .cdk-drag-placeholder>*{visibility:hidden}.layer-tree .cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.layer-tree .expand-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;flex-shrink:0;border-radius:2px}.layer-tree .expand-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .expand-btn i{font-size:10px}.layer-tree .expand-spacer{width:16px;flex-shrink:0}.layer-tree .node-icon{font-size:14px;color:var(--p-text-muted-color);flex-shrink:0}.layer-tree .node-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.layer-tree .node-label.node-label--named{color:var(--p-primary-color, #cb9090);font-weight:500}.layer-tree .node-rename-input{flex:1;min-width:0;border:1px solid var(--p-primary-color, #cb9090);border-radius:4px;background:var(--p-surface-0, #fff);color:var(--p-text-color);font-family:inherit;font-size:.72rem;padding:1px 4px;outline:none}.layer-tree .node-actions{display:flex;gap:2px;margin-inline-start:auto;opacity:0;transition:opacity .12s;flex-shrink:0}.layer-tree .tree-row:hover .node-actions,.layer-tree .tree-row.is-selected .node-actions{opacity:1}.layer-tree .tree-section-header{display:flex;align-items:center;gap:6px;padding:6px 12px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color);border-top:1px solid var(--p-surface-200);margin-top:4px}.layer-tree .tree-section-header i{font-size:12px}.layer-tree .tree-body--global{flex:0 0 auto}.layer-tree .tree-row--global{border-inline-start-color:transparent}.layer-tree .tree-row--global.is-selected{background:#638fcb1a;border-inline-start-color:#638fcb}.layer-tree .node-position-badge{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#638fcb;background:#638fcb1f;padding:1px 5px;border-radius:3px;flex-shrink:0}.layer-tree .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:4px;font-size:12px;transition:background .12s,color .12s}.layer-tree .action-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .action-btn--delete:hover{background:#dc26261a;color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }] }); }
6170
6327
  }
6171
6328
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: LayerTreeComponent, decorators: [{
6172
6329
  type: Component,
6173
6330
  args: [{ selector: 'app-layer-tree', standalone: false, template: "<div class=\"layer-tree\">\n <div class=\"tree-header\">\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'page'\" (click)=\"activeSection = 'page'\">\n <i class=\"ph-thin ph-stack\"></i> Page\n </button>\n <button class=\"section-tab\" [class.section-tab--active]=\"activeSection === 'global'\" (click)=\"activeSection = 'global'\">\n <i class=\"ph-thin ph-globe\"></i> Global\n <span *ngIf=\"flatGlobalTree.length\" class=\"section-tab-badge\">{{ flatGlobalTree.length }}</span>\n </button>\n </div>\n\n <!-- \u2500\u2500 Page elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div *ngIf=\"activeSection === 'page'\"\n class=\"tree-body\"\n cdkDropList\n [cdkDropListData]=\"flatTree\"\n (cdkDropListDropped)=\"onTreeDrop($event)\">\n <div *ngIf=\"!flatTree.length\" class=\"tree-empty\">\n <i class=\"ph-thin ph-tree-structure\"></i>\n <span>No layers yet</span>\n </div>\n\n <div *ngFor=\"let tn of flatTree\"\n class=\"tree-row\"\n cdkDrag\n [cdkDragData]=\"tn\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <i class=\"ph-thin ph-dots-six-vertical drag-handle\" cdkDragHandle></i>\n\n <button *ngIf=\"tn.hasChildren\" class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n <span *ngIf=\"!tn.hasChildren\" class=\"expand-spacer\"></span>\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <ng-container *ngIf=\"editingNodeId === tn.node.id; else staticLabel\">\n <input class=\"node-rename-input\"\n [value]=\"editingValue\"\n (input)=\"editingValue = $any($event.target).value\"\n (keydown.enter)=\"saveRename(tn)\"\n (keydown.escape)=\"cancelRename()\"\n (blur)=\"saveRename(tn)\"\n (click)=\"$event.stopPropagation()\"\n autofocus>\n </ng-container>\n <ng-template #staticLabel>\n <span class=\"node-label\" [class.node-label--named]=\"tn.node.label\">{{ tn.label }}</span>\n </ng-template>\n\n <span class=\"node-actions\">\n <button class=\"action-btn\" title=\"Rename\" (click)=\"startRename(tn, $event)\">\n <i class=\"ph-thin ph-pencil-simple\"></i>\n </button>\n <button class=\"action-btn\" title=\"Duplicate\" (click)=\"onDuplicateNode(tn, $event)\">\n <i class=\"ph-thin ph-copy\"></i>\n </button>\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n </div>\n\n <!-- \u2500\u2500 Global elements tree \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div *ngIf=\"activeSection === 'global'\" class=\"tree-body\">\n <div *ngIf=\"!flatGlobalTree.length\" class=\"tree-empty\">\n <i class=\"ph-thin ph-globe\"></i>\n <span>No global elements</span>\n </div>\n\n <div *ngFor=\"let tn of flatGlobalTree\"\n class=\"tree-row tree-row--global\"\n [class.is-selected]=\"tn.node.id === selectedNodeId\"\n [class.is-hovered]=\"tn.node.id === hoveredNodeId\"\n [style.padding-inline-start.px]=\"12 + tn.depth * 16\"\n (click)=\"onNodeClick(tn)\"\n (mouseenter)=\"onNodeMouseEnter(tn)\"\n (mouseleave)=\"onNodeMouseLeave()\">\n\n <button *ngIf=\"tn.hasChildren\" class=\"expand-btn\" (click)=\"toggleExpand(tn, $event)\">\n <i class=\"ph-thin\" [ngClass]=\"tn.expanded ? 'ph-caret-down' : 'ph-caret-right'\"></i>\n </button>\n <span *ngIf=\"!tn.hasChildren\" class=\"expand-spacer\"></span>\n\n <i [class]=\"tn.icon\" class=\"node-icon\"></i>\n <span class=\"node-label\">{{ tn.label }}</span>\n <span class=\"node-position-badge\">{{ tn.node.globalPosition }}</span>\n\n <span class=\"node-actions\">\n <button class=\"action-btn action-btn--delete\" title=\"Delete\" (click)=\"onDeleteNode(tn, $event)\">\n <i class=\"ph-thin ph-trash\"></i>\n </button>\n </span>\n </div>\n </div>\n</div>\n", styles: [".layer-tree{display:flex;flex-direction:column;height:100%;overflow:hidden}.layer-tree .tree-header{display:flex;gap:2px;padding:6px 8px;border-bottom:1px solid var(--p-surface-200);flex-shrink:0}.layer-tree .section-tab{display:inline-flex;align-items:center;gap:4px;flex:1;justify-content:center;padding:4px 8px;font-size:11px;font-weight:500;border:1px solid var(--p-surface-200);border-radius:4px;background:transparent;color:var(--p-text-muted-color);cursor:pointer;transition:background .12s,color .12s,border-color .12s;white-space:nowrap}.layer-tree .section-tab i{font-size:11px}.layer-tree .section-tab:hover{background:var(--p-surface-100);color:var(--p-text-color)}.layer-tree .section-tab--active{background:#cb909026;border-color:#cb9090;color:#cb9090}.layer-tree .section-tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;font-size:10px;font-weight:600;background:#cb9090;color:#fff;border-radius:8px}.layer-tree .tree-body{flex:1 1 0;overflow-y:auto;overflow-x:hidden;padding-block:4px}.layer-tree .tree-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:2rem 1rem;color:var(--p-text-muted-color);font-size:12px}.layer-tree .tree-empty i{font-size:22px}.layer-tree .tree-row{display:flex;align-items:center;gap:6px;height:28px;padding-inline-end:8px;cursor:pointer;font-size:12px;color:var(--p-text-color);border-inline-start:2px solid transparent;transition:background .12s,border-color .12s;-webkit-user-select:none;user-select:none}.layer-tree .tree-row:hover,.layer-tree .tree-row.is-hovered{background:var(--p-surface-100)}.layer-tree .tree-row.is-selected{background:#cb90901a;border-inline-start-color:#cb9090;font-weight:500}.layer-tree .drag-handle{font-size:10px;color:var(--p-text-muted-color);opacity:0;cursor:grab;flex-shrink:0;transition:opacity .12s}.layer-tree .tree-row:hover .drag-handle{opacity:.5}.layer-tree .drag-handle:hover{opacity:1!important}.layer-tree .cdk-drag-preview{box-sizing:border-box;display:flex;align-items:center;gap:6px;height:28px;padding:0 8px 0 12px;border-radius:6px;background:var(--p-surface-0);border:1px solid #cb9090;box-shadow:0 4px 12px #0000001f;font-size:12px}.layer-tree .cdk-drag-placeholder{height:2px;background:#cb9090;border-radius:1px;opacity:.6}.layer-tree .cdk-drag-placeholder>*{visibility:hidden}.layer-tree .cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.layer-tree .expand-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;flex-shrink:0;border-radius:2px}.layer-tree .expand-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .expand-btn i{font-size:10px}.layer-tree .expand-spacer{width:16px;flex-shrink:0}.layer-tree .node-icon{font-size:14px;color:var(--p-text-muted-color);flex-shrink:0}.layer-tree .node-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.layer-tree .node-label.node-label--named{color:var(--p-primary-color, #cb9090);font-weight:500}.layer-tree .node-rename-input{flex:1;min-width:0;border:1px solid var(--p-primary-color, #cb9090);border-radius:4px;background:var(--p-surface-0, #fff);color:var(--p-text-color);font-family:inherit;font-size:.72rem;padding:1px 4px;outline:none}.layer-tree .node-actions{display:flex;gap:2px;margin-inline-start:auto;opacity:0;transition:opacity .12s;flex-shrink:0}.layer-tree .tree-row:hover .node-actions,.layer-tree .tree-row.is-selected .node-actions{opacity:1}.layer-tree .tree-section-header{display:flex;align-items:center;gap:6px;padding:6px 12px 4px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color);border-top:1px solid var(--p-surface-200);margin-top:4px}.layer-tree .tree-section-header i{font-size:12px}.layer-tree .tree-body--global{flex:0 0 auto}.layer-tree .tree-row--global{border-inline-start-color:transparent}.layer-tree .tree-row--global.is-selected{background:#638fcb1a;border-inline-start-color:#638fcb}.layer-tree .node-position-badge{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#638fcb;background:#638fcb1f;padding:1px 5px;border-radius:3px;flex-shrink:0}.layer-tree .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--p-text-muted-color);cursor:pointer;border-radius:4px;font-size:12px;transition:background .12s,color .12s}.layer-tree .action-btn:hover{background:var(--p-surface-200);color:var(--p-text-color)}.layer-tree .action-btn--delete:hover{background:#dc26261a;color:#dc2626}\n"] }]
6174
- }], ctorParameters: () => [{ type: ComponentRegistryService }, { type: OverlayService }], propDecorators: { layout: [{
6331
+ }], ctorParameters: () => [{ type: ComponentRegistryService }, { type: OverlayService }, { type: DragEngineService }], propDecorators: { layout: [{
6175
6332
  type: Input
6176
6333
  }], globalElements: [{
6177
6334
  type: Input
@@ -7012,6 +7169,9 @@ class BuilderComponent {
7012
7169
  }
7013
7170
  onDrop(event) {
7014
7171
  const { payload, insertIndex } = event;
7172
+ const changesParent = payload.type === 'external' || payload.sourceId !== 'canvas-preview';
7173
+ if (changesParent && this.dragEngine.refuseInsert(payload.data, []))
7174
+ return;
7015
7175
  if (payload.type === 'external') {
7016
7176
  if (typeof payload.data === 'string') {
7017
7177
  const config = this.registry.createConfig(payload.data);
@@ -7302,7 +7462,7 @@ class BuilderComponent {
7302
7462
  }
7303
7463
  case ToolbarAction.Duplicate: {
7304
7464
  const result = this.findNodeInTree(node, this.layout) ?? this.findNodeInTree(node, this.globalElements);
7305
- if (result) {
7465
+ if (result && !this.dragEngine.refuseInsert(node, this.siblingAncestors(node))) {
7306
7466
  const clone = this.deepCloneWithNewIds(node);
7307
7467
  result.parent.splice(result.index + 1, 0, clone);
7308
7468
  this.cdr.detectChanges();
@@ -7462,6 +7622,8 @@ class BuilderComponent {
7462
7622
  const pastedNode = this.clipboard.getNode();
7463
7623
  if (!pastedNode)
7464
7624
  break;
7625
+ if (this.dragEngine.refuseInsert(pastedNode, this.siblingAncestors(node)))
7626
+ break;
7465
7627
  const clone = this.deepCloneWithNewIds(pastedNode);
7466
7628
  const result = this.findNodeInTree(node, this.layout) ?? this.findNodeInTree(node, this.globalElements);
7467
7629
  if (result) {
@@ -7544,6 +7706,10 @@ class BuilderComponent {
7544
7706
  this.cdr.markForCheck();
7545
7707
  }, 1200);
7546
7708
  }
7709
+ /** The nodes a sibling of `node` sits under — `node`'s ancestors, outermost first (empty at a root). */
7710
+ siblingAncestors(node) {
7711
+ return (nodeAncestorPathInEither(this.layout, this.globalElements, node) ?? [node]).slice(0, -1);
7712
+ }
7547
7713
  findNodeInTree(target, children) {
7548
7714
  for (let i = 0; i < children.length; i++) {
7549
7715
  if (children[i] === target) {
@@ -7637,7 +7803,8 @@ class BuilderComponent {
7637
7803
  if (inGlobals && !target.parent) {
7638
7804
  target = resolveInsertTarget(this.layout, null, n => this.dragEngine.isDropContainer(n.children));
7639
7805
  }
7640
- if (!this.dragEngine.insertPayload(target.children, target.index, data))
7806
+ const ancestors = target.parent ? (nodeAncestorPathInEither(this.layout, this.globalElements, target.parent) ?? [target.parent]) : [];
7807
+ if (!this.dragEngine.insertPayload(target.children, target.index, data, ancestors))
7641
7808
  return;
7642
7809
  this.cdr.detectChanges();
7643
7810
  }
@@ -11676,5 +11843,5 @@ class BuilderSpacerComponentConfig extends ComponentConfig {
11676
11843
  * Generated bundle index. Do not edit.
11677
11844
  */
11678
11845
 
11679
- export { ACTION_TYPE_OPTIONS, AuditCode, AuditPanelComponent, AuditSeverity, BODY_CRUMB_LABEL, BREAKPOINT_PRESENTATION, BUILDER_CANVAS_CONTAINER, BUILDER_CHROME_EN, BUILDER_FONT_LINK_ATTR, BUILDER_TOKEN_SCOPE_CLASS, BackgroundGroupStyleConfig, BorderGroupStyleConfig, BreadcrumbBarComponent, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDataSourceService, BuilderDialogComponent, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CANVAS_EMPTY_IMAGE_LABEL_EN, CANVAS_EMPTY_IMAGE_LABEL_KEY, CANVAS_ROOT_RESET_CSS, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, DEFAULT_ESSENTIAL_TRAITS, DRAG_HANDLE_ATTR, DataSourceRegistryService, DataSourceType, DialogComponentConfig, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, EDITOR_CHROME_ATTRIBUTE, ESSENTIAL_TRAITS_BY_TYPE, EffectsGroupStyleConfig, FIELD_KIND_PRESETS, FIELD_PARTS, FORM_PRESETS, FORM_TYPE, FieldComponentConfig, FieldGroupComponentConfig, FormComponentConfig, FormErrorComponentConfig, FormStateService, FormSuccessComponentConfig, GalleryConfig, GroupStyleConfig, INTERACTION_STATES, INVERSE_ACTION, IOX_BUILDER_CANVAS, IOX_BUILDER_LABELS, IOX_DATA_SOURCE_ENDPOINTS, InteractionEngineService, InteractionPresetRegistryService, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LAYOUT_PRESETS, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, PALETTE_PRESET_DEFS, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PositionGroupStyleConfig, PresetRegistryService, RELATIONAL_STATES, RELATIONAL_STATE_BASE, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, SCREEN_WIDTH_OPTIONS, STRUCTURAL_STATES, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, SizeGroupStyleConfig, SliderContainerComponent, SliderContainerConfig, SliderSlideComponent, SliderSlideConfig, SpacingGroupStyleConfig, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, SubmitButtonComponentConfig, SymbolRegistryService, TARGETED_STATES, TARGETED_STATE_BASE, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, TypographyGroupStyleConfig, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, VALIDATION_STATES, VALIDATION_STATE_CLASS, ViewportService, ZOOM_OPTIONS, arrayItemSchemaFields, arraySchemaFields, auditNodes, buildConsentPreset, buildElementStyleSpecimen, buildFieldKindPreset, buildFlavorSpecimen, buildFormPreset, buildFullStyleTraits, buildLayoutPreset, buildPalettePreset, buildPresetStyleTraits, canFluidise, canvasEmptyImageLabel, collapseColumnsProps, columnCountOf, defaultBuilderLabels, defaultPageSettings, dropTargetLabel, essentialTraitsFor, findNodeLocation, findParentOf, flattenSchemaFields, fluidiseProps, generateNodeId, isHiddenAt, isSpecimen, markSpecimen, matchesControlSearch, nearestFormNode, nodeAncestorPath, nodeAncestorPathInEither, reorderProps, resolveInsertTarget, resolveTraitControllerType, resolveTraitOptions, setStyleTrait, setTrait, stackChildrenProps, summarise, syncEmptyImagePlaceholder, syncNodeDesign, textNode, visibilityProps, wrapNode };
11846
+ export { ACTION_TYPE_OPTIONS, AuditCode, AuditPanelComponent, AuditSeverity, BODY_CRUMB_LABEL, BREAKPOINT_PRESENTATION, BUILDER_CANVAS_CONTAINER, BUILDER_CHROME_EN, BUILDER_FONT_LINK_ATTR, BUILDER_TOKEN_SCOPE_CLASS, BackgroundGroupStyleConfig, BorderGroupStyleConfig, BreadcrumbBarComponent, BuilderButtonBlockComponent, BuilderButtonComponentConfig, BuilderClipboardService, BuilderComponent, BuilderContainerComponent, BuilderContainerComponentConfig, BuilderDataSourceService, BuilderDialogComponent, BuilderDividerComponentConfig, BuilderHeadingComponentConfig, BuilderHistoryService, BuilderIconComponentConfig, BuilderImageComponentConfig, BuilderLinkComponentConfig, BuilderLinkedContainerComponent, BuilderLinkedContainerConfig, BuilderListComponentConfig, BuilderListItemComponentConfig, BuilderMode, BuilderRepeaterComponent, BuilderSpacerComponentConfig, ButtonBlockComponentConfig, CANVAS_EMPTY_IMAGE_LABEL_EN, CANVAS_EMPTY_IMAGE_LABEL_KEY, CANVAS_ROOT_RESET_CSS, CardComponentConfig, ChildLayoutGroupStyleConfig, ComponentConfig, ComponentRegistryService, DEFAULT_ESSENTIAL_TRAITS, DRAG_HANDLE_ATTR, DROP_REFUSED_CLASS, DataSourceRegistryService, DataSourceType, DialogComponentConfig, DimensionsGroupStyleConfig, DragEngineService, EASING_OPTIONS$1 as EASING_OPTIONS, EDITOR_CHROME_ATTRIBUTE, ESSENTIAL_TRAITS_BY_TYPE, EffectsGroupStyleConfig, FIELD_KIND_PRESETS, FIELD_PARTS, FORM_PRESETS, FORM_STATE_BLOCK_TYPES, FORM_TYPE, FieldComponentConfig, FieldGroupComponentConfig, FormComponentConfig, FormErrorComponentConfig, FormStateService, FormSuccessComponentConfig, GalleryConfig, GroupStyleConfig, INSERT_REFUSAL_LABEL_KEYS, INTERACTION_STATES, INVERSE_ACTION, IOX_BUILDER_CANVAS, IOX_BUILDER_LABELS, IOX_DATA_SOURCE_ENDPOINTS, InsertRefusal, InteractionEngineService, InteractionPresetRegistryService, IoxBuilderModule, IoxDraggableDirective, IoxDropzoneDirective, LAYOUT_PRESETS, LayerTreeComponent, LayoutGroupStyleConfig, ListGroupStyleConfig, NodeAction, OverlayComponent, OverlayService, PALETTE_PRESET_DEFS, PanelChildComponent, PanelComponent, PanelEventService, PanelEventTypes, PanelTypes, PositionGroupStyleConfig, PresetRegistryService, RELATIONAL_STATES, RELATIONAL_STATE_BASE, ROOT_ESCAPE_PX, ROUTE_ANIMATION_OPTIONS, RenderDirective, RepeaterComponentConfig, RootZone, SCREEN_WIDTH_OPTIONS, STRUCTURAL_STATES, SUPPORTED_STATES, SectionComponent, SectionComponentConfig, SizeGroupStyleConfig, SliderContainerComponent, SliderContainerConfig, SliderSlideComponent, SliderSlideConfig, SpacingGroupStyleConfig, StyleCategory, StyleRegistryService, TraitConfig as StyleTraitConfig, SubmitButtonComponentConfig, SymbolRegistryService, TARGETED_STATES, TARGETED_STATE_BASE, TRIGGER_OPTIONS, TextBlockComponentConfig, ToolbarAction, ToolbarComponent, TraitConfig, TraitInputType, TypographyGroupStyleConfig, UNITS_ALL, UNITS_DEG, UNITS_FIXED, UNITS_NO_VW, VALIDATION_STATES, VALIDATION_STATE_CLASS, ViewportService, ZOOM_OPTIONS, arrayItemSchemaFields, arraySchemaFields, auditNodes, buildConsentPreset, buildElementStyleSpecimen, buildFieldKindPreset, buildFlavorSpecimen, buildFormPreset, buildFullStyleTraits, buildLayoutPreset, buildPalettePreset, buildPresetStyleTraits, canFluidise, canvasEmptyImageLabel, collapseColumnsProps, columnCountOf, defaultBuilderLabels, defaultPageSettings, dropTargetLabel, escapesRoot, essentialTraitsFor, findNodeLocation, findParentOf, flattenSchemaFields, fluidiseProps, generateNodeId, insertRefusal, insertRefusalLabel, isHiddenAt, isSpecimen, markSpecimen, matchesControlSearch, nearestFormNode, nodeAncestorPath, nodeAncestorPathInEither, reorderProps, resolveInsertTarget, resolveTraitControllerType, resolveTraitOptions, rootEscapeBand, rootZone, setStyleTrait, setTrait, stackChildrenProps, summarise, syncEmptyImagePlaceholder, syncNodeDesign, textNode, visibilityProps, wrapNode };
11680
11847
  //# sourceMappingURL=vectoriox-iox-builder.mjs.map