Skip to content

Commit

Permalink
fix(autoclose): fix popup components auto closing in Safari 13 (#3454)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
maxokorokov committed Nov 8, 2019
1 parent 9a92667 commit c08e3b4
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 18 deletions.
Expand Up @@ -37,9 +37,9 @@
Static
</a>
<div ngbDropdownMenu aria-labelledby="navbarDropdown1" class="dropdown-menu">
<a class="dropdown-item" href="#" (click)="$event.preventDefault()">Action</a>
<a class="dropdown-item" href="#" (click)="$event.preventDefault()">Another action</a>
<a class="dropdown-item" href="#" (click)="$event.preventDefault()">Something else here</a>
<a ngbDropdownItem href="#" (click)="$event.preventDefault()">Action</a>
<a ngbDropdownItem href="#" (click)="$event.preventDefault()">Another action</a>
<a ngbDropdownItem href="#" (click)="$event.preventDefault()">Something else here</a>
</div>
</li>

Expand All @@ -48,9 +48,9 @@
Static right
</a>
<div ngbDropdownMenu aria-labelledby="navbarDropdown2" class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="#" (click)="$event.preventDefault()">Action</a>
<a class="dropdown-item" href="#" (click)="$event.preventDefault()">Another action</a>
<a class="dropdown-item" href="#" (click)="$event.preventDefault()">Something else here</a>
<a ngbDropdownItem href="#" (click)="$event.preventDefault()">Action</a>
<a ngbDropdownItem href="#" (click)="$event.preventDefault()">Another action</a>
<a ngbDropdownItem href="#" (click)="$event.preventDefault()">Something else here</a>
</div>
</li>

Expand All @@ -59,9 +59,9 @@
Dynamic
</a>
<div ngbDropdownMenu aria-labelledby="navbarDropdown3" class="dropdown-menu">
<a class="dropdown-item" href="#" (click)="$event.preventDefault()">Action</a>
<a class="dropdown-item" href="#" (click)="$event.preventDefault()">Another action</a>
<a class="dropdown-item" href="#" (click)="$event.preventDefault()">Something else here</a>
<a ngbDropdownItem href="#" (click)="$event.preventDefault()">Action</a>
<a ngbDropdownItem href="#" (click)="$event.preventDefault()">Another action</a>
<a ngbDropdownItem href="#" (click)="$event.preventDefault()">Something else here</a>
</div>
</li>
</ul>
Expand Down
22 changes: 13 additions & 9 deletions src/util/autoclose.ts
Expand Up @@ -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<any>,
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') {
Expand All @@ -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<MouseEvent>(document, iOS ? 'touchstart' : 'mousedown')
.pipe(map(shouldCloseOnClick), takeUntil(closed$));
const mouseDowns$ =
fromEvent<MouseEvent>(document, 'mousedown').pipe(map(shouldCloseOnClick), takeUntil(closed$));

const closeableClicks$ = fromEvent<MouseEvent>(document, iOS ? 'touchend' : 'mouseup')
const closeableClicks$ = fromEvent<MouseEvent>(document, 'mouseup')
.pipe(
withLatestFrom(mouseDowns$), filter(([_, shouldClose]) => shouldClose),
delay(iOS ? 16 : 0), takeUntil(closed$)) as Observable<MouseEvent>;
withLatestFrom(mouseDowns$), filter(([_, shouldClose]) => shouldClose), delay(0),
takeUntil(closed$)) as Observable<MouseEvent>;


race<Event>([escapes$, closeableClicks$]).subscribe(() => zone.run(close));
});
}));
}
}

0 comments on commit c08e3b4

Please sign in to comment.