UNPKG

@ichigo_san/graphing

Version:

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

279 lines (268 loc) 10.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DiagramService = void 0; class DiagramService { constructor(validationService, layoutService, exportService, technicalDetailsService) { this.validationService = validationService; this.layoutService = layoutService; this.exportService = exportService; this.technicalDetailsService = technicalDetailsService; } /** * Create a new diagram from data * @param {Object} diagramData - The diagram data * @param {Object} options - Creation options * @returns {Promise<Object>} The created diagram */ async createDiagram(diagramData, options = {}) { try { // Validate the diagram data const validatedData = await this.validationService.validateDiagram(diagramData); // Apply auto-layout if requested let processedData = validatedData; if (options.autoLayout !== false) { processedData = await this.layoutService.layout(validatedData); } // Enrich with technical details if requested if (options.includeTechnicalDetails !== false) { processedData = await this.technicalDetailsService.enrichDiagram(processedData); } return { success: true, diagram: processedData, metadata: { createdAt: new Date().toISOString(), version: '1.0', autoLayout: options.autoLayout !== false, technicalDetails: options.includeTechnicalDetails !== false } }; } catch (error) { return { success: false, error: error.message, diagram: null }; } } /** * Convert JSON diagram data to React Flow format * @param {Object} config - The diagram configuration * @returns {Object} React Flow compatible data */ jsonToReactFlow(config) { const { containers = [], nodes = [], connections = [] } = config; const reactFlowNodes = []; const reactFlowEdges = []; // Convert containers to React Flow nodes containers.forEach(container => { var _container$size, _container$size2, _container$size3, _container$size4; reactFlowNodes.push({ id: container.id, type: 'container', position: container.position, data: { label: container.label, color: container.color || '#E3F2FD', bgColor: container.bgColor || '#ffffff', borderColor: container.borderColor || '#ddd', icon: container.icon, description: container.description, width: ((_container$size = container.size) === null || _container$size === void 0 ? void 0 : _container$size.width) || 400, height: ((_container$size2 = container.size) === null || _container$size2 === void 0 ? void 0 : _container$size2.height) || 300 }, style: { width: ((_container$size3 = container.size) === null || _container$size3 === void 0 ? void 0 : _container$size3.width) || 400, height: ((_container$size4 = container.size) === null || _container$size4 === void 0 ? void 0 : _container$size4.height) || 300, zIndex: container.zIndex || 1 }, zIndex: container.zIndex || 1 }); }); // Convert nodes to React Flow nodes nodes.forEach(node => { var _node$size, _node$size2, _node$size3, _node$size4; reactFlowNodes.push({ id: node.id, type: node.type || 'component', position: node.position, parentNode: node.parentContainer, data: { label: node.label, color: node.color || '#E3F2FD', borderColor: node.borderColor || '#ddd', icon: node.icon, description: node.description, width: ((_node$size = node.size) === null || _node$size === void 0 ? void 0 : _node$size.width) || 150, height: ((_node$size2 = node.size) === null || _node$size2 === void 0 ? void 0 : _node$size2.height) || 80 }, style: { width: ((_node$size3 = node.size) === null || _node$size3 === void 0 ? void 0 : _node$size3.width) || 150, height: ((_node$size4 = node.size) === null || _node$size4 === void 0 ? void 0 : _node$size4.height) || 80, zIndex: node.zIndex || 10 }, zIndex: node.zIndex || 10 }); }); // Convert connections to React Flow edges connections.forEach(connection => { var _connection$style, _connection$style2, _connection$style3; reactFlowEdges.push({ id: connection.id, source: connection.source, target: connection.target, label: connection.label, type: connection.type || 'adjustable', animated: connection.animated || false, data: { label: connection.label, description: connection.description, waypoints: connection.waypoints, intersection: connection.intersection }, markerStart: connection.markerStart, markerEnd: connection.markerEnd, style: { strokeWidth: ((_connection$style = connection.style) === null || _connection$style === void 0 ? void 0 : _connection$style.strokeWidth) || 2, strokeDasharray: (_connection$style2 = connection.style) === null || _connection$style2 === void 0 ? void 0 : _connection$style2.strokeDasharray, stroke: ((_connection$style3 = connection.style) === null || _connection$style3 === void 0 ? void 0 : _connection$style3.stroke) || '#000000' }, zIndex: connection.zIndex || 5 }); }); return { nodes: reactFlowNodes, edges: reactFlowEdges }; } /** * Convert React Flow data to JSON format * @param {Array} nodes - React Flow nodes * @param {Array} edges - React Flow edges * @returns {Object} JSON format diagram data */ reactFlowToJson(nodes, edges) { const containers = nodes.filter(node => node.type === 'container').map(container => { var _container$style, _container$style2; return { id: container.id, label: container.data.label, position: container.position, size: { width: ((_container$style = container.style) === null || _container$style === void 0 ? void 0 : _container$style.width) || 400, height: ((_container$style2 = container.style) === null || _container$style2 === void 0 ? void 0 : _container$style2.height) || 300 }, color: container.data.color, bgColor: container.data.bgColor, borderColor: container.data.borderColor, icon: container.data.icon, description: container.data.description, zIndex: container.zIndex || 1 }; }); const diagramNodes = nodes.filter(node => node.type !== 'container').map(node => { var _node$style, _node$style2; return { id: node.id, label: node.data.label, type: node.type, position: node.position, parentContainer: node.parentNode, size: { width: ((_node$style = node.style) === null || _node$style === void 0 ? void 0 : _node$style.width) || 150, height: ((_node$style2 = node.style) === null || _node$style2 === void 0 ? void 0 : _node$style2.height) || 80 }, color: node.data.color, borderColor: node.data.borderColor, icon: node.data.icon, description: node.data.description, zIndex: node.zIndex || 10 }; }); const connections = edges.map(edge => { var _edge$data, _edge$data2, _edge$data3, _edge$data4, _edge$style, _edge$style2; return { id: edge.id, source: edge.source, target: edge.target, label: (_edge$data = edge.data) === null || _edge$data === void 0 ? void 0 : _edge$data.label, type: edge.type, animated: edge.animated, description: (_edge$data2 = edge.data) === null || _edge$data2 === void 0 ? void 0 : _edge$data2.description, waypoints: (_edge$data3 = edge.data) === null || _edge$data3 === void 0 ? void 0 : _edge$data3.waypoints, markerStart: edge.markerStart, markerEnd: edge.markerEnd, intersection: (_edge$data4 = edge.data) === null || _edge$data4 === void 0 ? void 0 : _edge$data4.intersection, style: { strokeWidth: ((_edge$style = edge.style) === null || _edge$style === void 0 ? void 0 : _edge$style.strokeWidth) || 2, strokeDasharray: (_edge$style2 = edge.style) === null || _edge$style2 === void 0 ? void 0 : _edge$style2.strokeDasharray }, zIndex: edge.zIndex || 5 }; }); return { metadata: { name: 'Architecture Diagram', description: 'Exported architecture diagram', version: '1.0', exportDate: new Date().toISOString() }, containers, nodes: diagramNodes, connections }; } /** * Get diagram bounds for viewport calculations * @param {Array} nodes - React Flow nodes * @returns {Object} Bounds object with x, y, width, height */ getDiagramBounds(nodes) { if (nodes.length === 0) { return { x: 0, y: 0, width: 0, height: 0 }; } let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; nodes.forEach(node => { var _node$__rf, _node$style3, _node$__rf2, _node$style4; const width = ((_node$__rf = node.__rf) === null || _node$__rf === void 0 ? void 0 : _node$__rf.width) || ((_node$style3 = node.style) === null || _node$style3 === void 0 ? void 0 : _node$style3.width) || 150; const height = ((_node$__rf2 = node.__rf) === null || _node$__rf2 === void 0 ? void 0 : _node$__rf2.height) || ((_node$style4 = node.style) === null || _node$style4 === void 0 ? void 0 : _node$style4.height) || 80; minX = Math.min(minX, node.position.x); minY = Math.min(minY, node.position.y); maxX = Math.max(maxX, node.position.x + width); maxY = Math.max(maxY, node.position.y + height); }); return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }; } /** * Validate diagram data against schema * @param {Object} diagramData - The diagram data to validate * @returns {boolean} True if valid, throws error if invalid */ validateDiagramData(diagramData) { // Simple validation - check if required fields exist if (!diagramData || !diagramData.containers || !diagramData.nodes || !diagramData.connections) { throw new Error('Validation failed: Missing required fields (containers, nodes, connections)'); } return true; } } exports.DiagramService = DiagramService;