1 | # React DnD Multi Backend [![NPM Version][npm-image]][npm-url] [![dependencies Status][deps-image]][deps-url] [![devDependencies Status][deps-dev-image]][deps-dev-url]
|
2 |
|
3 | [Try it here!](https://louisbrunner.github.io/dnd-multi-backend/examples/react-dnd-multi-backend.html)
|
4 |
|
5 | This project is a Drag'n'Drop backend compatible with [React DnD](https://github.com/react-dnd/react-dnd).
|
6 | It enables your application to use different DnD backends depending on the situation.
|
7 | You can either generate your own backend pipeline or use the default one (`HTML5toTouch`).
|
8 |
|
9 | [HTML5toTouch](src/HTML5toTouch.js) starts by using the [React DnD HTML5 Backend](https://react-dnd.github.io/react-dnd/docs/backends/html5), but switches to the [React DnD Touch Backend](https://react-dnd.github.io/react-dnd/docs/backends/touch) if a touch event is triggered.
|
10 | You application can smoothly use the nice HTML5 compatible backend and fallback on the Touch one on mobile devices!
|
11 |
|
12 | Moreover, because some backends don't support preview, a `Preview` component has been added to make it easier to mock the Drag'n'Drop "ghost".
|
13 |
|
14 | See the [migration section](#migrating) for instructions when switching from `2.x.x`, `3.x.x` or `4.x.x`.
|
15 |
|
16 |
|
17 | ## Installation
|
18 |
|
19 | ### NPM Installation
|
20 |
|
21 | ```sh
|
22 | npm install react-dnd-multi-backend
|
23 | ```
|
24 |
|
25 | You can then `MultiBackend = require('react-dnd-multi-backend')` or `import MultiBackend from 'react-dnd-multi-backend'`.
|
26 | To get the `HTML5toTouch` pipeline, just require/import `react-dnd-multi-backend/dist/BUILD_TYPE/HTML5toTouch` (where `BUILD_TYPE` is either `cjs` for CommonJS or `esm` for ES Module).
|
27 |
|
28 | ### Browser Installation
|
29 |
|
30 | Use the minified UMD build in the `dist` folder: https://www.jsdelivr.com/package/npm/react-dnd-multi-backend?path=dist%2Fumd.
|
31 |
|
32 | `react-dnd-multi-backend.min.js` exports a global `window.ReactDnDMultiBackend` when imported as a `<script>` tag.
|
33 |
|
34 | If you want to use the `HTML5toTouch` pipeline, also include `HTML5toTouch.min.js`.
|
35 | It exports a global `window.HTML5toTouch` when imported as a `<script>` tag.
|
36 | This file also includes the `HTML5` and `Touch` backends, so no need to include them as well.
|
37 |
|
38 |
|
39 | ## Usage
|
40 |
|
41 | ### Backend
|
42 |
|
43 | You can plug this backend in the `DragDropContext` the same way you do for any backend (e.g. `ReactDnDHTML5Backend`), you can see [the docs](https://react-dnd.github.io/react-dnd/docs/backends/html5) for more information.
|
44 |
|
45 | You must pass a 'pipeline' to use as argument. This package includes `HTML5toTouch`, but you can write your own.
|
46 | Note that if you include this file, you will have to add `react-dnd-html5-backend` and `react-dnd-touch-backend` to your `package.json` `dependencies`.
|
47 |
|
48 | ```js
|
49 | import { DndProvider } from 'react-dnd';
|
50 | import MultiBackend from 'react-dnd-multi-backend';
|
51 | import HTML5toTouch from 'react-dnd-multi-backend/dist/esm/HTML5toTouch'; // or any other pipeline
|
52 | ...
|
53 | const App = () => {
|
54 | return (
|
55 | <DndProvider backend={MultiBackend} options={HTML5toTouch}>
|
56 | <Example />
|
57 | </DndProvider>
|
58 | );
|
59 | };
|
60 | ```
|
61 |
|
62 | ### Create a custom pipeline
|
63 |
|
64 | Creating a pipeline is fairly easy. A pipeline is composed of a list of backends, the first one will be the default one, loaded at the start of the **MultiBackend**, the order of the rest isn't important.
|
65 |
|
66 | Each backend entry must specify one property: `backend`, containing the class of the Backend to instantiate.
|
67 | But other options are available:
|
68 |
|
69 | - `preview` (a boolean indicating if `Preview` components should be shown)
|
70 | - `transition` (an object returned by the `createTransition` function)
|
71 | - `skipDispatchOnTransition` (a boolean indicating transition events should not be dispatched to new backend, defaults to `false`. See [note below](#note-on-skipdispatchontransition) for details and use cases.)
|
72 |
|
73 | Here is the `HTML5toTouch` pipeline code as an example:
|
74 | ```js
|
75 | ...
|
76 | import HTML5Backend from 'react-dnd-html5-backend';
|
77 | import TouchBackend from 'react-dnd-touch-backend';
|
78 | import MultiBackend, { TouchTransition } from 'react-dnd-multi-backend';
|
79 | ...
|
80 | const HTML5toTouch = {
|
81 | backends: [
|
82 | {
|
83 | backend: HTML5Backend
|
84 | },
|
85 | {
|
86 | backend: TouchBackend({enableMouseEvents: true}), // Note that you can call your backends with options
|
87 | preview: true,
|
88 | transition: TouchTransition
|
89 | }
|
90 | ]
|
91 | };
|
92 | ...
|
93 | const App = () => {
|
94 | return (
|
95 | <DndProvider backend={MultiBackend} options={HTML5toTouch}>
|
96 | <Example />
|
97 | </DndProvider>
|
98 | );
|
99 | };
|
100 | ```
|
101 |
|
102 | `TouchTransition` is a predefined transition that you can use in your own pipelines, it is triggered when a *touchstart* is received. Transitions rea really easy to write, here is an example:
|
103 |
|
104 | ```js
|
105 | import { createTransition } from 'react-dnd-multi-backend';
|
106 |
|
107 | const TouchTransition = createTransition('touchstart', (event) => {
|
108 | return event.touches != null;
|
109 | });
|
110 | ```
|
111 |
|
112 | You can also import `HTML5DragTransition` which works the same way, but detects when a HTML5 DragEvent is received.
|
113 |
|
114 | #### Note on `skipDispatchOnTransition`
|
115 |
|
116 | By default, when an event triggers a transition, `dnd-multi-backend` dispatches a cloned version of the event after setting up the new backend. This allows the newly activated backend to handle the original event.
|
117 |
|
118 | If your app code or another library has registered event listeners for the same events that are being used for transitions, this duplicate event may cause problems.
|
119 |
|
120 | You can optionally disable this behavior per backend:
|
121 |
|
122 | ```js
|
123 | const CustomHTML5toTouch = {
|
124 | backends: [
|
125 | {
|
126 | backend: HTML5Backend,
|
127 | transition: MouseTransition
|
128 | // by default, will dispatch a duplicate `mousedown` event when this backend is activated
|
129 | },
|
130 | {
|
131 | backend: TouchBackend,
|
132 | // Note that you can call your backends with options
|
133 | options: {enableMouseEvents: true},
|
134 | preview: true,
|
135 | transition: TouchTransition,
|
136 | // will not dispatch a duplicate `touchstart` event when this backend is activated
|
137 | skipDispatchOnTransition: true
|
138 | }
|
139 | ]
|
140 | };
|
141 | ```
|
142 |
|
143 | **WARNING:** if you enable `skipDispatchOnTransition`, the backend transition will happen as expected, but the new backend may not handle the first event!
|
144 |
|
145 | In this example, the first `touchstart` event would trigger the `TouchBackend` to replace the `HTML5Backend`—but the user would have to start a new touch event for the `TouchBackend` to register a drag.
|
146 |
|
147 |
|
148 | ### Preview
|
149 |
|
150 | The `Preview` class is usable in two different ways: function-based and context-based.
|
151 | Both of them receive the same data formatted the same way, an object containing 3 properties:
|
152 |
|
153 | - `itemType`: the type of the item (`monitor.getItemType()`)
|
154 | - `item`: the item (`monitor.getItem()`)
|
155 | - `style`: an object representing the style (used for positioning), it should be passed to the `style` property of your preview component
|
156 |
|
157 | Note that this component will only be showed while using a backend flagged with `preview: true` (see [Create a custom pipeline](#create-a-custom-pipeline)) which is the case for the Touch backend in the default `HTML5toTouch` pipeline.
|
158 |
|
159 | #### Function-based
|
160 |
|
161 | ```js
|
162 | import MultiBackend, { Preview } from 'react-dnd-multi-backend';
|
163 | ...
|
164 | const generatePreview = ({itemType, item, style}) => {
|
165 | // render your preview
|
166 | };
|
167 | ...
|
168 | <Preview generator={generatePreview} />
|
169 | // or
|
170 | <Preview>{generatePreview}</Preview>
|
171 | ```
|
172 |
|
173 | #### Context-based
|
174 |
|
175 | ```js
|
176 | import MultiBackend, { Preview } from 'react-dnd-multi-backend';
|
177 | ...
|
178 | const MyPreview = () => {
|
179 | const {itemType, item, style} = useContext(Preview.Component);
|
180 | // render your preview
|
181 | };
|
182 | ...
|
183 | <Preview>
|
184 | <MyPreview />
|
185 | // or
|
186 | <Preview.Context.Consumer>
|
187 | {({itemType, item, style}) => /* render your preview */}
|
188 | </Preview.Context.Consumer>
|
189 | </Preview>
|
190 | ```
|
191 |
|
192 | ### Examples
|
193 |
|
194 | You can see an example [here](examples/).
|
195 |
|
196 |
|
197 | ## Migrating
|
198 |
|
199 | ### Migrating from 2.x.x
|
200 |
|
201 | In 2.x.x, the pipeline was static but corresponded with the behavior of `HTML5toTouch`, so just [including and passing this pipeline as a parameter](#backend) would give you the same experience as before.
|
202 |
|
203 | If you used the `start` option, it's a bit different.
|
204 | With `start: 0` or `start: Backend.HTML5`, **MultiBackend** simply used the default pipeline, so you can also just pass `HTML5toTouch`.
|
205 | With `start: 1` or `start: Backend.TOUCH`, **MultiBackend** would only use the TouchBackend, so you can replace **MultiBackend** with **TouchBackend** (however, you would lose the `Preview` component) or create a simple pipeline (see [Create a custom pipeline](#create-a-custom-pipeline)) and pass it as a parameter:
|
206 | ```js
|
207 | var TouchOnly = { backends: [{ backend: TouchBackend, preview: true }] };
|
208 | ```
|
209 |
|
210 | ### Migrating from 3.1.2
|
211 |
|
212 | Starting with `3.1.8`, the dependencies of `react-dnd-multi-backend` changed. `react`, `react-dom`, `react-dnd` become peer dependencies and you need to install them manually as `dependencies` in your project `package.json`.
|
213 |
|
214 | Note that if you use the `HTML5toTouch` pipeline, the same is true for `react-dnd-html5-backend` and `react-dnd-touch-backend`.
|
215 |
|
216 | ### Migrating from 3.x.x
|
217 |
|
218 | Starting with `4.0.0`, `react-dnd-multi-backend` will start using `react-dnd` (and the corresponding backends) `9.0.0` and later.
|
219 |
|
220 | This means you need to transition from `DragDropContext(MultiBackend(HTML5toTouch))(App)` to `<DndProvider backend={MultiBackend} options={HTML5toTouch}>`.
|
221 | Accordingly, the pipeline syntax changes and you should specify backend options as a separate property, e.g. `{backend: TouchBackend({enableMouseEvents: true})}` becomes `{backend: TouchBackend, options: {enableMouseEvents: true}}`.
|
222 | Note that if you use the `HTML5toTouch` pipeline, the same is true for `react-dnd-html5-backend` and `react-dnd-touch-backend`.
|
223 |
|
224 | ### Migrating from 4.x.x
|
225 |
|
226 | Starting with `5.0.0`, `react-dnd-preview` (which provides the `Preview` component) will start passing its arguments packed in one argument, an object `{itemType, item, style}`, instead of 3 different arguments (`itemType`, `item` and `style`). This means that will need to change your generator function to receive arguments correctly.
|
227 |
|
228 |
|
229 | ## License
|
230 |
|
231 | MIT, Copyright (c) 2016-2019 Louis Brunner
|
232 |
|
233 |
|
234 |
|
235 | [npm-image]: https://img.shields.io/npm/v/react-dnd-multi-backend.svg
|
236 | [npm-url]: https://npmjs.org/package/react-dnd-multi-backend
|
237 | [deps-image]: https://david-dm.org/louisbrunner/react-dnd-multi-backend/status.svg
|
238 | [deps-url]: https://david-dm.org/louisbrunner/react-dnd-multi-backend
|
239 | [deps-dev-image]: https://david-dm.org/louisbrunner/react-dnd-multi-backend/dev-status.svg
|
240 | [deps-dev-url]: https://david-dm.org/louisbrunner/react-dnd-multi-backend?type=dev
|