@@ -120,6 +120,19 @@ export function useDragDrop() {
120120 [ ]
121121 )
122122
123+ const calculateFolderDropPosition = useCallback (
124+ ( e : React . DragEvent , element : HTMLElement ) : 'before' | 'inside' | 'after' => {
125+ const rect = element . getBoundingClientRect ( )
126+ const relativeY = e . clientY - rect . top
127+ const height = rect . height
128+ // Top 25% = before, middle 50% = inside, bottom 25% = after
129+ if ( relativeY < height * 0.25 ) return 'before'
130+ if ( relativeY > height * 0.75 ) return 'after'
131+ return 'inside'
132+ } ,
133+ [ ]
134+ )
135+
123136 type SiblingItem = { type : 'folder' | 'workflow' ; id : string ; sortOrder : number }
124137
125138 const getDestinationFolderId = useCallback ( ( indicator : DropIndicator ) : string | null => {
@@ -193,24 +206,34 @@ export function useDragDrop() {
193206
194207 const setNormalizedDropIndicator = useCallback (
195208 ( indicator : DropIndicator | null ) => {
196- if ( ! indicator || indicator . position !== 'after' || indicator . targetId === 'root' ) {
197- setDropIndicator ( indicator )
198- return
199- }
209+ setDropIndicator ( ( prev ) => {
210+ let next : DropIndicator | null = indicator
211+
212+ // Normalize 'after' to 'before' of next sibling
213+ if ( indicator && indicator . position === 'after' && indicator . targetId !== 'root' ) {
214+ const siblings = getSiblingItems ( indicator . folderId )
215+ const currentIdx = siblings . findIndex ( ( s ) => s . id === indicator . targetId )
216+ const nextSibling = siblings [ currentIdx + 1 ]
217+ if ( nextSibling ) {
218+ next = {
219+ targetId : nextSibling . id ,
220+ position : 'before' ,
221+ folderId : indicator . folderId ,
222+ }
223+ }
224+ }
200225
201- const siblings = getSiblingItems ( indicator . folderId )
202- const currentIdx = siblings . findIndex ( ( s ) => s . id === indicator . targetId )
203- const nextSibling = siblings [ currentIdx + 1 ]
204-
205- if ( nextSibling ) {
206- setDropIndicator ( {
207- targetId : nextSibling . id ,
208- position : 'before' ,
209- folderId : indicator . folderId ,
210- } )
211- } else {
212- setDropIndicator ( indicator )
213- }
226+ // Skip update if indicator hasn't changed
227+ if (
228+ prev ?. targetId === next ?. targetId &&
229+ prev ?. position === next ?. position &&
230+ prev ?. folderId === next ?. folderId
231+ ) {
232+ return prev
233+ }
234+
235+ return next
236+ } )
214237 } ,
215238 [ getSiblingItems ]
216239 )
@@ -431,11 +454,19 @@ export function useDragDrop() {
431454 setHoverFolderId ( folderId )
432455 }
433456 } else {
457+ // Workflow being dragged over a folder
434458 const isSameParent = draggedSourceFolderRef . current === parentFolderId
435459 if ( isSameParent ) {
436- const position = calculateDropPosition ( e , e . currentTarget )
460+ // Same level - use three zones: top=before, middle=inside, bottom=after
461+ const position = calculateFolderDropPosition ( e , e . currentTarget )
437462 setNormalizedDropIndicator ( { targetId : folderId , position, folderId : parentFolderId } )
463+ if ( position === 'inside' ) {
464+ setHoverFolderId ( folderId )
465+ } else {
466+ setHoverFolderId ( null )
467+ }
438468 } else {
469+ // Different container - drop into folder
439470 setNormalizedDropIndicator ( {
440471 targetId : folderId ,
441472 position : 'inside' ,
@@ -450,7 +481,14 @@ export function useDragDrop() {
450481 } ,
451482 onDrop : handleDrop ,
452483 } ) ,
453- [ initDragOver , calculateDropPosition , setNormalizedDropIndicator , isLeavingElement , handleDrop ]
484+ [
485+ initDragOver ,
486+ calculateDropPosition ,
487+ calculateFolderDropPosition ,
488+ setNormalizedDropIndicator ,
489+ isLeavingElement ,
490+ handleDrop ,
491+ ]
454492 )
455493
456494 const createEmptyFolderDropZone = useCallback (
@@ -464,6 +502,23 @@ export function useDragDrop() {
464502 [ initDragOver , setNormalizedDropIndicator , handleDrop ]
465503 )
466504
505+ const createFolderContentDropZone = useCallback (
506+ ( folderId : string ) => ( {
507+ onDragOver : ( e : React . DragEvent < HTMLElement > ) => {
508+ e . preventDefault ( )
509+ e . stopPropagation ( ) // Prevent bubbling to root when in gaps between items
510+ lastDragYRef . current = e . clientY
511+ setIsDragging ( true )
512+ // Only set folder highlight if dragging from a different folder
513+ if ( draggedSourceFolderRef . current !== folderId ) {
514+ setNormalizedDropIndicator ( { targetId : folderId , position : 'inside' , folderId : null } )
515+ }
516+ } ,
517+ onDrop : handleDrop ,
518+ } ) ,
519+ [ setNormalizedDropIndicator , handleDrop ]
520+ )
521+
467522 const createRootDropZone = useCallback (
468523 ( ) => ( {
469524 onDragOver : ( e : React . DragEvent < HTMLElement > ) => {
@@ -506,6 +561,7 @@ export function useDragDrop() {
506561 createWorkflowDragHandlers,
507562 createFolderDragHandlers,
508563 createEmptyFolderDropZone,
564+ createFolderContentDropZone,
509565 createRootDropZone,
510566 handleDragStart,
511567 handleDragEnd,
0 commit comments