UNPKG

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