Files
webstudio/apps/builder/app/shared/pubsub/create.ts
T
Bogdan Chadkin 743d2f1842 refactor: add commands emitter (#2389)
Added new commands emitter utility to manage hotkeys across plugins and
separate a lot of logic from canvas.

Here migrated only togglePreview as an example.

Later we can build commands panel and hotkeys overrides on top of this
utility.
2023-09-29 20:47:25 +04:00

78 lines
2.5 KiB
TypeScript

import { createNanoEvents } from "nanoevents";
import { useCallback, useEffect, useRef } from "react";
import { batchUpdate } from "./raf-queue";
export const createPubsub = <PublishMap>() => {
type Action<Type extends keyof PublishMap> =
PublishMap[Type] extends undefined
? { type: Type; payload?: undefined }
: { type: Type; payload: PublishMap[Type] };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const emitter = createNanoEvents<Record<any, any>>();
if (typeof window === "object") {
window.addEventListener(
"message",
(event: MessageEvent) => {
// @todo this has no type safety built in, could be anything from any source.
// we could potentially maintain a list of valid event types at runtime
// at the very least we could add a brand property or something to our events
if (typeof event.data?.type === "string") {
// Execute all updates within a single batch to improve performance
batchUpdate(() => emitter.emit(event.data.type, event.data.payload));
}
},
false
);
}
return {
/**
* To publish a postMessage event on the current window and parent window from the iframe.
*/
publish<Type extends keyof PublishMap>(action: Action<Type>) {
window.parent.postMessage(action, "*");
window.postMessage(action, "*");
},
/**
* To publish a postMessage event on the iframe and parent window from the parent window.
*/
usePublish() {
const iframeRef = useRef<HTMLIFrameElement | null>(null);
const publishCallback = useCallback(
<Type extends keyof PublishMap>(action: Action<Type>) => {
const element = iframeRef.current;
if (element?.contentWindow == null) {
return;
}
element.contentWindow.postMessage(action, "*");
window.postMessage(action, "*");
},
[iframeRef]
);
return [publishCallback, iframeRef] as const;
},
/**
* To subscribe a message event on the current window.
*/
useSubscribe<Type extends keyof PublishMap>(
type: Type,
onAction: (payload: PublishMap[Type]) => void
) {
useEffect(() => {
return emitter.on(type, onAction);
}, [type, onAction]);
},
subscribe<Type extends keyof PublishMap>(
type: Type,
onAction: (payload: PublishMap[Type]) => void
) {
return emitter.on(type, onAction);
},
};
};