import { Component, EventEmitter, Output, ElementRef, Renderer } from '@angular/core'; import { DIRECTIONS } from "../lib/picker-directions"; import { Subject } from "rxjs/Subject"; import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/takeUntil'; @Component({ selector: 'emoji-picker', styles: [':host { position: absolute; z-index: 9999; }'], template: ` `, host: { '(document:mousedown)': 'onBackground($event)', '(mousedown)': '_lastHostMousedownEvent = $event', '(window:resize)': '_windowResize.next($event)' } }) export class EmojiPickerComponent { @Output('emoji-select') selectionEmitter = new EventEmitter(); @Output('picker-close') pickerCloseEmitter = new EventEmitter(); emojiPickerAutofocus: boolean; private _lastHostMousedownEvent; private _currentTarget: ElementRef; private _currentDirection: DIRECTIONS; private _windowResize = new Subject(); private _destroyed = new Subject(); constructor(private _renderer: Renderer, private _el: ElementRef) { this._windowResize .takeUntil(this._destroyed) .debounceTime(100) .subscribe(event => { this.setPosition(this._currentTarget, this._currentDirection); }) } setPosition(target: ElementRef, directionCode: DIRECTIONS = DIRECTIONS.bottom) { if (!target) { return console.error('Emoji-Picker: positioning failed due to missing target element or direction code'); } this._renderer.setElementStyle(this._el.nativeElement, 'transform', ''); /** Store anchor and direction */ this._currentTarget = target; this._currentDirection = directionCode; const targetBorders = target.nativeElement.getBoundingClientRect(), thisBorders = this._el.nativeElement.getBoundingClientRect(); let heightCorrection = 0, widthCorrection = 0; /** Setting up centering of picker for all cases */ switch (this._currentDirection) { case DIRECTIONS.top: case DIRECTIONS.bottom: widthCorrection = targetBorders.left - thisBorders.left + (targetBorders.width - thisBorders.width) / 2; break; case DIRECTIONS.left: case DIRECTIONS.right: heightCorrection = targetBorders.top - thisBorders.top + (targetBorders.height - thisBorders.height) / 2; break; } /** Setting up relative positioning for all cases */ switch (this._currentDirection) { case DIRECTIONS.top: heightCorrection = targetBorders.top - thisBorders.bottom; break; case DIRECTIONS.left: widthCorrection = targetBorders.left - thisBorders.right; break; case DIRECTIONS.right: widthCorrection = targetBorders.right - thisBorders.left; break; case DIRECTIONS.bottom: heightCorrection = targetBorders.bottom - thisBorders.top; break; } /** Correcting positioning due to overflow problems */ if (thisBorders.bottom + heightCorrection > window.innerHeight) { heightCorrection += window.innerHeight - (thisBorders.bottom + heightCorrection); } if (thisBorders.top + heightCorrection < 0) { heightCorrection -= (thisBorders.top + heightCorrection); } if (thisBorders.right + widthCorrection > window.innerWidth) { widthCorrection += window.innerWidth - (thisBorders.right + widthCorrection); } if (thisBorders.left + widthCorrection < 0) { widthCorrection -= (thisBorders.left + widthCorrection); } /** set the position adjustments to the emoji picker element */ this._renderer.setElementStyle(this._el.nativeElement, 'transform', `translate(${widthCorrection}px,${heightCorrection}px)`); } setAutofocus(value) { this.emojiPickerAutofocus = value; } onBackground(event) { /** internal mousedowns are ignored */ if (event === this._lastHostMousedownEvent || event.emojiPickerExempt) { return; } this.pickerCloseEmitter.emit(event); } ngOnDestroy() { this._destroyed.next(true); } }