Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dragging multiple elements #14

Closed
gaearon opened this issue Oct 25, 2014 · 32 comments
Closed

Support dragging multiple elements #14

gaearon opened this issue Oct 25, 2014 · 32 comments

Comments

@gaearon
Copy link
Member

gaearon commented Oct 25, 2014

There is a common pattern that is hard to implement with native drag and drop: dragging multiple elements. While selection mechanics might vary from app to app (cmd+clicking, checkboxes, predefined groups), but it would be nice to at least make it possible to support this scenario.

screen shot 2014-10-26 at 1 07 07

Because multiple dragged items may not be siblings in DOM and setDragImage(element, x, y) is pretty much insane and hasn't gotten any better, we won't burden ourselves with trying to render several elements in drag preview at once.

How can we help implement this scenario, if we can't show a "multiple" drag preview?

In a way, this scenario is already possible: consumers can manually keep track of selected elements, set dragPreview to some kind of generic placeholder Image and react appropriately to the dropping of (as far as business logic is concerned) several elements.

However there is currently no supported way for one element to know that it's part of a group that's being dragged. From react-dnd's point of view, if we drag something, this component gets getDragState(type).isDragging = true, but other components don't. From your app point of view, if you support multiple selection, you want all logically "selected" items to be aware that they're being dragged, even if only one of them is being actually "DOM-dragged".

What we need is a way for components to tell react-dnd that, “hey, although onDragStart was received by another component, I want to pretend that I'm being dragged too, and have my getDragState(type) mirror dragged component's getDragState(type), and have my endDrag(didDrop) called too so I could do my stuff”.

How would components opt into that?

@gaearon
Copy link
Member Author

gaearon commented Feb 8, 2015

