react-reorder-list 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -6
- package/dist/animation.js +7 -11
- package/dist/index.d.ts +3 -2
- package/dist/index.js +39 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,8 @@ A simple react component that facilitates the reordering of JSX/HTML elements th
|
|
|
3
3
|
## Features
|
|
4
4
|
- Reorders list of elements using drag and drop.
|
|
5
5
|
- Easy to use
|
|
6
|
-
-
|
|
6
|
+
- Smooth transition using animation.
|
|
7
|
+
- Listen to children updates. See [listen to children updates](#listen-to-children-updates)
|
|
7
8
|
- Handles nested lists easily. See [nested list usage](#nested-list-usage)
|
|
8
9
|
## Installation
|
|
9
10
|
To install react-reorder-list
|
|
@@ -25,7 +26,7 @@ To install react-reorder-list
|
|
|
25
26
|
|
|
26
27
|
Items in this list can be reordered by simply dragging an item and dropping it in place of another item.
|
|
27
28
|
#### Basic Usage
|
|
28
|
-
|
|
29
|
+
Each `<div>` component inside `<ReorderList>` can now be drag-and-dropped to another `<div>` to reorder them.
|
|
29
30
|
```jsx
|
|
30
31
|
import React from 'react'
|
|
31
32
|
import ReorderList from 'react-reorder-list'
|
|
@@ -34,13 +35,17 @@ export default function App() {
|
|
|
34
35
|
return <ReorderList>
|
|
35
36
|
{[0, 1, 2, 3, 4].map(i => {
|
|
36
37
|
{/* Having a unique key is important */}
|
|
37
|
-
return <div key={i}>{i}</div>
|
|
38
|
+
return <div key={i}>Item {i}</div>
|
|
38
39
|
})}
|
|
39
40
|
</ReorderList>
|
|
40
41
|
}
|
|
41
42
|
```
|
|
42
43
|
#### Usage with ReorderIcon
|
|
43
|
-
|
|
44
|
+
The dragging behavior can be changed using the `useOnlyIconToDrag` prop of `<ReorderList>` component.
|
|
45
|
+
|
|
46
|
+
If set to `true`, an item can be dragged only using the `<ReorderIcon>` present inside the item.
|
|
47
|
+
|
|
48
|
+
If set to `false`, an item can be dragged by clicking anywhere inside the item.
|
|
44
49
|
```jsx
|
|
45
50
|
import React from 'react'
|
|
46
51
|
import ReorderList, { ReorderIcon } from 'react-reorder-list'
|
|
@@ -59,6 +64,40 @@ export default function App() {
|
|
|
59
64
|
</ReorderList>
|
|
60
65
|
}
|
|
61
66
|
```
|
|
67
|
+
#### Listen to Children Updates
|
|
68
|
+
`<ReorderList>` can listen to updates to it's children components using the `watchChildrenUpdates` prop as shown below.
|
|
69
|
+
|
|
70
|
+
If set to `true`, updates to children like state changes, additions/omissions of children components will reflect in real time.
|
|
71
|
+
|
|
72
|
+
If set to `false`, any updates made in children component except reordering won't reflect.
|
|
73
|
+
```jsx
|
|
74
|
+
import React, { useState } from 'react'
|
|
75
|
+
import ReorderList from 'react-reorder-list'
|
|
76
|
+
|
|
77
|
+
export default function App() {
|
|
78
|
+
const [array, setArray] = useState([0, 1, 2, 3, 4])
|
|
79
|
+
|
|
80
|
+
function setNewArray() {
|
|
81
|
+
setArray(prev => {
|
|
82
|
+
const array = []
|
|
83
|
+
prev.forEach(_ => {
|
|
84
|
+
do {
|
|
85
|
+
var item = Math.floor(Math.random() * 9)
|
|
86
|
+
} while (array.includes(item))
|
|
87
|
+
array.push(item)
|
|
88
|
+
})
|
|
89
|
+
return array
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return <div>
|
|
94
|
+
<ReorderList watchChildrenUpdates={true} animationDuration={200}>
|
|
95
|
+
{array.map(i => <div key={i}>Item {i}</div>)}
|
|
96
|
+
</ReorderList>
|
|
97
|
+
<button onClick={setNewArray}>Click me</button>
|
|
98
|
+
</div>
|
|
99
|
+
}
|
|
100
|
+
```
|
|
62
101
|
#### Nested List Usage
|
|
63
102
|
```jsx
|
|
64
103
|
import React from 'react'
|
|
@@ -68,6 +107,7 @@ export default function App() {
|
|
|
68
107
|
return <ReorderList>
|
|
69
108
|
{[0, 1, 2].map(i => {
|
|
70
109
|
return <div key={i}>
|
|
110
|
+
<ReorderIcon />
|
|
71
111
|
<span>{'Parent' + i}</span>
|
|
72
112
|
<ReorderList useOnlyIconToDrag={true}>
|
|
73
113
|
{[0, 1, 2].map(j => {
|
|
@@ -88,9 +128,10 @@ Here is the full API for the `<ReorderList>` component, these properties can be
|
|
|
88
128
|
| - | - | - | - | - |
|
|
89
129
|
| `useOnlyIconToDrag` | `Boolean` | No | false | See [usage with ReorderIcon](#usage-with-reordericon) |
|
|
90
130
|
| `selectedItemOpacity` | `Number (0 to 1)` | No | 0.5 | This determines the opacity of the item being dragged, until released. |
|
|
91
|
-
| `animationDuration` | `Number` | No | 400 | The duration of swapping animation between items. If set to 0, animation will be disabled. |
|
|
131
|
+
| `animationDuration` | `Number` | No | 400 | The duration (in ms) of swapping animation between items. If set to 0, animation will be disabled. |
|
|
132
|
+
| `watchChildrenUpdates` | `Boolean` | No | false | Enable this to listen to any updates in children of `<ReorderList>` and update the state accordingly. See [listen to children updates](#listen-to-children-updates) |
|
|
92
133
|
| `onPositionChange` | [`PositionChangeHandler`](#positionchangehandler) | No | - | Function to be executed on item position change. |
|
|
93
|
-
| `disable` | `Boolean` | No | false | When set to true,
|
|
134
|
+
| `disable` | `Boolean` | No | false | When set to true, `<ReorderList>` will work as a plain `div` with no functionality. |
|
|
94
135
|
| `props` | `React.DetailedHTMLProps` | No | - | Props to customize the `<ReorderList>` component. |
|
|
95
136
|
### Types
|
|
96
137
|
#### PositionChangeHandler
|
package/dist/animation.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useLayoutEffect, Children,
|
|
1
|
+
import React, { useState, useLayoutEffect, Children, useEffect, useRef } from "react";
|
|
2
2
|
function usePrevious(value) {
|
|
3
3
|
const prevChildrenRef = useRef();
|
|
4
4
|
useEffect(() => { prevChildrenRef.current = value; }, [value]);
|
|
@@ -7,10 +7,7 @@ function usePrevious(value) {
|
|
|
7
7
|
;
|
|
8
8
|
function calculateBoundingBoxes(children) {
|
|
9
9
|
const boundingBoxes = {};
|
|
10
|
-
Children.forEach(children,
|
|
11
|
-
if (isValidElement(child))
|
|
12
|
-
boundingBoxes[child.key || i] = child.ref.current.getBoundingClientRect();
|
|
13
|
-
});
|
|
10
|
+
Children.forEach(children, child => boundingBoxes[child.key.split("/.")[0]] = child.ref.current.getBoundingClientRect());
|
|
14
11
|
return boundingBoxes;
|
|
15
12
|
}
|
|
16
13
|
;
|
|
@@ -23,12 +20,11 @@ function Animate({ duration, children }) {
|
|
|
23
20
|
}, [children]);
|
|
24
21
|
useLayoutEffect(() => {
|
|
25
22
|
if (prevBoundingBox && Object.keys(prevBoundingBox).length)
|
|
26
|
-
Children.forEach(children,
|
|
27
|
-
if (!isValidElement(child))
|
|
28
|
-
return;
|
|
23
|
+
Children.forEach(children, child => {
|
|
29
24
|
const domNode = child.ref.current;
|
|
30
|
-
const
|
|
31
|
-
const { left, top } =
|
|
25
|
+
const key = child.key.split("/.")[0];
|
|
26
|
+
const { left: prevLeft, top: prevTop } = prevBoundingBox[key] || {};
|
|
27
|
+
const { left, top } = boundingBox[key];
|
|
32
28
|
const changeInX = prevLeft - left, changeInY = prevTop - top;
|
|
33
29
|
if (changeInX || changeInY)
|
|
34
30
|
requestAnimationFrame(() => {
|
|
@@ -45,5 +41,5 @@ function Animate({ duration, children }) {
|
|
|
45
41
|
}
|
|
46
42
|
;
|
|
47
43
|
export default function Animation({ duration, children }) {
|
|
48
|
-
return duration ? React.createElement(Animate, { duration: duration }, children) : children;
|
|
44
|
+
return duration > 0 ? React.createElement(Animate, { duration: duration }, children) : children;
|
|
49
45
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { DetailedHTMLProps, HTMLAttributes, ReactNode } from
|
|
1
|
+
import React, { DetailedHTMLProps, HTMLAttributes, ReactNode } from "react";
|
|
2
2
|
export type Props = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
|
3
3
|
export type PositionChangeHandler = (params?: {
|
|
4
4
|
start?: number;
|
|
@@ -10,11 +10,12 @@ export type ReorderListProps = {
|
|
|
10
10
|
useOnlyIconToDrag?: boolean;
|
|
11
11
|
selectedItemOpacity?: number;
|
|
12
12
|
animationDuration?: number;
|
|
13
|
+
watchChildrenUpdates: boolean;
|
|
13
14
|
onPositionChange?: PositionChangeHandler;
|
|
14
15
|
disable?: boolean;
|
|
15
16
|
props?: Props;
|
|
16
17
|
children?: ReactNode;
|
|
17
18
|
};
|
|
18
19
|
export type { IconProps } from './icons.js';
|
|
19
|
-
export default function ReorderList({ useOnlyIconToDrag, selectedItemOpacity, animationDuration, onPositionChange, disable, props, children }: ReorderListProps): React.JSX.Element;
|
|
20
|
+
export default function ReorderList({ useOnlyIconToDrag, selectedItemOpacity, animationDuration, watchChildrenUpdates, onPositionChange, disable, props, children }: ReorderListProps): React.JSX.Element;
|
|
20
21
|
export declare const ReorderIcon: ({ children, style, ...props }: Props) => React.JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -9,36 +9,37 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import React, { Children, cloneElement, createRef, forwardRef, isValidElement, useMemo, useRef, useState } from
|
|
13
|
-
import { PiDotsSixVerticalBold } from
|
|
14
|
-
import Animation from
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
return child;
|
|
20
|
-
const childProps = {};
|
|
21
|
-
if (useOnlyIconToDrag && child.type.name === 'ReorderIcon') {
|
|
22
|
-
childProps.onPointerEnter = onPointerEnter;
|
|
23
|
-
childProps.onPointerLeave = onPointerLeave;
|
|
24
|
-
}
|
|
25
|
-
return cloneElement(child, Object.assign({ children: recursiveClone(child.props.children) }, childProps));
|
|
26
|
-
});
|
|
27
|
-
const recursiveChildren = useMemo(() => recursiveClone(children), [children]);
|
|
28
|
-
return React.createElement("div", Object.assign({ ref: ref, style: style }, props), recursiveChildren);
|
|
29
|
-
});
|
|
30
|
-
export default function ReorderList({ useOnlyIconToDrag = false, selectedItemOpacity = 0.5, animationDuration = 400, onPositionChange, disable = false, props, children }) {
|
|
31
|
-
const ref = useRef();
|
|
32
|
-
const [draggable, setDraggable] = useState(!useOnlyIconToDrag);
|
|
12
|
+
import React, { Children, cloneElement, createRef, forwardRef, isValidElement, useEffect, useMemo, useRef, useState } from "react";
|
|
13
|
+
import { PiDotsSixVerticalBold } from "./icons.js";
|
|
14
|
+
import Animation from "./animation.js";
|
|
15
|
+
const ReorderItemRef = forwardRef(ReorderItem);
|
|
16
|
+
export default function ReorderList({ useOnlyIconToDrag = false, selectedItemOpacity = 0.5, animationDuration = 400, watchChildrenUpdates = false, onPositionChange, disable = false, props, children }) {
|
|
17
|
+
const ref = useRef(null);
|
|
18
|
+
const [draggable, setDraggable] = useState(false);
|
|
33
19
|
const [start, setStart] = useState(-1);
|
|
34
20
|
const [selected, setSelected] = useState(-1);
|
|
35
|
-
const [items, setItems] = useState(children);
|
|
21
|
+
const [items, setItems] = useState(Children.toArray(children));
|
|
36
22
|
const [temp, setTemp] = useState({});
|
|
37
23
|
const [isAnimating, setIsAnimating] = useState(false);
|
|
24
|
+
const findIndex = (key) => key ? items.findIndex(item => { var _a, _b; return ((_b = (_a = item === null || item === void 0 ? void 0 : item.key) === null || _a === void 0 ? void 0 : _a.split(".$").at(-1)) !== null && _b !== void 0 ? _b : item === null || item === void 0 ? void 0 : item.toString()) === key; }) : -1;
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!watchChildrenUpdates)
|
|
27
|
+
return;
|
|
28
|
+
const items = [];
|
|
29
|
+
const newItems = [];
|
|
30
|
+
Children.forEach(children, child => {
|
|
31
|
+
var _a;
|
|
32
|
+
const index = findIndex((_a = child === null || child === void 0 ? void 0 : child.key) !== null && _a !== void 0 ? _a : child === null || child === void 0 ? void 0 : child.toString());
|
|
33
|
+
if (index === -1)
|
|
34
|
+
newItems.push(child);
|
|
35
|
+
else
|
|
36
|
+
items[index] = child;
|
|
37
|
+
});
|
|
38
|
+
setItems(items.filter(item => item !== undefined).concat(newItems));
|
|
39
|
+
}, [children]);
|
|
38
40
|
return React.createElement("div", Object.assign({ ref: ref }, props), disable ? children : React.createElement(Animation, { duration: +draggable && animationDuration }, Children.map(items, (child, i) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return React.createElement(ReorderItem, { key: child.key || i, ref: createRef(), useOnlyIconToDrag: useOnlyIconToDrag, draggable: draggable, style: { opacity: selected === i ? selectedItemOpacity : 1 }, onDragStart: event => {
|
|
41
|
+
var _a;
|
|
42
|
+
return React.createElement(ReorderItemRef, { key: (_a = child === null || child === void 0 ? void 0 : child.key) !== null && _a !== void 0 ? _a : child === null || child === void 0 ? void 0 : child.toString(), ref: createRef(), useOnlyIconToDrag: useOnlyIconToDrag, draggable: draggable, style: { opacity: selected === i ? selectedItemOpacity : 1 }, onDragStart: event => {
|
|
42
43
|
event.stopPropagation();
|
|
43
44
|
setStart(i);
|
|
44
45
|
setSelected(i);
|
|
@@ -68,6 +69,19 @@ export default function ReorderList({ useOnlyIconToDrag = false, selectedItemOpa
|
|
|
68
69
|
}, onPointerEnter: () => setDraggable(true), onPointerLeave: () => setDraggable(false) }, child);
|
|
69
70
|
})));
|
|
70
71
|
}
|
|
72
|
+
function ReorderItem({ useOnlyIconToDrag, draggable, style, onDragStart, onDragEnter, onDragEnd, onPointerEnter, onPointerLeave, children }, ref) {
|
|
73
|
+
let props = useOnlyIconToDrag ? {} : { onPointerEnter, onPointerLeave };
|
|
74
|
+
if (draggable)
|
|
75
|
+
props = Object.assign(Object.assign({}, props), { draggable, onDragStart, onDragEnter, onDragEnd });
|
|
76
|
+
const recursiveClone = (children) => Children.map(children, child => {
|
|
77
|
+
if (!isValidElement(child))
|
|
78
|
+
return child;
|
|
79
|
+
const childProps = useOnlyIconToDrag && child.type.name === 'ReorderIcon' ? { onPointerEnter, onPointerLeave } : {};
|
|
80
|
+
return cloneElement(child, Object.assign({ children: recursiveClone(child.props.children) }, childProps));
|
|
81
|
+
});
|
|
82
|
+
const recursiveChildren = useMemo(() => recursiveClone(children), [children]);
|
|
83
|
+
return React.createElement("div", Object.assign({ ref: ref, style: style }, props), recursiveChildren);
|
|
84
|
+
}
|
|
71
85
|
export const ReorderIcon = (_a) => {
|
|
72
86
|
var { children = React.createElement(PiDotsSixVerticalBold, null), style } = _a, props = __rest(_a, ["children", "style"]);
|
|
73
87
|
return React.createElement("span", Object.assign({ style: Object.assign({ cursor: "grab" }, style) }, props), children);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-reorder-list",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A simple react component that facilitates the reordering of JSX/HTML elements through drag-and-drop functionality, allowing for easy position changes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|