@vectoriox/iox-builder 8.0.0 → 9.0.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/fesm2022/vectoriox-iox-builder.mjs +286 -159
- package/fesm2022/vectoriox-iox-builder.mjs.map +1 -1
- package/index.d.ts +167 -88
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, InjectionToken, inject, NgZone, EventEmitter, createComponent, Output, Input,
|
|
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
|
|
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';
|
|
@@ -16,7 +16,7 @@ import { SelectModule } from 'primeng/select';
|
|
|
16
16
|
import { TooltipModule } from 'primeng/tooltip';
|
|
17
17
|
import { DialogModule } from 'primeng/dialog';
|
|
18
18
|
import * as i1 from '@vectoriox/iox-ui';
|
|
19
|
-
import { VALIDATION_STATES as VALIDATION_STATES$1, VALIDATION_STATE_CLASS as VALIDATION_STATE_CLASS$1, DEFAULT_WIDTH_BY_TYPE, COLOR_SCHEME_TRAIT, COLOR_SCHEMES, ColorScheme, Breakpoint, BREAKPOINTS, countOverrides, setOverride, NEW_NODE_STYLE_DEFAULTS, defaultElementStyleFor, applyElementStyle, stripSuppressedProps, DeclarationContext, buildPartSelector, planResponsiveRules, resolveStyleProps, buildTargetedStateSelector, statePseudoClass, compileTokenCssBlocks, catalogFontId, fontsForTokens, GOOGLE_FONTS_FILE_ORIGIN, googleFontHref, compileDeclarations, wrapInBreakpointQuery, BreakpointQueryKind, breakpointDef, breakpointForWidth, FormRenderState, safeSetInput, ContextStore, ComponentInstanceRegistryService, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, declaredInputNames, emptyImagePlaceholder, syncNodeDesignClasses, composeVirtualTraits, withDefaultHoverTransition, stateNamesIn, partClassName, resolveInputs, setInputOverride, collectContextSlotKeys, withContextSlots, compilePageStyle, nodeUsesContext, resolveBoundValue, IS_PREVIEW, planDataSource, dataSourcePlanToRequest, normalizeCollectionResult, resolveRepeaterItems, getByPath, computeRelative, IoxPageModule, PALETTE_PRESETS, elementStyleById, PART_CLASS_NAMES } from '@vectoriox/iox-ui';
|
|
19
|
+
import { VALIDATION_STATES as VALIDATION_STATES$1, VALIDATION_STATE_CLASS as VALIDATION_STATE_CLASS$1, DEFAULT_WIDTH_BY_TYPE, COLOR_SCHEME_TRAIT, COLOR_SCHEMES, ColorScheme, Breakpoint, BREAKPOINTS, countOverrides, setOverride, NEW_NODE_STYLE_DEFAULTS, defaultElementStyleFor, applyElementStyle, stripSuppressedProps, DeclarationContext, buildPartSelector, planResponsiveRules, resolveStyleProps, buildTargetedStateSelector, statePseudoClass, compileTokenCssBlocks, catalogFontId, fontsForTokens, GOOGLE_FONTS_FILE_ORIGIN, googleFontHref, compileDeclarations, wrapInBreakpointQuery, BreakpointQueryKind, breakpointDef, breakpointForWidth, FormRenderState, safeSetInput, ContextStore, ComponentInstanceRegistryService, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, declaredInputNames, emptyImagePlaceholder, syncNodeDesignClasses, composeVirtualTraits, withDefaultHoverTransition, stateNamesIn, partClassName, resolveInputs, setInputOverride, collectContextSlotKeys, withContextSlots, compilePageStyle, nodeUsesContext, resolveBoundValue, IS_PREVIEW, planDataSource, dataSourcePlanToRequest, normalizeCollectionResult, resolveRepeaterItems, getByPath, computeRelative, LinkType, buttonBlockHref, IoxPageModule, PALETTE_PRESETS, elementStyleById, PART_CLASS_NAMES } from '@vectoriox/iox-ui';
|
|
20
20
|
export { ComponentInstanceRegistryService, VIRTUAL_TRAIT_KEYS, composeVirtualTraits } from '@vectoriox/iox-ui';
|
|
21
21
|
import Lenis from 'lenis';
|
|
22
22
|
import { Subject, BehaviorSubject, of, forkJoin } from 'rxjs';
|
|
@@ -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 (
|
|
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
|
-
|
|
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',
|
|
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
|
|
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
|
-
|
|
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
|
}
|
|
@@ -8697,7 +8864,10 @@ class BuilderButtonBlockComponent {
|
|
|
8697
8864
|
this.children = [];
|
|
8698
8865
|
this.style = {};
|
|
8699
8866
|
this.tag = 'button';
|
|
8867
|
+
this.linkType = LinkType.External;
|
|
8700
8868
|
this.href = '';
|
|
8869
|
+
/** A page link shows its route as authored, as the canvas Link does (the engine resolves it). */
|
|
8870
|
+
this.pageRoute = '';
|
|
8701
8871
|
this.target = '_self';
|
|
8702
8872
|
this.type = 'button';
|
|
8703
8873
|
this.nodeId = '';
|
|
@@ -8706,6 +8876,9 @@ class BuilderButtonBlockComponent {
|
|
|
8706
8876
|
this.childMouseEnter = new EventEmitter();
|
|
8707
8877
|
this.childMouseLeave = new EventEmitter();
|
|
8708
8878
|
}
|
|
8879
|
+
get resolvedHref() {
|
|
8880
|
+
return buttonBlockHref(this);
|
|
8881
|
+
}
|
|
8709
8882
|
ngOnInit() {
|
|
8710
8883
|
// Register the HOST as the dropzone (was the ioxDropzone directive on the inner button).
|
|
8711
8884
|
this.dragEngine.registerDropzone(this.dropListId, this.elRef.nativeElement, this.children, this.cdr, (evt) => this.onDrop(evt));
|
|
@@ -8717,7 +8890,7 @@ class BuilderButtonBlockComponent {
|
|
|
8717
8890
|
this.dragEngine.handleDrop(this.children, event, this.dropListId, this.cdr);
|
|
8718
8891
|
}
|
|
8719
8892
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: BuilderButtonBlockComponent, deps: [{ token: i0.ElementRef }, { token: DragEngineService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8720
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.31", type: BuilderButtonBlockComponent, isStandalone: false, selector: "button[iox-button-block],a[iox-button-block]", inputs: { children: "children", style: "style", tag: "tag", href: "href", target: "target", type: "type", nodeId: "nodeId", dropListId: "dropListId" }, outputs: { childClick: "childClick", childMouseEnter: "childMouseEnter", childMouseLeave: "childMouseLeave" }, host: { properties: { "class": "'button-block-root iox-node-' + nodeId", "attr.href": "
|
|
8893
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.31", type: BuilderButtonBlockComponent, isStandalone: false, selector: "button[iox-button-block],a[iox-button-block]", inputs: { children: "children", style: "style", tag: "tag", linkType: "linkType", href: "href", pageRoute: "pageRoute", target: "target", type: "type", nodeId: "nodeId", dropListId: "dropListId" }, outputs: { childClick: "childClick", childMouseEnter: "childMouseEnter", childMouseLeave: "childMouseLeave" }, host: { properties: { "class": "'button-block-root iox-node-' + nodeId", "attr.href": "resolvedHref", "attr.target": "tag === 'a' ? (target || null) : null", "attr.type": "tag === 'a' ? null : type" } }, ngImport: i0, template: `
|
|
8721
8894
|
<span *ngIf="!children.length" class="button-block-placeholder" data-iox-chrome>Button</span>
|
|
8722
8895
|
|
|
8723
8896
|
<!-- WRAPPER COLLAPSE (task 9c): render children directly into the host; iox-outer + drag land
|
|
@@ -8747,7 +8920,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
|
|
|
8747
8920
|
</ng-container>
|
|
8748
8921
|
`, host: {
|
|
8749
8922
|
'[class]': "'button-block-root iox-node-' + nodeId",
|
|
8750
|
-
'[attr.href]':
|
|
8923
|
+
'[attr.href]': 'resolvedHref',
|
|
8751
8924
|
'[attr.target]': "tag === 'a' ? (target || null) : null",
|
|
8752
8925
|
'[attr.type]': "tag === 'a' ? null : type",
|
|
8753
8926
|
}, standalone: false, styles: [":host{box-sizing:border-box}.button-block-placeholder{flex:1;display:flex;align-items:center;justify-content:center;color:#64748b66;font-size:12px;pointer-events:none;-webkit-user-select:none;user-select:none}:host ::ng-deep>[class*=iox-outer-]{min-width:0}\n"] }]
|
|
@@ -8757,8 +8930,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
|
|
|
8757
8930
|
type: Input
|
|
8758
8931
|
}], tag: [{
|
|
8759
8932
|
type: Input
|
|
8933
|
+
}], linkType: [{
|
|
8934
|
+
type: Input
|
|
8760
8935
|
}], href: [{
|
|
8761
8936
|
type: Input
|
|
8937
|
+
}], pageRoute: [{
|
|
8938
|
+
type: Input
|
|
8762
8939
|
}], target: [{
|
|
8763
8940
|
type: Input
|
|
8764
8941
|
}], type: [{
|
|
@@ -10207,66 +10384,6 @@ function matchesControlSearch(name, label, term) {
|
|
|
10207
10384
|
return name.toLowerCase().includes(needle) || (label ?? '').toLowerCase().includes(needle);
|
|
10208
10385
|
}
|
|
10209
10386
|
|
|
10210
|
-
const FORM_PRESETS = Object.freeze([
|
|
10211
|
-
{
|
|
10212
|
-
id: 'contact', name: 'Contact', icon: 'ph-thin ph-envelope-simple',
|
|
10213
|
-
description: 'Name, email and a message. Stored, and emailed to you.',
|
|
10214
|
-
formName: 'Contact form',
|
|
10215
|
-
fields: [
|
|
10216
|
-
{ label: 'Full name', control: 'text', required: true },
|
|
10217
|
-
{ label: 'Email', control: 'email', required: true },
|
|
10218
|
-
{ label: 'Message', control: 'textarea', required: true, rows: 5 },
|
|
10219
|
-
],
|
|
10220
|
-
submitLabel: 'Send message',
|
|
10221
|
-
successText: 'Thanks — we’ll be in touch shortly.',
|
|
10222
|
-
errorText: 'We couldn’t send your message. Please try again — your answers are still here.',
|
|
10223
|
-
},
|
|
10224
|
-
{
|
|
10225
|
-
id: 'newsletter', name: 'Newsletter', icon: 'ph-thin ph-paper-plane-tilt',
|
|
10226
|
-
description: 'One email field plus consent. Stored.',
|
|
10227
|
-
formName: 'Newsletter signup',
|
|
10228
|
-
fields: [
|
|
10229
|
-
{ label: 'Email', control: 'email', required: true, placeholder: 'you@company.com' },
|
|
10230
|
-
// Consent is a checkbox that must be TICKED — `required` on a checkbox becomes the
|
|
10231
|
-
// `accepted` rule, with its own message, rather than a generic "this is required".
|
|
10232
|
-
{ label: 'Email me occasional updates', control: 'checkbox', required: true },
|
|
10233
|
-
],
|
|
10234
|
-
submitLabel: 'Subscribe',
|
|
10235
|
-
successText: 'You’re on the list.',
|
|
10236
|
-
errorText: 'We couldn’t sign you up. Please try again.',
|
|
10237
|
-
},
|
|
10238
|
-
{
|
|
10239
|
-
id: 'application', name: 'Application', icon: 'ph-thin ph-read-cv-logo',
|
|
10240
|
-
description: 'Details plus a CV upload. Stored, with an auto-reply.',
|
|
10241
|
-
formName: 'Application',
|
|
10242
|
-
fields: [
|
|
10243
|
-
{ label: 'Full name', control: 'text', required: true },
|
|
10244
|
-
{ label: 'Email', control: 'email', required: true },
|
|
10245
|
-
{ label: 'Phone', control: 'tel' },
|
|
10246
|
-
{ label: 'Role', control: 'select', required: true, optionsText: 'Design\nEngineering\nOther' },
|
|
10247
|
-
{ label: 'CV', control: 'file', required: true, help: 'PDF or Word, up to 5MB' },
|
|
10248
|
-
{ label: 'Anything else', control: 'textarea', rows: 4 },
|
|
10249
|
-
],
|
|
10250
|
-
submitLabel: 'Apply',
|
|
10251
|
-
successText: 'Application received — we’ll read it properly and reply.',
|
|
10252
|
-
errorText: 'We couldn’t submit your application. Please try again.',
|
|
10253
|
-
},
|
|
10254
|
-
{
|
|
10255
|
-
id: 'booking', name: 'Booking request', icon: 'ph-thin ph-calendar-check',
|
|
10256
|
-
description: 'Date and party size. Stored, and it notifies you.',
|
|
10257
|
-
formName: 'Booking request',
|
|
10258
|
-
fields: [
|
|
10259
|
-
{ label: 'Name', control: 'text', required: true },
|
|
10260
|
-
{ label: 'Email', control: 'email', required: true },
|
|
10261
|
-
{ label: 'Date', control: 'date', required: true },
|
|
10262
|
-
{ label: 'People', control: 'number', required: true },
|
|
10263
|
-
{ label: 'Notes', control: 'textarea', rows: 3, help: 'Allergies, access needs, anything useful' },
|
|
10264
|
-
],
|
|
10265
|
-
submitLabel: 'Request a booking',
|
|
10266
|
-
successText: 'Request received — we’ll confirm by email.',
|
|
10267
|
-
errorText: 'We couldn’t send your request. Please try again.',
|
|
10268
|
-
},
|
|
10269
|
-
]);
|
|
10270
10387
|
/**
|
|
10271
10388
|
* Build a preset's node tree.
|
|
10272
10389
|
*
|
|
@@ -10659,11 +10776,9 @@ function text(content, createConfig, styleId, styles = {}) {
|
|
|
10659
10776
|
wearing(node, styleId);
|
|
10660
10777
|
return styled(node, { padding: '0', margin: '0', ...styles });
|
|
10661
10778
|
}
|
|
10662
|
-
/** A button's label takes the BUTTON's type (
|
|
10663
|
-
|
|
10664
|
-
|
|
10665
|
-
letterSpacing: 'inherit', textTransform: 'inherit',
|
|
10666
|
-
};
|
|
10779
|
+
/** A button's label takes the BUTTON's type (size, weight, case, tracking) and ink — the contract's
|
|
10780
|
+
* `button-label` element style, the one the site composer gives a composed button's label too (W1.1). */
|
|
10781
|
+
const BUTTON_LABEL_STYLE = 'button-label';
|
|
10667
10782
|
function withChildren(node, children) {
|
|
10668
10783
|
if (!node || children.some(c => !c))
|
|
10669
10784
|
return null;
|
|
@@ -10689,7 +10804,7 @@ function button(styleId, label, createConfig, styles = {}) {
|
|
|
10689
10804
|
return null;
|
|
10690
10805
|
setTrait(btn, 'tag', 'button');
|
|
10691
10806
|
styled(btn, styles);
|
|
10692
|
-
return withChildren(btn, [text(label, createConfig,
|
|
10807
|
+
return withChildren(btn, [text(label, createConfig, BUTTON_LABEL_STYLE)]);
|
|
10693
10808
|
}
|
|
10694
10809
|
/** A Container sized by its content: the palette Container's 200px drop-zone min-height is canvas
|
|
10695
10810
|
* furniture, and a specimen has no drop zone. */
|
|
@@ -10863,6 +10978,10 @@ class BuilderButtonComponentConfig extends ComponentConfig {
|
|
|
10863
10978
|
}
|
|
10864
10979
|
}
|
|
10865
10980
|
|
|
10981
|
+
const BUTTON_LINK_TYPES = [
|
|
10982
|
+
{ label: 'External URL', value: LinkType.External },
|
|
10983
|
+
{ label: 'Website Page', value: LinkType.Page },
|
|
10984
|
+
];
|
|
10866
10985
|
class ButtonBlockComponentConfig extends ComponentConfig {
|
|
10867
10986
|
constructor() {
|
|
10868
10987
|
super('ButtonBlock', 'app-builder-button-block', 'ph-thin ph-cursor-click', 'Basic');
|
|
@@ -10871,13 +10990,21 @@ class ButtonBlockComponentConfig extends ComponentConfig {
|
|
|
10871
10990
|
this.children = [];
|
|
10872
10991
|
this.inputs = {
|
|
10873
10992
|
tag: 'select',
|
|
10993
|
+
linkType: 'select',
|
|
10874
10994
|
href: 'text',
|
|
10995
|
+
pageRoute: 'text',
|
|
10875
10996
|
target: 'select',
|
|
10876
10997
|
type: 'select',
|
|
10877
10998
|
};
|
|
10999
|
+
// A link button points at a URL or at a website PAGE by route — the Link's two link types
|
|
11000
|
+
// (W1.1): a page route is shown as the route here and resolved base-relative by the engine.
|
|
11001
|
+
// `href` / `pageRoute` depend on `linkType`, which depends on `tag`: a trait whose controlling
|
|
11002
|
+
// trait is hidden is hidden too, so a <button> shows neither.
|
|
10878
11003
|
this.traits = [
|
|
10879
11004
|
new TraitConfig('tag', 'Tag', TraitInputType.Select, ['button', 'a'], 'button'),
|
|
10880
|
-
new TraitConfig('
|
|
11005
|
+
new TraitConfig('linkType', 'Link Type', TraitInputType.Select, BUTTON_LINK_TYPES, LinkType.External, false, { trait: 'tag', values: 'a' }),
|
|
11006
|
+
new TraitConfig('href', 'URL', TraitInputType.Text, undefined, '', false, { trait: 'linkType', values: LinkType.External }),
|
|
11007
|
+
new TraitConfig('pageRoute', 'Page Route', TraitInputType.Text, undefined, '', false, { trait: 'linkType', values: LinkType.Page }),
|
|
10881
11008
|
new TraitConfig('target', 'Target', TraitInputType.Select, ['_self', '_blank'], '_self', false, { trait: 'tag', values: 'a' }),
|
|
10882
11009
|
new TraitConfig('type', 'Type', TraitInputType.Select, ['button', 'submit', 'reset'], 'button', false, { trait: 'tag', values: 'button' }),
|
|
10883
11010
|
];
|
|
@@ -11676,5 +11803,5 @@ class BuilderSpacerComponentConfig extends ComponentConfig {
|
|
|
11676
11803
|
* Generated bundle index. Do not edit.
|
|
11677
11804
|
*/
|
|
11678
11805
|
|
|
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,
|
|
11806
|
+
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_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
11807
|
//# sourceMappingURL=vectoriox-iox-builder.mjs.map
|