@novo-learning/novo-sdk
Version:
SDK for the Novolanguage Speech Analysis API
88 lines (77 loc) • 2.48 kB
text/typescript
export interface Event<T> {
name: string;
props: T;
}
export type EventCallbackFn<T> = (data: T) => void;
export type ChanneledEventBus<T = EventBus> = EventBus & { parent: T };
/**
* The EventBus allows for simple event dispatching and listening
* between different instances in the library.
*
* Each EventBus can create a new 'channel()' which allows it to
* broadcast and subscribe its own events.
*
* By default events will 'bubble up' when they're broadcasted, meaning
* all parent EventBus instances will receive the event.
*/
export class EventBus {
/**
* Internal list of callback listeners
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private readonly listeners: { [key: string]: EventCallbackFn<any>[] } = {};
constructor(readonly parent?: EventBus, readonly identifier?: string) {}
/**
* Creates a new EventBus 'channel' with the current EventBus
* set as its parent
*/
channel(identifier?: string): ChanneledEventBus<this> {
return new EventBus(this, identifier) as ChanneledEventBus<this>;
}
/**
* Shortcut to root EventBus
*/
root(): EventBus {
let eb = this.parent;
if (eb === undefined) {
return this;
}
while (eb.parent) {
eb = eb.parent;
}
return eb;
}
/**
* Add a new listener for the given event
*/
addEventListener<T = never>(event: string, callbackFn: EventCallbackFn<T>): void {
this.listeners[event] = (this.listeners[event] || []).concat(callbackFn);
}
/**
* Remove the given callback
*/
removeEventListener<T = never>(event: string, callbackFn?: EventCallbackFn<T>): void {
if (!callbackFn) {
this.listeners[event] = [];
} else {
const index = (this.listeners[event] || []).findIndex((fn) => fn === callbackFn);
if (index > -1) {
this.listeners[event].splice(index, 1);
}
}
}
/**
* Dispatch a new event with the given name and set of data and invoke
* all listener callbacks.
*
* Enable 'broadcast' to dispatch an event which bubbles up to all parent eventbusses.
* Note that this is enabled by default.
*/
dispatch<T = never>(event: string, data?: T, broadcast?: boolean): void {
(this.listeners[event] || []).forEach((callbackFn) => callbackFn(data));
if (broadcast !== false && this.parent) {
// Call parent with same arguments
this.parent.dispatch.apply(this.parent, [event, data, broadcast]);
}
}
}