diff --git a/components/Demo.jsx b/components/Demo.jsx new file mode 100644 index 0000000..67820f8 --- /dev/null +++ b/components/Demo.jsx @@ -0,0 +1,55 @@ +import { useEffect, useRef, useState } from "react"; + +/** + * Embeds a self-contained interactive demo (a static HTML file under + * `public/demos/`) inside an iframe. The demo reports its own content height + * via `postMessage({ [heightKey]: number })`, so the iframe grows to fit + * without an inner scrollbar. Theme (light/dark) syncs automatically: the demo + * reads `localStorage.theme`, which Nextra also writes, and both share the same + * origin. + */ +export function Demo({ + src, + title, + caption, + heightKey = "rafDemoHeight", + minHeight = 400, +}) { + const iframeRef = useRef(null); + const [height, setHeight] = useState(minHeight); + + useEffect(() => { + function handleMessage(event) { + if (event.source !== iframeRef.current?.contentWindow) return; + const value = event.data?.[heightKey]; + if (typeof value === "number") { + setHeight(value + 24); + } + } + window.addEventListener("message", handleMessage); + return () => window.removeEventListener("message", handleMessage); + }, [heightKey]); + + return ( +
+