import {Component, Input, Output, EventEmitter, OnChanges, SimpleChanges} from '@angular/core'; import {FormControl} from '@angular/forms'; import {InputState} from '../models/input.model'; import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {Observable} from 'rxjs/Observable'; import 'rxjs' @Component({ selector: 'multi-line-text-input-undo-component', template: `
{{label}}
`, styles: [` .wrapper { display: flex; flex-direction: row; } .textarea-wrapper { width: 100%; } .label { font-size: 18px; color: black; } .undo-buttons-container { display: flex; flex-direction: row; justify-content: center; } .undo-button, .redo-button { cursor: pointer; font-size: 28px; font-weight: 500; display: flex; flex-direction: column; justify-content: center; } `] }) export class MultiLineTextInputUndoComponent implements OnChanges { private inputSource: BehaviorSubject; public input$: Observable; @Input() public model: string; @Input() public placeholder: string = ''; @Input() public label: string; @Input() public control: FormControl = new FormControl('', []); @Output() public modelChange: EventEmitter = new EventEmitter(); @Output() public onChange: EventEmitter = new EventEmitter(); public initialValue: string; public ngOnChanges(simpleChanges: SimpleChanges): void { if (simpleChanges['model'] && simpleChanges['model'].isFirstChange()){ this.initialValue = simpleChanges['model'].currentValue; this.inputSource = new BehaviorSubject({ future: [], present:simpleChanges['model'].currentValue, past: []}); this.input$ = this.inputSource.asObservable(); } if (simpleChanges['model'] && !simpleChanges['model'].isFirstChange()){ const state = this.inputSource.value; this.inputSource.next({ future: [], present: this.model, past: [...state.past, state.present] }); } } public undo(): void { const state = this.inputSource.value; const newState = this.undoableInputReducer(state, {type: 'UNDO'}); this.inputSource.next(newState); } public redo(): void { const state = this.inputSource.value; const newState = this.undoableInputReducer(state, {type: 'REDO'}); this.inputSource.next(newState); } public undoableInputReducer(state: InputState, action) { return this.undoable(this.inputReducer)(state, action); } public inputReducer(state: InputState, action): InputState { const {past, present, future} = state; switch(action.type){ default: return { past: [...past, present], present: action.payload.value || '', future: [] } } } public undoable(reducer): (state: InputState, action) => InputState { return function (state: InputState, action) { console.log('State', state); const {past, present, future}: InputState = state; switch(action.type){ case'UNDO': if(past.length < 1) { return state; } const previous = past[past.length - 1]; const newPast: string[] = past.slice(0, -1); return { past: newPast, present: previous, future: [present, ...future] }; case'REDO': if(future.length < 1){ return state; } const next = future[0]; const newFuture = future.slice(1); return { past: [...past, present], present: next, future: newFuture }; default: const newPresent = reducer(state, action).present; if (present === newPresent){ return state; } return { past: [...past, present], present: newPresent, future: [] } } } } }