Skip to content

Commit fa0011c

Browse files
committed
feat(stepper): add input for controlling the animation duration
Adds a new input called `animationDuration` that allows users to control the animation duration of a stepper, similarly to other components. Fixes #17130.
1 parent 9d6f4c2 commit fa0011c

File tree

12 files changed

+101
-8
lines changed

12 files changed

+101
-8
lines changed

src/material-examples/example-module.ts

Lines changed: 4 additions & 4 deletions
Large diffs are not rendered by default.

src/material-examples/material/stepper/module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {StepperOptionalExample} from './stepper-optional/stepper-optional-exampl
1313
import {StepperOverviewExample} from './stepper-overview/stepper-overview-example';
1414
import {StepperStatesExample} from './stepper-states/stepper-states-example';
1515
import {StepperVerticalExample} from './stepper-vertical/stepper-vertical-example';
16+
import {StepperAnimationsExample} from './stepper-animations/stepper-animations-example';
1617

1718
export {
1819
StepperEditableExample,
@@ -32,6 +33,7 @@ const EXAMPLES = [
3233
StepperOverviewExample,
3334
StepperStatesExample,
3435
StepperVerticalExample,
36+
StepperAnimationsExample,
3537
];
3638

3739
@NgModule({
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.example-input-wrapper {
2+
margin-bottom: 16px;
3+
}
4+
5+
label {
6+
margin-right: 4px;
7+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<div class="example-input-wrapper">
2+
<label for="duration">Animation duration:</label>
3+
<input id="duration" value="2000" type="number" min="0" step="100" #duration>
4+
</div>
5+
6+
<mat-vertical-stepper [linear]="false" #stepper [animationDuration]="duration.value">
7+
<mat-step [stepControl]="firstFormGroup">
8+
<form [formGroup]="firstFormGroup">
9+
<ng-template matStepLabel>Fill out your name</ng-template>
10+
<mat-form-field>
11+
<input matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
12+
</mat-form-field>
13+
<div>
14+
<button mat-button matStepperNext>Next</button>
15+
</div>
16+
</form>
17+
</mat-step>
18+
<mat-step [stepControl]="secondFormGroup">
19+
<form [formGroup]="secondFormGroup">
20+
<ng-template matStepLabel>Fill out your address</ng-template>
21+
<mat-form-field>
22+
<input matInput placeholder="Address" formControlName="secondCtrl" required>
23+
</mat-form-field>
24+
<div>
25+
<button mat-button matStepperPrevious>Back</button>
26+
<button mat-button matStepperNext>Next</button>
27+
</div>
28+
</form>
29+
</mat-step>
30+
<mat-step>
31+
<ng-template matStepLabel>Done</ng-template>
32+
You are now done.
33+
<div>
34+
<button mat-button matStepperPrevious>Back</button>
35+
<button mat-button (click)="stepper.reset()">Reset</button>
36+
</div>
37+
</mat-step>
38+
</mat-vertical-stepper>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {Component} from '@angular/core';
2+
import {FormBuilder, FormGroup} from '@angular/forms';
3+
4+
/**
5+
* @title Stepper animations
6+
*/
7+
@Component({
8+
selector: 'stepper-animations-example',
9+
templateUrl: 'stepper-animations-example.html',
10+
styleUrls: ['stepper-animations-example.css'],
11+
})
12+
export class StepperAnimationsExample {
13+
constructor(private _formBuilder: FormBuilder) {}
14+
firstFormGroup: FormGroup = this._formBuilder.group({firstCtrl: ['']});
15+
secondFormGroup: FormGroup = this._formBuilder.group({secondCtrl: ['']});
16+
}

src/material/stepper/stepper-animations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ export const matStepperAnimations: {
2727
state('previous', style({transform: 'translate3d(-100%, 0, 0)', visibility: 'hidden'})),
2828
state('current', style({transform: 'none', visibility: 'visible'})),
2929
state('next', style({transform: 'translate3d(100%, 0, 0)', visibility: 'hidden'})),
30-
transition('* => *', animate('500ms cubic-bezier(0.35, 0, 0.25, 1)'))
30+
transition('* => *', animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'))
3131
]),
3232

3333
/** Animation that transitions the step along the Y axis in a vertical stepper. */
3434
verticalStepTransition: trigger('stepTransition', [
3535
state('previous', style({height: '0px', visibility: 'hidden'})),
3636
state('next', style({height: '0px', visibility: 'hidden'})),
3737
state('current', style({height: '*', visibility: 'visible'})),
38-
transition('* <=> current', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)'))
38+
transition('* <=> current', animate('{{animationDuration}} cubic-bezier(0.4, 0.0, 0.2, 1)'))
3939
])
4040
};

src/material/stepper/stepper-horizontal.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
<div *ngFor="let step of steps; let i = index"
3030
[attr.tabindex]="selectedIndex === i ? 0 : null"
3131
class="mat-horizontal-stepper-content" role="tabpanel"
32-
[@stepTransition]="_getAnimationDirection(i)"
32+
[@stepTransition]="{
33+
value: _getAnimationDirection(i),
34+
params: {animationDuration: animationDuration}
35+
}"
3336
(@stepTransition.done)="_animationDone.next($event)"
3437
[id]="_getStepContentId(i)"
3538
[attr.aria-labelledby]="_getStepLabelId(i)"

src/material/stepper/stepper-vertical.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
<div class="mat-vertical-content-container" [class.mat-stepper-vertical-line]="!isLast">
2525
<div class="mat-vertical-stepper-content" role="tabpanel"
2626
[attr.tabindex]="selectedIndex === i ? 0 : null"
27-
[@stepTransition]="_getAnimationDirection(i)"
27+
[@stepTransition]="{
28+
value: _getAnimationDirection(i),
29+
params: {animationDuration: animationDuration}
30+
}"
2831
(@stepTransition.done)="_animationDone.next($event)"
2932
[id]="_getStepContentId(i)"
3033
[attr.aria-labelledby]="_getStepLabelId(i)"

src/material/stepper/stepper.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@ by placing a `matStepperIcon` for each of the icons that you want to override. T
163163

164164
Note that you aren't limited to using the `mat-icon` component when providing custom icons.
165165

166+
### Controlling the stepper animation
167+
You can control the duration of the stepper's animation using the `animationDuration` input. If you
168+
want to disable the animation completely, you can do so by setting the properties to `0ms`.
169+
170+
<!-- example(stepper-animations) -->
171+
166172
#### Step States
167173
You can set the state of a step to whatever you want. The given state by default maps to an icon.
168174
However, it can be overridden the same way as mentioned above.

src/material/stepper/stepper.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,14 @@ describe('MatStepper', () => {
390390
expect(secondStepContentEl.getAttribute('tabindex')).toBe('0');
391391
});
392392

393+
it('should add units to unit-less values passed in to animationDuration', () => {
394+
const stepperComponent: MatStepper =
395+
fixture.debugElement.query(By.directive(MatStepper))!.componentInstance;
396+
397+
stepperComponent.animationDuration = '1337';
398+
expect(stepperComponent.animationDuration).toBe('1337ms');
399+
});
400+
393401
});
394402

