UNPKG

state-event-target

Version:
79 lines (69 loc) 1.96 kB
import { WindowBroadcast } from "./Broadcast"; type SinkEventTarget = EventTarget & { set: (...args: any[]) => void; clear: () => void; onMissing: (cb: EventListener) => void; }; type SinkEventHandlers<T extends SinkEventTarget> = { [eventType: string]: (target: T, detail: any) => void; }; export function sink<T extends SinkEventTarget>( fromBroadcastSource: WindowBroadcast<any>, toSinkTarget: T, namespace: string, eventHandlers: SinkEventHandlers<T> = { "state:update": (target, detail) => { if ("param" in detail || "key" in detail) { target.set(detail.param ?? detail.key, detail.value); } else { target.set(detail.value); } }, "state:reset": (target) => target.clear(), }, ): T { function handleMessage(event: MessageEvent<any>) { const { type, namespace: msgNamespace, detail } = event.data; if (msgNamespace !== namespace) { return; } if (eventHandlers[type]) { eventHandlers[type](toSinkTarget, detail); } else { toSinkTarget.dispatchEvent(new CustomEvent(type, { detail })); } } fromBroadcastSource.onMessage(handleMessage); toSinkTarget.onMissing((e: Event) => { const detail = (e as CustomEvent).detail; if (detail) { fromBroadcastSource.postMessage({ type: "state:request", namespace, detail: "param" in detail ? { param: detail.param } : "key" in detail ? { key: detail.key } : {}, }); } }); return new Proxy(toSinkTarget, { get(obj, prop) { const value = obj[prop as keyof T]; if (prop === "destroy") { return () => { fromBroadcastSource.detach(); if (typeof value === "function") { value.call(obj); } }; } if (typeof value === "function") { return value.bind(obj); } return value; }, }) as T; }