Skip to content

Commit

Permalink
feat(stepper): add linear mode (#1151)
Browse files Browse the repository at this point in the history
Closes #1040
  • Loading branch information
yggg authored and nnixaa committed Jan 15, 2019
1 parent 78e283c commit db5d214
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 19 deletions.
6 changes: 6 additions & 0 deletions src/app/playground-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,12 @@ export const PLAYGROUND_COMPONENTS: ComponentLink[] = [
component: 'StepperVerticalComponent',
name: 'Stepper Vertical',
},
{
path: 'stepper-linear.component',
link: '/stepper/stepper-linear.component',
component: 'StepperLinearComponent',
name: 'Stepper Linear',
},
],
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<ng-container *ngFor="let step of steps; let index = index; let first = first">

<div *ngIf="!first && !step.hidden"
[class.connector-past]="index < selectedIndex"
[class.connector-past]="index <= selectedIndex"
class="connector"></div>

<div *ngIf="!step.hidden" class="step"
Expand Down
75 changes: 58 additions & 17 deletions src/framework/theme/components/stepper/stepper.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Input,
QueryList,
} from '@angular/core';
import { convertToBoolProperty } from '../helpers';
import { NbStepComponent } from './step.component';

export enum NbStepperOrientation {
Expand Down Expand Up @@ -53,7 +54,12 @@ export enum NbStepperOrientation {
* <nb-step>
* </nb-stepper>
* ```
* Specify `[stepControl]="form"` and user can navigates only if submit previous step's form.
*
* When linear mode enabled user can't move forward unless current step is complete.
* @stacked-example(Linear, stepper/stepper-linear.component)
*
* Specify `[stepControl]="form"` and stepper allow go to the next step only if form is valid.
* You can disable it via `linear` mode setting.
* ```html
* // ...
* <nb-stepper orientation="horizontal">
Expand Down Expand Up @@ -112,11 +118,13 @@ export class NbStepperComponent {
}

set selectedIndex(index: number) {
if (this.steps) {
if (this.index !== index && this.isStepValid(index)) {
this.index = index;
}
} else {
if (!this.steps) {
this.index = index;
return;
}

this.markCurrentStepInteracted();
if (this.canBeSelected(index)) {
this.index = index;
}
}
Expand All @@ -132,7 +140,10 @@ export class NbStepperComponent {
}

set selected(step: NbStepComponent) {
this.selectedIndex = this.steps ? this.steps.toArray().indexOf(step) : -1;
if (!this.steps) {
return;
}
this.selectedIndex = this.steps.toArray().indexOf(step);
}

/**
Expand All @@ -141,44 +152,74 @@ export class NbStepperComponent {
*/
@Input() orientation: string = NbStepperOrientation.HORIZONTAL;

/**
* Allow moving forward only if the current step is complete
* @default true
*/
@Input()
set linear(value: boolean) {
this.linearValue = convertToBoolProperty(value);
}
get linear(): boolean {
return this.linearValue;
}
private linearValue = true;

private index = 0;

/**
* Navigate to next step
* */
next() {
this.selectedIndex = Math.min(this.index + 1, this.steps.length - 1);
this.selectedIndex = Math.min(this.selectedIndex + 1, this.steps.length - 1);
}

/**
* Navigate to previous step
* */
previous() {
this.selectedIndex = Math.max(this.index - 1, 0);
this.selectedIndex = Math.max(this.selectedIndex - 1, 0);
}

/**
* Reset stepper and stepControls to initial state
* */
reset() {
this.selectedIndex = 0;
this.index = 0;
this.steps.forEach(step => step.reset());
}

isStepSelected(step: NbStepComponent) {
return this.index === this.steps.toArray().indexOf(step);
return this.selected === step;
}

private isStepValid(index: number): boolean {
const steps = this.steps.toArray();
return this.steps.toArray()[index].completed;
}

private canBeSelected(indexToCheck: number): boolean {
const noSteps = !this.steps || this.steps.length === 0;
if (noSteps || indexToCheck < 0 || indexToCheck >= this.steps.length) {
return false;
}

steps[this.index].interacted = true;
if (indexToCheck <= this.selectedIndex || !this.linear) {
return true;
}

if (index >= this.index && index > 0) {
const currentStep = steps[this.index];
return currentStep.completed;
let isAllStepsValid = true;
for (let i = this.selectedIndex; i < indexToCheck; i++) {
if (!this.isStepValid(i)) {
isAllStepsValid = false;
break;
}
}
return isAllStepsValid;
}

return true;
private markCurrentStepInteracted() {
if (this.selected) {
this.selected.interacted = true;
}
}
}
70 changes: 70 additions & 0 deletions src/playground/with-layout/stepper/stepper-linear.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<nb-card>
<nb-card-body>
<button class="linear-mode-button"
nbButton
(click)="toggleLinearMode()">
{{ linearMode ? 'Disable' : 'Enable' }} linear mode
</button>

<nb-stepper #stepper [linear]="linearMode">

<nb-step [stepControl]="nameForm" label="Name">
<form #nameForm="ngForm" class="step-container">
<div class="input-group">
<input type="text"
placeholder="Enter your name"
class="form-control"
name="name"
required
nbInput
ngModel
#name="ngModel"
[status]="name.invalid ? 'danger' : ''">
</div>
<button nbButton nbStepperNext>next</button>
</form>
</nb-step>

<nb-step [stepControl]="movieForm" label="Movie">
<form #movieForm="ngForm" class="step-container">
<div class="input-group">
<input type="text"
placeholder="Enter your favorite movie"
class="form-control"
name="movie"
required
nbInput
ngModel
#movie="ngModel"
[status]="movie.invalid ? 'danger' : ''">
</div>
<button nbButton nbStepperNext>next</button>
</form>
</nb-step>

<nb-step [stepControl]="somethingForm" label="Something">
<form #somethingForm="ngForm" class="step-container">
<div class="input-group">
<input type="text"
placeholder="Enter something"
class="form-control"
name="something"
required
nbInput
ngModel
#somethingForm="ngModel"
[status]="somethingForm.invalid ? 'danger' : ''">
</div>
<button nbButton nbStepperNext>next</button>
</form>
</nb-step>

<nb-step [hidden]="true">
<div class="step-container">
<h3>Wizard completed!</h3>
<button nbButton (click)="stepper.reset()">Try again</button>
</div>
</nb-step>
</nb-stepper>
</nb-card-body>
</nb-card>
14 changes: 14 additions & 0 deletions src/playground/with-layout/stepper/stepper-linear.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Component } from '@angular/core';

@Component({
selector: 'nb-stepper-linear',
styleUrls: [ 'stepper-playground.component.scss' ],
templateUrl: './stepper-linear.component.html',
})
export class StepperLinearComponent {
linearMode = true;

toggleLinearMode() {
this.linearMode = !this.linearMode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@
.input-group {
padding-bottom: 1rem;
}

.linear-mode-button {
margin-bottom: 1rem;
}
5 changes: 5 additions & 0 deletions src/playground/with-layout/stepper/stepper-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { StepperShowcaseComponent } from './stepper-showcase.component';
import { StepperTestComponent } from './stepper-test.component';
import { StepperValidationComponent } from './stepper-validation.component';
import { StepperVerticalComponent } from './stepper-vertical.component';
import { StepperLinearComponent } from './stepper-linear.component';

const routes: Route[] = [
{
Expand All @@ -28,6 +29,10 @@ const routes: Route[] = [
path: 'stepper-vertical.component',
component: StepperVerticalComponent,
},
{
path: 'stepper-linear.component',
component: StepperLinearComponent,
},
];

@NgModule({
Expand Down
5 changes: 4 additions & 1 deletion src/playground/with-layout/stepper/stepper.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,26 @@

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NbButtonModule, NbCardModule, NbInputModule, NbStepperModule } from '@nebular/theme';
import { StepperRoutingModule } from './stepper-routing.module';
import { StepperShowcaseComponent } from './stepper-showcase.component';
import { StepperTestComponent } from './stepper-test.component';
import { StepperValidationComponent } from './stepper-validation.component';
import { StepperVerticalComponent } from './stepper-vertical.component';
import { StepperLinearComponent } from './stepper-linear.component';

@NgModule({
declarations: [
StepperShowcaseComponent,
StepperTestComponent,
StepperValidationComponent,
StepperVerticalComponent,
StepperLinearComponent,
],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
NbStepperModule,
NbCardModule,
Expand Down

0 comments on commit db5d214

Please sign in to comment.