From c08e3b4b368d2d5cf3008bfeed85a9dcabf1fff5 Mon Sep 17 00:00:00 2001 From: Max Okorokov Date: Fri, 8 Nov 2019 11:56:26 +0100 Subject: [PATCH] fix(autoclose): fix popup components auto closing in Safari 13 (#3454) Don't use touch events anymore as everything works fine with `mouseup/down` Fixes #3446 Fixes #3437 Fixes #3412 Fixes #3192 Fixes #3145 Fixes #3024 --- .../demos/navbar/dropdown-navbar.html | 18 +++++++-------- src/util/autoclose.ts | 22 +++++++++++-------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/demo/src/app/components/dropdown/demos/navbar/dropdown-navbar.html b/demo/src/app/components/dropdown/demos/navbar/dropdown-navbar.html index 8fb36619d5..b833125680 100644 --- a/demo/src/app/components/dropdown/demos/navbar/dropdown-navbar.html +++ b/demo/src/app/components/dropdown/demos/navbar/dropdown-navbar.html @@ -37,9 +37,9 @@ Static @@ -48,9 +48,9 @@ Static right @@ -59,9 +59,9 @@ Dynamic diff --git a/src/util/autoclose.ts b/src/util/autoclose.ts index 8020fee1cb..cccc1b60f9 100644 --- a/src/util/autoclose.ts +++ b/src/util/autoclose.ts @@ -18,16 +18,20 @@ if (typeof navigator !== 'undefined') { iOS = !!navigator.userAgent && /iPad|iPhone|iPod/.test(navigator.userAgent); } +// setting 'ngbAutoClose' synchronously on iOS results in immediate popup closing +// when tapping on the triggering element +const wrapAsyncForiOS = fn => iOS ? () => setTimeout(() => fn(), 100) : fn; + export function ngbAutoClose( zone: NgZone, document: any, type: boolean | 'inside' | 'outside', close: () => void, closed$: Observable, insideElements: HTMLElement[], ignoreElements?: HTMLElement[], insideSelector?: string) { // closing on ESC and outside clicks if (type) { - zone.runOutsideAngular(() => { + zone.runOutsideAngular(wrapAsyncForiOS(() => { - const shouldCloseOnClick = (event: MouseEvent | TouchEvent) => { + const shouldCloseOnClick = (event: MouseEvent) => { const element = event.target as HTMLElement; - if ((event instanceof MouseEvent && event.button === 2) || isContainedIn(element, ignoreElements)) { + if (event.button === 2 || isContainedIn(element, ignoreElements)) { return false; } if (type === 'inside') { @@ -48,16 +52,16 @@ export function ngbAutoClose( // we have to pre-calculate 'shouldCloseOnClick' on 'mousedown/touchstart', // because on 'mouseup/touchend' DOM nodes might be detached - const mouseDowns$ = fromEvent(document, iOS ? 'touchstart' : 'mousedown') - .pipe(map(shouldCloseOnClick), takeUntil(closed$)); + const mouseDowns$ = + fromEvent(document, 'mousedown').pipe(map(shouldCloseOnClick), takeUntil(closed$)); - const closeableClicks$ = fromEvent(document, iOS ? 'touchend' : 'mouseup') + const closeableClicks$ = fromEvent(document, 'mouseup') .pipe( - withLatestFrom(mouseDowns$), filter(([_, shouldClose]) => shouldClose), - delay(iOS ? 16 : 0), takeUntil(closed$)) as Observable; + withLatestFrom(mouseDowns$), filter(([_, shouldClose]) => shouldClose), delay(0), + takeUntil(closed$)) as Observable; race([escapes$, closeableClicks$]).subscribe(() => zone.run(close)); - }); + })); } }