feat: add iteration stepper to compound nodes and thread overlay props
Add a left/right stepper widget to compound node headers (LOOP, SPLIT, MULTICAST) when iteration overlay data is present. Thread executionOverlay, overlayActive, iterationState, and onIterationChange props through ProcessDiagram -> CompoundNode -> children and ProcessDiagram -> ErrorSection -> children so leaf DiagramNode instances render with execution state (green/red badges, dimming for skipped nodes). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import type { DiagramNode as DiagramNodeType, DiagramEdge as DiagramEdgeType } from '../../api/queries/diagrams';
|
||||
import type { NodeConfig } from './types';
|
||||
import type { NodeExecutionState } from '../ExecutionDiagram/types';
|
||||
import type { NodeExecutionState, IterationInfo } from '../ExecutionDiagram/types';
|
||||
import { colorForType, isCompoundType } from './node-colors';
|
||||
import { DiagramNode } from './DiagramNode';
|
||||
import { DiagramEdge } from './DiagramEdge';
|
||||
import styles from './ProcessDiagram.module.css';
|
||||
|
||||
const HEADER_HEIGHT = 22;
|
||||
const CORNER_RADIUS = 4;
|
||||
@@ -20,6 +21,12 @@ interface CompoundNodeProps {
|
||||
nodeConfigs?: Map<string, NodeConfig>;
|
||||
/** Execution overlay for edge traversal coloring */
|
||||
executionOverlay?: Map<string, NodeExecutionState>;
|
||||
/** Whether an execution overlay is active (enables dimming of skipped nodes) */
|
||||
overlayActive?: boolean;
|
||||
/** Per-compound iteration state */
|
||||
iterationState?: Map<string, IterationInfo>;
|
||||
/** Called when user changes iteration on a compound stepper */
|
||||
onIterationChange?: (compoundNodeId: string, iterationIndex: number) => void;
|
||||
onNodeClick: (nodeId: string) => void;
|
||||
onNodeDoubleClick?: (nodeId: string) => void;
|
||||
onNodeEnter: (nodeId: string) => void;
|
||||
@@ -29,6 +36,7 @@ interface CompoundNodeProps {
|
||||
export function CompoundNode({
|
||||
node, edges, parentX = 0, parentY = 0,
|
||||
selectedNodeId, hoveredNodeId, nodeConfigs, executionOverlay,
|
||||
overlayActive, iterationState, onIterationChange,
|
||||
onNodeClick, onNodeDoubleClick, onNodeEnter, onNodeLeave,
|
||||
}: CompoundNodeProps) {
|
||||
const x = (node.x ?? 0) - parentX;
|
||||
@@ -40,6 +48,8 @@ export function CompoundNode({
|
||||
const color = colorForType(node.type);
|
||||
const typeName = node.type?.replace(/^EIP_/, '').replace(/_/g, ' ') ?? '';
|
||||
const label = node.label ? `${typeName}: ${node.label}` : typeName;
|
||||
const iterationInfo = node.id ? iterationState?.get(node.id) : undefined;
|
||||
const headerWidth = w;
|
||||
|
||||
// Collect all descendant node IDs to filter edges that belong inside this compound
|
||||
const descendantIds = new Set<string>();
|
||||
@@ -79,6 +89,27 @@ export function CompoundNode({
|
||||
{label}
|
||||
</text>
|
||||
|
||||
{/* Iteration stepper (for LOOP, SPLIT, MULTICAST with overlay data) */}
|
||||
{iterationInfo && (
|
||||
<foreignObject x={headerWidth - 80} y={1} width={75} height={20}>
|
||||
<div className={styles.iterationStepper}>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onIterationChange?.(node.id!, iterationInfo.current - 1); }}
|
||||
disabled={iterationInfo.current <= 0}
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<span>{iterationInfo.current + 1} / {iterationInfo.total}</span>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onIterationChange?.(node.id!, iterationInfo.current + 1); }}
|
||||
disabled={iterationInfo.current >= iterationInfo.total - 1}
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
</foreignObject>
|
||||
)}
|
||||
|
||||
{/* Internal edges (rendered after background, before children) */}
|
||||
<g className="edges">
|
||||
{internalEdges.map((edge, i) => {
|
||||
@@ -112,6 +143,9 @@ export function CompoundNode({
|
||||
hoveredNodeId={hoveredNodeId}
|
||||
nodeConfigs={nodeConfigs}
|
||||
executionOverlay={executionOverlay}
|
||||
overlayActive={overlayActive}
|
||||
iterationState={iterationState}
|
||||
onIterationChange={onIterationChange}
|
||||
onNodeClick={onNodeClick}
|
||||
onNodeDoubleClick={onNodeDoubleClick}
|
||||
onNodeEnter={onNodeEnter}
|
||||
@@ -130,6 +164,8 @@ export function CompoundNode({
|
||||
isHovered={hoveredNodeId === child.id}
|
||||
isSelected={selectedNodeId === child.id}
|
||||
config={child.id ? nodeConfigs?.get(child.id) : undefined}
|
||||
executionState={executionOverlay?.get(child.id ?? '')}
|
||||
overlayActive={overlayActive}
|
||||
onClick={() => child.id && onNodeClick(child.id)}
|
||||
onDoubleClick={() => child.id && onNodeDoubleClick?.(child.id)}
|
||||
onMouseEnter={() => child.id && onNodeEnter(child.id)}
|
||||
|
||||
Reference in New Issue
Block a user