| | |
| | | /** |
| | | * Mitt: Tiny functional event emitter / pubsub |
| | | * |
| | | * @name mitt |
| | | * @param {Array} [all] Optional array of event names to registered handler functions |
| | | * @returns {Function} The function's instance |
| | | * copy to https://github.com/developit/mitt |
| | | * Expand clear method |
| | | */ |
| | | export default class Mitt { |
| | | private cache: Map<string, Array<(data: any) => void>>; |
| | | constructor(all = []) { |
| | | // A Map of event names to registered handler functions. |
| | | this.cache = new Map(all); |
| | | } |
| | | export type EventType = string | symbol; |
| | | |
| | | once(type: string, handler: Fn) { |
| | | const decor = (...args: any[]) => { |
| | | handler && handler.apply(this, args); |
| | | this.off(type, decor); |
| | | }; |
| | | this.on(type, decor); |
| | | return this; |
| | | } |
| | | // An event handler can take an optional event argument |
| | | // and should not return a value |
| | | export type Handler<T = unknown> = (event: T) => void; |
| | | export type WildcardHandler<T = Record<string, unknown>> = ( |
| | | type: keyof T, |
| | | event: T[keyof T], |
| | | ) => void; |
| | | |
| | | /** |
| | | * Register an event handler for the given type. |
| | | * |
| | | * @param {string|symbol} type Type of event to listen for, or `"*"` for all events |
| | | * @param {Function} handler Function to call in response to given event |
| | | */ |
| | | on(type: string, handler: Fn) { |
| | | const handlers = this.cache.get(type); |
| | | const added = handlers && handlers.push(handler); |
| | | if (!added) { |
| | | this.cache.set(type, [handler]); |
| | | } |
| | | } |
| | | // An array of all currently registered event handlers for a type |
| | | export type EventHandlerList<T = unknown> = Array<Handler<T>>; |
| | | export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>; |
| | | |
| | | /** |
| | | * Remove an event handler for the given type. |
| | | * |
| | | * @param {string|symbol} type Type of event to unregister `handler` from, or `"*"` |
| | | * @param {Function} handler Handler function to remove |
| | | */ |
| | | off(type: string, handler: Fn) { |
| | | const handlers = this.cache.get(type); |
| | | if (handlers) { |
| | | handlers.splice(handlers.indexOf(handler) >>> 0, 1); |
| | | } |
| | | } |
| | | // A map of event types and their corresponding event handlers. |
| | | export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map< |
| | | keyof Events | '*', |
| | | EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events> |
| | | >; |
| | | |
| | | /** |
| | | * Invoke all handlers for the given type. |
| | | * If present, `"*"` handlers are invoked after type-matched handlers. |
| | | * |
| | | * Note: Manually firing "*" handlers is not supported. |
| | | * |
| | | * @param {string|symbol} type The event type to invoke |
| | | * @param {*} [evt] Any value (object is recommended and powerful), passed to each handler |
| | | */ |
| | | emit(type: string, evt: any) { |
| | | for (const handler of (this.cache.get(type) || []).slice()) handler(evt); |
| | | for (const handler of (this.cache.get('*') || []).slice()) handler(type, evt); |
| | | } |
| | | export interface Emitter<Events extends Record<EventType, unknown>> { |
| | | all: EventHandlerMap<Events>; |
| | | |
| | | /** |
| | | * Remove all event handlers. |
| | | * |
| | | * Note: This will also remove event handlers passed via `mitt(all: EventHandlerMap)`. |
| | | */ |
| | | clear() { |
| | | this.cache.clear(); |
| | | } |
| | | on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void; |
| | | on(type: '*', handler: WildcardHandler<Events>): void; |
| | | |
| | | off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void; |
| | | off(type: '*', handler: WildcardHandler<Events>): void; |
| | | |
| | | emit<Key extends keyof Events>(type: Key, event: Events[Key]): void; |
| | | emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void; |
| | | clear(): void; |
| | | } |
| | | |
| | | /** |
| | | * Mitt: Tiny (~200b) functional event emitter / pubsub. |
| | | * @name mitt |
| | | * @returns {Mitt} |
| | | */ |
| | | export function mitt<Events extends Record<EventType, unknown>>( |
| | | all?: EventHandlerMap<Events>, |
| | | ): Emitter<Events> { |
| | | type GenericEventHandler = Handler<Events[keyof Events]> | WildcardHandler<Events>; |
| | | all = all || new Map(); |
| | | |
| | | return { |
| | | /** |
| | | * A Map of event names to registered handler functions. |
| | | */ |
| | | all, |
| | | |
| | | /** |
| | | * Register an event handler for the given type. |
| | | * @param {string|symbol} type Type of event to listen for, or `'*'` for all events |
| | | * @param {Function} handler Function to call in response to given event |
| | | * @memberOf mitt |
| | | */ |
| | | on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) { |
| | | const handlers: Array<GenericEventHandler> | undefined = all!.get(type); |
| | | if (handlers) { |
| | | handlers.push(handler); |
| | | } else { |
| | | all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>); |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * Remove an event handler for the given type. |
| | | * If `handler` is omitted, all handlers of the given type are removed. |
| | | * @param {string|symbol} type Type of event to unregister `handler` from (`'*'` to remove a wildcard handler) |
| | | * @param {Function} [handler] Handler function to remove |
| | | * @memberOf mitt |
| | | */ |
| | | off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) { |
| | | const handlers: Array<GenericEventHandler> | undefined = all!.get(type); |
| | | if (handlers) { |
| | | if (handler) { |
| | | handlers.splice(handlers.indexOf(handler) >>> 0, 1); |
| | | } else { |
| | | all!.set(type, []); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * Invoke all handlers for the given type. |
| | | * If present, `'*'` handlers are invoked after type-matched handlers. |
| | | * |
| | | * Note: Manually firing '*' handlers is not supported. |
| | | * |
| | | * @param {string|symbol} type The event type to invoke |
| | | * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler |
| | | * @memberOf mitt |
| | | */ |
| | | emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) { |
| | | let handlers = all!.get(type); |
| | | if (handlers) { |
| | | (handlers as EventHandlerList<Events[keyof Events]>).slice().forEach((handler) => { |
| | | handler(evt as Events[Key]); |
| | | }); |
| | | } |
| | | |
| | | handlers = all!.get('*'); |
| | | if (handlers) { |
| | | (handlers as WildCardEventHandlerList<Events>).slice().forEach((handler) => { |
| | | handler(type, evt as Events[Key]); |
| | | }); |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * Clear all |
| | | */ |
| | | clear() { |
| | | this.all.clear(); |
| | | }, |
| | | }; |
| | | } |