refactor: use custom properties in shadow host

This commit is contained in:
one 2025-08-15 16:18:32 +08:00
parent e801b9d298
commit 4c81443930
6 changed files with 31 additions and 41 deletions

View File

@ -1,10 +1,9 @@
import { AsyncInitializer } from '@renderer/utils/asyncInitializer'
import React, { memo, useCallback } from 'react'
import styled from 'styled-components'
import { useDebouncedRender } from './hooks/useDebouncedRender'
import ImagePreviewLayout from './ImagePreviewLayout'
import { PreviewHostCssWhite } from './styles'
import { ShadowWhiteContainer } from './styles'
import { BasicPreviewHandles, BasicPreviewProps } from './types'
import { renderSvgInShadowHost } from './utils'
@ -28,7 +27,7 @@ const GraphvizPreview = ({
const renderGraphviz = useCallback(async (content: string, container: HTMLDivElement) => {
const viz = await vizInitializer.get()
const svg = viz.renderString(content, { format: 'svg' })
renderSvgInShadowHost(svg, container, { customCss: PreviewHostCssWhite })
renderSvgInShadowHost(svg, container)
}, [])
// 使用预览渲染器 hook
@ -44,16 +43,9 @@ const GraphvizPreview = ({
ref={ref}
imageRef={containerRef}
source="graphviz">
<StyledGraphviz ref={containerRef} className="graphviz special-preview" />
<ShadowWhiteContainer ref={containerRef} className="graphviz special-preview" />
</ImagePreviewLayout>
)
}
const StyledGraphviz = styled.div`
overflow: auto;
position: relative;
width: 100%;
height: 100%;
`
export default memo(GraphvizPreview)

View File

@ -1,10 +1,10 @@
import { nanoid } from '@reduxjs/toolkit'
import { useMermaid } from '@renderer/hooks/useMermaid'
import React, { memo, useCallback, useEffect, useRef, useState } from 'react'
import styled from 'styled-components'
import { useDebouncedRender } from './hooks/useDebouncedRender'
import ImagePreviewLayout from './ImagePreviewLayout'
import { ShadowTransparentContainer } from './styles'
import { BasicPreviewHandles, BasicPreviewProps } from './types'
import { renderSvgInShadowHost } from './utils'
@ -129,16 +129,9 @@ const MermaidPreview = ({
ref={ref}
imageRef={containerRef}
source="mermaid">
<StyledMermaid ref={containerRef} className="mermaid special-preview" />
<ShadowTransparentContainer ref={containerRef} className="mermaid special-preview" />
</ImagePreviewLayout>
)
}
const StyledMermaid = styled.div`
overflow: auto;
position: relative;
width: 100%;
height: 100%;
`
export default memo(MermaidPreview)

View File

@ -4,7 +4,7 @@ import React, { memo, useCallback, useEffect } from 'react'
import { useDebouncedRender } from './hooks/useDebouncedRender'
import ImagePreviewLayout from './ImagePreviewLayout'
import { PreviewHostCssWhite } from './styles'
import { ShadowWhiteContainer } from './styles'
import { BasicPreviewHandles, BasicPreviewProps } from './types'
import { renderSvgInShadowHost } from './utils'
@ -104,7 +104,7 @@ const PlantUmlPreview = ({
}
const text = await response.text()
renderSvgInShadowHost(text, container, { customCss: PreviewHostCssWhite })
renderSvgInShadowHost(text, container)
}, [])
// 使用预览渲染器 hook
@ -129,7 +129,7 @@ const PlantUmlPreview = ({
ref={ref}
imageRef={containerRef}
source="plantuml">
<div ref={containerRef} className="plantuml-preview special-preview" />
<ShadowWhiteContainer ref={containerRef} className="plantuml-preview special-preview" />
</ImagePreviewLayout>
)
}

View File

@ -2,7 +2,7 @@ import { memo, useCallback } from 'react'
import { useDebouncedRender } from './hooks/useDebouncedRender'
import ImagePreviewLayout from './ImagePreviewLayout'
import { PreviewHostCssWhite } from './styles'
import { ShadowWhiteContainer } from './styles'
import { BasicPreviewHandles } from './types'
import { renderSvgInShadowHost } from './utils'
@ -19,7 +19,7 @@ interface SvgPreviewProps {
const SvgPreview = ({ children, enableToolbar = false, className, ref }: SvgPreviewProps) => {
// 定义渲染函数
const renderSvg = useCallback(async (content: string, container: HTMLDivElement) => {
renderSvgInShadowHost(content, container, { customCss: PreviewHostCssWhite })
renderSvgInShadowHost(content, container)
}, [])
// 使用预览渲染器 hook
@ -35,7 +35,7 @@ const SvgPreview = ({ children, enableToolbar = false, className, ref }: SvgPrev
ref={ref}
imageRef={containerRef}
source="svg">
<div ref={containerRef} className={className ?? 'svg-preview special-preview'}></div>
<ShadowWhiteContainer ref={containerRef} className={className ?? 'svg-preview special-preview'} />
</ImagePreviewLayout>
)
}

View File

@ -34,10 +34,14 @@ export const PreviewContainer = styled(Flex).attrs({ role: 'alert' })`
}
`
export const PreviewHostCssWhite = `
:host {
background-color: white;
border: 0.5px solid var(--color-code-background);
border-radius: 8px;
}
export const ShadowWhiteContainer = styled.div`
--shadow-host-background-color: white;
--shadow-host-border: 0.5px solid var(--color-code-background);
--shadow-host-border-radius: 8px;
`
export const ShadowTransparentContainer = styled.div`
--shadow-host-background-color: transparent;
--shadow-host-border: unset;
--shadow-host-border-radius: unset;
`

View File

@ -1,7 +1,3 @@
type ShadowHostOptions = {
customCss?: string // override styles
}
/**
* Renders an SVG string inside a host element's Shadow DOM to ensure style encapsulation.
* This function handles creating the shadow root, injecting base styles for the host,
@ -11,7 +7,7 @@ type ShadowHostOptions = {
* @param hostElement The container element that will host the Shadow DOM.
* @throws An error if the SVG content is invalid or cannot be parsed.
*/
export function renderSvgInShadowHost(svgContent: string, hostElement: HTMLElement, options?: ShadowHostOptions): void {
export function renderSvgInShadowHost(svgContent: string, hostElement: HTMLElement): void {
if (!hostElement) {
throw new Error('Host element for SVG rendering is not available.')
}
@ -19,8 +15,16 @@ export function renderSvgInShadowHost(svgContent: string, hostElement: HTMLEleme
const shadowRoot = hostElement.shadowRoot || hostElement.attachShadow({ mode: 'open' })
// Base styles for the host element
const base = `
const style = document.createElement('style')
style.textContent = `
:host {
--shadow-host-background-color: white;
--shadow-host-border: 0.5px solid var(--color-code-background);
--shadow-host-border-radius: 8px;
background-color: var(--shadow-host-background-color);
border: var(--shadow-host-border);
border-radius: var(--shadow-host-border-radius);
padding: 1em;
overflow: auto;
display: block;
@ -34,9 +38,6 @@ export function renderSvgInShadowHost(svgContent: string, hostElement: HTMLEleme
}
`
const style = document.createElement('style')
style.textContent = base + (options?.customCss ? `\n${options.customCss}\n` : '')
// Clear previous content and append new style and SVG
shadowRoot.innerHTML = ''
shadowRoot.appendChild(style)