@@ -118,39 +118,66 @@ export const FlowEditor = ({ projectId }: { projectId: number }) => {
118118  const  onNodesChange  =  useCallback ( 
119119    ( changes : NodeChange [ ] )  =>  { 
120120      setNodes ( ( nds )  =>  { 
121-         const  nextNodes  =  applyNodeChanges ( changes ,  nds ) ; 
122-         console . log ( 'nextNodes' ,  changes ,  nextNodes ) ; 
121+         let  nextNodes  =  applyNodeChanges ( changes ,  nds ) ; 
123122
123+         // Handle node dragging for grouping/ungrouping 
124124        changes . forEach ( ( change )  =>  { 
125-           if  ( change . type  ===  'position'  &&  'id'  in  change )  { 
126-             const  node  =  nextNodes . find ( ( n )  =>  n . id  ===  change . id ) ; 
127-             if  ( node  &&  node . type  &&  ! isGroupType ( node . type ) )  { 
125+           if  ( change . type  ===  'position' )  { 
126+             const  draggedNode  =  nextNodes . find ( ( n )  =>  n . id  ===  change . id ) ; 
127+             if  ( ! draggedNode  ||  ( draggedNode . type  &&  isGroupType ( draggedNode . type ) ) )  { 
128+               return ; 
129+             } 
130+ 
131+             // Calculate absolute position for the node 
132+             const  absolutePosition  =  {  ...draggedNode . position  } ; 
133+             const  oldParent  =  draggedNode . parentId  ? nextNodes . find ( n  =>  n . id  ===  draggedNode . parentId )  : null ; 
134+             if  ( oldParent )  { 
135+               absolutePosition . x  +=  oldParent . position . x ; 
136+               absolutePosition . y  +=  oldParent . position . y ; 
137+             } 
138+ 
139+             // During dragging, update the node's position to be absolute 
140+             if  ( 'dragging'  in  change  &&  change . dragging )  { 
141+               draggedNode . position  =  absolutePosition ; 
142+               draggedNode . parentId  =  undefined ; 
143+               draggedNode . extent  =  undefined ; 
144+               return ; 
145+             } 
146+ 
147+             // When drag ends, handle grouping/ungrouping 
148+             if  ( 'dragging'  in  change  &&  ! change . dragging )  { 
149+               // Find potential group node at the dragged node's absolute center position 
150+               const  nodeCenterPosition  =  { 
151+                 x : absolutePosition . x  +  ( draggedNode . width  ??  0 )  /  2 , 
152+                 y : absolutePosition . y  +  ( draggedNode . height  ??  0 )  /  2 , 
153+               } ; 
154+ 
155+               // Find any group node that contains this position 
128156              const  groupNode  =  nextNodes . find ( 
129157                ( n )  => 
130-                   n . type  &&  isGroupType ( n . type )  && 
131-                   isPositionInsideNode ( 
132-                     { 
133-                       x : node . position . x  +  ( node . width  ??  0 )  /  2 , 
134-                       y : node . position . y  +  ( node . height  ??  0 )  /  2 , 
135-                     } , 
136-                     n 
137-                   ) 
158+                   n . type  ===  'groupchat'  && 
159+                   n . id  !==  draggedNode . id  && 
160+                   ! n . parentId  &&  // Prevent nested groups 
161+                   nodeCenterPosition . x  >  n . position . x  && 
162+                   nodeCenterPosition . x  <  ( n . position . x  +  ( n . width  ??  0 ) )  && 
163+                   nodeCenterPosition . y  >  n . position . y  && 
164+                   nodeCenterPosition . y  <  ( n . position . y  +  ( n . height  ??  0 ) ) 
138165              ) ; 
166+ 
167+               // Handle grouping - if inside a group 
139168              if  ( groupNode )  { 
140-                 node . parentId  =  groupNode . id ; 
141-                 node . position  =  { 
142-                   x : node . position . x  -  groupNode . position . x , 
143-                   y : node . position . y  -  groupNode . position . y , 
169+                 // Node is entering a group 
170+                 draggedNode . parentId  =  groupNode . id ; 
171+                 draggedNode . position  =  { 
172+                   x : nodeCenterPosition . x  -  groupNode . position . x , 
173+                   y : nodeCenterPosition . y  -  groupNode . position . y , 
144174                } ; 
145-                 // Move the node to be after the group node in the array 
146-                 const  nodeIndex  =  nextNodes . indexOf ( node ) ; 
147-                 const  groupNodeIndex  =  nextNodes . indexOf ( groupNode ) ; 
148-                 if  ( nodeIndex  >  groupNodeIndex )  { 
149-                   nextNodes . splice ( nodeIndex ,  1 ) ; 
150-                   nextNodes . splice ( groupNodeIndex  +  1 ,  0 ,  node ) ; 
151-                 } 
152-               }  else  { 
153-                 node . parentId  =  undefined ; 
175+                 draggedNode . extent  =  'parent' ; 
176+ 
177+                 // Ensure group node is before its children 
178+                 nextNodes  =  nextNodes . filter ( n  =>  n . id  !==  draggedNode . id ) ; 
179+                 const  groupIndex  =  nextNodes . findIndex ( n  =>  n . id  ===  groupNode . id ) ; 
180+                 nextNodes . splice ( groupIndex  +  1 ,  0 ,  draggedNode ) ; 
154181              } 
155182            } 
156183          } 
@@ -218,18 +245,7 @@ export const FlowEditor = ({ projectId }: { projectId: number }) => {
218245    ( event : React . DragEvent )  =>  { 
219246      event . preventDefault ( ) ; 
220247      const  data  =  JSON . parse ( event . dataTransfer . getData ( 'application/json' ) ) ; 
221-       console . log ( 'onDrop' ,  data ) ; 
222-       if  ( ! data )  { 
223-         console . warn ( 'No data found in onDrop' ) ; 
224-         return ; 
225-       } 
226- 
227-       if  ( ! flowParent . current )  { 
228-         console . warn ( 
229-           'Unexpected null value of flowParent, drag & drop failed.' 
230-         ) ; 
231-         return ; 
232-       } 
248+       if  ( ! data  ||  ! flowParent . current )  return ; 
233249
234250      const  flowBounds  =  flowParent . current . getBoundingClientRect ( ) ; 
235251      const  position  =  screenToFlowPosition ( { 
@@ -239,16 +255,18 @@ export const FlowEditor = ({ projectId }: { projectId: number }) => {
239255
240256      const  {  offsetX,  offsetY,  ...cleanedData  }  =  data ; 
241257      const  newId  =  nanoid ( ) ; 
242-       const  newNode :  Node  =  { 
258+       const  newNode  =  { 
243259        id : `node-${ data . id }  -${ newId }  ` , 
244260        type : data . id , 
245261        position, 
246262        data : cleanedData , 
247-         selected : true , 
248-         dragHandle : '.drag-handle' , 
263+         selected : false , 
264+         draggable : true , 
265+         selectable : true , 
266+         focusable : true , 
249267        parentId : undefined , 
250268        style : undefined , 
251-       } ; 
269+       }   satisfies   Node ; 
252270
253271      // Find if we're dropping into a group node 
254272      const  groupNode  =  nodes . find ( 
@@ -260,32 +278,38 @@ export const FlowEditor = ({ projectId }: { projectId: number }) => {
260278          position . y  <  ( n . position . y  +  ( n . height  ??  0 ) ) 
261279      ) ; 
262280
263-       if  ( groupNode )  { 
264-         // Adjust position to be relative to the group node 
265-         newNode . position  =  { 
266-           x : position . x  -  groupNode . position . x , 
267-           y : position . y  -  groupNode . position . y , 
268-         } ; 
269-         newNode . parentId  =  groupNode . id ; 
270-         // If the node being dropped is also a group, adjust its size 
271-         if  ( data . id  ===  'groupchat' )  { 
272-           newNode . style  =  { 
273-             width : Math . min ( 300 ,  groupNode . width !  -  50 ) , 
274-             height : Math . min ( 200 ,  groupNode . height !  -  50 ) 
281+       setNodes ( ( nds )  =>  { 
282+         const  updatedNodes  =  nds . map ( ( n )  =>  ( {  ...n ,  selected : false  } ) ) ; 
283+ 
284+         if  ( groupNode )  { 
285+           // Adjust position to be relative to the group node 
286+           const  updatedNode  =  { 
287+             ...newNode , 
288+             position : { 
289+               x : position . x  -  groupNode . position . x , 
290+               y : position . y  -  groupNode . position . y , 
291+             } , 
292+             parentId : groupNode . id , 
293+             // If the node being dropped is also a group, adjust its size 
294+             ...( data . id  ===  'groupchat'  ? { 
295+               style : { 
296+                 width : Math . min ( 300 ,  groupNode . width !  -  50 ) , 
297+                 height : Math . min ( 200 ,  groupNode . height !  -  50 ) 
298+               } 
299+             }  : { } ) 
275300          } ; 
301+ 
302+           // Insert new node right after its parent group 
303+           const  groupIndex  =  updatedNodes . findIndex ( n  =>  n . id  ===  groupNode . id ) ; 
304+           if  ( groupIndex  !==  - 1 )  { 
305+             updatedNodes . splice ( groupIndex  +  1 ,  0 ,  updatedNode ) ; 
306+             return  updatedNodes ; 
307+           } 
276308        } 
277-       } 
278309
279-       setNodes ( ( nds )  => 
280-         nds . map ( ( n )  =>  ( {  ...n ,  selected : false  } ) ) 
281-           . concat ( { 
282-             ...newNode , 
283-             selected : false , 
284-             draggable : true , 
285-             selectable : true , 
286-             focusable : true , 
287-           } ) 
288-       ) ; 
310+         // If not in a group or group not found, just append to the end 
311+         return  [ ...updatedNodes ,  newNode ] ; 
312+       } ) ; 
289313    } , 
290314    [ nodes ,  screenToFlowPosition ,  setNodes ,  flowParent ] 
291315  ) ; 
0 commit comments