import { FormControl, Validators } from '@angular/forms'; import { Component, OnInit, Input, HostListener, ViewChild, ElementRef } from '@angular/core'; import { MatAutocompleteTrigger } from '@angular/material'; import { ComboBoxOptions } from './ComboBoxOptions'; import { Defaults } from '../../constant/defaults.constant'; import { Helper } from '../../helper'; import * as KEY_CODES from '../../constant/keycodes.constant'; @Component({ selector: 'rss-combo-box', templateUrl: './combo-box.component.html', styleUrls: ['./combo-box.component.scss'] }) export class ComboBoxComponent implements OnInit { @Input() control: FormControl; @Input() config: any; @Input() readonly: Boolean; @Input() label: any; @Input() options: any; @Input() placeholder = Defaults.COMBO_PLACEHOLDER; @Input() displayKey = Defaults.DATASOURCE_DISPLAY_KEY; @Input() maxLength = Defaults.INPUT_BOX_MAXLENGTH; @Input() required: boolean = Defaults.REQUIRED; @Input() requiredError: string = Defaults.REQUIRED_ERROR_MESSAGE; @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; filteredSource: any; displayedData: string[] = []; arrowDirection = ''; ARROW_DOWN = 'arrow_drop_down'; ARROW_UP = 'arrow_drop_up'; constructor(private el: ElementRef) { this.arrowDirection = this.ARROW_DOWN; } ngOnInit() { if (this.config) { const config = new ComboBoxOptions(this.config); Helper.merge(this, config); this.options = this.options || this.config.options; } if (!this.control) { console.error(Defaults.MISSING_FORM_CONTROL_ERROR_MESSAGE); } else if (!this.options) { console.error(Defaults.MISSING_DATA_SOURCE_ERROR_MESSAGE); } else { if (this.required) { this.control.setValidators(Validators.required); } this.displayedData = Helper.normalizeDataSource(this.options, this.displayKey); this.control.valueChanges .startWith(null) .map(name => { if (this.trigger && this.trigger.panelOpen) { this.arrowDirection = this.ARROW_UP; } else { this.arrowDirection = this.ARROW_DOWN; } return this.filterSource(name); }) .subscribe(list => { if (list && !list.length) { this.closePanel(); } this.filteredSource = list; }); } } filterSource(val: any) { val = this.sanitizeInput(val); if (this.displayedData.length) { return val ? this.displayedData .filter((s) => new RegExp(val, 'gi').test(s)) : this.displayedData; } } sanitizeInput(val) { return val && val.match(/^[a-zA-Z0-9-,().\s]*$/) ? val : ''; } onIconClick() { if (this.arrowDirection === this.ARROW_DOWN) { this.trigger.openPanel(); this.filteredSource = this.displayedData; } } @HostListener('window:keydown', ['$event']) keyboardInput(event) { if (event.keyCode === KEY_CODES.ESCAPE || event.keyCode === KEY_CODES.ENTER) { this.closePanel(); } if ((event.keyCode === KEY_CODES.DOWN_ARROW || event.keyCode === KEY_CODES.UP_ARROW) && this.trigger.panelOpen) { this.openPanel(); } if (event.keyCode === KEY_CODES.TAB) { if (this.trigger.panelOpen) { this.openPanel(); } else { this.closePanel(); } } } @HostListener('document:click', ['$event.target']) public onClick(targetElement) { if (!this.el.nativeElement.contains(targetElement)) { this.closePanel(); } else { if (this.arrowDirection === this.ARROW_DOWN) { this.openPanel(); } else { this.closePanel(); } } } closePanel() { this.arrowDirection = this.ARROW_DOWN; this.trigger.closePanel(); } openPanel() { this.arrowDirection = this.ARROW_UP; this.trigger.openPanel(); } displayRequiredError(fc: FormControl) { return Helper.displayRequiredError(fc); } }