import { Signal } from './signal.js';
/**
* Basic type safe event emitter
* @example
* ```ts
* const ms = new EventEmitter<{ onChange: { id: 'onChange' }; onDelete: { id: 'onDelete' } }>();
* ms.subscribe('onChange', (event) => {
* event.id; // 'onChange'
* });
* ms.subscribe('onDelete', (event) => {
* event.id; // 'onDelete'
* });
*
* ms.notify('onChange', { id: 'onChange' }); // event is type safe
* ```
* @example
payload type mismatch
* ```ts
* ms.notify('onChange', { id: 'onDelete' }); // ERROR!!!
* ```
* @example payload type mismatch
* ```ts
* ms.notify('onSomethingElse', { id: 'onDelete' }); // ERROR!!!
* ```
*/
export class EventEmitter {
protected events = new Map>();
/**
* Check if an event has subscribers
* @returns true if event has subscribers
*/
hasSubscribers = (event: EventId) => this.events.has(event);
/**
* Subscribe a handler for event
*
* @returns unsubscribe fn
*/
subscribe = (event: Event, handler: (data: Events[Event]) => void) => {
const bucket = this.events.get(event);
bucket ? bucket.subscribe(handler) : this.events.set(event, new Signal([handler]));
return () => this.unsubscribe(event, handler);
};
/**
* {@inheritDoc EventEmitter.subscribe}
*/
on = this.subscribe;
/**
* Adds a handler that will be called at most once.
*
* @returns unsubscribe fn
*/
once = (event: Event, handler: (data: Events[Event]) => void) => {
let bucket = this.events.get(event);
if (!bucket) {
bucket = new Signal();
this.events.set(event, bucket);
}
bucket.once(handler);
return () => this.unsubscribe(event, handler);
};
/**
* Unsubscribe a handler from event
*/
unsubscribe = (event: Event, handler: (data: Events[Event]) => void) => {
const bucket = this.events.get(event);
bucket?.unsubscribe(handler);
if (bucket?.size === 0) {
this.events.delete(event);
}
};
/**
* {@inheritDoc EventEmitter.unsubscribe}
*/
off = this.unsubscribe;
/**
* Drop all subscriptions of a signal
* @param event - signal id
*/
delete = (event: Event) => {
this.events.delete(event);
};
/**
* Drop all subscriptions
*/
clear = () => {
this.events.forEach((bucket) => bucket.clear());
this.events = new Map>();
};
/**
* {@inheritDoc Signal.notify}
* @param event - eve
* @param data - event data
*/
notify = (event: Event, data: Events[Event]) => {
this.events.get(event)?.notify(data);
};
/**
* {@inheritDoc EventEmitter.notify}
*/
emit = this.notify;
}
export type IEventEmitter> = Pick, keyof EventEmitter>;