99import { Direction } from '@angular/cdk/bidi' ;
1010import { ComponentPortal , Portal , PortalOutlet , TemplatePortal } from '@angular/cdk/portal' ;
1111import { ComponentRef , EmbeddedViewRef , NgZone } from '@angular/core' ;
12- import { Observable , Subject } from 'rxjs' ;
12+ import { Observable , Subject , empty } from 'rxjs' ;
1313import { take } from 'rxjs/operators' ;
1414import { OverlayKeyboardDispatcher } from './keyboard/overlay-keyboard-dispatcher' ;
1515import { OverlayConfig } from './overlay-config' ;
16+ import { CdkOverlayBackdrop } from './backdrop' ;
1617
1718
1819/** An object where all of its properties cannot be written. */
@@ -25,10 +26,10 @@ export type ImmutableObject<T> = {
2526 * Used to manipulate or dispose of said overlay.
2627 */
2728export class OverlayRef implements PortalOutlet {
28- private _backdropElement : HTMLElement | null = null ;
29- private _backdropClick : Subject < MouseEvent > = new Subject ( ) ;
29+ private _backdropClick = new Subject < MouseEvent > ( ) ;
3030 private _attachments = new Subject < void > ( ) ;
3131 private _detachments = new Subject < void > ( ) ;
32+ private _backdropInstance : CdkOverlayBackdrop | null ;
3233
3334 /** Stream of keydown events dispatched to this overlay. */
3435 _keydownEvents = new Subject < KeyboardEvent > ( ) ;
@@ -37,10 +38,10 @@ export class OverlayRef implements PortalOutlet {
3738 private _portalOutlet : PortalOutlet ,
3839 private _host : HTMLElement ,
3940 private _pane : HTMLElement ,
41+ private _backdropHost : PortalOutlet | null ,
4042 private _config : ImmutableObject < OverlayConfig > ,
4143 private _ngZone : NgZone ,
42- private _keyboardDispatcher : OverlayKeyboardDispatcher ,
43- private _document : Document ) {
44+ private _keyboardDispatcher : OverlayKeyboardDispatcher ) {
4445
4546 if ( _config . scrollStrategy ) {
4647 _config . scrollStrategy . attach ( this ) ;
@@ -54,7 +55,7 @@ export class OverlayRef implements PortalOutlet {
5455
5556 /** The overlay's backdrop HTML element. */
5657 get backdropElement ( ) : HTMLElement | null {
57- return this . _backdropElement ;
58+ return this . _backdropInstance ? this . _backdropInstance . _element . nativeElement : null ;
5859 }
5960
6061 /**
@@ -78,7 +79,7 @@ export class OverlayRef implements PortalOutlet {
7879 * @returns The portal attachment result.
7980 */
8081 attach ( portal : Portal < any > ) : any {
81- let attachResult = this . _portalOutlet . attach ( portal ) ;
82+ const attachResult = this . _portalOutlet . attach ( portal ) ;
8283
8384 if ( this . _config . positionStrategy ) {
8485 this . _config . positionStrategy . attach ( this ) ;
@@ -109,8 +110,10 @@ export class OverlayRef implements PortalOutlet {
109110 // Enable pointer events for the overlay pane element.
110111 this . _togglePointerEvents ( true ) ;
111112
112- if ( this . _config . hasBackdrop ) {
113- this . _attachBackdrop ( ) ;
113+ if ( this . _backdropHost ) {
114+ this . _backdropInstance =
115+ this . _backdropHost . attach ( new ComponentPortal ( CdkOverlayBackdrop ) ) . instance ;
116+ this . _backdropInstance ! . _setClass ( this . _config . backdropClass ! ) ;
114117 }
115118
116119 if ( this . _config . panelClass ) {
@@ -140,7 +143,9 @@ export class OverlayRef implements PortalOutlet {
140143 return ;
141144 }
142145
143- this . detachBackdrop ( ) ;
146+ if ( this . _backdropHost && this . _backdropHost . hasAttached ( ) ) {
147+ this . _backdropHost . detach ( ) ;
148+ }
144149
145150 // When the overlay is detached, the pane element should disable pointer events.
146151 // This is necessary because otherwise the pane element will cover the page and disable
@@ -178,7 +183,7 @@ export class OverlayRef implements PortalOutlet {
178183 this . _config . scrollStrategy . disable ( ) ;
179184 }
180185
181- this . detachBackdrop ( ) ;
186+ this . disposeBackdrop ( ) ;
182187 this . _keyboardDispatcher . remove ( this ) ;
183188 this . _portalOutlet . dispose ( ) ;
184189 this . _attachments . complete ( ) ;
@@ -204,7 +209,7 @@ export class OverlayRef implements PortalOutlet {
204209
205210 /** Gets an observable that emits when the backdrop has been clicked. */
206211 backdropClick ( ) : Observable < MouseEvent > {
207- return this . _backdropClick . asObservable ( ) ;
212+ return this . _backdropInstance ? this . _backdropInstance . _clickStream : empty ( ) ;
208213 }
209214
210215 /** Gets an observable that emits when the overlay has been attached. */
@@ -283,40 +288,6 @@ export class OverlayRef implements PortalOutlet {
283288 this . _pane . style . pointerEvents = enablePointer ? 'auto' : 'none' ;
284289 }
285290
286- /** Attaches a backdrop for this overlay. */
287- private _attachBackdrop ( ) {
288- const showingClass = 'cdk-overlay-backdrop-showing' ;
289-
290- this . _backdropElement = this . _document . createElement ( 'div' ) ;
291- this . _backdropElement . classList . add ( 'cdk-overlay-backdrop' ) ;
292-
293- if ( this . _config . backdropClass ) {
294- this . _backdropElement . classList . add ( this . _config . backdropClass ) ;
295- }
296-
297- // Insert the backdrop before the pane in the DOM order,
298- // in order to handle stacked overlays properly.
299- this . _host . parentElement ! . insertBefore ( this . _backdropElement , this . _host ) ;
300-
301- // Forward backdrop clicks such that the consumer of the overlay can perform whatever
302- // action desired when such a click occurs (usually closing the overlay).
303- this . _backdropElement . addEventListener ( 'click' ,
304- ( event : MouseEvent ) => this . _backdropClick . next ( event ) ) ;
305-
306- // Add class to fade-in the backdrop after one frame.
307- if ( typeof requestAnimationFrame !== 'undefined' ) {
308- this . _ngZone . runOutsideAngular ( ( ) => {
309- requestAnimationFrame ( ( ) => {
310- if ( this . _backdropElement ) {
311- this . _backdropElement . classList . add ( showingClass ) ;
312- }
313- } ) ;
314- } ) ;
315- } else {
316- this . _backdropElement . classList . add ( showingClass ) ;
317- }
318- }
319-
320291 /**
321292 * Updates the stacking order of the element, moving it to the top if necessary.
322293 * This is required in cases where one overlay was detached, while another one,
@@ -330,43 +301,30 @@ export class OverlayRef implements PortalOutlet {
330301 }
331302 }
332303
333- /** Detaches the backdrop (if any) associated with the overlay. */
334- detachBackdrop ( ) : void {
335- let backdropToDetach = this . _backdropElement ;
336-
337- if ( backdropToDetach ) {
338- let finishDetach = ( ) => {
339- // It may not be attached to anything in certain cases (e.g. unit tests).
340- if ( backdropToDetach && backdropToDetach . parentNode ) {
341- backdropToDetach . parentNode . removeChild ( backdropToDetach ) ;
342- }
343-
344- // It is possible that a new portal has been attached to this overlay since we started
345- // removing the backdrop. If that is the case, only clear the backdrop reference if it
346- // is still the same instance that we started to remove.
347- if ( this . _backdropElement == backdropToDetach ) {
348- this . _backdropElement = null ;
349- }
350- } ;
351-
352- backdropToDetach . classList . remove ( 'cdk-overlay-backdrop-showing' ) ;
304+ /** Animates out and disposes of the backdrop. */
305+ disposeBackdrop ( ) : void {
306+ if ( this . _backdropHost ) {
307+ if ( this . _backdropHost . hasAttached ( ) ) {
308+ this . _backdropHost . detach ( ) ;
353309
354- if ( this . _config . backdropClass ) {
355- backdropToDetach . classList . remove ( this . _config . backdropClass ) ;
310+ this . _backdropInstance ! . _animationStream . pipe ( take ( 1 ) ) . subscribe ( ( ) => {
311+ this . _backdropHost ! . dispose ( ) ;
312+ this . _backdropHost = this . _backdropInstance = null ;
313+ } ) ;
314+ } else {
315+ this . _backdropHost . dispose ( ) ;
356316 }
357-
358- backdropToDetach . addEventListener ( 'transitionend' , finishDetach ) ;
359-
360- // If the backdrop doesn't have a transition, the `transitionend` event won't fire.
361- // In this case we make it unclickable and we try to remove it after a delay.
362- backdropToDetach . style . pointerEvents = 'none' ;
363-
364- // Run this outside the Angular zone because there's nothing that Angular cares about.
365- // If it were to run inside the Angular zone, every test that used Overlay would have to be
366- // either async or fakeAsync.
367- this . _ngZone . runOutsideAngular ( ( ) => setTimeout ( finishDetach , 500 ) ) ;
368317 }
369318 }
319+
320+ /**
321+ * Detaches the backdrop (if any) associated with the overlay.
322+ * @deprecated Use `disposeBackdrop` instead.
323+ * @deletion -target 7.0.0
324+ */
325+ detachBackdrop ( ) : void {
326+ this . disposeBackdrop ( ) ;
327+ }
370328}
371329
372330function formatCssUnit ( value : number | string ) {
0 commit comments