@ichigo_san/graphing
Version:
A lightweight UML-style diagram editor built with React Flow and Tailwind CSS
241 lines (231 loc) • 13 kB
JavaScript
"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;