@os-design/drag-sort 1.0.19 → 1.0.21

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.
@@ -0,0 +1,490 @@
1
+ import { Position } from '@os-design/use-drag';
2
+ import useEvent from '@os-design/use-event';
3
+ import useThrottle from '@os-design/use-throttle';
4
+ import {
5
+ CSSProperties,
6
+ RefObject,
7
+ useCallback,
8
+ useEffect,
9
+ useMemo,
10
+ useRef,
11
+ } from 'react';
12
+ import ListStore from './ListStore';
13
+ import NodeList, { ExistingNode, Node, NodeProps } from './NodeList';
14
+ import getElementScroll from './getElementScroll';
15
+ import getNodeRect from './getNodeRect';
16
+ import useBlankNode from './useBlankNode';
17
+ import useGetNodeStyle from './useGetNodeStyle';
18
+ import useInitRect from './useInitRect';
19
+ import useInitScrollOffset from './useInitScrollOffset';
20
+ import useMoveNode from './useMoveNode';
21
+ import useTargetList from './useTargetList';
22
+
23
+ export interface DraggedNode {
24
+ list: NodeList;
25
+ node: ExistingNode;
26
+ position: Position;
27
+ }
28
+
29
+ interface TargetNode {
30
+ list: NodeList;
31
+ node: ExistingNode;
32
+ }
33
+
34
+ export interface ItemLocation {
35
+ listId: string;
36
+ index: number;
37
+ id: string;
38
+ }
39
+
40
+ export type DragEndHandler = (
41
+ dragged: ItemLocation,
42
+ target: ItemLocation
43
+ ) => void;
44
+
45
+ interface UseDragEffectProps {
46
+ draggedNode: DraggedNode | null;
47
+ cursorPosition: Position;
48
+ listStoreRef: RefObject<ListStore>;
49
+ onDragEnd: DragEndHandler;
50
+ }
51
+
52
+ const HIDDEN_NODE_STYLE: CSSProperties = {
53
+ opacity: 0,
54
+ pointerEvents: 'none',
55
+ };
56
+
57
+ const createEmptyNode = ({
58
+ prev = null,
59
+ next = null,
60
+ }: {
61
+ prev?: Node;
62
+ next?: Node;
63
+ }): ExistingNode => [prev, next, { current: null }, () => {}, -1, 'id'];
64
+
65
+ /* eslint-disable no-constant-condition */
66
+
67
+ const useDragEffect = (props: UseDragEffectProps) => {
68
+ const { draggedNode, cursorPosition, listStoreRef, onDragEnd } = props;
69
+
70
+ const targetRef = useRef<ItemLocation | null>(null);
71
+ const targetNodeRef = useRef<TargetNode | null>(draggedNode);
72
+
73
+ useEffect(() => {
74
+ targetNodeRef.current = draggedNode;
75
+ }, [draggedNode]);
76
+
77
+ const onDragEndRef = useRef(onDragEnd);
78
+ useEffect(() => {
79
+ onDragEndRef.current = onDragEnd;
80
+ }, [onDragEnd]);
81
+
82
+ // The initial bounds of the dragged node.
83
+ // We can't read the bounds of the dragged node on the fly because the node can be unmounted in the virtual list.
84
+ const initDraggedNodeRect = useInitRect(
85
+ draggedNode ? draggedNode.node[2] : undefined
86
+ );
87
+
88
+ // The initial scroll position of the list where the dragged node is located.
89
+ // Used to detect the actual position of the dragged node. The purpose is the same as with initDraggedNodeRect.
90
+ const initDraggedNodeListScrollOffset = useInitScrollOffset(
91
+ draggedNode ? draggedNode.list.ref : undefined
92
+ );
93
+
94
+ // The central position of the dragged node
95
+ const position = useMemo(() => {
96
+ if (!draggedNode || !initDraggedNodeRect) return null;
97
+ const { x, y } = cursorPosition;
98
+ const { initWidth, initHeight } = initDraggedNodeRect;
99
+ return {
100
+ x: x - draggedNode.position.x + initWidth / 2,
101
+ y: y - draggedNode.position.y + initHeight / 2,
102
+ };
103
+ }, [cursorPosition, draggedNode, initDraggedNodeRect]);
104
+
105
+ // The list in which the cursor is located
106
+ const targetList = useTargetList(position, listStoreRef);
107
+
108
+ // Returns the style for moving the node in the specified direction
109
+ const getNodeStyle = useGetNodeStyle(initDraggedNodeRect);
110
+
111
+ // Moves the node up or down
112
+ const moveNode = useMoveNode({ position, draggedNode, getNodeStyle });
113
+
114
+ const setTargetNode = useCallback((list: NodeList, node: ExistingNode) => {
115
+ const [, , , , nodeIndex, nodeId] = node;
116
+ targetNodeRef.current = { list, node };
117
+ targetRef.current = {
118
+ listId: list.id,
119
+ index: nodeIndex,
120
+ id: nodeId,
121
+ };
122
+ }, []);
123
+
124
+ const clearTargetNode = useCallback(() => {
125
+ targetNodeRef.current = null;
126
+ targetRef.current = null;
127
+ }, []);
128
+
129
+ // Returns the central position of the dragged node in the list
130
+ const getDraggedNodePos = useCallback(() => {
131
+ if (
132
+ !targetList ||
133
+ !targetList.ref.current ||
134
+ !initDraggedNodeRect ||
135
+ !initDraggedNodeListScrollOffset
136
+ ) {
137
+ return null;
138
+ }
139
+ const { initX, initY, initWidth, initHeight } = initDraggedNodeRect;
140
+ const { initScrollLeft, initScrollTop } = initDraggedNodeListScrollOffset;
141
+ const { scrollLeft, scrollTop } = getElementScroll(targetList.ref.current);
142
+ return {
143
+ x: initX + initWidth / 2 + initScrollLeft - scrollLeft,
144
+ y: initY + initHeight / 2 + initScrollTop - scrollTop,
145
+ };
146
+ }, [initDraggedNodeListScrollOffset, initDraggedNodeRect, targetList]);
147
+
148
+ const getDraggedNodePosRef = useRef(getDraggedNodePos);
149
+ useEffect(() => {
150
+ getDraggedNodePosRef.current = getDraggedNodePos;
151
+ }, [getDraggedNodePos]);
152
+
153
+ // Hide the dragged node
154
+ useEffect(() => {
155
+ if (!draggedNode) return () => {};
156
+ const [, , , draggedNodeSetStyle] = draggedNode.node;
157
+ draggedNodeSetStyle(HIDDEN_NODE_STYLE);
158
+ return () => draggedNodeSetStyle({});
159
+ }, [draggedNode]);
160
+
161
+ // Append the blank node to the list to increase the height of it.
162
+ // Used when the dragged node is located inside another list.
163
+ const removeBlankNode = useBlankNode({
164
+ draggedNode,
165
+ targetList,
166
+ initDraggedNodeRect,
167
+ });
168
+
169
+ const updateTargetNode = useCallback(() => {
170
+ if (!draggedNode) return;
171
+ const prevTargetNode = targetNodeRef.current; // The last updated node
172
+
173
+ // Dragging outside the origin list
174
+ if (
175
+ prevTargetNode &&
176
+ prevTargetNode.list === draggedNode.list &&
177
+ targetList !== draggedNode.list
178
+ ) {
179
+ moveNode({
180
+ list: prevTargetNode.list,
181
+ startNode: prevTargetNode.node,
182
+ direction: 'down',
183
+ destination: 'end',
184
+ });
185
+ clearTargetNode();
186
+ }
187
+
188
+ // Dragging outside another list
189
+ if (
190
+ prevTargetNode &&
191
+ prevTargetNode.list !== draggedNode.list &&
192
+ targetList !== prevTargetNode.list
193
+ ) {
194
+ moveNode({
195
+ list: prevTargetNode.list,
196
+ startNode: prevTargetNode.node,
197
+ direction: 'down',
198
+ destination: 'end',
199
+ isAnotherList: true,
200
+ });
201
+ removeBlankNode();
202
+ clearTargetNode();
203
+ }
204
+
205
+ // Dragging inside the origin list
206
+ if (!prevTargetNode && targetList === draggedNode.list) {
207
+ const tail = targetList.getTail();
208
+ if (!tail) return;
209
+ const node = moveNode({
210
+ list: targetList,
211
+ startNode: tail,
212
+ direction: 'up',
213
+ destination: 'cursor',
214
+ });
215
+ setTargetNode(targetList, node);
216
+ return;
217
+ }
218
+
219
+ // Dragging inside another list
220
+ if (
221
+ targetList &&
222
+ targetList !== draggedNode.list &&
223
+ (!prevTargetNode || prevTargetNode.list !== targetList)
224
+ ) {
225
+ const tail = targetList.getTail();
226
+ if (!tail) return;
227
+ const node = moveNode({
228
+ list: targetList,
229
+ startNode: tail,
230
+ direction: 'up',
231
+ destination: 'cursor',
232
+ isAnotherList: true,
233
+ });
234
+ setTargetNode(targetList, node);
235
+ return;
236
+ }
237
+
238
+ // Dragging in the origin list
239
+ if (
240
+ prevTargetNode &&
241
+ prevTargetNode.list === draggedNode.list &&
242
+ targetList === draggedNode.list
243
+ ) {
244
+ const axis = targetList.horizontal ? 'x' : 'y';
245
+ const [, , prevTargetNodeRef, ,] = prevTargetNode.node;
246
+ if (!prevTargetNodeRef.current) {
247
+ // The target node was unmounted. It happens when the virtual list is used.
248
+ // If the cursor is above the dragged node, we need to move the nodes down from the tail to the node where
249
+ // the cursor is located. Otherwise, we need to move the nodes up from the head to the node where the cursor
250
+ // is located.
251
+ const draggedNodePos = getDraggedNodePosRef.current();
252
+ if (!position || !draggedNodePos) return;
253
+ const isDraggingUp = position[axis] < draggedNodePos[axis];
254
+ if (isDraggingUp) {
255
+ const tail = targetList.getTail();
256
+ if (!tail || !tail[2].current) return;
257
+ const node = moveNode({
258
+ list: targetList,
259
+ startNode: createEmptyNode({ prev: tail }),
260
+ direction: 'up',
261
+ destination: 'cursor',
262
+ });
263
+ setTargetNode(targetList, node);
264
+ } else {
265
+ const head = targetList.getHead();
266
+ if (!head || !head[2].current) return;
267
+ const node = moveNode({
268
+ list: targetList,
269
+ startNode: createEmptyNode({ next: head }),
270
+ direction: 'down',
271
+ destination: 'cursor',
272
+ });
273
+ setTargetNode(targetList, node);
274
+ }
275
+ return;
276
+ }
277
+ const prevTargetNodeRect = getNodeRect(prevTargetNode.node[2]);
278
+ const startRectProp = targetList.horizontal ? 'left' : 'top';
279
+ if (!position || !prevTargetNodeRect) return;
280
+ const isMoveUp = position[axis] < prevTargetNodeRect[startRectProp];
281
+ const node = isMoveUp
282
+ ? moveNode({
283
+ list: targetList,
284
+ startNode: prevTargetNode.node,
285
+ direction: 'up',
286
+ destination: 'cursor',
287
+ })
288
+ : moveNode({
289
+ list: targetList,
290
+ startNode: prevTargetNode.node,
291
+ direction: 'down',
292
+ destination: 'cursor',
293
+ });
294
+ setTargetNode(targetList, node);
295
+ return;
296
+ }
297
+
298
+ // Dragging in another list
299
+ if (
300
+ targetList &&
301
+ targetList !== draggedNode.list &&
302
+ prevTargetNode &&
303
+ prevTargetNode.list === targetList
304
+ ) {
305
+ const axis = targetList.horizontal ? 'x' : 'y';
306
+ const prevTargetNodeRect = getNodeRect(prevTargetNode.node[2]);
307
+ const startRectProp = targetList.horizontal ? 'left' : 'top';
308
+ if (!position || !prevTargetNodeRect) return;
309
+ const isMoveUp = position[axis] < prevTargetNodeRect[startRectProp];
310
+ const node = isMoveUp
311
+ ? moveNode({
312
+ list: targetList,
313
+ startNode: prevTargetNode.node,
314
+ direction: 'up',
315
+ destination: 'cursor',
316
+ isAnotherList: true,
317
+ })
318
+ : moveNode({
319
+ list: targetList,
320
+ startNode: prevTargetNode.node,
321
+ direction: 'down',
322
+ destination: 'cursor',
323
+ isAnotherList: true,
324
+ });
325
+ setTargetNode(targetList, node);
326
+ }
327
+ }, [
328
+ draggedNode,
329
+ targetList,
330
+ moveNode,
331
+ clearTargetNode,
332
+ removeBlankNode,
333
+ setTargetNode,
334
+ position,
335
+ ]);
336
+
337
+ // Update the target node if either the position or the target list has been changed
338
+ useEffect(() => {
339
+ updateTargetNode();
340
+ }, [updateTargetNode]);
341
+
342
+ // Update the target node if the target list has been scrolled
343
+ const [throttledUpdateTargetNode] = useThrottle(updateTargetNode, 100);
344
+ useEvent(
345
+ (targetList ? targetList.ref : undefined) as unknown as EventTarget,
346
+ 'scroll',
347
+ throttledUpdateTargetNode
348
+ );
349
+ useEvent(window, 'scroll', throttledUpdateTargetNode);
350
+
351
+ // Reset styles of the affected nodes when the dragged node was dropped
352
+ useEffect(() => {
353
+ if (!draggedNode) return () => {};
354
+ const [, , , , draggedNodeIndex] = draggedNode.node;
355
+ return () => {
356
+ const targetNode = targetNodeRef.current;
357
+
358
+ // If the dragged node was outside the origin list, reset the styles for the nodes,
359
+ // starting at the tail and ending with the dragged node in the origin list.
360
+ if (!targetNode || targetNode.list !== draggedNode.list) {
361
+ const tail = draggedNode.list.getTail();
362
+ if (tail) {
363
+ let node = tail;
364
+ while (true) {
365
+ const [prev, , , nodeSetStyle, nodeIndex] = node;
366
+ nodeSetStyle({});
367
+ if (!prev || nodeIndex <= draggedNodeIndex) break;
368
+ node = prev;
369
+ }
370
+ }
371
+ }
372
+
373
+ // If the dragged node was inside another list, reset the styles for the nodes,
374
+ // starting at the tail and ending with the dragged node in the target list.
375
+ if (targetNode && targetNode.list !== draggedNode.list) {
376
+ const tail = targetNode.list.getTail();
377
+ const [, , , , targetNodeIndex] = targetNode.node;
378
+ if (tail) {
379
+ let node = tail;
380
+ while (true) {
381
+ const [prev, , , nodeSetStyle, nodeIndex] = node;
382
+ nodeSetStyle({});
383
+ if (!prev || nodeIndex <= targetNodeIndex) break;
384
+ node = prev;
385
+ }
386
+ }
387
+ }
388
+
389
+ // If the dragged node was moved inside the origin list, reset the styles for the nodes,
390
+ // starting with target node and ending with the dragged node.
391
+ if (targetNode && targetNode.list === draggedNode.list) {
392
+ const [, , , targetNodeSetStyle, targetNodeIndex] = targetNode.node;
393
+ if (targetNodeIndex > draggedNodeIndex) {
394
+ let { node } = targetNode;
395
+ while (true) {
396
+ const [prev, , , nodeSetStyle, nodeIndex] = node;
397
+ nodeSetStyle({});
398
+ if (!prev || nodeIndex <= draggedNodeIndex) break;
399
+ node = prev;
400
+ }
401
+ } else if (targetNodeIndex < draggedNodeIndex) {
402
+ let { node } = targetNode;
403
+ while (true) {
404
+ const [, next, , nodeSetStyle, nodeIndex] = node;
405
+ nodeSetStyle({});
406
+ if (!next || nodeIndex >= draggedNodeIndex) break;
407
+ node = next;
408
+ }
409
+ } else if (targetNodeIndex === draggedNodeIndex) {
410
+ targetNodeSetStyle({});
411
+ }
412
+ }
413
+ };
414
+ }, [draggedNode]);
415
+
416
+ // Update the position of the newly mounted nodes in the origin list (used in the virtual list)
417
+ useEffect(() => {
418
+ if (!draggedNode) return () => {};
419
+ const [, , , , draggedNodeIndex] = draggedNode.node;
420
+
421
+ const update = (nodeProps: NodeProps) => {
422
+ const { setStyle, index } = nodeProps;
423
+ const targetNode = targetNodeRef.current;
424
+
425
+ // Set the hidden style, if the mounted node is the dragged node
426
+ if (index === draggedNodeIndex) {
427
+ setStyle(HIDDEN_NODE_STYLE);
428
+ return;
429
+ }
430
+
431
+ // If the dragged node is inside the origin list
432
+ if (targetNode && targetNode.list === draggedNode.list) {
433
+ // Move the mounted node up/down, if it is located between the dragged and target node
434
+ const [, , , , targetNodeIndex] = targetNode.node;
435
+ if (index > draggedNodeIndex && index < targetNodeIndex) {
436
+ setStyle(getNodeStyle('up', targetNode.list.horizontal));
437
+ } else if (index < draggedNodeIndex && index > targetNodeIndex) {
438
+ setStyle(getNodeStyle('down', targetNode.list.horizontal));
439
+ }
440
+ } else if (index > draggedNodeIndex) {
441
+ // Otherwise, move the mounted node up, if it is located below the dragged node
442
+ setStyle(getNodeStyle('up', draggedNode.list.horizontal));
443
+ }
444
+ };
445
+
446
+ draggedNode.list.addListener(update);
447
+ return () => draggedNode.list.removeListener(update);
448
+ }, [draggedNode, getNodeStyle]);
449
+
450
+ // Update the position of the newly mounted nodes in the target list (used in the virtual list)
451
+ useEffect(() => {
452
+ if (!draggedNode || !targetList || targetList === draggedNode.list) {
453
+ return () => {};
454
+ }
455
+
456
+ const update = (nodeProps: NodeProps) => {
457
+ const { setStyle, index } = nodeProps;
458
+ const targetNode = targetNodeRef.current;
459
+ if (!targetNode) return;
460
+ const [, , , , targetNodeIndex] = targetNode.node;
461
+ if (index >= targetNodeIndex) {
462
+ // Move the mounted node down, if it is located below the target node or if it is the target node
463
+ setStyle(getNodeStyle('down', targetList.horizontal));
464
+ }
465
+ };
466
+
467
+ targetList.addListener(update);
468
+ return () => targetList.removeListener(update);
469
+ }, [draggedNode, getNodeStyle, targetList]);
470
+
471
+ // Call the onDragEnd callback if the draggedNode was changed to null
472
+ useEffect(() => {
473
+ if (!draggedNode) return () => {};
474
+ return () => {
475
+ // eslint-disable-next-line react-hooks/exhaustive-deps
476
+ const target = targetRef.current;
477
+ if (!target) return;
478
+ const [, , , , draggedNodeIndex, draggedNodeId] = draggedNode.node;
479
+ const dragged = {
480
+ listId: draggedNode.list.id,
481
+ index: draggedNodeIndex,
482
+ id: draggedNodeId,
483
+ };
484
+ if (dragged.id === target.id && dragged.index === target.index) return;
485
+ onDragEndRef.current(dragged, target);
486
+ };
487
+ }, [draggedNode]);
488
+ };
489
+
490
+ export default useDragEffect;
@@ -0,0 +1,21 @@
1
+ import React, { MouseEvent, TouchEvent, useContext } from 'react';
2
+ import { ExistingNode, NodeProps } from './NodeList';
3
+
4
+ interface DroppableContextProps {
5
+ registerNode: (props: NodeProps) => ExistingNode;
6
+ deregisterNode: (node: ExistingNode) => void;
7
+ onMouseDown: (e: MouseEvent, node: ExistingNode) => void;
8
+ onTouchStart: (e: TouchEvent, node: ExistingNode) => void;
9
+ }
10
+
11
+ export const DroppableContext = React.createContext<DroppableContextProps>({
12
+ registerNode: () =>
13
+ [null, null, { current: null }, () => {}, -1, 'id'] as ExistingNode,
14
+ deregisterNode: () => {},
15
+ onMouseDown: () => {},
16
+ onTouchStart: () => {},
17
+ });
18
+
19
+ const useDroppable = (): DroppableContextProps => useContext(DroppableContext);
20
+
21
+ export default useDroppable;
@@ -0,0 +1,6 @@
1
+ import { useMemo } from 'react';
2
+
3
+ const useGeneratedId = () =>
4
+ useMemo(() => Math.random().toString(16).slice(2), []);
5
+
6
+ export default useGeneratedId;
@@ -0,0 +1,34 @@
1
+ import { CSSProperties, useCallback, useEffect, useRef } from 'react';
2
+
3
+ interface InitSize {
4
+ initWidth: number;
5
+ initHeight: number;
6
+ }
7
+
8
+ export type NodeStyleGetter = (
9
+ type: 'up' | 'down' | 'init',
10
+ horizontal: boolean
11
+ ) => CSSProperties;
12
+
13
+ const useGetNodeStyle = (size: InitSize | null): NodeStyleGetter => {
14
+ const sizeRef = useRef(size);
15
+
16
+ useEffect(() => {
17
+ sizeRef.current = size;
18
+ }, [size]);
19
+
20
+ return useCallback<NodeStyleGetter>((type, horizontal) => {
21
+ if (type === 'init' || !sizeRef.current) return {};
22
+
23
+ const translateFn = horizontal ? 'translateX' : 'translateY';
24
+ const sizeProp = horizontal ? 'initWidth' : 'initHeight';
25
+ const sign = type === 'down' ? 1 : -1;
26
+
27
+ const style: CSSProperties = {};
28
+ style.transform = `${translateFn}(${sign * sizeRef.current[sizeProp]}px)`;
29
+
30
+ return style;
31
+ }, []);
32
+ };
33
+
34
+ export default useGetNodeStyle;
@@ -0,0 +1,17 @@
1
+ import { RefObject, useMemo } from 'react';
2
+
3
+ const useInitRect = (ref?: RefObject<HTMLElement>) =>
4
+ useMemo(() => {
5
+ if (!ref || !ref.current) return null;
6
+
7
+ const rect = ref.current.getBoundingClientRect();
8
+
9
+ return {
10
+ initX: rect.x,
11
+ initY: rect.y,
12
+ initWidth: rect.width,
13
+ initHeight: rect.height,
14
+ };
15
+ }, [ref]);
16
+
17
+ export default useInitRect;
@@ -0,0 +1,16 @@
1
+ import { RefObject, useMemo } from 'react';
2
+ import getElementScroll from './getElementScroll';
3
+
4
+ const useInitScrollOffset = (ref?: RefObject<HTMLElement>) =>
5
+ useMemo(() => {
6
+ if (!ref || !ref.current) return null;
7
+
8
+ const scrollOffset = getElementScroll(ref.current);
9
+
10
+ return {
11
+ initScrollLeft: scrollOffset.scrollLeft,
12
+ initScrollTop: scrollOffset.scrollTop,
13
+ };
14
+ }, [ref]);
15
+
16
+ export default useInitScrollOffset;
@@ -0,0 +1,97 @@
1
+ import { Position } from '@os-design/use-drag';
2
+ import { useCallback } from 'react';
3
+ import NodeList, { ExistingNode } from './NodeList';
4
+ import getNodeRect from './getNodeRect';
5
+ import { NodeStyleGetter } from './useGetNodeStyle';
6
+
7
+ interface DraggedNode {
8
+ node: ExistingNode;
9
+ }
10
+
11
+ interface UseMoveNodeProps {
12
+ position: Position | null;
13
+ draggedNode: DraggedNode | null;
14
+ getNodeStyle: NodeStyleGetter;
15
+ }
16
+
17
+ interface MoveProps {
18
+ list: NodeList;
19
+ startNode: ExistingNode;
20
+ direction: 'up' | 'down';
21
+ destination: 'cursor' | 'end';
22
+ isAnotherList?: boolean;
23
+ }
24
+
25
+ /* eslint-disable no-constant-condition */
26
+
27
+ const useMoveNode = (props: UseMoveNodeProps) => {
28
+ const { position, draggedNode, getNodeStyle } = props;
29
+
30
+ return useCallback(
31
+ (options: MoveProps) => {
32
+ const {
33
+ list,
34
+ startNode,
35
+ direction,
36
+ destination,
37
+ isAnotherList = false,
38
+ } = options;
39
+
40
+ if (!draggedNode || !position) return startNode;
41
+ const [, , , , draggedNodeIndex] = draggedNode.node;
42
+ const axis = list.horizontal ? 'x' : 'y';
43
+ const startRectProp = list.horizontal ? 'left' : 'top';
44
+ const endRectProp = list.horizontal ? 'right' : 'bottom';
45
+ let node = startNode;
46
+
47
+ if (direction === 'up') {
48
+ while (true) {
49
+ const [prev, , , nodeSetStyle, nodeIndex] = node;
50
+ if (!prev) return node;
51
+
52
+ const [, , prevNodeRef, prevNodeSetStyle, prevNodeIndex] = prev;
53
+
54
+ if (destination === 'cursor') {
55
+ const prevNodeRect = getNodeRect(prevNodeRef);
56
+ if (prevNodeRect && position[axis] > prevNodeRect[endRectProp]) {
57
+ return node;
58
+ }
59
+ }
60
+
61
+ if (isAnotherList || prevNodeIndex < draggedNodeIndex) {
62
+ prevNodeSetStyle(getNodeStyle('down', list.horizontal));
63
+ } else if (nodeIndex > draggedNodeIndex) {
64
+ nodeSetStyle(getNodeStyle('init', list.horizontal));
65
+ }
66
+
67
+ node = prev;
68
+ }
69
+ } else {
70
+ while (true) {
71
+ const [, next, , nodeSetStyle, nodeIndex] = node;
72
+ if (!next) return node;
73
+
74
+ const [, , nextNodeRef, nextNodeSetStyle, nextNodeIndex] = next;
75
+
76
+ if (destination === 'cursor') {
77
+ const nextNodeRect = getNodeRect(nextNodeRef);
78
+ if (nextNodeRect && position[axis] < nextNodeRect[startRectProp]) {
79
+ return node;
80
+ }
81
+ }
82
+
83
+ if (isAnotherList || nodeIndex < draggedNodeIndex) {
84
+ nodeSetStyle(getNodeStyle('init', list.horizontal));
85
+ } else if (nextNodeIndex > draggedNodeIndex) {
86
+ nextNodeSetStyle(getNodeStyle('up', list.horizontal));
87
+ }
88
+
89
+ node = next;
90
+ }
91
+ }
92
+ },
93
+ [draggedNode, getNodeStyle, position]
94
+ );
95
+ };
96
+
97
+ export default useMoveNode;