diff --git a/src/demo-app/stepper/stepper-demo.html b/src/demo-app/stepper/stepper-demo.html
index 0dd4274921ba..b11dbb7aedf7 100644
--- a/src/demo-app/stepper/stepper-demo.html
+++ b/src/demo-app/stepper/stepper-demo.html
@@ -1,8 +1,10 @@
Disable linear mode
+Vertical
-
Linear Vertical Stepper Demo using a single form
+Linear Stepper Demo using a single form
Linear Horizontal Stepper Demo using a different form for each step
diff --git a/src/demo-app/stepper/stepper-demo.ts b/src/demo-app/stepper/stepper-demo.ts
index 22426f7c6af1..bafae37d4bbf 100644
--- a/src/demo-app/stepper/stepper-demo.ts
+++ b/src/demo-app/stepper/stepper-demo.ts
@@ -18,6 +18,7 @@ export class StepperDemo {
formGroup: FormGroup;
isNonLinear = false;
isNonEditable = false;
+ isVertical = true;
nameFormGroup: FormGroup;
emailFormGroup: FormGroup;
diff --git a/src/lib/stepper/stepper-animations.ts b/src/lib/stepper/stepper-animations.ts
new file mode 100644
index 000000000000..652825330231
--- /dev/null
+++ b/src/lib/stepper/stepper-animations.ts
@@ -0,0 +1,35 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {
+ animate,
+ state,
+ style,
+ transition,
+ trigger,
+ AnimationTriggerMetadata,
+} from '@angular/animations';
+
+/**
+ * Animations used by the Material stepper.
+ * @docs-private
+ */
+export const matStepperAnimations: AnimationTriggerMetadata[] = [
+ trigger('horizontalStepTransition', [
+ state('previous', style({transform: 'translate3d(-100%, 0, 0)', visibility: 'hidden'})),
+ state('current', style({transform: 'none', visibility: 'visible'})),
+ state('next', style({transform: 'translate3d(100%, 0, 0)', visibility: 'hidden'})),
+ transition('* => *', animate('500ms cubic-bezier(0.35, 0, 0.25, 1)'))
+ ]),
+ trigger('verticalStepTransition', [
+ state('previous', style({height: '0px', visibility: 'hidden'})),
+ state('next', style({height: '0px', visibility: 'hidden'})),
+ state('current', style({height: '*', visibility: 'visible'})),
+ transition('* <=> current', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)'))
+ ])
+];
diff --git a/src/lib/stepper/stepper-horizontal.html b/src/lib/stepper/stepper-horizontal.html
deleted file mode 100644
index 5388801e155c..000000000000
--- a/src/lib/stepper/stepper-horizontal.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
diff --git a/src/lib/stepper/stepper-vertical.html b/src/lib/stepper/stepper-vertical.html
deleted file mode 100644
index 41bbefec72e7..000000000000
--- a/src/lib/stepper/stepper-vertical.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
diff --git a/src/lib/stepper/stepper.html b/src/lib/stepper/stepper.html
new file mode 100644
index 000000000000..37686a564738
--- /dev/null
+++ b/src/lib/stepper/stepper.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/stepper/stepper.ts b/src/lib/stepper/stepper.ts
index a5c795bc2cfa..c7aef06aa20f 100644
--- a/src/lib/stepper/stepper.ts
+++ b/src/lib/stepper/stepper.ts
@@ -6,29 +6,33 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {animate, state, style, transition, trigger} from '@angular/animations';
import {CdkStep, CdkStepper} from '@angular/cdk/stepper';
import {
AfterContentInit,
Component,
ContentChild,
ContentChildren,
- Directive,
ElementRef,
forwardRef,
Inject,
+ Input,
QueryList,
SkipSelf,
ViewChildren,
ViewEncapsulation,
ChangeDetectionStrategy,
+ OnInit,
} from '@angular/core';
import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
import {ErrorStateMatcher} from '@angular/material/core';
import {MatStepHeader} from './step-header';
import {MatStepLabel} from './step-label';
+import {matStepperAnimations} from './stepper-animations';
import {takeUntil} from 'rxjs/operators/takeUntil';
+/** Possible orientations for the Material stepper. */
+export type MatStepperOrientation = 'horizontal' | 'vertical';
+
@Component({
moduleId: module.id,
selector: 'mat-step',
@@ -61,8 +65,23 @@ export class MatStep extends CdkStep implements ErrorStateMatcher {
}
}
-@Directive({
- selector: '[matStepper]'
+@Component({
+ moduleId: module.id,
+ selector: 'mat-stepper, [matStepper]',
+ templateUrl: 'stepper.html',
+ styleUrls: ['stepper.css'],
+ inputs: ['selectedIndex'],
+ exportAs: 'matStepper',
+ encapsulation: ViewEncapsulation.None,
+ preserveWhitespaces: false,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ animations: matStepperAnimations,
+ host: {
+ '[class.mat-stepper-horizontal]': 'orientation === "horizontal"',
+ '[class.mat-stepper-vertical]': 'orientation === "vertical"',
+ '[attr.aria-orientation]': 'orientation',
+ 'role': 'tablist',
+ },
})
export class MatStepper extends CdkStepper implements AfterContentInit {
/** The list of step headers of the steps in the stepper. */
@@ -71,6 +90,9 @@ export class MatStepper extends CdkStepper implements AfterContentInit {
/** Steps that the stepper holds. */
@ContentChildren(MatStep) _steps: QueryList;
+ /** Orientation of the stepper. */
+ @Input() orientation: MatStepperOrientation = 'vertical';
+
ngAfterContentInit() {
// Mark the component for change detection whenever the content children query changes
this._steps.changes.pipe(takeUntil(this._destroyed)).subscribe(() => this._stateChanged());
@@ -79,54 +101,46 @@ export class MatStepper extends CdkStepper implements AfterContentInit {
@Component({
moduleId: module.id,
+ encapsulation: ViewEncapsulation.None,
+ preserveWhitespaces: false,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ templateUrl: 'stepper.html',
+ styleUrls: ['stepper.css'],
selector: 'mat-horizontal-stepper',
exportAs: 'matHorizontalStepper',
- templateUrl: 'stepper-horizontal.html',
- styleUrls: ['stepper.css'],
- inputs: ['selectedIndex'],
+ animations: matStepperAnimations,
+ providers: [{provide: MatStepper, useExisting: MatHorizontalStepper}],
host: {
'class': 'mat-stepper-horizontal',
'aria-orientation': 'horizontal',
'role': 'tablist',
},
- animations: [
- trigger('stepTransition', [
- state('previous', style({transform: 'translate3d(-100%, 0, 0)', visibility: 'hidden'})),
- state('current', style({transform: 'none', visibility: 'visible'})),
- state('next', style({transform: 'translate3d(100%, 0, 0)', visibility: 'hidden'})),
- transition('* => *', animate('500ms cubic-bezier(0.35, 0, 0.25, 1)'))
- ])
- ],
- providers: [{provide: MatStepper, useExisting: MatHorizontalStepper}],
- encapsulation: ViewEncapsulation.None,
- preserveWhitespaces: false,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class MatHorizontalStepper extends MatStepper { }
+export class MatHorizontalStepper extends MatStepper implements OnInit {
+ ngOnInit() {
+ this.orientation = 'horizontal';
+ }
+}
@Component({
moduleId: module.id,
+ encapsulation: ViewEncapsulation.None,
+ preserveWhitespaces: false,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ templateUrl: 'stepper.html',
+ styleUrls: ['stepper.css'],
selector: 'mat-vertical-stepper',
exportAs: 'matVerticalStepper',
- templateUrl: 'stepper-vertical.html',
- styleUrls: ['stepper.css'],
- inputs: ['selectedIndex'],
+ providers: [{provide: MatStepper, useExisting: MatVerticalStepper}],
+ animations: matStepperAnimations,
host: {
'class': 'mat-stepper-vertical',
'aria-orientation': 'vertical',
'role': 'tablist',
},
- animations: [
- trigger('stepTransition', [
- state('previous', style({height: '0px', visibility: 'hidden'})),
- state('next', style({height: '0px', visibility: 'hidden'})),
- state('current', style({height: '*', visibility: 'visible'})),
- transition('* <=> current', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)'))
- ])
- ],
- providers: [{provide: MatStepper, useExisting: MatVerticalStepper}],
- encapsulation: ViewEncapsulation.None,
- preserveWhitespaces: false,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class MatVerticalStepper extends MatStepper { }
+export class MatVerticalStepper extends MatStepper implements OnInit {
+ ngOnInit() {
+ this.orientation = 'vertical';
+ }
+}