index.tsx 2.5 KB

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