dnd-kit: RectIntersection is not working with draggable items inside a scrollable container

The draggable items that are scrolled are not calculating the intersectionRatio correctly.

Please, see this code sandbox for an example: https://codesandbox.io/s/react-dnd-grid-0ylzl?file=/src/App.js

NOTE: Try dragging the last draggable item into one droppable area.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 11
  • Comments: 18 (4 by maintainers)

Most upvoted comments

Hey @jeserodz, seems like a legit bug, thanks for the report!

Hi I wonder if there’s an update on this ticket? The branch at #54 seems to have gone stale. I have a workaround with a different collision detector which uses the current active item rather than the collisionRect which seems to work for the meantime. Obviously not something we want to keep in our codebase however. Thanks.

import {
  Active,
  CollisionDetection,
  LayoutRect,
  UniqueIdentifier,
} from "@dnd-kit/core";

/**
 * Returns the intersecting rectangle area between two rectangles
 */
function getIntersectionRatio(entry: LayoutRect, active: Active): number {
  const {
    top: currentTop = 0,
    left: currentLeft = 0,
    width: currentWidth = 0,
    height: currentHeight = 0,
  } = active.rect.current.translated ?? {};

  const top = Math.max(currentTop, entry.offsetTop);
  const left = Math.max(currentLeft, entry.offsetLeft);
  const right = Math.min(
    currentLeft + currentWidth,
    entry.offsetLeft + entry.width
  );
  const bottom = Math.min(
    currentTop + currentHeight,
    entry.offsetTop + entry.height
  );
  const width = right - left;
  const height = bottom - top;

  if (left < right && top < bottom) {
    const targetArea = currentWidth * currentHeight;
    const entryArea = entry.width * entry.height;
    const intersectionArea = width * height;
    const intersectionRatio =
      intersectionArea / (targetArea + entryArea - intersectionArea);

    return Number(intersectionRatio.toFixed(4));
  }

  // Rectangles do not overlap, or overlap has an area of zero (edge/corner overlap)
  return 0;
}

/**
 * Returns the rectangle that has the greatest intersection area with a given
 * rectangle in an array of rectangles.
 */
export const activeRectIntersection: CollisionDetection = ({
  active,
  droppableContainers,
}) => {
  let maxIntersectionRatio = 0;
  let maxIntersectingDroppableContainer: UniqueIdentifier | null = null;

  for (const droppableContainer of droppableContainers) {
    const {
      rect: { current: rect },
    } = droppableContainer;

    if (rect) {
      const intersectionRatio = getIntersectionRatio(rect, active);

      if (intersectionRatio > maxIntersectionRatio) {
        maxIntersectionRatio = intersectionRatio;
        maxIntersectingDroppableContainer = droppableContainer.id;
      }
    }
  }

  return maxIntersectingDroppableContainer;
};

+1 Having a huge pain trying to find a workaround for this bug. Here’s the video example, sorta duplicating the initial record, but you may find a couple of additional things.

As a solution, (or at least a temporary solution), it’d be just awesome to be able to remove certain scrollable ancestors from position calculation. Obviously, intersection is calculated using a concatenation of body scrollTop and the left panel scrollable block scrollTop. I don’t need left scrollable block to be scrolled at all here, would be great to just remove it from either calculations, and scrollable items as well

https://user-images.githubusercontent.com/71796791/123527550-5f540d00-d6e9-11eb-97f0-8e6ae5e6c8e3.mp4

For the time being, you can use the closestCenter or closestCorners collision detection algorithms as a workaround, as this is a bug that will take a bit of time to fix.

I’m having the same issue. how can I fix drag issue on scrollable container?

First of all thanks for building such a developer friendly DnD library @clauderic

We are building a kanban board and our first choice naturally went towards using react-beautiful-dnd and after trying out that, we end up facing this issue https://github.com/atlassian/react-beautiful-dnd/issues/131 and then we have to remove the package as it is not gonna be solved anytime soon.

After searching for other libraries, we found dnd-kit to be promising. Especially after seeing this example. So we replaced react-beautiful-dnd with dnd-kit and things were going great until we hit this issue https://github.com/clauderic/dnd-kit/issues/73

I saw you are working on https://github.com/clauderic/dnd-kit/pull/54 Will this solve this issue?

I am open to discuss this and support it further with your help and guidance.

Not sure if this bug is the cause for the following as well: https://codesandbox.io/s/confident-pike-ofooi

  • Multiple containers with grids of Droppable+Draggable squares
  • Dragging within own container: auto scrolling works fine
  • Dragging between containers: auto scrolls “over” container, then starts scrolling “from” container
  • onDragOver gets called repeatedly when the above occurs

Note: this doesn’t use @dnd-kit/sortable, as it’s not necessary for my particular use case.

https://user-images.githubusercontent.com/9291779/117454064-4ac08780-af78-11eb-8ce6-3ba1735d5222.mp4