clauderic / dnd-kit

The modern, lightweight, performant, accessible and extensible drag & drop toolkit for React.
http://dndkit.com
MIT License
12.87k stars 640 forks source link

No sorting animation on dragOver #1146

Open GradySain opened 1 year ago

GradySain commented 1 year ago

I've had this listed as a discussion for months, moving it into an issue because I can't find a solution...

Here's a video that explains the problem (turn up audio for voice over):

https://github.com/clauderic/dnd-kit/assets/97621176/bda18bfc-1748-4155-8ad0-611f17956000

Here's the source:

https://stackblitz.com/edit/react-zsldgk?file=src/App.js

I'm trying to create setup that allows a user to drag items from the left side bar, and drop it onto the drop zone. Items can be re-used, dragged any number of times to the drop zone. When dragging over the drop zone, new "temporary" items should automatically be created (if they are dragged over existing items), and automatically sorted until they are finally dropped.

Where I'm stuck- I can't get the sorting animation to work when items are being dragged over the drop zone. Once items are in the drop zone, they can be dragged, and the sorting animation works correctly.

There are many things "wrong" in the current state of the code. Before I go on to correct those issues, I need to understand why why the sorting animation isn't working. I commented out the use of DragOverlay on purpose, again to understand why the current state of the code isn't working...

If anyone has any hints or clues as to what I'm doing wrong here, I'd be grateful for input. I built this example using many other existing examples, trying to get the right combinations of features for my setup.

ehrro commented 1 year ago

Hello look at my implementation and tell me if you need something like this?

https://app.simplyfitness.com/workout-builder/new

GradySain commented 1 year ago

The fix I ended up using. In App.js (in the stackblitz in original post), replace these lines (111-117)

const temp = [...droppedItems];
      const lastAddedID = temp.length;
      setDroppedItems((items) => {
        const oldIndex = items.findIndex((item) => item.sortID === lastAddedID);
        const newIndex = items.findIndex((item) => item.sortID === over.id);
        return arrayMove(items, oldIndex, newIndex);
      });

with this:

// found it here: https://codesandbox.io/s/github/jdthorpe/dnd-kit-sortable-poc?file=/src/App.tsx:3398-3509
      if (oldIndex === newIndex) return;
      // also critical, seems to be against react rules, directly mutating state, but works...
      setDroppedItems(arrayMove(droppedItems, newIndex, oldIndex));

I don't use approaches like this anywhere else in my code. But for some reason this, and only this, seemed to work. Have no idea why.

ehrro commented 1 year ago

I used this a custom state hook use Immer to solve this problem: https://github.com/immerjs/use-immer