Files
cameleer-server/ui/src/components/ProcessDiagram/CompoundNode.tsx

83 lines
2.5 KiB
TypeScript
Raw Normal View History

import type { DiagramNode as DiagramNodeType } from '../../api/queries/diagrams';
import type { NodeConfig } from './types';
import { colorForType } from './node-colors';
import { DiagramNode } from './DiagramNode';
const HEADER_HEIGHT = 22;
const CORNER_RADIUS = 4;
interface CompoundNodeProps {
node: DiagramNodeType;
selectedNodeId?: string;
hoveredNodeId: string | null;
nodeConfigs?: Map<string, NodeConfig>;
onNodeClick: (nodeId: string) => void;
onNodeEnter: (nodeId: string) => void;
onNodeLeave: () => void;
}
export function CompoundNode({
node, selectedNodeId, hoveredNodeId, nodeConfigs,
onNodeClick, onNodeEnter, onNodeLeave,
}: CompoundNodeProps) {
const x = node.x ?? 0;
const y = node.y ?? 0;
const w = node.width ?? 200;
const h = node.height ?? 100;
const color = colorForType(node.type);
const typeName = node.type?.replace(/^EIP_/, '').replace(/_/g, ' ') ?? '';
const label = node.label ? `${typeName}: ${node.label}` : typeName;
return (
<g data-node-id={node.id} transform={`translate(${x}, ${y})`}>
{/* Container body */}
<rect
x={0}
y={0}
width={w}
height={h}
rx={CORNER_RADIUS}
fill="white"
stroke={color}
strokeWidth={1.5}
/>
{/* Colored header bar */}
<rect x={0} y={0} width={w} height={HEADER_HEIGHT} rx={CORNER_RADIUS} fill={color} />
<rect x={CORNER_RADIUS} y={CORNER_RADIUS} width={w - CORNER_RADIUS * 2} height={HEADER_HEIGHT - CORNER_RADIUS} fill={color} />
{/* Header label */}
<text
x={w / 2}
y={HEADER_HEIGHT / 2 + 4}
fill="white"
fontSize={10}
fontWeight={600}
textAnchor="middle"
>
{label}
</text>
{/* Children nodes (positioned relative to compound) */}
{node.children?.map(child => (
<DiagramNode
key={child.id}
node={{
...child,
// Children have absolute coordinates from the backend,
// but since we're inside the compound's translate, subtract parent offset
x: (child.x ?? 0) - x,
y: (child.y ?? 0) - y,
}}
isHovered={hoveredNodeId === child.id}
isSelected={selectedNodeId === child.id}
config={child.id ? nodeConfigs?.get(child.id) : undefined}
onClick={() => child.id && onNodeClick(child.id)}
onMouseEnter={() => child.id && onNodeEnter(child.id)}
onMouseLeave={onNodeLeave}
/>
))}
</g>
);
}