Skip to content

Commit

Permalink
feat(step): create step component to control forms by step.
Browse files Browse the repository at this point in the history
  • Loading branch information
WilliamAguera committed Sep 30, 2019
1 parent 3c28b93 commit ce89cea
Show file tree
Hide file tree
Showing 12 changed files with 480 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {AfterViewInit, ChangeDetectorRef, ContentChild, Directive, ElementRef, HostListener, OnInit} from '@angular/core';
import {StepService} from '../services/step.service';
import {TlButton} from '../../button/button';

@Directive({
selector: '[stepFinish]'
})
export class StepFinishDirective implements AfterViewInit, OnInit {

@ContentChild( TlButton, { static: true } ) button: TlButton;

constructor( private elementRef: ElementRef,
private change: ChangeDetectorRef,
private stepService: StepService ) {
}

@HostListener('click')
onClick() {
if (this.stepService.isFormValid()) {
this.stepService.finish();
}
}

ngOnInit() {
this.elementRef.nativeElement.hidden = 'true';
this.changes();
}

ngAfterViewInit() {
this.setDisabled();
this.stepService.onChangeStatusForm.subscribe(() => {
setTimeout(() => {
this.setDisabled();
});
});
}

private setDisabled() {
this.button.disabled = !this.stepService.isFormValid();
this.change.detectChanges();
}

private changes() {
this.stepService.onChange.subscribe(() => {
this.elementRef.nativeElement.hidden = !this.stepService.isLastStep();
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {AfterViewInit, ChangeDetectorRef, ContentChild, Directive, ElementRef, HostListener, OnInit} from '@angular/core';
import {StepService} from '../services/step.service';
import {TlButton} from '../../button/button';

@Directive({
selector: '[stepNext]'
})
export class StepNextDirective implements OnInit, AfterViewInit {

@ContentChild(TlButton, {static: true}) button: TlButton;

constructor(private elementRef: ElementRef,
private change: ChangeDetectorRef,
private stepService: StepService) {
}

@HostListener('click')
onClick() {
if ( !this.stepService.isValidateForm() ) {
this.stepService.next();
return;
}
if (this.stepService.isFormValid()) {
this.stepService.next();
}
}

ngOnInit() {
this.stepService.onChange.subscribe(() => {
this.elementRef.nativeElement.hidden = this.stepService.isLastStep();
});
}

ngAfterViewInit() {
this.setDisabled();
this.stepService.onChangeStatusForm.subscribe(() => {
setTimeout(() => {
this.setDisabled();
});
});
}

private setDisabled() {
if ( this.stepService.isValidateForm()) {
this.button.disabled = !this.stepService.isFormValid();
this.change.detectChanges();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {Directive, ElementRef, HostListener, OnInit} from '@angular/core';
import {StepService} from '../services/step.service';

@Directive({
selector: '[stepPrevious]'
})
export class StepPreviousDirective implements OnInit {

constructor( private elementRef: ElementRef, private stepService: StepService ) {
}

@HostListener('click')
onClick() {
this.stepService.previous();
}

ngOnInit() {
this.elementRef.nativeElement.hidden = this.stepService.isFirstStep();
this.stepService.onChange.subscribe(() => {
this.elementRef.nativeElement.hidden = this.stepService.isFirstStep();
});
}

}
31 changes: 31 additions & 0 deletions projects/truly-ui/src/components/step/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

import { TlStep } from './step';
import {TlStepForm} from './parts/step-form/step-form';
import {StepNextDirective} from './directives/step-next.directive';
import {StepPreviousDirective} from './directives/step-previous.directive';
import {StepFinishDirective} from './directives/step-finish.directive';

@NgModule({
imports: [
CommonModule,
FormsModule
],
declarations: [
TlStep,
TlStepForm,
StepNextDirective,
StepPreviousDirective,
StepFinishDirective
],
exports: [
TlStep,
TlStepForm,
StepNextDirective,
StepPreviousDirective,
StepFinishDirective
]
})
export class StepModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div class="step-form-wrapper" *ngIf="selected">
<ng-content></ng-content>
</div>

Empty file.
50 changes: 50 additions & 0 deletions projects/truly-ui/src/components/step/parts/step-form/step-form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {FormGroup} from '@angular/forms';
import {StepService} from '../../services/step.service';
import {Subscription} from 'rxjs';

let index = 0;

@Component({
selector: 'tl-step-form',
templateUrl: './step-form.html',
styleUrls: ['./step-form.scss']
})
export class TlStepForm implements OnInit, OnDestroy {

@Input() form: FormGroup;

@Input() validateForm = true;

@Input() label = `step${index++}`;

@Input() templateIcon = null;

public selected = false;

private subscription = new Subscription();

constructor( private stepService: StepService ) { }

ngOnInit() {
this.changes();
this.statusChanges();
}

changes() {
this.subscription.add(this.stepService.onChange.subscribe(() => {
this.stepService.formStatusChange( this.form.status !== 'INVALID' );
}));
}

statusChanges() {
this.subscription.add(this.form.statusChanges.subscribe(( status: string ) => {
this.stepService.formStatusChange( status !== 'INVALID' );
}));
}

ngOnDestroy() {
this.subscription.unsubscribe();
}

}
71 changes: 71 additions & 0 deletions projects/truly-ui/src/components/step/services/step.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {Injectable} from '@angular/core';
import {Subject} from 'rxjs';

@Injectable()
export class StepService {

public onChange = new Subject();

public onFinish = new Subject();

public onChangeStatusForm = new Subject();

private currentStep = 0;

private steps;

constructor() {
}

setSteps(steps: Array<any>) {
this.steps = steps;
}

setCurrentStep(step: number) {
this.currentStep = step;
}

isFormValid() {
return this.steps[this.currentStep].form.valid;
}

isValidateForm() {
return this.steps[this.currentStep].validateForm;
}

next() {
if (!this.isLastStep()) {
this.currentStep = this.currentStep + 1;
this.onChange.next(this.currentStep);
}
}

finish() {
const form = {};
this.steps.forEach(( item, index ) => {
const id = `step${index}`;
Object.assign( form, { [id]: item.form.value });
});
this.onFinish.next( form );
}

previous() {
if (this.currentStep > 0) {
this.currentStep = this.currentStep - 1;
this.onChange.next(this.currentStep);
}
}

formStatusChange( status: boolean ) {
this.onChangeStatusForm.next( status );
}

isFirstStep() {
return this.currentStep === 0;
}

isLastStep() {
return this.steps.length - 1 === this.currentStep;
}

}
34 changes: 34 additions & 0 deletions projects/truly-ui/src/components/step/step-theme.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@import "../core/styles/theming/theming";

@mixin _tl-step-theme-schema( $theme, $action ){
//SKELETON COMPONENT HERE
}

@mixin tl-step-theme( $theme ) {

.tl-step{
&.basic {
@include _tl-step-theme-schema($theme, 'basic')
}

&.primary {
@include _tl-step-theme-schema($theme, 'primary')
}

&.success {
@include _tl-step-theme-schema($theme, 'success')
}

&.danger {
@include _tl-step-theme-schema($theme, 'danger')
}

&.warning {
@include _tl-step-theme-schema($theme, 'warning')
}

&.information {
@include _tl-step-theme-schema($theme, 'information')
}
}
}
15 changes: 15 additions & 0 deletions projects/truly-ui/src/components/step/step.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="step-wrapper">
<div class="step-item" *ngFor="let item of stepsArray; let i = index; let last = last">
<ng-container *ngIf="!item.templateIcon">
<div class="circle" [class.selected]="selected === i">{{ i + 1 }}</div>
</ng-container>
<div class="step-icon">
<ng-container *ngTemplateOutlet="item?.templateIcon"></ng-container>
</div>
<span class="label" [class.selected]="selected === i">{{ item?.label }}</span>
<div class="separator" *ngIf="!last"><div class="line"></div></div>
</div>
</div>
<div class="step-content">
<ng-content></ng-content>
</div>
59 changes: 59 additions & 0 deletions projects/truly-ui/src/components/step/step.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.step-wrapper {
display: flex;
align-items: center;
>.step-item {
display: flex;
align-items: center;
justify-content: flex-start;
flex: 1;
padding: 0 10px;
box-sizing: border-box;
>.circle {
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid #cccccc;
color: #7f7c7c;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: background 300ms ease-in-out;
&.selected {
color: white;
background: #66CC99;
border: 0;
}
}
>.label {
padding: 0 10px;
color: #7f7c7c;
white-space: nowrap;
&.selected {
color: #4d4a4a;
font-weight: 600;
}
}
>.step-icon {
font-size: 32px;
color: #66CC99;
}
>.separator {
height: 40px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
.line {
height: 1px;
background: #e0e0e0;
width: 100%;
}
}
&:last-child {
flex: 0;
}
}

}

Loading

0 comments on commit ce89cea

Please sign in to comment.