@vectoriox/iox-builder 1.7.6 → 1.7.8
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.
|
@@ -1283,9 +1283,13 @@ class InteractionEngineService {
|
|
|
1283
1283
|
}
|
|
1284
1284
|
/** Wire all interactions for a node to its rendered DOM element. */
|
|
1285
1285
|
attach(node) { this.runOutside(() => this._attach(node)); }
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1286
|
+
/** Attach interactions to a node whose element is NOT overlay-registered — e.g. repeater/slider
|
|
1287
|
+
* PREVIEW CLONES, which are non-selectable but must still animate (parity with the SSR runtime,
|
|
1288
|
+
* which animates every repeated row). See RenderDirective. */
|
|
1289
|
+
attachTo(node, element) { this.runOutside(() => this._attach(node, element)); }
|
|
1290
|
+
_attach(node, elementOverride) {
|
|
1291
|
+
const element = elementOverride ?? this.overlayService.getNodeRef(node)?.element;
|
|
1292
|
+
if (!element)
|
|
1289
1293
|
return;
|
|
1290
1294
|
const inBuilder = this.isBuilderMode();
|
|
1291
1295
|
// Apply any pre-states queued for THIS element by earlier-registered nodes.
|
|
@@ -1294,7 +1298,7 @@ class InteractionEngineService {
|
|
|
1294
1298
|
const pending = this.pendingPreStates.get(node.id);
|
|
1295
1299
|
if (pending) {
|
|
1296
1300
|
for (const action of pending) {
|
|
1297
|
-
this.applyPreState(
|
|
1301
|
+
this.applyPreState(element, action);
|
|
1298
1302
|
}
|
|
1299
1303
|
this.pendingPreStates.delete(node.id);
|
|
1300
1304
|
}
|
|
@@ -1312,7 +1316,7 @@ class InteractionEngineService {
|
|
|
1312
1316
|
if (!skipPreState) {
|
|
1313
1317
|
for (const action of ix.actions) {
|
|
1314
1318
|
if (this.ENTRANCE_TYPES.has(action.type)) {
|
|
1315
|
-
const target = this.resolveTarget(node, action);
|
|
1319
|
+
const target = this.resolveTarget(node, action, element);
|
|
1316
1320
|
if (target) {
|
|
1317
1321
|
this.applyPreState(target, action);
|
|
1318
1322
|
}
|
|
@@ -1324,7 +1328,7 @@ class InteractionEngineService {
|
|
|
1324
1328
|
}
|
|
1325
1329
|
}
|
|
1326
1330
|
}
|
|
1327
|
-
const cleanup = this.attachInteraction(node, ix,
|
|
1331
|
+
const cleanup = this.attachInteraction(node, ix, element);
|
|
1328
1332
|
cleanups.push({ node, interaction: ix, cleanup });
|
|
1329
1333
|
}
|
|
1330
1334
|
this.attached.set(node, cleanups);
|
|
@@ -1628,10 +1632,9 @@ class InteractionEngineService {
|
|
|
1628
1632
|
break;
|
|
1629
1633
|
}
|
|
1630
1634
|
}
|
|
1631
|
-
resolveTarget(ownerNode, action) {
|
|
1632
|
-
if (action.target === 'self') {
|
|
1633
|
-
|
|
1634
|
-
return ref?.element ?? null;
|
|
1635
|
+
resolveTarget(ownerNode, action, selfEl) {
|
|
1636
|
+
if (action.target === 'self' || !action.target) {
|
|
1637
|
+
return selfEl ?? this.overlayService.getNodeRef(ownerNode)?.element ?? null;
|
|
1635
1638
|
}
|
|
1636
1639
|
// Find by node ID
|
|
1637
1640
|
for (const [n, ref] of this.overlayService.getAllNodeEntries()) {
|
|
@@ -2608,6 +2611,8 @@ class MoveableCanvasService {
|
|
|
2608
2611
|
this.sourceId = this.resolveDropzoneId(element);
|
|
2609
2612
|
this.ensureMoveable();
|
|
2610
2613
|
this.moveable.target = element;
|
|
2614
|
+
// Snap against sibling/cousin nodes (not self, ancestors, or own descendants).
|
|
2615
|
+
this.moveable.elementGuidelines = this.guidelines(element);
|
|
2611
2616
|
// Drag is initiated by grabbing the ELEMENT itself (like interact.js) — not a Moveable
|
|
2612
2617
|
// overlay area — so a plain click still passes through to selection/child-selection and only
|
|
2613
2618
|
// a movement past the threshold starts a drag. Keeps Moveable visually silent (no hit-area box).
|
|
@@ -2616,6 +2621,14 @@ class MoveableCanvasService {
|
|
|
2616
2621
|
}
|
|
2617
2622
|
/** Recompute the control-box position (call on scroll/zoom/layout changes). */
|
|
2618
2623
|
refresh() { this.moveable?.updateRect(); }
|
|
2624
|
+
/** Snap guideline elements for `target`: every node host except itself, its ancestors, and its
|
|
2625
|
+
* own descendants — i.e. siblings/cousins to align edges/centers against. */
|
|
2626
|
+
guidelines(target) {
|
|
2627
|
+
if (!this.container)
|
|
2628
|
+
return [];
|
|
2629
|
+
const all = Array.from(this.container.querySelectorAll('[class*="iox-node-"]'));
|
|
2630
|
+
return all.filter(el => el !== target && !target.contains(el) && !el.contains(target));
|
|
2631
|
+
}
|
|
2619
2632
|
destroy() { this.destroyMoveable(); this.container = null; }
|
|
2620
2633
|
// ── internals ────────────────────────────────────────────────────────────
|
|
2621
2634
|
ensureMoveable() {
|
|
@@ -2635,6 +2648,14 @@ class MoveableCanvasService {
|
|
|
2635
2648
|
hideDefaultLines: true,
|
|
2636
2649
|
renderDirections: ['nw', 'ne', 'sw', 'se'],
|
|
2637
2650
|
className: 'iox-moveable-silent',
|
|
2651
|
+
// Snap-to-guides: align edges/centers to sibling elements while dragging/resizing, with
|
|
2652
|
+
// the distance readout. elementGuidelines are refreshed per selection (see select()).
|
|
2653
|
+
snappable: true,
|
|
2654
|
+
snapThreshold: 6,
|
|
2655
|
+
snapDirections: { top: true, left: true, bottom: true, right: true, center: true, middle: true },
|
|
2656
|
+
elementSnapDirections: { top: true, left: true, bottom: true, right: true, center: true, middle: true },
|
|
2657
|
+
snapGap: true,
|
|
2658
|
+
isDisplaySnapDigit: true,
|
|
2638
2659
|
});
|
|
2639
2660
|
this.moveable.on('dragStart', (e) => {
|
|
2640
2661
|
// Never start a drag from inside inline text editing.
|
|
@@ -3060,10 +3081,19 @@ class RenderDirective {
|
|
|
3060
3081
|
this.styleRegistry.upsert(cssId, composedBase);
|
|
3061
3082
|
// Restore per-state overrides saved from a previous session.
|
|
3062
3083
|
if (node.stateStyles) {
|
|
3084
|
+
const childEl = componentRef.location?.nativeElement;
|
|
3063
3085
|
for (const state of Object.keys(node.stateStyles)) {
|
|
3064
3086
|
const stateMap = node.stateStyles[state];
|
|
3065
3087
|
if (stateMap && Object.keys(stateMap).length) {
|
|
3066
|
-
|
|
3088
|
+
// Relational states (parent-hover) compile to
|
|
3089
|
+
// `.iox-node-{parentId}:hover .iox-node-{childId}` — they need the CURRENT parent id.
|
|
3090
|
+
// Resolve it from the DOM on EVERY render, so the rule is correct on load AND after a
|
|
3091
|
+
// cross-container move (which re-renders the child with a fresh instance). (issue #1)
|
|
3092
|
+
let relativeToId;
|
|
3093
|
+
if (RELATIONAL_STATES.includes(state) && childEl?.parentElement) {
|
|
3094
|
+
relativeToId = this.findParentNodeElement(childEl.parentElement)?.node?.id;
|
|
3095
|
+
}
|
|
3096
|
+
this.styleRegistry.upsert(cssId, composeVirtualTraits(stateMap, initialStyle), state, relativeToId);
|
|
3067
3097
|
}
|
|
3068
3098
|
}
|
|
3069
3099
|
}
|
|
@@ -3158,11 +3188,15 @@ class RenderDirective {
|
|
|
3158
3188
|
this.overlayService.triggerContextMenu(event.clientX, event.clientY);
|
|
3159
3189
|
});
|
|
3160
3190
|
}
|
|
3161
|
-
// Wire up interactions (animations) after the element is in the DOM.
|
|
3162
|
-
//
|
|
3191
|
+
// Wire up interactions (animations) after the element is in the DOM. Preview clones are
|
|
3192
|
+
// non-selectable (not overlay-registered) but MUST still animate — parity with the SSR
|
|
3193
|
+
// runtime, which animates every repeated row — so attach by element (issue #2).
|
|
3163
3194
|
if (!isPreviewClone) {
|
|
3164
3195
|
this.interactionEngine.attach(node);
|
|
3165
3196
|
}
|
|
3197
|
+
else if (nativeEl) {
|
|
3198
|
+
this.interactionEngine.attachTo(node, nativeEl);
|
|
3199
|
+
}
|
|
3166
3200
|
}
|
|
3167
3201
|
/** Try setInput(); fall back to direct assignment for non-@Input properties. */
|
|
3168
3202
|
safeSetInput(ref, name, value) {
|