import { RawEvents } from "../lib/RawEvents" export { RawEvents } export interface IEventMap { [typePath: string]: any } type listenCloser = () => void /** * 事件中心 * - 支持类型化事件定义\ * - 支持事件类型路径\ * * @example * * ```ts let eventHub = new EventHub<{ network: { e: number; xx: string } click: { s: number } }>() eventHub.emit("update", { itemId: "", index: 0 }) // ok eventHub.emit("update", { itemId: "", index: "1" }) // error index type error eventHub.on("update", (data) => { data.itemId // string type hint data.index // number type hint }) ``` */ export class EventHub { #rawEvents = new RawEvents() #hasAnyListener = false /** 监听事件 */ on( typePath: EventKey, listener: (data: TEventMap[EventKey], typePath?: string) => void, options?: { /** 不返会取消监听回调函数 */ noCloser?: boolean } ): listenCloser { this.#rawEvents.on(typePath, listener) // 有监听全部事件的监听器 if (typePath === "*") this.#hasAnyListener = true if (!options?.noCloser) { return () => this.#rawEvents.off(typePath, listener) } return undefined } /** 触发事件 * 会等待所有监听器执行完毕后返回 */ emit( typePath: EventKey, data?: TEventMap[EventKey], options?: { /** 不按路径解析事件类型 */ noTypePath?: boolean } ): void { const type = typePath if (!options?.noTypePath && type.indexOf("/") > -1) { const paths = this.#resolveEmitPaths(type) for (let i = 0; i < paths.length; i++) { this.#rawEvents.emit(paths[i], data, typePath) } } else { this.#rawEvents.emit(type, data) } this.#emitAny(type, data) } /** 触发事件 * 会等待所有监听器执行完毕后返回 */ async emitAsync( typePath: EventKey, data?: TEventMap[EventKey], options?: { /** 不按路径解析事件类型 */ noTypePath?: boolean } ): Promise { const type = typePath if (!options?.noTypePath && type.indexOf("/") > -1) { const paths = this.#resolveEmitPaths(type) for (let i = 0; i < paths.length; i++) { await this.#rawEvents.emitAsync(paths[i], data, typePath) } } else { await this.#rawEvents.emitAsync(type, data) } await this.#emitAnyAsync(type, data) } /** 取消监听 */ off( typePath: string, listener?: (eventData: TEventMap[EventKey]) => void ) { this.#rawEvents.off(typePath, listener) } /** * 清空全部监听器 */ offAll() { this.#rawEvents.clear() } //---------------------- /** 触发 * 监听 */ #emitAny(typePath: string, data: any) { if (this.#hasAnyListener && typePath !== "*") { this.#rawEvents.emit("*", data, typePath) } } /** 触发 * 监听 (Async) */ async #emitAnyAsync(typePath: string, data: any) { if (this.#hasAnyListener && typePath !== "*") { await this.#rawEvents.emitAsync("*", data, typePath) } } #cache_emitPaths: { [type: string]: string[] } = {} #resolveEmitPaths(type: string) { let paths = this.#cache_emitPaths[type] if (paths) return paths const parts = type.split("/") paths = [] let currentPath = "" for (let i = 0; i < parts.length; i++) { currentPath = currentPath + (i > 0 ? "/" : "") + parts[i] paths.push(currentPath) } this.#cache_emitPaths[type] = paths return paths } } type IEventMapKeys = { [P in keyof TEventMap]: P } /** * 把事件类型定义转换为基于事件名的枚举对象\ 建议使用 defineEvents 代替 * @example * ```ts let EventsDefine = { update: <{ itemId: string; index: number }>{}, } let eventHub = new EventHub() const Events = eventDefineToEnum(EventsDefine) eventHub.on(Events.update, () => {}) * */ export function eventDefineToEnum(eventDefine: TEventMap): IEventMapKeys { return Object.fromEntries(Object.entries(eventDefine).map(([key, val]) => [key, key])) } /** * 定义事件类型的辅助函数\ * 给定一个对象来定义事件类型,将返回 2 个值:「`Events` 事件枚举对象」和「`EventDefine` 事件类型定义对象」\ * - `Events` 用于获取事件名,如 `Events.update`\ * - `EventDefine` 用于作为类型定义传递给 EventHub,如 `new EventHub`\ * @example * ```ts const [ MyEvents, MyEventDefine ] = defineEvents({ update: <{ itemId: string; index: number }>{}, delete: <{ itemId: string }>{}, }) let eventHub = new EventHub() eventHub.on(MyEvents.update, (data) => {}) * * ``` * * @param eventDefine */ export function defineEvents(eventDefine:T) :[IEventMapKeys, T] { return [eventDefineToEnum(eventDefine), eventDefine] }