react-native-reorderable-list 0.6.1 → 0.7.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.
Files changed (116) hide show
  1. package/README.md +28 -3
  2. package/lib/commonjs/components/NestedReorderableList.js +41 -0
  3. package/lib/commonjs/components/NestedReorderableList.js.map +1 -0
  4. package/lib/commonjs/components/ReorderableList.js +29 -0
  5. package/lib/commonjs/components/ReorderableList.js.map +1 -0
  6. package/lib/commonjs/components/ReorderableListCell.js +1 -3
  7. package/lib/commonjs/components/ReorderableListCell.js.map +1 -1
  8. package/lib/commonjs/components/{ReorderableList/ReorderableList.js → ReorderableListCore/ReorderableListCore.js} +34 -9
  9. package/lib/commonjs/components/ReorderableListCore/ReorderableListCore.js.map +1 -0
  10. package/lib/commonjs/components/ReorderableListCore/constants.ios.js.map +1 -0
  11. package/lib/commonjs/components/ReorderableListCore/constants.js.map +1 -0
  12. package/lib/commonjs/components/ReorderableListCore/index.js +17 -0
  13. package/lib/commonjs/components/ReorderableListCore/index.js.map +1 -0
  14. package/lib/commonjs/components/{ReorderableList/useReorderableList.js → ReorderableListCore/useReorderableListCore.js} +171 -61
  15. package/lib/commonjs/components/ReorderableListCore/useReorderableListCore.js.map +1 -0
  16. package/lib/commonjs/components/ScrollViewContainer.js +53 -0
  17. package/lib/commonjs/components/ScrollViewContainer.js.map +1 -0
  18. package/lib/commonjs/components/index.js +22 -0
  19. package/lib/commonjs/components/index.js.map +1 -1
  20. package/lib/commonjs/contexts/ScrollViewContainerContext.js +10 -0
  21. package/lib/commonjs/contexts/ScrollViewContainerContext.js.map +1 -0
  22. package/lib/commonjs/contexts/index.js +11 -0
  23. package/lib/commonjs/contexts/index.js.map +1 -1
  24. package/lib/commonjs/index.js +12 -0
  25. package/lib/commonjs/index.js.map +1 -1
  26. package/lib/module/components/NestedReorderableList.js +33 -0
  27. package/lib/module/components/NestedReorderableList.js.map +1 -0
  28. package/lib/module/components/ReorderableList.js +21 -0
  29. package/lib/module/components/ReorderableList.js.map +1 -0
  30. package/lib/module/components/ReorderableListCell.js +1 -3
  31. package/lib/module/components/ReorderableListCell.js.map +1 -1
  32. package/lib/module/components/{ReorderableList/ReorderableList.js → ReorderableListCore/ReorderableListCore.js} +37 -12
  33. package/lib/module/components/ReorderableListCore/ReorderableListCore.js.map +1 -0
  34. package/lib/module/components/ReorderableListCore/constants.ios.js.map +1 -0
  35. package/lib/module/components/{ReorderableList → ReorderableListCore}/constants.js.map +1 -1
  36. package/lib/module/components/ReorderableListCore/index.js +2 -0
  37. package/lib/module/components/ReorderableListCore/index.js.map +1 -0
  38. package/lib/module/components/{ReorderableList/useReorderableList.js → ReorderableListCore/useReorderableListCore.js} +170 -60
  39. package/lib/module/components/ReorderableListCore/useReorderableListCore.js.map +1 -0
  40. package/lib/module/components/ScrollViewContainer.js +44 -0
  41. package/lib/module/components/ScrollViewContainer.js.map +1 -0
  42. package/lib/module/components/index.js +2 -0
  43. package/lib/module/components/index.js.map +1 -1
  44. package/lib/module/contexts/ScrollViewContainerContext.js +3 -0
  45. package/lib/module/contexts/ScrollViewContainerContext.js.map +1 -0
  46. package/lib/module/contexts/index.js +1 -0
  47. package/lib/module/contexts/index.js.map +1 -1
  48. package/lib/module/index.js +2 -2
  49. package/lib/module/index.js.map +1 -1
  50. package/lib/typescript/components/NestedReorderableList.d.ts +5 -0
  51. package/lib/typescript/components/NestedReorderableList.d.ts.map +1 -0
  52. package/lib/typescript/components/ReorderableList.d.ts +5 -0
  53. package/lib/typescript/components/ReorderableList.d.ts.map +1 -0
  54. package/lib/typescript/components/ReorderableListCell.d.ts.map +1 -1
  55. package/lib/typescript/components/ReorderableListCore/ReorderableListCore.d.ts +20 -0
  56. package/lib/typescript/components/ReorderableListCore/ReorderableListCore.d.ts.map +1 -0
  57. package/lib/typescript/components/ReorderableListCore/constants.d.ts.map +1 -0
  58. package/lib/typescript/components/ReorderableListCore/constants.ios.d.ts.map +1 -0
  59. package/lib/typescript/components/ReorderableListCore/index.d.ts +2 -0
  60. package/lib/typescript/components/ReorderableListCore/index.d.ts.map +1 -0
  61. package/lib/typescript/components/ReorderableListCore/useReorderableListCore.d.ts +44 -0
  62. package/lib/typescript/components/ReorderableListCore/useReorderableListCore.d.ts.map +1 -0
  63. package/lib/typescript/components/ScrollViewContainer.d.ts +4 -0
  64. package/lib/typescript/components/ScrollViewContainer.d.ts.map +1 -0
  65. package/lib/typescript/components/index.d.ts +2 -0
  66. package/lib/typescript/components/index.d.ts.map +1 -1
  67. package/lib/typescript/contexts/ScrollViewContainerContext.d.ts +15 -0
  68. package/lib/typescript/contexts/ScrollViewContainerContext.d.ts.map +1 -0
  69. package/lib/typescript/contexts/index.d.ts +1 -0
  70. package/lib/typescript/contexts/index.d.ts.map +1 -1
  71. package/lib/typescript/index.d.ts +3 -3
  72. package/lib/typescript/index.d.ts.map +1 -1
  73. package/lib/typescript/types/props.d.ts +13 -1
  74. package/lib/typescript/types/props.d.ts.map +1 -1
  75. package/package.json +1 -1
  76. package/src/components/NestedReorderableList.tsx +43 -0
  77. package/src/components/ReorderableList.tsx +29 -0
  78. package/src/components/ReorderableListCell.tsx +1 -3
  79. package/src/components/ReorderableListCore/ReorderableListCore.tsx +162 -0
  80. package/src/components/ReorderableListCore/index.ts +1 -0
  81. package/src/components/{ReorderableList/useReorderableList.ts → ReorderableListCore/useReorderableListCore.ts} +252 -75
  82. package/src/components/ScrollViewContainer.tsx +74 -0
  83. package/src/components/index.ts +2 -0
  84. package/src/contexts/ScrollViewContainerContext.ts +18 -0
  85. package/src/contexts/index.ts +1 -0
  86. package/src/index.ts +10 -1
  87. package/src/types/props.ts +21 -1
  88. package/lib/commonjs/components/ReorderableList/ReorderableList.js.map +0 -1
  89. package/lib/commonjs/components/ReorderableList/constants.ios.js.map +0 -1
  90. package/lib/commonjs/components/ReorderableList/constants.js.map +0 -1
  91. package/lib/commonjs/components/ReorderableList/index.js +0 -17
  92. package/lib/commonjs/components/ReorderableList/index.js.map +0 -1
  93. package/lib/commonjs/components/ReorderableList/useReorderableList.js.map +0 -1
  94. package/lib/module/components/ReorderableList/ReorderableList.js.map +0 -1
  95. package/lib/module/components/ReorderableList/constants.ios.js.map +0 -1
  96. package/lib/module/components/ReorderableList/index.js +0 -2
  97. package/lib/module/components/ReorderableList/index.js.map +0 -1
  98. package/lib/module/components/ReorderableList/useReorderableList.js.map +0 -1
  99. package/lib/typescript/components/ReorderableList/ReorderableList.d.ts +0 -8
  100. package/lib/typescript/components/ReorderableList/ReorderableList.d.ts.map +0 -1
  101. package/lib/typescript/components/ReorderableList/constants.d.ts.map +0 -1
  102. package/lib/typescript/components/ReorderableList/constants.ios.d.ts.map +0 -1
  103. package/lib/typescript/components/ReorderableList/index.d.ts +0 -2
  104. package/lib/typescript/components/ReorderableList/index.d.ts.map +0 -1
  105. package/lib/typescript/components/ReorderableList/useReorderableList.d.ts +0 -36
  106. package/lib/typescript/components/ReorderableList/useReorderableList.d.ts.map +0 -1
  107. package/src/components/ReorderableList/ReorderableList.tsx +0 -109
  108. package/src/components/ReorderableList/index.ts +0 -1
  109. /package/lib/commonjs/components/{ReorderableList → ReorderableListCore}/constants.ios.js +0 -0
  110. /package/lib/commonjs/components/{ReorderableList → ReorderableListCore}/constants.js +0 -0
  111. /package/lib/module/components/{ReorderableList → ReorderableListCore}/constants.ios.js +0 -0
  112. /package/lib/module/components/{ReorderableList → ReorderableListCore}/constants.js +0 -0
  113. /package/lib/typescript/components/{ReorderableList → ReorderableListCore}/constants.d.ts +0 -0
  114. /package/lib/typescript/components/{ReorderableList → ReorderableListCore}/constants.ios.d.ts +0 -0
  115. /package/src/components/{ReorderableList → ReorderableListCore}/constants.ios.ts +0 -0
  116. /package/src/components/{ReorderableList → ReorderableListCore}/constants.ts +0 -0