Not a priority. We can revisit after introducing drag source keys (#38 (comment)).

@gaearon gaearon closed this as completed Feb 8, 2015
@gaearon gaearon added this to the v1.x milestone Feb 8, 2015
@gaearon gaearon reopened this Feb 8, 2015
@nelix
Copy link
Collaborator

nelix commented Feb 9, 2015

I'll be interested in this down the road, although I think you might be able to just wrap a section of your component tree with a component that has a dragSource...
Like if you hold shift and click, it expands what sections of the array are copied into the dragItem or something?

@gaearon
Copy link
Member Author

gaearon commented Feb 9, 2015

Kinda.

First we learn to pass null component (needed for #38) and a different component with same key (needed for #53).

Then we add groupKey which is optional. Like key, it is specified in drag source and is a string. When you begin dragging something with a groupKey, we will call beginDrag not just on the caller, but on every mounted drag source with the same groupKey, and gather their items into an array. We will pass these items instead of item to drop target methods. When dragging is over, we will call acceptDrop on drop target with an item array, and then call each drag source's endDrag with their item.

What's fun is that we can draw DragLayer with a custom “composite” thumbnail component and a counter like I screenshotted above because we know the count from items.length.

@nelix
Copy link
Collaborator

nelix commented Feb 9, 2015

Thats a way less ugly idea than what I had in mind

@gre
Copy link
Contributor

gre commented Mar 11, 2015

👍 for this feature :-)

kkga pushed a commit that referenced this issue Apr 21, 2015
Update README to include css in getting started
@gaearon
Copy link
Member Author

gaearon commented Sep 13, 2015

Closing, as I don't currently have a use case for this, and it's a significant implementation effort.

@gaearon gaearon closed this as completed Sep 13, 2015
@gaearon gaearon removed this from the v1.x milestone Sep 13, 2015
@jedwards1211
Copy link
Contributor

@gaearon

I used Java/Swing DnD back in the day, and they had probably an army of developers who grappled with this problem and came up with a pretty refined, extremely generic and flexible framework. I would recommend researching old desktop frameworks as they probably solved all the difficult challenges with making a good DnD framework like this.

The way to do this in Java/Swing (although it had no built-in support for drag images) was to make the tree view itself the drag source, and make it able to decide whether or not a drag should be initiated based on its current state and the drag start position. Is monitor.getInitialSourceClientOffset() available when canDrag and beginDrag are called? People will definitely want it to be available. They will probably also want modifier keys (ctrl, alt, shift) to be available and to be able to make drag sources specify whether to move or copy, and drop targets to specify whether they support move and/or copy, based upon any of these factors, because that is possible in Java/Swing and I'm guessing other frameworks. Swing also had the concept of drag gesture recognizers that could be swapped out.

I'd predict that treating all the items in the list as individual draggables will just be difficult compared to having the list component be the single drag source that figures out what kind of drop payload / preview image to construct based upon what's selected in it.

Consider also that a developer might want to change the order of the items being dragged based upon which item the user clicked and dragged from. I believe Windows Explorer behaves this way.

@danii1
Copy link

danii1 commented Dec 24, 2015

Has anyone implemented dragging multiple items with react-dnd in their applications? What's your current solution for this?

@nayemmajhar
Copy link

did you mean multiple drop target for single drag source?

@stouchardIpso
Copy link

+1 @danii1 : Has anyone implemented dragging multiple items ?

@danii1
Copy link

danii1 commented Sep 8, 2016

I implemented it, solution is basically what is described in the first post:

  1. Keep track of your selected items
  2. Implement CustomDragLayer (see https://gaearon.github.io/react-dnd/docs-drag-layer.html) and render your selected items yourself
  3. Handle drop if dropped item is part of the selection

@serle
Copy link

serle commented Nov 23, 2016

I implemented it by:

  1. Keep track of selected items in a Set
  2. Pass this as a property to the dnd Source
  3. In beginDrag retrieve the Set form the props and return it as the drag item
  4. Create a custom drag preview
  5. On EndDrag move all the items in the Set into the dnd Target

@thessem
Copy link

thessem commented Mar 18, 2017

I have a slightly different use case for this I think, I want to draw nodes that are draggable with edges that aren't draggable but do move when nodes they're attached to move. My current implementation is a CustomDragLayer that draws the edges when it detects a DraggableNode is moving (also drawn on the CustomDragLayer but I think it might be better to model it as a multiple drag situation by implementing all edges as DragSources with isDragging() implemented to detect if a node they're attached to is moving so that they can draw themselves instead of having the CustomDragLayer inject the coordinates as props.

@neha-brahmankar
Copy link

@danii1
Can you please share the one you have implemented for a reference

@nayemmajhar
Copy link

I will write a post and add repo about multiple and nested drag and drop with react-dnd + redux.

@pavel06081991
Copy link

@nayemmajhar, it would be great

@paulius005
Copy link

@nayemmajhar any update on an example with this?

@nayemmajhar
Copy link

nayemmajhar commented Jun 16, 2017

I have developed this application using react DnD with reduxJS long ago https://www.joomshaper.com/page-builder I am writing a tutorial but need time publish

@ianmclean2011
Copy link

@serle Would you be able to share a code example of how you did this?

@paulius005
Copy link

@ianmclean2011 check it out here: #590

@ianmclean2011
Copy link

I'm trying to implement multi-drag in an example similar to the sortable example here: https://react-dnd.github.io/react-dnd/examples-sortable-simple.html. I want to be able to select any objects, and then move them as a unit up and down the list. I'm having trouble wrapping my head around how to use the examples/info shared above to do this. Any thoughts/ideas/feedback on how this might work?

@melvynhills
Copy link

melvynhills commented Mar 1, 2018

If anyone's interested, I implemented a multiple drag / grid view: https://codesandbox.io/s/9j897k0mwy.
You can use cmd/ctrl and shift keys to select multiple items, then drag them around the grid and insert them anywhere.

This is still a demo, but I might abstract the code a little bit more and package it in a component later.

@jmcrthrs
Copy link

jmcrthrs commented Mar 1, 2018

@melvynhills Have you investigated a solution that doesn't require passing all the selected cards to each Card component? I don't think there is a solution considering the Card's beginDrag() needs a reference to all selected Cards.

@melvynhills
Copy link

@jmcrthrs No, it doesn't seem possible to me with react-dnd API. I don't think there's a way to know before dragging which specific item is going to be dragged. That's also the only way to know whether the dragged item is within the multiple selection you made, or if it's outside of that selection that we then need to replace.

@jedwards1211
Copy link
Contributor

@melvynhills @jmcrthrs other desktop dnd frameworks I've used rely on making lists and tables the drag sources/drop targets instead of making each individual row, cell, item etc. its own drag source and/or drop target. The implementation for dragging multiple items, as well as getting the specific drop location within the list or table, comes out a lot cleaner that way. When a list receives a drag start event it can simply look at its current selected items.

@melvynhills
Copy link

@jedwards1211 do you think it's doable with this library? Especially the use-case of starting dragging an item that wasn't selected before the drag start. I'm not sure it's the way react-dnd is supposed to work.

@piotrpawlik
Copy link

piotrpawlik commented Nov 27, 2019

Does anybody solved case in which there are multiple drop targets?
Let's say one is dragging multiple items (using one of the solutions above) and one item ends up on one type of drop target and the other on the different one. drop will be triggered just for drag source which initiated drag, but not for the other one. Any ideas how to trigger drop individually for each item being moved.
Example:
Is development slow, Online Whiteboard for Visual Collaboration 2019-11-27 10-45-33

EDIT: Created separate issue for this one #1650

@jedwards1211
Copy link
Contributor

jedwards1211 commented Nov 27, 2019

@melvynhills probably if a mousedown handler triggers item selection (which you should be doing, all software with DnD I have used works this way), it will work just fine with React DnD for the container component to be the drag source, because the drag events will come after mousedown.

@jedwards1211
Copy link
Contributor

Have you investigated a solution that doesn't require passing all the selected cards to each Card component?

This is an example of why I would recommend making the container component the drag source instead of the individual items. I haven't needed to implement multiselect with React DnD but maybe sometime I can make a sandbox to convince folks it's the more maintainable approach

@abhidnyag
Copy link

Has anyone implemented dragging multiple items with react-beautiful-dnd in their applications? What's your current solution for this?

@znwang25
Copy link

znwang25 commented Dec 6, 2020

@abhidnyag Here is my current solution with react-dnd based on @melvynhills's codesandbox. I rewrote the code using new APIs and react hooks.

You can try the live demo here.

@aniketADG
Copy link

aniketADG commented Jul 7, 2022

Feature request : Hover on target image makes copy of selected images which is given in below image
@znwang25 and @melvynhills Is it possible if I drag multiple images and hover on another image then that all selected drag images get paste another image.
Something like multiple image selection and upload
image
check above image in that green note indicate image 4 to 22 is copy inside that image.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests