diff --git a/.gitignore b/.gitignore index 9d4e681c5..54bd8e244 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,6 @@ CLAUDE.md *.pyc .vite reports/ + +# Local iPad dev setup (proxy) +/local-dev/ diff --git a/libs/ui/layout/src/lib/close-on-scroll.directive.ts b/libs/ui/layout/src/lib/close-on-scroll.directive.ts index 6bf5594fc..566a977e4 100644 --- a/libs/ui/layout/src/lib/close-on-scroll.directive.ts +++ b/libs/ui/layout/src/lib/close-on-scroll.directive.ts @@ -84,8 +84,11 @@ export class CloseOnScrollDirective implements OnDestroy { } this.#isActive = true; - // Delay listener registration to next frame to skip any stale scroll events - this.#pendingActivation = requestAnimationFrame(() => { + // Delay listener registration to skip scroll events caused by: + // 1. Stale scroll events from before activation + // 2. iOS Safari's automatic "scroll into view" when focusing elements + // Using setTimeout with 100ms to ensure iOS scroll-into-view completes + this.#pendingActivation = window.setTimeout(() => { this.#scrollListener = (event: Event) => { const excludeElement = this.closeOnScrollExclude(); if (excludeElement?.contains(event.target as HTMLElement)) { @@ -101,12 +104,12 @@ export class CloseOnScrollDirective implements OnDestroy { { capture: true, passive: true }, ); this.#logger.debug('Activated scroll listener'); - }); + }, 100); } #deactivate(): void { if (this.#pendingActivation) { - cancelAnimationFrame(this.#pendingActivation); + clearTimeout(this.#pendingActivation); this.#pendingActivation = undefined; }