react-native-reorderable-list 0.12.0 → 0.13.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 +0 -1
- package/lib/commonjs/components/ReorderableListCell.js +2 -2
- package/lib/commonjs/components/ReorderableListCell.js.map +1 -1
- package/lib/commonjs/components/ReorderableListCore/ReorderableListCore.js +0 -2
- package/lib/commonjs/components/ReorderableListCore/ReorderableListCore.js.map +1 -1
- package/lib/commonjs/components/ReorderableListCore/useReorderableListCore.js +84 -59
- package/lib/commonjs/components/ReorderableListCore/useReorderableListCore.js.map +1 -1
- package/lib/module/components/ReorderableListCell.js +2 -2
- package/lib/module/components/ReorderableListCell.js.map +1 -1
- package/lib/module/components/ReorderableListCore/ReorderableListCore.js +0 -2
- package/lib/module/components/ReorderableListCore/ReorderableListCore.js.map +1 -1
- package/lib/module/components/ReorderableListCore/useReorderableListCore.js +84 -59
- package/lib/module/components/ReorderableListCore/useReorderableListCore.js.map +1 -1
- package/lib/typescript/components/ReorderableListCore/ReorderableListCore.d.ts.map +1 -1
- package/lib/typescript/components/ReorderableListCore/useReorderableListCore.d.ts +1 -2
- package/lib/typescript/components/ReorderableListCore/useReorderableListCore.d.ts.map +1 -1
- package/lib/typescript/types/props.d.ts +0 -5
- package/lib/typescript/types/props.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/ReorderableListCell.tsx +3 -3
- package/src/components/ReorderableListCore/ReorderableListCore.tsx +0 -2
- package/src/components/ReorderableListCore/useReorderableListCore.ts +136 -95
- package/src/types/props.ts +0 -5
|
@@ -54,7 +54,6 @@ interface UseReorderableListCoreArgs<T> {
|
|
|
54
54
|
autoscrollDelay: number;
|
|
55
55
|
autoscrollActivationDelta: number;
|
|
56
56
|
animationDuration: number;
|
|
57
|
-
dragReorderThreshold: number;
|
|
58
57
|
onReorder: (event: ReorderableListReorderEvent) => void;
|
|
59
58
|
onDragStart?: (event: ReorderableListDragStartEvent) => void;
|
|
60
59
|
onDragEnd?: (event: ReorderableListDragEndEvent) => void;
|
|
@@ -81,7 +80,6 @@ export const useReorderableListCore = <T>({
|
|
|
81
80
|
autoscrollDelay,
|
|
82
81
|
autoscrollActivationDelta,
|
|
83
82
|
animationDuration,
|
|
84
|
-
dragReorderThreshold,
|
|
85
83
|
onReorder,
|
|
86
84
|
onDragStart,
|
|
87
85
|
onDragEnd,
|
|
@@ -105,22 +103,25 @@ export const useReorderableListCore = <T>({
|
|
|
105
103
|
const gestureState = useSharedValue<State>(State.UNDETERMINED);
|
|
106
104
|
const currentY = useSharedValue(0);
|
|
107
105
|
const currentTranslationY = useSharedValue(0);
|
|
106
|
+
const currentItemDragCenterY = useSharedValue<number | null>(null);
|
|
107
|
+
const startItemDragCenterY = useSharedValue<number>(0);
|
|
108
108
|
const flatListScrollOffsetY = useSharedValue(0);
|
|
109
109
|
const flatListHeightY = useSharedValue(0);
|
|
110
110
|
const nestedFlatListPositionY = useSharedValue(0);
|
|
111
|
+
// The scroll y translation of the list since drag start
|
|
111
112
|
const dragScrollTranslationY = useSharedValue(0);
|
|
113
|
+
// The initial scroll offset y of the list on drag start
|
|
112
114
|
const dragInitialScrollOffsetY = useSharedValue(0);
|
|
115
|
+
// The scroll y translation of the ScrollViewContainer since drag start
|
|
113
116
|
const scrollViewDragScrollTranslationY = useSharedValue(0);
|
|
117
|
+
// The initial scroll offset y of the ScrollViewContainer on drag start
|
|
114
118
|
const scrollViewDragInitialScrollOffsetY = useSharedValue(0);
|
|
115
119
|
const draggedHeight = useSharedValue(0);
|
|
116
120
|
const itemOffset = useSharedValue<number[]>([]);
|
|
117
121
|
const itemHeight = useSharedValue<number[]>([]);
|
|
118
122
|
const autoscrollTrigger = useSharedValue(-1);
|
|
119
123
|
const lastAutoscrollTrigger = useSharedValue(-1);
|
|
120
|
-
const previousY = useSharedValue(0);
|
|
121
124
|
const dragY = useSharedValue(0);
|
|
122
|
-
const previousDirection = useSharedValue(0);
|
|
123
|
-
const previousIndex = useSharedValue(-1);
|
|
124
125
|
const currentIndex = useSharedValue(-1);
|
|
125
126
|
const draggedIndex = useSharedValue(-1);
|
|
126
127
|
const state = useSharedValue<ReorderableListState>(ReorderableListState.IDLE);
|
|
@@ -175,6 +176,8 @@ export const useReorderableListCore = <T>({
|
|
|
175
176
|
* Decides the intended drag direction of the user.
|
|
176
177
|
* This is used to to determine if the user intends to autoscroll
|
|
177
178
|
* when within the threshold area.
|
|
179
|
+
*
|
|
180
|
+
* @param e - The payload of the pan gesture update event.
|
|
178
181
|
*/
|
|
179
182
|
const setDragDirection = useCallback(
|
|
180
183
|
(e: GestureUpdateEvent<PanGestureHandlerEventPayload>) => {
|
|
@@ -196,6 +199,39 @@ export const useReorderableListCore = <T>({
|
|
|
196
199
|
[dragDirection, lastDragDirectionPivot, autoscrollDelta],
|
|
197
200
|
);
|
|
198
201
|
|
|
202
|
+
const setCurrentItemDragCenterY = useCallback(
|
|
203
|
+
(e: GestureUpdateEvent<PanGestureHandlerEventPayload>) => {
|
|
204
|
+
'worklet';
|
|
205
|
+
|
|
206
|
+
if (currentItemDragCenterY.value === null) {
|
|
207
|
+
if (currentIndex.value >= 0) {
|
|
208
|
+
const itemCenter = itemHeight.value[currentIndex.value] * 0.5;
|
|
209
|
+
// the y coordinate of the item relative to the list
|
|
210
|
+
const itemY =
|
|
211
|
+
itemOffset.value[currentIndex.value] -
|
|
212
|
+
(flatListScrollOffsetY.value +
|
|
213
|
+
scrollViewDragScrollTranslationY.value);
|
|
214
|
+
|
|
215
|
+
const value = itemY + itemCenter + e.translationY;
|
|
216
|
+
startItemDragCenterY.value = value;
|
|
217
|
+
currentItemDragCenterY.value = value;
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
currentItemDragCenterY.value =
|
|
221
|
+
startItemDragCenterY.value + e.translationY;
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
[
|
|
225
|
+
currentItemDragCenterY,
|
|
226
|
+
currentIndex,
|
|
227
|
+
startItemDragCenterY,
|
|
228
|
+
itemOffset,
|
|
229
|
+
itemHeight,
|
|
230
|
+
flatListScrollOffsetY,
|
|
231
|
+
scrollViewDragScrollTranslationY,
|
|
232
|
+
],
|
|
233
|
+
);
|
|
234
|
+
|
|
199
235
|
const panGestureHandler = useMemo(
|
|
200
236
|
() =>
|
|
201
237
|
Gesture.Pan()
|
|
@@ -215,6 +251,8 @@ export const useReorderableListCore = <T>({
|
|
|
215
251
|
}
|
|
216
252
|
|
|
217
253
|
if (state.value !== ReorderableListState.RELEASED) {
|
|
254
|
+
setCurrentItemDragCenterY(e);
|
|
255
|
+
|
|
218
256
|
currentY.value = startY.value + e.translationY;
|
|
219
257
|
currentTranslationY.value = e.translationY;
|
|
220
258
|
dragY.value =
|
|
@@ -233,9 +271,10 @@ export const useReorderableListCore = <T>({
|
|
|
233
271
|
currentTranslationY,
|
|
234
272
|
dragY,
|
|
235
273
|
gestureState,
|
|
236
|
-
setDragDirection,
|
|
237
274
|
dragScrollTranslationY,
|
|
238
275
|
scrollViewDragScrollTranslationY,
|
|
276
|
+
setDragDirection,
|
|
277
|
+
setCurrentItemDragCenterY,
|
|
239
278
|
],
|
|
240
279
|
);
|
|
241
280
|
|
|
@@ -295,6 +334,7 @@ export const useReorderableListCore = <T>({
|
|
|
295
334
|
scrollViewDragScrollTranslationY.value = 0;
|
|
296
335
|
dragDirection.value = 0;
|
|
297
336
|
lastDragDirectionPivot.value = null;
|
|
337
|
+
currentItemDragCenterY.value = null;
|
|
298
338
|
}, [
|
|
299
339
|
state,
|
|
300
340
|
draggedIndex,
|
|
@@ -303,6 +343,7 @@ export const useReorderableListCore = <T>({
|
|
|
303
343
|
scrollViewDragScrollTranslationY,
|
|
304
344
|
dragDirection,
|
|
305
345
|
lastDragDirectionPivot,
|
|
346
|
+
currentItemDragCenterY,
|
|
306
347
|
]);
|
|
307
348
|
|
|
308
349
|
const resetSharedValuesAfterAnimations = useCallback(() => {
|
|
@@ -325,102 +366,106 @@ export const useReorderableListCore = <T>({
|
|
|
325
366
|
}
|
|
326
367
|
};
|
|
327
368
|
|
|
328
|
-
const
|
|
329
|
-
(
|
|
369
|
+
const recomputeLayout = useCallback(
|
|
370
|
+
(from: number, to: number) => {
|
|
330
371
|
'worklet';
|
|
331
372
|
|
|
332
|
-
const
|
|
333
|
-
const
|
|
373
|
+
const itemDirection = to > from;
|
|
374
|
+
const index1 = itemDirection ? from : to;
|
|
375
|
+
const index2 = itemDirection ? to : from;
|
|
376
|
+
|
|
377
|
+
const newOffset1 = itemOffset.value[index1];
|
|
378
|
+
const newHeight1 = itemHeight.value[index2];
|
|
379
|
+
const newOffset2 =
|
|
380
|
+
itemOffset.value[index2] +
|
|
381
|
+
itemHeight.value[index2] -
|
|
382
|
+
itemHeight.value[index1];
|
|
383
|
+
const newHeight2 = itemHeight.value[index1];
|
|
384
|
+
|
|
385
|
+
itemOffset.value[index1] = newOffset1;
|
|
386
|
+
itemHeight.value[index1] = newHeight1;
|
|
387
|
+
itemOffset.value[index2] = newOffset2;
|
|
388
|
+
itemHeight.value[index2] = newHeight2;
|
|
389
|
+
},
|
|
390
|
+
[itemOffset, itemHeight],
|
|
391
|
+
);
|
|
334
392
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
393
|
+
/**
|
|
394
|
+
* Computes a potential new drop container for the current dragged item and evaluates
|
|
395
|
+
* whether the dragged item center is nearer to the center of the current container or the new one.
|
|
396
|
+
*
|
|
397
|
+
* @returns The new index if the center of the dragged item is closer to the center of
|
|
398
|
+
* the new drop container or the current index if closer to the current drop container.
|
|
399
|
+
*/
|
|
400
|
+
const computeCurrentIndex = useCallback(() => {
|
|
401
|
+
'worklet';
|
|
339
402
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const offset = itemOffset.value[i] + height * threshold * direction;
|
|
403
|
+
if (currentItemDragCenterY.value === null) {
|
|
404
|
+
return currentIndex.value;
|
|
405
|
+
}
|
|
344
406
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
407
|
+
// apply scroll offset and scroll container translation
|
|
408
|
+
const relativeDragCenterY =
|
|
409
|
+
flatListScrollOffsetY.value +
|
|
410
|
+
scrollViewDragScrollTranslationY.value +
|
|
411
|
+
currentItemDragCenterY.value;
|
|
412
|
+
|
|
413
|
+
const currentOffset = itemOffset.value[currentIndex.value];
|
|
414
|
+
const currentHeight = itemHeight.value[currentIndex.value];
|
|
415
|
+
const currentCenter = currentOffset + currentHeight * 0.5;
|
|
416
|
+
|
|
417
|
+
const max = itemOffset.value.length;
|
|
418
|
+
const possibleIndex =
|
|
419
|
+
relativeDragCenterY < currentCenter
|
|
420
|
+
? Math.max(0, currentIndex.value - 1)
|
|
421
|
+
: Math.min(max - 1, currentIndex.value + 1);
|
|
422
|
+
|
|
423
|
+
if (currentIndex.value !== possibleIndex) {
|
|
424
|
+
let possibleOffset = itemOffset.value[possibleIndex];
|
|
425
|
+
if (possibleIndex > currentIndex.value) {
|
|
426
|
+
possibleOffset += itemHeight.value[possibleIndex] - currentHeight;
|
|
352
427
|
}
|
|
353
428
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
[
|
|
360
|
-
dragReorderThreshold,
|
|
361
|
-
currentIndex,
|
|
362
|
-
flatListScrollOffsetY,
|
|
363
|
-
previousDirection,
|
|
364
|
-
itemOffset,
|
|
365
|
-
itemHeight,
|
|
366
|
-
],
|
|
367
|
-
);
|
|
429
|
+
const possibleCenter = possibleOffset + currentHeight * 0.5;
|
|
430
|
+
const distanceFromCurrent = Math.abs(relativeDragCenterY - currentCenter);
|
|
431
|
+
const distanceFromPossible = Math.abs(
|
|
432
|
+
relativeDragCenterY - possibleCenter,
|
|
433
|
+
);
|
|
368
434
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
435
|
+
return distanceFromCurrent <= distanceFromPossible
|
|
436
|
+
? currentIndex.value
|
|
437
|
+
: possibleIndex;
|
|
438
|
+
}
|
|
372
439
|
|
|
373
|
-
|
|
374
|
-
|
|
440
|
+
return currentIndex.value;
|
|
441
|
+
}, [
|
|
442
|
+
currentIndex,
|
|
443
|
+
currentItemDragCenterY,
|
|
444
|
+
itemOffset,
|
|
445
|
+
itemHeight,
|
|
446
|
+
flatListScrollOffsetY,
|
|
447
|
+
scrollViewDragScrollTranslationY,
|
|
448
|
+
]);
|
|
375
449
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const newOffset2 =
|
|
389
|
-
itemOffset.value[index2] +
|
|
390
|
-
(itemHeight.value[index2] - itemHeight.value[index1]);
|
|
391
|
-
const newHeight2 = itemHeight.value[index1];
|
|
392
|
-
|
|
393
|
-
itemOffset.value[index1] = newOffset1;
|
|
394
|
-
itemHeight.value[index1] = newHeight1;
|
|
395
|
-
itemOffset.value[index2] = newOffset2;
|
|
396
|
-
itemHeight.value[index2] = newHeight2;
|
|
397
|
-
|
|
398
|
-
previousY.value = y;
|
|
399
|
-
previousDirection.value = newDirection;
|
|
400
|
-
previousIndex.value = currentIndex.value;
|
|
401
|
-
currentIndex.value = newIndex;
|
|
402
|
-
|
|
403
|
-
onIndexChange?.({index: newIndex});
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
[
|
|
407
|
-
currentIndex,
|
|
408
|
-
previousIndex,
|
|
409
|
-
previousDirection,
|
|
410
|
-
previousY,
|
|
411
|
-
itemOffset,
|
|
412
|
-
itemHeight,
|
|
413
|
-
getIndexFromY,
|
|
414
|
-
onIndexChange,
|
|
415
|
-
],
|
|
416
|
-
);
|
|
450
|
+
const setCurrentIndex = useCallback(() => {
|
|
451
|
+
'worklet';
|
|
452
|
+
|
|
453
|
+
const newIndex = computeCurrentIndex();
|
|
454
|
+
|
|
455
|
+
if (currentIndex.value !== newIndex) {
|
|
456
|
+
recomputeLayout(currentIndex.value, newIndex);
|
|
457
|
+
currentIndex.value = newIndex;
|
|
458
|
+
|
|
459
|
+
onIndexChange?.({index: newIndex});
|
|
460
|
+
}
|
|
461
|
+
}, [currentIndex, computeCurrentIndex, recomputeLayout, onIndexChange]);
|
|
417
462
|
|
|
418
463
|
const runDefaultDragAnimations = useCallback(
|
|
419
464
|
(type: 'start' | 'end') => {
|
|
420
465
|
'worklet';
|
|
421
466
|
|
|
422
467
|
// if no custom scale run default
|
|
423
|
-
if (!(cellAnimations && '
|
|
468
|
+
if (!(cellAnimations && 'transform' in cellAnimations)) {
|
|
424
469
|
const scaleConfig = SCALE_ANIMATION_CONFIG_DEFAULT[type];
|
|
425
470
|
scaleDefault.value = withTiming(scaleConfig.toValue, scaleConfig);
|
|
426
471
|
}
|
|
@@ -625,7 +670,7 @@ export const useReorderableListCore = <T>({
|
|
|
625
670
|
state.value === ReorderableListState.DRAGGED ||
|
|
626
671
|
state.value === ReorderableListState.AUTOSCROLL
|
|
627
672
|
) {
|
|
628
|
-
setCurrentIndex(
|
|
673
|
+
setCurrentIndex();
|
|
629
674
|
|
|
630
675
|
// Trigger autoscroll when:
|
|
631
676
|
// 1. Within the threshold area (top or bottom of list)
|
|
@@ -674,7 +719,7 @@ export const useReorderableListCore = <T>({
|
|
|
674
719
|
|
|
675
720
|
// when autoscrolling user may not be moving his finger so we need
|
|
676
721
|
// to update the current position of the dragged item here
|
|
677
|
-
setCurrentIndex(
|
|
722
|
+
setCurrentIndex();
|
|
678
723
|
}
|
|
679
724
|
},
|
|
680
725
|
);
|
|
@@ -711,8 +756,7 @@ export const useReorderableListCore = <T>({
|
|
|
711
756
|
value => {
|
|
712
757
|
if (value && scrollViewScrollEnabled) {
|
|
713
758
|
// checking if the list is not scrollable instead of the scrolling state
|
|
714
|
-
// fixes a bug on iOS where the item is shifted after autoscrolling and
|
|
715
|
-
// moving await from autoscroll area
|
|
759
|
+
// fixes a bug on iOS where the item is shifted after autoscrolling and moving away from the area
|
|
716
760
|
if (!scrollViewScrollEnabled.value) {
|
|
717
761
|
scrollViewDragScrollTranslationY.value =
|
|
718
762
|
value - scrollViewDragInitialScrollOffsetY.value;
|
|
@@ -747,13 +791,11 @@ export const useReorderableListCore = <T>({
|
|
|
747
791
|
}
|
|
748
792
|
|
|
749
793
|
dragInitialScrollOffsetY.value = flatListScrollOffsetY.value;
|
|
750
|
-
scrollViewDragInitialScrollOffsetY.value =
|
|
751
|
-
|
|
752
|
-
: 0;
|
|
794
|
+
scrollViewDragInitialScrollOffsetY.value =
|
|
795
|
+
scrollViewScrollOffsetY?.value || 0;
|
|
753
796
|
|
|
754
797
|
draggedHeight.value = itemHeight.value[index];
|
|
755
798
|
draggedIndex.value = index;
|
|
756
|
-
previousIndex.value = -1;
|
|
757
799
|
currentIndex.value = index;
|
|
758
800
|
state.value = ReorderableListState.DRAGGED;
|
|
759
801
|
|
|
@@ -772,7 +814,6 @@ export const useReorderableListCore = <T>({
|
|
|
772
814
|
scrollViewDragInitialScrollOffsetY,
|
|
773
815
|
setScrollEnabled,
|
|
774
816
|
currentIndex,
|
|
775
|
-
previousIndex,
|
|
776
817
|
draggedHeight,
|
|
777
818
|
draggedIndex,
|
|
778
819
|
state,
|
package/src/types/props.ts
CHANGED
|
@@ -95,11 +95,6 @@ export interface ReorderableListProps<T>
|
|
|
95
95
|
* Default: `5`.
|
|
96
96
|
*/
|
|
97
97
|
autoscrollActivationDelta?: number;
|
|
98
|
-
/**
|
|
99
|
-
* Specifies the fraction of an item's size at which it will shift when a dragged item crosses over it.
|
|
100
|
-
* For example, a value of 0.2 means the item shifts when the dragged item passes 20% of its height (vertical list). Default is `0.2`.
|
|
101
|
-
*/
|
|
102
|
-
dragReorderThreshold?: number;
|
|
103
98
|
/**
|
|
104
99
|
* Duration of the animations in milliseconds.
|
|
105
100
|
* Be aware that users won't be able to drag a new item until the dragged item is released and
|