react-reorder-list
Advanced tools
Comparing version 0.6.0 to 0.6.1
import { useState, useLayoutEffect, Children, useEffect, useRef } from "react"; | ||
function usePrevious(value) { | ||
const prevChildrenRef = useRef(); | ||
useEffect(() => { prevChildrenRef.current = value; }, [value]); | ||
useEffect(() => { | ||
prevChildrenRef.current = value; | ||
}, [value]); | ||
return prevChildrenRef.current; | ||
} | ||
; | ||
function calculateBoundingBoxes(children) { | ||
const boundingBoxes = {}; | ||
Children.forEach(children, child => boundingBoxes[child.key.split("/.")[0]] = child.ref.current.getBoundingClientRect()); | ||
Children.forEach(children, (child) => (boundingBoxes[child.key.split("/.")[0]] = child.ref.current.getBoundingClientRect())); | ||
return boundingBoxes; | ||
} | ||
; | ||
export default function Animation({ duration, children }) { | ||
@@ -25,3 +25,3 @@ const [boundingBox, setBoundingBox] = useState({}); | ||
if (duration > 0 && prevBoundingBox && Object.keys(prevBoundingBox).length) | ||
Children.forEach(children, child => { | ||
Children.forEach(children, (child) => { | ||
const domNode = child.ref.current; | ||
@@ -45,2 +45,1 @@ const key = child.key.split("/.")[0]; | ||
} | ||
; |
import React from "react"; | ||
export function PiDotsSixVerticalBold(props) { | ||
return React.createElement("span", Object.assign({}, props), | ||
return (React.createElement("span", Object.assign({}, props), | ||
React.createElement("svg", { viewBox: "0 0 256 256", fill: "currentColor", width: "1.25rem", height: "1.25rem" }, | ||
React.createElement("path", { d: "M108,60A16,16,0,1,1,92,44,16,16,0,0,1,108,60Zm56,16a16,16,0,1,0-16-16A16,16,0,0,0,164,76ZM92,112a16,16,0,1,0,16,16A16,16,0,0,0,92,112Zm72,0a16,16,0,1,0,16,16A16,16,0,0,0,164,112ZM92,180a16,16,0,1,0,16,16A16,16,0,0,0,92,180Zm72,0a16,16,0,1,0,16,16A16,16,0,0,0,164,180Z" }))); | ||
React.createElement("path", { d: "M108,60A16,16,0,1,1,92,44,16,16,0,0,1,108,60Zm56,16a16,16,0,1,0-16-16A16,16,0,0,0,164,76ZM92,112a16,16,0,1,0,16,16A16,16,0,0,0,92,112Zm72,0a16,16,0,1,0,16,16A16,16,0,0,0,164,112ZM92,180a16,16,0,1,0,16,16A16,16,0,0,0,92,180Zm72,0a16,16,0,1,0,16,16A16,16,0,0,0,164,180Z" })))); | ||
} |
import React, { ReactNode } from "react"; | ||
import { Props } from "./hooks.js"; | ||
export type { Props } from './hooks.js'; | ||
export type { Props } from "./hooks.js"; | ||
export type PositionChangeHandler = (params?: { | ||
@@ -22,4 +22,4 @@ start?: number; | ||
}; | ||
export type { IconProps } from './icons.js'; | ||
export type { IconProps } from "./icons.js"; | ||
export default function ReorderList({ useOnlyIconToDrag, selectedItemOpacity, animationDuration, watchChildrenUpdates, preserveOrder, onPositionChange, disabled, props, children }: ReorderListProps): React.JSX.Element; | ||
export declare function ReorderIcon({ children, style, ...props }: Props): React.JSX.Element; |
@@ -17,3 +17,3 @@ var __rest = (this && this.__rest) || function (s, e) { | ||
if (typeof window !== "undefined") | ||
import('drag-drop-touch'); | ||
import("drag-drop-touch"); | ||
const scrollThreshold = { x: 10, y: 100 }; | ||
@@ -29,4 +29,4 @@ const ReorderItemRef = forwardRef(ReorderItem); | ||
const [scroll, setScroll] = useState(); | ||
const refs = useMemo(() => items.map(_ => createRef()), [items]); | ||
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; | ||
const refs = useMemo(() => items.map((_) => createRef()), [items]); | ||
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); | ||
useEffect(() => { | ||
@@ -40,3 +40,3 @@ if (!watchChildrenUpdates) | ||
const newItems = []; | ||
Children.forEach(children, child => { | ||
Children.forEach(children, (child) => { | ||
var _a; | ||
@@ -49,3 +49,3 @@ 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()); | ||
}); | ||
setItems(items.filter(item => item !== undefined).concat(newItems)); | ||
setItems(items.filter((item) => item !== undefined).concat(newItems)); | ||
} | ||
@@ -62,3 +62,3 @@ else | ||
if (left < 0 || top < 0 || scrollWidth - scrollX > innerWidth - left || scrollHeight - scrollY > innerHeight - top) | ||
scrollBy({ left, top, behavior: 'instant' }); | ||
scrollBy({ left, top, behavior: "instant" }); | ||
}, 20); | ||
@@ -74,5 +74,5 @@ return () => clearInterval(interval); | ||
} | ||
return React.createElement("div", Object.assign({ ref: ref }, props), disabled ? children : React.createElement(Animation, { duration: +(start !== -1 && !scroll) && animationDuration }, Children.map(items, (child, i) => { | ||
return (React.createElement("div", Object.assign({ ref: ref }, props), disabled ? (children) : (React.createElement(Animation, { duration: +(start !== -1 && !scroll) && animationDuration }, Children.map(items, (child, i) => { | ||
var _a; | ||
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: refs[i], useOnlyIconToDrag: useOnlyIconToDrag, style: { opacity: selected === i ? selectedItemOpacity : 1, touchAction: "pan-y" }, onDragStart: event => { | ||
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: refs[i], useOnlyIconToDrag: useOnlyIconToDrag, style: { opacity: selected === i ? selectedItemOpacity : 1, touchAction: "pan-y" }, onDragStart: (event) => { | ||
event.stopPropagation(); | ||
@@ -82,3 +82,3 @@ setStart(i); | ||
setTemp({ items, rect: ref.current.children[i].getBoundingClientRect() }); | ||
}, onDragEnter: event => { | ||
}, onDragEnter: (event) => { | ||
event.stopPropagation(); | ||
@@ -99,3 +99,3 @@ if (start === -1 || selected === i || isAnimating) | ||
setTimeout(() => setIsAnimating(false), animationDuration); | ||
}, onDragEnd: handleDragEnd, onTouchMove: event => { | ||
}, onDragEnd: handleDragEnd, onTouchMove: (event) => { | ||
const { clientX, screenX, clientY, screenY } = event.touches[0]; | ||
@@ -111,5 +111,5 @@ let left = 0, top = 0; | ||
top = 10; | ||
setScroll((left || top) ? { left, top } : undefined); | ||
}, onTouchEnd: () => setScroll(undefined) }, child); | ||
}))); | ||
setScroll(left || top ? { left, top } : undefined); | ||
}, onTouchEnd: () => setScroll(undefined) }, child)); | ||
}))))); | ||
} | ||
@@ -119,3 +119,3 @@ function ReorderItem(_a, ref) { | ||
const _b = useDraggable(), { draggable } = _b, _c = _b.draggableProps, { onTouchEnd: draggableOnTouchEnd } = _c, draggableProps = __rest(_c, ["onTouchEnd"]); | ||
const recursiveClone = (children) => Children.map(children, child => { | ||
const recursiveClone = (children) => Children.map(children, (child) => { | ||
if (!isValidElement(child)) | ||
@@ -125,11 +125,11 @@ return child; | ||
}); | ||
const recursiveChildren = useMemo(() => useOnlyIconToDrag ? recursiveClone(children) : children, [children]); | ||
return React.createElement("div", Object.assign({ ref: ref, draggable: draggable }, props, (!useOnlyIconToDrag && draggableProps), { onTouchEnd: event => { | ||
const recursiveChildren = useMemo(() => (useOnlyIconToDrag ? recursiveClone(children) : children), [useOnlyIconToDrag, children]); | ||
return (React.createElement("div", Object.assign({ ref: ref, draggable: draggable }, props, (!useOnlyIconToDrag && draggableProps), { onTouchEnd: (event) => { | ||
draggableOnTouchEnd(event); | ||
propOnTouchEnd(event); | ||
} }), recursiveChildren); | ||
} }), recursiveChildren)); | ||
} | ||
export function ReorderIcon(_a) { | ||
var { children = React.createElement(PiDotsSixVerticalBold, null), style } = _a, props = __rest(_a, ["children", "style"]); | ||
return React.createElement("span", Object.assign({ style: Object.assign({ cursor: "grab" }, style) }, props), children); | ||
return (React.createElement("span", Object.assign({ style: Object.assign({ cursor: "grab" }, style) }, props), children)); | ||
} |
{ | ||
"name": "react-reorder-list", | ||
"version": "0.6.0", | ||
"version": "0.6.1", | ||
"description": "A simple react component that facilitates the reordering of JSX/HTML elements through drag-and-drop functionality, allowing for easy position changes.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
169
README.md
# react-reorder-list | ||
A simple react component that facilitates the reordering of JSX/HTML elements through drag-and-drop functionality, allowing for easy position changes. | ||
## Features | ||
- Reorders list of elements using drag and drop. | ||
@@ -9,4 +12,7 @@ - Easy to use | ||
- Handles nested lists easily. See [nested list usage](#nested-list-usage) | ||
## Installation | ||
To install react-reorder-list | ||
```bash | ||
@@ -25,22 +31,30 @@ # with npm: | ||
``` | ||
## Usage | ||
`react-reorder-list` default exports `<ReorderList>` component which encapsulates all the list items as its children. | ||
Items in this list can be reordered by simply dragging an item and dropping it in place of another item. | ||
#### Basic Usage | ||
Each `<div>` component inside `<ReorderList>` can now be drag-and-dropped to another `<div>` to reorder them. | ||
```jsx | ||
import React from 'react' | ||
import ReorderList from 'react-reorder-list' | ||
import React from "react"; | ||
import ReorderList from "react-reorder-list"; | ||
export default function App() { | ||
return <ReorderList> | ||
{[0, 1, 2, 3, 4].map(i => { | ||
{/* Having a unique key is important */} | ||
return <div key={i}>Item {i}</div> | ||
})} | ||
return ( | ||
<ReorderList> | ||
{[0, 1, 2, 3, 4].map((i) => { | ||
return <div key={i}>Item {i}</div>; // Having a unique key is important | ||
})} | ||
</ReorderList> | ||
); | ||
} | ||
``` | ||
#### Usage with ReorderIcon | ||
The dragging behavior can be changed using the `useOnlyIconToDrag` prop of `<ReorderList>` component. | ||
@@ -51,2 +65,3 @@ | ||
If set to `true`, an item can be dragged only using the `<ReorderIcon>` present inside the item. | ||
```jsx | ||
@@ -57,16 +72,18 @@ import React from 'react' | ||
export default function App() { | ||
return <ReorderList useOnlyIconToDrag={true}> | ||
{[0, 1, 2, 3, 4].map(i => { | ||
return <div key={i}> | ||
<ReorderIcon /> {/* Default icon */} | ||
<ReorderIcon> | ||
{/* Custom icon/component */} | ||
</Reordericon> | ||
<span>{i}</span> | ||
</div> | ||
})} | ||
</ReorderList> | ||
return <ReorderList useOnlyIconToDrag={true}> | ||
{[0, 1, 2, 3, 4].map(i => { | ||
return <div key={i}> | ||
<ReorderIcon /> {/* Default icon */} | ||
<ReorderIcon> | ||
{/* Custom icon/component */} | ||
</Reordericon> | ||
<span>{i}</span> | ||
</div> | ||
})} | ||
</ReorderList> | ||
} | ||
``` | ||
#### Listen to Children Updates | ||
`<ReorderList>` can listen to updates to it's children components using the `watchChildrenUpdates` prop as shown below. | ||
@@ -81,55 +98,70 @@ | ||
NOTE: The props `watchChildrenUpdates` and `preserveOrder` should be used carefully to avoid any unexpected behaviour | ||
```jsx | ||
import React, { useState } from 'react' | ||
import ReorderList from 'react-reorder-list' | ||
import React, { useState } from "react"; | ||
import ReorderList from "react-reorder-list"; | ||
export default function App() { | ||
const [array, setArray] = useState([0, 1, 2, 3, 4]) | ||
const [array, setArray] = useState([0, 1, 2, 3, 4]); | ||
function setNewArray() { | ||
setArray(prev => { | ||
const array = [] | ||
prev.forEach(_ => { | ||
do { | ||
var item = Math.floor(Math.random() * 9) | ||
} while (array.includes(item)) | ||
array.push(item) | ||
}) | ||
return array | ||
}) | ||
} | ||
function setNewArray() { | ||
setArray((prev) => { | ||
const array = []; | ||
prev.forEach((_) => { | ||
do { | ||
var item = Math.floor(Math.random() * 9); | ||
} while (array.includes(item)); | ||
array.push(item); | ||
}); | ||
return array; | ||
}); | ||
} | ||
return <div> | ||
<ReorderList watchChildrenUpdates={true} animationDuration={200}> | ||
{array.map(i => <div key={i}>Item {i}</div>)} | ||
</ReorderList> | ||
<button onClick={setNewArray}>Click me</button> | ||
return ( | ||
<div> | ||
<ReorderList watchChildrenUpdates={true} animationDuration={200}> | ||
{array.map((i) => ( | ||
<div key={i}>Item {i}</div> | ||
))} | ||
</ReorderList> | ||
<button onClick={setNewArray}>Click me</button> | ||
</div> | ||
); | ||
} | ||
``` | ||
#### Nested List Usage | ||
```jsx | ||
import React from 'react' | ||
import ReorderList, { ReorderIcon } from 'react-reorder-list' | ||
import React from "react"; | ||
import ReorderList, { ReorderIcon } from "react-reorder-list"; | ||
export default function App() { | ||
return <ReorderList> | ||
{[0, 1, 2].map(i => { | ||
return <div key={i}> | ||
<ReorderIcon /> | ||
<span>{'Parent' + i}</span> | ||
<ReorderList useOnlyIconToDrag={true}> | ||
{[0, 1, 2].map(j => { | ||
return <div key={j} style={{ paddingLeft: '16px' }}> | ||
<ReorderIcon /> | ||
<span>{'Child' + i + j}</span> | ||
</div> | ||
})} | ||
</ReorderList> | ||
</div> | ||
})} | ||
return ( | ||
<ReorderList> | ||
{[0, 1, 2].map((i) => { | ||
return ( | ||
<div key={i}> | ||
<ReorderIcon /> | ||
<span>{"Parent" + i}</span> | ||
<ReorderList useOnlyIconToDrag={true}> | ||
{[0, 1, 2].map((j) => { | ||
return ( | ||
<div key={j} style={{ paddingLeft: "16px" }}> | ||
<ReorderIcon /> | ||
<span>{"Child" + i + j}</span> | ||
</div> | ||
); | ||
})} | ||
</ReorderList> | ||
</div> | ||
); | ||
})} | ||
</ReorderList> | ||
); | ||
} | ||
``` | ||
## ReorderList Component API Reference | ||
Here is the full API for the `<ReorderList>` component, these properties can be set on an instance of ReorderList: | ||
@@ -146,17 +178,22 @@ | Parameter | Type | Required | Default | Description | | ||
| `props` | `React.DetailedHTMLProps` | No | - | Props to customize the `<ReorderList>` component. | | ||
### Types | ||
#### PositionChangeHandler | ||
```typescript | ||
import { ReactNode } from 'react'; | ||
type RevertHandler = () => void | ||
type PositionChangeParams = { | ||
start?: number // Index of the item being dragged | ||
end?: number // Index of the item being displaced by the starting item | ||
oldItems?: ReactNode[] // Array of children before reordering | ||
newItems?: ReactNode[] // Array of children after reordering | ||
revert: RevertHandler // A fallback handler to revert the reordering | ||
} | ||
type PositionChangeHandler = (params?: PositionChangeParams) => void | ||
import { ReactNode } from "react"; | ||
type RevertHandler = () => void; | ||
type PositionChangeParams = { | ||
start?: number; // Index of the item being dragged | ||
end?: number; // Index of the item being displaced by the starting item | ||
oldItems?: ReactNode[]; // Array of children before reordering | ||
newItems?: ReactNode[]; // Array of children after reordering | ||
revert: RevertHandler; // A fallback handler to revert the reordering | ||
}; | ||
type PositionChangeHandler = (params?: PositionChangeParams) => void; | ||
``` | ||
## Author | ||
[Sahil Aggarwal](https://www.github.com/SahilAggarwal2004) | ||
[Sahil Aggarwal](https://www.github.com/SahilAggarwal2004) |
20932
194
223