import React, { useState, useRef, useCallback, useEffect } from 'react'; import type { YjsDocument } from '../crdt/YjsDocument'; import { exportDrawing, type ExportFormat } from '../services/exportService'; import type { ExportResult } from '../types/cad.types'; import { importDrawing, type ImportFormat, type ImportResult } from '../services/importService'; import PrintPreview from './PrintPreview/PrintPreview'; import './RibbonBar.css'; interface RibbonBarProps { onTabChange: (tab: string) => void; yjsDoc?: React.RefObject; } const RibbonBar: React.FC = ({ onTabChange, yjsDoc }) => { const [activeTab, setActiveTab] = useState('draw'); const [showExportMenu, setShowExportMenu] = useState(false); const [showImportMenu, setShowImportMenu] = useState(false); const [importStatus, setImportStatus] = useState(null); const [showPrintPreview, setShowPrintPreview] = useState(false); const fileInputRef = useRef(null); const pendingImportFormat = useRef('json'); // Refs for dropdown wrappers (for outside-click detection) const exportDropdownRef = useRef(null); const importDropdownRef = useRef(null); // Ref to track the trigger button that opened the print preview (for focus restoration) const printTriggerRef = useRef(null); const tabs = [ { id: 'draw', label: 'Draw', icon: '✏️' }, { id: 'modify', label: 'Modify', icon: '🔧' }, { id: 'annotate', label: 'Annotate', icon: '📝' }, { id: 'view', label: 'View', icon: '👁️' }, { id: 'tools', label: 'Tools', icon: '🛠️' }, ]; const handleTabClick = (tabId: string) => { setActiveTab(tabId); onTabChange(tabId); }; const handleExport = useCallback(async (format: ExportFormat) => { setShowExportMenu(false); const doc = yjsDoc?.current; if (!doc) { setImportStatus('Error: No document available'); setTimeout(() => setImportStatus(null), 3000); return; } try { const result: ExportResult = await exportDrawing(doc, format); if (!result.success) { setImportStatus(`Export error: ${result.error}`); setTimeout(() => setImportStatus(null), 5000); } } catch (e) { setImportStatus(`Export error: ${(e as Error).message}`); setTimeout(() => setImportStatus(null), 5000); } }, [yjsDoc]); const triggerImport = useCallback((format: ImportFormat) => { setShowImportMenu(false); pendingImportFormat.current = format; if (fileInputRef.current) { fileInputRef.current.accept = format === 'json' ? '.json,application/json' : '.dxf,application/dxf'; fileInputRef.current.click(); } }, []); const handleFileSelected = useCallback(async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; const doc = yjsDoc?.current; if (!doc) { setImportStatus('Error: No document available'); setTimeout(() => setImportStatus(null), 3000); return; } try { const result: ImportResult = await importDrawing(file, pendingImportFormat.current, doc); if (result.success) { setImportStatus(`Imported: ${result.elementsImported} elements, ${result.layersImported} layers`); } else { setImportStatus(`Import failed: ${result.errors.join(', ')}`); } } catch (err) { setImportStatus(`Import error: ${(err as Error).message}`); } setTimeout(() => setImportStatus(null), 5000); if (fileInputRef.current) { fileInputRef.current.value = ''; } }, [yjsDoc]); // Outside-click and Escape close for dropdowns useEffect(() => { const handleClickOutside = (e: MouseEvent) => { const target = e.target as Node; if (showExportMenu && exportDropdownRef.current && !exportDropdownRef.current.contains(target)) { setShowExportMenu(false); } if (showImportMenu && importDropdownRef.current && !importDropdownRef.current.contains(target)) { setShowImportMenu(false); } }; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') { if (showExportMenu) { setShowExportMenu(false); } if (showImportMenu) { setShowImportMenu(false); } } }; if (showExportMenu || showImportMenu) { document.addEventListener('mousedown', handleClickOutside); document.addEventListener('keydown', handleKeyDown); } return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('keydown', handleKeyDown); }; }, [showExportMenu, showImportMenu]); return (
{tabs.map((tab) => ( ))}