|
import { AfterViewInit, ChangeDetectorRef, Component, DoCheck, HostBinding, Input, OnDestroy, OnInit } from '@angular/core'; |
|
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|
import { BehaviorSubject, merge } from 'rxjs'; |
|
import { distinctUntilChanged, skip, startWith } from 'rxjs/operators'; |
|
import { SubSink } from 'subsink'; |
|
import { TextboxSize } from './textbox-size.type'; |
|
|
|
@Component({ |
|
selector: 'app-textbox', |
|
templateUrl: './textbox.component.html', |
|
styleUrls: ['./textbox.component.scss'], |
|
providers: [ |
|
{ |
|
provide: NG_VALUE_ACCESSOR, |
|
useExisting: TextboxComponent, |
|
multi: true, |
|
} |
|
] |
|
}) |
|
export class TextboxComponent implements ControlValueAccessor, AfterViewInit, DoCheck, OnDestroy, OnInit { |
|
|
|
@Input() placeholder?: string; |
|
@Input() icon?: string; |
|
@Input() type = 'text'; |
|
@Input() size: TextboxSize = 'large'; |
|
|
|
@Input() |
|
@HostBinding('class.--volatile-placeholder') |
|
volatilePlaceholder = false; |
|
|
|
@Input() |
|
@HostBinding('class.--dark') |
|
dark = false; |
|
|
|
@HostBinding('class.--has-content') |
|
hasContent = false; |
|
|
|
@HostBinding('class.--valid') |
|
isValid = false; |
|
|
|
@HostBinding('class.--invalid') |
|
isInvalid = false; |
|
|
|
onTouched = () => {}; |
|
control: FormControl; |
|
|
|
private subs = new SubSink(); |
|
private touched$ = new BehaviorSubject(false); |
|
|
|
constructor ( |
|
private cd: ChangeDetectorRef, |
|
) { |
|
this.control = new FormControl(); |
|
} |
|
|
|
ngDoCheck() { |
|
// Check if touched has changed |
|
if (this.touched$.value !== this.control.touched) |
|
this.touched$.next(true); |
|
} |
|
|
|
ngAfterViewInit() { |
|
this.subs.sink = merge( |
|
// Only call when touched changed |
|
this.touched$.pipe( |
|
startWith(false), |
|
distinctUntilChanged(), |
|
skip(1), |
|
), |
|
// Also call on value or validity changes |
|
this.control.valueChanges, |
|
this.control.statusChanges, |
|
).subscribe(() => this.updateModifiers()); |
|
} |
|
|
|
ngOnInit(): void { |
|
this.updateModifiers(); |
|
} |
|
|
|
ngOnDestroy() { |
|
this.subs.unsubscribe(); |
|
} |
|
|
|
private updateModifiers() { |
|
// Neither empty nor null |
|
this.hasContent = this.control.value !== '' && this.control.value !== null; |
|
|
|
// Needs to be touched or have content in order to be applied |
|
this.isValid = (this.control.touched || this.hasContent) && this.control.valid; |
|
this.isInvalid = (this.control.touched || this.hasContent) && !this.control.valid; |
|
|
|
this.cd.detectChanges(); |
|
} |
|
|
|
mkClasses(own = '') { |
|
const pr = "textbox--"; |
|
return `${pr}${this.size} ${own}`; |
|
} |
|
|
|
writeValue(obj: any): void { |
|
this.control.patchValue(obj); |
|
this.updateModifiers(); |
|
} |
|
|
|
registerOnChange(fn: any): void { |
|
this.control.valueChanges.subscribe(fn); |
|
} |
|
|
|
registerOnTouched(fn: any): void { |
|
this.onTouched = fn; |
|
} |
|
} |