diff --git a/resources/js/Components/MetroMap/MetroCanvas.vue b/resources/js/Components/MetroMap/MetroCanvas.vue index 2d4774d..913d9b0 100644 --- a/resources/js/Components/MetroMap/MetroCanvas.vue +++ b/resources/js/Components/MetroMap/MetroCanvas.vue @@ -772,7 +772,7 @@ const renderDimension = (dimData, opacity, parentGroup) => { }) .attr('stroke-width', 2) - // Child-dimension indicator + // Child-dimension indicator: populated dimension (has nodes inside) nodeGroups.filter(d => d.children && (d.children.nodes?.length ?? 0) > 0) .append('circle') .attr('r', 16) @@ -783,7 +783,6 @@ const renderDimension = (dimData, opacity, parentGroup) => { .attr('opacity', 0.5) .attr('filter', 'url(#childGlow)') - // "[+]" label for nodes with children nodeGroups.filter(d => d.children && (d.children.nodes?.length ?? 0) > 0) .append('text') .attr('x', 13) @@ -794,6 +793,26 @@ const renderDimension = (dimData, opacity, parentGroup) => { .attr('opacity', 0.8) .text('[+]') + // Empty dimension indicator (dimension exists but no nodes yet) + nodeGroups.filter(d => d.children && (d.children.nodes?.length ?? 0) === 0) + .append('circle') + .attr('r', 14) + .attr('fill', 'none') + .attr('stroke', '#8892b0') + .attr('stroke-width', 0.8) + .attr('stroke-dasharray', '2,4') + .attr('opacity', 0.4) + + nodeGroups.filter(d => d.children && (d.children.nodes?.length ?? 0) === 0) + .append('text') + .attr('x', 13) + .attr('y', -13) + .attr('fill', '#8892b0') + .attr('font-family', "'VT323', monospace") + .attr('font-size', '10px') + .attr('opacity', 0.5) + .text('[○]') + // Station label nodeGroups.append('text') .attr('x', 0) @@ -880,22 +899,35 @@ const handleContextEdit = () => { closeContextMenu() } -const handleContextAddChild = () => { - if (contextMenu.value.node) { - emit('create-node', { - x: contextMenu.value.canvasX, - y: contextMenu.value.canvasY, - parentNodeId: contextMenu.value.node.id, - parentEntityType: contextMenu.value.node.entityType ?? null, - parentEntityId: contextMenu.value.node.entityId ?? null, - dimensionId: contextMenu.value.node.id, - depth: currentDepth.value + 1, - addToNode: contextMenu.value.node, - }) +/** "Add dimension" — attach an empty children object to a node, making it zoomable */ +const handleContextAddDimension = () => { + const node = contextMenu.value.node + if (!node) { closeContextMenu(); return } + + // Create empty dimension structure on the node + node.children = { + lines: [], + nodes: [], + connections: [], + parentEntityType: node.entityType ?? null, + parentEntityId: node.entityId ?? null, + parentName: node.name ?? null, } + + // Re-render to show the new [○] indicator + renderMap() closeContextMenu() } +/** "Open dimension" — zoom into a node that already has children */ +const handleContextZoomIn = () => { + const node = contextMenu.value.node + if (!node?.children) { closeContextMenu(); return } + + closeContextMenu() + commitDimensionChange('in', node) +} + const handleContextDelete = () => { if (contextMenu.value.node) emit('delete-node', contextMenu.value.node) closeContextMenu() @@ -1055,9 +1087,16 @@ defineExpose({ +