0.7.1 • Published 1 year ago

react-native-nested-drag v0.7.1

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

Overview

This library is a declarative drag-and-drop system for React Native, written in TypeScript. It is designed to be flexible and powerful with the goal of supporting many use cases. Default settings should cover the most common scenarios with minimal configuration.

Screenshots

example nested example nested movable

More screenshots

Snack

There is an example on snack though it works fine only on real device.

Installation

npm i react-native-nested-drag

or using yarn

yarn add react-native-nested-drag

Usage

Import the library components:

import { DragView, DropView, DragProvider } from 'react-native-nested-drag'

Basic example

<DragProvider>
  <DropView>
    <Text>drop here!</Text>
  </DropView>
  <DragView>
    <Text>drag me</Text>
  </DragView>
</DragProvider>

DragProvider

Wrap the part of your application that you want to enable drag and drop for inside DragProvider.

<DragProvider>
  {/*  */}
</DragProvider>

Props

All props are optional. | Prop | Type | Default | Description |
| ------------- | ---------- | ------------- | ------------- | | mockEventManager | IDndEventManager | | Only for testing | | overlapMode | OverlapMode | 'last' | Specifies events call order when multiple DropView in same area. After sorting with comparer, DropView events will be called last to first. You can't change it dynamically.|

DragView

The element that you want to make draggable.

The DragView won't be actually dragged, instead a copy appears on DragStart to make sure element always on top.

      <DragView payload={1}>
        <View style={styles.itemContainer}>
          <Text>+1</Text>
        </View>
      </DragView>

Props

All props are optional.

Available all ViewProps except for GestureResponderHandlers

PropTypeDefaultDescription
payloadanyAn arbitrary value (often) unique to this draggable that can later be used to determine which draggable item was dropped onto a droppable
styleViewStyleStyle applied while this view is NOT being dragged
dragStyleViewStyleStyle applied while this view is being dragged
overStyleViewStyleStyle applied while this view is being dragged over a receiver
copyDragStyleViewStyleStyle applied to the copy while this view is being dragged
copyOverStyleViewStyleStyle applied to the copy while this view is being dragged over a receiver
disabledbooleanfalseSpecifies whether this view can be dragged
longPressDelaynumber0Time interval in ms after which this view can be dragged
vibroDurationnumber0Vibration duration on drag start in ms
movablebooleanfalseSpecifies whether this view stays where it has been dragged
movableOffsetIPosition{x:0,y:0}It useful only when you want nested movable DragView appear correctly inside the draggable copy or for restoring movable state. This prop shouldn't be used just for formatting
onDragStartDraggableDragStartCallback that is triggerd when user starts dragging the DragView
onDragDraggableDragStartCallback that is triggered repeatedly when user dragging the DragView not over any receiver
onEnterDraggableEnterOverCallback that is triggered when the DragView initially dragged over a new receiver
onOverDraggableEnterOverCallback that is triggered repeatedly when the DragView dragged over a receiver
onExitDraggableExitCallback that is triggered when the DragView leaves the droppable area
onDragEndDraggableEndCallback that is triggered when the DragView drag ends not over any receiver or is cancelled
onDropDraggableDropCallback that is triggered when the DragView drag ends over a receiver
animationEndOptionsSpringAnimationConfig{ overshootClamping: true}Animated.SpringAnimationConfig to customize dragEnd animation
animationDropOptionsTimingAnimationConfigAnimated.TimingAnimationConfig to customize drop fadeout animation

DragHandleView

DragHandleView is useful when you want to restrict area where drag can start.

Just place one or more inside DragView. Notice that Button and TouchableOpacity work inside DragView and also restrict possible drag start area (you can't start drag on buttons).

    <DragView>
        <Text>part 1</Text>
        <DragHandleView style={styles.handle}>
          <Text>drag here!</Text>
        </DragHandleView>
    </DragView>

Props

All props are optional.

Available all ViewProps except for GestureResponderHandlers

DropView

The element that you want to receive droppable events.

  const drop = (_, payload) => {
    console.log(payload)
  }
  //...
  
    <DropView onDrop={add}>
      <Text>drop here!</Text>
    </DropView>

Props

All props are optional.

Available all ViewProps

PropTypeDefaultDescription
payloadanyAn arbitrary value (often) unique to this DropView that can later be used to determine in which DropView the DragView was dropped to
styleViewStyleStyle applied while over this DropView is NOT dragging a DragView
overStyleViewStyleStyle applied while dragging a DragView over this DropView
disabledbooleanfalseSpecifies whether this view receives DnD events
onEnterDroppableEnterExitCallback that is triggered when a DragView initially dragged over this DropView
onOverDroppableDropOverCallback that is triggered repeatedly when a DragView dragged over this DropView
onExitDroppableEnterExitCallback that is triggered when a DragView leaves this DropView
onDropDroppableDropOverCallback that is triggered when a DragView dragging ends over this DropView

Testing

Import the library components:

import { DragProvider, MockDndEventManager } from 'react-native-nested-drag'

Create mockEventManager:

const mockEventManager = new MockDndEventManager()

Wrap your component for test render:

<DragProvider mockEventManager={mockEventManager}>
  <YourComponent>
</DragProvider>

Call DnD action like so:

mockEventManager.drop(mockEventManager.draggables[0], mockEventManager.droppables[0])

If you have animation errors set before the action:

jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper')
jest.useFakeTimers()

Full example with 'testing-library/react-native'

License

This software library is licensed under the MIT License.

Caveats and tips

  • If nested elements rendered base on own state clone appears as initial state. Possible workaround is move that state outside DragProvider (e.g. state management library).

  • If you want to store offset for movable DragView (may be because previous point) onDragEnd and onDrop receives it as parameter.

  • Don't put these styles on DragView (if necessary add View inside or outside):

    • margin
    • transform
    • position
    • opasity (allowed for dragStyle and overStyle only)
  • % width style doesn't work well in many cases.

  • There is useEffect listeners on props so make sure your props won't change on every render.

instead

...movableOffset={{x:50, y:-10}}...

use

...
const [movableOffset] = useState({x:50, y:-10});

...movableOffset={movableOffset}...
  • Available option for overlap handling is next callback (for onDrop and onOver)

  • Overlapping DropView is tricky especially when nested.

graph LR;
    A-->B;
    A-->C;
    D;

where B and C nested to A.

Droppables order: B, C, A, D

Moreover if you are using dynamic 'disabled' prop, which causes unregister order may become completely unpredictable.

You may try to use next callback or comparer function for overlapMode but much easier just avoid overlapping whenever it's possible.

  • onLayout event probably still calls only once (when component renders first time) inside DragView and DropView (also don't use it inside 'View' with onLayout if elements may change positions on screen besides dragging.)
0.7.1

1 year ago

0.7.0

1 year ago

0.6.7

1 year ago

0.6.6

1 year ago

0.6.5

1 year ago

0.6.4

1 year ago

0.6.3

1 year ago

0.6.2

1 year ago

0.6.1

1 year ago

0.6.0

1 year ago

0.5.0

1 year ago