useExportSchemaToImage.ts 2.8 KB

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