66 * found in the LICENSE file at https://angular.dev/license
77 */
88
9- import { Direction , Directionality } from '../bidi' ;
10- import { ComponentPortal , Portal , PortalOutlet , TemplatePortal } from '../portal' ;
9+ import { Location } from '@angular/common' ;
1110import {
1211 AfterRenderRef ,
1312 ComponentRef ,
@@ -16,19 +15,17 @@ import {
1615 NgZone ,
1716 Renderer2 ,
1817 afterNextRender ,
19- afterRender ,
20- untracked ,
2118} from '@angular/core' ;
22- import { Location } from '@angular/common' ;
23- import { Observable , Subject , merge , SubscriptionLike , Subscription } from 'rxjs' ;
24- import { takeUntil } from 'rxjs/operators' ;
19+ import { Observable , Subject , Subscription , SubscriptionLike } from 'rxjs' ;
20+ import { Direction , Directionality } from '../bidi' ;
21+ import { coerceArray , coerceCssPixelValue } from '../coercion' ;
22+ import { ComponentPortal , Portal , PortalOutlet , TemplatePortal } from '../portal' ;
23+ import { BackdropRef } from './backdrop-ref' ;
2524import { OverlayKeyboardDispatcher } from './dispatchers/overlay-keyboard-dispatcher' ;
2625import { OverlayOutsideClickDispatcher } from './dispatchers/overlay-outside-click-dispatcher' ;
2726import { OverlayConfig } from './overlay-config' ;
28- import { coerceCssPixelValue , coerceArray } from '../coercion' ;
2927import { PositionStrategy } from './position/position-strategy' ;
3028import { ScrollStrategy } from './scroll' ;
31- import { BackdropRef } from './backdrop-ref' ;
3229
3330/** An object where all of its properties cannot be written. */
3431export type ImmutableObject < T > = {
@@ -60,10 +57,6 @@ export class OverlayRef implements PortalOutlet {
6057 /** Stream of mouse outside events dispatched to this overlay. */
6158 readonly _outsidePointerEvents = new Subject < MouseEvent > ( ) ;
6259
63- private _renders = new Subject < void > ( ) ;
64-
65- private _afterRenderRef : AfterRenderRef ;
66-
6760 /** Reference to the currently-running `afterNextRender` call. */
6861 private _afterNextRenderRef : AfterRenderRef | undefined ;
6962
@@ -87,18 +80,6 @@ export class OverlayRef implements PortalOutlet {
8780 }
8881
8982 this . _positionStrategy = _config . positionStrategy ;
90-
91- // Users could open the overlay from an `effect`, in which case we need to
92- // run the `afterRender` as `untracked`. We don't recommend that users do
93- // this, but we also don't want to break users who are doing it.
94- this . _afterRenderRef = untracked ( ( ) =>
95- afterRender (
96- ( ) => {
97- this . _renders . next ( ) ;
98- } ,
99- { injector : this . _injector } ,
100- ) ,
101- ) ;
10283 }
10384
10485 /** The overlay's HTML element */
@@ -182,6 +163,7 @@ export class OverlayRef implements PortalOutlet {
182163
183164 // Only emit the `attachments` event once all other setup is done.
184165 this . _attachments . next ( ) ;
166+ this . _detachWhenEmptyMutationObserver ?. disconnect ( ) ;
185167
186168 // Track this overlay by the keyboard dispatcher
187169 this . _keyboardDispatcher . add ( this ) ;
@@ -242,6 +224,7 @@ export class OverlayRef implements PortalOutlet {
242224
243225 // Only emit after everything is detached.
244226 this . _detachments . next ( ) ;
227+ this . _detachWhenEmptyMutationObserver ?. disconnect ( ) ;
245228
246229 // Remove this overlay from keyboard dispatcher tracking.
247230 this . _keyboardDispatcher . remove ( this ) ;
@@ -281,8 +264,7 @@ export class OverlayRef implements PortalOutlet {
281264 }
282265
283266 this . _detachments . complete ( ) ;
284- this . _afterRenderRef . destroy ( ) ;
285- this . _renders . complete ( ) ;
267+ this . _detachWhenEmptyMutationObserver ?. disconnect ( ) ;
286268 }
287269
288270 /** Whether the overlay has attached content. */
@@ -488,34 +470,34 @@ export class OverlayRef implements PortalOutlet {
488470 }
489471 }
490472
473+ private _detachWhenEmptyMutationObserver : MutationObserver | null = null ;
474+
475+ private _detachContent ( ) {
476+ // Needs a couple of checks for the pane and host, because
477+ // they may have been removed by the time the zone stabilizes.
478+ if ( ! this . _pane || ! this . _host || this . _pane . children . length === 0 ) {
479+ if ( this . _pane && this . _config . panelClass ) {
480+ this . _toggleClasses ( this . _pane , this . _config . panelClass , false ) ;
481+ }
482+
483+ if ( this . _host && this . _host . parentElement ) {
484+ this . _previousHostParent = this . _host . parentElement ;
485+ this . _host . remove ( ) ;
486+ }
487+
488+ this . _detachWhenEmptyMutationObserver ?. disconnect ( ) ;
489+ }
490+ }
491+
491492 /** Detaches the overlay content next time the zone stabilizes. */
492493 private _detachContentWhenEmpty ( ) {
493- // Normally we wouldn't have to explicitly run this outside the `NgZone`, however
494- // if the consumer is using `zone-patch-rxjs`, the `Subscription.unsubscribe` call will
495- // be patched to run inside the zone, which will throw us into an infinite loop.
496- this . _ngZone . runOutsideAngular ( ( ) => {
497- // We can't remove the host here immediately, because the overlay pane's content
498- // might still be animating. This stream helps us avoid interrupting the animation
499- // by waiting for the pane to become empty.
500- const subscription = this . _renders
501- . pipe ( takeUntil ( merge ( this . _attachments , this . _detachments ) ) )
502- . subscribe ( ( ) => {
503- // Needs a couple of checks for the pane and host, because
504- // they may have been removed by the time the zone stabilizes.
505- if ( ! this . _pane || ! this . _host || this . _pane . children . length === 0 ) {
506- if ( this . _pane && this . _config . panelClass ) {
507- this . _toggleClasses ( this . _pane , this . _config . panelClass , false ) ;
508- }
509-
510- if ( this . _host && this . _host . parentElement ) {
511- this . _previousHostParent = this . _host . parentElement ;
512- this . _host . remove ( ) ;
513- }
514-
515- subscription . unsubscribe ( ) ;
516- }
517- } ) ;
518- } ) ;
494+ if ( globalThis . MutationObserver && this . _pane ) {
495+ this . _detachWhenEmptyMutationObserver ||= new globalThis . MutationObserver ( ( ) => {
496+ this . _detachContent ( ) ;
497+ } ) ;
498+ this . _detachWhenEmptyMutationObserver . observe ( this . _pane , { childList : true } ) ;
499+ }
500+ this . _detachContent ( ) ;
519501 }
520502
521503 /** Disposes of a scroll strategy. */
0 commit comments