UNPKG

angular2-tag-input-clarity

Version:

Tag input component for Angular 2 using VMware Clarity Styling

175 lines (153 loc) 4.4 kB
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'; @Component({ selector: 'rl-tag-input-autocomplete', template: ` <div *ngFor="let item of items; let itemIndex = index" [ngClass]="{ 'is-selected': selectedItemIndex === itemIndex }" (click)="selectItem(itemIndex)" class="rl-autocomplete-item"> {{item}} </div> `, styles: [` :host { box-shadow: 0 1.5px 4px rgba(0, 0, 0, 0.24), 0 1.5px 6px rgba(0, 0, 0, 0.12); display: block; position: absolute; top: 100%; color: #444444; background: white; padding: 0 0; } :host .rl-autocomplete-item { padding: 0 16px; height: 48px; line-height: 48px; } :host .is-selected { background: #eeeeee; } `] }) export class TagInputAutocompleteComponent implements OnChanges, OnDestroy, OnInit { @Input() items: string[]; @Input() selectFirstItem: boolean = false; @Output() itemSelected: EventEmitter<string> = new EventEmitter<string>(); @Output() 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(); } }