@@ -3,6 +3,7 @@ import {
3
3
  FlatList,
4
4
  LayoutChangeEvent,
5
5
  NativeScrollEvent,
6
+ ScrollView,
6
7
  unstable_batchedUpdates,
7
8
  } from 'react-native';
8
9
 
@@ -10,8 +11,8 @@ import {Gesture, State} from 'react-native-gesture-handler';
10
11
  import Animated, {
11
12
  AnimatedRef,
12
13
  Easing,
14
+ SharedValue,
13
15
  cancelAnimation,
14
- measure,
15
16
  runOnJS,
16
17
  runOnUI,
17
18
  scrollTo,
@@ -32,7 +33,7 @@ const hasAutomaticBatching = version.length
32
33
  ? parseInt(version[0], 10) >= 18
33
34
  : false;
34
35
 
35
- interface UseReorderableListArgs<T> {
36
+ interface UseReorderableListCoreArgs<T> {
36
37
  ref: React.ForwardedRef<FlatList<T>>;
37
38
  autoscrollThreshold: number;
38
39
  autoscrollSpeedScale: number;
@@ -43,9 +44,16 @@ interface UseReorderableListArgs<T> {
43
44
  onDragEnd?: (event: ReorderableListDragEndEvent) => void;
44
45
  onScroll?: (event: NativeScrollEvent) => void;
45
46
  onLayout?: (event: LayoutChangeEvent) => void;
47
+ scrollViewContainerRef: React.RefObject<ScrollView> | undefined;
48
+ scrollViewHeightY: SharedValue<number> | undefined;
49
+ scrollViewScrollOffsetY: SharedValue<number> | undefined;
50
+ scrollViewScrollEnabled: SharedValue<boolean> | undefined;
51
+ initialScrollEnabled: boolean | undefined;
52
+ initialScrollViewScrollEnabled: boolean | undefined;
53
+ nestedScrollable: boolean | undefined;
46
54
  }
47
55
 
48
- export const useReorderableList = <T>({
56
+ export const useReorderableListCore = <T>({
49
57
  ref,
50
58
  autoscrollThreshold,
51
59
  autoscrollSpeedScale,
@@ -56,21 +64,29 @@ export const useReorderableList = <T>({
56
64
  onDragEnd,
57
65
  onScroll,
58
66
  onLayout,
59
- }: UseReorderableListArgs<T>) => {
60
- const scrollEnabled = useSharedValue(true);
61
- const flatList = useAnimatedRef<FlatList>();
67
+ scrollViewContainerRef,
68
+ scrollViewHeightY,
69
+ scrollViewScrollOffsetY,
70
+ scrollViewScrollEnabled,
71
+ initialScrollEnabled,
72
+ initialScrollViewScrollEnabled,
73
+ nestedScrollable,
74
+ }: UseReorderableListCoreArgs<T>) => {
75
+ const flatListRef = useAnimatedRef<FlatList>();
76
+ const scrollEnabled = useSharedValue(initialScrollEnabled);
62
77
  const gestureState = useSharedValue<State>(State.UNDETERMINED);
63
78
  const currentY = useSharedValue(0);
64
79
  const currentTranslationY = useSharedValue(0);
65
- const containerPositionY = useSharedValue(0);
66
- const currentScrollOffsetY = useSharedValue(0);
80
+ const flatListScrollOffsetY = useSharedValue(0);
81
+ const flatListHeightY = useSharedValue(0);
82
+ const nestedFlatListPositionY = useSharedValue(0);
67
83
  const dragScrollTranslationY = useSharedValue(0);
68
84
  const dragInitialScrollOffsetY = useSharedValue(0);
85
+ const scrollViewDragScrollTranslationY = useSharedValue(0);
86
+ const scrollViewDragInitialScrollOffsetY = useSharedValue(0);
69
87
  const draggedHeight = useSharedValue(0);
70
88
  const itemOffset = useSharedValue<number[]>([]);
71
89
  const itemHeight = useSharedValue<number[]>([]);
72
- const topAutoscrollArea = useSharedValue(0);
73
- const bottomAutoscrollArea = useSharedValue(0);
74
90
  const autoscrollTrigger = useSharedValue(-1);
75
91
  const lastAutoscrollTrigger = useSharedValue(-1);
76
92
  const previousY = useSharedValue(0);
@@ -108,10 +124,8 @@ export const useReorderableList = <T>({
108
124
  .onBegin(e => {
109
125
  // prevent new dragging until item is completely released
110
126
  if (state.value === ReorderableListState.IDLE) {
111
- const relativeY = e.absoluteY - containerPositionY.value;
112
-
113
- startY.value = relativeY;
114
- currentY.value = relativeY;
127
+ startY.value = e.y;
128
+ currentY.value = e.y;
115
129
  currentTranslationY.value = e.translationY;
116
130
  if (draggedIndex.value >= 0) {
117
131
  dragY.value = e.translationY;
@@ -124,7 +138,10 @@ export const useReorderableList = <T>({
124
138
  currentY.value = startY.value + e.translationY;
125
139
  currentTranslationY.value = e.translationY;
126
140
  if (draggedIndex.value >= 0) {
127
- dragY.value = e.translationY + dragScrollTranslationY.value;
141
+ dragY.value =
142
+ e.translationY +
143
+ dragScrollTranslationY.value +
144
+ scrollViewDragScrollTranslationY.value;
128
145
  }
129
146
  gestureState.value = e.state;
130
147
  }
@@ -132,10 +149,10 @@ export const useReorderableList = <T>({
132
149
  .onEnd(e => (gestureState.value = e.state))
133
150
  .onFinalize(e => (gestureState.value = e.state)),
134
151
  [
135
- containerPositionY,
136
152
  currentTranslationY,
137
153
  currentY,
138
154
  dragScrollTranslationY,
155
+ scrollViewDragScrollTranslationY,
139
156
  draggedIndex,
140
157
  gestureState,
141
158
  dragY,
@@ -151,22 +168,50 @@ export const useReorderableList = <T>({
151
168
 
152
169
  const setScrollEnabled = useCallback(
153
170
  (enabled: boolean) => {
154
- scrollEnabled.value = enabled;
155
- flatList.current?.setNativeProps({scrollEnabled: enabled});
171
+ // if scroll is enabled on list props then we can toggle it
172
+ if (initialScrollEnabled) {
173
+ scrollEnabled.value = enabled;
174
+ flatListRef.current?.setNativeProps({scrollEnabled: enabled});
175
+ }
176
+
177
+ if (
178
+ scrollViewContainerRef &&
179
+ scrollViewScrollEnabled &&
180
+ initialScrollViewScrollEnabled
181
+ ) {
182
+ scrollViewScrollEnabled.value = enabled;
183
+ scrollViewContainerRef.current?.setNativeProps({
184
+ scrollEnabled: enabled,
185
+ });
186
+ }
156
187
  },
157
- [flatList, scrollEnabled],
188
+ [
189
+ initialScrollEnabled,
190
+ flatListRef,
191
+ scrollEnabled,
192
+ initialScrollViewScrollEnabled,
193
+ scrollViewContainerRef,
194
+ scrollViewScrollEnabled,
195
+ ],
158
196
  );
159
197
 
160
198
  const resetSharedValues = useCallback(() => {
161
199
  'worklet';
162
200
 
163
- draggedIndex.value = -1;
164
201
  // current index is reset on item render for the on end event
165
- dragY.value = 0;
202
+ draggedIndex.value = -1;
166
203
  // released flag is reset after release is triggered in the item
167
204
  state.value = ReorderableListState.IDLE;
205
+ dragY.value = 0;
168
206
  dragScrollTranslationY.value = 0;
169
- }, [draggedIndex, dragY, state, dragScrollTranslationY]);
207
+ scrollViewDragScrollTranslationY.value = 0;
208
+ }, [
209
+ dragY,
210
+ dragScrollTranslationY,
211
+ scrollViewDragScrollTranslationY,
212
+ draggedIndex,
213
+ state,
214
+ ]);
170
215
 
171
216
  const reorder = (fromIndex: number, toIndex: number) => {
172
217
  runOnUI(resetSharedValues)();
@@ -188,7 +233,7 @@ export const useReorderableList = <T>({
188
233
  (y: number) => {
189
234
  'worklet';
190
235
 
191
- const relativeY = currentScrollOffsetY.value + y;
236
+ const relativeY = flatListScrollOffsetY.value + y;
192
237
  const count = itemOffset.value.length;
193
238
 
194
239
  for (let i = 0; i < count; i++) {
@@ -218,7 +263,7 @@ export const useReorderableList = <T>({
218
263
  [
219
264
  dragReorderThreshold,
220
265
  currentIndex,
221
- currentScrollOffsetY,
266
+ flatListScrollOffsetY,
222
267
  previousDirection,
223
268
  itemOffset,
224
269
  itemHeight,
@@ -328,8 +373,119 @@ export const useReorderableList = <T>({
328
373
  },
329
374
  );
330
375
 
376
+ const calculateHiddenArea = useCallback(() => {
377
+ 'worklet';
378
+ if (!scrollViewScrollOffsetY || !scrollViewHeightY) {
379
+ return {top: 0, bottom: 0};
380
+ }
381
+
382
+ // hidden area cannot be negative
383
+ const top = Math.max(
384
+ 0,
385
+ scrollViewScrollOffsetY.value - nestedFlatListPositionY.value,
386
+ );
387
+ const bottom = Math.max(
388
+ 0,
389
+ nestedFlatListPositionY.value +
390
+ flatListHeightY.value -
391
+ (scrollViewScrollOffsetY.value + scrollViewHeightY.value),
392
+ );
393
+
394
+ return {top, bottom};
395
+ }, [
396
+ scrollViewScrollOffsetY,
397
+ scrollViewHeightY,
398
+ nestedFlatListPositionY,
399
+ flatListHeightY,
400
+ ]);
401
+
402
+ const calculateThresholdArea = useCallback(
403
+ (hiddenArea: {top: number; bottom: number}) => {
404
+ 'worklet';
405
+ const threshold = Math.max(0, Math.min(autoscrollThreshold, 0.4));
406
+ const visibleHeight =
407
+ flatListHeightY.value - (hiddenArea.top + hiddenArea.bottom);
408
+
409
+ const top = visibleHeight * threshold;
410
+ const bottom = flatListHeightY.value - top;
411
+
412
+ return {top, bottom};
413
+ },
414
+ [autoscrollThreshold, flatListHeightY],
415
+ );
416
+
417
+ const calculateThresholdAreaParent = useCallback(
418
+ (hiddenArea: {top: number; bottom: number}) => {
419
+ 'worklet';
420
+ const threshold = Math.max(0, Math.min(autoscrollThreshold, 0.4));
421
+ const top = flatListHeightY.value * threshold;
422
+ const bottom = flatListHeightY.value - top;
423
+
424
+ // if the hidden area is 0 then we don't have a threshold area
425
+ // we might have floating errors like 0.0001 which we should ignore
426
+ return {
427
+ top: hiddenArea.top > 0.1 ? top + hiddenArea.top : 0,
428
+ bottom: hiddenArea.bottom > 0.1 ? bottom - hiddenArea.bottom : 0,
429
+ };
430
+ },
431
+ [autoscrollThreshold, flatListHeightY],
432
+ );
433
+
434
+ const shouldScrollParent = useCallback(
435
+ (y: number) => {
436
+ 'worklet';
437
+ const hiddenArea = calculateHiddenArea();
438
+ const thresholdAreaParent = calculateThresholdAreaParent(hiddenArea);
439
+
440
+ // we might have floating errors like 0.0001 which we should ignore
441
+ return (
442
+ (hiddenArea.top > 0.1 && y <= thresholdAreaParent.top) ||
443
+ (hiddenArea.bottom > 0.1 && y >= thresholdAreaParent.bottom)
444
+ );
445
+ },
446
+ [calculateHiddenArea, calculateThresholdAreaParent],
447
+ );
448
+
449
+ const scrollDirection = useCallback(
450
+ (y: number) => {
451
+ 'worklet';
452
+ const hiddenArea = calculateHiddenArea();
453
+
454
+ if (shouldScrollParent(y)) {
455
+ const thresholdAreaParent = calculateThresholdAreaParent(hiddenArea);
456
+ if (y <= thresholdAreaParent.top) {
457
+ return -1;
458
+ }
459
+
460
+ if (y >= thresholdAreaParent.bottom) {
461
+ return 1;
462
+ }
463
+
464
+ return 0;
465
+ } else if (nestedScrollable) {
466
+ const thresholdArea = calculateThresholdArea(hiddenArea);
467
+ if (y <= thresholdArea.top) {
468
+ return -1;
469
+ }
470
+
471
+ if (y >= thresholdArea.bottom) {
472
+ return 1;
473
+ }
474
+ }
475
+
476
+ return 0;
477
+ },
478
+ [
479
+ nestedScrollable,
480
+ shouldScrollParent,
481
+ calculateHiddenArea,
482
+ calculateThresholdArea,
483
+ calculateThresholdAreaParent,
484
+ ],
485
+ );
486
+
331
487
  useAnimatedReaction(
332
- () => currentY.value,
488
+ () => currentY.value + scrollViewDragScrollTranslationY.value,
333
489
  y => {
334
490
  if (
335
491
  state.value === ReorderableListState.DRAGGING ||
@@ -337,7 +493,7 @@ export const useReorderableList = <T>({
337
493
  ) {
338
494
  setCurrentIndex(y);
339
495
 
340
- if (y <= topAutoscrollArea.value || y >= bottomAutoscrollArea.value) {
496
+ if (scrollDirection(y)) {
341
497
  if (state.value !== ReorderableListState.AUTO_SCROLL) {
342
498
  // trigger autoscroll
343
499
  lastAutoscrollTrigger.value = autoscrollTrigger.value;
@@ -358,40 +514,48 @@ export const useReorderableList = <T>({
358
514
  autoscrollTrigger.value !== lastAutoscrollTrigger.value &&
359
515
  state.value === ReorderableListState.AUTO_SCROLL
360
516
  ) {
517
+ let y = currentY.value + scrollViewDragScrollTranslationY.value;
361
518
  const autoscrollIncrement =
362
- (currentY.value <= topAutoscrollArea.value
363
- ? -AUTOSCROLL_INCREMENT
364
- : AUTOSCROLL_INCREMENT) * autoscrollSpeedScale;
519
+ scrollDirection(y) * AUTOSCROLL_INCREMENT * autoscrollSpeedScale;
365
520
 
366
521
  if (autoscrollIncrement !== 0) {
367
- scrollTo(
368
- flatList as unknown as AnimatedRef<Animated.ScrollView>,
369
- 0,
370
- currentScrollOffsetY.value + autoscrollIncrement,
371
- true,
372
- );
522
+ let scrollOffset = flatListScrollOffsetY.value;
523
+ let listRef =
524
+ flatListRef as unknown as AnimatedRef<Animated.ScrollView>;
525
+
526
+ if (shouldScrollParent(y) && scrollViewScrollOffsetY) {
527
+ scrollOffset = scrollViewScrollOffsetY.value;
528
+ listRef =
529
+ scrollViewContainerRef as unknown as AnimatedRef<Animated.ScrollView>;
530
+ }
531
+
532
+ scrollTo(listRef, 0, scrollOffset + autoscrollIncrement, true);
373
533
  }
374
534
 
375
535
  // when autoscrolling user may not be moving his finger so we need
376
536
  // to update the current position of the dragged item here
377
- setCurrentIndex(currentY.value);
537
+ setCurrentIndex(y);
378
538
  }
379
539
  },
380
540
  );
381
541
 
542
+ // flatlist scroll handler
382
543
  const handleScroll = useAnimatedScrollHandler(e => {
383
- currentScrollOffsetY.value = e.contentOffset.y;
544
+ flatListScrollOffsetY.value = e.contentOffset.y;
384
545
 
385
546
  // checking if the list is not scrollable instead of the scrolling state
386
547
  // fixes a bug on iOS where the item is shifted after autoscrolling and then
387
548
  // moving await from autoscroll area
388
549
  if (!scrollEnabled.value) {
389
550
  dragScrollTranslationY.value =
390
- currentScrollOffsetY.value - dragInitialScrollOffsetY.value;
551
+ flatListScrollOffsetY.value - dragInitialScrollOffsetY.value;
391
552
  }
392
553
 
393
554
  if (state.value === ReorderableListState.AUTO_SCROLL) {
394
- dragY.value = currentTranslationY.value + dragScrollTranslationY.value;
555
+ dragY.value =
556
+ currentTranslationY.value +
557
+ dragScrollTranslationY.value +
558
+ scrollViewDragScrollTranslationY.value;
395
559
 
396
560
  cancelAnimation(autoscrollTrigger);
397
561
 
@@ -400,79 +564,92 @@ export const useReorderableList = <T>({
400
564
  autoscrollDelay,
401
565
  withTiming(autoscrollTrigger.value * -1, {duration: 0}),
402
566
  );
403
- } else {
404
567
  }
405
568
 
406
- if (onScroll) {
407
- onScroll(e);
408
- }
569
+ onScroll?.(e);
409
570
  });
410
571
 
572
+ // parent scroll handler
573
+ useAnimatedReaction(
574
+ () => scrollViewScrollOffsetY?.value,
575
+ value => {
576
+ if (value && scrollViewScrollEnabled) {
577
+ // checking if the list is not scrollable instead of the scrolling state
578
+ // fixes a bug on iOS where the item is shifted after autoscrolling and then
579
+ // moving await from autoscroll area
580
+ if (!scrollViewScrollEnabled.value) {
581
+ scrollViewDragScrollTranslationY.value =
582
+ value - scrollViewDragInitialScrollOffsetY.value;
583
+ }
584
+
585
+ if (state.value === ReorderableListState.AUTO_SCROLL) {
586
+ dragY.value =
587
+ currentTranslationY.value + scrollViewDragScrollTranslationY.value;
588
+
589
+ cancelAnimation(autoscrollTrigger);
590
+
591
+ lastAutoscrollTrigger.value = autoscrollTrigger.value;
592
+ autoscrollTrigger.value = withDelay(
593
+ autoscrollDelay,
594
+ withTiming(autoscrollTrigger.value * -1, {duration: 0}),
595
+ );
596
+ }
597
+ }
598
+ },
599
+ );
600
+
411
601
  const startDrag = useCallback(
412
602
  (index: number) => {
413
603
  'worklet';
414
604
 
415
605
  // allow new drag when item is completely released
416
606
  if (state.value === ReorderableListState.IDLE) {
607
+ // resetting shared values again fixes a flickeing bug in nested lists where
608
+ // after scrolling the parent list it would offset the new dragged item in another nested list
609
+ resetSharedValues();
610
+
611
+ dragInitialScrollOffsetY.value = flatListScrollOffsetY.value;
612
+ scrollViewDragInitialScrollOffsetY.value = scrollViewScrollOffsetY
613
+ ? scrollViewScrollOffsetY.value
614
+ : 0;
615
+
417
616
  draggedHeight.value = itemHeight.value[index];
418
617
  draggedIndex.value = index;
419
618
  previousIndex.value = -1;
420
619
  currentIndex.value = index;
421
620
  state.value = ReorderableListState.DRAGGING;
422
- dragInitialScrollOffsetY.value = currentScrollOffsetY.value;
423
621
 
424
622
  runOnJS(setScrollEnabled)(false);
425
623
  }
426
624
  },
427
625
  [
626
+ resetSharedValues,
627
+ dragInitialScrollOffsetY,
628
+ scrollViewScrollOffsetY,
629
+ scrollViewDragInitialScrollOffsetY,
428
630
  setScrollEnabled,
429
631
  currentIndex,
430
632
  previousIndex,
431
633
  draggedHeight,
432
634
  draggedIndex,
433
635
  state,
434
- currentScrollOffsetY,
435
- dragInitialScrollOffsetY,
636
+ flatListScrollOffsetY,
436
637
  itemHeight,
437
638
  ],
438
639
  );
439
640
 
440
- const measureFlatList = useCallback(() => {
441
- 'worklet';
442
-
443
- const measurement = measure(flatList);
444
- if (measurement === null) {
445
- return;
446
- }
447
-
448
- containerPositionY.value = measurement.pageY;
449
- }, [flatList, containerPositionY]);
450
-
451
641
  const handleFlatListLayout = useCallback(
452
642
  (e: LayoutChangeEvent) => {
453
- runOnUI(measureFlatList)();
454
-
455
- const threshold = Math.max(0, Math.min(autoscrollThreshold, 0.4));
456
- const {height} = e.nativeEvent.layout;
457
-
458
- topAutoscrollArea.value = height * threshold;
459
- bottomAutoscrollArea.value = height * (1 - threshold);
643
+ nestedFlatListPositionY.value = e.nativeEvent.layout.y;
644
+ flatListHeightY.value = e.nativeEvent.layout.height;
460
645
 
461
- if (onLayout) {
462
- onLayout(e);
463
- }
646
+ onLayout?.(e);
464
647
  },
465
- [
466
- measureFlatList,
467
- topAutoscrollArea,
468
- bottomAutoscrollArea,
469
- autoscrollThreshold,
470
- onLayout,
471
- ],
648
+ [nestedFlatListPositionY, flatListHeightY, onLayout],
472
649
  );
473
650
 
474
651
  const handleRef = (value: FlatList<T>) => {
475
- flatList(value);
652
+ flatListRef(value);
476
653
 
477
654
  if (typeof ref === 'function') {
478
655
  ref(value);
@@ -0,0 +1,74 @@
1
+ import React, {useMemo} from 'react';
2
+ import {LayoutChangeEvent} from 'react-native';
3
+
4
+ import {Gesture, GestureDetector} from 'react-native-gesture-handler';
5
+ import Animated, {
6
+ useAnimatedRef,
7
+ useAnimatedScrollHandler,
8
+ useSharedValue,
9
+ } from 'react-native-reanimated';
10
+
11
+ import {ScrollViewContainerContext} from '../contexts/ScrollViewContainerContext';
12
+ import type {ScrollViewContainerProps} from '../types';
13
+
14
+ export const ScrollViewContainer: React.FC<ScrollViewContainerProps> = ({
15
+ onLayout,
16
+ onScroll,
17
+ scrollEnabled = true,
18
+ ...rest
19
+ }) => {
20
+ const scrollViewScrollEnabled = useSharedValue(scrollEnabled);
21
+ const scrollViewContainerRef = useAnimatedRef<Animated.ScrollView>();
22
+ const scrollViewScrollOffsetY = useSharedValue(0);
23
+ const scrollViewHeightY = useSharedValue(0);
24
+
25
+ const outerScrollGesture = useMemo(() => Gesture.Native(), []);
26
+
27
+ const handleScroll = useAnimatedScrollHandler(
28
+ e => {
29
+ scrollViewScrollOffsetY.value = e.contentOffset.y;
30
+
31
+ onScroll?.(e);
32
+ },
33
+ [scrollViewScrollOffsetY],
34
+ );
35
+
36
+ const contextValue = useMemo(
37
+ () => ({
38
+ scrollViewContainerRef,
39
+ scrollViewHeightY,
40
+ scrollViewScrollOffsetY,
41
+ scrollViewScrollEnabled,
42
+ outerScrollGesture,
43
+ initialScrollViewScrollEnabled: scrollEnabled,
44
+ }),
45
+ [
46
+ scrollViewContainerRef,
47
+ scrollViewHeightY,
48
+ scrollViewScrollOffsetY,
49
+ scrollViewScrollEnabled,
50
+ outerScrollGesture,
51
+ scrollEnabled,
52
+ ],
53
+ );
54
+
55
+ const handleLayout = (e: LayoutChangeEvent) => {
56
+ scrollViewHeightY.value = e.nativeEvent.layout.height;
57
+
58
+ onLayout?.(e);
59
+ };
60
+
61
+ return (
62
+ <ScrollViewContainerContext.Provider value={contextValue}>
63
+ <GestureDetector gesture={outerScrollGesture}>
64
+ <Animated.ScrollView
65
+ {...rest}
66
+ ref={scrollViewContainerRef}
67
+ onScroll={handleScroll}
68
+ onLayout={handleLayout}
69
+ scrollEnabled={scrollEnabled}
70
+ />
71
+ </GestureDetector>
72
+ </ScrollViewContainerContext.Provider>
73
+ );
74
+ };
@@ -1,3 +1,5 @@
1
1
  export * from './ReorderableList';
2
+ export * from './NestedReorderableList';
2
3
  export * from './ReorderableListCell';
3
4
  export * from './ReorderableListItem';
5
+ export * from './ScrollViewContainer';
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import {ScrollView} from 'react-native';
3
+
4
+ import {NativeGesture} from 'react-native-gesture-handler';
5
+ import {SharedValue} from 'react-native-reanimated';
6
+
7
+ interface ScrollViewContainerContextData {
8
+ scrollViewContainerRef: React.RefObject<ScrollView>;
9
+ scrollViewHeightY: SharedValue<number>;
10
+ scrollViewScrollOffsetY: SharedValue<number>;
11
+ scrollViewScrollEnabled: SharedValue<boolean>;
12
+ outerScrollGesture: NativeGesture;
13
+ initialScrollViewScrollEnabled: boolean;
14
+ }
15
+
16
+ export const ScrollViewContainerContext = React.createContext<
17
+ ScrollViewContainerContextData | undefined
18
+ >(undefined);
@@ -1,2 +1,3 @@
1
1
  export * from './ReorderableCellContext';
2
2
  export * from './ReorderableListContext';
3
+ export * from './ScrollViewContainerContext';
package/src/index.ts CHANGED
@@ -1,4 +1,9 @@
1
- import {ReorderableList, ReorderableListItem} from './components';
1
+ import {
2
+ NestedReorderableList,
3
+ ReorderableList,
4
+ ReorderableListItem,
5
+ ScrollViewContainer,
6
+ } from './components';
2
7
  import {
3
8
  useReorderableDrag,
4
9
  useReorderableDragEnd,
@@ -10,6 +15,7 @@ import type {
10
15
  ReorderableListItemProps,
11
16
  ReorderableListProps,
12
17
  ReorderableListReorderEvent,
18
+ ScrollViewContainerProps,
13
19
  } from './types';
14
20
  import {reorderItems} from './utils';
15
21
 
@@ -23,6 +29,9 @@ export {
23
29
  ReorderableListItem,
24
30
  ReorderableListItemConfig,
25
31
  ReorderableListItemProps,
32
+ ScrollViewContainer,
33
+ ScrollViewContainerProps,
34
+ NestedReorderableList,
26
35
  reorderItems,
27
36
  };
28
37
  export default ReorderableList;
@@ -1,4 +1,9 @@
1
- import type {FlatListProps, NativeScrollEvent, ViewProps} from 'react-native';
1
+ import type {
2
+ FlatListProps,
3
+ NativeScrollEvent,
4
+ ScrollViewProps,
5
+ ViewProps,
6
+ } from 'react-native';
2
7
 
3
8
  import {EasingFunction} from 'react-native-reanimated';
4
9
 
@@ -114,3 +119,18 @@ export interface ReorderableListItemProps extends ViewProps {
114
119
  */
115
120
  scaleAnimationConfig?: ReorderableListItemConfig;
116
121
  }
122
+
123
+ export interface ScrollViewContainerProps
124
+ extends Omit<ScrollViewProps, 'onScroll'> {
125
+ /**
126
+ * Event fired at most once per frame during scrolling. Needs to be a `worklet`. See [Reanimated docs](https://docs.swmansion.com/react-native-reanimated) for further info.
127
+ */
128
+ onScroll?: (event: NativeScrollEvent) => void;
129
+ }
130
+
131
+ export interface NestedReorderableListProps<T> extends ReorderableListProps<T> {
132
+ /**
133
+ * Whether the nested list is scrollable or not. If the nested list has a fixed height and it's scrollable it should be set to `true`, otherwise `false`. Default: `false`.
134
+ */
135
+ scrollable?: boolean;
136
+ }
@@ -1 +0,0 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeGestureHandler","_reactNativeReanimated","_interopRequireDefault","_constants","_useReorderableList","_contexts","_ReorderableListCell","e","__esModule","default","_getRequireWildcardCache","WeakMap","r","t","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","hasOwnProperty","call","i","set","_extends","assign","bind","arguments","length","apply","AnimatedFlatList","Animated","createAnimatedComponent","FlatList","ReorderableList","data","autoscrollThreshold","autoscrollSpeedScale","autoscrollDelay","AUTOSCROLL_DELAY","animationDuration","dragReorderThreshold","onLayout","onReorder","onScroll","onDragEnd","keyExtractor","extraData","rest","ref","gestureHandler","handleScroll","handleFlatListLayout","handleRef","startDrag","listContextValue","itemOffset","itemHeight","dragY","draggedIndex","duration","useReorderableList","renderAnimatedCell","useCallback","cellKey","props","createElement","ReorderableListCell","key","index","ReorderableListContext","Provider","value","GestureDetector","gesture","CellRendererComponent","scrollEventThrottle","horizontal","removeClippedSubviews","numColumns","MemoizedReorderableList","exports","React","memo","forwardRef"],"sourceRoot":"../../../../src","sources":["components/ReorderableList/ReorderableList.tsx"],"mappings":"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","ignoreList":[]}
@@ -1 +0,0 @@
1
- {"version":3,"names":["AUTOSCROLL_INCREMENT","exports","AUTOSCROLL_DELAY"],"sourceRoot":"../../../../src","sources":["components/ReorderableList/constants.ios.ts"],"mappings":";;;;;;AAAA;AACO,MAAMA,oBAAoB,GAAAC,OAAA,CAAAD,oBAAA,GAAG,EAAE;AAC/B,MAAME,gBAAgB,GAAAD,OAAA,CAAAC,gBAAA,GAAG,GAAG","ignoreList":[]}