novo-elements
Version:
Bullhorn's NOVO Element Repository for Angular 2
175 lines (162 loc) • 6.58 kB
text/typescript
// NG2
import { Component, Input, Output, EventEmitter, OnDestroy, AfterContentInit, ViewChild, forwardRef, ElementRef, OnInit, OnChanges, SimpleChanges, HostBinding, HostListener, ChangeDetectorRef, NgZone, ChangeDetectionStrategy } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { TAB, ENTER, ESCAPE } from '@angular/cdk/keycodes';
import { DOCUMENT } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
// APP
import { NovoOverlayTemplate } from '../overlay/Overlay';
import { KeyCodes } from '../../utils/key-codes/KeyCodes';
import { NovoLabelService } from '../../services/novo-label-service';
import { Helpers } from '../../utils/Helpers';
// Value accessor for the component (supports ngModel)
const SEARCH_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NovoSearchBoxElement),
multi: true
};
export class NovoSearchBoxElement implements ControlValueAccessor, OnDestroy, AfterContentInit {
public name: string;
public icon: string = 'search';
public placeholder: string = 'Search...';
public alwaysOpen: boolean = false;
public theme: string = 'positive';
public closeOnSelect: boolean = true;
public displayField: string;
public displayValue: string;
public hint: string;
public searchChanged: EventEmitter<string> = new EventEmitter<string>();
focused: boolean = false;
public value: any;
/** View -> model callback called when value changes */
_onChange: (value: any) => void = () => { };
/** View -> model callback called when autocomplete has been touched */
_onTouched = () => { };
/** Element for the panel containing the autocomplete options. */
overlay: any;
input: any;
private keyboardObserver: Subscription;
constructor(
public element: ElementRef,
public labels: NovoLabelService,
private _changeDetectorRef: ChangeDetectorRef,
private _zone: NgZone
) {
}
public ngAfterContentInit(): void {
this.keyboardObserver = Observable.fromEvent(this.input.nativeElement, 'keyup')
.debounceTime(300)
.subscribe((event: KeyboardEvent) => this.searchChanged.emit(this.input.nativeElement.value))
}
public ngOnDestroy(): void {
this.keyboardObserver.unsubscribe();
}
/**
* @name showFasterFind
* @description This function shows the picker and adds the active class (for animation)
*/
showSearch(event?: any, forceClose: boolean = false) {
if (!this.panelOpen) {
// Reset search
// Set focus on search
setTimeout(() => {
let element = this.input.nativeElement;
if (element) {
element.focus();
}
}, 10);
}
}
onFocus() {
this._zone.run(() => {
this.focused = true;
this.openPanel();
});
}
onBlur() {
this.focused = false;
}
/** BEGIN: Convienient Panel Methods. */
openPanel(): void {
this.overlay.openPanel();
}
closePanel(): void {
this.overlay.closePanel();
}
get panelOpen(): boolean {
return this.overlay && this.overlay.panelOpen;
}
get active(): boolean {
return this.panelOpen || this.alwaysOpen;
}
/** END: Convienient Panel Methods. */
_handleKeydown(event: KeyboardEvent): void {
if ((event.keyCode === ESCAPE || event.keyCode === ENTER || event.keyCode === TAB) && this.panelOpen) {
this.closePanel();
event.stopPropagation();
}
}
_handleInput(event: KeyboardEvent): void {
if (document.activeElement === event.target) {
this._onChange((event.target as HTMLInputElement).value);
//this.openPanel();
}
}
writeValue(value: any): void {
this._setValue(value);
}
registerOnChange(fn: (value: any) => {}): void {
this._onChange = fn;
}
registerOnTouched(fn: () => {}) {
this._onTouched = fn;
}
private _setValue(value: any): void {
this.value = value;
let toDisplay = value;
if (value && this.displayField) {
toDisplay = value.hasOwnProperty(this.displayField) ? value[this.displayField] : value;
}
// Simply falling back to an empty string if the display value is falsy does not work properly.
// The display value can also be the number zero and shouldn't fall back to an empty string.
this.displayValue = toDisplay ? toDisplay : '';
this.input.nativeElement.value = this.displayValue;
this._changeDetectorRef.markForCheck();
}
/**
* This method closes the panel, and if a value is specified, also sets the associated
* control to that value. It will also mark the control as dirty if this interaction
* stemmed from the user.
*/
public setValueAndClose(event: any | null): void {
if (event && event.value) {
this._setValue(event.value);
this._onChange(event.value);
}
this.closePanel();
}
/**
* Clear any previous selected option and emit a selection change event for this option
*/
public clearValue(skip: any) {
this.writeValue(null);
this._onChange(null);
}
}