react-beautiful-dnd
react-beautiful-dnd copied to clipboard
Impossible to use dynamic `Droppable` in recursive structure
Hello !
I'm trying to create a DND recursive tree. Each leaf of that tree get two Droppable
components with different type
. The Draggable
pieces of the first one are very simple and I do not have any problem moving them. However, the Draggable
pieces of the second one are new leaves on this tree and it seems that it is not possible to put leaves in these dynamic pieces : https://codesandbox.io/s/react-beautiful-dnd-recursive-c7he2
Expected behavior
Dynamic Droppable
in recursive DND tree can receive Draggable
pieces.
Actual behavior
It is not possible to move piece
in these dynamic Droppable
Steps to reproduce
- Open https://codesandbox.io/s/react-beautiful-dnd-recursive-c7he2
- Add new
lot
(red) in the top level - Try to move "NEW"
lot
in "OLD" one : It's working - Try to move "OLD"
lot
in "NEW" one : It's not working
Suggested solution?
No idea, sorry 🧐
What version of React
are you using?
17.0.1
What version of react-beautiful-dnd
are you running?
13.0.0
What browser are you using?
Chrome 86.0.4240.183
Demo
https://codesandbox.io/s/react-beautiful-dnd-recursive-c7he2
I am experiencing the same issue with a thrice-nested object. As you can see in moulinraphael's CodeSandbox, at three levels of nesting, dragging a component experiences pretty significant graphical glitches.
I am also experiencing the same. Has anyone found a work around?
I have been looking into this, and it seems to be something wrong with detecting the "furthest" droppable target to a draggable. I believe it's a problem with this function in get-droppable-over.js:
function getFurthestAway({
pageBorderBox,
draggable,
candidates,
}: GetFurthestArgs): ?DroppableId {
// We are not comparing the center of the home list with the target list as it would
// give preference to giant lists
// We are measuring the distance from where the draggable started
// to where it is *hitting* the candidate
// Note: The hit point might technically not be in the bounds of the candidate
const startCenter: Position = draggable.page.borderBox.center;
const sorted: WithDistance[] = candidates
.map((candidate: DroppableDimension): WithDistance => {
const axis: Axis = candidate.axis;
const target: Position = patch(
candidate.axis.line,
// use the current center of the dragging item on the main axis
pageBorderBox.center[axis.line],
// use the center of the list on the cross axis
candidate.page.borderBox.center[axis.crossAxisLine],
);
return {
id: candidate.descriptor.id,
distance: distance(startCenter, target),
};
})
// largest value will be first
.sort((a: WithDistance, b: WithDistance) => b.distance - a.distance);
// just being safe
return sorted[0] ? sorted[0].id : null;
}
I'm stepping through it to debug it right now, and even though it has the correct "drop" candidates, it seems to think they're all equidistant from the start of the drag, at least in my code.
Huh. Okay for me it was because the Droppables had the same x-value. And the nested lists were all aligned along the y-axis. I fixed it by adding a margin to each level.
It looks like the function above is called when it returns multiple targets:
// Multiple options returned
// Should only occur with really large items
// Going to use fallback: distance from home
return getFurthestAway({
pageBorderBox,
draggable,
candidates,
});
Since the function in my previous comment only takes the Droppable's cross-axis into account when judging distance, it doesn't actually do any sorting of the "candidates" list and just returns whichever one is at index 0.
In my case, it's better to show each different level at a deeper x-position for clarity, but it does strike me that stacked nested droppables on top of each other is a valid case that should be supported. I haven't tried any alternative approaches to distance, but it seems like sorting the candidates by how deep they are in a DOM branch would be better
I have the same issue, the dynamic aspect of the bug comes from the candidates
order which is calculated by doing Object.values(droppables)
.
Assuming the following nested structure within a parent Droppable:
- Fruits (Droppable)
- Apple
- Pear
- Grains (Droppable)
- Barley
- Rice
When dragging Barley
between Fruits
and Grains
: toDroppableList(droppables)
will be [Fruits, Grains]
. Since Fruits
is first, it will be selected and it will be easy to move the item up.
If we drag Grains
up, the list looks like:
- Grains (Droppable)
- Barley
- Rice
- Fruits (Droppable)
- Apple
- Pear
When dragging Apple
between Fruits
and Grains
: toDroppableList(droppables)
will be [Fruits, Grains]
(The last added/moved droppable moves to the end). In this case,Fruits
will be selected and the Grains
droppable doesn't get notified until the dragged item is completely over the Grains
droppable, making the move harder.
Hi!
Defect easy to reproduce in the next form:
- Add to state 3 OLD lots
- Drop any of them to another and back
- Thats all - there are impossible to drop other lots to that lot
See video:
https://user-images.githubusercontent.com/2771769/113180898-ecf29e80-9259-11eb-8ac8-e2992d06547e.mp4
Hello, I was having the same problem (see here example1), it worked fine when dragging to child droppables, but would go out of control when dragging out. After reading about reparanting I thought that was my problem (and it kinda was).
However, after implementing the cloneApi(reparanting) (see here example2), even though the items that aren't droppables work fine, the nested lists aren't draggable anymore.
I might be doing something wrong, but please try the cloneApi/reparanting to see if the results are the same as mine. If it fixes yours, please share :)
I'm doing a random backgroundColor on mount. While on the example1 only the nested lists that were dragged to another list would change color (makes sense, no problem there), on example2 all the lists change colors before breaking. Saddly I can't take much out of it, but maybe someone can come up with an explanation.
Hello, Same issue. Did you find a solution ?
you can use event.stopPropagation();