wwf
2025-05-20 938c3e5a587ce950a94964ea509b9e7f8834dfae
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import { useEffect, useRef, useState } from 'react'
import { SVG } from '@svgdotjs/svg.js'
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
import DOMPurify from 'dompurify'
 
export const SVGRenderer = ({ content }: { content: string }) => {
  const svgRef = useRef<HTMLDivElement>(null)
  const [imagePreview, setImagePreview] = useState('')
  const [windowSize, setWindowSize] = useState({
    width: typeof window !== 'undefined' ? window.innerWidth : 0,
    height: typeof window !== 'undefined' ? window.innerHeight : 0,
  })
 
  const svgToDataURL = (svgElement: Element): string => {
    const svgString = new XMLSerializer().serializeToString(svgElement)
    const base64String = Buffer.from(svgString).toString('base64')
    return `data:image/svg+xml;base64,${base64String}`
  }
 
  useEffect(() => {
    const handleResize = () => {
      setWindowSize({ width: window.innerWidth, height: window.innerHeight })
    }
 
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])
 
  useEffect(() => {
    if (svgRef.current) {
      try {
        svgRef.current.innerHTML = ''
        const draw = SVG().addTo(svgRef.current)
 
        const parser = new DOMParser()
        const svgDoc = parser.parseFromString(content, 'image/svg+xml')
        const svgElement = svgDoc.documentElement
 
        if (!(svgElement instanceof SVGElement))
          throw new Error('Invalid SVG content')
 
        const originalWidth = Number.parseInt(svgElement.getAttribute('width') || '400', 10)
        const originalHeight = Number.parseInt(svgElement.getAttribute('height') || '600', 10)
        draw.viewbox(0, 0, originalWidth, originalHeight)
 
        svgRef.current.style.width = `${Math.min(originalWidth, 298)}px`
 
        const rootElement = draw.svg(DOMPurify.sanitize(content))
 
        rootElement.click(() => {
          setImagePreview(svgToDataURL(svgElement as Element))
        })
      }
      catch {
        if (svgRef.current)
          svgRef.current.innerHTML = '<span style="padding: 1rem;">Error rendering SVG. Wait for the image content to complete.</span>'
      }
    }
  }, [content, windowSize])
 
  return (
    <>
      <div ref={svgRef} style={{
        maxHeight: '80vh',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        cursor: 'pointer',
        wordBreak: 'break-word',
        whiteSpace: 'normal',
        margin: '0 auto',
      }} />
      {imagePreview && (<ImagePreview url={imagePreview} title='Preview' onCancel={() => setImagePreview('')} />)}
    </>
  )
}
 
export default SVGRenderer