Stepper alternative label (#12674)

Added the "alternative label" version, as by [(old) spec](https://material.io/archive/guidelines/components/steppers.html#steppers-types-of-steppers).

Tryed on Chrome, Firefox, Edge and IE11 and everything seems fine.
Added a paragraph in the stepper documentation and an example.

I'm not sure the code style is ok so if you have suggestions I'll be happy to clean it up a bit, just tell me how :)
This commit is contained in:
Paolo Caleffi
2018-09-21 19:16:53 +02:00
committed by mmalerba
parent d15431ba84
commit e4c6cc98bf
6 changed files with 134 additions and 2 deletions
+6
View File
@@ -40,6 +40,12 @@ For more complex labels, add a template with the `matStepLabel` directive inside
</mat-vertical-stepper>
```
#### Label position
For `mat-horizontal-stepper` it's possible to define the position of the label. `end` is the default value, while `bottom` will place it under the step icon instead of at its side.
This behaviour is controlled by `labelPosition` property.
<!-- example(stepper-label-position-bottom) -->
### Stepper buttons
There are two button directives to support navigation between different steps:
`matStepperPrevious` and `matStepperNext`.
+58
View File
@@ -2,6 +2,7 @@
$mat-horizontal-stepper-header-height: 72px !default;
$mat-stepper-label-header-height: 24px !default;
$mat-stepper-label-position-bottom-top-gap: 16px !default;
$mat-stepper-side-gap: 24px !default;
$mat-vertical-stepper-content-margin: 36px !default;
$mat-stepper-line-width: 1px !default;
@@ -16,6 +17,10 @@ $mat-stepper-line-gap: 8px !default;
white-space: nowrap;
display: flex;
align-items: center;
.mat-stepper-label-position-bottom & {
align-items: flex-start;
}
}
.mat-stepper-horizontal-line {
@@ -25,6 +30,25 @@ $mat-stepper-line-gap: 8px !default;
height: 0;
margin: 0 $mat-stepper-line-gap - $mat-stepper-side-gap;
min-width: $mat-stepper-line-gap + $mat-stepper-side-gap;
.mat-stepper-label-position-bottom & {
margin: 0;
min-width: 0;
position: relative;
top: $mat-stepper-side-gap + $mat-stepper-label-header-height / 2;
}
}
%mat-header-horizontal-line-label-position-bottom {
border-top-color: rgba(0, 0, 0, 0.12);
border-top-width: $mat-stepper-line-width;
border-top-style: solid;
content: '';
display: inline-block;
height: 0;
position: absolute;
top: $mat-stepper-side-gap + $mat-stepper-label-header-height / 2;
width: calc(50% - #{$mat-stepper-label-header-height / 2 + $mat-stepper-line-gap});
}
.mat-horizontal-stepper-header {
@@ -43,6 +67,40 @@ $mat-stepper-line-gap: 8px !default;
margin-left: $mat-stepper-line-gap;
}
}
.mat-stepper-label-position-bottom & {
box-sizing: border-box;
flex-direction: column;
// We use auto instead of fixed 104px (by spec) because when there is an optional step
// the height is greater than that
height: auto;
padding: $mat-stepper-side-gap;
[dir='ltr'] &:not(:last-child)::after,
[dir='rtl'] &:not(:first-child)::after {
@extend %mat-header-horizontal-line-label-position-bottom;
right: 0;
}
[dir='ltr'] &:not(:first-child)::before,
[dir='rtl'] &:not(:last-child)::before {
@extend %mat-header-horizontal-line-label-position-bottom;
left: 0;
}
& .mat-step-icon,
& .mat-step-icon-not-touched {
// Cleans margin both for ltr and rtl direction
margin-right: 0;
margin-left: 0;
}
& .mat-step-label {
padding: $mat-stepper-label-position-bottom-top-gap 0 0 0;
text-align: center;
width: 100%;
}
}
}
.mat-vertical-stepper-header {
+10 -2
View File
@@ -27,6 +27,7 @@ import {
EventEmitter,
forwardRef,
Inject,
Input,
Optional,
Output,
QueryList,
@@ -38,9 +39,10 @@ import {
import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
import {DOCUMENT} from '@angular/common';
import {ErrorStateMatcher} from '@angular/material/core';
import {takeUntil} from 'rxjs/operators';
import {MatStepHeader} from './step-header';
import {MatStepLabel} from './step-label';
import {takeUntil} from 'rxjs/operators';
import {matStepperAnimations} from './stepper-animations';
import {MatStepperIcon, MatStepperIconContext} from './stepper-icon';
@@ -124,6 +126,8 @@ export class MatStepper extends _CdkStepper implements AfterContentInit {
inputs: ['selectedIndex'],
host: {
'class': 'mat-stepper-horizontal',
'[class.mat-stepper-label-position-end]': 'labelPosition == "end"',
'[class.mat-stepper-label-position-bottom]': 'labelPosition == "bottom"',
'aria-orientation': 'horizontal',
'role': 'tablist',
},
@@ -132,7 +136,11 @@ export class MatStepper extends _CdkStepper implements AfterContentInit {
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MatHorizontalStepper extends MatStepper { }
export class MatHorizontalStepper extends MatStepper {
/** Whether the label should display in bottom or end position. */
@Input()
labelPosition: 'bottom' | 'end' = 'end';
}
@Component({
moduleId: module.id,
@@ -0,0 +1 @@
/** No CSS for this example */
@@ -0,0 +1,33 @@
<mat-horizontal-stepper labelPosition="bottom" #stepper>
<mat-step [stepControl]="firstFormGroup">
<form [formGroup]="firstFormGroup">
<ng-template matStepLabel>Fill out your name</ng-template>
<mat-form-field>
<input matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
</mat-form-field>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step [stepControl]="secondFormGroup" optional>
<form [formGroup]="secondFormGroup">
<ng-template matStepLabel>Fill out your address</ng-template>
<mat-form-field>
<input matInput placeholder="Address" formControlName="secondCtrl" required>
</mat-form-field>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step>
<ng-template matStepLabel>Done</ng-template>
You are now done.
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button (click)="stepper.reset()">Reset</button>
</div>
</mat-step>
</mat-horizontal-stepper>
@@ -0,0 +1,26 @@
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
/**
* @title Stepper label bottom position
*/
@Component({
selector: 'stepper-label-position-bottom-example',
templateUrl: 'stepper-label-position-bottom-example.html',
styleUrls: ['stepper-label-position-bottom-example.css'],
})
export class StepperLabelPositionBottomExample implements OnInit {
firstFormGroup: FormGroup;
secondFormGroup: FormGroup;
constructor(private _formBuilder: FormBuilder) {}
ngOnInit() {
this.firstFormGroup = this._formBuilder.group({
firstCtrl: ['', Validators.required]
});
this.secondFormGroup = this._formBuilder.group({
secondCtrl: ['', Validators.required]
});
}
}