import React from "react"; import { createRoot } from "react-dom/client"; type InteractiveProps = { id: string; label: string; caption?: string; description?: string; fallback?: string; attrs?: Record; }; type InteractiveModule = { default: React.ComponentType; }; const modules = import.meta.glob("./*/index.tsx"); function readProps(element: HTMLElement): InteractiveProps { const encoded = element.dataset.interactiveProps; if (!encoded) { return { id: element.dataset.interactiveId ?? "", label: "", }; } try { return JSON.parse(decodeURIComponent(encoded)) as InteractiveProps; } catch { return { id: element.dataset.interactiveId ?? "", label: "", fallback: element.textContent?.trim() ?? "", }; } } async function mountInteractive(element: HTMLElement) { const id = element.dataset.interactiveId; if (!id || element.dataset.interactiveMounted) return; const modulePath = `./${id}/index.tsx`; const loader = modules[modulePath]; if (!loader) { element.dataset.interactiveState = "missing-component"; return; } element.dataset.interactiveMounted = "true"; element.dataset.interactiveState = "loading"; try { const props = readProps(element); const { default: Component } = await loader(); element.replaceChildren(); createRoot(element).render(); element.dataset.interactiveState = "ready"; } catch (error) { element.dataset.interactiveState = "failed"; console.error(`Failed to mount interactive "${id}"`, error); } } function mountAllInteractives() { document .querySelectorAll(".interactive-mount[data-interactive-id]") .forEach((element) => { void mountInteractive(element); }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", mountAllInteractives, { once: true }); } else { mountAllInteractives(); }