Files
resolutionflow/frontend/src/lib/drawio-export.ts
chihlasm 2a4220b496 feat(network): draw.io XML export
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 01:25:49 +00:00

100 lines
4.5 KiB
TypeScript

import type { Node, Edge } from '@xyflow/react'
import type { DeviceNodeData } from '@/components/network/nodes/DeviceNode'
import type { GroupNodeData } from '@/types/network-diagram'
// Maps our device slugs to draw.io Cisco stencil shape styles
const SLUG_TO_DRAWIO_STYLE: Record<string, string> = {
'router': 'shape=mxgraph.cisco.routers.router;',
'switch': 'shape=mxgraph.cisco.switches.layer_3_switch;',
'access-point': 'shape=mxgraph.cisco.misc.access_point;',
'load-balancer': 'shape=mxgraph.cisco.misc.generic_building;',
'firewall': 'shape=mxgraph.cisco.firewalls.firewall;',
'badge-reader': 'shape=mxgraph.cisco.misc.generic_building;',
'server': 'shape=mxgraph.cisco.servers.standard_server;',
'vm': 'shape=mxgraph.cisco.servers.standard_server;',
'container': 'shape=mxgraph.cisco.servers.standard_server;',
'nas': 'shape=mxgraph.cisco.storage.tape_storage_library;',
'san': 'shape=mxgraph.cisco.storage.tape_storage_library;',
'cloud-storage': 'shape=mxgraph.cisco.misc.cloud;',
'cloud': 'shape=mxgraph.cisco.misc.cloud;',
'aws': 'shape=mxgraph.cisco.misc.cloud;',
'azure': 'shape=mxgraph.cisco.misc.cloud;',
'gcp': 'shape=mxgraph.cisco.misc.cloud;',
'isp': 'shape=mxgraph.cisco.misc.cloud;',
'workstation': 'shape=mxgraph.cisco.computers_and_peripherals.pc;',
'laptop': 'shape=mxgraph.cisco.computers_and_peripherals.laptop;',
'tablet': 'shape=mxgraph.cisco.computers_and_peripherals.laptop;',
'phone': 'shape=mxgraph.cisco.computers_and_peripherals.ip_phone;',
'printer': 'shape=mxgraph.cisco.computers_and_peripherals.printer;',
'ups': 'shape=mxgraph.cisco.misc.generic_building;',
'pdu': 'shape=mxgraph.cisco.misc.generic_building;',
'rack': 'shape=mxgraph.cisco.misc.generic_building;',
'patch-panel': 'shape=mxgraph.cisco.misc.generic_building;',
'camera': 'shape=mxgraph.cisco.misc.generic_building;',
'nvr': 'shape=mxgraph.cisco.misc.generic_building;',
'iot': 'shape=mxgraph.cisco.misc.generic_building;',
}
const BASE_NODE_STYLE =
'sketch=0;html=1;pointerEvents=1;dashed=0;fillColor=#036897;strokeColor=#ffffff;strokeWidth=2;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;'
const GROUP_STYLE =
'swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;collapsible=0;marginBottom=0;swimlaneHead=0;fillColor=none;'
function esc(s: string): string {
return s
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
}
export function exportToDrawio(nodes: Node[], edges: Edge[]): string {
const cells: string[] = [
'<mxCell id="0"/>',
'<mxCell id="1" parent="0"/>',
]
for (const node of nodes) {
const w = typeof node.style?.width === 'number' ? node.style.width : (node.measured?.width ?? 120)
const h = typeof node.style?.height === 'number' ? node.style.height : (node.measured?.height ?? 120)
const x = node.position.x
const y = node.position.y
const parentId = node.parentId ?? '1'
if (node.type === 'group') {
const gd = node.data as GroupNodeData
cells.push(
`<mxCell id="${esc(node.id)}" value="${esc(gd.label ?? '')}" style="${GROUP_STYLE}" vertex="1" parent="${esc(parentId)}">` +
`<mxGeometry x="${x}" y="${y}" width="${w}" height="${h}" as="geometry"/>` +
`</mxCell>`,
)
} else {
const dd = node.data as DeviceNodeData
const slug = dd.deviceType ?? 'server'
const shapeStyle = SLUG_TO_DRAWIO_STYLE[slug] ?? 'rounded=1;whiteSpace=wrap;html=1;'
cells.push(
`<mxCell id="${esc(node.id)}" value="${esc(dd.label ?? '')}" style="${shapeStyle}${BASE_NODE_STYLE}" vertex="1" parent="${esc(parentId)}">` +
`<mxGeometry x="${x}" y="${y}" width="${w}" height="${h}" as="geometry"/>` +
`</mxCell>`,
)
}
}
for (const edge of edges) {
const label = typeof edge.label === 'string' ? edge.label : ''
cells.push(
`<mxCell id="${esc(edge.id)}" value="${esc(label)}" style="edgeStyle=orthogonalEdgeStyle;html=1;" edge="1" source="${esc(edge.source)}" target="${esc(edge.target)}" parent="1">` +
`<mxGeometry relative="1" as="geometry"/>` +
`</mxCell>`,
)
}
const xml =
`<?xml version="1.0" encoding="UTF-8"?>\n` +
`<mxGraphModel><root>\n` +
cells.join('\n') +
`\n</root></mxGraphModel>`
return xml
}