nuclearpasta / react-native-drax

A drag-and-drop system for React Native
MIT License
554 stars 69 forks source link

Feature request: ability to cancel snapback animation #83

Closed rejhgadellaabsl closed 3 years ago

rejhgadellaabsl commented 3 years ago

I'm working on reordering items in a grid of 3x3 grid of DragViews. I noticed the issue with numColumns > 1 (see #14) on DragList, so I'm trying to work around this with a custom implementation. Something like:

<DragProvider>

  <DraxView
    onDragStart={/* ... */}
    onReceiveDragEnter={/* ... */}
    onReceiveDragDrop={this._onReceiveDragDrop.bind(this, index)}
    payload={{index, itemData}}
  />

<DragProvider>

In onReceiveDragDrop, I do the re-ordering, which works, but the draggable then snaps back to its old position.

_onReceiveDragDrop(toIndex, event) {
  const { state } = this;
  const { dragged: { payload } } = event;

  // Get copy of items, remove the dragged item from its original index
  const items = deepCopy(state.items);
  items.splice(payload.index);

  // Build new items list, insert dragged item at toIndex
  const newItems = [];
  for (let i = 0; i < items.length; i++) {
    if (i == toIndex) {
      newItems.push(payload.itemData);
    }
    items.push(items[i]);
  }

  event.preventSnapback(); // <= something like this?

  this.setState({items: newItems});
}

It would be nice if I could prevent the snapback from the onReceiveDragDrop callback - or, maybe even better, set a new target?

Note: I'm not entirely sure if canceling the snapback animation is already possible. If it is, I just don't know how to do it (API documentation would be really awesome ;))

lafiosca commented 3 years ago

Hi! I'm sorry for the lack of documentation and maintenance on this project right now. But I do have a little bit of good news here; I seem to recall that the library does support this functionality. Take a look at these comments:

https://github.com/nuclearpasta/react-native-drax/blob/051295857fd840e38eb79ded9edabee31f8d7178/src/types.ts#L142-L155

All of the following methods should be able to return a new target or DraxSnapbackTargetPreset.None to cancel:

Without digging into the code deeply, I seem to recall that if multiple handlers return, the precedence goes: dragged item, then receiver, then monitor.

Try making your _onReceiveDragDrop function return an enum value or a position. If you're not using TypeScript, you should be able to return 1 for DraxSnapbackTargetPreset.None or { x, y } for a position. I think the position should be an offset from the upper left corner of the DraxProvider.

rejhgadellaabsl commented 3 years ago

Thanks! I'll look into this tomorrow or early next week and report back!

rejhgadellaabsl commented 3 years ago

This works! I needed to figure out how to properly mearue/calculate the position of the item but once I got that and returned {x, y} from _onReceiveDragDrop, the draggable neatly snaps to the target. Thanks!

vladlenskiy commented 3 years ago

@rejhgadellaabsl Hey! Can you send example code? I try to do a 2 columns list with swap elements by drag and drop, but have a problem with animation :(