@vectoriox/iox-builder 1.4.74 → 1.4.75

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.
@@ -1448,58 +1448,51 @@ class InteractionEngineService {
1448
1448
  this.runActions(node, ix.actions);
1449
1449
  };
1450
1450
  const canvasRoot = this.overlayService.getIntersectionRoot();
1451
- if (canvasRoot) {
1452
- // Builder mode: IntersectionObserver with a scaled/transformed canvas root is unreliable.
1453
- // Use getBoundingClientRect against the canvas clipping container instead.
1454
- const isInCanvasViewport = () => {
1455
- const rootRect = canvasRoot.getBoundingClientRect();
1456
- const elRect = element.getBoundingClientRect();
1457
- const overlapH = Math.min(elRect.bottom, rootRect.bottom) - Math.max(elRect.top, rootRect.top);
1458
- const elH = elRect.bottom - elRect.top;
1459
- return elH > 0 && overlapH / elH >= 0.1;
1460
- };
1461
- let fired = false;
1462
- const checkAndFire = () => {
1463
- if (fired)
1464
- return;
1465
- if (isInCanvasViewport()) {
1466
- fired = true;
1467
- fire();
1468
- }
1469
- };
1470
- // Initial checks: a single RAF fires before deeply-nested Angular components
1471
- // have settled their layout. Schedule retries so above-fold elements are
1472
- // detected even when render takes a few frames.
1473
- const rafId = requestAnimationFrame(checkAndFire);
1474
- const t1 = window.setTimeout(checkAndFire, 100);
1475
- const t2 = window.setTimeout(checkAndFire, 400);
1476
- // Lenis changes scrollTop on .preview-scroll → native scroll event fires.
1477
- // Builder also dispatches a synthetic scroll on .preview-lenis-content.
1478
- // Listen on both so we catch all scroll notifications.
1479
- const lenisEl = this.overlayService.getScrollContainer();
1480
- canvasRoot.addEventListener('scroll', checkAndFire, { passive: true });
1481
- if (lenisEl)
1482
- lenisEl.addEventListener('scroll', checkAndFire, { passive: true });
1483
- return () => {
1484
- fired = true; // stop any pending checks
1485
- cancelAnimationFrame(rafId);
1486
- clearTimeout(t1);
1487
- clearTimeout(t2);
1488
- canvasRoot.removeEventListener('scroll', checkAndFire);
1489
- if (lenisEl)
1490
- lenisEl.removeEventListener('scroll', checkAndFire);
1491
- };
1492
- }
1493
- // Production mode: standard IntersectionObserver against browser viewport.
1451
+ // IntersectionObserver (browser viewport, no root) fires immediately for elements
1452
+ // already visible in the canvas frame. Works for initial above-fold detection.
1494
1453
  const observer = new IntersectionObserver((entries) => {
1495
1454
  for (const entry of entries) {
1496
1455
  if (entry.isIntersecting) {
1456
+ observer.disconnect();
1497
1457
  fire();
1498
1458
  }
1499
1459
  }
1500
1460
  }, { threshold: 0.1 });
1501
1461
  observer.observe(element);
1502
- return () => observer.disconnect();
1462
+ if (!canvasRoot) {
1463
+ // Production mode: IntersectionObserver alone is sufficient.
1464
+ return () => observer.disconnect();
1465
+ }
1466
+ // Builder mode: Lenis scrolls .preview-scroll via scrollTop on an overflow:hidden
1467
+ // element. Browsers may not re-trigger IntersectionObserver when scrollTop changes
1468
+ // on a non-scroll-container. Add a manual BoundingClientRect check on every Lenis
1469
+ // scroll frame (dispatched as a native 'scroll' event on .preview-lenis-content).
1470
+ const isInCanvasViewport = () => {
1471
+ const rootRect = canvasRoot.getBoundingClientRect();
1472
+ const elRect = element.getBoundingClientRect();
1473
+ const overlapH = Math.min(elRect.bottom, rootRect.bottom) - Math.max(elRect.top, rootRect.top);
1474
+ const elH = elRect.bottom - elRect.top;
1475
+ return elH > 0 && overlapH / elH >= 0.1;
1476
+ };
1477
+ let scrollFired = false;
1478
+ const onLenisScroll = () => {
1479
+ if (scrollFired)
1480
+ return;
1481
+ if (isInCanvasViewport()) {
1482
+ scrollFired = true;
1483
+ observer.disconnect();
1484
+ fire();
1485
+ }
1486
+ };
1487
+ const lenisEl = this.overlayService.getScrollContainer();
1488
+ if (lenisEl)
1489
+ lenisEl.addEventListener('scroll', onLenisScroll, { passive: true });
1490
+ return () => {
1491
+ scrollFired = true;
1492
+ observer.disconnect();
1493
+ if (lenisEl)
1494
+ lenisEl.removeEventListener('scroll', onLenisScroll);
1495
+ };
1503
1496
  }
1504
1497
  isBuilderMode() {
1505
1498
  return document.body.classList.contains('builder-active');
@@ -1660,11 +1653,7 @@ class InteractionEngineService {
1660
1653
  // Mark as hidden so the builder overlay and drag engine can ignore this element
1661
1654
  // and all of its children regardless of which entrance animation type is used.
1662
1655
  element.setAttribute('data-iox-hidden', '');
1663
- // In builder mode the overlay handles all pointer interaction — omit
1664
- // pointer-events:none so wheel events still reach Lenis and canvas scroll works.
1665
- if (!this.isBuilderMode()) {
1666
- element.style.setProperty('pointer-events', 'none');
1667
- }
1656
+ element.style.setProperty('pointer-events', 'none');
1668
1657
  this.preStatedElements.add(element);
1669
1658
  }
1670
1659
  clearInlineAnimationStyles(element) {
@@ -2774,9 +2763,13 @@ class RenderDirective {
2774
2763
  nativeEl.addEventListener('mouseenter', (event) => {
2775
2764
  if (event._fromClick)
2776
2765
  return;
2766
+ if (nativeEl.hasAttribute('data-iox-hidden'))
2767
+ return;
2777
2768
  this.onMouseEnter.emit({ element: nativeEl, node });
2778
2769
  });
2779
2770
  nativeEl.addEventListener('mouseleave', (event) => {
2771
+ if (nativeEl.hasAttribute('data-iox-hidden'))
2772
+ return;
2780
2773
  // When leaving a child component, check if the mouse moved to a
2781
2774
  // parent component (e.g. card → section). If so, emit mouseenter
2782
2775
  // for that parent instead of clearing hover entirely.
@@ -2792,12 +2785,15 @@ class RenderDirective {
2792
2785
  });
2793
2786
  nativeEl.addEventListener('click', (event) => {
2794
2787
  event.stopPropagation();
2788
+ if (nativeEl.hasAttribute('data-iox-hidden'))
2789
+ return;
2795
2790
  this.onClick.emit({ element: nativeEl, node, componentRef });
2796
2791
  });
2797
2792
  nativeEl.addEventListener('contextmenu', (event) => {
2798
2793
  event.preventDefault();
2799
2794
  event.stopPropagation();
2800
- // Select the element first, then open the more menu at cursor position.
2795
+ if (nativeEl.hasAttribute('data-iox-hidden'))
2796
+ return;
2801
2797
  this.onClick.emit({ element: nativeEl, node, componentRef });
2802
2798
  this.overlayService.triggerContextMenu(event.clientX, event.clientY);
2803
2799
  });