import React from 'react'; import { DrawIoEmbed } from 'react-drawio'; import { cn } from '@/lib/utils'; export interface DiagramEditorHandle { getXml: () => string; } export interface DiagramEditorProps { xml: string; readOnly?: boolean; className?: string; onChange?: (xml: string) => void; } const BLANK_XML = ''; function detectDark(): boolean { if (typeof document === 'undefined') return false; const theme = document.documentElement.getAttribute('data-theme'); return theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches); } export const DiagramEditor = React.forwardRef( function DiagramEditor({ xml, readOnly, className, onChange }, ref) { const latestXmlRef = React.useRef(xml); const drawioRef = React.useRef>(null); const hasShownTemplate = React.useRef(false); const [isDark, setIsDark] = React.useState(detectDark); const stableXmlRef = React.useRef(xml); // When the parent switches files (xml prop changes), reset the stable // reference so the new content renders instead of the first file's content. const prevXmlRef = React.useRef(xml); if (prevXmlRef.current !== xml) { prevXmlRef.current = xml; stableXmlRef.current = xml; latestXmlRef.current = xml; } const prevIsDark = React.useRef(isDark); if (prevIsDark.current !== isDark) { prevIsDark.current = isDark; stableXmlRef.current = latestXmlRef.current; } const containerRef = React.useRef(null); React.useEffect(() => { const check = () => setIsDark(detectDark()); check(); const observer = new MutationObserver(check); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); return () => observer.disconnect(); }, []); // Focus the iframe once on mount so keyboard shortcuts work without // clicking the canvas. Intentionally not keyed on isDark — re-focusing // on every theme toggle would steal keyboard focus from the user. React.useEffect(() => { const id = setTimeout(() => { containerRef.current?.querySelector('.diagrams-iframe')?.focus(); }, 600); return () => clearTimeout(id); }, []); React.useImperativeHandle(ref, () => ({ getXml: () => latestXmlRef.current, })); const handleLoad = React.useCallback(() => { if (!xml && !hasShownTemplate.current) { hasShownTemplate.current = true; setTimeout(() => { drawioRef.current?.template({}); }, 500); } }, [xml]); const handleAutoSave = React.useCallback((data: { xml: string }) => { latestXmlRef.current = data.xml; onChange?.(data.xml); }, [onChange]); return (
); }, );