@@ -17,7 +17,6 @@ import {
17
17
import { ESCAPE , hasModifierKey } from '@angular/cdk/keycodes' ;
18
18
import { BreakpointObserver , Breakpoints , BreakpointState } from '@angular/cdk/layout' ;
19
19
import {
20
- ConnectedPosition ,
21
20
FlexibleConnectedPositionStrategy ,
22
21
HorizontalConnectionPos ,
23
22
OriginConnectionPosition ,
@@ -207,6 +206,10 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
207
206
}
208
207
set hideDelay ( value : NumberInput ) {
209
208
this . _hideDelay = coerceNumberProperty ( value ) ;
209
+
210
+ if ( this . _tooltipInstance ) {
211
+ this . _tooltipInstance . _mouseLeaveHideDelay = this . _hideDelay ;
212
+ }
210
213
}
211
214
private _hideDelay = this . _defaultOptions . hideDelay ;
212
215
@@ -376,14 +379,16 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
376
379
this . _detach ( ) ;
377
380
this . _portal =
378
381
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
381
386
. afterHidden ( )
382
387
. pipe ( takeUntil ( this . _destroyed ) )
383
388
. subscribe ( ( ) => this . _detach ( ) ) ;
384
389
this . _setTooltipClass ( this . _tooltipClass ) ;
385
390
this . _updateTooltipMessage ( ) ;
386
- this . _tooltipInstance ! . show ( delay ) ;
391
+ instance . show ( delay ) ;
387
392
}
388
393
389
394
/** 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>
483
488
const overlay = this . _getOverlayPosition ( ) ;
484
489
485
490
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 } ,
488
493
] ) ;
489
494
}
490
495
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
-
496
496
/**
497
497
* Returns the origin position and a fallback position based on the user's position preference.
498
498
* The fallback position is the inverse of the origin (e.g. `'below' -> 'above'`).
@@ -687,7 +687,15 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
687
687
const exitListeners : ( readonly [ string , EventListenerOrEventListenerObject ] ) [ ] = [ ] ;
688
688
if ( this . _platformSupportsMouseEvents ( ) ) {
689
689
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
+ ] ,
691
699
[ 'wheel' , event => this . _wheelListener ( event as WheelEvent ) ] ,
692
700
) ;
693
701
} else if ( this . touchGestures !== 'off' ) {
@@ -824,6 +832,12 @@ export abstract class _TooltipComponentBase implements OnDestroy {
824
832
/** Property watched by the animation framework to show or hide the tooltip */
825
833
_visibility : TooltipVisibility = 'initial' ;
826
834
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
+
827
841
/** Whether interactions on the page should close the tooltip */
828
842
private _closeOnInteraction : boolean = false ;
829
843
@@ -885,6 +899,7 @@ export abstract class _TooltipComponentBase implements OnDestroy {
885
899
clearTimeout ( this . _showTimeoutId ) ;
886
900
clearTimeout ( this . _hideTimeoutId ) ;
887
901
this . _onHide . complete ( ) ;
902
+ this . _triggerElement = null ! ;
888
903
}
889
904
890
905
_animationStart ( ) {
@@ -923,6 +938,12 @@ export abstract class _TooltipComponentBase implements OnDestroy {
923
938
this . _changeDetectorRef . markForCheck ( ) ;
924
939
}
925
940
941
+ _handleMouseLeave ( { relatedTarget} : MouseEvent ) {
942
+ if ( ! relatedTarget || ! this . _triggerElement . contains ( relatedTarget as Node ) ) {
943
+ this . hide ( this . _mouseLeaveHideDelay ) ;
944
+ }
945
+ }
946
+
926
947
/**
927
948
* Callback for when the timeout in this.show() gets completed.
928
949
* 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 {
946
967
// Forces the element to have a layout in IE and Edge. This fixes issues where the element
947
968
// won't be rendered if the animations are disabled or there is no web animations polyfill.
948
969
'[style.zoom]' : '_visibility === "visible" ? 1 : null' ,
970
+ '(mouseleave)' : '_handleMouseLeave($event)' ,
949
971
'aria-hidden' : 'true' ,
950
972
} ,
951
973
} )
0 commit comments