@@ -12,8 +12,10 @@ import {OverlayConfig} from './overlay-config';
1212import { OverlayKeyboardDispatcher } from './keyboard/overlay-keyboard-dispatcher' ;
1313import { Observable } from 'rxjs/Observable' ;
1414import { Subject } from 'rxjs/Subject' ;
15+ import { MatBackdrop } from './backdrop' ;
16+ import { ComponentPortal } from '@angular/cdk/portal' ;
1517import { first } from 'rxjs/operators/first' ;
16-
18+ import { empty } from 'rxjs/observable/empty' ;
1719
1820/**
1921 * Reference to an overlay that has been created with the Overlay service.
@@ -24,13 +26,15 @@ export class OverlayRef implements PortalOutlet {
2426 private _backdropClick : Subject < any > = new Subject ( ) ;
2527 private _attachments = new Subject < void > ( ) ;
2628 private _detachments = new Subject < void > ( ) ;
29+ private _backdropInstance : MatBackdrop | null ;
2730
2831 /** Stream of keydown events dispatched to this overlay. */
2932 _keydownEvents = new Subject < KeyboardEvent > ( ) ;
3033
3134 constructor (
3235 private _portalOutlet : PortalOutlet ,
3336 private _pane : HTMLElement ,
37+ private _backdropHost : PortalOutlet | null ,
3438 private _config : OverlayConfig ,
3539 private _ngZone : NgZone ,
3640 private _keyboardDispatcher : OverlayKeyboardDispatcher ) {
@@ -51,7 +55,7 @@ export class OverlayRef implements PortalOutlet {
5155 * @returns The portal attachment result.
5256 */
5357 attach ( portal : Portal < any > ) : any {
54- let attachResult = this . _portalOutlet . attach ( portal ) ;
58+ const attachResult = this . _portalOutlet . attach ( portal ) ;
5559
5660 if ( this . _config . positionStrategy ) {
5761 this . _config . positionStrategy . attach ( this ) ;
@@ -76,14 +80,15 @@ export class OverlayRef implements PortalOutlet {
7680 // Enable pointer events for the overlay pane element.
7781 this . _togglePointerEvents ( true ) ;
7882
79- if ( this . _config . hasBackdrop ) {
80- this . _attachBackdrop ( ) ;
83+ if ( this . _backdropHost ) {
84+ this . _backdropInstance = this . _backdropHost . attach ( new ComponentPortal ( MatBackdrop ) ) . instance ;
85+ this . _backdropInstance ! . _setClass ( this . _config . backdropClass ! ) ;
8186 }
8287
8388 if ( this . _config . panelClass ) {
8489 // We can't do a spread here, because IE doesn't support setting multiple classes.
8590 if ( Array . isArray ( this . _config . panelClass ) ) {
86- this . _config . panelClass . forEach ( cls => this . _pane . classList . add ( cls ) ) ;
91+ this . _config . panelClass . forEach ( cssClass => this . _pane . classList . add ( cssClass ) ) ;
8792 } else {
8893 this . _pane . classList . add ( this . _config . panelClass ) ;
8994 }
@@ -107,7 +112,9 @@ export class OverlayRef implements PortalOutlet {
107112 return ;
108113 }
109114
110- this . detachBackdrop ( ) ;
115+ if ( this . _backdropHost && this . _backdropHost . hasAttached ( ) ) {
116+ this . _backdropHost . detach ( ) ;
117+ }
111118
112119 // When the overlay is detached, the pane element should disable pointer events.
113120 // This is necessary because otherwise the pane element will cover the page and disable
@@ -147,7 +154,7 @@ export class OverlayRef implements PortalOutlet {
147154 this . _config . scrollStrategy . disable ( ) ;
148155 }
149156
150- this . detachBackdrop ( ) ;
157+ this . disposeBackdrop ( ) ;
151158 this . _portalOutlet . dispose ( ) ;
152159 this . _attachments . complete ( ) ;
153160 this . _backdropClick . complete ( ) ;
@@ -170,7 +177,7 @@ export class OverlayRef implements PortalOutlet {
170177 * Gets an observable that emits when the backdrop has been clicked.
171178 */
172179 backdropClick ( ) : Observable < void > {
173- return this . _backdropClick . asObservable ( ) ;
180+ return this . _backdropInstance ? this . _backdropInstance . _clickStream : empty < void > ( ) ;
174181 }
175182
176183 /** Gets an observable that emits when the overlay has been attached. */
@@ -239,31 +246,6 @@ export class OverlayRef implements PortalOutlet {
239246 this . _pane . style . pointerEvents = enablePointer ? 'auto' : 'none' ;
240247 }
241248
242- /** Attaches a backdrop for this overlay. */
243- private _attachBackdrop ( ) {
244- this . _backdropElement = document . createElement ( 'div' ) ;
245- this . _backdropElement . classList . add ( 'cdk-overlay-backdrop' ) ;
246-
247- if ( this . _config . backdropClass ) {
248- this . _backdropElement . classList . add ( this . _config . backdropClass ) ;
249- }
250-
251- // Insert the backdrop before the pane in the DOM order,
252- // in order to handle stacked overlays properly.
253- this . _pane . parentElement ! . insertBefore ( this . _backdropElement , this . _pane ) ;
254-
255- // Forward backdrop clicks such that the consumer of the overlay can perform whatever
256- // action desired when such a click occurs (usually closing the overlay).
257- this . _backdropElement . addEventListener ( 'click' , ( ) => this . _backdropClick . next ( null ) ) ;
258-
259- // Add class to fade-in the backdrop after one frame.
260- requestAnimationFrame ( ( ) => {
261- if ( this . _backdropElement ) {
262- this . _backdropElement . classList . add ( 'cdk-overlay-backdrop-showing' ) ;
263- }
264- } ) ;
265- }
266-
267249 /**
268250 * Updates the stacking order of the element, moving it to the top if necessary.
269251 * This is required in cases where one overlay was detached, while another one,
@@ -277,43 +259,19 @@ export class OverlayRef implements PortalOutlet {
277259 }
278260 }
279261
280- /** Detaches the backdrop (if any) associated with the overlay. */
281- detachBackdrop ( ) : void {
282- let backdropToDetach = this . _backdropElement ;
283-
284- if ( backdropToDetach ) {
285- let finishDetach = ( ) => {
286- // It may not be attached to anything in certain cases (e.g. unit tests).
287- if ( backdropToDetach && backdropToDetach . parentNode ) {
288- backdropToDetach . parentNode . removeChild ( backdropToDetach ) ;
289- }
290-
291- // It is possible that a new portal has been attached to this overlay since we started
292- // removing the backdrop. If that is the case, only clear the backdrop reference if it
293- // is still the same instance that we started to remove.
294- if ( this . _backdropElement == backdropToDetach ) {
295- this . _backdropElement = null ;
296- }
297- } ;
298-
299- backdropToDetach . classList . remove ( 'cdk-overlay-backdrop-showing' ) ;
300-
301- if ( this . _config . backdropClass ) {
302- backdropToDetach . classList . remove ( this . _config . backdropClass ) ;
303- }
304-
305- backdropToDetach . addEventListener ( 'transitionend' , finishDetach ) ;
306-
307- // If the backdrop doesn't have a transition, the `transitionend` event won't fire.
308- // In this case we make it unclickable and we try to remove it after a delay.
309- backdropToDetach . style . pointerEvents = 'none' ;
262+ /** Animates out and disposes of the backdrop. */
263+ disposeBackdrop ( ) : void {
264+ if ( this . _backdropHost ) {
265+ if ( this . _backdropHost . hasAttached ( ) ) {
266+ this . _backdropHost . detach ( ) ;
310267
311- // Run this outside the Angular zone because there's nothing that Angular cares about.
312- // If it were to run inside the Angular zone, every test that used Overlay would have to be
313- // either async or fakeAsync.
314- this . _ngZone . runOutsideAngular ( ( ) => {
315- setTimeout ( finishDetach , 500 ) ;
316- } ) ;
268+ this . _backdropInstance ! . _animationStream . pipe ( first ( ) ) . subscribe ( ( ) => {
269+ this . _backdropHost ! . dispose ( ) ;
270+ this . _backdropHost = this . _backdropInstance = null ;
271+ } ) ;
272+ } else {
273+ this . _backdropHost . dispose ( ) ;
274+ }
317275 }
318276 }
319277}
0 commit comments