Skip to content

Commit

Permalink
feat(esl-utils): getEventListeners() method introduced for `Synteti…
Browse files Browse the repository at this point in the history
…cEventTarget` (#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
  • Loading branch information
fshovchko authored Mar 31, 2023
1 parent 1c31e21 commit c293b61
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,9 @@ export class ESLEventTargetDecorator<Args extends any[]> 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 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down
2 changes: 1 addition & 1 deletion src/modules/esl-media-query/core/esl-media-rule-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export class ESLMediaRuleList<T = any> 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));
}
Expand Down
34 changes: 25 additions & 9 deletions src/modules/esl-utils/dom/events/target.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<string, EventListenerOrEventListenerObject[]> = {};
private readonly [LISTENERS]: Record<string, EventListenerOrEventListenerObject[]> = {};

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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
});
Expand Down

0 comments on commit c293b61

Please sign in to comment.