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

- + Fill out your name @@ -40,7 +42,7 @@

Linear Vertical 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'; + } +}