angular2-tag-input-clarity
Version:
Tag input component for Angular 2 using VMware Clarity Styling
175 lines (153 loc) • 4.4 kB
text/typescript
import { Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { KEYS } from '../../shared/tag-input-keys';
export class TagInputAutocompleteComponent implements OnChanges, OnDestroy, OnInit {
items: string[];
selectFirstItem: boolean = false;
itemSelected: EventEmitter<string> = new EventEmitter<string>();
enterPressed: EventEmitter<any> = new EventEmitter<any>();
public selectedItemIndex: number = null;
private keySubscription: Subscription;
private get itemsCount(): Number {
return this.items ? this.items.length : 0;
}
constructor(private elementRef: ElementRef) { }
ngOnInit() {
this.keySubscription = Observable.fromEvent(window, 'keydown')
.filter(
(event: KeyboardEvent) =>
event.keyCode === KEYS.upArrow ||
event.keyCode === KEYS.downArrow ||
event.keyCode === KEYS.enter ||
event.keyCode === KEYS.esc
)
.do((event: KeyboardEvent) => {
switch (event.keyCode) {
case KEYS.downArrow:
this.handleDownArrow();
break;
case KEYS.upArrow:
this.handleUpArrow();
break;
case KEYS.enter:
this.selectItem();
this.enterPressed.emit();
break;
case KEYS.esc:
break;
}
event.stopPropagation();
event.preventDefault();
})
.subscribe();
}
ensureHighlightVisible() {
let container = this.elementRef.nativeElement.querySelector('.sk-select-results__container');
if (!container) {
return;
}
let choices = container.querySelectorAll('.sk-select-results__item');
if (choices.length < 1) {
return;
}
if (this.selectedItemIndex < 0) {
return;
}
let highlighted: any = choices[this.selectedItemIndex];
if (!highlighted) {
return;
}
let posY: number = highlighted.offsetTop + highlighted.clientHeight - container.scrollTop;
let height: number = container.offsetHeight;
if (posY > height) {
container.scrollTop += posY - height;
} else if (posY < highlighted.clientHeight) {
container.scrollTop -= highlighted.clientHeight - posY;
}
}
goToTop() {
this.selectedItemIndex = 0;
this.ensureHighlightVisible();
}
goToBottom(itemsCount) {
this.selectedItemIndex = itemsCount - 1;
this.ensureHighlightVisible();
}
goToNext() {
if (this.selectedItemIndex + 1 < this.itemsCount) {
this.selectedItemIndex++;
} else {
this.goToTop();
}
this.ensureHighlightVisible();
}
goToPrevious() {
if (this.selectedItemIndex - 1 >= 0) {
this.selectedItemIndex--;
} else {
this.goToBottom(this.itemsCount);
}
this.ensureHighlightVisible();
}
handleUpArrow() {
if (this.selectedItemIndex === null) {
this.goToBottom(this.itemsCount);
return false;
}
this.goToPrevious();
}
handleDownArrow() {
// Initialize to zero if first time results are shown
if (this.selectedItemIndex === null) {
this.goToTop();
return false;
}
this.goToNext();
}
selectItem(itemIndex?: number): void {
let itemToEmit = itemIndex ? this.items[itemIndex] : this.items[this.selectedItemIndex];
if (itemToEmit) {
this.itemSelected.emit(itemToEmit);
}
}
ngOnChanges(changes) {
if (this.selectFirstItem && this.itemsCount > 0) {
this.goToTop();
}
}
ngOnDestroy() {
this.keySubscription.unsubscribe();
}
}