@vectoriox/iox-builder 1.4.73 → 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,44 +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
- const checkAndFire = () => { if (isInCanvasViewport())
1462
- fire(); };
1463
- // Initial check — defer one animation frame so layout is settled after render.
1464
- const rafId = requestAnimationFrame(checkAndFire);
1465
- // Lenis changes scrollTop on .preview-scroll → native scroll event fires.
1466
- // Builder also dispatches a synthetic scroll on .preview-lenis-content.
1467
- // Listen on both so we catch all scroll notifications.
1468
- const lenisEl = this.overlayService.getScrollContainer();
1469
- canvasRoot.addEventListener('scroll', checkAndFire, { passive: true });
1470
- if (lenisEl)
1471
- lenisEl.addEventListener('scroll', checkAndFire, { passive: true });
1472
- return () => {
1473
- cancelAnimationFrame(rafId);
1474
- canvasRoot.removeEventListener('scroll', checkAndFire);
1475
- if (lenisEl)
1476
- lenisEl.removeEventListener('scroll', checkAndFire);
1477
- };
1478
- }
1479
- // 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.
1480
1453
  const observer = new IntersectionObserver((entries) => {
1481
1454
  for (const entry of entries) {
1482
1455
  if (entry.isIntersecting) {
1456
+ observer.disconnect();
1483
1457
  fire();
1484
1458
  }
1485
1459
  }
1486
1460
  }, { threshold: 0.1 });
1487
1461
  observer.observe(element);
1488
- 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
+ };
1489
1496
  }
1490
1497
  isBuilderMode() {
1491
1498
  return document.body.classList.contains('builder-active');
@@ -2756,9 +2763,13 @@ class RenderDirective {
2756
2763
  nativeEl.addEventListener('mouseenter', (event) => {
2757
2764
  if (event._fromClick)
2758
2765
  return;
2766
+ if (nativeEl.hasAttribute('data-iox-hidden'))
2767
+ return;
2759
2768
  this.onMouseEnter.emit({ element: nativeEl, node });
2760
2769
  });
2761
2770
  nativeEl.addEventListener('mouseleave', (event) => {
2771
+ if (nativeEl.hasAttribute('data-iox-hidden'))
2772
+ return;
2762
2773
  // When leaving a child component, check if the mouse moved to a
2763
2774
  // parent component (e.g. card → section). If so, emit mouseenter
2764
2775
  // for that parent instead of clearing hover entirely.
@@ -2774,12 +2785,15 @@ class RenderDirective {
2774
2785
  });
2775
2786
  nativeEl.addEventListener('click', (event) => {
2776
2787
  event.stopPropagation();
2788
+ if (nativeEl.hasAttribute('data-iox-hidden'))
2789
+ return;
2777
2790
  this.onClick.emit({ element: nativeEl, node, componentRef });
2778
2791
  });
2779
2792
  nativeEl.addEventListener('contextmenu', (event) => {
2780
2793
  event.preventDefault();
2781
2794
  event.stopPropagation();
2782
- // Select the element first, then open the more menu at cursor position.
2795
+ if (nativeEl.hasAttribute('data-iox-hidden'))
2796
+ return;
2783
2797
  this.onClick.emit({ element: nativeEl, node, componentRef });
2784
2798
  this.overlayService.triggerContextMenu(event.clientX, event.clientY);
2785
2799
  });