-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Open
Labels
enhancementNew feature or requestNew feature or requesthelp wantedExtra attention is neededExtra attention is needed
Description
Provide a general summary of the issue here
The documentation says that getKeysForDrag
returns the selected items.
I want to overwrite this function to drag and drop selected items separately.
Even though I overwrite it, drag and drop don't work as expected.
GridList.bug.mp4
Overwriting getKeysForDrag
, full example in "Steps to reproduce"
dragAndDropHooks.useDraggableCollectionState =
function useDraggableCollectionStateOverride(
props: DraggableCollectionStateOpts
) {
const draggableHook = useDraggableCollectionState({
...props,
...options,
} as DraggableCollectionStateOptions);
draggableHook.getKeysForDrag = function (key: string) {
return new Set([key]); //Just the clicked item be a key for drag
};
return draggableHook;
};
🤔 Expected Behavior?
Items of a GridList to be reordered (drag and drop) separately
😯 Current Behavior
All the selected keys are dragged
💁 Possible Solution
I'm not sure, I didn't realize looking at the source code where onReorder
evaluates the event.keys
🔦 Context
No response
🖥️ Steps to Reproduce
Repository
You can just clone and run this repository:
https://github.com/JonRC/react-aria-grid-list-bug
Code
Here is the code used in the repository below if you want reproduce by your own
/* eslint-disable @typescript-eslint/no-empty-object-type */
import {
DraggableCollectionStateOptions,
useDraggableCollectionState,
useListData,
} from "react-stately";
import {
GridList,
GridListItem,
useDragAndDrop,
Checkbox as AriaCheckbox,
CheckboxProps,
DragAndDropOptions,
} from "react-aria-components";
import { useMemo } from "react";
interface DraggableCollectionStateOpts
extends Omit<DraggableCollectionStateOptions, "getItems"> {}
function App() {
const list = useListData({
initialItems: [
{ id: 1, name: "Adobe Photoshop" },
{ id: 2, name: "Adobe XD" },
{ id: 3, name: "Adobe Dreamweaver" },
{ id: 4, name: "Adobe InDesign" },
{ id: 5, name: "Adobe Connect" },
],
});
const options = useMemo<DragAndDropOptions>(
() => ({
getItems: (keys) =>
[...keys].map((key) => ({
"text/plain": list.getItem(key)?.name ?? "",
})),
onReorder(e) {
if (e.target.dropPosition === "before") {
list.moveBefore(e.target.key, e.keys);
} else if (e.target.dropPosition === "after") {
list.moveAfter(e.target.key, e.keys);
}
},
}),
[list]
);
const { dragAndDropHooks } = useDragAndDrop(options);
dragAndDropHooks.useDraggableCollectionState =
function useDraggableCollectionStateOverride(
props: DraggableCollectionStateOpts
) {
const draggableHook = useDraggableCollectionState({
...props,
...options,
} as DraggableCollectionStateOptions);
draggableHook.getKeysForDrag = function (key: string) {
return new Set([key]); //Just the clicked item be a key for drag
};
return draggableHook;
};
return (
<GridList
aria-label="Reorderable list"
selectionMode="multiple"
items={list.items}
dragAndDropHooks={dragAndDropHooks}
style={{ marginLeft: "100px" }}
>
{(item) => (
<GridListItem style={{ display: "flex" }}>
{() => (
<>
<Checkbox slot="selection" />
{item.name}
</>
)}
</GridListItem>
)}
</GridList>
);
}
export function Checkbox(props: CheckboxProps) {
return (
<AriaCheckbox {...props} style={{ display: "flex" }}>
{({ isSelected, isIndeterminate }) => (
<>
<div>{isIndeterminate ? "[ ]" : isSelected ? "[x]" : "[ ]"}</div>
{props.children}
</>
)}
</AriaCheckbox>
);
}
export default App;
Version
"react-aria-components": "^1.8.0"
What browsers are you seeing the problem on?
Chrome
If other, please specify.
No response
What operating system are you using?
Linux, Ubuntu
🧢 Your Company/Team
No response
🕷 Tracking Issue
No response
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requesthelp wantedExtra attention is neededExtra attention is needed
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
JonRC commentedon Apr 29, 2025
Discovered that this behaviour is hardcoded.
Look at react-spectrum/packages/@react-stately/dnd/src
/useDraggableCollectionState.ts:73
I propose to receive an optional parameter called
getKeysForDrag
and replace the name ofgetKeys
withdefaulGetKetsForDrag
and use it as default whengetKeysForDrag
is not passed.I'd like to receive feedback from the contributor. This way, I can implement this improvement if necessary.
LFDanLu commentedon Apr 30, 2025
Mind sharing what overall UX you are trying to accomplish with this? We could perhaps provide the
draggedKey
that is set here togetKeys
so that users can override this behavior like you mentionedJonRC commentedon Apr 30, 2025
I'm trying to drag selected items separately. Look at the video to see how the selected items are dragged together.
I think your idea is a good option.
I will create a pull request implementing it, it's me first contribution here.
LFDanLu commentedon Apr 30, 2025
To elaborate, we understand that you want to drag an item independently from the GridList's selection state, but we are curious what high level user experience you are aiming for (would selection be taken into account for other actions? would the user never be able to drag multiple items at once? what kind of app flow would this be used in).
As for an example of the idea, I was thinking along the lines of https://react-spectrum.adobe.com/react-aria/useDraggableCollection.html#example where perhaps the
getItems
could include thedraggedKey
as one of its args, and thus the user could instead opt to only return that key's data. Not a fully fleshed out idea though since ideally, would need to try it first. Essentially mimics the api we have for renderPreview.