OpenMAIC-React / src /components /scene-renderers /interactive-renderer.tsx
muthuk1's picture
Convert OpenMAIC from Next.js to React (Vite)
f56a29b verified
import { useMemo, useRef, useEffect, useCallback } from 'react';
import type { InteractiveContent } from '@/lib/types/stage';
import { useWidgetIframeStore } from '@/lib/store/widget-iframe';
import { patchHtmlForIframe } from '@/lib/utils/iframe';
interface InteractiveRendererProps {
readonly content: InteractiveContent;
readonly sceneId: string;
}
export function InteractiveRenderer({ content, sceneId }: InteractiveRendererProps) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const registerIframe = useWidgetIframeStore((state) => state.registerIframe);
const setActiveScene = useWidgetIframeStore((state) => state.setActiveScene);
const patchedHtml = useMemo(
() => (content.html ? patchHtmlForIframe(content.html) : undefined),
[content.html],
);
// Create iframe messaging callback
const sendMessageToIframe = useCallback((type: string, payload: Record<string, unknown>) => {
if (iframeRef.current?.contentWindow) {
iframeRef.current.contentWindow.postMessage({ type, ...payload }, '*');
}
}, []);
// Register iframe messaging callback on mount, unregister on unmount
// Key by sceneId to prevent race conditions on scene switch
useEffect(() => {
registerIframe(sceneId, sendMessageToIframe);
setActiveScene(sceneId);
return () => {
registerIframe(sceneId, null);
};
}, [sceneId, registerIframe, sendMessageToIframe, setActiveScene]);
return (
<div className="w-full h-full relative">
<iframe
ref={iframeRef}
srcDoc={patchedHtml}
src={patchedHtml ? undefined : content.url}
className="absolute inset-0 w-full h-full border-0"
title={`Interactive Scene ${sceneId}`}
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
/>
</div>
);
}