Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(autoclose): fix popup components auto closing in Safari 13 #3454

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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));
});
}));
}
}