useExportSchemaToImage.ts91 lines · main
| 1 | import { toPng, toSvg } from 'html-to-image' |
| 2 | import { useMemo, useState } from 'react' |
| 3 | import { toast } from 'sonner' |
| 4 | |
| 5 | import { useStaticEffectEvent } from '@/hooks/useStaticEffectEvent' |
| 6 | |
| 7 | export 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 |
| 77 | export 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 | } |