2022-07-28 17:46:52 +02:00
|
|
|
type Listener<Value> = (data: Value)=> void;
|
|
|
|
type CallbackHandler<EventType> = (data: EventType)=> void;
|
|
|
|
|
|
|
|
// EventKeyType is used to distinguish events (e.g. a 'ClickEvent' vs a 'TouchEvent')
|
|
|
|
// while EventMessageType is the type of the data sent with an event (can be `void`)
|
|
|
|
export default class EventDispatcher<EventKeyType extends string|symbol|number, EventMessageType> {
|
|
|
|
// Partial marks all fields as optional. To initialize with an empty object, this is required.
|
|
|
|
// See https://stackoverflow.com/a/64526384
|
2023-06-30 10:16:08 +02:00
|
|
|
private listeners: Partial<Record<EventKeyType, Listener<EventMessageType>[]>>;
|
2022-07-28 17:46:52 +02:00
|
|
|
public constructor() {
|
|
|
|
this.listeners = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
public dispatch(eventName: EventKeyType, event: EventMessageType = null) {
|
|
|
|
if (!this.listeners[eventName]) return;
|
|
|
|
|
|
|
|
const ls = this.listeners[eventName];
|
|
|
|
for (let i = 0; i < ls.length; i++) {
|
|
|
|
ls[i](event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public on(eventName: EventKeyType, callback: CallbackHandler<EventMessageType>) {
|
|
|
|
if (!this.listeners[eventName]) this.listeners[eventName] = [];
|
|
|
|
this.listeners[eventName].push(callback);
|
|
|
|
|
|
|
|
return {
|
|
|
|
// Retuns false if the listener has already been removed, true otherwise.
|
|
|
|
remove: (): boolean => {
|
|
|
|
const originalListeners = this.listeners[eventName];
|
|
|
|
this.off(eventName, callback);
|
|
|
|
|
|
|
|
return originalListeners.length !== this.listeners[eventName].length;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Equivalent to calling .remove() on the object returned by .on
|
|
|
|
public off(eventName: EventKeyType, callback: CallbackHandler<EventMessageType>) {
|
|
|
|
if (!this.listeners[eventName]) return;
|
|
|
|
|
|
|
|
// Replace the current list of listeners with a new, shortened list.
|
|
|
|
// This allows any iterators over this.listeners to continue iterating
|
|
|
|
// without skipping elements.
|
|
|
|
this.listeners[eventName] = this.listeners[eventName].filter(
|
2023-08-22 12:58:53 +02:00
|
|
|
otherCallback => otherCallback !== callback,
|
2022-07-28 17:46:52 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|