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(tooltip): prevent autoclose with default triggers on Android #3587

Merged
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
20 changes: 9 additions & 11 deletions src/util/autoclose.ts
Expand Up @@ -10,24 +10,22 @@ const isContainedIn = (element: HTMLElement, array?: HTMLElement[]) =>
const matchesSelectorIfAny = (element: HTMLElement, selector?: string) =>
!selector || closest(element, selector) != null;

// we'll have to use 'touch' events instead of 'mouse' events on iOS and add a more significant delay
// to avoid re-opening when handling (click) on a toggling element
// we have to add a more significant delay to avoid re-opening when handling (click) on a toggling element
// TODO: use proper Angular platform detection when NgbAutoClose becomes a service and we can inject PLATFORM_ID
let iOS = false;
if (typeof navigator !== 'undefined') {
iOS = !!navigator.userAgent && /iPad|iPhone|iPod/.test(navigator.userAgent);
}
const isMobile = () => typeof navigator !== 'undefined' ?
!!navigator.userAgent && /iPad|iPhone|iPod|Android/.test(navigator.userAgent) :

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this won't work for iPads with iOS >= 13. Since this version userAgent does not contain iPad.

https://stackoverflow.com/questions/2153877/what-is-the-ipad-user-agent/56923008#56923008

I checked it on browserstack and I can confirm that the issue still persists. This commit did the fix for Android though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yikes, thanks for the info. Of course we should not use userAgent at all for this...

false;

// setting 'ngbAutoClose' synchronously on iOS results in immediate popup closing
// setting 'ngbAutoClose' synchronously on mobile results in immediate popup closing
// when tapping on the triggering element
const wrapAsyncForiOS = fn => iOS ? () => setTimeout(() => fn(), 100) : fn;
const wrapAsyncForMobile = fn => isMobile() ? () => 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(wrapAsyncForiOS(() => {
zone.runOutsideAngular(wrapAsyncForMobile(() => {

const shouldCloseOnClick = (event: MouseEvent) => {
const element = event.target as HTMLElement;
Expand All @@ -50,8 +48,8 @@ export function ngbAutoClose(
filter(e => e.which === Key.Escape), tap(e => e.preventDefault()));


// we have to pre-calculate 'shouldCloseOnClick' on 'mousedown/touchstart',
// because on 'mouseup/touchend' DOM nodes might be detached
// we have to pre-calculate 'shouldCloseOnClick' on 'mousedown',
// because on 'mouseup' DOM nodes might be detached
const mouseDowns$ =
fromEvent<MouseEvent>(document, 'mousedown').pipe(map(shouldCloseOnClick), takeUntil(closed$));

Expand Down