Skip to content

Commit b342e83

Browse files
committed
Merge branch 'stepper' of github.com:angular/material2 into multiforms
2 parents 6d002c7 + dfecded commit b342e83

File tree

11 files changed

+396
-61
lines changed

11 files changed

+396
-61
lines changed

src/cdk/stepper/stepper.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
import {LEFT_ARROW, RIGHT_ARROW, ENTER, SPACE} from '@angular/cdk/keyboard';
2626
import {CdkStepLabel} from './step-label';
2727
import {coerceBooleanProperty} from '@angular/cdk/coercion';
28+
import {AbstractControl} from '@angular/forms';
2829

2930
/** Used to generate unique ID for each stepper component. */
3031
let nextId = 0;
@@ -46,7 +47,7 @@ export class CdkStepperSelectionEvent {
4647

4748
@Component({
4849
selector: 'cdk-step',
49-
templateUrl: 'step.html',
50+
templateUrl: 'step.html'
5051
})
5152
export class CdkStep {
5253
/** Template for step label if it exists. */
@@ -66,6 +67,14 @@ export class CdkStep {
6667
/** Whether user has seen the expanded step content or not . */
6768
interacted = false;
6869

70+
/** The top level abstract control of the step. */
71+
@Input()
72+
get stepControl() { return this._stepControl; }
73+
set stepControl(control: AbstractControl) {
74+
this._stepControl = control;
75+
}
76+
private _stepControl: AbstractControl;
77+
6978
/** Label of the step. */
7079
@Input()
7180
label: string;
@@ -82,7 +91,7 @@ export class CdkStep {
8291
selector: 'cdk-stepper',
8392
host: {
8493
'(focus)': '_focusStep()',
85-
'(keydown)': '_onKeydown($event)',
94+
'(keydown)': '_onKeydown($event)'
8695
},
8796
})
8897
export class CdkStepper {
@@ -92,15 +101,17 @@ export class CdkStepper {
92101
/** The list of step headers of the steps in the stepper. */
93102
_stepHeader: QueryList<ElementRef>;
94103

104+
/** Whether the validity of previous steps should be checked or not. */
105+
@Input()
106+
get linear() { return this._linear; }
107+
set linear(value: any) { this._linear = coerceBooleanProperty(value); }
108+
private _linear = false;
109+
95110
/** The index of the selected step. */
96111
@Input()
97112
get selectedIndex() { return this._selectedIndex; }
98113
set selectedIndex(index: number) {
99-
this._steps.toArray()[this._selectedIndex].interacted = true;
100-
for (let i = 0; i < index; i++) {
101-
if (!this._steps.toArray()[i].valid) { return; }
102-
}
103-
if (this._selectedIndex != index) {
114+
if (this._selectedIndex != index && !this._anyControlsInvalid(index)) {
104115
this._emitStepperSelectionEvent(index);
105116
this._focusStep(this._selectedIndex);
106117
}
@@ -182,4 +193,17 @@ export class CdkStepper {
182193
this._focusIndex = index;
183194
this._stepHeader.toArray()[this._focusIndex].nativeElement.focus();
184195
}
196+
197+
private _anyControlsInvalid(index: number): boolean {
198+
const stepsArray = this._steps.toArray();
199+
stepsArray[this._selectedIndex].interacted = true;
200+
if (this._linear) {
201+
for (let i = 0; i < index; i++) {
202+
if (!stepsArray[i].stepControl.valid) {
203+
return true;
204+
}
205+
}
206+
}
207+
return false;
208+
}
185209
}

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

Lines changed: 152 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,102 @@
1+
<h2>Linear Vertical Stepper Demo</h2>
2+
<md-checkbox [(ngModel)]="isNonLinear">Disable linear mode</md-checkbox>
3+
<form [formGroup]="formGroup">
4+
<md-vertical-stepper formArrayName="formArray" [linear]="!isNonLinear">
5+
<md-step formGroupName="0" [stepControl]="formArray.get([0])">
6+
<ng-template mdStepLabel>Fill out your name</ng-template>
7+
<md-input-container>
8+
<input mdInput placeholder="First Name" formControlName="firstNameFormCtrl" required>
9+
<md-error>This field is required</md-error>
10+
</md-input-container>
11+
12+
<md-input-container>
13+
<input mdInput placeholder="Last Name" formControlName="lastNameFormCtrl" required>
14+
<md-error>This field is required</md-error>
15+
</md-input-container>
16+
<div>
17+
<button md-button mdStepperNext type="button">Next</button>
18+
</div>
19+
</md-step>
20+
21+
<md-step formGroupName="1" [stepControl]="formArray.get([1])">
22+
<ng-template mdStepLabel>
23+
<div>Fill out your phone number</div>
24+
</ng-template>
25+
<md-input-container>
26+
<input mdInput placeholder="Phone number" formControlName="phoneFormCtrl">
27+
<md-error>This field is required</md-error>
28+
</md-input-container>
29+
<div>
30+
<button md-button mdStepperPrevious type="button">Back</button>
31+
<button md-button mdStepperNext type="button">Next</button>
32+
</div>
33+
</md-step>
34+
35+
<md-step>
36+
<ng-template mdStepLabel>Confirm your information</ng-template>
37+
Everything seems correct.
38+
<div>
39+
<button md-button>Done</button>
40+
</div>
41+
</md-step>
42+
</md-vertical-stepper>
43+
</form>
44+
45+
<h2>Vertical Stepper Demo</h2>
46+
<md-vertical-stepper>
47+
<md-step>
48+
<ng-template mdStepLabel>Fill out your name</ng-template>
49+
<md-input-container>
50+
<input mdInput placeholder="First Name">
51+
<md-error>This field is required</md-error>
52+
</md-input-container>
53+
54+
<md-input-container>
55+
<input mdInput placeholder="Last Name">
56+
<md-error>This field is required</md-error>
57+
</md-input-container>
58+
<div>
59+
<button md-button mdStepperNext type="button">Next</button>
60+
</div>
61+
</md-step>
62+
63+
<md-step>
64+
<ng-template mdStepLabel>
65+
<div>Fill out your phone number</div>
66+
</ng-template>
67+
<md-input-container>
68+
<input mdInput placeholder="Phone number">
69+
<md-error>This field is required</md-error>
70+
</md-input-container>
71+
<div>
72+
<button md-button mdStepperPrevious type="button">Back</button>
73+
<button md-button mdStepperNext type="button">Next</button>
74+
</div>
75+
</md-step>
76+
77+
<md-step>
78+
<ng-template mdStepLabel>
79+
<div>Fill out your address</div>
80+
</ng-template>
81+
<md-input-container>
82+
<input mdInput placeholder="Address">
83+
<md-error>This field is required</md-error>
84+
</md-input-container>
85+
<div>
86+
<button md-button mdStepperPrevious type="button">Back</button>
87+
<button md-button mdStepperNext type="button">Next</button>
88+
</div>
89+
</md-step>
90+
91+
<md-step>
92+
<ng-template mdStepLabel>Confirm your information</ng-template>
93+
Everything seems correct.
94+
<div>
95+
<button md-button>Done</button>
96+
</div>
97+
</md-step>
98+
</md-vertical-stepper>
99+
1100
<h2>Linear Vertical Stepper Demo</h2>
2101
<md-vertical-stepper>
3102
<md-step [valid]="nameFormGroup.valid">
@@ -46,21 +145,62 @@ <h2>Linear Vertical Stepper Demo</h2>
46145

47146
<h2>Horizontal Stepper Demo</h2>
48147
<md-horizontal-stepper>
49-
<md-step *ngFor="let step of steps" [label]="step.label">
148+
<md-step>
149+
<ng-template mdStepLabel>Fill out your name</ng-template>
50150
<md-input-container>
51-
<input mdInput placeholder="Answer" [(ngModel)]="step.content">
151+
<input mdInput placeholder="First Name">
152+
<md-error>This field is required</md-error>
153+
</md-input-container>
154+
155+
<md-input-container>
156+
<input mdInput placeholder="Last Name">
157+
<md-error>This field is required</md-error>
52158
</md-input-container>
53159
<div>
54-
<button md-button mdStepperPrevious>Back</button>
55-
<button md-button mdStepperNext>Next</button>
160+
<button md-button mdStepperNext type="button">Next</button>
161+
</div>
162+
</md-step>
163+
164+
<md-step>
165+
<ng-template mdStepLabel>
166+
<div>Fill out your phone number</div>
167+
</ng-template>
168+
<md-input-container>
169+
<input mdInput placeholder="Phone number">
170+
<md-error>This field is required</md-error>
171+
</md-input-container>
172+
<div>
173+
<button md-button mdStepperPrevious type="button">Back</button>
174+
<button md-button mdStepperNext type="button">Next</button>
175+
</div>
176+
</md-step>
177+
178+
<md-step>
179+
<ng-template mdStepLabel>
180+
<div>Fill out your address</div>
181+
</ng-template>
182+
<md-input-container>
183+
<input mdInput placeholder="Address">
184+
<md-error>This field is required</md-error>
185+
</md-input-container>
186+
<div>
187+
<button md-button mdStepperPrevious type="button">Back</button>
188+
<button md-button mdStepperNext type="button">Next</button>
189+
</div>
190+
</md-step>
191+
192+
<md-step>
193+
<ng-template mdStepLabel>Confirm your information</ng-template>
194+
Everything seems correct.
195+
<div>
196+
<button md-button>Done</button>
56197
</div>
57198
</md-step>
58199
</md-horizontal-stepper>
59200

60-
<h2>Horizontal Stepper Demo with Templated Label</h2>
201+
<h2>Horizontal Stepper Demo</h2>
61202
<md-horizontal-stepper>
62-
<md-step *ngFor="let step of steps">
63-
<ng-template mdStepLabel>{{step.label}}</ng-template>
203+
<md-step *ngFor="let step of steps" [label]="step.label">
64204
<md-input-container>
65205
<input mdInput placeholder="Answer" [(ngModel)]="step.content">
66206
</md-input-container>
@@ -71,9 +211,10 @@ <h2>Horizontal Stepper Demo with Templated Label</h2>
71211
</md-step>
72212
</md-horizontal-stepper>
73213

74-
<h2>Vertical Stepper Demo</h2>
75-
<md-vertical-stepper>
76-
<md-step *ngFor="let step of steps" [label]="step.label">
214+
<h2>Horizontal Stepper Demo with Templated Label</h2>
215+
<md-horizontal-stepper>
216+
<md-step *ngFor="let step of steps">
217+
<ng-template mdStepLabel>{{step.label}}</ng-template>
77218
<md-input-container>
78219
<input mdInput placeholder="Answer" [(ngModel)]="step.content">
79220
</md-input-container>
@@ -82,4 +223,4 @@ <h2>Vertical Stepper Demo</h2>
82223
<button md-button mdStepperNext>Next</button>
83224
</div>
84225
</md-step>
85-
</md-vertical-stepper>
226+
</md-horizontal-stepper>

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import {Component} from '@angular/core';
2-
import {FormControl, FormGroup, Validators} from '@angular/forms';
2+
import {FormControl, Validators, FormBuilder, FormGroup} from '@angular/forms';
33

44
@Component({
55
moduleId: module.id,
66
selector: 'stepper-demo',
77
templateUrl: 'stepper-demo.html',
8-
styleUrls: ['stepper-demo.scss'],
8+
styleUrls: ['stepper-demo.scss']
99
})
1010
export class StepperDemo {
11+
formGroup: FormGroup;
12+
isNonLinear = false;
13+
1114
nameFormGroup: FormGroup;
1215
phoneFormGroup: FormGroup;
1316

@@ -18,7 +21,24 @@ export class StepperDemo {
1821
{label: 'You are now done', content: 'Finished!'}
1922
];
2023

24+
/** Returns a FormArray with the name 'formArray'. */
25+
get formArray() { return this.formGroup.get('formArray'); }
26+
27+
constructor(private _formBuilder: FormBuilder) { }
28+
2129
ngOnInit() {
30+
this.formGroup = this._formBuilder.group({
31+
formArray: this._formBuilder.array([
32+
this._formBuilder.group({
33+
firstNameFormCtrl: ['', Validators.required],
34+
lastNameFormCtrl: ['', Validators.required],
35+
}),
36+
this._formBuilder.group({
37+
phoneFormCtrl: [''],
38+
})
39+
])
40+
});
41+
2242
this.nameFormGroup = new FormGroup({
2343
firstNameFormCtrl: new FormControl('', Validators.required),
2444
lastNameFormCtrl: new FormControl('', Validators.required)

src/lib/core/theming/_all-theme.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
@import '../../sidenav/sidenav-theme';
2424
@import '../../slide-toggle/slide-toggle-theme';
2525
@import '../../slider/slider-theme';
26+
@import '../../stepper/stepper-theme';
2627
@import '../../tabs/tabs-theme';
2728
@import '../../toolbar/toolbar-theme';
2829
@import '../../tooltip/tooltip-theme';
@@ -55,6 +56,7 @@
5556
@include mat-sidenav-theme($theme);
5657
@include mat-slide-toggle-theme($theme);
5758
@include mat-slider-theme($theme);
59+
@include mat-stepper-theme($theme);
5860
@include mat-tabs-theme($theme);
5961
@include mat-toolbar-theme($theme);
6062
@include mat-tooltip-theme($theme);

src/lib/stepper/_stepper-theme.scss

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@import '../core/theming/palette';
2+
@import '../core/theming/theming';
3+
@import '../core/typography/_typography-utils.scss';
4+
5+
@mixin mat-stepper-theme($theme) {
6+
$foreground: map-get($theme, foreground);
7+
$background: map-get($theme, background);
8+
$primary: map-get($theme, primary);
9+
10+
.mat-horizontal-stepper-header, .mat-vertical-stepper-header {
11+
12+
.mat-stepper-label {
13+
color: mat-color($foreground, text);
14+
}
15+
16+
.mat-stepper-index {
17+
background-color: mat-color($primary);
18+
color: mat-color($primary, default-contrast);
19+
}
20+
21+
&[aria-selected='false'] {
22+
.mat-stepper-label {
23+
color: mat-color($foreground, disabled-text);
24+
}
25+
26+
.mat-stepper-index {
27+
background-color: mat-color($foreground, disabled-text);
28+
}
29+
}
30+
}
31+
32+
.mat-stepper-horizontal, .mat-stepper-vertical {
33+
background-color: mat-color($background, card);
34+
}
35+
36+
.vertical-content-container {
37+
border-left-color: mat-color($foreground, divider);
38+
}
39+
40+
.connector-line {
41+
border-top-color: mat-color($foreground, divider);
42+
}
43+
}

0 commit comments

Comments
 (0)