"use client" import dynamic from "next/dynamic" import { memo, useCallback, useState } from "react" import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types" import type { DocsGraphicDrawSavePayload } from "@/lib/drive/docs-graphic-draw-bridge" export function docsExcalidrawEditorKey(drawScene: string | null): string { if (!drawScene) return "new-draw" return `draw-${drawScene.length}-${drawScene.slice(0, 48)}` } const ExcalidrawEditor = dynamic( async () => { await import("@excalidraw/excalidraw/index.css") const { Excalidraw, restoreElements, restoreAppState } = await import( "@excalidraw/excalidraw" ) type ExcalidrawInitialDataState = import("@excalidraw/excalidraw/types").ExcalidrawInitialDataState type BinaryFiles = import("@excalidraw/excalidraw/types").BinaryFiles const { memo, useEffect, useMemo, useRef } = await import("react") function parseInitialData(drawScene: string | null): ExcalidrawInitialDataState | undefined { if (!drawScene) return undefined try { const data = JSON.parse(drawScene) as { elements?: Parameters[0] appState?: Parameters[0] files?: BinaryFiles } const initialData: ExcalidrawInitialDataState = { elements: restoreElements(data.elements ?? [], null), appState: restoreAppState(data.appState ?? {}, null), files: data.files, } return initialData } catch { return undefined } } const Inner = memo(function Inner({ drawScene, onReady, }: { drawScene: string | null onReady: (api: ExcalidrawImperativeAPI) => void }) { const apiRef = useRef(null) const initialData = useMemo(() => parseInitialData(drawScene), [drawScene]) useEffect(() => { const api = apiRef.current if (!api || !drawScene) return const data = parseInitialData(drawScene) if (!data) return api.updateScene({ elements: data.elements ?? [], appState: data.appState, files: data.files, }) }, [drawScene]) return (
{ apiRef.current = api onReady(api) }} initialData={initialData} langCode="fr-FR" UIOptions={{ canvasActions: { changeViewBackgroundColor: true, clearCanvas: true, export: false, loadScene: false, saveToActiveFile: false, toggleTheme: false, }, }} />
) }) return Inner }, { ssr: false, loading: () => (
Chargement de l'éditeur…
), } ) function readSvgDimensions(svg: SVGSVGElement): { width: number; height: number } { const viewBox = svg.getAttribute("viewBox") if (viewBox) { const parts = viewBox.split(/\s+/).map(Number) if (parts.length === 4 && parts.every((n) => Number.isFinite(n))) { return { width: Math.max(24, Math.round(parts[2])), height: Math.max(24, Math.round(parts[3])), } } } const width = Number(svg.getAttribute("width")) const height = Number(svg.getAttribute("height")) if (Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0) { return { width: Math.round(width), height: Math.round(height) } } return { width: 320, height: 240 } } function DocsExcalidrawEditorInner({ drawScene, onReady, }: { drawScene: string | null onReady: (api: ExcalidrawImperativeAPI) => void }) { return } export const DocsExcalidrawEditor = memo(DocsExcalidrawEditorInner) export function useDocsExcalidrawSave(api: ExcalidrawImperativeAPI | null) { const [saving, setSaving] = useState(false) const exportDrawing = useCallback(async (): Promise => { if (!api) return null setSaving(true) try { const elements = api.getSceneElements() const appState = api.getAppState() const files = api.getFiles() const { exportToSvg, serializeAsJSON } = await import("@excalidraw/excalidraw") const drawScene = serializeAsJSON(elements, appState, files, "local") const svg = await exportToSvg({ elements, appState, files, skipInliningFonts: true, }) const svgString = new XMLSerializer().serializeToString(svg) const src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}` const { width, height } = readSvgDimensions(svg) return { drawScene, src, width, height } } finally { setSaving(false) } }, [api]) return { exportDrawing, saving } }