From 5d54920e8132dfcea87e0607827008f22f6f992a Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 25 Oct 2018 01:13:35 +0200 Subject: [PATCH] fix(tooltip): add fallback for touch devices if Hammer isn't loaded (#13580) Unlike other components that we use Hammer on, the tooltip won't show up at all on a touch device if Hammer isn't loaded. These changes add a fallback to using a `touchstart` event to trigger the tooltip. Also fixes the `_manualListeners` not being cleared on Android. Fixes #13536. --- src/lib/tooltip/tooltip.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/lib/tooltip/tooltip.ts b/src/lib/tooltip/tooltip.ts index 83690831db0f..4feca9edf1d5 100644 --- a/src/lib/tooltip/tooltip.ts +++ b/src/lib/tooltip/tooltip.ts @@ -11,6 +11,7 @@ import {Directionality} from '@angular/cdk/bidi'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; import {ESCAPE} from '@angular/cdk/keycodes'; import {BreakpointObserver, Breakpoints, BreakpointState} from '@angular/cdk/layout'; +import {HammerLoader, HAMMER_LOADER} from '@angular/platform-browser'; import { FlexibleConnectedPositionStrategy, HorizontalConnectionPos, @@ -201,27 +202,34 @@ export class MatTooltip implements OnDestroy { private _scrollDispatcher: ScrollDispatcher, private _viewContainerRef: ViewContainerRef, private _ngZone: NgZone, - private _platform: Platform, + platform: Platform, private _ariaDescriber: AriaDescriber, private _focusMonitor: FocusMonitor, @Inject(MAT_TOOLTIP_SCROLL_STRATEGY) scrollStrategy: any, @Optional() private _dir: Directionality, @Optional() @Inject(MAT_TOOLTIP_DEFAULT_OPTIONS) - private _defaultOptions: MatTooltipDefaultOptions) { + private _defaultOptions: MatTooltipDefaultOptions, + @Optional() @Inject(HAMMER_LOADER) hammerLoader?: HammerLoader) { this._scrollStrategy = scrollStrategy; const element: HTMLElement = _elementRef.nativeElement; const elementStyle = element.style as CSSStyleDeclaration & {webkitUserDrag: string}; + const hasGestures = typeof window === 'undefined' || (window as any).Hammer || hammerLoader; // The mouse events shouldn't be bound on mobile devices, because they can prevent the // first tap from firing its click event or can cause the tooltip to open for clicks. - if (!_platform.IOS && !_platform.ANDROID) { + if (!platform.IOS && !platform.ANDROID) { this._manualListeners .set('mouseenter', () => this.show()) - .set('mouseleave', () => this.hide()) - .forEach((listener, event) => element.addEventListener(event, listener)); + .set('mouseleave', () => this.hide()); + } else if (!hasGestures) { + // If Hammerjs isn't loaded, fall back to showing on `touchstart`, otherwise + // there's no way for the user to trigger the tooltip on a touch device. + this._manualListeners.set('touchstart', () => this.show()); } + this._manualListeners.forEach((listener, event) => element.addEventListener(event, listener)); + if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { // When we bind a gesture event on an element (in this case `longpress`), HammerJS // will add some inline styles by default, including `user-select: none`. This is @@ -258,12 +266,10 @@ export class MatTooltip implements OnDestroy { } // Clean up the event listeners set in the constructor - if (!this._platform.IOS) { - this._manualListeners.forEach((listener, event) => - this._elementRef.nativeElement.removeEventListener(event, listener)); - - this._manualListeners.clear(); - } + this._manualListeners.forEach((listener, event) => { + this._elementRef.nativeElement.removeEventListener(event, listener); + }); + this._manualListeners.clear(); this._destroyed.next(); this._destroyed.complete();