UNPKG

@ichigo_san/graphing

Version:

A lightweight UML-style diagram editor built with React Flow and Tailwind CSS

241 lines (231 loc) 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _lucideReact = require("lucide-react"); var _ExportService = require("../../services/ExportService"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } const ExportModal = ({ isOpen, onClose, diagramData, className = '' }) => { const [selectedFormat, setSelectedFormat] = (0, _react.useState)('png'); const [quality, setQuality] = (0, _react.useState)(0.9); const [scale, setScale] = (0, _react.useState)(1); const [backgroundColor, setBackgroundColor] = (0, _react.useState)('#ffffff'); const [includeBackground, setIncludeBackground] = (0, _react.useState)(false); const [isExporting, setIsExporting] = (0, _react.useState)(false); const [exportProgress, setExportProgress] = (0, _react.useState)(0); const [exportError, setExportError] = (0, _react.useState)(null); const [exportSuccess, setExportSuccess] = (0, _react.useState)(false); const exportService = (0, _react.useMemo)(() => new _ExportService.ExportService(), []); // Get format information const formatInfo = (0, _react.useMemo)(() => { return exportService.getFormatInfo(selectedFormat) || {}; }, [selectedFormat, exportService]); // Available formats const availableFormats = (0, _react.useMemo)(() => { return exportService.getAvailableFormats().map(format => ({ id: format, ...exportService.getFormatInfo(format) })); }, [exportService]); // Handle format change const handleFormatChange = (0, _react.useCallback)(format => { setSelectedFormat(format); setExportError(null); setExportSuccess(false); // Reset quality and background options based on format const info = exportService.getFormatInfo(format); if (info) { if (info.qualityRange) { setQuality(info.qualityRange.default); } if (!info.supportsTransparency) { setIncludeBackground(true); } } }, [exportService]); // Handle export const handleExport = (0, _react.useCallback)(async () => { if (!diagramData) { setExportError('No diagram data available for export'); return; } setIsExporting(true); setExportProgress(0); setExportError(null); setExportSuccess(false); try { // Validate options const validatedOptions = exportService.validateExportOptions(selectedFormat, { quality, scale, backgroundColor, includeBackground }); // Simulate progress const progressInterval = setInterval(() => { setExportProgress(prev => { if (prev >= 90) { clearInterval(progressInterval); return 90; } return prev + 10; }); }, 100); // Perform export const result = await exportService.export(diagramData, selectedFormat, validatedOptions); clearInterval(progressInterval); setExportProgress(100); if (result.success) { // Create download link exportService.createDownloadLink(result.data); setExportSuccess(true); // Auto-close after success setTimeout(() => { onClose(); setExportSuccess(false); }, 2000); } else { setExportError(result.error || 'Export failed'); } } catch (error) { setExportError(error.message || 'Export failed'); } finally { setIsExporting(false); setExportProgress(0); } }, [diagramData, selectedFormat, quality, scale, backgroundColor, includeBackground, exportService, onClose]); // Handle close const handleClose = (0, _react.useCallback)(() => { if (!isExporting) { onClose(); setExportError(null); setExportSuccess(false); setExportProgress(0); } }, [isExporting, onClose]); if (!isOpen) return null; return /*#__PURE__*/_react.default.createElement("div", { className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" }, /*#__PURE__*/_react.default.createElement("div", { className: `bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full mx-4 max-h-[90vh] overflow-y-auto ${className}` }, /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700" }, /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center gap-3" }, /*#__PURE__*/_react.default.createElement(_lucideReact.Download, { className: "w-6 h-6 text-blue-500" }), /*#__PURE__*/_react.default.createElement("h2", { className: "text-xl font-semibold text-gray-900 dark:text-gray-100" }, "Export Diagram")), /*#__PURE__*/_react.default.createElement("button", { onClick: handleClose, disabled: isExporting, className: "p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors disabled:opacity-50" }, /*#__PURE__*/_react.default.createElement(_lucideReact.X, { className: "w-5 h-5 text-gray-500" }))), /*#__PURE__*/_react.default.createElement("div", { className: "p-6 space-y-6" }, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-3" }, "Export Format"), /*#__PURE__*/_react.default.createElement("div", { className: "grid grid-cols-2 gap-3" }, availableFormats.map(format => /*#__PURE__*/_react.default.createElement("button", { key: format.id, onClick: () => handleFormatChange(format.id), className: `p-4 border-2 rounded-lg text-left transition-all ${selectedFormat === format.id ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20' : 'border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500'}` }, /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, format.id === 'png' && /*#__PURE__*/_react.default.createElement(_lucideReact.Image, { className: "w-4 h-4" }), format.id === 'jpg' && /*#__PURE__*/_react.default.createElement(_lucideReact.Image, { className: "w-4 h-4" }), format.id === 'svg' && /*#__PURE__*/_react.default.createElement(_lucideReact.FileText, { className: "w-4 h-4" }), format.id === 'json' && /*#__PURE__*/_react.default.createElement(_lucideReact.FileText, { className: "w-4 h-4" }), format.id === 'drawio' && /*#__PURE__*/_react.default.createElement(_lucideReact.FileText, { className: "w-4 h-4" }), /*#__PURE__*/_react.default.createElement("span", { className: "font-medium text-gray-900 dark:text-gray-100" }, format.name)), /*#__PURE__*/_react.default.createElement("p", { className: "text-xs text-gray-500 dark:text-gray-400" }, format.description))))), formatInfo.qualityRange && /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "Quality (", Math.round(quality * 100), "%)"), /*#__PURE__*/_react.default.createElement("input", { type: "range", min: formatInfo.qualityRange.min, max: formatInfo.qualityRange.max, step: 0.1, value: quality, onChange: e => setQuality(parseFloat(e.target.value)), className: "w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider" }), /*#__PURE__*/_react.default.createElement("div", { className: "flex justify-between text-xs text-gray-500 dark:text-gray-400 mt-1" }, /*#__PURE__*/_react.default.createElement("span", null, Math.round(formatInfo.qualityRange.min * 100), "%"), /*#__PURE__*/_react.default.createElement("span", null, Math.round(formatInfo.qualityRange.max * 100), "%"))), formatInfo.scaleRange && /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "Resolution Scale (", scale, "x)"), /*#__PURE__*/_react.default.createElement("div", { className: "flex gap-2" }, [0.5, 1, 2, 3].map(scaleOption => /*#__PURE__*/_react.default.createElement("button", { key: scaleOption, onClick: () => setScale(scaleOption), className: `px-3 py-1 rounded text-sm font-medium transition-colors ${scale === scaleOption ? 'bg-blue-500 text-white' : 'bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-500'}` }, scaleOption, "x")))), formatInfo.supportsTransparency && /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("label", { className: "flex items-center gap-2 text-sm font-medium text-gray-700 dark:text-gray-300" }, /*#__PURE__*/_react.default.createElement("input", { type: "checkbox", checked: includeBackground, onChange: e => setIncludeBackground(e.target.checked), className: "rounded border-gray-300 text-blue-600 focus:ring-blue-500" }), "Include Background"), includeBackground && /*#__PURE__*/_react.default.createElement("div", { className: "mt-2" }, /*#__PURE__*/_react.default.createElement("label", { className: "block text-xs text-gray-500 dark:text-gray-400 mb-1" }, "Background Color"), /*#__PURE__*/_react.default.createElement("input", { type: "color", value: backgroundColor, onChange: e => setBackgroundColor(e.target.value), className: "w-full h-8 rounded border border-gray-300 dark:border-gray-600" }))), exportError && /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center gap-2 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg" }, /*#__PURE__*/_react.default.createElement(_lucideReact.AlertCircle, { className: "w-4 h-4 text-red-500" }), /*#__PURE__*/_react.default.createElement("span", { className: "text-sm text-red-700 dark:text-red-300" }, exportError)), exportSuccess && /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center gap-2 p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg" }, /*#__PURE__*/_react.default.createElement(_lucideReact.CheckCircle, { className: "w-4 h-4 text-green-500" }), /*#__PURE__*/_react.default.createElement("span", { className: "text-sm text-green-700 dark:text-green-300" }, "Export completed successfully!")), isExporting && /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "flex justify-between text-sm text-gray-600 dark:text-gray-400 mb-1" }, /*#__PURE__*/_react.default.createElement("span", null, "Exporting..."), /*#__PURE__*/_react.default.createElement("span", null, exportProgress, "%")), /*#__PURE__*/_react.default.createElement("div", { className: "w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2" }, /*#__PURE__*/_react.default.createElement("div", { className: "bg-blue-500 h-2 rounded-full transition-all duration-300", style: { width: `${exportProgress}%` } })))), /*#__PURE__*/_react.default.createElement("div", { className: "flex justify-end gap-3 p-6 border-t border-gray-200 dark:border-gray-700" }, /*#__PURE__*/_react.default.createElement("button", { onClick: handleClose, disabled: isExporting, className: "px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors disabled:opacity-50" }, "Cancel"), /*#__PURE__*/_react.default.createElement("button", { onClick: handleExport, disabled: isExporting || !diagramData, className: "px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors disabled:opacity-50 flex items-center gap-2" }, isExporting ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", { className: "w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" }), "Exporting...") : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_lucideReact.Download, { className: "w-4 h-4" }), "Export"))))); }; var _default = exports.default = ExportModal;