import {Component, EventEmitter} from '@angular/core'; import {OnChanges, SimpleChange} from '@angular/core'; import {ContentChild, AfterContentInit} from '@angular/core'; import {TemplateRef} from '@angular/core'; import {CORE_DIRECTIVES} from '@angular/common'; import {FocusOnShow} from './focusOnShow'; import {BdVRepeat, BdItemTemplate} from '../VRepeat/vrepeat'; import {BdOptionTemplate} from './option-template'; export {BdOptionTemplate}; const DEFAULT_ITEM_TEXT_FUNCTION = new Function('$item', 'return $item;'); @Component({ selector: 'bd-select', inputs: ['value', 'options', 'itemText', 'placeholder'], outputs: ['valueChange', 'blur'], directives: [CORE_DIRECTIVES, BdVRepeat, BdItemTemplate, FocusOnShow], styles: [require('./select.scss')], template: `
` }) export class BdSelect implements AfterContentInit, OnChanges { public placeholder: string; public options: Array; public visibleOptions: Array; public isPopupOpen: boolean; public isVisible: boolean; public selectedOptionText: string; public value: any; public valueChange: EventEmitter; public blur: EventEmitter; public selectedOption: any; private selectedOptionIndex: number; public optionTemplate: TemplateRef; @ContentChild(BdOptionTemplate) optionTemplateChild: BdOptionTemplate; public itemText: string; public itemTextFunction: Function; private _searchPhrase: string; constructor() { this.valueChange = new EventEmitter(); this.blur = new EventEmitter(false); this._searchPhrase = ''; this.placeholder = ''; this.isPopupOpen = false; this.isVisible = true; this.visibleOptions = []; this.selectedOptionText = ''; this.selectedOptionIndex = 0; this.itemTextFunction = DEFAULT_ITEM_TEXT_FUNCTION; } optionClicked(item: any) { this.valueChange.emit(item); this.value = item; this.selectedOptionText = this.itemTextFunction(item); this.isPopupOpen = false; } keydown(event: KeyboardEvent) { switch (event.which) { case 13: //enter this.isPopupOpen = false; this.optionClicked(this.selectedOption); break; case 27: //escape this.isPopupOpen = false; break; case 38: //arrow up event.preventDefault(); this.changeSelectedOption(-1); break; case 40: //arrow down event.preventDefault(); this.changeSelectedOption(1); break; } } set searchPhrase(value: string) { this._searchPhrase = value; this.visibleOptions = this.options; const filterExpr = new RegExp(value, 'i'); this.visibleOptions = this.options.filter((option) => this.itemTextFunction(option).search(filterExpr) !== -1); this.selectedOptionIndex = 0; this.selectedOption = this.visibleOptions[this.selectedOptionIndex]; } get searchPhrase() { return this._searchPhrase; } ngOnChanges(changes: { [key: string]: SimpleChange }) { if (changes['options'] && changes['options'].currentValue) { this.visibleOptions = this.options; this._searchPhrase = ''; this.selectedOptionIndex = 0; this.selectedOption = this.visibleOptions[this.selectedOptionIndex]; } if (changes['itemText'] && changes['itemText'].currentValue) { this.itemTextFunction = new Function('$item', `return ${this.itemText};`); } } ngAfterContentInit() { if (this.optionTemplateChild) { this.optionTemplate = this.optionTemplateChild.getTemplate(); } } private changeSelectedOption(positionChange: number) { let nextPosition = this.selectedOptionIndex + positionChange; if (nextPosition < 0) { nextPosition = this.visibleOptions.length - 1; } if (nextPosition >= this.visibleOptions.length) { nextPosition = 0; } this.selectedOptionIndex = nextPosition; this.selectedOption = this.visibleOptions[this.selectedOptionIndex]; } }