395403
describe('basic stepper when attempting to set the selected step too early', () => {

src/material/stepper/stepper.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ export class MatStepper extends CdkStepper implements AfterContentInit {
9898
/** Whether ripples should be disabled for the step headers. */
9999
@Input() disableRipple: boolean;
100100

101+
/** Duration for the animation. Will be normalized to milliseconds if no units are set. */
102+
@Input()
103+
get animationDuration(): string { return this._animationDuration; }
104+
set animationDuration(value: string) {
105+
this._animationDuration = /^\d+$/.test(value) ? value + 'ms' : value;
106+
}
107+
private _animationDuration = '500ms';
108+
101109
/** Consumer-specified template-refs to be used to override the header icons. */
102110
_iconOverrides: {[key: string]: TemplateRef<MatStepperIconContext>} = {};
103111

@@ -181,5 +189,6 @@ export class MatVerticalStepper extends MatStepper {
181189
@Inject(DOCUMENT) _document?: any) {
182190
super(dir, changeDetectorRef, elementRef, _document);
183191
this._orientation = 'vertical';
192+
this.animationDuration = '225ms';
184193
}
185194
}

tools/public_api_guard/material/stepper.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export declare class MatStepper extends CdkStepper implements AfterContentInit {
5151
_stepHeader: QueryList<MatStepHeader>;
5252
_steps: QueryList<MatStep>;
5353
readonly animationDone: EventEmitter<void>;
54+
animationDuration: string;
5455
disableRipple: boolean;
5556
ngAfterContentInit(): void;
5657
}

0 commit comments

Comments
 (0)