mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-05 03:28:59 +09:00
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.
78 lines
2.5 KiB
TypeScript
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);
|
|
},
|
|
};
|
|
};
|