1
0
silverbullet/web/components/panel.tsx
2022-12-22 16:20:05 +01:00

111 lines
2.4 KiB
TypeScript

import { useEffect, useRef } from "../deps.ts";
import { Editor } from "../editor.tsx";
import { PanelConfig } from "../types.ts";
export const panelHtml = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<base target="_top">
<script>
window.addEventListener("message", (message) => {
const data = message.data;
switch (data.type) {
case "html":
document.body.innerHTML = data.html;
if (data.script) {
try {
eval(data.script);
} catch (e) {
console.error("Error evaling script", e);
}
}
break;
}
});
function sendEvent(name, ...args) {
window.parent.postMessage({ type: "event", name, args, }, "*");
}
function api(obj) {
window.parent.postMessage(obj, "*");
}
function updateHeight() {
api({
type: "setHeight",
height: document.documentElement.offsetHeight,
});
}
function loadJsByUrl(url) {
const script = document.createElement("script");
script.src = url;
return new Promise((resolve) => {
script.onload = resolve;
document.documentElement.firstChild.appendChild(script);
});
}
</script>
</head>
<body>
Send me HTML
</body>
</html>`;
export function Panel({
config,
editor,
}: {
config: PanelConfig;
editor: Editor;
}) {
const iFrameRef = useRef<HTMLIFrameElement>(null);
useEffect(() => {
function loadContent() {
if (iFrameRef.current?.contentWindow) {
iFrameRef.current.contentWindow.postMessage({
type: "html",
html: config.html,
script: config.script,
});
}
}
if (!iFrameRef.current) {
return;
}
const iframe = iFrameRef.current;
iframe.onload = loadContent;
loadContent();
return () => {
iframe.onload = null;
};
}, [config.html, config.script]);
useEffect(() => {
const messageListener = (evt: any) => {
if (evt.source !== iFrameRef.current!.contentWindow) {
return;
}
const data = evt.data;
if (!data) {
return;
}
if (data.type === "event") {
editor.dispatchAppEvent(data.name, ...data.args);
}
};
globalThis.addEventListener("message", messageListener);
return () => {
globalThis.removeEventListener("message", messageListener);
};
}, []);
return (
<div className="sb-panel" style={{ flex: config.mode }}>
<iframe srcDoc={panelHtml} ref={iFrameRef} />
</div>
);
}