* ParallelNode renders two formats: * - Format 1 (inline steps): parallelSteps[] displayed as a container with NestedStepRenderer * - Format 2 (module+params): module name + SubmoduleInlineViewer (original behaviour)
({ data, selected, id }: NodeProps)
| 14 | * - Format 2 (module+params): module name + SubmoduleInlineViewer (original behaviour) |
| 15 | */ |
| 16 | function ParallelNode({ data, selected, id }: NodeProps) { |
| 17 | const parallelData = data as ParallelNodeData; |
| 18 | const config = NODE_CONFIGS.parallel; |
| 19 | const isFormat1 = parallelData.parallelSteps && parallelData.parallelSteps.length > 0; |
| 20 | const paramsCount = Array.isArray(parallelData.parameters_list) ? parallelData.parameters_list.length : 0; |
| 21 | |
| 22 | const [isDragOver, setIsDragOver] = useState(false); |
| 23 | const { setNodes } = useReactFlow(); |
| 24 | const { selectedInnerStep, selectInnerStep } = useInnerStep(); |
| 25 | const layoutDirection = useLayoutDirection(); |
| 26 | const targetPos = layoutDirection === 'LR' ? Position.Left : Position.Top; |
| 27 | const sourcePos = layoutDirection === 'LR' ? Position.Right : Position.Bottom; |
| 28 | |
| 29 | const parallelSteps = parallelData.parallelSteps || []; |
| 30 | |
| 31 | // ── Drag-drop for Format 2 (module) ────────────────────────────── |
| 32 | const handleModuleDragOver = useCallback((e: React.DragEvent) => { |
| 33 | const moduleData = e.dataTransfer.getData('application/module'); |
| 34 | if (moduleData) { |
| 35 | e.preventDefault(); |
| 36 | e.stopPropagation(); |
| 37 | setIsDragOver(true); |
| 38 | } |
| 39 | }, []); |
| 40 | |
| 41 | const handleModuleDragLeave = useCallback((e: React.DragEvent) => { |
| 42 | e.stopPropagation(); |
| 43 | setIsDragOver(false); |
| 44 | }, []); |
| 45 | |
| 46 | const handleModuleDrop = useCallback((e: React.DragEvent) => { |
| 47 | e.preventDefault(); |
| 48 | e.stopPropagation(); |
| 49 | setIsDragOver(false); |
| 50 | |
| 51 | const moduleData = e.dataTransfer.getData('application/module'); |
| 52 | if (!moduleData) return; |
| 53 | |
| 54 | try { |
| 55 | const module = JSON.parse(moduleData); |
| 56 | setNodes((nodes) => |
| 57 | nodes.map((node) => { |
| 58 | if (node.id === id) { |
| 59 | const currentParams = (node.data as ParallelNodeData).parameters_list || []; |
| 60 | const newParams = currentParams.length === 0 |
| 61 | ? [module.defaultParams || {}] |
| 62 | : currentParams; |
| 63 | return { |
| 64 | ...node, |
| 65 | data: { |
| 66 | ...node.data, |
| 67 | module: module.path, |
| 68 | label: `Parallel: ${module.name}`, |
| 69 | parameters_list: newParams, |
| 70 | parallelSteps: undefined, // switch to Format 2 |
| 71 | moduleContents: undefined, |
| 72 | }, |
| 73 | }; |
nothing calls this directly
no test coverage detected