Reputation: 669
I'm trying to implement this design for the steps, but not successful so far, the closest I got, was giving color to the line next to the current active step:
.mat-horizontal-stepper-header {
padding: 0 8px 0 16px !important;
&[ng-reflect-active="true"]+.mat-stepper-horizontal-line {
border-top-color: rgba(37, 82, 245, 0.54) !important;
}
}
but the purpose is to color the previous line of the active step as in the picture.
any thoughts on this? here's a stackblitz for repro https://stackblitz.com/edit/angular-ngcsei
Upvotes: 7
Views: 24161
Reputation: 114
Here is my attempt to style the last edited step icon. You can apply this logic for label as well. Instead of creating a custom class and mixins, you can achieve the same behaviour by invoking a simple function on the click of Next button from the mat-step
.
Here is the code which I wrote -
declare the stepper variable
@ViewChild('stepper') stepper: MatStepper;
Invoke this markPreviousStepHeaderAsSelected()
function from next button click handler
markPreviousStepHeaderAsSelected() {
this.stepper._stepHeader.forEach((matStepHeader, index) => {
if (index < this.stepper.selectedIndex) {
setTimeout(() => {
const matStepIconElement = this.getMatStepIconElement(matStepHeader);
if (matStepIconElement) {
matStepIconElement.classList.add('mat-step-icon-selected');
}
});
}
});
}
PS - You can set native classes for selecting label as well.
Upvotes: 0
Reputation: 121
I have update the solution to work on Production env. ng-reflect attribute may not be available in prod env.
I have read all above solutions. I want to share mine. You only need below two properties to understand, no need to create custom index.
[ng-reflect-selected='false'] and [ng-reflect-active='true']
There are three states of above two.
[ng-reflect-selected='true'] and [ng-reflect-active='true']
[ng-reflect-selected='false'] and [ng-reflect-active='true']
[ng-reflect-selected='false'] and [ng-reflect-active='false']
Now come to the solution. To achieve this state.
you need to use this [ng-reflect-selected='false'] and [ng-reflect-active='true']
.mat-horizontal-stepper-header {
&[ng-reflect-selected='false'][ng-reflect-active='true'] + .mat-stepper-horizontal-line {
border-color: #4ca131; //green color
}
}
The above CSS structure converted in HTML looks like
<element class="mat-horizontal-stepper-header" ng-reflect-selected="false"
ng-reflect-active="true">
<element class="mat-stepper-horizontal-line">
It will change the horizontal line color before current header.
The above solution might not work in production env as ng-reflect attributes may not be available. For that you can check below solution - as aria-selected attribute is available in both dev and prod env.
.mat-horizontal-stepper-header .mat-stepper-horizontal-line {
border-top-color: green; //Green Color
border-top-width: 1px;
border-top-style: solid;
}
.mat-horizontal-stepper-header[aria-selected='true'] ~ .mat-stepper-horizontal-line{
border-top-color: hsl(0, 0%, 50%) !important;
}
What above I have done is by default I made all horizontal line color to green and then owerrite all horizontal line color to grey after the selected/current wizard step.This will makes all previous visited stepper header to green.
Upvotes: 0
Reputation: 1019
I am late to this thread, thinking this may be helpful for someone. I have the same requirement except for the icon colors I tried every solution suggested but nothing was working as expected. I tried the below solution which is working fine for me-
@mixin styleStepLine($index) {
.mat-horizontal-stepper-header {
&+.mat-stepper-horizontal-line:nth-child(#{$index}) {
height: 2px;
background-image: linear-gradient(to right, #00b495, #00aeea);
}
}
}
@mixin styleEditedStepIcon($index) {
.mat-horizontal-stepper-header:nth-child(#{$index}) {
.mat-step-icon:not(.mat-step-icon-selected) {
background-color: red;
}
}
}
@mixin styleUnEditedStepIcon($index) {
.mat-horizontal-stepper-header:nth-child(#{$index}) {
.mat-step-icon:not(.mat-step-icon-selected) {
background-color: #e8e8e8;
}
}
}
.last-edited-step-1 {
@include styleStepLine(2);
}
.last-edited-step-2 {
@include styleStepLine(2);
@include styleStepLine(4);
}
.mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:first-child)::before,
.mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:last-child)::after,
[dir=rtl] .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:first-child)::after,
[dir=rtl] .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:last-child)::before {
width: 0!important;
}
.mat-step-header .mat-step-header-ripple {
display: none;
}
.mat-step-header.cdk-keyboard-focused,
.mat-step-header.cdk-program-focused,
.mat-step-header:hover {
background-color: #fff;
}
.mat-stepper-label-position-bottom .mat-horizontal-stepper-header {
padding: 8px 0 8px 0 !important;
width: 35px !important;
}
.mat-stepper-label-position-bottom .mat-stepper-horizontal-line {
top: 20px !important;
}
<mat-horizontal-stepper linear #stepper ngClass="{{ 'last-edited-step-' + stepper.selectedIndex }}" labelPosition="bottom">
<mat-step [stepControl]="firstFormGroup" errorMessage="Name is required.">
<form [formGroup]="firstFormGroup">
<ng-template matStepLabel>h1</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>
<mat-form-field appearance="outline">
<mat-label>Outline form field</mat-label>
<input matInput placeholder="Placeholder">
<mat-hint>Hint</mat-hint>
</mat-form-field>
</form>
</mat-step>
<mat-step [stepControl]="secondFormGroup" errorMessage="Address is required.">
<form [formGroup]="secondFormGroup">
<ng-template matStepLabel>h2</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>
Upvotes: 4
Reputation: 1334
Here's my solution, inspired on the one proposed by @dazzed.
In the HTML, I'm giving a class to the mat-horizontal-stepper
of 'last-edited-step-'last-edited-step-' + stepper.selectedIndex
.
In terms of CSS, I'm dynamically generating classes with SASS for loops, so looping ($i
) from .last-edited-step-last-edited-step-1
up to .last-edited-step-last-edited-step-42
(42 as an example, that is meant to be the number of steps, of course). Then inside each of them, I'm looping again ($j
) between the first and the nth line and then assigning the properties (in my case, a green border colour).
stepper.component.html
<mat-horizontal-stepper #stepper [linear]="true" class="{{ 'last-edited-step-' + stepper.selectedIndex }}">
<mat-step>
</mat-step>
<mat-step>
</mat-step>
<mat-step>
</mat-step>
</mat-horizontal-stepper>
stepper.component.sass
/deep/ mat-horizontal-stepper
@for $i from 1 through 42
&.last-edited-step-#{$i}
@for $j from 1 through $i
.mat-stepper-horizontal-line:nth-of-type(#{$j})
border-color: #00c190 !important
Using that, together with styling a few other elements (the circles, icons, line proportions) you can get something like this:
Upvotes: 4
Reputation: 1
This works perfect to me to change selected step
.mat-horizontal-stepper-header {
&[ng-reflect-selected="true"].mat-step-header {
background-color: #000;
}
&[ng-reflect-selected="false"].mat-step-header {
background-color: #fff;
}
}
Upvotes: 0
Reputation: 669
In production environment those reflect active attributes don't exist, so I came up with this solution based on the index of the elements. I just give a class to the mat-horizontal-stepper element with the index of the active step: last-edited-step-0, last-edited-step-1, last-edited-step-2. Then I created this mixins:
@mixin styleStepLine($index) {
.mat-horizontal-stepper-header {
&+.mat-stepper-horizontal-line:nth-child(#{$index}) {
height: 2px;
background-image: linear-gradient(to right, #00b495, #00aeea);
}
}
}
@mixin styleEditedStepIcon($index) {
.mat-horizontal-stepper-header:nth-child(#{$index}) {
.mat-step-icon:not(.mat-step-icon-selected) {
background-color: map-get($colors, 'light-green');
}
}
}
@mixin styleUnEditedStepIcon($index) {
.mat-horizontal-stepper-header:nth-child(#{$index}) {
.mat-step-icon:not(.mat-step-icon-selected) {
background-color: #e8e8e8;
}
}
}
.last-edited-step-1 {
@include styleStepLine(2);
@include styleEditedStepIcon(1);
@include styleUnEditedStepIcon(3);
}
.last-edited-step-2 {
@include styleStepLine(2);
@include styleStepLine(4);
@include styleEditedStepIcon(1);
@include styleEditedStepIcon(3);
}
Upvotes: 5
Reputation: 90
I did something similar just a while ago, while it's not perfect, it might help you get there. You can think the other way around. Try to target all lines passed the active step. In this example all steps after the active one have a dashed line and all before have a solid line.
.mat-stepper-horizontal-line {
border-top-style: solid;
}
.mat-horizontal-stepper-header {
&[ng-reflect-active='false'] + .mat-stepper-horizontal-line {
border-top-style: dashed;
}
&[ng-reflect-selected='true'] + .mat-stepper-horizontal-line {
border-top-style: dashed;
}
}
Upvotes: 0