@@ -36,7 +36,7 @@ import {
36
36
ANIMATION_MODULE_TYPE ,
37
37
} from '@angular/core' ;
38
38
import { Subject } from 'rxjs' ;
39
- import { distinctUntilChanged , filter , startWith , take } from 'rxjs/operators' ;
39
+ import { filter , startWith , take } from 'rxjs/operators' ;
40
40
import { MatAccordionBase , MatAccordionTogglePosition , MAT_ACCORDION } from './accordion-base' ;
41
41
import { matExpansionAnimations } from './expansion-animations' ;
42
42
import { MAT_EXPANSION_PANEL } from './expansion-panel-base' ;
@@ -147,9 +147,6 @@ export class MatExpansionPanel
147
147
/** ID for the associated header element. Used for a11y labelling. */
148
148
_headerId = `mat-expansion-panel-header-${ uniqueId ++ } ` ;
149
149
150
- /** Stream of body animation done events. */
151
- readonly _bodyAnimationDone = new Subject < AnimationEvent > ( ) ;
152
-
153
150
constructor (
154
151
@Optional ( ) @SkipSelf ( ) @Inject ( MAT_ACCORDION ) accordion : MatAccordionBase ,
155
152
_changeDetectorRef : ChangeDetectorRef ,
@@ -165,24 +162,6 @@ export class MatExpansionPanel
165
162
this . accordion = accordion ;
166
163
this . _document = _document ;
167
164
168
- // We need a Subject with distinctUntilChanged, because the `done` event
169
- // fires twice on some browsers. See https://github.com/angular/angular/issues/24084
170
- this . _bodyAnimationDone
171
- . pipe (
172
- distinctUntilChanged ( ( x , y ) => {
173
- return x . fromState === y . fromState && x . toState === y . toState ;
174
- } ) ,
175
- )
176
- . subscribe ( event => {
177
- if ( event . fromState !== 'void' ) {
178
- if ( event . toState === 'expanded' ) {
179
- this . afterExpand . emit ( ) ;
180
- } else if ( event . toState === 'collapsed' ) {
181
- this . afterCollapse . emit ( ) ;
182
- }
183
- }
184
- } ) ;
185
-
186
165
if ( defaultOptions ) {
187
166
this . hideToggle = defaultOptions . hideToggle ;
188
167
}
@@ -237,7 +216,6 @@ export class MatExpansionPanel
237
216
238
217
override ngOnDestroy ( ) {
239
218
super . ngOnDestroy ( ) ;
240
- this . _bodyAnimationDone . complete ( ) ;
241
219
this . _inputChanges . complete ( ) ;
242
220
}
243
221
@@ -251,6 +229,35 @@ export class MatExpansionPanel
251
229
252
230
return false ;
253
231
}
232
+
233
+ /** Called when the expansion animation has started. */
234
+ protected _animationStarted ( event : AnimationEvent ) {
235
+ if ( ! isInitialAnimation ( event ) ) {
236
+ // Prevent the user from tabbing into the content while it's animating.
237
+ // TODO(crisbeto): maybe use `inert` to prevent focus from entering while closed as well
238
+ // instead of `visibility`? Will allow us to clean up some code but needs more testing.
239
+ this . _body ?. nativeElement . setAttribute ( 'inert' , '' ) ;
240
+ }
241
+ }
242
+
243
+ /** Called when the expansion animation has finished. */
244
+ protected _animationDone ( event : AnimationEvent ) {
245
+ if ( ! isInitialAnimation ( event ) ) {
246
+ if ( event . toState === 'expanded' ) {
247
+ this . afterExpand . emit ( ) ;
248
+ } else if ( event . toState === 'collapsed' ) {
249
+ this . afterCollapse . emit ( ) ;
250
+ }
251
+
252
+ // Re-enabled tabbing once the animation is finished.
253
+ this . _body ?. nativeElement . removeAttribute ( 'inert' ) ;
254
+ }
255
+ }
256
+ }
257
+
258
+ /** Checks whether an animation is the initial setup animation. */
259
+ function isInitialAnimation ( event : AnimationEvent ) : boolean {
260
+ return event . fromState === 'void' ;
254
261
}
255
262
256
263
/**
0 commit comments