- DeviceNode: flat bg-card (no surface gradient), darker icon plate inset, correct text-muted token for category label - GroupNode: label pill gets bg-card/90 background so it reads against canvas - ConnectionEdge: label now has border + bg-card so it doesn't float invisible - BaseHandle: tightened to 12px with accent-toned border - NodeStatusIndicator: glow reduced to 0.15 opacity (design system compliant) - ContextMenu: Ungroup now uses Ungroup icon instead of BoxSelect - DeviceToolbar: group type icons coloured with semantic palette - PropertiesPanel: empty state gets icon tile + cleaner copy hierarchy - DiagramEditor: shortcut ? button repositioned above MiniMap, accent hover - NetworkDiagrams list: card thumbnail placeholder uses dot-grid pattern, card menu gets icons and divider before destructive action Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
73 lines
2.4 KiB
TypeScript
73 lines
2.4 KiB
TypeScript
import { memo } from 'react'
|
|
import { BaseEdge, EdgeLabelRenderer, getStraightPath, getBezierPath, getSmoothStepPath, type EdgeProps } from '@xyflow/react'
|
|
|
|
interface ConnectionEdgeData {
|
|
connectionType?: string
|
|
routing?: string | null
|
|
speed?: string | null
|
|
notes?: string | null
|
|
[key: string]: unknown
|
|
}
|
|
|
|
const CONNECTION_STYLES: Record<string, { stroke: string; strokeDasharray?: string; strokeWidth: number }> = {
|
|
ethernet: { stroke: '#60a5fa', strokeWidth: 2 },
|
|
fiber: { stroke: '#34d399', strokeWidth: 3 },
|
|
wifi: { stroke: '#a78bfa', strokeDasharray: '3,3', strokeWidth: 2 },
|
|
vpn: { stroke: '#eab308', strokeDasharray: '8,4', strokeWidth: 2 },
|
|
vlan: { stroke: '#848b9b', strokeWidth: 2 },
|
|
wan: { stroke: '#f87171', strokeDasharray: '12,4', strokeWidth: 2 },
|
|
}
|
|
|
|
const DEFAULT_STYLE = { stroke: '#848b9b', strokeWidth: 2 }
|
|
|
|
function getEdgePath(routing: string | null | undefined, props: EdgeProps) {
|
|
const base = {
|
|
sourceX: props.sourceX,
|
|
sourceY: props.sourceY,
|
|
sourcePosition: props.sourcePosition,
|
|
targetX: props.targetX,
|
|
targetY: props.targetY,
|
|
targetPosition: props.targetPosition,
|
|
}
|
|
if (routing === 'curved') return getBezierPath(base)
|
|
if (routing === 'step') return getSmoothStepPath(base)
|
|
if (routing === 'orthogonal') return getSmoothStepPath({ ...base, borderRadius: 0 })
|
|
return getStraightPath(base)
|
|
}
|
|
|
|
function ConnectionEdgeComponent(props: EdgeProps) {
|
|
const edgeData = props.data as ConnectionEdgeData | undefined
|
|
const connectionType = edgeData?.connectionType || 'ethernet'
|
|
const style = CONNECTION_STYLES[connectionType] || DEFAULT_STYLE
|
|
|
|
const [edgePath, labelX, labelY] = getEdgePath(edgeData?.routing, props)
|
|
|
|
return (
|
|
<>
|
|
<BaseEdge
|
|
path={edgePath}
|
|
style={{
|
|
...style,
|
|
...(props.selected ? { stroke: '#60a5fa', strokeWidth: style.strokeWidth + 1 } : {}),
|
|
}}
|
|
/>
|
|
{props.label && (
|
|
<EdgeLabelRenderer>
|
|
<div
|
|
className="nodrag nopan rounded border border-default bg-card px-1.5 py-0.5 text-[10px] text-muted-foreground shadow-sm"
|
|
style={{
|
|
position: 'absolute',
|
|
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
|
|
pointerEvents: 'all',
|
|
}}
|
|
>
|
|
{props.label}
|
|
</div>
|
|
</EdgeLabelRenderer>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
export const ConnectionEdge = memo(ConnectionEdgeComponent)
|