react-reorder-list 0.3.0 → 0.4.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 CHANGED
@@ -67,7 +67,7 @@ export default function App() {
67
67
  #### Listen to Children Updates
68
68
  `<ReorderList>` can listen to updates to it's children components using the `watchChildrenUpdates` prop as shown below.
69
69
 
70
- If set to `true`, updates to children like state changes, additions/omissions of children components will reflect in real time.
70
+ If set to `true`, updates to children like state changes, additions/omissions of children components will reflect in real time. Note that if an item is being dragged and an update occurs at the moment, that item will be placed at respective location and `onPositionChange` will be called to prevent any inconsistency.
71
71
 
72
72
  If set to `false`, any updates made in children component except reordering won't reflect.
73
73
  ```jsx
@@ -131,13 +131,21 @@ Here is the full API for the `<ReorderList>` component, these properties can be
131
131
  | `animationDuration` | `Number` | No | 400 | The duration (in ms) of swapping animation between items. If set to 0, animation will be disabled. |
132
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) |
133
133
  | `onPositionChange` | [`PositionChangeHandler`](#positionchangehandler) | No | - | Function to be executed on item position change. |
134
- | `disable` | `Boolean` | No | false | When set to true, `<ReorderList>` will work as a plain `div` with no functionality. |
134
+ | `disabled` | `Boolean` | No | false | When set to true, `<ReorderList>` will work as a plain `div` with no functionality. |
135
135
  | `props` | `React.DetailedHTMLProps` | No | - | Props to customize the `<ReorderList>` component. |
136
136
  ### Types
137
137
  #### PositionChangeHandler
138
138
  ```typescript
139
139
  import { ReactNode } from 'react';
140
- type PositionChangeHandler = (params?: { start?: number, end?: number, oldItems?: ReactNode, newItems?: ReactNode }) => void
140
+ type RevertHandler = () => void
141
+ type PositionChangeParams = {
142
+ start?: number // Index of the item being dragged
143
+ end?: number // Index of the item being displaced by the starting item
144
+ oldItems?: ReactNode[] // Array of children before reordering
145
+ newItems?: ReactNode[] // Array of children after reordering
146
+ revert: RevertHandler // A fallback handler to revert the reordering
147
+ }
148
+ type PositionChangeHandler = (params?: PositionChangeParams) => void
141
149
  ```
142
150
  ## Author
143
151
  [Sahil Aggarwal](https://www.github.com/SahilAggarwal2004)
@@ -1,7 +1,7 @@
1
- import React, { ReactNode } from "react";
1
+ import { ReactNode } from "react";
2
2
  type AnimationProps = {
3
3
  duration: number;
4
4
  children: ReactNode;
5
5
  };
6
- export default function Animation({ duration, children }: AnimationProps): string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null | undefined;
6
+ export default function Animation({ duration, children }: AnimationProps): ReactNode;
7
7
  export {};
package/dist/animation.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useLayoutEffect, Children, useEffect, useRef } from "react";
1
+ import { useState, useLayoutEffect, Children, useEffect, useRef } from "react";
2
2
  function usePrevious(value) {
3
3
  const prevChildrenRef = useRef();
4
4
  useEffect(() => { prevChildrenRef.current = value; }, [value]);
@@ -11,15 +11,17 @@ function calculateBoundingBoxes(children) {
11
11
  return boundingBoxes;
12
12
  }
13
13
  ;
14
- function Animate({ duration, children }) {
14
+ export default function Animation({ duration, children }) {
15
15
  const [boundingBox, setBoundingBox] = useState({});
16
16
  const prevBoundingBox = usePrevious(boundingBox);
17
17
  useLayoutEffect(() => {
18
- const newBoundingBox = calculateBoundingBoxes(children);
19
- setBoundingBox(newBoundingBox);
18
+ if (duration > 0)
19
+ setBoundingBox(calculateBoundingBoxes(children));
20
+ else
21
+ setBoundingBox({});
20
22
  }, [children]);
21
23
  useLayoutEffect(() => {
22
- if (prevBoundingBox && Object.keys(prevBoundingBox).length)
24
+ if (duration > 0 && prevBoundingBox && Object.keys(prevBoundingBox).length)
23
25
  Children.forEach(children, child => {
24
26
  const domNode = child.ref.current;
25
27
  const key = child.key.split("/.")[0];
@@ -40,6 +42,3 @@ function Animate({ duration, children }) {
40
42
  return children;
41
43
  }
42
44
  ;
43
- export default function Animation({ duration, children }) {
44
- return duration > 0 ? React.createElement(Animate, { duration: duration }, children) : children;
45
- }
package/dist/index.d.ts CHANGED
@@ -3,8 +3,9 @@ export type Props = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivEle
3
3
  export type PositionChangeHandler = (params?: {
4
4
  start?: number;
5
5
  end?: number;
6
- oldItems?: ReactNode;
7
- newItems?: ReactNode;
6
+ oldItems?: ReactNode[];
7
+ newItems?: ReactNode[];
8
+ revert?: () => void;
8
9
  }) => void;
9
10
  export type ReorderListProps = {
10
11
  useOnlyIconToDrag?: boolean;
@@ -12,10 +13,10 @@ export type ReorderListProps = {
12
13
  animationDuration?: number;
13
14
  watchChildrenUpdates: boolean;
14
15
  onPositionChange?: PositionChangeHandler;
15
- disable?: boolean;
16
+ disabled?: boolean;
16
17
  props?: Props;
17
18
  children?: ReactNode;
18
19
  };
19
20
  export type { IconProps } from './icons.js';
20
- export default function ReorderList({ useOnlyIconToDrag, selectedItemOpacity, animationDuration, watchChildrenUpdates, onPositionChange, disable, props, children }: ReorderListProps): React.JSX.Element;
21
+ export default function ReorderList({ useOnlyIconToDrag, selectedItemOpacity, animationDuration, watchChildrenUpdates, onPositionChange, disabled, props, children }: ReorderListProps): React.JSX.Element;
21
22
  export declare const ReorderIcon: ({ children, style, ...props }: Props) => React.JSX.Element;
package/dist/index.js CHANGED
@@ -13,18 +13,20 @@ import React, { Children, cloneElement, createRef, forwardRef, isValidElement, u
13
13
  import { PiDotsSixVerticalBold } from "./icons.js";
14
14
  import Animation from "./animation.js";
15
15
  const ReorderItemRef = forwardRef(ReorderItem);
16
- export default function ReorderList({ useOnlyIconToDrag = false, selectedItemOpacity = 0.5, animationDuration = 400, watchChildrenUpdates = false, onPositionChange, disable = false, props, children }) {
16
+ export default function ReorderList({ useOnlyIconToDrag = false, selectedItemOpacity = 0.5, animationDuration = 400, watchChildrenUpdates = false, onPositionChange, disabled = false, props, children }) {
17
17
  const ref = useRef(null);
18
- const [draggable, setDraggable] = useState(false);
19
18
  const [start, setStart] = useState(-1);
20
19
  const [selected, setSelected] = useState(-1);
21
20
  const [items, setItems] = useState(Children.toArray(children));
22
21
  const [temp, setTemp] = useState({});
23
22
  const [isAnimating, setIsAnimating] = useState(false);
23
+ const refs = useMemo(() => items.map(_ => createRef()), [items]);
24
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
25
  useEffect(() => {
26
26
  if (!watchChildrenUpdates)
27
27
  return;
28
+ if (selected !== -1)
29
+ handleDragEnd(selected);
28
30
  const items = [];
29
31
  const newItems = [];
30
32
  Children.forEach(children, child => {
@@ -37,9 +39,15 @@ export default function ReorderList({ useOnlyIconToDrag = false, selectedItemOpa
37
39
  });
38
40
  setItems(items.filter(item => item !== undefined).concat(newItems));
39
41
  }, [children]);
40
- return React.createElement("div", Object.assign({ ref: ref }, props), disable ? children : React.createElement(Animation, { duration: +draggable && animationDuration }, Children.map(items, (child, i) => {
42
+ function handleDragEnd(end) {
43
+ if (end !== start)
44
+ onPositionChange === null || onPositionChange === void 0 ? void 0 : onPositionChange({ start, end, oldItems: temp.items, newItems: items, revert: () => setItems(temp.items) });
45
+ setStart(-1);
46
+ setSelected(-1);
47
+ }
48
+ return React.createElement("div", Object.assign({ ref: ref }, props), disabled ? children : React.createElement(Animation, { duration: +(start !== -1) && animationDuration }, Children.map(items, (child, i) => {
41
49
  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 => {
50
+ 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 }, onDragStart: event => {
43
51
  event.stopPropagation();
44
52
  setStart(i);
45
53
  setSelected(i);
@@ -62,17 +70,17 @@ export default function ReorderList({ useOnlyIconToDrag = false, selectedItemOpa
62
70
  setTimeout(() => setIsAnimating(false), animationDuration);
63
71
  }, onDragEnd: event => {
64
72
  event.stopPropagation();
65
- if (i !== start)
66
- onPositionChange === null || onPositionChange === void 0 ? void 0 : onPositionChange({ start, end: i, oldItems: temp.items, newItems: items });
67
- setStart(-1);
68
- setSelected(-1);
69
- }, onPointerEnter: () => setDraggable(true), onPointerLeave: () => setDraggable(false) }, child);
73
+ handleDragEnd(i);
74
+ } }, child);
70
75
  })));
71
76
  }
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 });
77
+ function ReorderItem({ useOnlyIconToDrag, style, onDragStart, onDragEnter, onDragEnd, children }, ref) {
78
+ const [draggable, setDraggable] = useState(false);
79
+ const onPointerEnter = () => setDraggable(true);
80
+ const onPointerLeave = () => setDraggable(false);
81
+ let props = { draggable, onDragStart, onDragEnter, onDragEnd };
82
+ if (!useOnlyIconToDrag)
83
+ props = Object.assign(Object.assign({}, props), { onPointerEnter, onPointerLeave });
76
84
  const recursiveClone = (children) => Children.map(children, child => {
77
85
  if (!isValidElement(child))
78
86
  return child;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-reorder-list",
3
- "version": "0.3.0",
3
+ "version": "0.4.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",