dnd-kit icon indicating copy to clipboard operation
dnd-kit copied to clipboard

Possible to combine sortables?

Open sampl opened this issue 3 years ago • 12 comments

I'm trying to use dnd-kit on a board where cards can be reordered and combined, similar to this example from react-beautiful-dnd.

Is this possible to do using the current @dnd-kit/sortable package? I can probably do it with @dnd-kit/core but I assume I'd miss out on things like keyboard sorting and sorting strategies.

Any thoughts/guidance would be much appreciated!

This library is awesome by the way 😁

sampl avatar Mar 02 '21 16:03 sampl

This should be possible yes, you'll need to put in a bit of work though.

Say you were building a vertical list, I'd start by forking the verticalListSortingStrategy and updating the threshold after which a node should shift positions.

You'd also need to update the onDragEnd handler to either combine the items if dropped within the given threshold or reorder thee items.

If you want to support keyboard input, you would also need to fork the keyboard coordinates getter to first move into the threshold that doesn't cause the node to shift position on the first keydown event, and then outside of that threshold on the second keydown event.

clauderic avatar Mar 03 '21 14:03 clauderic

@clauderic - I was wondering if you could point me to where I can update the threshold after which a node should shift positions. I've been been playing around with the different sorting strategies, but can't find where this threshold is set. Any help would be much appreciated, thanks!

kylebcarlson avatar Nov 15 '21 21:11 kylebcarlson

@kylebcarlson We are currently looking at this too. The place I can find thresholds is in /utilities/algorithms under the various collision detection algorithms.

The issue we face however is that given that theres only one idea of over its not entirely clear to me how this would work. Lets say you put a minimum coverage of 60% in rectIntersection. This would be fine for a trigger to merge, but now theres no trigger to reorder, and if you are in between cards on your hover, the active card is considered over a null index, which means the app will want to revert it to its original location.

Were you able to make any further progress here? Or find another solution that supports grid reorder + combine? We really need it for our project and would like to create/find a solution.

Our first attempt was to get react-beautiful-dnd to support grid, then we thought that maybe it would be easier to add combine to this one. Now im not so sure this will be easily doable either without some way to provide more context about the over card in the onDrag/onDrop functions

sean-esper avatar Jan 05 '22 17:01 sean-esper

I'm a little confused why this is tagged as documentation and closed. Seems like an active feature request?

sean-esper avatar Jan 05 '22 17:01 sean-esper

hey @sean-esper! sorry for the late reply, i put this work aside for a couple months. Unfortunately I was unable to find a grid reorder/combine solution. It seems like all the libraries I've seen support grid but not combine or vice-versa.

kylebcarlson avatar Jan 31 '22 20:01 kylebcarlson

@kylebcarlson we figured this out in the next version of dnd-kit. See https://github.com/clauderic/dnd-kit/issues/127

sean-esper avatar Jan 31 '22 20:01 sean-esper

There seems to be an issue with the approach from #127 and vertical lists (haven’t tested the other variants, but it most certainly affects horizontal lists as well). It’s difficult to explain, so let’s go through what happens step by step:

  1. There’re items in a vertical list—A and B—in that order: A is above B.
  2. If A is dragged onto B without sorting (i.e. the visual A–B order is preserved), they’re combined as expected.
  3. If A is dragged past B’s center, so that the order changes and becomes B-A, and then A is dragged up (without releasing it) to combine it with B after the list was visually sorted, nothing is combined because of this line:
const shouldCombine =
+ secondCollidingId !== active.id &&
  collisionRatio != null &&
  collisionRatio > 0.1 &&
  collisionRatio < 0.4

When the order changes to B-A, it’s only a visual change, and A is essentially dropped onto itself, so to fix this, the list should sort before we can combine, and in case you’re wondering if we can sort onDragOver—we can’t, it messes up the sorting animations. It works the same way in longer lists, e.g., if A-B-C changes to C-A-B without sorting first, C drops onto B, and not A. I set up a basic example on CodeSandbox—see the console output when dragging.

Unless I’m missing something, I don’t see a clean or straightforward way to deal with this, so any advice or opinion is appreciated.

awgv avatar Apr 22 '22 16:04 awgv

I’ve been looking for workarounds and found a pattern that’s a bit weird in terms of user experience, but I’ve decided to give it a go nonetheless—sharing a rough sketch of how it might look like in case somebody finds it useful (my use-case is an extension of the pointerWithin). You basically divide each item in a vertical list in half; the left side always sorts, and the right side always combines:

https://user-images.githubusercontent.com/6409354/165963684-2cbeadbe-e49c-4fe0-9d18-1aa199e915bc.mp4

awgv avatar Apr 29 '22 14:04 awgv

Hey @amgv did you manage to solve this or is there any update on this issue?

bitionaire avatar Jun 10 '22 20:06 bitionaire

Hey @bitionaire. nice to meet you. I think you are confused about who made the comment, but don't worry about it. It's an interesting discussion. thank you so much for tagging me 😄

amgv avatar Jun 10 '22 21:06 amgv

😅

Hey @bitionaire, I ditched detection via collision ratios, am currently using the pattern that I posted above—check the video, and if it’s something that might work for you, see the CodeSandbox link. Let me know if you have questions, I might be able to help some more.

awgv avatar Jun 11 '22 00:06 awgv

Hehe, sorry about the typo and thanks for your replies! :)

I saw the video, but I don't like the usability of the solution for our use case. I'll take a look into the sources of the library and will give it a try to fix the problem (... and update the order ahead of the calculation of the collisions).

Another way would be to get the bounding boxes on drag end and try to map them on the positions of the elements in the list. Though it might work, it would be very hacky

bitionaire avatar Jun 12 '22 15:06 bitionaire

My solution to this problem may be helpful to someone. In this case, sorting is activated with a threshold.

Screencast from 2023-02-26 15-47-15.webm

cjvnjde avatar Feb 26 '23 13:02 cjvnjde

Can we please get this example in the storybook? As it is quite a common case. And personally, I'm struggling to make it work without any flickers or glitches.

GoranRibic avatar Mar 28 '23 14:03 GoranRibic

The official answer here that you can just modify the strategy and/or collision detection doesn't really make sense.

A classic sortable list can have a stateless ordering strategy, yes, but if there is item combining, the threshold for when an item should shift should be contextual: it should be when you move the mouse past it, not across its middle. This can be either the top or bottom edge depending which way you are moving the mouse, and whether it is shifting to a new position or shifting back.

The best-working example above still has the problem that only half of each item is usable before it shifts. If you do this with items that are only 1 line tall, it is not really usable.

unconed avatar May 26 '23 18:05 unconed