diff --git a/packages/@react-stately/layout/src/ListLayout.ts b/packages/@react-stately/layout/src/ListLayout.ts index eccd19653c5..a2ae2b68a7d 100644 --- a/packages/@react-stately/layout/src/ListLayout.ts +++ b/packages/@react-stately/layout/src/ListLayout.ts @@ -585,7 +585,7 @@ export class ListLayout exte let layoutInfo = this.getLayoutInfo(target.key)!; let rect: Rect; if (target.dropPosition === 'before') { - rect = new Rect(layoutInfo.rect.x, layoutInfo.rect.y - this.dropIndicatorThickness / 2, layoutInfo.rect.width, this.dropIndicatorThickness); + rect = new Rect(layoutInfo.rect.x, Math.max(0, layoutInfo.rect.y - this.dropIndicatorThickness / 2), layoutInfo.rect.width, this.dropIndicatorThickness); } else if (target.dropPosition === 'after') { // Render after last visible descendant of the drop target. let targetNode = this.collection.getItem(target.key); diff --git a/packages/react-aria-components/src/DragAndDrop.tsx b/packages/react-aria-components/src/DragAndDrop.tsx index d0f0d8fcb36..f5bfb0e6e61 100644 --- a/packages/react-aria-components/src/DragAndDrop.tsx +++ b/packages/react-aria-components/src/DragAndDrop.tsx @@ -75,7 +75,26 @@ export function useDndPersistedKeys(selectionManager: MultipleSelectionManager, dropTargetKey = dropState.target.key; if (dropState.target.dropPosition === 'after') { // Normalize to the "before" drop position since we only render those to the DOM. - dropTargetKey = dropState.collection.getKeyAfter(dropTargetKey) ?? dropTargetKey; + let nextKey = dropState.collection.getKeyAfter(dropTargetKey); + if (nextKey != null) { + let targetLevel = dropState.collection.getItem(dropTargetKey)?.level ?? 0; + // Skip over any rows that are descendants of the target ("after" position should be after all children) + while (nextKey) { + let node = dropState.collection.getItem(nextKey); + // eslint-disable-next-line max-depth + if (!node) { + break; + } + // Stop once we find a node at the same level or higher + // eslint-disable-next-line max-depth + if ((node.level ?? 0) <= targetLevel) { + break; + } + nextKey = dropState.collection.getKeyAfter(nextKey); + } + } + + dropTargetKey = nextKey ?? dropTargetKey; } }