From 88e64d3f42ea0338c2c2f8500ede20ddcf26555c Mon Sep 17 00:00:00 2001 From: Paolo Caleffi Date: Tue, 14 Aug 2018 16:59:03 +0200 Subject: [PATCH] feat(stepper): add alternative label version --- src/lib/stepper/stepper.md | 6 ++ src/lib/stepper/stepper.scss | 58 +++++++++++++++++++ src/lib/stepper/stepper.ts | 14 ++++- .../stepper-label-position-bottom-example.css | 1 + ...stepper-label-position-bottom-example.html | 33 +++++++++++ .../stepper-label-position-bottom-example.ts | 26 +++++++++ 6 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.css create mode 100644 src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.html create mode 100644 src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.ts diff --git a/src/lib/stepper/stepper.md b/src/lib/stepper/stepper.md index 5ffda6069ae3..2bded88f584c 100644 --- a/src/lib/stepper/stepper.md +++ b/src/lib/stepper/stepper.md @@ -40,6 +40,12 @@ For more complex labels, add a template with the `matStepLabel` directive inside ``` +#### 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. + + + ### Stepper buttons There are two button directives to support navigation between different steps: `matStepperPrevious` and `matStepperNext`. diff --git a/src/lib/stepper/stepper.scss b/src/lib/stepper/stepper.scss index dd3268395e57..ab57701d83a7 100644 --- a/src/lib/stepper/stepper.scss +++ b/src/lib/stepper/stepper.scss @@ -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 { @@ -44,6 +68,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 { diff --git a/src/lib/stepper/stepper.ts b/src/lib/stepper/stepper.ts index cd042fcbf9b0..6b92899fbb50 100644 --- a/src/lib/stepper/stepper.ts +++ b/src/lib/stepper/stepper.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ +import {AnimationEvent} from '@angular/animations'; import {Directionality} from '@angular/cdk/bidi'; import {CdkStep, CdkStepper, StepContentPositionState} from '@angular/cdk/stepper'; -import {AnimationEvent} from '@angular/animations'; import { AfterContentInit, ChangeDetectionStrategy, @@ -21,6 +21,7 @@ import { EventEmitter, forwardRef, Inject, + Input, Optional, Output, QueryList, @@ -32,9 +33,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'; @@ -123,6 +125,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', }, @@ -131,7 +135,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, diff --git a/src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.css b/src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.html b/src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.html new file mode 100644 index 000000000000..931635a17ea8 --- /dev/null +++ b/src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.html @@ -0,0 +1,33 @@ + + +
+ Fill out your name + + + +
+ +
+
+
+ +
+ Fill out your address + + + +
+ + +
+
+
+ + Done + You are now done. +
+ + +
+
+
diff --git a/src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.ts b/src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.ts new file mode 100644 index 000000000000..406dccb64d1b --- /dev/null +++ b/src/material-examples/stepper-label-position-bottom/stepper-label-position-bottom-example.ts @@ -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] + }); + } +}