62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
|
import mermaid, { MermaidConfig } from 'mermaid';
|
|
import { SxProps } from '@mui/material/styles';
|
|
import { Box } from '@mui/material';
|
|
import { useResizeObserverAndMutationObserver } from '../hooks/useAutoScrollToBottom';
|
|
|
|
const defaultMermaidConfig : MermaidConfig = {
|
|
startOnLoad: true,
|
|
securityLevel: 'loose',
|
|
fontFamily: 'Fira Code',
|
|
};
|
|
|
|
interface MermaidProps {
|
|
chart: string;
|
|
sx?: SxProps;
|
|
className?: string;
|
|
mermaidConfig?: MermaidConfig;
|
|
}
|
|
|
|
const Mermaid: React.FC<MermaidProps> = (props: MermaidProps) => {
|
|
const { chart, sx, className, mermaidConfig } = props;
|
|
const [ visible, setVisible] = useState<boolean>(false);
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
const checkVisible = useCallback(() => {
|
|
if (containerRef.current) {
|
|
const { width, height } = containerRef.current.getBoundingClientRect();
|
|
if (width > 0 && height > 0) {
|
|
setVisible(true);
|
|
}
|
|
}
|
|
}, [containerRef, setVisible]);
|
|
|
|
useEffect(() => {
|
|
const renderMermaid = async () => {
|
|
if (containerRef.current && visible && chart) {
|
|
try {
|
|
await mermaid.initialize(mermaidConfig || defaultMermaidConfig);
|
|
await mermaid.run({ nodes: [containerRef.current] });
|
|
} catch (e) {
|
|
console.error("Mermaid render error:", e, containerRef.current);
|
|
}
|
|
}
|
|
}
|
|
renderMermaid();
|
|
}, [containerRef, mermaidConfig, visible, chart]);
|
|
|
|
// Observe container and TextField size, plus DOM changes
|
|
useResizeObserverAndMutationObserver(containerRef, null, checkVisible);
|
|
|
|
return <Box className={className || "Mermaid"} ref={containerRef} sx={{
|
|
display: "flex",
|
|
flexGrow: 1,
|
|
...sx
|
|
}}>
|
|
{chart}
|
|
</Box>;
|
|
};
|
|
|
|
export {
|
|
Mermaid
|
|
}; |