react-beautiful-dnd icon indicating copy to clipboard operation
react-beautiful-dnd copied to clipboard

Impossible to use dynamic `Droppable` in recursive structure

Open moulinraphael opened this issue 4 years ago • 9 comments

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

  1. Open https://codesandbox.io/s/react-beautiful-dnd-recursive-c7he2
  2. Add new lot (red) in the top level
  3. Try to move "NEW" lot in "OLD" one : It's working
  4. 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

moulinraphael avatar Nov 10 '20 05:11 moulinraphael

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.

absolutelysimon avatar Nov 24 '20 01:11 absolutelysimon

I am also experiencing the same. Has anyone found a work around?

patrickml avatar Jan 07 '21 15:01 patrickml

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.

p-somers avatar Jan 17 '21 15:01 p-somers

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

p-somers avatar Jan 17 '21 20:01 p-somers

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.

MartinCastellanos-at avatar Feb 02 '21 02:02 MartinCastellanos-at

Hi!

Defect easy to reproduce in the next form:

  1. Add to state 3 OLD lots
  2. Drop any of them to another and back
  3. 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

boardgames avatar Mar 31 '21 16:03 boardgames

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.

diversos avatar Mar 31 '21 16:03 diversos

Hello, Same issue. Did you find a solution ?

LudovicF42 avatar Jul 15 '21 14:07 LudovicF42

you can use event.stopPropagation();

matinHosseinpour avatar Mar 11 '24 13:03 matinHosseinpour