) {
@@ -859,6 +964,7 @@ function TreeDropIndicator(props: TreeDropIndicatorProps, ref: ForwardedRef}
data-drop-target={isDropTarget || undefined}>
diff --git a/packages/react-aria-components/stories/Tree.stories.tsx b/packages/react-aria-components/stories/Tree.stories.tsx
index f089d0ec971..0f42ba4fbb3 100644
--- a/packages/react-aria-components/stories/Tree.stories.tsx
+++ b/packages/react-aria-components/stories/Tree.stories.tsx
@@ -567,9 +567,15 @@ function TreeDragAndDropExample(args) {
let getItems = (keys) => [...keys].map(key => {
let item = treeData.getItem(key)!;
+
+ let serializeItem = (nodeItem) => ({
+ ...nodeItem.value,
+ childItems: nodeItem.children ? [...nodeItem.children].map(serializeItem) : []
+ });
+
return {
'text/plain': item.value.name,
- 'tree-item': JSON.stringify(item.value)
+ 'tree-item': JSON.stringify(serializeItem(item))
};
});
@@ -626,7 +632,7 @@ function SecondTree(args) {
getChildren: item => item.childItems
});
- let getItems = async (e) => {
+ let processIncomingItems = async (e) => {
return await Promise.all(e.items.filter(isTextDropItem).map(async item => {
let parsed = JSON.parse(await item.getText('tree-item'));
let convertItem = item => ({
@@ -638,10 +644,26 @@ function SecondTree(args) {
}));
};
+ let getItems = (keys) => [...keys].map(key => {
+ let item = treeData.getItem(key)!;
+
+ let serializeItem = (nodeItem) => ({
+ ...nodeItem.value,
+ childItems: nodeItem.children ? [...nodeItem.children].map(serializeItem) : []
+ });
+
+ return {
+ 'text/plain': item.value.name,
+ 'tree-item': JSON.stringify(serializeItem(item))
+ };
+ });
+
let {dragAndDropHooks} = useDragAndDrop({
+ getItems, // Enable dragging FROM this tree
+ getAllowedDropOperations: () => ['move'],
acceptedDragTypes: ['tree-item'],
async onInsert(e) {
- let items = await getItems(e);
+ let items = await processIncomingItems(e);
if (e.target.dropPosition === 'before') {
treeData.insertBefore(e.target.key, ...items);
} else if (e.target.dropPosition === 'after') {
@@ -649,12 +671,35 @@ function SecondTree(args) {
}
},
async onItemDrop(e) {
- let items = await getItems(e);
+ let items = await processIncomingItems(e);
treeData.insert(e.target.key, 0, ...items);
},
async onRootDrop(e) {
- let items = await getItems(e);
+ let items = await processIncomingItems(e);
treeData.insert(null, 0, ...items);
+ },
+ [args.dropFunction]: (e: DroppableCollectionReorderEvent) => {
+ console.log(`moving [${[...e.keys].join(',')}] ${e.target.dropPosition} ${e.target.key} in SecondTree`);
+ try {
+ if (e.target.dropPosition === 'before') {
+ treeData.moveBefore(e.target.key, e.keys);
+ } else if (e.target.dropPosition === 'after') {
+ treeData.moveAfter(e.target.key, e.keys);
+ } else if (e.target.dropPosition === 'on') {
+ let targetNode = treeData.getItem(e.target.key);
+ if (targetNode) {
+ let targetIndex = targetNode.children ? targetNode.children.length : 0;
+ let keyArray = Array.from(e.keys);
+ for (let i = 0; i < keyArray.length; i++) {
+ treeData.move(keyArray[i], e.target.key, targetIndex + i);
+ }
+ } else {
+ console.error('Target node not found for drop on:', e.target.key);
+ }
+ }
+ } catch (error) {
+ console.error(error);
+ }
}
});