feat: add interactive ProcessDiagram SVG component (sub-project 1/3)
New interactive route diagram component with SVG rendering using server-computed ELK layout coordinates. TIBCO BW5-inspired top-bar card node style with zoom/pan, hover toolbars, config badges, and error handler sections below the main flow. Backend: add direction query parameter (LR/TB) to diagram render endpoints, defaulting to left-to-right layout. Frontend: 14-file ProcessDiagram component in ui/src/components/ with DiagramNode, CompoundNode, DiagramEdge, ConfigBadge, NodeToolbar, ErrorSection, ZoomControls, and supporting hooks. Dev test page at /dev/diagram for validation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
49
ui/src/components/ProcessDiagram/DiagramEdge.tsx
Normal file
49
ui/src/components/ProcessDiagram/DiagramEdge.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { DiagramEdge as DiagramEdgeType } from '../../api/queries/diagrams';
|
||||
|
||||
interface DiagramEdgeProps {
|
||||
edge: DiagramEdgeType;
|
||||
offsetY?: number;
|
||||
}
|
||||
|
||||
export function DiagramEdge({ edge, offsetY = 0 }: DiagramEdgeProps) {
|
||||
const pts = edge.points;
|
||||
if (!pts || pts.length < 2) return null;
|
||||
|
||||
// Build SVG path: move to first point, then cubic bezier or line to rest
|
||||
let d = `M ${pts[0][0]} ${pts[0][1] + offsetY}`;
|
||||
|
||||
if (pts.length === 2) {
|
||||
d += ` L ${pts[1][0]} ${pts[1][1] + offsetY}`;
|
||||
} else if (pts.length === 4) {
|
||||
// 4 points: start, control1, control2, end → cubic bezier
|
||||
d += ` C ${pts[1][0]} ${pts[1][1] + offsetY}, ${pts[2][0]} ${pts[2][1] + offsetY}, ${pts[3][0]} ${pts[3][1] + offsetY}`;
|
||||
} else {
|
||||
// Multiple points: connect with line segments through intermediate points
|
||||
for (let i = 1; i < pts.length; i++) {
|
||||
d += ` L ${pts[i][0]} ${pts[i][1] + offsetY}`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<g className="diagram-edge">
|
||||
<path
|
||||
d={d}
|
||||
fill="none"
|
||||
stroke="#9CA3AF"
|
||||
strokeWidth={1.5}
|
||||
markerEnd="url(#arrowhead)"
|
||||
/>
|
||||
{edge.label && pts.length >= 2 && (
|
||||
<text
|
||||
x={(pts[0][0] + pts[pts.length - 1][0]) / 2}
|
||||
y={(pts[0][1] + pts[pts.length - 1][1]) / 2 + offsetY - 6}
|
||||
fill="#9C9184"
|
||||
fontSize={9}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{edge.label}
|
||||
</text>
|
||||
)}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user