9
9
import { Direction , Directionality } 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' ;
13
- import { take } from 'rxjs/operators' ;
12
+ import { Observable , Subject , merge } from 'rxjs' ;
13
+ import { take , takeUntil } from 'rxjs/operators' ;
14
14
import { OverlayKeyboardDispatcher } from './keyboard/overlay-keyboard-dispatcher' ;
15
15
import { OverlayConfig } from './overlay-config' ;
16
16
import { coerceCssPixelValue , coerceArray } from '@angular/cdk/coercion' ;
@@ -31,6 +31,12 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
31
31
private _backdropClick : Subject < MouseEvent > = new Subject ( ) ;
32
32
private _attachments = new Subject < void > ( ) ;
33
33
private _detachments = new Subject < void > ( ) ;
34
+
35
+ /**
36
+ * Reference to the parent of the `_host` at the time it was detached. Used to restore
37
+ * the `_host` to its original position in the DOM when it gets re-attached.
38
+ */
39
+ private _previousHostParent : HTMLElement ;
34
40
private _keydownEventsObservable : Observable < KeyboardEvent > = Observable . create ( observer => {
35
41
const subscription = this . _keydownEvents . subscribe ( observer ) ;
36
42
this . _keydownEventSubscriptions ++ ;
@@ -99,6 +105,10 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
99
105
}
100
106
101
107
// Update the pane element with the given configuration.
108
+ if ( ! this . _host . parentElement && this . _previousHostParent ) {
109
+ this . _previousHostParent . appendChild ( this . _host ) ;
110
+ }
111
+
102
112
this . _updateStackingOrder ( ) ;
103
113
this . _updateElementSize ( ) ;
104
114
this . _updateElementDirection ( ) ;
@@ -176,6 +186,26 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
176
186
// Remove this overlay from keyboard dispatcher tracking.
177
187
this . _keyboardDispatcher . remove ( this ) ;
178
188
189
+ // Keeping the host element in DOM the can cause scroll jank, because it still gets rendered,
190
+ // even though it's transparent and unclickable. We can't remove the host here immediately,
191
+ // because the overlay pane's content might still be animating. This stream helps us avoid
192
+ // interrupting the animation by waiting for the pane to become empty.
193
+ const subscription = this . _ngZone . onStable
194
+ . asObservable ( )
195
+ . pipe ( takeUntil ( merge ( this . _attachments , this . _detachments ) ) )
196
+ . subscribe ( ( ) => {
197
+ // Needs a couple of checks for the pane and host, because
198
+ // they may have been removed by the time the zone stabilizes.
199
+ if ( ! this . _pane || ! this . _host || this . _pane . children . length === 0 ) {
200
+ if ( this . _host && this . _host . parentElement ) {
201
+ this . _previousHostParent = this . _host . parentElement ;
202
+ this . _previousHostParent . removeChild ( this . _host ) ;
203
+ }
204
+
205
+ subscription . unsubscribe ( ) ;
206
+ }
207
+ } ) ;
208
+
179
209
return detachmentResult ;
180
210
}
181
211
@@ -203,7 +233,7 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
203
233
this . _host = null ! ;
204
234
}
205
235
206
- this . _pane = null ! ;
236
+ this . _previousHostParent = this . _pane = null ! ;
207
237
208
238
if ( isAttached ) {
209
239
this . _detachments . next ( ) ;
0 commit comments