diff --git a/invokeai/frontend/web/src/common/components/IAIDroppable.tsx b/invokeai/frontend/web/src/common/components/IAIDroppable.tsx index e4fb121c78..bf98961c21 100644 --- a/invokeai/frontend/web/src/common/components/IAIDroppable.tsx +++ b/invokeai/frontend/web/src/common/components/IAIDroppable.tsx @@ -31,7 +31,7 @@ const IAIDroppable = (props: IAIDroppableProps) => { insetInlineStart={0} w="full" h="full" - pointerEvents="none" + pointerEvents={active ? 'auto' : 'none'} > {isValidDrop(data, active) && ( diff --git a/invokeai/frontend/web/src/features/dnd/components/AppDndContext.tsx b/invokeai/frontend/web/src/features/dnd/components/AppDndContext.tsx index bffe738aa9..bd77383843 100644 --- a/invokeai/frontend/web/src/features/dnd/components/AppDndContext.tsx +++ b/invokeai/frontend/web/src/features/dnd/components/AppDndContext.tsx @@ -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} diff --git a/invokeai/frontend/web/src/features/dnd/util/customPointerWithin.ts b/invokeai/frontend/web/src/features/dnd/util/customPointerWithin.ts new file mode 100644 index 0000000000..68f1e98b16 --- /dev/null +++ b/invokeai/frontend/web/src/features/dnd/util/customPointerWithin.ts @@ -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, + }); +};