Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@dnd-kit/react source and target in sortable list always the same in onDragEnd handler #1564

Open
rossyman opened this issue Dec 12, 2024 · 12 comments

Comments

@rossyman
Copy link

rossyman commented Dec 12, 2024

When composing a multiple sortable list and handling the onDragEnd event, both operation.source and operation.target are always set to the currently dragged item when re-arranging the order of a parent list (i.e.: moving the position of a kanban column).

When dragging the column, the target is only ever the correct one when you are dragging directly over the top of the target element itself.

When @dnd-kit/react automatically re-positions other columns in the sortable list to "preview" where the dragged column will be dropped, it causes the target to be subsequently set to the currently dragged column when there's no column "beneath" it besides the "ghost column".

This makes it near enough impossible to trigger a change such as updating a column's position in an external DB:

  const handleDragEnd = (cancelled: boolean, operation: KanbanDragEvent<TGroup, TItem>) => {
    if (!cancelled && operation.source && operation.target) {
      const { source, target } = operation
      switch (source.type) {
        case 'column':
          props.onGroupReorder(source.data, target.data)
          break
        case 'item':
          props.onItemMove(source.data, target.data)
          break
      }
    }
  }

Reproduction

I've created a reproduction of this issue by forking a minimal version of the multiple sortable lists example on CodeSandbox.
View the reproduction on CodeSandbox

@RubenZx
Copy link

RubenZx commented Dec 12, 2024

I'm facing the same issue even in a Sortable list.

@clauderic
Copy link
Owner

This is because the DOM gets optimistically updated by @dnd-kit by default. If you would like to manage DOM and state manually, you can call event.preventDefault() in onDragOver

<DragDropProvider
  onDragOver={(event) => {
    event.preventDefault();
  }}

@rossyman
Copy link
Author

rossyman commented Dec 12, 2024

Even if dnd-kit automatically handles the transition optimistically, how do we access the resulting index within onDragEnd @clauderic? My use-case is essentially that we need to retrieve the new index and send it to the B/E for persistence.

One of the issues I continue to run into is that if I track the last dragged over item via onDragOver, and move an item in position N, to N+1 and then back to N, all without dropping the item, the last dragged over item will still be N+1, and therefore I cannot accurately determine what position the resulting drop occurred at.

@rossyman
Copy link
Author

There could potentially be a solution to this. If I clone the columns and items arrays, then have dnd-kit optimistically update the cloned arrays within onDragOver. Then inside of onDragEnd I can determine the new position based on the cloned array, or roll back if event.cancelled.

@clauderic
Copy link
Owner

You can read the index and group on start and end:

event.source.sortable.index and event.source.sortable.group

@rossyman
Copy link
Author

rossyman commented Dec 12, 2024

Makes sense @clauderic, thanks for clarifying, I'll post my updated implementation here shortly incase anyone else experiences something similar.

One last thing though, what would the correct generics be for DragOperation<T, U> then when using Sortable? From what I can see Sortable doesn't actually extend Draggable which is what the generics expect, rather it's a container that houses both a Draggable and a Droppable element. However, source and target appear to both expect Draggable or Droppable as their value.

@clauderic
Copy link
Owner

You can import the isSortable type guard from @dnd-kit/dom/sortable

@rossyman
Copy link
Author

The isSortable type-guard is not publicly exposed by the package, but using a type-guard would be ideal.

@clauderic
Copy link
Owner

clauderic commented Dec 12, 2024

It is exported: https://github.com/clauderic/dnd-kit/blob/experimental/packages/dom/src/sortable/index.ts#L3

You have to import it from @dnd-kit/dom/sortable but I will make a note to re-export it from @dnd-kit/react/sortable

@rossyman
Copy link
Author

Maybe I'm just being obtuse here, but TS doesn't seem to think it's exported, and when looking inside of the built JS files in v0.0.5, I cannot see the function being re-exported as part of sortable.js

Usage

Screenshot 2024-12-12 at 17 04 04

sortable.js

Screenshot 2024-12-12 at 17 05 14 Screenshot 2024-12-12 at 17 06 18

From what I can see, it appears as though the type-guard is being inlined during the build and is not actually being exported by the API of @dnd-kit/dom/sortable

@clauderic
Copy link
Owner

Oh, it must only exported in the nightly builds, I have not merged 0.0.6 yet.

Try installing one of the nightly builds, the latest one is 0.0.6-beta-20241204184550

@rossyman
Copy link
Author

rossyman commented Dec 12, 2024

Ah yes nice one, it's present in the nightly build 🚀 May also make sense to export SortableDraggable and SortableDroppable for people to use in their DragOperation generics on custom handlers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants