@@ -120,11 +120,11 @@ function calculateTaskSize(task: BuilderTask): SizeResult {
120120 const maxChildHeight = Math . max ( ...childSizes . map ( ( s ) => s . height ) ) ;
121121 const totalChildWidth = childSizes . reduce ( ( sum , s ) => sum + s . width , 0 ) ;
122122
123- // Lane content height = drop zone + child + drop zone
124- const laneContentHeight = DROP_ZONE_HEIGHT + VERTICAL_GAP + maxChildHeight + VERTICAL_GAP + DROP_ZONE_HEIGHT ;
123+ // Lane content height = just child height (inter-lane gaps handle reordering)
124+ const laneContentHeight = maxChildHeight ;
125125
126- // Total width = padding + all children + gaps + empty lane (if can add more)
127- const lanesWidth = totalChildWidth + ( children . length - 1 ) * LANE_GAP + ( canAddMore ? LANE_GAP + EMPTY_LANE_WIDTH : 0 ) ;
126+ // Total width = inter-lane gaps (one before each lane) + children + empty lane
127+ const lanesWidth = children . length * LANE_GAP + totalChildWidth + ( canAddMore ? LANE_GAP + EMPTY_LANE_WIDTH : 0 ) ;
128128
129129 return {
130130 width : CONTAINER_PADDING_X * 2 + Math . max ( NODE_WIDTH , lanesWidth ) ,
@@ -300,74 +300,48 @@ export function useBuilderGraphLayout(
300300 }
301301 } ) ;
302302 } else if ( isConcurrent && childCount > 0 ) {
303- // Other concurrent tasks: render parallel lanes
303+ // Other concurrent tasks: render parallel lanes with inter-lane drop zones
304304 const childSizes = children . map ( ( child ) => calculateTaskSize ( child ) ) ;
305+ const maxChildHeight = Math . max ( ...childSizes . map ( ( s ) => s . height ) ) ;
305306
306307 let laneX = CONTAINER_PADDING_X ;
307308 const laneTopY = CONTAINER_PADDING_TOP ;
309+ const gapHeight = maxChildHeight ;
308310
309311 for ( let i = 0 ; i < childCount ; i ++ ) {
310312 const child = children [ i ] ;
311313 const childSize = childSizes [ i ] ;
312314
313- // Center child in its lane
314- const laneWidth = childSize . width ;
315- const childX = laneX ;
316- const childY = laneTopY + DROP_ZONE_HEIGHT + VERTICAL_GAP ;
317-
318- // Top drop zone for this lane
319- const topDropId = generateId ( 'drop' ) ;
315+ // Inter-lane drop zone before this lane (for reordering and inserting)
316+ const gapDropId = generateId ( 'drop' ) ;
320317 nodes . push ( {
321- id : topDropId ,
318+ id : gapDropId ,
322319 type : 'dropZone' ,
323- position : { x : childX , y : laneTopY } ,
320+ position : { x : laneX , y : laneTopY } ,
324321 parentNode : nodeId ,
325322 extent : 'parent' ,
326323 draggable : false ,
327324 data : {
328325 dropId : isCleanup ? `cleanup-insert-before-${ child . id } ` : `insert-before-${ child . id } ` ,
326+ isInterLane : true ,
329327 parentTaskId : task . id ,
330328 insertIndex : i ,
331- disabled : false ,
332329 isCleanup,
333330 } as DropZoneNodeData ,
334- style : { width : laneWidth , height : DROP_ZONE_HEIGHT } ,
331+ style : { width : LANE_GAP , height : gapHeight } ,
335332 } ) ;
336333
337- // Process child task
338- const childResult = processTask ( child , childX , childY , nodeId , undefined , isCleanup ) ;
339-
340- // Bottom drop zone for this lane
341- const bottomDropId = generateId ( 'drop' ) ;
342- const bottomY = childY + childSize . height + VERTICAL_GAP ;
343- nodes . push ( {
344- id : bottomDropId ,
345- type : 'dropZone' ,
346- position : { x : childX , y : bottomY } ,
347- parentNode : nodeId ,
348- extent : 'parent' ,
349- draggable : false ,
350- data : {
351- dropId : isCleanup ? `cleanup-insert-after-children-${ child . id } ` : `insert-after-children-${ child . id } ` ,
352- parentTaskId : task . id ,
353- insertIndex : i + 1 ,
354- disabled : true , // Can't insert after in concurrent
355- isCleanup,
356- } as DropZoneNodeData ,
357- style : { width : laneWidth , height : DROP_ZONE_HEIGHT } ,
358- } ) ;
334+ laneX += LANE_GAP ;
359335
360- // Edges for this lane
361- addEdge ( topDropId , childResult . nodeId , edgeColor ) ;
362- addEdge ( childResult . nodeId , bottomDropId , edgeColor ) ;
336+ // Process child task directly in the lane
337+ processTask ( child , laneX , laneTopY , nodeId , undefined , isCleanup ) ;
363338
364- laneX += laneWidth + LANE_GAP ;
339+ laneX += childSize . width ;
365340 }
366341
367342 // Add empty lane for adding new parallel task (if allowed)
368343 if ( canAddMore ) {
369- const maxChildHeight2 = Math . max ( ...childSizes . map ( ( s ) => s . height ) ) ;
370- const emptyLaneHeight = DROP_ZONE_HEIGHT + VERTICAL_GAP + maxChildHeight2 + VERTICAL_GAP + DROP_ZONE_HEIGHT ;
344+ laneX += LANE_GAP ;
371345 const emptyLaneDropId = generateId ( 'drop' ) ;
372346 nodes . push ( {
373347 id : emptyLaneDropId ,
@@ -377,13 +351,13 @@ export function useBuilderGraphLayout(
377351 extent : 'parent' ,
378352 draggable : false ,
379353 data : {
380- dropId : isCleanup ? `cleanup-insert-first-child -${ task . id } ` : `insert-first-child -${ task . id } ` ,
354+ dropId : isCleanup ? `cleanup-insert-after-children -${ task . id } ` : `insert-after-children -${ task . id } ` ,
381355 isHorizontal : true ,
382356 parentTaskId : task . id ,
383357 insertIndex : childCount ,
384358 isCleanup,
385359 } as DropZoneNodeData ,
386- style : { width : EMPTY_LANE_WIDTH , height : emptyLaneHeight } ,
360+ style : { width : EMPTY_LANE_WIDTH , height : gapHeight } ,
387361 } ) ;
388362 }
389363 } else {
0 commit comments