react-native-reorderable-list 0.12.0 → 0.13.1
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/NestedReorderableList.js +2 -0
- package/lib/commonjs/components/NestedReorderableList.js.map +1 -1
- package/lib/commonjs/components/ReorderableList.js +4 -3
- package/lib/commonjs/components/ReorderableList.js.map +1 -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 +3 -3
- package/lib/commonjs/components/ReorderableListCore/ReorderableListCore.js.map +1 -1
- package/lib/commonjs/components/ReorderableListCore/useReorderableListCore.js +160 -99
- package/lib/commonjs/components/ReorderableListCore/useReorderableListCore.js.map +1 -1
- package/lib/commonjs/components/ScrollViewContainer.js +14 -3
- package/lib/commonjs/components/ScrollViewContainer.js.map +1 -1
- package/lib/commonjs/contexts/ScrollViewContainerContext.js.map +1 -1
- package/lib/module/components/NestedReorderableList.js +2 -0
- package/lib/module/components/NestedReorderableList.js.map +1 -1
- package/lib/module/components/ReorderableList.js +4 -3
- package/lib/module/components/ReorderableList.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 +3 -3
- package/lib/module/components/ReorderableListCore/ReorderableListCore.js.map +1 -1
- package/lib/module/components/ReorderableListCore/useReorderableListCore.js +161 -100
- package/lib/module/components/ReorderableListCore/useReorderableListCore.js.map +1 -1
- package/lib/module/components/ScrollViewContainer.js +16 -5
- package/lib/module/components/ScrollViewContainer.js.map +1 -1
- package/lib/module/contexts/ScrollViewContainerContext.js.map +1 -1
- package/lib/typescript/components/NestedReorderableList.d.ts.map +1 -1
- package/lib/typescript/components/ReorderableList.d.ts.map +1 -1
- package/lib/typescript/components/ReorderableListCore/ReorderableListCore.d.ts +1 -0
- package/lib/typescript/components/ReorderableListCore/ReorderableListCore.d.ts.map +1 -1
- package/lib/typescript/components/ReorderableListCore/useReorderableListCore.d.ts +4 -5
- package/lib/typescript/components/ReorderableListCore/useReorderableListCore.d.ts.map +1 -1
- package/lib/typescript/components/ScrollViewContainer.d.ts.map +1 -1
- package/lib/typescript/contexts/ScrollViewContainerContext.d.ts +1 -0
- package/lib/typescript/contexts/ScrollViewContainerContext.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/NestedReorderableList.tsx +2 -0
- package/src/components/ReorderableList.tsx +3 -2
- package/src/components/ReorderableListCell.tsx +3 -3
- package/src/components/ReorderableListCore/ReorderableListCore.tsx +4 -3
- package/src/components/ReorderableListCore/useReorderableListCore.ts +277 -184
- package/src/components/ScrollViewContainer.tsx +23 -5
- package/src/contexts/ScrollViewContainerContext.ts +1 -0
- package/src/types/props.ts +0 -5
|
@@ -16,12 +16,14 @@ import Animated, {
|
|
|
16
16
|
AnimatedRef,
|
|
17
17
|
Easing,
|
|
18
18
|
SharedValue,
|
|
19
|
+
measure,
|
|
19
20
|
runOnJS,
|
|
20
21
|
runOnUI,
|
|
21
22
|
scrollTo,
|
|
22
23
|
useAnimatedReaction,
|
|
23
24
|
useAnimatedRef,
|
|
24
25
|
useAnimatedScrollHandler,
|
|
26
|
+
useDerivedValue,
|
|
25
27
|
useSharedValue,
|
|
26
28
|
withDelay,
|
|
27
29
|
withTiming,
|
|
@@ -37,9 +39,9 @@ import {
|
|
|
37
39
|
ReorderableListDragEndEvent,
|
|
38
40
|
ReorderableListDragStartEvent,
|
|
39
41
|
ReorderableListIndexChangeEvent,
|
|
42
|
+
ReorderableListReorderEvent,
|
|
40
43
|
ReorderableListState,
|
|
41
44
|
} from '../../types';
|
|
42
|
-
import type {ReorderableListReorderEvent} from '../../types';
|
|
43
45
|
|
|
44
46
|
const version = React.version.split('.');
|
|
45
47
|
const hasAutomaticBatching = version.length
|
|
@@ -54,19 +56,19 @@ interface UseReorderableListCoreArgs<T> {
|
|
|
54
56
|
autoscrollDelay: number;
|
|
55
57
|
autoscrollActivationDelta: number;
|
|
56
58
|
animationDuration: number;
|
|
57
|
-
dragReorderThreshold: number;
|
|
58
59
|
onReorder: (event: ReorderableListReorderEvent) => void;
|
|
59
60
|
onDragStart?: (event: ReorderableListDragStartEvent) => void;
|
|
60
61
|
onDragEnd?: (event: ReorderableListDragEndEvent) => void;
|
|
61
62
|
onIndexChange?: (event: ReorderableListIndexChangeEvent) => void;
|
|
62
63
|
onLayout?: (event: LayoutChangeEvent) => void;
|
|
63
64
|
scrollViewContainerRef: React.RefObject<ScrollView> | undefined;
|
|
65
|
+
scrollViewPageY: SharedValue<number> | undefined;
|
|
64
66
|
scrollViewHeightY: SharedValue<number> | undefined;
|
|
65
67
|
scrollViewScrollOffsetY: SharedValue<number> | undefined;
|
|
66
68
|
scrollViewScrollEnabled: SharedValue<boolean> | undefined;
|
|
69
|
+
scrollable: boolean | undefined;
|
|
67
70
|
initialScrollEnabled: boolean | undefined;
|
|
68
71
|
initialScrollViewScrollEnabled: boolean | undefined;
|
|
69
|
-
nestedScrollable: boolean | undefined;
|
|
70
72
|
cellAnimations: ReorderableListCellAnimations | undefined;
|
|
71
73
|
shouldUpdateActiveItem: boolean | undefined;
|
|
72
74
|
panEnabled: boolean;
|
|
@@ -81,19 +83,19 @@ export const useReorderableListCore = <T>({
|
|
|
81
83
|
autoscrollDelay,
|
|
82
84
|
autoscrollActivationDelta,
|
|
83
85
|
animationDuration,
|
|
84
|
-
dragReorderThreshold,
|
|
85
86
|
onReorder,
|
|
86
87
|
onDragStart,
|
|
87
88
|
onDragEnd,
|
|
88
89
|
onLayout,
|
|
89
90
|
onIndexChange,
|
|
90
91
|
scrollViewContainerRef,
|
|
92
|
+
scrollViewPageY,
|
|
91
93
|
scrollViewHeightY,
|
|
92
94
|
scrollViewScrollOffsetY,
|
|
93
95
|
scrollViewScrollEnabled,
|
|
96
|
+
scrollable,
|
|
94
97
|
initialScrollEnabled,
|
|
95
98
|
initialScrollViewScrollEnabled,
|
|
96
|
-
nestedScrollable,
|
|
97
99
|
cellAnimations,
|
|
98
100
|
shouldUpdateActiveItem,
|
|
99
101
|
panActivateAfterLongPress,
|
|
@@ -105,22 +107,25 @@ export const useReorderableListCore = <T>({
|
|
|
105
107
|
const gestureState = useSharedValue<State>(State.UNDETERMINED);
|
|
106
108
|
const currentY = useSharedValue(0);
|
|
107
109
|
const currentTranslationY = useSharedValue(0);
|
|
110
|
+
const currentItemDragCenterY = useSharedValue<number | null>(null);
|
|
111
|
+
const startItemDragCenterY = useSharedValue<number>(0);
|
|
108
112
|
const flatListScrollOffsetY = useSharedValue(0);
|
|
109
113
|
const flatListHeightY = useSharedValue(0);
|
|
110
|
-
const
|
|
114
|
+
const flatListPageY = useSharedValue(0);
|
|
115
|
+
// The scroll y translation of the list since drag start
|
|
111
116
|
const dragScrollTranslationY = useSharedValue(0);
|
|
117
|
+
// The initial scroll offset y of the list on drag start
|
|
112
118
|
const dragInitialScrollOffsetY = useSharedValue(0);
|
|
119
|
+
// The scroll y translation of the ScrollViewContainer since drag start
|
|
113
120
|
const scrollViewDragScrollTranslationY = useSharedValue(0);
|
|
121
|
+
// The initial scroll offset y of the ScrollViewContainer on drag start
|
|
114
122
|
const scrollViewDragInitialScrollOffsetY = useSharedValue(0);
|
|
115
123
|
const draggedHeight = useSharedValue(0);
|
|
116
124
|
const itemOffset = useSharedValue<number[]>([]);
|
|
117
125
|
const itemHeight = useSharedValue<number[]>([]);
|
|
118
126
|
const autoscrollTrigger = useSharedValue(-1);
|
|
119
127
|
const lastAutoscrollTrigger = useSharedValue(-1);
|
|
120
|
-
const previousY = useSharedValue(0);
|
|
121
128
|
const dragY = useSharedValue(0);
|
|
122
|
-
const previousDirection = useSharedValue(0);
|
|
123
|
-
const previousIndex = useSharedValue(-1);
|
|
124
129
|
const currentIndex = useSharedValue(-1);
|
|
125
130
|
const draggedIndex = useSharedValue(-1);
|
|
126
131
|
const state = useSharedValue<ReorderableListState>(ReorderableListState.IDLE);
|
|
@@ -135,6 +140,11 @@ export const useReorderableListCore = <T>({
|
|
|
135
140
|
const lastDragDirectionPivot = useSharedValue<number | null>(null);
|
|
136
141
|
const autoscrollDelta = useSharedValue(autoscrollActivationDelta);
|
|
137
142
|
|
|
143
|
+
// Position of the list relative to the scroll container
|
|
144
|
+
const nestedFlatListPositionY = useDerivedValue(
|
|
145
|
+
() => flatListPageY.value - (scrollViewPageY?.value || 0),
|
|
146
|
+
);
|
|
147
|
+
|
|
138
148
|
useEffect(() => {
|
|
139
149
|
duration.value = animationDuration;
|
|
140
150
|
autoscrollDelta.value = autoscrollActivationDelta;
|
|
@@ -175,6 +185,8 @@ export const useReorderableListCore = <T>({
|
|
|
175
185
|
* Decides the intended drag direction of the user.
|
|
176
186
|
* This is used to to determine if the user intends to autoscroll
|
|
177
187
|
* when within the threshold area.
|
|
188
|
+
*
|
|
189
|
+
* @param e - The payload of the pan gesture update event.
|
|
178
190
|
*/
|
|
179
191
|
const setDragDirection = useCallback(
|
|
180
192
|
(e: GestureUpdateEvent<PanGestureHandlerEventPayload>) => {
|
|
@@ -196,6 +208,39 @@ export const useReorderableListCore = <T>({
|
|
|
196
208
|
[dragDirection, lastDragDirectionPivot, autoscrollDelta],
|
|
197
209
|
);
|
|
198
210
|
|
|
211
|
+
const setCurrentItemDragCenterY = useCallback(
|
|
212
|
+
(e: GestureUpdateEvent<PanGestureHandlerEventPayload>) => {
|
|
213
|
+
'worklet';
|
|
214
|
+
|
|
215
|
+
if (currentItemDragCenterY.value === null) {
|
|
216
|
+
if (currentIndex.value >= 0) {
|
|
217
|
+
const itemCenter = itemHeight.value[currentIndex.value] * 0.5;
|
|
218
|
+
// the y coordinate of the item relative to the list
|
|
219
|
+
const itemY =
|
|
220
|
+
itemOffset.value[currentIndex.value] -
|
|
221
|
+
(flatListScrollOffsetY.value +
|
|
222
|
+
scrollViewDragScrollTranslationY.value);
|
|
223
|
+
|
|
224
|
+
const value = itemY + itemCenter + e.translationY;
|
|
225
|
+
startItemDragCenterY.value = value;
|
|
226
|
+
currentItemDragCenterY.value = value;
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
currentItemDragCenterY.value =
|
|
230
|
+
startItemDragCenterY.value + e.translationY;
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
[
|
|
234
|
+
currentItemDragCenterY,
|
|
235
|
+
currentIndex,
|
|
236
|
+
startItemDragCenterY,
|
|
237
|
+
itemOffset,
|
|
238
|
+
itemHeight,
|
|
239
|
+
flatListScrollOffsetY,
|
|
240
|
+
scrollViewDragScrollTranslationY,
|
|
241
|
+
],
|
|
242
|
+
);
|
|
243
|
+
|
|
199
244
|
const panGestureHandler = useMemo(
|
|
200
245
|
() =>
|
|
201
246
|
Gesture.Pan()
|
|
@@ -215,6 +260,8 @@ export const useReorderableListCore = <T>({
|
|
|
215
260
|
}
|
|
216
261
|
|
|
217
262
|
if (state.value !== ReorderableListState.RELEASED) {
|
|
263
|
+
setCurrentItemDragCenterY(e);
|
|
264
|
+
|
|
218
265
|
currentY.value = startY.value + e.translationY;
|
|
219
266
|
currentTranslationY.value = e.translationY;
|
|
220
267
|
dragY.value =
|
|
@@ -233,9 +280,10 @@ export const useReorderableListCore = <T>({
|
|
|
233
280
|
currentTranslationY,
|
|
234
281
|
dragY,
|
|
235
282
|
gestureState,
|
|
236
|
-
setDragDirection,
|
|
237
283
|
dragScrollTranslationY,
|
|
238
284
|
scrollViewDragScrollTranslationY,
|
|
285
|
+
setDragDirection,
|
|
286
|
+
setCurrentItemDragCenterY,
|
|
239
287
|
],
|
|
240
288
|
);
|
|
241
289
|
|
|
@@ -295,6 +343,7 @@ export const useReorderableListCore = <T>({
|
|
|
295
343
|
scrollViewDragScrollTranslationY.value = 0;
|
|
296
344
|
dragDirection.value = 0;
|
|
297
345
|
lastDragDirectionPivot.value = null;
|
|
346
|
+
currentItemDragCenterY.value = null;
|
|
298
347
|
}, [
|
|
299
348
|
state,
|
|
300
349
|
draggedIndex,
|
|
@@ -303,6 +352,7 @@ export const useReorderableListCore = <T>({
|
|
|
303
352
|
scrollViewDragScrollTranslationY,
|
|
304
353
|
dragDirection,
|
|
305
354
|
lastDragDirectionPivot,
|
|
355
|
+
currentItemDragCenterY,
|
|
306
356
|
]);
|
|
307
357
|
|
|
308
358
|
const resetSharedValuesAfterAnimations = useCallback(() => {
|
|
@@ -325,102 +375,106 @@ export const useReorderableListCore = <T>({
|
|
|
325
375
|
}
|
|
326
376
|
};
|
|
327
377
|
|
|
328
|
-
const
|
|
329
|
-
(
|
|
378
|
+
const recomputeLayout = useCallback(
|
|
379
|
+
(from: number, to: number) => {
|
|
330
380
|
'worklet';
|
|
331
381
|
|
|
332
|
-
const
|
|
333
|
-
const
|
|
382
|
+
const itemDirection = to > from;
|
|
383
|
+
const index1 = itemDirection ? from : to;
|
|
384
|
+
const index2 = itemDirection ? to : from;
|
|
385
|
+
|
|
386
|
+
const newOffset1 = itemOffset.value[index1];
|
|
387
|
+
const newHeight1 = itemHeight.value[index2];
|
|
388
|
+
const newOffset2 =
|
|
389
|
+
itemOffset.value[index2] +
|
|
390
|
+
itemHeight.value[index2] -
|
|
391
|
+
itemHeight.value[index1];
|
|
392
|
+
const newHeight2 = itemHeight.value[index1];
|
|
393
|
+
|
|
394
|
+
itemOffset.value[index1] = newOffset1;
|
|
395
|
+
itemHeight.value[index1] = newHeight1;
|
|
396
|
+
itemOffset.value[index2] = newOffset2;
|
|
397
|
+
itemHeight.value[index2] = newHeight2;
|
|
398
|
+
},
|
|
399
|
+
[itemOffset, itemHeight],
|
|
400
|
+
);
|
|
334
401
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
402
|
+
/**
|
|
403
|
+
* Computes a potential new drop container for the current dragged item and evaluates
|
|
404
|
+
* whether the dragged item center is nearer to the center of the current container or the new one.
|
|
405
|
+
*
|
|
406
|
+
* @returns The new index if the center of the dragged item is closer to the center of
|
|
407
|
+
* the new drop container or the current index if closer to the current drop container.
|
|
408
|
+
*/
|
|
409
|
+
const computeCurrentIndex = useCallback(() => {
|
|
410
|
+
'worklet';
|
|
339
411
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const offset = itemOffset.value[i] + height * threshold * direction;
|
|
412
|
+
if (currentItemDragCenterY.value === null) {
|
|
413
|
+
return currentIndex.value;
|
|
414
|
+
}
|
|
344
415
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
416
|
+
// apply scroll offset and scroll container translation
|
|
417
|
+
const relativeDragCenterY =
|
|
418
|
+
flatListScrollOffsetY.value +
|
|
419
|
+
scrollViewDragScrollTranslationY.value +
|
|
420
|
+
currentItemDragCenterY.value;
|
|
421
|
+
|
|
422
|
+
const currentOffset = itemOffset.value[currentIndex.value];
|
|
423
|
+
const currentHeight = itemHeight.value[currentIndex.value];
|
|
424
|
+
const currentCenter = currentOffset + currentHeight * 0.5;
|
|
425
|
+
|
|
426
|
+
const max = itemOffset.value.length;
|
|
427
|
+
const possibleIndex =
|
|
428
|
+
relativeDragCenterY < currentCenter
|
|
429
|
+
? Math.max(0, currentIndex.value - 1)
|
|
430
|
+
: Math.min(max - 1, currentIndex.value + 1);
|
|
431
|
+
|
|
432
|
+
if (currentIndex.value !== possibleIndex) {
|
|
433
|
+
let possibleOffset = itemOffset.value[possibleIndex];
|
|
434
|
+
if (possibleIndex > currentIndex.value) {
|
|
435
|
+
possibleOffset += itemHeight.value[possibleIndex] - currentHeight;
|
|
352
436
|
}
|
|
353
437
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
[
|
|
360
|
-
dragReorderThreshold,
|
|
361
|
-
currentIndex,
|
|
362
|
-
flatListScrollOffsetY,
|
|
363
|
-
previousDirection,
|
|
364
|
-
itemOffset,
|
|
365
|
-
itemHeight,
|
|
366
|
-
],
|
|
367
|
-
);
|
|
438
|
+
const possibleCenter = possibleOffset + currentHeight * 0.5;
|
|
439
|
+
const distanceFromCurrent = Math.abs(relativeDragCenterY - currentCenter);
|
|
440
|
+
const distanceFromPossible = Math.abs(
|
|
441
|
+
relativeDragCenterY - possibleCenter,
|
|
442
|
+
);
|
|
368
443
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
444
|
+
return distanceFromCurrent <= distanceFromPossible
|
|
445
|
+
? currentIndex.value
|
|
446
|
+
: possibleIndex;
|
|
447
|
+
}
|
|
372
448
|
|
|
373
|
-
|
|
374
|
-
|
|
449
|
+
return currentIndex.value;
|
|
450
|
+
}, [
|
|
451
|
+
currentIndex,
|
|
452
|
+
currentItemDragCenterY,
|
|
453
|
+
itemOffset,
|
|
454
|
+
itemHeight,
|
|
455
|
+
flatListScrollOffsetY,
|
|
456
|
+
scrollViewDragScrollTranslationY,
|
|
457
|
+
]);
|
|
375
458
|
|
|
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
|
-
);
|
|
459
|
+
const setCurrentIndex = useCallback(() => {
|
|
460
|
+
'worklet';
|
|
461
|
+
|
|
462
|
+
const newIndex = computeCurrentIndex();
|
|
463
|
+
|
|
464
|
+
if (currentIndex.value !== newIndex) {
|
|
465
|
+
recomputeLayout(currentIndex.value, newIndex);
|
|
466
|
+
currentIndex.value = newIndex;
|
|
467
|
+
|
|
468
|
+
onIndexChange?.({index: newIndex});
|
|
469
|
+
}
|
|
470
|
+
}, [currentIndex, computeCurrentIndex, recomputeLayout, onIndexChange]);
|
|
417
471
|
|
|
418
472
|
const runDefaultDragAnimations = useCallback(
|
|
419
473
|
(type: 'start' | 'end') => {
|
|
420
474
|
'worklet';
|
|
421
475
|
|
|
422
476
|
// if no custom scale run default
|
|
423
|
-
if (!(cellAnimations && '
|
|
477
|
+
if (!(cellAnimations && 'transform' in cellAnimations)) {
|
|
424
478
|
const scaleConfig = SCALE_ANIMATION_CONFIG_DEFAULT[type];
|
|
425
479
|
scaleDefault.value = withTiming(scaleConfig.toValue, scaleConfig);
|
|
426
480
|
}
|
|
@@ -498,7 +552,7 @@ export const useReorderableListCore = <T>({
|
|
|
498
552
|
},
|
|
499
553
|
);
|
|
500
554
|
|
|
501
|
-
const
|
|
555
|
+
const computeHiddenArea = useCallback(() => {
|
|
502
556
|
'worklet';
|
|
503
557
|
if (!scrollViewScrollOffsetY || !scrollViewHeightY) {
|
|
504
558
|
return {top: 0, bottom: 0};
|
|
@@ -524,114 +578,132 @@ export const useReorderableListCore = <T>({
|
|
|
524
578
|
flatListHeightY,
|
|
525
579
|
]);
|
|
526
580
|
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
'worklet';
|
|
530
|
-
const offsetTop = Math.max(0, autoscrollThresholdOffset?.top || 0);
|
|
531
|
-
const offsetBottom = Math.max(0, autoscrollThresholdOffset?.bottom || 0);
|
|
532
|
-
const threshold = Math.max(0, Math.min(autoscrollThreshold, 0.4));
|
|
533
|
-
const visibleHeight =
|
|
534
|
-
flatListHeightY.value -
|
|
535
|
-
(hiddenArea.top + hiddenArea.bottom) -
|
|
536
|
-
(offsetTop + offsetBottom);
|
|
581
|
+
const computeThresholdArea = useCallback(() => {
|
|
582
|
+
'worklet';
|
|
537
583
|
|
|
538
|
-
|
|
539
|
-
const top = area + offsetTop;
|
|
540
|
-
const bottom = flatListHeightY.value - area - offsetBottom;
|
|
584
|
+
const hiddenArea = computeHiddenArea();
|
|
541
585
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
586
|
+
const offsetTop = Math.max(0, autoscrollThresholdOffset?.top || 0);
|
|
587
|
+
const offsetBottom = Math.max(0, autoscrollThresholdOffset?.bottom || 0);
|
|
588
|
+
const threshold = Math.max(0, Math.min(autoscrollThreshold, 0.4));
|
|
589
|
+
const visibleHeight =
|
|
590
|
+
flatListHeightY.value -
|
|
591
|
+
(hiddenArea.top + hiddenArea.bottom) -
|
|
592
|
+
(offsetTop + offsetBottom);
|
|
546
593
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
const offsetTop = Math.max(0, autoscrollThresholdOffset?.top || 0);
|
|
551
|
-
const offsetBottom = Math.max(0, autoscrollThresholdOffset?.bottom || 0);
|
|
552
|
-
const threshold = Math.max(0, Math.min(autoscrollThreshold, 0.4));
|
|
553
|
-
|
|
554
|
-
const area = flatListHeightY.value * threshold;
|
|
555
|
-
const top = area + offsetTop;
|
|
556
|
-
const bottom = flatListHeightY.value - area - offsetBottom;
|
|
557
|
-
|
|
558
|
-
// if the hidden area is 0 then we don't have a threshold area
|
|
559
|
-
// we might have floating errors like 0.0001 which we should ignore
|
|
560
|
-
return {
|
|
561
|
-
top: hiddenArea.top > 0.1 ? top + hiddenArea.top : 0,
|
|
562
|
-
bottom: hiddenArea.bottom > 0.1 ? bottom - hiddenArea.bottom : 0,
|
|
563
|
-
};
|
|
564
|
-
},
|
|
565
|
-
[autoscrollThreshold, autoscrollThresholdOffset, flatListHeightY],
|
|
566
|
-
);
|
|
594
|
+
const area = visibleHeight * threshold;
|
|
595
|
+
const top = area + offsetTop;
|
|
596
|
+
const bottom = flatListHeightY.value - area - offsetBottom;
|
|
567
597
|
|
|
568
|
-
|
|
598
|
+
return {top, bottom};
|
|
599
|
+
}, [
|
|
600
|
+
computeHiddenArea,
|
|
601
|
+
autoscrollThreshold,
|
|
602
|
+
autoscrollThresholdOffset,
|
|
603
|
+
flatListHeightY,
|
|
604
|
+
]);
|
|
605
|
+
|
|
606
|
+
const computeContainerThresholdArea = useCallback(() => {
|
|
607
|
+
'worklet';
|
|
608
|
+
if (!scrollViewHeightY) {
|
|
609
|
+
return {top: -Infinity, bottom: Infinity};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const offsetTop = Math.max(0, autoscrollThresholdOffset?.top || 0);
|
|
613
|
+
const offsetBottom = Math.max(0, autoscrollThresholdOffset?.bottom || 0);
|
|
614
|
+
const threshold = Math.max(0, Math.min(autoscrollThreshold, 0.4));
|
|
615
|
+
const visibleHeight = scrollViewHeightY.value - (offsetTop + offsetBottom);
|
|
616
|
+
|
|
617
|
+
const area = visibleHeight * threshold;
|
|
618
|
+
const top = area + offsetTop;
|
|
619
|
+
const bottom = visibleHeight - area - offsetBottom;
|
|
620
|
+
|
|
621
|
+
return {top, bottom};
|
|
622
|
+
}, [autoscrollThreshold, autoscrollThresholdOffset, scrollViewHeightY]);
|
|
623
|
+
|
|
624
|
+
const shouldScrollContainer = useCallback(
|
|
569
625
|
(y: number) => {
|
|
570
626
|
'worklet';
|
|
571
|
-
const
|
|
572
|
-
const
|
|
627
|
+
const containerThresholdArea = computeContainerThresholdArea();
|
|
628
|
+
const nestedListHiddenArea = computeHiddenArea();
|
|
573
629
|
|
|
574
|
-
//
|
|
630
|
+
// We should scroll the container if there's a hidden part of the nested list.
|
|
631
|
+
// We might have floating errors like 0.0001 which we should ignore.
|
|
575
632
|
return (
|
|
576
|
-
(
|
|
577
|
-
(
|
|
633
|
+
(nestedListHiddenArea.top > 0.01 && y <= containerThresholdArea.top) ||
|
|
634
|
+
(nestedListHiddenArea.bottom > 0.01 &&
|
|
635
|
+
y >= containerThresholdArea.bottom)
|
|
578
636
|
);
|
|
579
637
|
},
|
|
580
|
-
[
|
|
638
|
+
[computeHiddenArea, computeContainerThresholdArea],
|
|
581
639
|
);
|
|
582
640
|
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
'worklet';
|
|
586
|
-
const hiddenArea = calculateHiddenArea();
|
|
641
|
+
const getRelativeContainerY = useCallback(() => {
|
|
642
|
+
'worklet';
|
|
587
643
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
644
|
+
return (
|
|
645
|
+
currentY.value +
|
|
646
|
+
nestedFlatListPositionY.value -
|
|
647
|
+
scrollViewDragInitialScrollOffsetY.value
|
|
648
|
+
);
|
|
649
|
+
}, [currentY, nestedFlatListPositionY, scrollViewDragInitialScrollOffsetY]);
|
|
593
650
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
651
|
+
const getRelativeListY = useCallback(() => {
|
|
652
|
+
'worklet';
|
|
597
653
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
const thresholdArea = calculateThresholdArea(hiddenArea);
|
|
601
|
-
if (y <= thresholdArea.top) {
|
|
602
|
-
return -1;
|
|
603
|
-
}
|
|
654
|
+
return currentY.value + scrollViewDragScrollTranslationY.value;
|
|
655
|
+
}, [currentY, scrollViewDragScrollTranslationY]);
|
|
604
656
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
657
|
+
const scrollDirection = useCallback(() => {
|
|
658
|
+
'worklet';
|
|
659
|
+
|
|
660
|
+
const relativeContainerY = getRelativeContainerY();
|
|
661
|
+
if (shouldScrollContainer(relativeContainerY)) {
|
|
662
|
+
const containerThresholdArea = computeContainerThresholdArea();
|
|
663
|
+
if (relativeContainerY <= containerThresholdArea.top) {
|
|
664
|
+
return -1;
|
|
608
665
|
}
|
|
609
666
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
667
|
+
if (relativeContainerY >= containerThresholdArea.bottom) {
|
|
668
|
+
return 1;
|
|
669
|
+
}
|
|
670
|
+
} else if (scrollable) {
|
|
671
|
+
const relativeListY = getRelativeListY();
|
|
672
|
+
const thresholdArea = computeThresholdArea();
|
|
673
|
+
|
|
674
|
+
if (relativeListY <= thresholdArea.top) {
|
|
675
|
+
return -1;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (relativeListY >= thresholdArea.bottom) {
|
|
679
|
+
return 1;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return 0;
|
|
684
|
+
}, [
|
|
685
|
+
shouldScrollContainer,
|
|
686
|
+
computeThresholdArea,
|
|
687
|
+
computeContainerThresholdArea,
|
|
688
|
+
getRelativeContainerY,
|
|
689
|
+
getRelativeListY,
|
|
690
|
+
scrollable,
|
|
691
|
+
]);
|
|
620
692
|
|
|
621
693
|
useAnimatedReaction(
|
|
622
|
-
() => currentY.value
|
|
623
|
-
|
|
694
|
+
() => currentY.value,
|
|
695
|
+
() => {
|
|
624
696
|
if (
|
|
625
697
|
state.value === ReorderableListState.DRAGGED ||
|
|
626
698
|
state.value === ReorderableListState.AUTOSCROLL
|
|
627
699
|
) {
|
|
628
|
-
setCurrentIndex(
|
|
700
|
+
setCurrentIndex();
|
|
629
701
|
|
|
630
702
|
// Trigger autoscroll when:
|
|
631
703
|
// 1. Within the threshold area (top or bottom of list)
|
|
632
704
|
// 2. Have dragged in the same direction as the scroll
|
|
633
705
|
// 3. Not already in autoscroll mode
|
|
634
|
-
if (dragDirection.value === scrollDirection(
|
|
706
|
+
if (dragDirection.value === scrollDirection()) {
|
|
635
707
|
// When the first two conditions are met and it's already in autoscroll mode, we let it continue (no-op)
|
|
636
708
|
if (state.value !== ReorderableListState.AUTOSCROLL) {
|
|
637
709
|
state.value = ReorderableListState.AUTOSCROLL;
|
|
@@ -652,9 +724,8 @@ export const useReorderableListCore = <T>({
|
|
|
652
724
|
autoscrollTrigger.value !== lastAutoscrollTrigger.value &&
|
|
653
725
|
state.value === ReorderableListState.AUTOSCROLL
|
|
654
726
|
) {
|
|
655
|
-
let y = currentY.value + scrollViewDragScrollTranslationY.value;
|
|
656
727
|
const autoscrollIncrement =
|
|
657
|
-
|
|
728
|
+
dragDirection.value *
|
|
658
729
|
AUTOSCROLL_CONFIG.increment *
|
|
659
730
|
autoscrollSpeedScale;
|
|
660
731
|
|
|
@@ -663,7 +734,13 @@ export const useReorderableListCore = <T>({
|
|
|
663
734
|
let listRef =
|
|
664
735
|
flatListRef as unknown as AnimatedRef<Animated.ScrollView>;
|
|
665
736
|
|
|
666
|
-
|
|
737
|
+
// Checking on every autoscroll whether to scroll the container,
|
|
738
|
+
// this allows to smoothly pass the scroll from the container to the nested list
|
|
739
|
+
// without any gesture input.
|
|
740
|
+
if (
|
|
741
|
+
scrollViewScrollOffsetY &&
|
|
742
|
+
shouldScrollContainer(getRelativeContainerY())
|
|
743
|
+
) {
|
|
667
744
|
scrollOffset = scrollViewScrollOffsetY.value;
|
|
668
745
|
listRef =
|
|
669
746
|
scrollViewContainerRef as unknown as AnimatedRef<Animated.ScrollView>;
|
|
@@ -674,7 +751,7 @@ export const useReorderableListCore = <T>({
|
|
|
674
751
|
|
|
675
752
|
// when autoscrolling user may not be moving his finger so we need
|
|
676
753
|
// to update the current position of the dragged item here
|
|
677
|
-
setCurrentIndex(
|
|
754
|
+
setCurrentIndex();
|
|
678
755
|
}
|
|
679
756
|
},
|
|
680
757
|
);
|
|
@@ -705,14 +782,13 @@ export const useReorderableListCore = <T>({
|
|
|
705
782
|
}
|
|
706
783
|
});
|
|
707
784
|
|
|
708
|
-
//
|
|
785
|
+
// container scroll handler
|
|
709
786
|
useAnimatedReaction(
|
|
710
787
|
() => scrollViewScrollOffsetY?.value,
|
|
711
788
|
value => {
|
|
712
789
|
if (value && scrollViewScrollEnabled) {
|
|
713
790
|
// 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
|
|
791
|
+
// fixes a bug on iOS where the item is shifted after autoscrolling and moving away from the area
|
|
716
792
|
if (!scrollViewScrollEnabled.value) {
|
|
717
793
|
scrollViewDragScrollTranslationY.value =
|
|
718
794
|
value - scrollViewDragInitialScrollOffsetY.value;
|
|
@@ -747,13 +823,11 @@ export const useReorderableListCore = <T>({
|
|
|
747
823
|
}
|
|
748
824
|
|
|
749
825
|
dragInitialScrollOffsetY.value = flatListScrollOffsetY.value;
|
|
750
|
-
scrollViewDragInitialScrollOffsetY.value =
|
|
751
|
-
|
|
752
|
-
: 0;
|
|
826
|
+
scrollViewDragInitialScrollOffsetY.value =
|
|
827
|
+
scrollViewScrollOffsetY?.value || 0;
|
|
753
828
|
|
|
754
829
|
draggedHeight.value = itemHeight.value[index];
|
|
755
830
|
draggedIndex.value = index;
|
|
756
|
-
previousIndex.value = -1;
|
|
757
831
|
currentIndex.value = index;
|
|
758
832
|
state.value = ReorderableListState.DRAGGED;
|
|
759
833
|
|
|
@@ -772,7 +846,6 @@ export const useReorderableListCore = <T>({
|
|
|
772
846
|
scrollViewDragInitialScrollOffsetY,
|
|
773
847
|
setScrollEnabled,
|
|
774
848
|
currentIndex,
|
|
775
|
-
previousIndex,
|
|
776
849
|
draggedHeight,
|
|
777
850
|
draggedIndex,
|
|
778
851
|
state,
|
|
@@ -785,12 +858,32 @@ export const useReorderableListCore = <T>({
|
|
|
785
858
|
|
|
786
859
|
const handleFlatListLayout = useCallback(
|
|
787
860
|
(e: LayoutChangeEvent) => {
|
|
788
|
-
nestedFlatListPositionY.value = e.nativeEvent.layout.y;
|
|
789
861
|
flatListHeightY.value = e.nativeEvent.layout.height;
|
|
790
862
|
|
|
863
|
+
// If nested in a scroll container.
|
|
864
|
+
if (scrollViewScrollOffsetY) {
|
|
865
|
+
// Timeout fixes a bug where measure returns height 0.
|
|
866
|
+
setTimeout(() => {
|
|
867
|
+
runOnUI(() => {
|
|
868
|
+
const measurement = measure(flatListRef);
|
|
869
|
+
if (!measurement) {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
flatListPageY.value = measurement.pageY;
|
|
874
|
+
})();
|
|
875
|
+
}, 100);
|
|
876
|
+
}
|
|
877
|
+
|
|
791
878
|
onLayout?.(e);
|
|
792
879
|
},
|
|
793
|
-
[
|
|
880
|
+
[
|
|
881
|
+
flatListRef,
|
|
882
|
+
flatListPageY,
|
|
883
|
+
flatListHeightY,
|
|
884
|
+
scrollViewScrollOffsetY,
|
|
885
|
+
onLayout,
|
|
886
|
+
],
|
|
794
887
|
);
|
|
795
888
|
|
|
796
889
|
const handleRef = (value: FlatList<T>) => {
|