Skip to content

Commit 93169b2

Browse files
committed
fixup! fix(material/stepper): switch away from animations module
1 parent 7c958f4 commit 93169b2

File tree

3 files changed

+41
-4
lines changed

3 files changed

+41
-4
lines changed

src/material/stepper/stepper.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@
203203
transform: none;
204204
height: auto;
205205
}
206+
207+
.mat-stepper-horizontal:not(.mat-stepper-animating) &.mat-horizontal-stepper-content-current {
208+
overflow: visible;
209+
}
206210
}
207211

208212
.mat-horizontal-content-container {

src/material/stepper/stepper.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {CdkStep, CdkStepper} from '@angular/cdk/stepper';
1010
import {
1111
AfterContentInit,
12+
AfterViewInit,
1213
ANIMATION_MODULE_TYPE,
1314
ChangeDetectionStrategy,
1415
Component,
@@ -23,6 +24,7 @@ import {
2324
Output,
2425
QueryList,
2526
Renderer2,
27+
signal,
2628
TemplateRef,
2729
ViewChildren,
2830
ViewContainerRef,
@@ -127,6 +129,7 @@ export class MatStep extends CdkStep implements ErrorStateMatcher, AfterContentI
127129
'[class.mat-stepper-label-position-bottom]':
128130
'orientation === "horizontal" && labelPosition == "bottom"',
129131
'[class.mat-stepper-header-position-bottom]': 'headerPosition === "bottom"',
132+
'[class.mat-stepper-animating]': '_isAnimating()',
130133
'[style.--mat-stepper-animation-duration]': '_getAnimationDuration()',
131134
'[attr.aria-orientation]': 'orientation',
132135
'role': 'tablist',
@@ -136,11 +139,12 @@ export class MatStep extends CdkStep implements ErrorStateMatcher, AfterContentI
136139
changeDetection: ChangeDetectionStrategy.OnPush,
137140
imports: [NgTemplateOutlet, MatStepHeader],
138141
})
139-
export class MatStepper extends CdkStepper implements AfterContentInit, OnDestroy {
142+
export class MatStepper extends CdkStepper implements AfterViewInit, AfterContentInit, OnDestroy {
140143
private _ngZone = inject(NgZone);
141144
private _renderer = inject(Renderer2);
142145
private _animationsModule = inject(ANIMATION_MODULE_TYPE, {optional: true});
143146
private _cleanupTransition: (() => void) | undefined;
147+
protected _isAnimating = signal(false);
144148

145149
/** The list of step headers of the steps in the stepper. */
146150
@ViewChildren(MatStepHeader) override _stepHeader: QueryList<MatStepHeader> = undefined!;
@@ -223,7 +227,9 @@ export class MatStepper extends CdkStepper implements AfterContentInit, OnDestro
223227
this.selectedIndexChange.pipe(takeUntil(this._destroyed)).subscribe(() => {
224228
const duration = this._getAnimationDuration();
225229
if (duration === '0ms' || duration === '0s') {
226-
this.animationDone.emit();
230+
this._onAnimationDone();
231+
} else {
232+
this._isAnimating.set(true);
227233
}
228234
});
229235

@@ -244,6 +250,23 @@ export class MatStepper extends CdkStepper implements AfterContentInit, OnDestro
244250
});
245251
}
246252

253+
override ngAfterViewInit(): void {
254+
super.ngAfterViewInit();
255+
256+
// Prior to #30314 the stepper had animation `done` events bound to each animated container.
257+
// The animations module was firing them on initialization and for each subsequent animation.
258+
// Since the events were bound in the template, it had the unintended side-effect of triggering
259+
// change detection as well. It appears that this side-effect ended up being load-bearing,
260+
// because it was ensuring that the content elements (e.g. `matStepLabel`) that are defined
261+
// in sub-components actually get picked up in a timely fashion. This subscription simulates
262+
// the same change detection by using `queueMicrotask` similarly to the animations module.
263+
if (typeof queueMicrotask === 'function') {
264+
this._animatedContainers.changes
265+
.pipe(startWith(null), takeUntil(this._destroyed))
266+
.subscribe(() => queueMicrotask(() => this._stateChanged()));
267+
}
268+
}
269+
247270
override ngOnDestroy(): void {
248271
super.ngOnDestroy();
249272
this._cleanupTransition?.();
@@ -292,7 +315,12 @@ export class MatStepper extends CdkStepper implements AfterContentInit, OnDestro
292315
this._animatedContainers.find(ref => ref.nativeElement === target);
293316

294317
if (shouldEmit) {
295-
this.animationDone.emit();
318+
this._onAnimationDone();
296319
}
297320
};
321+
322+
private _onAnimationDone() {
323+
this._isAnimating.set(false);
324+
this.animationDone.emit();
325+
}
298326
}

tools/public_api_guard/material/stepper.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { Subject } from 'rxjs';
3434
import { TemplatePortal } from '@angular/cdk/portal';
3535
import { TemplateRef } from '@angular/core';
3636
import { ThemePalette } from '@angular/material/core';
37+
import { WritableSignal } from '@angular/core';
3738

3839
// @public
3940
export const MAT_STEPPER_INTL_PROVIDER: {
@@ -116,7 +117,7 @@ export class MatStepLabel extends CdkStepLabel {
116117
}
117118

118119
// @public (undocumented)
119-
export class MatStepper extends CdkStepper implements AfterContentInit, OnDestroy {
120+
export class MatStepper extends CdkStepper implements AfterViewInit, AfterContentInit, OnDestroy {
120121
constructor(...args: unknown[]);
121122
_animatedContainers: QueryList<ElementRef>;
122123
readonly animationDone: EventEmitter<void>;
@@ -129,11 +130,15 @@ export class MatStepper extends CdkStepper implements AfterContentInit, OnDestro
129130
headerPosition: 'top' | 'bottom';
130131
_iconOverrides: Record<string, TemplateRef<MatStepperIconContext>>;
131132
_icons: QueryList<MatStepperIcon>;
133+
// (undocumented)
134+
protected _isAnimating: WritableSignal<boolean>;
132135
protected _isServer: boolean;
133136
labelPosition: 'bottom' | 'end';
134137
// (undocumented)
135138
ngAfterContentInit(): void;
136139
// (undocumented)
140+
ngAfterViewInit(): void;
141+
// (undocumented)
137142
ngOnDestroy(): void;
138143
_stepHeader: QueryList<MatStepHeader>;
139144
// (undocumented)

0 commit comments

Comments
 (0)