UNPKG

7.9 kBMarkdownView Raw
1# `react-sortablejs`
2
3React bindings to [SortableJS](https://github.com/SortableJS/Sortable)
4
5[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
6
7Please note that this is not considered ready for production, as there are still a number of bugs being sent through.
8
9## Features
10
11## Installation
12
13`sortablejs` and `@types/sortablejs` are peer dependencies. The latter only used if intellisense/typescript is desired.
14
15```shell
16npm install --save react-sortablejs sortablejs
17npm install --save-dev @types/sortablejs
18
19# OR
20yarn add react-sortablejs sortablejs
21yarn add -D @types/sortablejs
22```
23
24## Learn
25
26Here is the TLDR of what sortable is:
27
28```md
29- Shopping List: # list of items / sortable. This represents `react-sortablejs`
30 - eggs # list item. These are all the items in the list and is what you move around.
31 - bread # list item
32 - milk # list item
33```
34
35## Usage/Examples
36
37### Function Component
38
39```tsx
40import React, { FC, useState } from "react";
41import { ReactSortable } from "react-sortablejs";
42
43interface ItemType {
44 id: number;
45 name: string;
46}
47
48export const BasicFunction: FC = (props) => {
49 const [state, setState] = useState<ItemType[]>([
50 { id: 1, name: "shrek" },
51 { id: 2, name: "fiona" },
52 ]);
53
54 return (
55 <ReactSortable list={state} setList={setState}>
56 {state.map((item) => (
57 <div key={item.id}>{item.name}</div>
58 ))}
59 </ReactSortable>
60 );
61};
62```
63
64### Class Component
65
66```tsx
67import React, { Component } from "react";
68import { ReactSortable } from "react-sortablejs";
69
70interface BasicClassState {
71 list: { id: string; name: string }[];
72}
73
74export class BasicClass extends Component<{}, BasicClassState> {
75 state: BasicClassState = {
76 list: [{ id: "1", name: "shrek" }],
77 };
78 render() {
79 return (
80 <ReactSortable
81 list={this.state.list}
82 setList={(newState) => this.setState({ list: newState })}
83 >
84 {this.state.list.map((item) => (
85 <div key={item.id}>{item.name}</div>
86 ))}
87 </ReactSortable>
88 );
89 }
90}
91```
92
93## Plugins
94
95Sortable has some pretty cool plugins such as MultiDrag and Swap.
96
97By Default:
98
99- AutoScroll is premounted and enabled.
100- OnSpill is premounted and NOT enabled.
101- MultiDrag and Swap and NOT premounted and NOT enabled
102
103You must mount the plugin with sortable **ONCE ONLY**.
104
105```tsx
106import React from "react";
107import { ReactSortable, Sortable, MultiDrag, Swap } from "react-sortablejs";
108
109// mount whatever plugins you'd like to. These are the only current options.
110Sortable.mount(new MultiDrag(), new Swap());
111
112const App = () => {
113 const [state, setState] = useState([
114 { id: 1, name: "shrek" },
115 { id: 2, name: "fiona" },
116 ]);
117
118 return (
119 <ReactSortable
120 multiDrag // enables mutidrag
121 // OR
122 swap // enables swap
123 >
124 {state.map((item) => (
125 <div key={item.id}>{item.name}</div>
126 ))}
127 </ReactSortable>
128 );
129};
130```
131
132## Sortable API
133
134For a comprehensive list of options, please visit https://github.com/SortableJS/Sortable#options.
135
136Those options are applied as follows.
137
138```tsx
139Sortable.create(element, {
140 group: " groupName",
141 animation: 200,
142 delayOnTouchStart: true,
143 delay: 2,
144});
145
146// --------------------------
147// Will now be..
148// --------------------------
149
150import React from "react";
151import { ReactSortable } from "react-sortablejs";
152
153const App = () => {
154 const [state, setState] = useState([
155 { id: 1, name: "shrek" },
156 { id: 2, name: "fiona" },
157 ]);
158
159 return (
160 <ReactSortable
161 // here they are!
162 group="groupName"
163 animation={200}
164 delayOnTouchStart={true}
165 delay={2}
166 >
167 {state.map((item) => (
168 <div key={item.id}>{item.name}</div>
169 ))}
170 </ReactSortable>
171 );
172};
173```
174
175## React API
176
177### id, className, style
178
179These are all defaults DOM attributes. Nothing special here.
180
181### list
182
183The same as `state` in `const [ state, setState] = useState([{ id: 1}, {id: 2}])`
184
185`state` must be an array of items, with each item being an object that has the following shape:
186
187```ts
188 /** The unique id associated with your item. It's recommended this is the same as the key prop for your list item. */
189 id: string | number;
190 /** When true, the item is selected using MultiDrag */
191 selected?: boolean;
192 /** When true, the item is deemed "chosen", which basically just a mousedown event. */
193 chosen?: boolean;
194 /** When true, it will not be possible to pick this item up in the list. */
195 filtered?: boolean;
196 [property: string]: any;
197```
198
199### setList
200
201The same as `setState` in `const [ state, setState] = useState([{ id: 1}, {id: 2}])`
202
203### clone
204
205If you're using `{group: { name: 'groupName', pull: 'clone'}}`, this means your in 'clone' mode. You should provide a function for this.
206
207Check out the source code of the clone example for more information. I'll write it here soon.
208
209### tag
210
211ReactSortable is a `div` element by default. This can be changed to be any HTML element (for example `ul`, `ol`)
212or can be a React component.
213
214This value, be the component or the HTML element should be passed down under `props.tag`.
215
216Let's explore both here.
217
218#### HTML Element
219
220Here we will use a `ul`. You can use any HTML.
221Just add the string and ReactSortable will use a `li` instead of a `div`.
222
223```tsx
224import React, { FC, useState } from "react";
225import { ReactSortable } from "react-sortablejs";
226
227export const BasicFunction: FC = (props) => {
228 const [state, setState] = useState([{ id: "1", name: "shrek" }]);
229
230 return (
231 <ReactSortable tag="ul" list={state} setList={setState}>
232 {state.map((item) => (
233 <li key={item.id}>{item.name}</li>
234 ))}
235 </ReactSortable>
236 );
237};
238```
239
240#### Custom Component
241
242When using a custom component in the `tag` prop, the only component it allows is a `forwardRef` component.
243Currently we only support components who use the `React.forwardRef` API.
244
245If it doesn't have one, you can add one using `React.forwardRef()`.
246
247> todo: Some third party UI components may have nested elements to create the look they're after.
248> This could be an issue and not sure how to fix.
249
250```tsx
251import React, { FC, useState, forwardRef } from "react";
252import { ReactSortable } from "react-sortablejs";
253
254// This is just like a normal component, but now has a ref.
255const CustomComponent = forwardRef<HTMLDivElement, any>((props, ref) => {
256 return <div ref={ref}>{props.children}</div>;
257});
258
259export const BasicFunction: FC = (props) => {
260 const [state, setState] = useState([
261 { id: 1, name: "shrek" },
262 { id: 2, name: "fiona" },
263 ]);
264
265 return (
266 <ReactSortable tag={CustomComponent} list={state} setList={setState}>
267 {state.map((item) => (
268 <div key={item.id}>{item.name}</div>
269 ))}
270 </ReactSortable>
271 );
272};
273```
274
275## How does it work?
276
277Sortable affects the DOM, adding, and removing nodes/css when it needs to in order to achieve the smooth transitions we all know an love.
278This component reverses many of it's actions of the DOM so React can handle this when the state changes.
279
280## Caveats / Gotchas
281
282### `key !== index`
283
284DO NOT use the index as a key for your list items. Sorting will not work.
285
286In all the examples above, I used an object with an ID. You should do the same!
287
288I may even enforce this into the design to eliminate errors.
289
290### Nesting
291
292#### Problem
293
294Basically the child updates the state twice. I'm working on this.
295
296#### What does work?
297
298Our usage indicates that as long as we only move items between lists that don't use the same `setState` function.
299
300I hope to provide an example soon.
301
302#### Solutions
303
304We don't have anything that works 100%, but here I'd like to spit ball some potential avenues to look down.
305
306- Use `onMove` to handle state changes instead of `onAdd`,`onRemove`, etc.
307- Create a Sortable plugin specifically for react-sortbalejs