import { AfterViewInit, Component, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild } from "@angular/core"; import {AbstractControl, FormArray, FormControl, ValidatorFn} from "@angular/forms"; import {HelperFunctions} from "../../utils/HelperFunctions.class"; import {Subscription} from "rxjs"; import {EnvProperties} from "../../utils/properties/env-properties"; import {properties} from "../../../../environments/environment"; export interface Option { icon?: string, iconClass?: string, value: any, label: string } export interface Placeholder { label: string, static?: boolean } declare var UIkit; /** * WARNING! dashboard-input selector is @deprecated, use input instead * * */ @Component({ selector: '[dashboard-input], [input]', template: `
{{getLabel(formControl.value)}}
{{getLabel(formControl.value)}}
{{getLabel(chip.value)}}
{{formControl.errors.error}} Please provide a valid URL (e.g. https://example.com) Note: Prefer urls like "https://example.com/my-secure-image.png" instead of "http://example.com/my-image.png". Browsers may not load non secure content. ` }) export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChanges { /** Deprecated options*/ /** @deprecated */ @Input('label') label: string; /** @deprecated */ @Input() extraLeft: boolean = true; /** @deprecated */ @Input() gridSmall: boolean = false; /** @deprecated */ @Input() hideControl: boolean = false; /** @deprecated */ @Input() flex: 'middle' | 'top' | 'bottom' = 'middle'; /** @deprecated */ @Input() iconLeft: boolean = false; /** @deprecated */ @Input() removable: boolean = true; /** @deprecated */ @Input() smallChip: boolean = false; /** @deprecated */ @Input() panelWidth: number = 300; /** @deprecated */ @Input() panelClass: string = null; /** Basic information */ @Input('formInput') formControl: AbstractControl; @Input('type') type: 'text' | 'URL' | 'logoURL' | 'autocomplete' | 'textarea' | 'select' | 'chips' = 'text'; @Input() value: any | any[]; @Output() valueChange = new EventEmitter(); @Input('hint') hint: string; /** Text */ @ViewChild('input') input: ElementRef; /** Textarea options */ @ViewChild('textArea') textArea: ElementRef; @Input('rows') rows: number = 3; /** Select | Autocomplete | chips available options */ @Input('options') options: Option[] = []; @Input() tooltip: boolean = false; /** Chips && Autocomplete*/ public filteredOptions: Option[] = []; public searchControl: FormControl; /** Use modifier's class(es) to change view of your Input */ @Input() inputClass: string = 'inner'; /** Icon on the input */ @Input() icon: string = null; /** Chip options */ @Input() addExtraChips: boolean = false; @Input() showOptionsOnEmpty: boolean = true; @Input() validators: ValidatorFn[] | ValidatorFn; @Input() disabled: boolean = false; @Output() focusEmitter: EventEmitter = new EventEmitter(); /** LogoUrl information */ public secure: boolean = true; /** Internal basic information */ public placeholderInfo: Placeholder = {label: '', static: true}; public required: boolean = false; public focused: boolean = false; public opened: boolean = false; public properties: EnvProperties = properties; private initValue: any; private subscriptions: any[] = []; @ViewChild('inputBox') inputBox: ElementRef; @ViewChild('optionBox') optionBox: ElementRef; @ViewChild('searchInput') searchInput: ElementRef; @Input() set placeholder(placeholder: string | Placeholder) { if(typeof placeholder === 'string') { this.placeholderInfo = {label: placeholder, static: false}; } else { if(placeholder.static && (this.type === 'select' || this.type === 'autocomplete' || this.type === 'chips' || this.hint)) { placeholder.static = false; console.debug('Static placeholder is not available in this type of input and if hint is available.'); } this.placeholderInfo = placeholder; } } constructor(private elementRef: ElementRef) { if (elementRef.nativeElement.hasAttribute('dashboard-input') && this.properties.environment === "development") { console.warn("'dashboard-input' selector is deprecated; use 'input' instead."); } } @HostListener('document:click', ['$event']) click(event) { this.focus(this.inputBox && this.inputBox.nativeElement.contains(event.target)); } ngOnInit() { if (!this.formControl) { if (Array.isArray(this.value)) { this.formControl = new FormArray([]); this.value.forEach(value => { this.formAsArray.push(new FormControl(value, this.validators)); }); } else { this.formControl = new FormControl(this.value); this.formControl.setValidators(this.validators); } if (this.disabled) { this.formControl.disable(); } } } ngAfterViewInit() { this.reset(); } ngOnChanges(changes: SimpleChanges) { if (changes.formControl || changes.validators || changes.options) { this.reset(); } } ngOnDestroy(): void { this.unsubscribe(); } get formAsControl(): FormControl { if (this.formControl instanceof FormControl) { return this.formControl; } else { return null; } } get formAsArray(): FormArray { if (this.formControl instanceof FormArray) { return this.formControl; } else { return null; } } reset() { this.secure = true; this.unsubscribe(); this.initValue = HelperFunctions.copy(this.formControl.value); if (this.type === 'logoURL') { this.secure = (!this.initValue || this.initValue.includes('https://')); } if (this.options) { this.filteredOptions = this.filter(''); } if (this.type === 'chips' || this.type === 'autocomplete') { this.searchControl = new FormControl('', this.validators); this.subscriptions.push(this.searchControl.valueChanges.subscribe(value => { this.filteredOptions = this.filter(value); if (this.focused) { this.open(true); setTimeout(() => { this.searchInput.nativeElement.focus(); this.searchInput.nativeElement.value = value; }, 0); } })); } if (this.formControl && this.formControl.validator) { let validator = this.formControl.validator({} as AbstractControl); this.required = (validator && validator.required); } this.subscriptions.push(this.formControl.valueChanges.subscribe(value => { value = (value === '') ? null : value; if (this.type === 'logoURL') { this.secure = (!value || value.includes('https://')); } if (this.initValue === value || (this.initValue === '' && value === null)) { this.formControl.markAsPristine(); } else { this.formControl.markAsDirty(); } if (this.searchControl) { this.searchControl.setValue(null); } this.value = this.formControl.value; this.valueChange.emit(this.value); })); if (this.input) { this.input.nativeElement.disabled = this.formControl.disabled; } } unsubscribe() { this.subscriptions.forEach(subscription => { if (subscription instanceof Subscription) { subscription.unsubscribe(); } }); } stopPropagation() { event.stopPropagation(); } remove(index: number) { this.formAsArray.removeAt(index); this.formAsArray.markAsDirty(); this.focus(true); this.searchControl.setValue(''); this.stopPropagation(); } private filter(value: string): Option[] { let options = this.options; if (this.type === "chips") { options = options.filter(option => !this.formAsArray.value.find(value => HelperFunctions.equals(option.value, value))); } if ((!value || value.length == 0)) { return (this.showOptionsOnEmpty) ? options : []; } const filterValue = value.toString().toLowerCase(); return options.filter(option => option.label.toLowerCase().indexOf(filterValue) != -1); } add() { if (this.addExtraChips && this.searchControl.value && this.searchControl.valid) { this.stopPropagation(); this.formAsArray.push(new FormControl(this.searchControl.value, this.validators)); this.formAsArray.markAsDirty(); this.focus(true); this.searchControl.setValue(''); } } getLabel(value: any) { let option = this.options.find(option => HelperFunctions.equals(option.value, value)); return (option) ? option.label : value; } focus(value: boolean) { if(this.focused) { this.formControl.markAsTouched(); } this.focused = value; if (this.focused) { if (this.input) { this.input.nativeElement.focus(); } else if (this.textArea) { this.textArea.nativeElement.focus(); } else if (this.searchInput) { this.searchInput.nativeElement.focus(); } if (this.type === 'select') { this.open(!this.opened); } else if (this.type !== 'autocomplete' || !this.formControl.value) { this.open(true); } } else { this.open(false); if (this.searchControl) { this.add(); } } this.focusEmitter.emit(this.focused); } open(value: boolean) { this.opened = value && this.formControl.enabled; setTimeout(() => { if(this.optionBox) { if (this.opened) { UIkit.dropdown(this.optionBox.nativeElement).show(); } else { UIkit.dropdown(this.optionBox.nativeElement).hide(); } } }, 0); } resetSearch(event: any) { event.stopPropagation(); this.formControl.markAsDirty(); this.formControl.setValue(null); this.focus(true); this.searchControl.setValue(''); } selectOption(option: Option, event) { if(this.formControl.enabled) { if (this.formAsControl) { this.formAsControl.setValue(option.value); } else if (this.formAsArray) { this.formAsArray.push(new FormControl(option.value)); this.formAsArray.markAsDirty(); event.stopPropagation(); this.focus(true); this.searchControl.setValue(''); } } } }