import {Directive, OnInit, OnDestroy, Output, EventEmitter, ElementRef, Input} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/observable/fromEvent'; import 'rxjs/add/operator/delay'; import 'rxjs/add/operator/do'; import {Subscriber} from "rxjs"; @Directive({ selector: '[click-outside-or-esc]' }) export class ClickOutsideOrEsc implements OnInit, OnDestroy { private listening: boolean; private subscriptions: any[] = []; @Input() public targetId = null; @Input() public escClose = true; @Input() public clickClose = true; @Output('clickOutside') clickOutside: EventEmitter; constructor(private _elRef: ElementRef) { this.listening = false; this.clickOutside = new EventEmitter(); } ngOnInit() { if(typeof document !== 'undefined') { this.subscriptions.push(Observable .fromEvent(document, 'click') .delay(1) .do(() => { this.listening = true; }).subscribe((event: MouseEvent) => { this.onGlobalClick(event); })); this.subscriptions.push(Observable .fromEvent(document, 'keydown') .delay(1) .do(() => { this.listening = true; }).subscribe((event: KeyboardEvent) => { if (event.keyCode === 27 && this.escClose) { this.clickOutside.emit({ target: (event.target || null), value: true }); } })); } } ngOnDestroy() { if (this.subscriptions) { this.subscriptions.forEach((subscription: Subscriber) => { subscription.unsubscribe(); }); } this.subscriptions = []; } onGlobalClick(event: MouseEvent) { if (event instanceof MouseEvent && this.listening === true) { let element: HTMLElement = event.target; while (element) { if(element.id === this.targetId) { this.clickOutside.emit({ target: (event.target || null), value: false }); return; } element = element.parentElement; } if(this.clickClose) { this.clickOutside.emit({ target: (event.target || null), value: true }); } } } }