refactor(MermaidPreview): separate measurement from rendering

This commit is contained in:
one 2025-08-15 15:50:36 +08:00
parent 851b98bf5c
commit c3e61690d7
2 changed files with 35 additions and 13 deletions

View File

@ -14,8 +14,10 @@ const vizInitializer = new AsyncInitializer(async () => {
return await module.instance()
})
/** Graphviz
* 使 usePreviewRenderer hook
/**
* Graphviz
* - 使 useDebouncedRender
* - 使 shadow dom SVG
*/
const GraphvizPreview = ({
children,

View File

@ -8,9 +8,10 @@ import ImagePreviewLayout from './ImagePreviewLayout'
import { BasicPreviewHandles, BasicPreviewProps } from './types'
import { renderSvgInShadowHost } from './utils'
/** Mermaid
* 使 usePreviewRenderer hook
* FIXME: 等将来 mermaid-js
/**
* Mermaid
* - 使 useDebouncedRender
* - 使 shadow dom SVG
*/
const MermaidPreview = ({
children,
@ -21,20 +22,39 @@ const MermaidPreview = ({
const diagramId = useRef<string>(`mermaid-${nanoid(6)}`).current
const [isVisible, setIsVisible] = useState(true)
// 定义渲染函数
/**
* shadow dom
* 退 innerHTML
*/
const renderMermaid = useCallback(
async (content: string, container: HTMLDivElement) => {
// 验证语法,提前抛出异常
await mermaid.parse(content)
const { svg } = await mermaid.render(diagramId, content, container)
// 获取容器宽度
const { width } = container.getBoundingClientRect()
if (width === 0) return
// 避免不可见时产生 undefined 和 NaN
const fixedSvg = svg.replace(/translate\(undefined,\s*NaN\)/g, 'translate(0, 0)')
// 创建临时的 div 用于 mermaid 测量
const measureEl = document.createElement('div')
measureEl.style.position = 'absolute'
measureEl.style.left = '-9999px'
measureEl.style.top = '-9999px'
measureEl.style.width = `${width}px`
document.body.appendChild(measureEl)
// 使用 shadow dom如果有问题可以回退到 innerHTML
renderSvgInShadowHost(fixedSvg, container)
// container.innerHTML = fixedSvg
try {
const { svg } = await mermaid.render(diagramId, content, measureEl)
// 避免不可见时产生 undefined 和 NaN
const fixedSvg = svg.replace(/translate\(undefined,\s*NaN\)/g, 'translate(0, 0)')
// 有问题可以回退到 innerHTML
renderSvgInShadowHost(fixedSvg, container)
// container.innerHTML = fixedSvg
} finally {
document.body.removeChild(measureEl)
}
},
[diagramId, mermaid]
)
@ -67,7 +87,7 @@ const MermaidPreview = ({
const element = containerRef.current
if (!element) return
const currentlyVisible = element.offsetParent !== null
const currentlyVisible = element.offsetParent !== null && element.offsetWidth > 0 && element.offsetHeight > 0
setIsVisible(currentlyVisible)
}