import type { ExportPreferences, ExportSettings } from '@zakodium/nmrium-core'; import type { ReactNode, RefObject } from 'react'; import { createContext, useContext, useImperativeHandle, useRef, useState, } from 'react'; import { useChartData } from '../../context/ChartContext.js'; import { usePreferences } from '../../context/PreferencesContext.js'; import { useToaster } from '../../context/ToasterContext.js'; import { useExportViewPort } from '../../hooks/useExport.js'; import { useWorkspaceExportSettings } from '../../hooks/useWorkspaceExportSettings.js'; import { ExportContent } from './ExportContent.js'; type ExportFormat = 'png' | 'svg'; type ExportDestination = 'file' | 'clipboard'; type ExportHandlers = Record< ExportDestination, Partial Promise>> >; interface ExportOptions { format: ExportFormat; destination?: ExportDestination; } interface ExportManagerState { export: (options: ExportOptions) => void; exportFinished: () => void; } const ExportManagerContext = createContext | null>(null); export function useExportManagerAPI() { const context = useContext(ExportManagerContext); if (!context) { throw new Error( 'useExportManagerAPI must be used within an ExportManagerProvider', ); } return context; } interface ExportManagerProviderProps { children: ReactNode; } export function ExportManagerProvider(props: ExportManagerProviderProps) { const { children } = props; const ref = useRef(null); return ( {children} ); } interface ExportManagerControllerProps { children: ReactNode; } export function ExportManagerController(props: ExportManagerControllerProps) { const { children } = props; const { data, width } = useChartData(); const hasDataToExport = data.length > 0; const [exportOptions, triggerExport] = useState(null); const exportRef = useExportManagerAPI(); const workspaceExportSettings = useWorkspaceExportSettings(); const { dispatch } = usePreferences(); const toaster = useToaster(); useImperativeHandle( exportRef, () => ({ export: (options: ExportOptions) => { triggerExport(options); }, exportFinished: () => { triggerExport(null); }, }), [], ); const { saveAsPNGHandler, saveAsSVGHandler, copyPNGToClipboardHandler } = useExportViewPort(); function handleCloseExportOptionsDialog() { triggerExport(null); } function handleExport( targetElement: HTMLElement, options: ExportSettings, ): void { if (!exportOptions) { return; } if (!hasDataToExport) { handleCloseExportOptionsDialog(); toaster.show({ intent: 'danger', message: 'No spectra available for export', }); return; } const { format, destination = 'file' } = exportOptions; const handlers: ExportHandlers = { file: { png: saveAsPNGHandler, svg: saveAsSVGHandler, }, clipboard: { png: copyPNGToClipboardHandler, }, }; const runExport = async () => { const handler = handlers[destination]?.[format]; if (handler) { try { await handler(targetElement); } catch { handleCloseExportOptionsDialog(); } } else { // eslint-disable-next-line no-console console.error( `Unsupported format '${format}' for destination '${destination}'.`, ); } handleCloseExportOptionsDialog(); dispatch({ type: 'CHANGE_EXPORT_SETTINGS', payload: { key: format, options }, }); }; void runExport(); } if (!exportOptions) return null; const { format, destination = 'file' } = exportOptions; let exportAs: keyof ExportPreferences = format; if (destination === 'clipboard') { exportAs = 'clipboard'; } const settings = workspaceExportSettings[exportAs]; return ( {children} ); }