fix(ui): skip firing collision detection on dnd when droppable scrolled out

Requires some additional logic in the collision detection algorithm.

Closes #4621
This commit is contained in:
psychedelicious 2023-09-21 16:41:35 +10:00 committed by Kent Keirsey
parent b4790002c7
commit 6d1057c560
3 changed files with 41 additions and 3 deletions

View File

@ -31,7 +31,7 @@ const IAIDroppable = (props: IAIDroppableProps) => {
insetInlineStart={0}
w="full"
h="full"
pointerEvents="none"
pointerEvents={active ? 'auto' : 'none'}
>
<AnimatePresence>
{isValidDrop(data, active) && (

View File

@ -2,7 +2,6 @@ import {
DragOverlay,
MouseSensor,
TouchSensor,
pointerWithin,
useSensor,
useSensors,
} from '@dnd-kit/core';
@ -14,6 +13,7 @@ import { AnimatePresence, motion } from 'framer-motion';
import { PropsWithChildren, memo, useCallback, useState } from 'react';
import { useScaledModifer } from '../hooks/useScaledCenteredModifer';
import { DragEndEvent, DragStartEvent, TypesafeDraggableData } from '../types';
import { customPointerWithin } from '../util/customPointerWithin';
import { DndContextTypesafe } from './DndContextTypesafe';
import DragPreview from './DragPreview';
@ -77,7 +77,7 @@ const AppDndContext = (props: PropsWithChildren) => {
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
sensors={sensors}
collisionDetection={pointerWithin}
collisionDetection={customPointerWithin}
autoScroll={false}
>
{props.children}

View File

@ -0,0 +1,38 @@
import { CollisionDetection, pointerWithin } from '@dnd-kit/core';
/**
* Filters out droppable elements that are overflowed, then applies the pointerWithin collision detection.
*
* Fixes collision detection firing on droppables that are not visible, having been scrolled out of view.
*
* See https://github.com/clauderic/dnd-kit/issues/1198
*/
export const customPointerWithin: CollisionDetection = (arg) => {
if (!arg.pointerCoordinates) {
// sanity check
return [];
}
// Get all elements at the pointer coordinates. This excludes elements which are overflowed,
// so it won't include the droppable elements that are scrolled out of view.
const targetElements = document.elementsFromPoint(
arg.pointerCoordinates.x,
arg.pointerCoordinates.y
);
const filteredDroppableContainers = arg.droppableContainers.filter(
(container) => {
if (!container.node.current) {
return false;
}
// Only include droppable elements that are in the list of elements at the pointer coordinates.
return targetElements.includes(container.node.current);
}
);
// Run the provided collision detection with the filtered droppable elements.
return pointerWithin({
...arg,
droppableContainers: filteredDroppableContainers,
});
};