diff --git a/index.php b/index.php
index e0c29c0..e981cc4 100644
--- a/index.php
+++ b/index.php
@@ -1745,7 +1745,7 @@ require_once 'config.php';
.style('cursor', 'pointer')
.on('click', (event, d) => {
console.log('Edge clicked:', d);
- // TODO: Show edge properties
+ this.selectEdge(event.currentTarget, d);
});
// Add edge labels
@@ -1788,7 +1788,7 @@ require_once 'config.php';
// Click handler for nodes
nodeGroups.on('click', (event, d) => {
console.log('Node clicked:', d);
- // TODO: Show node properties
+ this.selectNode(event.currentTarget, d);
});
this.nodeElements = nodeGroups;
@@ -1863,6 +1863,330 @@ require_once 'config.php';
console.log(`Node ${d.id} positioned at (${d.fx}, ${d.fy})`);
});
}
+
+ selectNode(element, nodeData) {
+ // Clear previous selections
+ this.clearSelections();
+
+ // Highlight selected node
+ d3.select(element).select('rect, ellipse, polygon')
+ .attr('stroke', '#ff6b6b')
+ .attr('stroke-width', 3)
+ .style('filter', 'drop-shadow(0 0 5px rgba(255, 107, 107, 0.5))');
+
+ this.selectedElement = element;
+ this.selectedData = nodeData;
+
+ // Show property panel using existing MermaidEditor method
+ this.showNodePropertyPanel(nodeData);
+ }
+
+ selectEdge(element, edgeData) {
+ // Clear previous selections
+ this.clearSelections();
+
+ // Highlight selected edge
+ d3.select(element)
+ .attr('stroke', '#ff6b6b')
+ .attr('stroke-width', 3);
+
+ this.selectedElement = element;
+ this.selectedData = edgeData;
+
+ // Show property panel using existing MermaidEditor method
+ this.showEdgePropertyPanel(edgeData);
+ }
+
+ clearSelections() {
+ // Clear node selections
+ this.svg.selectAll('.node-group rect, .node-group ellipse, .node-group polygon')
+ .attr('stroke', '#000')
+ .attr('stroke-width', 2)
+ .style('filter', 'none');
+
+ // Clear edge selections
+ this.svg.selectAll('.links line')
+ .attr('stroke', d => d.style.stroke || '#000')
+ .attr('stroke-width', d => d.style.strokeWidth || 2);
+
+ this.selectedElement = null;
+ this.selectedData = null;
+ }
+
+ showNodePropertyPanel(nodeData) {
+ const panel = document.getElementById('propertyPanel');
+ const content = document.getElementById('propertyContent');
+
+ content.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ panel.classList.add('visible');
+ panel.style.display = 'block';
+ }
+
+ showEdgePropertyPanel(edgeData) {
+ const panel = document.getElementById('propertyPanel');
+ const content = document.getElementById('propertyContent');
+
+ content.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${edgeData.style.strokeWidth || 2}px
+
+
+
+ `;
+
+ // Add live width display
+ setTimeout(() => {
+ const widthSlider = document.getElementById('d3EdgeWidth');
+ const widthDisplay = document.getElementById('d3WidthValue');
+ if (widthSlider && widthDisplay) {
+ widthSlider.addEventListener('input', () => {
+ widthDisplay.textContent = widthSlider.value + 'px';
+ });
+ }
+ }, 100);
+
+ panel.classList.add('visible');
+ panel.style.display = 'block';
+ }
+
+ updateNodeProperties() {
+ if (!this.selectedData) return;
+
+ const nodeText = document.getElementById('d3NodeText').value;
+ const nodeShape = document.getElementById('d3NodeShape').value;
+ const nodeColor = document.getElementById('d3NodeColor').value;
+ const textColor = document.getElementById('d3TextColor').value;
+
+ console.log('Updating D3 node properties:', {
+ id: this.selectedData.id,
+ text: nodeText,
+ shape: nodeShape,
+ color: nodeColor,
+ textColor: textColor
+ });
+
+ // Update the data
+ this.selectedData.text = nodeText;
+ this.selectedData.shape = nodeShape;
+ this.selectedData.style.fill = nodeColor;
+ this.selectedData.style.color = textColor;
+
+ // Update the visual elements
+ const selectedGroup = d3.select(this.selectedElement);
+
+ // Update text
+ selectedGroup.select('text')
+ .text(nodeText)
+ .attr('fill', textColor);
+
+ // Update shape and color
+ selectedGroup.select('rect, ellipse, polygon')
+ .attr('fill', nodeColor);
+
+ // If shape changed, redraw the node
+ if (nodeShape !== this.selectedData.shape) {
+ selectedGroup.select('rect, ellipse, polygon').remove();
+ this.addNodeShape(selectedGroup, this.selectedData);
+ }
+
+ console.log('Node updated successfully');
+
+ // Generate updated Mermaid code
+ this.generateMermaidCode();
+ }
+
+ updateEdgeProperties() {
+ if (!this.selectedData) return;
+
+ const edgeLabel = document.getElementById('d3EdgeLabel').value;
+ const edgeType = document.getElementById('d3EdgeType').value;
+ const edgeColor = document.getElementById('d3EdgeColor').value;
+ const edgeWidth = document.getElementById('d3EdgeWidth').value;
+
+ console.log('Updating D3 edge properties:', {
+ label: edgeLabel,
+ type: edgeType,
+ color: edgeColor,
+ width: edgeWidth
+ });
+
+ // Update the data
+ this.selectedData.label = edgeLabel;
+ this.selectedData.type = edgeType;
+ this.selectedData.style.stroke = edgeColor;
+ this.selectedData.style.strokeWidth = parseInt(edgeWidth);
+
+ // Update the visual elements
+ d3.select(this.selectedElement)
+ .attr('stroke', edgeColor)
+ .attr('stroke-width', edgeWidth);
+
+ // Update or add edge label
+ if (edgeLabel) {
+ // Find or create label element
+ let labelElement = this.svg.select(`.links text[data-edge="${this.selectedData.source.id}-${this.selectedData.target.id}"]`);
+ if (labelElement.empty()) {
+ labelElement = this.svg.select('.links').append('text')
+ .attr('data-edge', `${this.selectedData.source.id}-${this.selectedData.target.id}`)
+ .attr('font-size', 12)
+ .attr('fill', '#333')
+ .attr('text-anchor', 'middle')
+ .style('pointer-events', 'none');
+ }
+ labelElement.text(edgeLabel);
+ }
+
+ console.log('Edge updated successfully');
+
+ // Generate updated Mermaid code
+ this.generateMermaidCode();
+ }
+
+ deleteNode() {
+ if (!this.selectedData) return;
+
+ const nodeId = this.selectedData.id;
+ console.log('Deleting node:', nodeId);
+
+ // Remove from data arrays
+ this.nodes = this.nodes.filter(n => n.id !== nodeId);
+ this.edges = this.edges.filter(e =>
+ (e.source.id || e.source) !== nodeId &&
+ (e.target.id || e.target) !== nodeId
+ );
+
+ // Remove visual elements
+ d3.select(this.selectedElement).remove();
+
+ // Remove connected edges
+ this.svg.selectAll('.links line')
+ .filter(d => (d.source.id || d.source) === nodeId || (d.target.id || d.target) === nodeId)
+ .remove();
+
+ // Remove connected edge labels
+ this.svg.selectAll('.links text')
+ .filter(d => (d.source.id || d.source) === nodeId || (d.target.id || d.target) === nodeId)
+ .remove();
+
+ // Hide property panel
+ document.getElementById('propertyPanel').style.display = 'none';
+
+ // Update simulation
+ this.simulation.nodes(this.nodes);
+ this.simulation.force('link').links(this.edges);
+ this.simulation.alpha(1).restart();
+
+ // Generate updated Mermaid code
+ this.generateMermaidCode();
+ }
+
+ deleteEdge() {
+ if (!this.selectedData) return;
+
+ console.log('Deleting edge:', this.selectedData);
+
+ // Remove from data array
+ this.edges = this.edges.filter(e => e !== this.selectedData);
+
+ // Remove visual elements
+ d3.select(this.selectedElement).remove();
+
+ // Remove label if exists
+ this.svg.selectAll('.links text')
+ .filter(d => d === this.selectedData)
+ .remove();
+
+ // Hide property panel
+ document.getElementById('propertyPanel').style.display = 'none';
+
+ // Update simulation
+ this.simulation.force('link').links(this.edges);
+ this.simulation.alpha(1).restart();
+
+ // Generate updated Mermaid code
+ this.generateMermaidCode();
+ }
+
+ generateMermaidCode() {
+ console.log('Generating Mermaid code from D3 data...');
+
+ // Convert D3 data back to parser format
+ const nodes = new Map();
+ this.nodes.forEach(node => {
+ nodes.set(node.id, {
+ id: node.id,
+ text: node.text,
+ shape: node.shape,
+ style: node.style,
+ position: { x: node.fx || node.x, y: node.fy || node.y }
+ });
+ });
+
+ const edges = this.edges.map(edge => ({
+ from: edge.source.id || edge.source,
+ to: edge.target.id || edge.target,
+ type: edge.type,
+ label: edge.label || '',
+ style: edge.style
+ }));
+
+ // Use existing parser to generate code
+ const newCode = this.parser.generateCode(
+ nodes,
+ edges,
+ 'flowchart',
+ 'TD',
+ new Map() // No subgraphs for now
+ );
+
+ // Update textarea
+ document.getElementById('mermaidInput').value = newCode;
+
+ console.log('Generated Mermaid code:', newCode);
+ }
}
// Initialize both renderers