useExportSchemaToImage.ts91 lines · main
1import { toPng, toSvg } from 'html-to-image'
2import { useMemo, useState } from 'react'
3import { toast } from 'sonner'
4
5import { useStaticEffectEvent } from '@/hooks/useStaticEffectEvent'
6
7export const useExportSchemaToImage = () => {
8 const [isDownloading, setIsDownloading] = useState(false)
9 // By doing this once and passing the result to html-to-image options, we avoid html-to-image calculating it for every node.
10 // This improves performance a lot. See https://github.com/bubkoo/html-to-image/issues/542#issuecomment-3249408793
11 const allPropertyNames = useMemo(() => getAllPropertyNames(), [])
12
13 const exportSchemaToImage = useStaticEffectEvent(
14 async ({
15 element,
16 projectRef,
17 x,
18 y,
19 zoom,
20 format,
21 }: {
22 element: HTMLElement
23 projectRef: string
24 x: number
25 y: number
26 zoom: number
27 format: 'svg' | 'png'
28 }) => {
29 setIsDownloading(true)
30 const width = element.clientWidth
31 const height = element.clientHeight
32
33 const options = {
34 includeStyleProperties: allPropertyNames,
35 backgroundColor: 'white',
36 width,
37 height,
38 style: {
39 width: width.toString(),
40 height: height.toString(),
41 transform: `translate(${x}px, ${y}px) scale(${zoom})`,
42 },
43 skipFonts: true,
44 }
45 try {
46 if (format === 'svg') {
47 const data = await toSvg(element, options)
48 const a = document.createElement('a')
49 a.setAttribute('download', `briven-schema-${projectRef}.svg`)
50 a.setAttribute('href', data)
51 a.click()
52 toast.success('Successfully downloaded as SVG')
53 } else if (format === 'png') {
54 const data = await toPng(element, options)
55 const a = document.createElement('a')
56 a.setAttribute('download', `briven-schema-${projectRef}.png`)
57 a.setAttribute('href', data)
58 a.click()
59 toast.success('Successfully downloaded as PNG')
60 }
61 } catch (error) {
62 console.error('Failed to download:', error)
63 toast.error(`Failed to download current view: ${(error as Error).message}`)
64 } finally {
65 setIsDownloading(false)
66 }
67 }
68 )
69
70 return useMemo(
71 () => ({ isDownloading, exportSchemaToImage }),
72 [isDownloading, exportSchemaToImage]
73 )
74}
75
76// Get all property names accessible through getComputedStyle(), excluding custom properties
77export const getAllPropertyNames = () => {
78 if (typeof document === 'undefined' || typeof getComputedStyle === 'undefined') {
79 return []
80 }
81
82 const names = []
83 const style = getComputedStyle(document.documentElement)
84 for (let i = 0; i < style.length; i++) {
85 const name = style[i]
86 if (!name.startsWith('--')) {
87 names.push(name)
88 }
89 }
90 return names
91}