From c293b61e61e913a7e299e76a2aad88c90bbc9a12 Mon Sep 17 00:00:00 2001 From: fshovchko <60318924+fshovchko@users.noreply.github.com> Date: Fri, 31 Mar 2023 03:37:33 +0300 Subject: [PATCH] feat(esl-utils): `getEventListeners()` method introduced for `SynteticEventTarget` (#1548) `hasEventListener(n: number)` is deprecated, use `getEventListeners().length` for extended checks `hasEventListener(type: string, n: number)` is deprecated use `getEventListeners(type).length` for extended checks --- .../core/targets/decorated.target.ts | 7 ++-- .../core/targets/resize.adapter.ts | 2 +- .../core/conditions/media-query-condition.ts | 2 +- .../core/conditions/media-query-containers.ts | 2 +- .../core/esl-media-rule-list.ts | 2 +- src/modules/esl-utils/dom/events/target.ts | 34 ++++++++++++++----- 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/modules/esl-event-listener/core/targets/decorated.target.ts b/src/modules/esl-event-listener/core/targets/decorated.target.ts index 472e7ff75..af3dd0bb2 100644 --- a/src/modules/esl-event-listener/core/targets/decorated.target.ts +++ b/src/modules/esl-event-listener/core/targets/decorated.target.ts @@ -51,10 +51,9 @@ export class ESLEventTargetDecorator extends SyntheticEventT public override addEventListener(event: any, callback: EventListener = event): void { super.addEventListener(event, callback); - if (!this.hasEventListener(event, 1)) { - const {target} = this; - ESLEventListener.subscribe(this, this.createHandler(), {event, target}); - } + if (this.getEventListeners(event).length > 1) return; + const {target} = this; + ESLEventListener.subscribe(this, this.createHandler(), {event, target}); } /** Unsubscribes from the instance active rule change */ diff --git a/src/modules/esl-event-listener/core/targets/resize.adapter.ts b/src/modules/esl-event-listener/core/targets/resize.adapter.ts index dcff5e788..9532b8ea2 100644 --- a/src/modules/esl-event-listener/core/targets/resize.adapter.ts +++ b/src/modules/esl-event-listener/core/targets/resize.adapter.ts @@ -51,7 +51,7 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget { } super.addEventListener('resize', callback); - if (this.hasEventListener('resize', 1)) return; + if (this.getEventListeners('resize').length > 1) return; ESLResizeObserverTarget.observer$$.observe(this.target); } diff --git a/src/modules/esl-media-query/core/conditions/media-query-condition.ts b/src/modules/esl-media-query/core/conditions/media-query-condition.ts index cf7a874d8..83813b0dd 100644 --- a/src/modules/esl-media-query/core/conditions/media-query-condition.ts +++ b/src/modules/esl-media-query/core/conditions/media-query-condition.ts @@ -28,7 +28,7 @@ export class MediaQueryCondition extends SyntheticEventTarget implements IMediaQ public override addEventListener(type: 'change', callback: EventListener): void; public override addEventListener(type: any, callback: EventListener = type): void { super.addEventListener(type, callback); - if (this.hasEventListener(1)) return; + if (this.getEventListeners('change').length > 1) return; if (typeof this._mq.addEventListener === 'function') { this._mq.addEventListener('change', this._onChange); } else { diff --git a/src/modules/esl-media-query/core/conditions/media-query-containers.ts b/src/modules/esl-media-query/core/conditions/media-query-containers.ts index 3ed18688f..d39e3919d 100644 --- a/src/modules/esl-media-query/core/conditions/media-query-containers.ts +++ b/src/modules/esl-media-query/core/conditions/media-query-containers.ts @@ -23,7 +23,7 @@ abstract class MediaQueryContainer extends SyntheticEventTarget implements IMedi public override addEventListener(type: 'change', callback: EventListener): void; public override addEventListener(type: any, callback: EventListener = type): void { super.addEventListener(type, callback); - if (this.hasEventListener(1)) return; + if (this.getEventListeners('change').length > 1) return; this.items.forEach((item) => item.addEventListener('change', this._onChange)); } diff --git a/src/modules/esl-media-query/core/esl-media-rule-list.ts b/src/modules/esl-media-query/core/esl-media-rule-list.ts index 6a63d5421..05cdfab62 100644 --- a/src/modules/esl-media-query/core/esl-media-rule-list.ts +++ b/src/modules/esl-media-query/core/esl-media-rule-list.ts @@ -151,7 +151,7 @@ export class ESLMediaRuleList extends SyntheticEventTarget { public override addEventListener(type: 'change', callback: EventListener): void; public override addEventListener(type: any, callback: EventListener = type): void { super.addEventListener(type, callback); - if (this.hasEventListener(1)) return; + if (this.getEventListeners('change').length > 1) return; this._value = this.computedValue; this.rules.forEach((rule) => rule.addEventListener(this._onMatchChanged)); } diff --git a/src/modules/esl-utils/dom/events/target.ts b/src/modules/esl-utils/dom/events/target.ts index 01d87a2c4..a3e3328c5 100644 --- a/src/modules/esl-utils/dom/events/target.ts +++ b/src/modules/esl-utils/dom/events/target.ts @@ -1,5 +1,9 @@ +import {flat, uniq} from '../../misc/array'; import {overrideEvent} from './misc'; +/** Key to store listeners on the {@link SyntheticEventTarget} instance*/ +const LISTENERS = Symbol('_listeners'); + /** * Synthetic implementation of EventTarget * Replicates behavior of native event @@ -9,14 +13,24 @@ export class SyntheticEventTarget implements EventTarget { // Event type to use in the shortcutted calls public static DEFAULT_EVENT = 'change'; - private readonly _listeners: Record = {}; + private readonly [LISTENERS]: Record = {}; + + protected getEventListeners(): EventListenerOrEventListenerObject[]; + protected getEventListeners(type: string): EventListenerOrEventListenerObject[]; + protected getEventListeners(type?: string): EventListenerOrEventListenerObject[] { + if (typeof type !== 'string') return uniq(flat(Object.values(this[LISTENERS]))); + return this[LISTENERS][type] || []; + } public hasEventListener(): boolean; + public hasEventListener(type: string): boolean; + /** @deprecated alias for `addEventListener` */ public hasEventListener(type: string | number): boolean; + /** @deprecated alias for `addEventListener` */ public hasEventListener(type: string, minCount: number): boolean; public hasEventListener(type: string | number = 'change', minCount: number = 0): boolean { - if (typeof type !== 'string') return this.hasEventListener('change', type || 0); - return this._listeners[type]?.length > minCount; + if (typeof type !== 'string') return this.hasEventListener('change', type || 0); // TODO: remove in 5.0.0 + return this.getEventListeners(type).length > minCount; } public addEventListener(callback: EventListenerOrEventListenerObject): void; @@ -25,9 +39,10 @@ export class SyntheticEventTarget implements EventTarget { if (typeof type !== 'string') return this.addEventListener((this.constructor as typeof SyntheticEventTarget).DEFAULT_EVENT, type); validateEventListenerType(callback); - if (this._listeners[type] && this._listeners[type].includes(callback!)) return; - if (this._listeners[type]) this._listeners[type].push(callback!); - else Object.assign(this._listeners, {[type]: [callback]}); + const listeners = this[LISTENERS][type]; + if (listeners?.includes(callback!)) return; + if (listeners) listeners.push(callback!); + else Object.assign(this[LISTENERS], {[type]: [callback]}); } public removeEventListener(callback: EventListenerOrEventListenerObject): void; @@ -36,8 +51,9 @@ export class SyntheticEventTarget implements EventTarget { if (typeof type !== 'string') return this.removeEventListener((this.constructor as typeof SyntheticEventTarget).DEFAULT_EVENT, type); validateEventListenerType(callback); - if (!this._listeners[type]) return; - this._listeners[type] = this._listeners[type].filter((cb) => cb !== callback); + const listeners = this[LISTENERS][type]; + if (!listeners) return; + this[LISTENERS][type] = listeners.filter((cb) => cb !== callback); } public dispatchEvent(e: Event): boolean; @@ -48,7 +64,7 @@ export class SyntheticEventTarget implements EventTarget { if (target) overrideEvent(e, 'target', target); // TODO: remove in 5.0.0 if (!e.target) overrideEvent(e, 'target', this); if (!e.srcElement) overrideEvent(e, 'srcElement', e.target); // TODO: remove in 5.0.0 - this._listeners[e.type]?.forEach((listener) => { + this.getEventListeners(e.type).forEach((listener) => { if (typeof listener === 'function') listener.call(this, e); else listener.handleEvent.call(listener, e); });