From db5d21416884dbfa86a70266915ce61653991cad Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Tue, 15 Jan 2019 18:41:09 +0300 Subject: [PATCH] feat(stepper): add linear mode (#1151) Closes #1040 --- src/app/playground-components.ts | 6 ++ .../components/stepper/stepper.component.html | 2 +- .../components/stepper/stepper.component.ts | 75 ++++++++++++++----- .../stepper/stepper-linear.component.html | 70 +++++++++++++++++ .../stepper/stepper-linear.component.ts | 14 ++++ .../stepper/stepper-playground.component.scss | 4 + .../stepper/stepper-routing.module.ts | 5 ++ .../with-layout/stepper/stepper.module.ts | 5 +- 8 files changed, 162 insertions(+), 19 deletions(-) create mode 100644 src/playground/with-layout/stepper/stepper-linear.component.html create mode 100644 src/playground/with-layout/stepper/stepper-linear.component.ts diff --git a/src/app/playground-components.ts b/src/app/playground-components.ts index f1d8bdd904..dec2885b04 100644 --- a/src/app/playground-components.ts +++ b/src/app/playground-components.ts @@ -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', + }, ], }, { diff --git a/src/framework/theme/components/stepper/stepper.component.html b/src/framework/theme/components/stepper/stepper.component.html index e05d13f703..43c2c6c083 100644 --- a/src/framework/theme/components/stepper/stepper.component.html +++ b/src/framework/theme/components/stepper/stepper.component.html @@ -3,7 +3,7 @@
* * ``` - * 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 * // ... * @@ -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; } } @@ -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); } /** @@ -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; + } } } diff --git a/src/playground/with-layout/stepper/stepper-linear.component.html b/src/playground/with-layout/stepper/stepper-linear.component.html new file mode 100644 index 0000000000..417113a7e6 --- /dev/null +++ b/src/playground/with-layout/stepper/stepper-linear.component.html @@ -0,0 +1,70 @@ + + + + + + + +
+
+ +
+ +
+
+ + +
+
+ +
+ +
+
+ + +
+
+ +
+ +
+
+ + +
+

Wizard completed!

+ +
+
+
+
+
diff --git a/src/playground/with-layout/stepper/stepper-linear.component.ts b/src/playground/with-layout/stepper/stepper-linear.component.ts new file mode 100644 index 0000000000..c1b941ae89 --- /dev/null +++ b/src/playground/with-layout/stepper/stepper-linear.component.ts @@ -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; + } +} diff --git a/src/playground/with-layout/stepper/stepper-playground.component.scss b/src/playground/with-layout/stepper/stepper-playground.component.scss index e8ce011725..e203e63cb6 100644 --- a/src/playground/with-layout/stepper/stepper-playground.component.scss +++ b/src/playground/with-layout/stepper/stepper-playground.component.scss @@ -14,3 +14,7 @@ .input-group { padding-bottom: 1rem; } + +.linear-mode-button { + margin-bottom: 1rem; +} diff --git a/src/playground/with-layout/stepper/stepper-routing.module.ts b/src/playground/with-layout/stepper/stepper-routing.module.ts index 6d1d5cefb7..9b0605e6f1 100644 --- a/src/playground/with-layout/stepper/stepper-routing.module.ts +++ b/src/playground/with-layout/stepper/stepper-routing.module.ts @@ -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[] = [ { @@ -28,6 +29,10 @@ const routes: Route[] = [ path: 'stepper-vertical.component', component: StepperVerticalComponent, }, + { + path: 'stepper-linear.component', + component: StepperLinearComponent, + }, ]; @NgModule({ diff --git a/src/playground/with-layout/stepper/stepper.module.ts b/src/playground/with-layout/stepper/stepper.module.ts index 743cc8dae6..df5d9a0235 100644 --- a/src/playground/with-layout/stepper/stepper.module.ts +++ b/src/playground/with-layout/stepper/stepper.module.ts @@ -6,13 +6,14 @@ 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: [ @@ -20,9 +21,11 @@ import { StepperVerticalComponent } from './stepper-vertical.component'; StepperTestComponent, StepperValidationComponent, StepperVerticalComponent, + StepperLinearComponent, ], imports: [ CommonModule, + FormsModule, ReactiveFormsModule, NbStepperModule, NbCardModule,