dnd-kit
dnd-kit copied to clipboard
Autoscroll results in offset for dropzones in sticky parents
Problem Description
In a case where draggable elements are placed inside a scrollable area, the auto-scroll feature is super helpful as it scrolls when a currently dragged element comes close to the edges.
Normally this works just fine and the options allow pretty fine grained control of the auto-scroll.
However, when there is a dropzone inside that scrollable area that has position: sticky
to prevent it from scrolling out of view, the auto-scroll introduces an offset with every scrolling action that is applied by the auto-scroll to the drop-zone rect before the collision detection is performed.
Optically nothing breaks initially, but a drop-zone that has a hover effect will show the hover effect when the offset bounding box is hovered instead of the visible bounding box.
Sidenotes
While trying to work around this issue I noticed that the auto-scroll feature can be turned off globally by options, but can only be turned off locally by nesting another DndContext
. Unfortunately this would break the global drag'n'drop support for the application I'm working on.
It might be a nice addition to be able to exempt certain scroll containers without turning it off globally as well (e.g. via attribute or class name).
On the other hand the ability to nest multiple DndContext
where children serve as a configuration device and the parent does all the heavy lifting (drag overlay, drop handling, etc.) would be a nice addition as well. But I guess that's a bigger refactoring and beyond the scope of this issue.
Reproduction
I made a small example in this sandbox: https://codesandbox.io/s/dndkit-autoscroll-with-sticky-dropzone-70m37f
Scrolling horizontally via auto-scroll will prevent reaching the sticky drop zone (dashed border shows successful hovering over the drop zone)
https://user-images.githubusercontent.com/16539774/186900278-f2620221-6004-423e-a1f8-565d050ed761.mov
p.s.: I haven't forgotten about this library and my plans with it, but time is a very limited resource for me right now - more contributions to come soon hopefully ;)
After some more investigation I found a temporary solution to this problem.
I noticed that the measurements for droppables were done before and after the drag, but not while dragging.
Using a number
as the measuring frequency
for droppables instead of 'optimized'
forces the measurement even while dragging and corrects any offset formerly applied to the sticky droppables.
This is my workaround:
const measuring = useMemo(() => {
return {
droppable: {
strategy: MeasuringStrategy.Always,
frequency: isDragging ? 500 : MeasuringFrequency.Optimized,
},
};
}, [isDragging]);