9
9
import { Direction } from '@angular/cdk/bidi' ;
10
10
import { ComponentPortal , Portal , PortalOutlet , TemplatePortal } from '@angular/cdk/portal' ;
11
11
import { ComponentRef , EmbeddedViewRef , NgZone } from '@angular/core' ;
12
- import { Observable , Subject } from 'rxjs' ;
12
+ import { Observable , Subject , empty } from 'rxjs' ;
13
13
import { take } from 'rxjs/operators' ;
14
14
import { OverlayKeyboardDispatcher } from './keyboard/overlay-keyboard-dispatcher' ;
15
15
import { OverlayConfig } from './overlay-config' ;
16
16
import { coerceCssPixelValue } from '@angular/cdk/coercion' ;
17
+ import { CdkOverlayBackdrop } from './backdrop' ;
17
18
18
19
19
20
/** An object where all of its properties cannot be written. */
@@ -26,10 +27,10 @@ export type ImmutableObject<T> = {
26
27
* Used to manipulate or dispose of said overlay.
27
28
*/
28
29
export class OverlayRef implements PortalOutlet {
29
- private _backdropElement : HTMLElement | null = null ;
30
- private _backdropClick : Subject < MouseEvent > = new Subject ( ) ;
30
+ private _backdropClick = new Subject < MouseEvent > ( ) ;
31
31
private _attachments = new Subject < void > ( ) ;
32
32
private _detachments = new Subject < void > ( ) ;
33
+ private _backdropInstance : CdkOverlayBackdrop | null ;
33
34
34
35
/** Stream of keydown events dispatched to this overlay. */
35
36
_keydownEvents = new Subject < KeyboardEvent > ( ) ;
@@ -38,10 +39,10 @@ export class OverlayRef implements PortalOutlet {
38
39
private _portalOutlet : PortalOutlet ,
39
40
private _host : HTMLElement ,
40
41
private _pane : HTMLElement ,
42
+ private _backdropHost : PortalOutlet | null ,
41
43
private _config : ImmutableObject < OverlayConfig > ,
42
44
private _ngZone : NgZone ,
43
- private _keyboardDispatcher : OverlayKeyboardDispatcher ,
44
- private _document : Document ) {
45
+ private _keyboardDispatcher : OverlayKeyboardDispatcher ) {
45
46
46
47
if ( _config . scrollStrategy ) {
47
48
_config . scrollStrategy . attach ( this ) ;
@@ -55,7 +56,7 @@ export class OverlayRef implements PortalOutlet {
55
56
56
57
/** The overlay's backdrop HTML element. */
57
58
get backdropElement ( ) : HTMLElement | null {
58
- return this . _backdropElement ;
59
+ return this . _backdropInstance ? this . _backdropInstance . _element . nativeElement : null ;
59
60
}
60
61
61
62
/**
@@ -79,7 +80,7 @@ export class OverlayRef implements PortalOutlet {
79
80
* @returns The portal attachment result.
80
81
*/
81
82
attach ( portal : Portal < any > ) : any {
82
- let attachResult = this . _portalOutlet . attach ( portal ) ;
83
+ const attachResult = this . _portalOutlet . attach ( portal ) ;
83
84
84
85
if ( this . _config . positionStrategy ) {
85
86
this . _config . positionStrategy . attach ( this ) ;
@@ -110,8 +111,10 @@ export class OverlayRef implements PortalOutlet {
110
111
// Enable pointer events for the overlay pane element.
111
112
this . _togglePointerEvents ( true ) ;
112
113
113
- if ( this . _config . hasBackdrop ) {
114
- this . _attachBackdrop ( ) ;
114
+ if ( this . _backdropHost ) {
115
+ this . _backdropInstance =
116
+ this . _backdropHost . attach ( new ComponentPortal ( CdkOverlayBackdrop ) ) . instance ;
117
+ this . _backdropInstance ! . _setClass ( this . _config . backdropClass ! ) ;
115
118
}
116
119
117
120
if ( this . _config . panelClass ) {
@@ -141,7 +144,9 @@ export class OverlayRef implements PortalOutlet {
141
144
return ;
142
145
}
143
146
144
- this . detachBackdrop ( ) ;
147
+ if ( this . _backdropHost && this . _backdropHost . hasAttached ( ) ) {
148
+ this . _backdropHost . detach ( ) ;
149
+ }
145
150
146
151
// When the overlay is detached, the pane element should disable pointer events.
147
152
// This is necessary because otherwise the pane element will cover the page and disable
@@ -179,7 +184,7 @@ export class OverlayRef implements PortalOutlet {
179
184
this . _config . scrollStrategy . disable ( ) ;
180
185
}
181
186
182
- this . detachBackdrop ( ) ;
187
+ this . disposeBackdrop ( ) ;
183
188
this . _keyboardDispatcher . remove ( this ) ;
184
189
this . _portalOutlet . dispose ( ) ;
185
190
this . _attachments . complete ( ) ;
@@ -205,7 +210,7 @@ export class OverlayRef implements PortalOutlet {
205
210
206
211
/** Gets an observable that emits when the backdrop has been clicked. */
207
212
backdropClick ( ) : Observable < MouseEvent > {
208
- return this . _backdropClick . asObservable ( ) ;
213
+ return this . _backdropInstance ? this . _backdropInstance . _clickStream : empty ( ) ;
209
214
}
210
215
211
216
/** Gets an observable that emits when the overlay has been attached. */
@@ -284,40 +289,6 @@ export class OverlayRef implements PortalOutlet {
284
289
this . _pane . style . pointerEvents = enablePointer ? 'auto' : 'none' ;
285
290
}
286
291
287
- /** Attaches a backdrop for this overlay. */
288
- private _attachBackdrop ( ) {
289
- const showingClass = 'cdk-overlay-backdrop-showing' ;
290
-
291
- this . _backdropElement = this . _document . createElement ( 'div' ) ;
292
- this . _backdropElement . classList . add ( 'cdk-overlay-backdrop' ) ;
293
-
294
- if ( this . _config . backdropClass ) {
295
- this . _backdropElement . classList . add ( this . _config . backdropClass ) ;
296
- }
297
-
298
- // Insert the backdrop before the pane in the DOM order,
299
- // in order to handle stacked overlays properly.
300
- this . _host . parentElement ! . insertBefore ( this . _backdropElement , this . _host ) ;
301
-
302
- // Forward backdrop clicks such that the consumer of the overlay can perform whatever
303
- // action desired when such a click occurs (usually closing the overlay).
304
- this . _backdropElement . addEventListener ( 'click' ,
305
- ( event : MouseEvent ) => this . _backdropClick . next ( event ) ) ;
306
-
307
- // Add class to fade-in the backdrop after one frame.
308
- if ( typeof requestAnimationFrame !== 'undefined' ) {
309
- this . _ngZone . runOutsideAngular ( ( ) => {
310
- requestAnimationFrame ( ( ) => {
311
- if ( this . _backdropElement ) {
312
- this . _backdropElement . classList . add ( showingClass ) ;
313
- }
314
- } ) ;
315
- } ) ;
316
- } else {
317
- this . _backdropElement . classList . add ( showingClass ) ;
318
- }
319
- }
320
-
321
292
/**
322
293
* Updates the stacking order of the element, moving it to the top if necessary.
323
294
* This is required in cases where one overlay was detached, while another one,
@@ -331,43 +302,30 @@ export class OverlayRef implements PortalOutlet {
331
302
}
332
303
}
333
304
334
- /** Detaches the backdrop (if any) associated with the overlay. */
335
- detachBackdrop ( ) : void {
336
- let backdropToDetach = this . _backdropElement ;
337
-
338
- if ( backdropToDetach ) {
339
- let finishDetach = ( ) => {
340
- // It may not be attached to anything in certain cases (e.g. unit tests).
341
- if ( backdropToDetach && backdropToDetach . parentNode ) {
342
- backdropToDetach . parentNode . removeChild ( backdropToDetach ) ;
343
- }
344
-
345
- // It is possible that a new portal has been attached to this overlay since we started
346
- // removing the backdrop. If that is the case, only clear the backdrop reference if it
347
- // is still the same instance that we started to remove.
348
- if ( this . _backdropElement == backdropToDetach ) {
349
- this . _backdropElement = null ;
350
- }
351
- } ;
352
-
353
- backdropToDetach . classList . remove ( 'cdk-overlay-backdrop-showing' ) ;
305
+ /** Animates out and disposes of the backdrop. */
306
+ disposeBackdrop ( ) : void {
307
+ if ( this . _backdropHost ) {
308
+ if ( this . _backdropHost . hasAttached ( ) ) {
309
+ this . _backdropHost . detach ( ) ;
354
310
355
- if ( this . _config . backdropClass ) {
356
- backdropToDetach . classList . remove ( this . _config . backdropClass ) ;
311
+ this . _backdropInstance ! . _animationStream . pipe ( take ( 1 ) ) . subscribe ( ( ) => {
312
+ this . _backdropHost ! . dispose ( ) ;
313
+ this . _backdropHost = this . _backdropInstance = null ;
314
+ } ) ;
315
+ } else {
316
+ this . _backdropHost . dispose ( ) ;
357
317
}
358
-
359
- backdropToDetach . addEventListener ( 'transitionend' , finishDetach ) ;
360
-
361
- // If the backdrop doesn't have a transition, the `transitionend` event won't fire.
362
- // In this case we make it unclickable and we try to remove it after a delay.
363
- backdropToDetach . style . pointerEvents = 'none' ;
364
-
365
- // Run this outside the Angular zone because there's nothing that Angular cares about.
366
- // If it were to run inside the Angular zone, every test that used Overlay would have to be
367
- // either async or fakeAsync.
368
- this . _ngZone . runOutsideAngular ( ( ) => setTimeout ( finishDetach , 500 ) ) ;
369
318
}
370
319
}
320
+
321
+ /**
322
+ * Detaches the backdrop (if any) associated with the overlay.
323
+ * @deprecated Use `disposeBackdrop` instead.
324
+ * @deletion -target 7.0.0
325
+ */
326
+ detachBackdrop ( ) : void {
327
+ this . disposeBackdrop ( ) ;
328
+ }
371
329
}
372
330
373
331
0 commit comments