Skip to content

Commit 62d3ec6

Browse files
committed
feat(material/stepper): allow for orientation to be changed dynamically
Combines `mat-vertical-stepper` and `mat-horizontal-stepper` into a single `mat-stepper` class in order to allow for the orientation to be changed dynamically. Also deprecates `MatVerticalStepper` and `MatHorizontalStepper`. This is a reimplementation of #9173, however this time I took a different approach which should make it easier to maintain and eventually remove the two separate steppers. It should result in a smaller bundle as well. The main differences are: 1. Rather than have 3 components (`MatStepper`, `MatVerticalStepper` and `MatHorizontalStepper`), these changes combine everything into `MatStepper` while `MatVerticalStepper` and `MatHorizontalStepper` are only used as injection tokens for backwards compatibility. The `selector` and `exportAs` of `MatStepper` is changed to match the two individual steppers and the orientation is inferred from the tag name. This will make it much easier to remove the deprecated directives. Furthermore, it should result in a smaller bundle since the template and styles only need to be inlined in one place. 2. `MatVerticalStepper` and `MatHorizontalStepper` are turned into very basic directives that have the same public API as `MatStepper` and they proxy everything to it. This is primarily so that if somebody managed to get a hold of a `MatVerticalStepper` or `MatHorizontalStepper` instance, or they used the old classes to type their own code, it wouldn't result in a breaking change. Relates to #7700.
1 parent 85b5df6 commit 62d3ec6

File tree

13 files changed

+214
-217
lines changed

13 files changed

+214
-217
lines changed

