ng-click-outside2
Version:
Angular directive for handling click events outside an element.
301 lines (291 loc) • 13.3 kB
JavaScript
import * as i0 from '@angular/core';
import { input, inject, Directive, booleanAttribute, output, ElementRef, NgZone, afterNextRender, effect } from '@angular/core';
import { DOCUMENT } from '@angular/common';
/**
* Optimization for Treeshaking: https://angular.io/guide/lightweight-injection-tokens
*/
class NgClickOutsideExcludeToken {
}
/**
* Directive to exclude elements from the click-outside
*/
class NgClickOutsideExcludeDirective extends NgClickOutsideExcludeToken {
constructor() {
super(...arguments);
/**
* A comma-separated string of DOM element queries to exclude when clicking outside of the element.
* For example: `[exclude]="'button,.btn-primary'"`.
*/
this.clickOutsideExclude = input('');
this.document = inject(DOCUMENT);
}
excludeCheck() {
const clickOutsideExclude = this.clickOutsideExclude();
if (clickOutsideExclude) {
try {
const nodes = Array.from(this.document.querySelectorAll(clickOutsideExclude));
if (nodes) {
return nodes;
}
}
catch (err) {
console.error('[ng-click-outside] Check your exclude selector syntax.', err);
}
}
return [];
}
isExclude(target) {
const nodesExcluded = this.excludeCheck();
for (let excludedNode of nodesExcluded) {
if (excludedNode.contains(target)) {
return true;
}
}
return false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideExcludeDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.3", type: NgClickOutsideExcludeDirective, isStandalone: true, selector: "[clickOutsideExclude]", inputs: { clickOutsideExclude: { classPropertyName: "clickOutsideExclude", publicName: "clickOutsideExclude", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
{ provide: NgClickOutsideExcludeToken, useExisting: NgClickOutsideExcludeDirective }
], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideExcludeDirective, decorators: [{
type: Directive,
args: [{
selector: '[clickOutsideExclude]',
standalone: true,
providers: [
{ provide: NgClickOutsideExcludeToken, useExisting: NgClickOutsideExcludeDirective }
]
}]
}] });
function arrayAttribute(events) {
if (Array.isArray(events)) {
return events;
}
else {
return events.split(',');
}
}
/**
* Directive to detect clicks outside of the current element
*
* ```typescript
* @Component({
* selector: 'app',
* template: `
* <div (clickOutside)="onClickedOutside($event)">Click outside this</div>
* `
* })
* export class AppComponent {
* onClickedOutside(e: Event) {
* console.log('Clicked outside:', e);
* }
* }
* ```
*/
class NgClickOutsideDirective {
constructor() {
/**
* Enables directive.
*/
this.clickOutsideEnabled = input(true, { transform: booleanAttribute });
/**
* A comma-separated list of events to cause the trigger.
* ### For example, for additional mobile support:
* `[clickOutsideEvents]="'click,touchstart'"`
*/
this.clickOutsideEvents = input(['click'], {
transform: arrayAttribute
});
/**
* Outside Click Event
*/
this.clickOutside = output();
this.excludeDirective = inject(NgClickOutsideExcludeToken, { host: true, optional: true });
this._el = inject(ElementRef);
this._ngZone = inject(NgZone);
this.document = inject(DOCUMENT);
this._initOnClickBody = this._initOnClickBody.bind(this);
this._onClickBody = this._onClickBody.bind(this);
afterNextRender(() => this._init());
}
ngOnDestroy() {
this._removeClickOutsideListener();
}
_init() {
this._initOnClickBody();
}
_initOnClickBody() {
this._initClickOutsideListener();
}
_emit(ev) {
this._ngZone.run(() => this.clickOutside.emit(ev));
}
_initClickOutsideListener() {
this._ngZone.runOutsideAngular(() => {
this.clickOutsideEvents().forEach(e => this.document.addEventListener(e, this._onClickBody));
});
}
_removeClickOutsideListener() {
this._ngZone.runOutsideAngular(() => {
this.clickOutsideEvents().forEach(e => this.document.removeEventListener(e, this._onClickBody));
});
}
_onClickBody(ev) {
if (!this.clickOutsideEnabled()) {
return;
}
if (!this._el.nativeElement.contains(ev.target) && !this.excludeDirective?.isExclude(ev.target)) {
this._emit(ev);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.3", type: NgClickOutsideDirective, isStandalone: true, selector: "[clickOutside]:not([delayClickOutsideInit]):not([attachOutsideOnClick])", inputs: { clickOutsideEnabled: { classPropertyName: "clickOutsideEnabled", publicName: "clickOutsideEnabled", isSignal: true, isRequired: false, transformFunction: null }, clickOutsideEvents: { classPropertyName: "clickOutsideEvents", publicName: "clickOutsideEvents", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickOutside: "clickOutside" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideDirective, decorators: [{
type: Directive,
args: [{
selector: '[clickOutside]:not([delayClickOutsideInit]):not([attachOutsideOnClick])',
standalone: true,
}]
}], ctorParameters: () => [] });
/**
* Directive only starts after a single click and the outside click event handler
* will then be removed after a click outside has occurred.
*/
class NgClickOutsideAttachOutsideDirective extends NgClickOutsideDirective {
constructor() {
super();
/**
* By default, the outside click event handler is automatically attached.
*
* Explicitely setting this to `true`sets the handler after the element is clicked. The outside click event handler
* will then be removed after a click outside has occurred.
*/
this.attachOutsideOnClick = input.required({ transform: booleanAttribute });
effect(() => {
this.attachOutsideOnClick();
this._init();
});
}
ngOnDestroy() {
super.ngOnDestroy();
this._removeAttachOutsideOnClickListener();
}
_init() {
if (this.attachOutsideOnClick()) {
this._initAttachOutsideOnClickListener();
}
else {
this._initOnClickBody();
}
}
_emit(ev) {
if (this.attachOutsideOnClick()) {
this._removeClickOutsideListener();
}
super._emit(ev);
}
_initAttachOutsideOnClickListener() {
this._ngZone.runOutsideAngular(() => {
this.clickOutsideEvents().forEach(e => this._el.nativeElement.addEventListener(e, this._initOnClickBody));
});
}
_removeAttachOutsideOnClickListener() {
this._ngZone.runOutsideAngular(() => {
this.clickOutsideEvents().forEach(e => this._el.nativeElement.removeEventListener(e, this._initOnClickBody));
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideAttachOutsideDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.3", type: NgClickOutsideAttachOutsideDirective, isStandalone: true, selector: "[clickOutside][attachOutsideOnClick]", inputs: { attachOutsideOnClick: { classPropertyName: "attachOutsideOnClick", publicName: "attachOutsideOnClick", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideAttachOutsideDirective, decorators: [{
type: Directive,
args: [{
selector: '[clickOutside][attachOutsideOnClick]',
standalone: true
}]
}], ctorParameters: () => [] });
/**
* Click oustside Directive but with an setTimeout on the listener
* This may help for items that are conditionally shown ([see issue #13](https://github.com/arkon/ng-click-outside/issues/13)).
*/
class NgClickOutsideDelayOutsideDirective extends NgClickOutsideDirective {
constructor() {
super(...arguments);
/**
* Delays the initialization of the click outside handler.
* This may help for items that are conditionally shown ([see issue #13](https://github.com/arkon/ng-click-outside/issues/13)).
*/
this.delayClickOutsideInit = input.required({ transform: booleanAttribute });
}
_initOnClickBody() {
if (this.delayClickOutsideInit()) {
setTimeout(this._initClickOutsideListener.bind(this));
}
else {
this._initClickOutsideListener();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideDelayOutsideDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.3", type: NgClickOutsideDelayOutsideDirective, isStandalone: true, selector: "[clickOutside][delayClickOutsideInit]", inputs: { delayClickOutsideInit: { classPropertyName: "delayClickOutsideInit", publicName: "delayClickOutsideInit", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideDelayOutsideDirective, decorators: [{
type: Directive,
args: [{
selector: '[clickOutside][delayClickOutsideInit]',
standalone: true
}]
}] });
/**
* emits an event when user clicks outside of applications' window while it's visible.
* Especially useful if page contains iframes.
*/
class NgClickOutsideEmitOnBlurDirective {
constructor() {
this._ngZone = inject(NgZone);
this.document = inject(DOCUMENT);
this.blurWindow = output();
this._onWindowBlur = this._onWindowBlur.bind(this);
afterNextRender(() => this._initWindowBlurListener());
}
ngOnDestroy() {
this._removeWindowBlurListener();
}
_initWindowBlurListener() {
this._ngZone.runOutsideAngular(() => {
this.document.defaultView?.addEventListener('blur', this._onWindowBlur);
});
}
/**
* Resolves problem with outside click on iframe
* @see https://github.com/arkon/ng-click-outside/issues/32
*/
_onWindowBlur(ev) {
if (!this.document.hidden) {
this._ngZone.run(() => this.blurWindow.emit(ev));
}
}
_removeWindowBlurListener() {
this._ngZone.runOutsideAngular(() => {
this.document.defaultView?.removeEventListener('blur', this._onWindowBlur);
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideEmitOnBlurDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.3", type: NgClickOutsideEmitOnBlurDirective, isStandalone: true, selector: "[clickOutsideEmitOnBlur]", outputs: { blurWindow: "blurWindow" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgClickOutsideEmitOnBlurDirective, decorators: [{
type: Directive,
args: [{
selector: '[clickOutsideEmitOnBlur]',
standalone: true
}]
}], ctorParameters: () => [] });
/*
* Public API Surface of ng-click-outside2
*/
/**
* Generated bundle index. Do not edit.
*/
export { NgClickOutsideAttachOutsideDirective, NgClickOutsideDelayOutsideDirective, NgClickOutsideDirective, NgClickOutsideEmitOnBlurDirective, NgClickOutsideExcludeDirective, NgClickOutsideExcludeToken };
//# sourceMappingURL=ng-click-outside2.mjs.map