From 16ab6bfba14227f343858e112e5ad8928161b18e Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 25 Apr 2025 18:10:56 -0500 Subject: [PATCH 1/5] support dropping on items --- .../dnd/src/useDroppableCollection.ts | 2 +- .../react-aria-components/example/index.css | 5 +++ packages/react-aria-components/src/Tree.tsx | 34 +++++++++++++------ .../stories/Tree.stories.tsx | 13 +++---- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/packages/@react-aria/dnd/src/useDroppableCollection.ts b/packages/@react-aria/dnd/src/useDroppableCollection.ts index 234793a78e6..fa17d25b8ac 100644 --- a/packages/@react-aria/dnd/src/useDroppableCollection.ts +++ b/packages/@react-aria/dnd/src/useDroppableCollection.ts @@ -139,7 +139,7 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: await onItemDrop({items: filteredItems, dropOperation, isInternal, target}); } - if (target.dropPosition !== 'on') { + if (target.dropPosition !== 'on' || localState.state.collection.getItem(target.key)?.hasChildNodes) { if (!isInternal && onInsert) { await onInsert({items: filteredItems, dropOperation, target}); } diff --git a/packages/react-aria-components/example/index.css b/packages/react-aria-components/example/index.css index 3a56707c261..43a01fb013b 100644 --- a/packages/react-aria-components/example/index.css +++ b/packages/react-aria-components/example/index.css @@ -52,6 +52,11 @@ html { background: lightseagreen; color: white; } + + &[data-drop-target] { + background: purple; + color: white; + } } .menu { diff --git a/packages/react-aria-components/src/Tree.tsx b/packages/react-aria-components/src/Tree.tsx index 290a8bb317a..aac53d9fa66 100644 --- a/packages/react-aria-components/src/Tree.tsx +++ b/packages/react-aria-components/src/Tree.tsx @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import {AriaTreeItemOptions, AriaTreeProps, DraggableItemResult, DropIndicatorProps, DroppableCollectionResult, DroppableItemResult, FocusScope, ListKeyboardDelegate, mergeProps, useCollator, useFocusRing, useGridListSelectionCheckbox, useHover, useLocale, useTree, useTreeItem} from 'react-aria'; +import {AriaTreeItemOptions, AriaTreeProps, DraggableItemResult, DropIndicatorAria, DropIndicatorProps, DroppableCollectionResult, DroppableItemResult, FocusScope, ListKeyboardDelegate, mergeProps, useCollator, useFocusRing, useGridListSelectionCheckbox, useHover, useLocale, useTree, useTreeItem, useVisuallyHidden} from 'react-aria'; import {ButtonContext} from './Button'; import {CheckboxContext} from './RSPContexts'; import {Collection, CollectionBuilder, CollectionNode, createBranchComponent, createLeafComponent, useCachedChildren} from '@react-aria/collections'; @@ -260,6 +260,10 @@ function TreeInner({props, collection, treeRef: ref}: TreeInne { keyboardDelegate, dropTargetDelegate, + shouldAcceptItemDrop: (target) => { + let item = state.collection.getItem(target.key); + return item?.hasChildNodes ?? false; + }, onDropActivate: (e) => { // Expand collapsed item when dragging over if (e.target.type === 'item') { @@ -514,13 +518,17 @@ export const TreeItem = /*#__PURE__*/ createBranchComponent('item', (null); + let {visuallyHiddenProps} = useVisuallyHidden(); if (dropState && dragAndDropHooks) { - droppableItem = dragAndDropHooks.useDroppableItem!({target: {type: 'item', key: item.key, dropPosition: 'on'}}, dropState, ref); + dropIndicator = dragAndDropHooks.useDropIndicator!({ + target: {type: 'item', key: item.key, dropPosition: 'on'} + }, dropState, dropIndicatorRef); } + let isDragging = dragState && dragState.isDragging(item.key); - let isDropTarget = droppableItem?.isDropTarget; let selectionMode = state.selectionManager.selectionMode; let selectionBehavior = state.selectionManager.selectionBehavior; @@ -538,8 +546,8 @@ export const TreeItem = /*#__PURE__*/ createBranchComponent('item', (null); useEffect(() => { if (dragState && !dragButtonRef.current && process.env.NODE_ENV !== 'production') { - console.warn('Draggable items in a Table must contain a