src/cdk/stepper/stepper.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -315,11 +315,16 @@ export class CdkStepper implements AfterContentInit, AfterViewInit, OnDestroy {
315315
/** Used to track unique ID for each stepper component. */
316316
_groupId: number;
317317

318-
// Note that this isn't an `Input` so it doesn't bleed into the Material stepper.
319318
/** Orientation of the stepper. */
319+
@Input()
320320
get orientation(): StepperOrientation { return this._orientation; }
321321
set orientation(value: StepperOrientation) {
322-
this._updateOrientation(value);
322+
// This is a protected method so that `MatSteppter` can hook into it.
323+
this._orientation = value;
324+
325+
if (this._keyManager) {
326+
this._keyManager.withVerticalOrientation(value === 'vertical');
327+
}
323328
}
324329

325330
/**
@@ -432,16 +437,6 @@ export class CdkStepper implements AfterContentInit, AfterViewInit, OnDestroy {
432437
this._getGuidelineLogic(step, isCurrentStep, state);
433438
}
434439

435-
/** Updates the stepper orientation. */
436-
protected _updateOrientation(value: StepperOrientation) {
437-
// This is a protected method so that `MatSteppter` can hook into it.
438-
this._orientation = value;
439-
440-
if (this._keyManager) {
441-
this._keyManager.withVerticalOrientation(value === 'vertical');
442-
}
443-
}
444-
445440
private _getDefaultIndicatorLogic(step: CdkStep, isCurrentStep: boolean): StepState {
446441
if (step._showError && step.hasError && !isCurrentStep) {
447442
return STEP_STATE.ERROR;

src/dev-app/stepper/stepper-demo.html

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
<p>
55
<mat-checkbox [(ngModel)]="disableRipple">Disable header ripple</mat-checkbox>
66
</p>
7+
<p>
8+
<mat-checkbox [(ngModel)]="isVertical">Vertical</mat-checkbox>
9+
</p>
710
<p>
811
<button mat-stroked-button (click)="showLabelBottom = !showLabelBottom">
912
Toggle label position
@@ -18,10 +21,15 @@
1821
</mat-form-field>
1922
</p>
2023

21-
<h3>Linear Vertical Stepper Demo using a single form</h3>
24+
<h3>Linear Stepper Demo using a single form</h3>
2225
<form [formGroup]="formGroup">
23-
<mat-vertical-stepper #linearVerticalStepper="matVerticalStepper" formArrayName="formArray"
24-
[linear]="!isNonLinear" [disableRipple]="disableRipple" [color]="theme">
26+
<mat-stepper
27+
#linearStepper="matVerticalStepper"
28+
formArrayName="formArray"
29+
[orientation]="isVertical ? 'vertical' : 'horizontal'"
30+
[linear]="!isNonLinear"
31+
[disableRipple]="disableRipple"
32+
[color]="theme">
2533
<mat-step formGroupName="0" [stepControl]="formArray?.get([0]) === null ? undefined! : formArray?.get([0])!">
2634
<ng-template matStepLabel>Fill out your name</ng-template>
2735
<mat-form-field>
@@ -61,10 +69,10 @@ <h3>Linear Vertical Stepper Demo using a single form</h3>
6169
Everything seems correct.
6270
<div>
6371
<button mat-button>Done</button>
64-
<button type="button" mat-button (click)="linearVerticalStepper.reset()">Reset</button>
72+
<button type="button" mat-button (click)="linearStepper.reset()">Reset</button>
6573
</div>
6674
</mat-step>
67-
</mat-vertical-stepper>
75+
</mat-stepper>
6876
</form>
6977

7078
<h3>Linear Horizontal Stepper Demo using a different form for each step</h3>

src/dev-app/stepper/stepper-demo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export class StepperDemo implements OnInit {
2121
isNonEditable = false;
2222
disableRipple = false;
2323
showLabelBottom = false;
24+
isVertical = false;
2425

2526
nameFormGroup: FormGroup;
2627
emailFormGroup: FormGroup;

src/material/stepper/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
export {StepperOrientation} from '@angular/cdk/stepper';
910
export * from './stepper-module';
1011
export * from './step-label';
1112
export * from './stepper';

src/material/stepper/stepper-animations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const matStepperAnimations: {
2323
readonly verticalStepTransition: AnimationTriggerMetadata;
2424
} = {
2525
/** Animation that transitions the step along the X axis in a horizontal stepper. */
26-
horizontalStepTransition: trigger('stepTransition', [
26+
horizontalStepTransition: trigger('horizontalStepTransition', [
2727
state('previous', style({transform: 'translate3d(-100%, 0, 0)', visibility: 'hidden'})),
2828
// Transition to '', rather than `visible`, because visibility on a child element overrides
2929
// the one from the parent, making this element focusable inside of a `hidden` element.
@@ -33,7 +33,7 @@ export const matStepperAnimations: {
3333
]),
3434

3535
/** Animation that transitions the step along the Y axis in a vertical stepper. */
36-
verticalStepTransition: trigger('stepTransition', [
36+
verticalStepTransition: trigger('verticalStepTransition', [
3737
state('previous', style({height: '0px', visibility: 'hidden'})),
3838
state('next', style({height: '0px', visibility: 'hidden'})),
3939
// Transition to '', rather than `visible`, because visibility on a child element overrides

src/material/stepper/stepper-horizontal.html

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/material/stepper/stepper-module.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ import {MatStepContent} from './step-content';
3434
],
3535
exports: [
3636
MatCommonModule,
37-
MatHorizontalStepper,
38-
MatVerticalStepper,
3937
MatStep,
4038
MatStepLabel,
4139
MatStepper,

src/material/stepper/stepper-vertical.html

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/material/stepper/stepper.html

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<ng-container [ngSwitch]="orientation">
2+
<!-- Horizontal stepper -->
3+
<ng-container *ngSwitchCase="'horizontal'">
4+
<div class="mat-horizontal-stepper-header-container">
5+
<ng-container *ngFor="let step of steps; let i = index; let isLast = last">
6+
<ng-container
7+
[ngTemplateOutlet]="stepTemplate"
8+
[ngTemplateOutletContext]="{step: step, i: i}"></ng-container>
9+
<div *ngIf="!isLast" class="mat-stepper-horizontal-line"></div>
10+
</ng-container>
11+
</div>
12+
13+
<div class="mat-horizontal-content-container">
14+
<div *ngFor="let step of steps; let i = index"
15+
class="mat-horizontal-stepper-content" role="tabpanel"
16+
[@horizontalStepTransition]="_getAnimationDirection(i)"
17+
(@horizontalStepTransition.done)="_animationDone.next($event)"
18+
[id]="_getStepContentId(i)"
19+
[attr.aria-labelledby]="_getStepLabelId(i)"
20+
[attr.aria-expanded]="selectedIndex === i">
21+
<ng-container [ngTemplateOutlet]="step.content"></ng-container>
22+
</div>
23+
</div>
24+
</ng-container>
25+
26+
<!-- Vertical stepper -->
27+
<ng-container *ngSwitchCase="'vertical'">
28+
<div class="mat-step" *ngFor="let step of steps; let i = index; let isLast = last">
29+
<ng-container
30+
[ngTemplateOutlet]="stepTemplate"
31+
[ngTemplateOutletContext]="{step: step, i: i}"></ng-container>
32+
<div class="mat-vertical-content-container" [class.mat-stepper-vertical-line]="!isLast">
33+
<div class="mat-vertical-stepper-content" role="tabpanel"
34+
[@verticalStepTransition]="_getAnimationDirection(i)"
35+
(@verticalStepTransition.done)="_animationDone.next($event)"
36+
[id]="_getStepContentId(i)"
37+
[attr.aria-labelledby]="_getStepLabelId(i)"
38+
[attr.aria-expanded]="selectedIndex === i">
39+
<div class="mat-vertical-content">
40+
<ng-container [ngTemplateOutlet]="step.content"></ng-container>
41+
</div>
42+
</div>
43+
</div>
44+
</div>
45+
</ng-container>
46+
47+
</ng-container>
48+
49+
<!-- Common step templating -->
50+
<ng-template let-step="step" let-i="i" #stepTemplate>
51+
<mat-step-header
52+
[class.mat-horizontal-stepper-header]="orientation === 'horizontal'"
53+
[class.mat-vertical-stepper-header]="orientation === 'vertical'"
54+
(click)="step.select()"
55+
(keydown)="_onKeydown($event)"
56+
[tabIndex]="_getFocusIndex() === i ? 0 : -1"
57+
[id]="_getStepLabelId(i)"
58+
[attr.aria-posinset]="i + 1"
59+
[attr.aria-setsize]="steps.length"
60+
[attr.aria-controls]="_getStepContentId(i)"
61+
[attr.aria-selected]="selectedIndex == i"
62+
[attr.aria-label]="step.ariaLabel || null"
63+
[attr.aria-labelledby]="(!step.ariaLabel && step.ariaLabelledby) ? step.ariaLabelledby : null"
64+
[index]="i"
65+
[state]="_getIndicatorType(i, step.state)"
66+
[label]="step.stepLabel || step.label"
67+
[selected]="selectedIndex === i"
68+
[active]="step.completed || selectedIndex === i || !linear"
69+
[optional]="step.optional"
70+
[errorMessage]="step.errorMessage"
71+
[iconOverrides]="_iconOverrides"
72+
[disableRipple]="disableRipple"
73+
[color]="step.color || color"></mat-step-header>
74+
</ng-template>

0 commit comments

Comments
 (0)