index.tsx 2.6 KB

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