@@ -17,7 +17,6 @@ import {
1717import { ESCAPE , hasModifierKey } from '@angular/cdk/keycodes' ;
1818import { BreakpointObserver , Breakpoints , BreakpointState } from '@angular/cdk/layout' ;
1919import {
20- ConnectedPosition ,
2120 FlexibleConnectedPositionStrategy ,
2221 HorizontalConnectionPos ,
2322 OriginConnectionPosition ,
@@ -207,6 +206,10 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
207206 }
208207 set hideDelay ( value : NumberInput ) {
209208 this . _hideDelay = coerceNumberProperty ( value ) ;
209+
210+ if ( this . _tooltipInstance ) {
211+ this . _tooltipInstance . _mouseLeaveHideDelay = this . _hideDelay ;
212+ }
210213 }
211214 private _hideDelay = this . _defaultOptions . hideDelay ;
212215
@@ -376,14 +379,16 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
376379 this . _detach ( ) ;
377380 this . _portal =
378381 this . _portal || new ComponentPortal ( this . _tooltipComponent , this . _viewContainerRef ) ;
379- this . _tooltipInstance = overlayRef . attach ( this . _portal ) . instance ;
380- this . _tooltipInstance
382+ const instance = ( this . _tooltipInstance = overlayRef . attach ( this . _portal ) . instance ) ;
383+ instance . _triggerElement = this . _elementRef . nativeElement ;
384+ instance . _mouseLeaveHideDelay = this . _hideDelay ;
385+ instance
381386 . afterHidden ( )
382387 . pipe ( takeUntil ( this . _destroyed ) )
383388 . subscribe ( ( ) => this . _detach ( ) ) ;
384389 this . _setTooltipClass ( this . _tooltipClass ) ;
385390 this . _updateTooltipMessage ( ) ;
386- this . _tooltipInstance ! . show ( delay ) ;
391+ instance . show ( delay ) ;
387392 }
388393
389394 /** Hides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input */
@@ -483,16 +488,11 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
483488 const overlay = this . _getOverlayPosition ( ) ;
484489
485490 position . withPositions ( [
486- this . _addOffset ( { ...origin . main , ...overlay . main } ) ,
487- this . _addOffset ( { ...origin . fallback , ...overlay . fallback } ) ,
491+ { ...origin . main , ...overlay . main } ,
492+ { ...origin . fallback , ...overlay . fallback } ,
488493 ] ) ;
489494 }
490495
491- /** Adds the configured offset to a position. Used as a hook for child classes. */
492- protected _addOffset ( position : ConnectedPosition ) : ConnectedPosition {
493- return position ;
494- }
495-
496496 /**
497497 * Returns the origin position and a fallback position based on the user's position preference.
498498 * The fallback position is the inverse of the origin (e.g. `'below' -> 'above'`).
@@ -687,7 +687,15 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
687687 const exitListeners : ( readonly [ string , EventListenerOrEventListenerObject ] ) [ ] = [ ] ;
688688 if ( this . _platformSupportsMouseEvents ( ) ) {
689689 exitListeners . push (
690- [ 'mouseleave' , ( ) => this . hide ( ) ] ,
690+ [
691+ 'mouseleave' ,
692+ event => {
693+ const newTarget = ( event as MouseEvent ) . relatedTarget as Node | null ;
694+ if ( ! newTarget || ! this . _overlayRef ?. overlayElement . contains ( newTarget ) ) {
695+ this . hide ( ) ;
696+ }
697+ } ,
698+ ] ,
691699 [ 'wheel' , event => this . _wheelListener ( event as WheelEvent ) ] ,
692700 ) ;
693701 } else if ( this . touchGestures !== 'off' ) {
@@ -824,6 +832,12 @@ export abstract class _TooltipComponentBase implements OnDestroy {
824832 /** Property watched by the animation framework to show or hide the tooltip */
825833 _visibility : TooltipVisibility = 'initial' ;
826834
835+ /** Element that caused the tooltip to open. */
836+ _triggerElement : HTMLElement ;
837+
838+ /** Amount of milliseconds to delay the closing sequence. */
839+ _mouseLeaveHideDelay : number ;
840+
827841 /** Whether interactions on the page should close the tooltip */
828842 private _closeOnInteraction : boolean = false ;
829843
@@ -885,6 +899,7 @@ export abstract class _TooltipComponentBase implements OnDestroy {
885899 clearTimeout ( this . _showTimeoutId ) ;
886900 clearTimeout ( this . _hideTimeoutId ) ;
887901 this . _onHide . complete ( ) ;
902+ this . _triggerElement = null ! ;
888903 }
889904
890905 _animationStart ( ) {
@@ -923,6 +938,12 @@ export abstract class _TooltipComponentBase implements OnDestroy {
923938 this . _changeDetectorRef . markForCheck ( ) ;
924939 }
925940
941+ _handleMouseLeave ( { relatedTarget} : MouseEvent ) {
942+ if ( ! relatedTarget || ! this . _triggerElement . contains ( relatedTarget as Node ) ) {
943+ this . hide ( this . _mouseLeaveHideDelay ) ;
944+ }
945+ }
946+
926947 /**
927948 * Callback for when the timeout in this.show() gets completed.
928949 * This method is only needed by the mdc-tooltip, and so it is only implemented
@@ -946,6 +967,7 @@ export abstract class _TooltipComponentBase implements OnDestroy {
946967 // Forces the element to have a layout in IE and Edge. This fixes issues where the element
947968 // won't be rendered if the animations are disabled or there is no web animations polyfill.
948969 '[style.zoom]' : '_visibility === "visible" ? 1 : null' ,
970+ '(mouseleave)' : '_handleMouseLeave($event)' ,
949971 'aria-hidden' : 'true' ,
950972 } ,
951973} )
0 commit comments