@vectoriox/iox-builder 1.4.72 → 1.4.74

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.
@@ -1431,31 +1431,73 @@ class InteractionEngineService {
1431
1431
  }
1432
1432
  attachViewportEnter(node, ix, element) {
1433
1433
  const firedSet = new Set();
1434
- // In builder mode, use .preview-scroll as root so IntersectionObserver fires
1435
- // against the canvas clipping container (scrollTop-driven) rather than the browser viewport.
1436
- const root = this.overlayService.getIntersectionRoot() ?? null;
1437
- const observer = new IntersectionObserver((entries) => {
1438
- for (const entry of entries) {
1439
- if (entry.isIntersecting) {
1440
- const key = ix.id;
1434
+ const fire = () => {
1435
+ const key = ix.id;
1436
+ if (ix.actions.some(a => a.once) && firedSet.has(key))
1437
+ return;
1438
+ if (this.hasPreStatedAncestor(element)) {
1439
+ this.deferUntilVisible(element, () => {
1441
1440
  if (ix.actions.some(a => a.once) && firedSet.has(key))
1442
- continue;
1443
- // Element is in viewport but may be inside a still-fading parent.
1444
- // Defer until all pre-stated ancestors finish their entrance animation.
1445
- if (this.hasPreStatedAncestor(element)) {
1446
- this.deferUntilVisible(element, () => {
1447
- if (ix.actions.some(a => a.once) && firedSet.has(key))
1448
- return;
1449
- firedSet.add(key);
1450
- this.runActions(node, ix.actions);
1451
- });
1452
- continue;
1453
- }
1441
+ return;
1454
1442
  firedSet.add(key);
1455
1443
  this.runActions(node, ix.actions);
1444
+ });
1445
+ return;
1446
+ }
1447
+ firedSet.add(key);
1448
+ this.runActions(node, ix.actions);
1449
+ };
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.
1494
+ const observer = new IntersectionObserver((entries) => {
1495
+ for (const entry of entries) {
1496
+ if (entry.isIntersecting) {
1497
+ fire();
1456
1498
  }
1457
1499
  }
1458
- }, { threshold: 0.1, root });
1500
+ }, { threshold: 0.1 });
1459
1501
  observer.observe(element);
1460
1502
  return () => observer.disconnect();
1461
1503
  }
@@ -1618,7 +1660,11 @@ class InteractionEngineService {
1618
1660
  // Mark as hidden so the builder overlay and drag engine can ignore this element
1619
1661
  // and all of its children regardless of which entrance animation type is used.
1620
1662
  element.setAttribute('data-iox-hidden', '');
1621
- element.style.setProperty('pointer-events', 'none');
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
+ }
1622
1668
  this.preStatedElements.add(element);
1623
1669
  }
1624
1670
  clearInlineAnimationStyles(element) {