@@ -15,18 +15,18 @@ import {
15
15
HorizontalConnectionPos ,
16
16
OriginConnectionPosition ,
17
17
Overlay ,
18
- OverlayConfig ,
18
+ ScrollDispatcher ,
19
19
OverlayConnectionPosition ,
20
20
OverlayRef ,
21
21
RepositionScrollStrategy ,
22
22
ScrollStrategy ,
23
23
VerticalConnectionPos ,
24
+ ConnectedPositionStrategy ,
24
25
} from '@angular/cdk/overlay' ;
25
26
import { Platform } from '@angular/cdk/platform' ;
26
27
import { ComponentPortal } from '@angular/cdk/portal' ;
27
28
import { take } from 'rxjs/operators/take' ;
28
- import { merge } from 'rxjs/observable/merge' ;
29
- import { ScrollDispatcher } from '@angular/cdk/scrolling' ;
29
+ import { filter } from 'rxjs/operators/filter' ;
30
30
import {
31
31
ChangeDetectionStrategy ,
32
32
ChangeDetectorRef ,
@@ -108,6 +108,7 @@ export class MatTooltip implements OnDestroy {
108
108
_overlayRef : OverlayRef | null ;
109
109
_tooltipInstance : TooltipComponent | null ;
110
110
111
+ private _portal : ComponentPortal < TooltipComponent > ;
111
112
private _position : TooltipPosition = 'below' ;
112
113
private _disabled : boolean = false ;
113
114
private _tooltipClass : string | string [ ] | Set < string > | { [ key : string ] : any } ;
@@ -119,10 +120,11 @@ export class MatTooltip implements OnDestroy {
119
120
if ( value !== this . _position ) {
120
121
this . _position = value ;
121
122
122
- // TODO(andrewjs): When the overlay's position can be dynamically changed, do not destroy
123
- // the tooltip.
124
- if ( this . _tooltipInstance ) {
125
- this . _disposeTooltip ( ) ;
123
+ if ( this . _overlayRef ) {
124
+ // TODO(andrewjs): When the overlay's position can be
125
+ // dynamically changed, do not destroy the tooltip.
126
+ this . _detach ( ) ;
127
+ this . _updatePosition ( ) ;
126
128
}
127
129
}
128
130
}
@@ -236,15 +238,15 @@ export class MatTooltip implements OnDestroy {
236
238
* Dispose the tooltip when destroyed.
237
239
*/
238
240
ngOnDestroy ( ) {
239
- if ( this . _tooltipInstance ) {
240
- this . _disposeTooltip ( ) ;
241
+ if ( this . _overlayRef ) {
242
+ this . _overlayRef . dispose ( ) ;
243
+ this . _tooltipInstance = null ;
241
244
}
242
245
243
246
// Clean up the event listeners set in the constructor
244
247
if ( ! this . _platform . IOS ) {
245
- this . _manualListeners . forEach ( ( listener , event ) => {
246
- this . _elementRef . nativeElement . removeEventListener ( event , listener ) ;
247
- } ) ;
248
+ this . _manualListeners . forEach ( ( listener , event ) =>
249
+ this . _elementRef . nativeElement . removeEventListener ( event , listener ) ) ;
248
250
249
251
this . _manualListeners . clear ( ) ;
250
252
}
@@ -257,10 +259,12 @@ export class MatTooltip implements OnDestroy {
257
259
show ( delay : number = this . showDelay ) : void {
258
260
if ( this . disabled || ! this . message ) { return ; }
259
261
260
- if ( ! this . _tooltipInstance ) {
261
- this . _createTooltip ( ) ;
262
- }
262
+ const overlayRef = this . _createOverlay ( ) ;
263
263
264
+ this . _detach ( ) ;
265
+ this . _portal = this . _portal || new ComponentPortal ( TooltipComponent , this . _viewContainerRef ) ;
266
+ this . _tooltipInstance = overlayRef . attach ( this . _portal ) . instance ;
267
+ this . _tooltipInstance . afterHidden ( ) . subscribe ( ( ) => this . _detach ( ) ) ;
264
268
this . _setTooltipClass ( this . _tooltipClass ) ;
265
269
this . _updateTooltipMessage ( ) ;
266
270
this . _tooltipInstance ! . show ( this . _position , delay ) ;
@@ -296,73 +300,68 @@ export class MatTooltip implements OnDestroy {
296
300
this . hide ( this . _defaultOptions ? this . _defaultOptions . touchendHideDelay : 1500 ) ;
297
301
}
298
302
299
- /** Create the tooltip to display */
300
- private _createTooltip ( ) : void {
301
- const overlayRef = this . _createOverlay ( ) ;
302
- const portal = new ComponentPortal ( TooltipComponent , this . _viewContainerRef ) ;
303
-
304
- this . _tooltipInstance = overlayRef . attach ( portal ) . instance ;
305
-
306
- // Dispose of the tooltip when the overlay is detached.
307
- merge ( this . _tooltipInstance ! . afterHidden ( ) , overlayRef . detachments ( ) ) . subscribe ( ( ) => {
308
- // Check first if the tooltip has already been removed through this components destroy.
309
- if ( this . _tooltipInstance ) {
310
- this . _disposeTooltip ( ) ;
311
- }
312
- } ) ;
313
- }
314
-
315
303
/** Create the overlay config and position strategy */
316
304
private _createOverlay ( ) : OverlayRef {
305
+ if ( this . _overlayRef ) {
306
+ return this . _overlayRef ;
307
+ }
308
+
317
309
const origin = this . _getOrigin ( ) ;
318
310
const overlay = this . _getOverlayPosition ( ) ;
319
311
320
312
// Create connected position strategy that listens for scroll events to reposition.
321
313
const strategy = this . _overlay
322
314
. position ( )
323
315
. connectedTo ( this . _elementRef , origin . main , overlay . main )
324
- . withFallbackPosition ( origin . fallback , overlay . fallback ) ;
325
-
326
- const scrollableAncestors = this . _scrollDispatcher
327
- . getAncestorScrollContainers ( this . _elementRef ) ;
328
-
329
- strategy . withScrollableContainers ( scrollableAncestors ) ;
330
-
331
- strategy . onPositionChange . subscribe ( change => {
332
- if ( this . _tooltipInstance ) {
333
- if ( change . scrollableViewProperties . isOverlayClipped && this . _tooltipInstance . isVisible ( ) ) {
334
- // After position changes occur and the overlay is clipped by
335
- // a parent scrollable then close the tooltip.
336
- this . _ngZone . run ( ( ) => this . hide ( 0 ) ) ;
337
- } else {
338
- // Otherwise recalculate the origin based on the new position.
339
- this . _tooltipInstance . _setTransformOrigin ( change . connectionPair ) ;
340
- }
316
+ . withFallbackPosition ( origin . fallback , overlay . fallback )
317
+ . withScrollableContainers (
318
+ this . _scrollDispatcher . getAncestorScrollContainers ( this . _elementRef )
319
+ ) ;
320
+
321
+ strategy . onPositionChange . pipe ( filter ( ( ) => ! ! this . _tooltipInstance ) ) . subscribe ( change => {
322
+ if ( change . scrollableViewProperties . isOverlayClipped && this . _tooltipInstance ! . isVisible ( ) ) {
323
+ // After position changes occur and the overlay is clipped by
324
+ // a parent scrollable then close the tooltip.
325
+ this . _ngZone . run ( ( ) => this . hide ( 0 ) ) ;
326
+ } else {
327
+ // Otherwise recalculate the origin based on the new position.
328
+ this . _tooltipInstance ! . _setTransformOrigin ( change . connectionPair ) ;
341
329
}
342
330
} ) ;
343
331
344
- const config = new OverlayConfig ( {
332
+ this . _overlayRef = this . _overlay . create ( {
345
333
direction : this . _dir ? this . _dir . value : 'ltr' ,
346
334
positionStrategy : strategy ,
347
335
panelClass : TOOLTIP_PANEL_CLASS ,
348
336
scrollStrategy : this . _scrollStrategy ( )
349
337
} ) ;
350
338
351
- this . _overlayRef = this . _overlay . create ( config ) ;
339
+ this . _overlayRef . detachments ( ) . subscribe ( ( ) => this . _detach ( ) ) ;
352
340
353
341
return this . _overlayRef ;
354
342
}
355
343
356
- /** Disposes the current tooltip and the overlay it is attached to */
357
- private _disposeTooltip ( ) : void {
358
- if ( this . _overlayRef ) {
359
- this . _overlayRef . dispose ( ) ;
360
- this . _overlayRef = null ;
344
+ /** Detaches the currently-attached tooltip. */
345
+ private _detach ( ) {
346
+ if ( this . _overlayRef && this . _overlayRef . hasAttached ( ) ) {
347
+ this . _overlayRef . detach ( ) ;
361
348
}
362
349
363
350
this . _tooltipInstance = null ;
364
351
}
365
352
353
+ /** Updates the position of the current tooltip. */
354
+ private _updatePosition ( ) {
355
+ const position = this . _overlayRef ! . getConfig ( ) . positionStrategy as ConnectedPositionStrategy ;
356
+ const origin = this . _getOrigin ( ) ;
357
+ const overlay = this . _getOverlayPosition ( ) ;
358
+
359
+ position
360
+ . withPositions ( [ ] )
361
+ . withFallbackPosition ( origin . main , overlay . main )
362
+ . withFallbackPosition ( origin . fallback , overlay . fallback ) ;
363
+ }
364
+
366
365
/**
367
366
* Returns the origin position and a fallback position based on the user's position preference.
368
367
* The fallback position is the inverse of the origin (e.g. `'below' -> 'above'`).
0 commit comments