import {Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core'; import {fromEvent, Subscriber} from 'rxjs'; import {delay, tap} from 'rxjs/operators'; @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(fromEvent(document, 'click').pipe( delay(1), tap(() => { this.listening = true; })).subscribe((event: MouseEvent) => { this.onGlobalClick(event); })); this.subscriptions.push(fromEvent(document, 'click').pipe( delay(1), tap(() => { 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 }); } } } }