react-native-reorderable-list 0.16.2 → 0.17.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 +12 -5
- package/lib/commonjs/components/NestedReorderableList.js +6 -6
- package/lib/commonjs/components/NestedReorderableList.js.map +1 -1
- package/lib/commonjs/components/ReorderableList.js +3 -3
- package/lib/commonjs/components/ReorderableList.js.map +1 -1
- package/lib/commonjs/components/ReorderableListCell.js +20 -18
- package/lib/commonjs/components/ReorderableListCell.js.map +1 -1
- package/lib/commonjs/components/ReorderableListCore.js +168 -158
- package/lib/commonjs/components/ReorderableListCore.js.map +1 -1
- package/lib/commonjs/components/ScrollViewContainer.js +13 -12
- package/lib/commonjs/components/ScrollViewContainer.js.map +1 -1
- package/lib/commonjs/contexts/ReorderableListContext.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/NestedReorderableList.js +6 -6
- package/lib/module/components/NestedReorderableList.js.map +1 -1
- package/lib/module/components/ReorderableList.js +3 -3
- package/lib/module/components/ReorderableList.js.map +1 -1
- package/lib/module/components/ReorderableListCell.js +20 -18
- package/lib/module/components/ReorderableListCell.js.map +1 -1
- package/lib/module/components/ReorderableListCore.js +168 -158
- package/lib/module/components/ReorderableListCore.js.map +1 -1
- package/lib/module/components/ScrollViewContainer.js +13 -12
- package/lib/module/components/ScrollViewContainer.js.map +1 -1
- package/lib/module/contexts/ReorderableListContext.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/components/ReorderableListCell.d.ts +3 -3
- package/lib/typescript/components/ReorderableListCell.d.ts.map +1 -1
- package/lib/typescript/components/ReorderableListCore.d.ts +3 -3
- package/lib/typescript/components/ReorderableListCore.d.ts.map +1 -1
- package/lib/typescript/components/ScrollViewContainer.d.ts.map +1 -1
- package/lib/typescript/contexts/ReorderableListContext.d.ts +2 -1
- package/lib/typescript/contexts/ReorderableListContext.d.ts.map +1 -1
- package/lib/typescript/contexts/ScrollViewContainerContext.d.ts +3 -3
- package/lib/typescript/contexts/ScrollViewContainerContext.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types/props.d.ts +18 -4
- package/lib/typescript/types/props.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/NestedReorderableList.tsx +6 -6
- package/src/components/ReorderableList.tsx +3 -3
- package/src/components/ReorderableListCell.tsx +27 -19
- package/src/components/ReorderableListCore.tsx +277 -217
- package/src/components/ScrollViewContainer.tsx +27 -14
- package/src/contexts/ReorderableListContext.ts +2 -1
- package/src/contexts/ScrollViewContainerContext.ts +3 -3
- package/src/index.ts +2 -0
- package/src/types/props.ts +24 -5
|
@@ -23,9 +23,9 @@ const ReorderableListCore = ({
|
|
|
23
23
|
onDragEnd,
|
|
24
24
|
onIndexChange,
|
|
25
25
|
scrollViewContainerRef,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
scrollViewPageXY,
|
|
27
|
+
scrollViewSize,
|
|
28
|
+
scrollViewScrollOffsetXY,
|
|
29
29
|
scrollViewScrollEnabledProp,
|
|
30
30
|
setScrollViewForceDisableScroll,
|
|
31
31
|
scrollable,
|
|
@@ -50,35 +50,35 @@ const ReorderableListCore = ({
|
|
|
50
50
|
const scrollEnabledProp = usePropAsSharedValue(scrollEnabled);
|
|
51
51
|
const currentScrollEnabled = useSharedValue(scrollEnabled);
|
|
52
52
|
const gestureState = useSharedValue(State.UNDETERMINED);
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
// The scroll y translation of the list since drag start
|
|
61
|
-
const
|
|
62
|
-
// The initial scroll offset y of the list on drag start
|
|
63
|
-
const
|
|
64
|
-
// The scroll y translation of the ScrollViewContainer since drag start
|
|
65
|
-
const
|
|
66
|
-
// The initial scroll offset y of the ScrollViewContainer on drag start
|
|
67
|
-
const
|
|
68
|
-
const
|
|
53
|
+
const currentXY = useSharedValue(0);
|
|
54
|
+
const currentTranslationXY = useSharedValue(0);
|
|
55
|
+
const currentItemDragCenterXY = useSharedValue(null);
|
|
56
|
+
const startItemDragCenterXY = useSharedValue(0);
|
|
57
|
+
const flatListScrollOffsetXY = useSharedValue(0);
|
|
58
|
+
const flatListSize = useSharedValue(0);
|
|
59
|
+
const flatListPageXY = useSharedValue(0);
|
|
60
|
+
// The scroll x or y translation of the list since drag start
|
|
61
|
+
const dragScrollTranslationXY = useSharedValue(0);
|
|
62
|
+
// The initial scroll offset x or y of the list on drag start
|
|
63
|
+
const dragInitialScrollOffsetXY = useSharedValue(0);
|
|
64
|
+
// The scroll x or y translation of the ScrollViewContainer since drag start
|
|
65
|
+
const scrollViewDragScrollTranslationXY = useSharedValue(0);
|
|
66
|
+
// The initial scroll offset x or y of the ScrollViewContainer on drag start
|
|
67
|
+
const scrollViewDragInitialScrollOffsetXY = useSharedValue(0);
|
|
68
|
+
const draggedSize = useSharedValue(0);
|
|
69
69
|
const itemOffset = useSharedValue([]);
|
|
70
|
-
const
|
|
71
|
-
// We need to track data length since itemOffset and
|
|
70
|
+
const itemSize = useSharedValue([]);
|
|
71
|
+
// We need to track data length since itemOffset and itemSize might contain more data than we need.
|
|
72
72
|
// e.g. items are removed from the list, in which case layout data for those items is set to 0.
|
|
73
73
|
const itemCount = useSharedValue(data.length);
|
|
74
74
|
const autoscrollTrigger = useSharedValue(-1);
|
|
75
75
|
const lastAutoscrollTrigger = useSharedValue(-1);
|
|
76
|
-
const
|
|
76
|
+
const dragXY = useSharedValue(0);
|
|
77
77
|
const currentIndex = useSharedValue(-1);
|
|
78
78
|
const draggedIndex = useSharedValue(-1);
|
|
79
79
|
const state = useSharedValue(ReorderableListState.IDLE);
|
|
80
80
|
const dragEndHandlers = useSharedValue([]);
|
|
81
|
-
const
|
|
81
|
+
const startXY = useSharedValue(0);
|
|
82
82
|
const scaleDefault = useSharedValue(1);
|
|
83
83
|
const opacityDefault = useSharedValue(1);
|
|
84
84
|
const dragDirection = useSharedValue(0);
|
|
@@ -90,9 +90,10 @@ const ReorderableListCore = ({
|
|
|
90
90
|
const animationDurationProp = usePropAsSharedValue(animationDuration);
|
|
91
91
|
const autoscrollActivationDeltaProp = usePropAsSharedValue(autoscrollActivationDelta);
|
|
92
92
|
const dragEnabledProp = usePropAsSharedValue(dragEnabled ?? true);
|
|
93
|
+
const horizontalProp = usePropAsSharedValue(!!rest.horizontal);
|
|
93
94
|
|
|
94
95
|
// Position of the list relative to the scroll container
|
|
95
|
-
const
|
|
96
|
+
const nestedFlatListPositionXY = useDerivedValue(() => flatListPageXY.value - ((scrollViewPageXY === null || scrollViewPageXY === void 0 ? void 0 : scrollViewPageXY.value) || 0));
|
|
96
97
|
useEffect(() => {
|
|
97
98
|
itemCount.value = data.length;
|
|
98
99
|
|
|
@@ -102,13 +103,13 @@ const ReorderableListCore = ({
|
|
|
102
103
|
if (data.length < prevItemCount.current) {
|
|
103
104
|
for (let i = data.length; i < prevItemCount.current; i++) {
|
|
104
105
|
runOnUI(() => {
|
|
105
|
-
|
|
106
|
+
itemSize.value[i] = 0;
|
|
106
107
|
itemOffset.value[i] = 0;
|
|
107
108
|
})();
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
prevItemCount.current = data.length;
|
|
111
|
-
}, [data.length,
|
|
112
|
+
}, [data.length, itemSize, itemOffset, itemCount]);
|
|
112
113
|
useEffect(() => {
|
|
113
114
|
if (!markedCellsRef.current ||
|
|
114
115
|
// Clean keys once they surpass by 10% the size of the list itself.
|
|
@@ -141,12 +142,13 @@ const ReorderableListCore = ({
|
|
|
141
142
|
return `${cellKey}#${mark}`;
|
|
142
143
|
}, []);
|
|
143
144
|
const listContextValue = useMemo(() => ({
|
|
144
|
-
|
|
145
|
+
draggedSize,
|
|
145
146
|
currentIndex,
|
|
146
147
|
draggedIndex,
|
|
147
148
|
dragEndHandlers,
|
|
148
149
|
activeIndex,
|
|
149
150
|
itemLayoutAnimation: itemLayoutAnimationPropRef,
|
|
151
|
+
horizontal: horizontalProp,
|
|
150
152
|
cellAnimations: {
|
|
151
153
|
...cellAnimations,
|
|
152
154
|
transform: cellAnimations && 'transform' in cellAnimations ? cellAnimations.transform : [{
|
|
@@ -154,7 +156,7 @@ const ReorderableListCore = ({
|
|
|
154
156
|
}],
|
|
155
157
|
opacity: cellAnimations && 'opacity' in cellAnimations ? cellAnimations.opacity : opacityDefault
|
|
156
158
|
}
|
|
157
|
-
}), [
|
|
159
|
+
}), [draggedSize, currentIndex, draggedIndex, dragEndHandlers, activeIndex, cellAnimations, itemLayoutAnimationPropRef, scaleDefault, opacityDefault, horizontalProp]);
|
|
158
160
|
|
|
159
161
|
/**
|
|
160
162
|
* Decides the intended drag direction of the user.
|
|
@@ -166,41 +168,46 @@ const ReorderableListCore = ({
|
|
|
166
168
|
const setDragDirection = useCallback(e => {
|
|
167
169
|
'worklet';
|
|
168
170
|
|
|
169
|
-
const
|
|
171
|
+
const absoluteXY = horizontalProp.value ? e.absoluteX : e.absoluteY;
|
|
172
|
+
const velocityXY = horizontalProp.value ? e.velocityX : e.velocityY;
|
|
173
|
+
const direction = velocityXY > 0 ? 1 : -1;
|
|
170
174
|
if (direction !== dragDirection.value) {
|
|
171
175
|
if (lastDragDirectionPivot.value === null) {
|
|
172
|
-
lastDragDirectionPivot.value =
|
|
173
|
-
} else if (Math.abs(
|
|
176
|
+
lastDragDirectionPivot.value = absoluteXY;
|
|
177
|
+
} else if (Math.abs(absoluteXY - lastDragDirectionPivot.value) >= autoscrollActivationDeltaProp.value) {
|
|
174
178
|
dragDirection.value = direction;
|
|
175
|
-
lastDragDirectionPivot.value =
|
|
179
|
+
lastDragDirectionPivot.value = absoluteXY;
|
|
176
180
|
}
|
|
177
181
|
}
|
|
178
|
-
}, [dragDirection, lastDragDirectionPivot, autoscrollActivationDeltaProp]);
|
|
179
|
-
const
|
|
182
|
+
}, [dragDirection, lastDragDirectionPivot, autoscrollActivationDeltaProp, horizontalProp]);
|
|
183
|
+
const setCurrentItemDragCenterXY = useCallback(e => {
|
|
180
184
|
'worklet';
|
|
181
185
|
|
|
182
|
-
|
|
186
|
+
const translationXY = horizontalProp.value ? e.translationX : e.translationY;
|
|
187
|
+
if (currentItemDragCenterXY.value === null) {
|
|
183
188
|
if (currentIndex.value >= 0) {
|
|
184
|
-
const itemCenter =
|
|
185
|
-
// the y coordinate of the item relative to the list
|
|
186
|
-
const
|
|
187
|
-
const value =
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
const itemCenter = itemSize.value[currentIndex.value] * 0.5;
|
|
190
|
+
// the x or y coordinate of the item relative to the list
|
|
191
|
+
const itemXY = itemOffset.value[currentIndex.value] - (flatListScrollOffsetXY.value + scrollViewDragScrollTranslationXY.value);
|
|
192
|
+
const value = itemXY + itemCenter + translationXY;
|
|
193
|
+
startItemDragCenterXY.value = value;
|
|
194
|
+
currentItemDragCenterXY.value = value;
|
|
190
195
|
}
|
|
191
196
|
} else {
|
|
192
|
-
|
|
197
|
+
currentItemDragCenterXY.value = startItemDragCenterXY.value + translationXY;
|
|
193
198
|
}
|
|
194
|
-
}, [
|
|
199
|
+
}, [horizontalProp, currentItemDragCenterXY, currentIndex, startItemDragCenterXY, itemOffset, itemSize, flatListScrollOffsetXY, scrollViewDragScrollTranslationXY]);
|
|
195
200
|
const panGestureHandler = useMemo(() => (panGesture || Gesture.Pan()).onBegin(e => {
|
|
196
201
|
'worklet';
|
|
197
202
|
|
|
198
203
|
// prevent new dragging until item is completely released
|
|
199
204
|
if (state.value === ReorderableListState.IDLE) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
const xy = horizontalProp.value ? e.x : e.y;
|
|
206
|
+
const translationXY = horizontalProp.value ? e.translationX : e.translationY;
|
|
207
|
+
startXY.value = xy;
|
|
208
|
+
currentXY.value = xy;
|
|
209
|
+
currentTranslationXY.value = translationXY;
|
|
210
|
+
dragXY.value = translationXY;
|
|
204
211
|
gestureState.value = e.state;
|
|
205
212
|
}
|
|
206
213
|
}).onUpdate(e => {
|
|
@@ -210,10 +217,11 @@ const ReorderableListCore = ({
|
|
|
210
217
|
setDragDirection(e);
|
|
211
218
|
}
|
|
212
219
|
if (state.value !== ReorderableListState.RELEASED) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
220
|
+
setCurrentItemDragCenterXY(e);
|
|
221
|
+
const translationXY = horizontalProp.value ? e.translationX : e.translationY;
|
|
222
|
+
currentXY.value = startXY.value + translationXY;
|
|
223
|
+
currentTranslationXY.value = translationXY;
|
|
224
|
+
dragXY.value = translationXY + dragScrollTranslationXY.value + scrollViewDragScrollTranslationXY.value;
|
|
217
225
|
gestureState.value = e.state;
|
|
218
226
|
}
|
|
219
227
|
}).onEnd(e => {
|
|
@@ -224,7 +232,7 @@ const ReorderableListCore = ({
|
|
|
224
232
|
'worklet';
|
|
225
233
|
|
|
226
234
|
gestureState.value = e.state;
|
|
227
|
-
}), [panGesture, state,
|
|
235
|
+
}), [panGesture, state, startXY, currentXY, currentTranslationXY, dragXY, gestureState, dragScrollTranslationXY, scrollViewDragScrollTranslationXY, setDragDirection, setCurrentItemDragCenterXY, horizontalProp]);
|
|
228
236
|
const panGestureHandlerWithPropOptions = useMemo(() => {
|
|
229
237
|
if (typeof panActivateAfterLongPress === 'number') {
|
|
230
238
|
panGestureHandler.activateAfterLongPress(panActivateAfterLongPress);
|
|
@@ -268,13 +276,13 @@ const ReorderableListCore = ({
|
|
|
268
276
|
|
|
269
277
|
state.value = ReorderableListState.IDLE;
|
|
270
278
|
draggedIndex.value = -1;
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
279
|
+
dragXY.value = 0;
|
|
280
|
+
dragScrollTranslationXY.value = 0;
|
|
281
|
+
scrollViewDragScrollTranslationXY.value = 0;
|
|
274
282
|
dragDirection.value = 0;
|
|
275
283
|
lastDragDirectionPivot.value = null;
|
|
276
|
-
|
|
277
|
-
}, [state, draggedIndex,
|
|
284
|
+
currentItemDragCenterXY.value = null;
|
|
285
|
+
}, [state, draggedIndex, dragXY, dragScrollTranslationXY, scrollViewDragScrollTranslationXY, dragDirection, lastDragDirectionPivot, currentItemDragCenterXY]);
|
|
278
286
|
const resetSharedValuesAfterAnimations = useCallback(() => {
|
|
279
287
|
setTimeout(runOnUI(resetSharedValues), animationDurationProp.value);
|
|
280
288
|
}, [resetSharedValues, animationDurationProp]);
|
|
@@ -311,14 +319,14 @@ const ReorderableListCore = ({
|
|
|
311
319
|
const index1 = itemDirection ? from : to;
|
|
312
320
|
const index2 = itemDirection ? to : from;
|
|
313
321
|
const newOffset1 = itemOffset.value[index1];
|
|
314
|
-
const
|
|
315
|
-
const newOffset2 = itemOffset.value[index2] +
|
|
316
|
-
const
|
|
322
|
+
const newSize1 = itemSize.value[index2];
|
|
323
|
+
const newOffset2 = itemOffset.value[index2] + itemSize.value[index2] - itemSize.value[index1];
|
|
324
|
+
const newSize2 = itemSize.value[index1];
|
|
317
325
|
itemOffset.value[index1] = newOffset1;
|
|
318
|
-
|
|
326
|
+
itemSize.value[index1] = newSize1;
|
|
319
327
|
itemOffset.value[index2] = newOffset2;
|
|
320
|
-
|
|
321
|
-
}, [itemOffset,
|
|
328
|
+
itemSize.value[index2] = newSize2;
|
|
329
|
+
}, [itemOffset, itemSize]);
|
|
322
330
|
|
|
323
331
|
/**
|
|
324
332
|
* Computes a potential new drop container for the current dragged item and evaluates
|
|
@@ -330,29 +338,29 @@ const ReorderableListCore = ({
|
|
|
330
338
|
const computeCurrentIndex = useCallback(() => {
|
|
331
339
|
'worklet';
|
|
332
340
|
|
|
333
|
-
if (
|
|
341
|
+
if (currentItemDragCenterXY.value === null) {
|
|
334
342
|
return currentIndex.value;
|
|
335
343
|
}
|
|
336
344
|
|
|
337
|
-
//
|
|
338
|
-
const
|
|
345
|
+
// Apply scroll offset and scroll container translation.
|
|
346
|
+
const relativeDragCenterXY = flatListScrollOffsetXY.value + scrollViewDragScrollTranslationXY.value + currentItemDragCenterXY.value;
|
|
339
347
|
const currentOffset = itemOffset.value[currentIndex.value];
|
|
340
|
-
const
|
|
341
|
-
const currentCenter = currentOffset +
|
|
348
|
+
const currentSize = itemSize.value[currentIndex.value];
|
|
349
|
+
const currentCenter = currentOffset + currentSize * 0.5;
|
|
342
350
|
const max = itemCount.value;
|
|
343
|
-
const possibleIndex =
|
|
351
|
+
const possibleIndex = relativeDragCenterXY < currentCenter ? Math.max(0, currentIndex.value - 1) : Math.min(max - 1, currentIndex.value + 1);
|
|
344
352
|
if (currentIndex.value !== possibleIndex) {
|
|
345
353
|
let possibleOffset = itemOffset.value[possibleIndex];
|
|
346
354
|
if (possibleIndex > currentIndex.value) {
|
|
347
|
-
possibleOffset +=
|
|
355
|
+
possibleOffset += itemSize.value[possibleIndex] - currentSize;
|
|
348
356
|
}
|
|
349
|
-
const possibleCenter = possibleOffset +
|
|
350
|
-
const distanceFromCurrent = Math.abs(
|
|
351
|
-
const distanceFromPossible = Math.abs(
|
|
357
|
+
const possibleCenter = possibleOffset + currentSize * 0.5;
|
|
358
|
+
const distanceFromCurrent = Math.abs(relativeDragCenterXY - currentCenter);
|
|
359
|
+
const distanceFromPossible = Math.abs(relativeDragCenterXY - possibleCenter);
|
|
352
360
|
return distanceFromCurrent <= distanceFromPossible ? currentIndex.value : possibleIndex;
|
|
353
361
|
}
|
|
354
362
|
return currentIndex.value;
|
|
355
|
-
}, [currentIndex,
|
|
363
|
+
}, [currentIndex, currentItemDragCenterXY, itemCount, itemOffset, itemSize, flatListScrollOffsetXY, scrollViewDragScrollTranslationXY]);
|
|
356
364
|
const setCurrentIndex = useCallback(() => {
|
|
357
365
|
'worklet';
|
|
358
366
|
|
|
@@ -374,7 +382,7 @@ const ReorderableListCore = ({
|
|
|
374
382
|
scaleDefault.value = withTiming(scaleConfig.toValue, scaleConfig);
|
|
375
383
|
}
|
|
376
384
|
|
|
377
|
-
//
|
|
385
|
+
// If no custom opacity run the default.
|
|
378
386
|
if (!(cellAnimations && 'opacity' in cellAnimations)) {
|
|
379
387
|
const opacityConfig = OPACITY_ANIMATION_CONFIG_DEFAULT[type];
|
|
380
388
|
opacityDefault.value = withTiming(opacityConfig.toValue, opacityConfig);
|
|
@@ -390,7 +398,7 @@ const ReorderableListCore = ({
|
|
|
390
398
|
runOnJS(setActiveIndex)(-1);
|
|
391
399
|
}
|
|
392
400
|
|
|
393
|
-
//
|
|
401
|
+
// Trigger onDragEnd event.
|
|
394
402
|
let e = {
|
|
395
403
|
from: draggedIndex.value,
|
|
396
404
|
to: currentIndex.value
|
|
@@ -403,23 +411,23 @@ const ReorderableListCore = ({
|
|
|
403
411
|
|
|
404
412
|
// they are actually swapped on drag translation
|
|
405
413
|
const currentItemOffset = itemOffset.value[draggedIndex.value];
|
|
406
|
-
const
|
|
414
|
+
const currentItemSize = itemSize.value[draggedIndex.value];
|
|
407
415
|
const draggedItemOffset = itemOffset.value[currentIndex.value];
|
|
408
|
-
const
|
|
409
|
-
const
|
|
416
|
+
const draggedItemSize = itemSize.value[currentIndex.value];
|
|
417
|
+
const newPositionXY = currentIndex.value > draggedIndex.value ? draggedItemOffset - currentItemOffset : draggedItemOffset - currentItemOffset + (draggedItemSize - currentItemSize);
|
|
410
418
|
runDefaultDragAnimations('end');
|
|
411
|
-
if (
|
|
412
|
-
//
|
|
413
|
-
|
|
419
|
+
if (dragXY.value !== newPositionXY) {
|
|
420
|
+
// Animate dragged item to its new position on release.
|
|
421
|
+
dragXY.value = withTiming(newPositionXY, {
|
|
414
422
|
duration: animationDurationProp.value,
|
|
415
423
|
easing: Easing.out(Easing.ease)
|
|
416
424
|
}, () => {
|
|
417
425
|
runOnJS(reorder)(draggedIndex.value, currentIndex.value);
|
|
418
426
|
});
|
|
419
427
|
} else {
|
|
420
|
-
//
|
|
428
|
+
// User might drag and release the item without moving it so,
|
|
421
429
|
// since the animation end callback is not executed in that case
|
|
422
|
-
// we need to reset values as the reorder function would do
|
|
430
|
+
// we need to reset values as the reorder function would do.
|
|
423
431
|
runOnJS(resetSharedValuesAfterAnimations)();
|
|
424
432
|
}
|
|
425
433
|
}
|
|
@@ -427,58 +435,58 @@ const ReorderableListCore = ({
|
|
|
427
435
|
const computeHiddenArea = useCallback(() => {
|
|
428
436
|
'worklet';
|
|
429
437
|
|
|
430
|
-
if (!
|
|
438
|
+
if (!scrollViewScrollOffsetXY || !scrollViewSize) {
|
|
431
439
|
return {
|
|
432
|
-
|
|
433
|
-
|
|
440
|
+
start: 0,
|
|
441
|
+
end: 0
|
|
434
442
|
};
|
|
435
443
|
}
|
|
436
444
|
|
|
437
445
|
// hidden area cannot be negative
|
|
438
|
-
const
|
|
439
|
-
const
|
|
446
|
+
const start = Math.max(0, scrollViewScrollOffsetXY.value - nestedFlatListPositionXY.value);
|
|
447
|
+
const end = Math.max(0, nestedFlatListPositionXY.value + flatListSize.value - (scrollViewScrollOffsetXY.value + scrollViewSize.value));
|
|
440
448
|
return {
|
|
441
|
-
|
|
442
|
-
|
|
449
|
+
start,
|
|
450
|
+
end
|
|
443
451
|
};
|
|
444
|
-
}, [
|
|
452
|
+
}, [scrollViewScrollOffsetXY, scrollViewSize, nestedFlatListPositionXY, flatListSize]);
|
|
445
453
|
const computeThresholdArea = useCallback(() => {
|
|
446
454
|
'worklet';
|
|
447
455
|
|
|
448
456
|
const hiddenArea = computeHiddenArea();
|
|
449
|
-
const
|
|
450
|
-
const
|
|
457
|
+
const offsetStart = Math.max(0, (autoscrollThresholdOffset === null || autoscrollThresholdOffset === void 0 ? void 0 : autoscrollThresholdOffset.start) || (autoscrollThresholdOffset === null || autoscrollThresholdOffset === void 0 ? void 0 : autoscrollThresholdOffset.top) || 0);
|
|
458
|
+
const offsetEnd = Math.max(0, (autoscrollThresholdOffset === null || autoscrollThresholdOffset === void 0 ? void 0 : autoscrollThresholdOffset.end) || (autoscrollThresholdOffset === null || autoscrollThresholdOffset === void 0 ? void 0 : autoscrollThresholdOffset.bottom) || 0);
|
|
451
459
|
const threshold = Math.max(0, Math.min(autoscrollThreshold, 0.4));
|
|
452
|
-
const
|
|
453
|
-
const area =
|
|
454
|
-
const
|
|
455
|
-
const
|
|
460
|
+
const visibleSize = flatListSize.value - (hiddenArea.start + hiddenArea.end) - (offsetStart + offsetEnd);
|
|
461
|
+
const area = visibleSize * threshold;
|
|
462
|
+
const start = area + offsetStart;
|
|
463
|
+
const end = flatListSize.value - area - offsetEnd;
|
|
456
464
|
return {
|
|
457
|
-
|
|
458
|
-
|
|
465
|
+
start,
|
|
466
|
+
end
|
|
459
467
|
};
|
|
460
|
-
}, [computeHiddenArea, autoscrollThreshold, autoscrollThresholdOffset,
|
|
468
|
+
}, [computeHiddenArea, autoscrollThreshold, autoscrollThresholdOffset, flatListSize]);
|
|
461
469
|
const computeContainerThresholdArea = useCallback(() => {
|
|
462
470
|
'worklet';
|
|
463
471
|
|
|
464
|
-
if (!
|
|
472
|
+
if (!scrollViewSize) {
|
|
465
473
|
return {
|
|
466
|
-
|
|
467
|
-
|
|
474
|
+
start: -Infinity,
|
|
475
|
+
end: Infinity
|
|
468
476
|
};
|
|
469
477
|
}
|
|
470
|
-
const
|
|
471
|
-
const
|
|
478
|
+
const offsetStart = Math.max(0, (autoscrollThresholdOffset === null || autoscrollThresholdOffset === void 0 ? void 0 : autoscrollThresholdOffset.start) || (autoscrollThresholdOffset === null || autoscrollThresholdOffset === void 0 ? void 0 : autoscrollThresholdOffset.top) || 0);
|
|
479
|
+
const offsetEnd = Math.max(0, (autoscrollThresholdOffset === null || autoscrollThresholdOffset === void 0 ? void 0 : autoscrollThresholdOffset.end) || (autoscrollThresholdOffset === null || autoscrollThresholdOffset === void 0 ? void 0 : autoscrollThresholdOffset.bottom) || 0);
|
|
472
480
|
const threshold = Math.max(0, Math.min(autoscrollThreshold, 0.4));
|
|
473
|
-
const
|
|
474
|
-
const area =
|
|
475
|
-
const
|
|
476
|
-
const
|
|
481
|
+
const visibleSize = scrollViewSize.value - (offsetStart + offsetEnd);
|
|
482
|
+
const area = visibleSize * threshold;
|
|
483
|
+
const start = area + offsetStart;
|
|
484
|
+
const end = visibleSize - area - offsetEnd;
|
|
477
485
|
return {
|
|
478
|
-
|
|
479
|
-
|
|
486
|
+
start,
|
|
487
|
+
end
|
|
480
488
|
};
|
|
481
|
-
}, [autoscrollThreshold, autoscrollThresholdOffset,
|
|
489
|
+
}, [autoscrollThreshold, autoscrollThresholdOffset, scrollViewSize]);
|
|
482
490
|
const shouldScrollContainer = useCallback(y => {
|
|
483
491
|
'worklet';
|
|
484
492
|
|
|
@@ -487,52 +495,53 @@ const ReorderableListCore = ({
|
|
|
487
495
|
|
|
488
496
|
// We should scroll the container if there's a hidden part of the nested list.
|
|
489
497
|
// We might have floating errors like 0.0001 which we should ignore.
|
|
490
|
-
return nestedListHiddenArea.
|
|
498
|
+
return nestedListHiddenArea.start > 0.01 && y <= containerThresholdArea.start || nestedListHiddenArea.end > 0.01 && y >= containerThresholdArea.end;
|
|
491
499
|
}, [computeHiddenArea, computeContainerThresholdArea]);
|
|
492
|
-
const
|
|
500
|
+
const getRelativeContainerXY = useCallback(() => {
|
|
493
501
|
'worklet';
|
|
494
502
|
|
|
495
|
-
return
|
|
496
|
-
}, [
|
|
497
|
-
const
|
|
503
|
+
return currentXY.value + nestedFlatListPositionXY.value - scrollViewDragInitialScrollOffsetXY.value;
|
|
504
|
+
}, [currentXY, nestedFlatListPositionXY, scrollViewDragInitialScrollOffsetXY]);
|
|
505
|
+
const getRelativeListXY = useCallback(() => {
|
|
498
506
|
'worklet';
|
|
499
507
|
|
|
500
|
-
return
|
|
501
|
-
}, [
|
|
508
|
+
return currentXY.value + scrollViewDragScrollTranslationXY.value;
|
|
509
|
+
}, [currentXY, scrollViewDragScrollTranslationXY]);
|
|
502
510
|
const scrollDirection = useCallback(() => {
|
|
503
511
|
'worklet';
|
|
504
512
|
|
|
505
|
-
const
|
|
506
|
-
if (shouldScrollContainer(
|
|
513
|
+
const relativeContainerXY = getRelativeContainerXY();
|
|
514
|
+
if (shouldScrollContainer(relativeContainerXY)) {
|
|
507
515
|
const containerThresholdArea = computeContainerThresholdArea();
|
|
508
|
-
if (
|
|
516
|
+
if (relativeContainerXY <= containerThresholdArea.start) {
|
|
509
517
|
return -1;
|
|
510
518
|
}
|
|
511
|
-
if (
|
|
519
|
+
if (relativeContainerXY >= containerThresholdArea.end) {
|
|
512
520
|
return 1;
|
|
513
521
|
}
|
|
514
522
|
} else if (scrollable) {
|
|
515
|
-
const
|
|
523
|
+
const relativeListXY = getRelativeListXY();
|
|
516
524
|
const thresholdArea = computeThresholdArea();
|
|
517
|
-
if (
|
|
525
|
+
if (relativeListXY <= thresholdArea.start) {
|
|
518
526
|
return -1;
|
|
519
527
|
}
|
|
520
|
-
if (
|
|
528
|
+
if (relativeListXY >= thresholdArea.end) {
|
|
521
529
|
return 1;
|
|
522
530
|
}
|
|
523
531
|
}
|
|
524
532
|
return 0;
|
|
525
|
-
}, [shouldScrollContainer, computeThresholdArea, computeContainerThresholdArea,
|
|
526
|
-
useAnimatedReaction(() =>
|
|
533
|
+
}, [shouldScrollContainer, computeThresholdArea, computeContainerThresholdArea, getRelativeContainerXY, getRelativeListXY, scrollable]);
|
|
534
|
+
useAnimatedReaction(() => currentXY.value, () => {
|
|
527
535
|
if (state.value === ReorderableListState.DRAGGED || state.value === ReorderableListState.AUTOSCROLL) {
|
|
528
536
|
setCurrentIndex();
|
|
529
537
|
|
|
530
538
|
// Trigger autoscroll when:
|
|
531
|
-
// 1. Within the threshold area (
|
|
539
|
+
// 1. Within the threshold area (start or end of list)
|
|
532
540
|
// 2. Have dragged in the same direction as the scroll
|
|
533
541
|
// 3. Not already in autoscroll mode
|
|
534
542
|
if (dragDirection.value === scrollDirection()) {
|
|
535
|
-
// When the first two conditions are met and it's already in autoscroll mode,
|
|
543
|
+
// When the first two conditions are met and it's already in autoscroll mode,
|
|
544
|
+
// we let it continue (no-op).
|
|
536
545
|
if (state.value !== ReorderableListState.AUTOSCROLL) {
|
|
537
546
|
state.value = ReorderableListState.AUTOSCROLL;
|
|
538
547
|
lastAutoscrollTrigger.value = autoscrollTrigger.value;
|
|
@@ -547,17 +556,18 @@ const ReorderableListCore = ({
|
|
|
547
556
|
if (autoscrollTrigger.value !== lastAutoscrollTrigger.value && state.value === ReorderableListState.AUTOSCROLL) {
|
|
548
557
|
const autoscrollIncrement = dragDirection.value * AUTOSCROLL_CONFIG.increment * autoscrollSpeedScale;
|
|
549
558
|
if (autoscrollIncrement !== 0) {
|
|
550
|
-
let scrollOffset =
|
|
559
|
+
let scrollOffset = flatListScrollOffsetXY.value;
|
|
551
560
|
let listRef = flatListRef;
|
|
552
561
|
|
|
553
562
|
// Checking on every autoscroll whether to scroll the container,
|
|
554
563
|
// this allows to smoothly pass the scroll from the container to the nested list
|
|
555
564
|
// without any gesture input.
|
|
556
|
-
if (
|
|
557
|
-
scrollOffset =
|
|
565
|
+
if (scrollViewScrollOffsetXY && shouldScrollContainer(getRelativeContainerXY())) {
|
|
566
|
+
scrollOffset = scrollViewScrollOffsetXY.value;
|
|
558
567
|
listRef = scrollViewContainerRef;
|
|
559
568
|
}
|
|
560
|
-
|
|
569
|
+
const scrollToValue = scrollOffset + autoscrollIncrement;
|
|
570
|
+
scrollTo(listRef, horizontalProp.value ? scrollToValue : 0, horizontalProp.value ? 0 : scrollToValue, true);
|
|
561
571
|
}
|
|
562
572
|
|
|
563
573
|
// when autoscrolling user may not be moving his finger so we need
|
|
@@ -568,16 +578,16 @@ const ReorderableListCore = ({
|
|
|
568
578
|
|
|
569
579
|
// flatlist scroll handler
|
|
570
580
|
const handleScroll = useAnimatedScrollHandler(e => {
|
|
571
|
-
|
|
581
|
+
flatListScrollOffsetXY.value = horizontalProp.value ? e.contentOffset.x : e.contentOffset.y;
|
|
572
582
|
|
|
573
583
|
// Checking if the list is not scrollable instead of the scrolling state.
|
|
574
584
|
// Fixes a bug on iOS where the item is shifted after autoscrolling and then
|
|
575
585
|
// moving away from the area.
|
|
576
586
|
if (!currentScrollEnabled.value) {
|
|
577
|
-
|
|
587
|
+
dragScrollTranslationXY.value = flatListScrollOffsetXY.value - dragInitialScrollOffsetXY.value;
|
|
578
588
|
}
|
|
579
589
|
if (state.value === ReorderableListState.AUTOSCROLL) {
|
|
580
|
-
|
|
590
|
+
dragXY.value = currentTranslationXY.value + dragScrollTranslationXY.value + scrollViewDragScrollTranslationXY.value;
|
|
581
591
|
lastAutoscrollTrigger.value = autoscrollTrigger.value;
|
|
582
592
|
autoscrollTrigger.value = withDelay(autoscrollDelay, withTiming(autoscrollTrigger.value * -1, {
|
|
583
593
|
duration: 0
|
|
@@ -586,16 +596,16 @@ const ReorderableListCore = ({
|
|
|
586
596
|
});
|
|
587
597
|
|
|
588
598
|
// container scroll handler
|
|
589
|
-
useAnimatedReaction(() =>
|
|
599
|
+
useAnimatedReaction(() => scrollViewScrollOffsetXY === null || scrollViewScrollOffsetXY === void 0 ? void 0 : scrollViewScrollOffsetXY.value, value => {
|
|
590
600
|
if (value) {
|
|
591
601
|
// Checking if the list is not scrollable instead of the scrolling state.
|
|
592
|
-
// Fixes a bug on iOS where the item is shifted after autoscrolling and then
|
|
602
|
+
// Fixes a bug on iOS where the item is shifted, after autoscrolling and then
|
|
593
603
|
// moving away from the area.
|
|
594
604
|
if (!currentScrollEnabled.value) {
|
|
595
|
-
|
|
605
|
+
scrollViewDragScrollTranslationXY.value = value - scrollViewDragInitialScrollOffsetXY.value;
|
|
596
606
|
}
|
|
597
607
|
if (state.value === ReorderableListState.AUTOSCROLL) {
|
|
598
|
-
|
|
608
|
+
dragXY.value = currentTranslationXY.value + scrollViewDragScrollTranslationXY.value;
|
|
599
609
|
lastAutoscrollTrigger.value = autoscrollTrigger.value;
|
|
600
610
|
autoscrollTrigger.value = withDelay(autoscrollDelay, withTiming(autoscrollTrigger.value * -1, {
|
|
601
611
|
duration: 0
|
|
@@ -610,17 +620,17 @@ const ReorderableListCore = ({
|
|
|
610
620
|
return;
|
|
611
621
|
}
|
|
612
622
|
|
|
613
|
-
//
|
|
623
|
+
// Allow new drag when item is completely released.
|
|
614
624
|
if (state.value === ReorderableListState.IDLE) {
|
|
615
|
-
//
|
|
616
|
-
// after scrolling the parent list it would offset the new dragged item in another nested list
|
|
625
|
+
// Resetting shared values again fixes a flickeing bug in nested lists where
|
|
626
|
+
// after scrolling the parent list it would offset the new dragged item in another nested list.
|
|
617
627
|
resetSharedValues();
|
|
618
628
|
if (shouldUpdateActiveItem) {
|
|
619
629
|
runOnJS(setActiveIndex)(index);
|
|
620
630
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
631
|
+
dragInitialScrollOffsetXY.value = flatListScrollOffsetXY.value;
|
|
632
|
+
scrollViewDragInitialScrollOffsetXY.value = (scrollViewScrollOffsetXY === null || scrollViewScrollOffsetXY === void 0 ? void 0 : scrollViewScrollOffsetXY.value) || 0;
|
|
633
|
+
draggedSize.value = itemSize.value[index];
|
|
624
634
|
draggedIndex.value = index;
|
|
625
635
|
currentIndex.value = index;
|
|
626
636
|
state.value = ReorderableListState.DRAGGED;
|
|
@@ -632,29 +642,30 @@ const ReorderableListCore = ({
|
|
|
632
642
|
index
|
|
633
643
|
});
|
|
634
644
|
}
|
|
635
|
-
}, [dragEnabledProp, resetSharedValues, shouldUpdateActiveItem,
|
|
645
|
+
}, [dragEnabledProp, resetSharedValues, shouldUpdateActiveItem, dragInitialScrollOffsetXY, scrollViewScrollOffsetXY, scrollViewDragInitialScrollOffsetXY, setScrollEnabled, currentIndex, draggedSize, draggedIndex, state, flatListScrollOffsetXY, itemSize, onDragStart, runDefaultDragAnimations]);
|
|
636
646
|
const handleFlatListLayout = useCallback(e => {
|
|
637
|
-
|
|
647
|
+
flatListSize.value = horizontalProp.value ? e.nativeEvent.layout.width : e.nativeEvent.layout.height;
|
|
638
648
|
|
|
639
649
|
// If nested in a scroll container.
|
|
640
|
-
if (
|
|
641
|
-
// Timeout fixes a bug where measure returns height 0.
|
|
650
|
+
if (scrollViewScrollOffsetXY) {
|
|
651
|
+
// Timeout fixes a bug where measure returns width or height 0.
|
|
642
652
|
setTimeout(() => {
|
|
643
653
|
runOnUI(() => {
|
|
644
654
|
const measurement = measure(flatListRef);
|
|
645
655
|
if (!measurement) {
|
|
646
656
|
return;
|
|
647
657
|
}
|
|
658
|
+
const pageXY = horizontalProp.value ? measurement.pageX : measurement.pageY;
|
|
648
659
|
|
|
649
660
|
// We need to use pageY because the list might be nested into other views,
|
|
650
661
|
// It's important that we take the measurement of the list without any scroll offset
|
|
651
662
|
// from the scroll container.
|
|
652
|
-
|
|
663
|
+
flatListPageXY.value = pageXY + ((scrollViewScrollOffsetXY === null || scrollViewScrollOffsetXY === void 0 ? void 0 : scrollViewScrollOffsetXY.value) || 0);
|
|
653
664
|
})();
|
|
654
665
|
}, 100);
|
|
655
666
|
}
|
|
656
667
|
onLayout === null || onLayout === void 0 || onLayout(e);
|
|
657
|
-
}, [flatListRef,
|
|
668
|
+
}, [flatListRef, flatListPageXY, flatListSize, horizontalProp, scrollViewScrollOffsetXY, onLayout]);
|
|
658
669
|
const handleRef = useCallback(value => {
|
|
659
670
|
flatListRef(value);
|
|
660
671
|
if (typeof ref === 'function') {
|
|
@@ -664,7 +675,7 @@ const ReorderableListCore = ({
|
|
|
664
675
|
}
|
|
665
676
|
}, [flatListRef, ref]);
|
|
666
677
|
const combinedGesture = useMemo(() => {
|
|
667
|
-
//
|
|
678
|
+
// Android is able to handle nested scroll view, but not the full size ones like iOS.
|
|
668
679
|
if (outerScrollGesture && !(Platform.OS === 'android' && scrollable)) {
|
|
669
680
|
return Gesture.Simultaneous(outerScrollGesture, gestureHandler);
|
|
670
681
|
}
|
|
@@ -678,8 +689,8 @@ const ReorderableListCore = ({
|
|
|
678
689
|
// forces remount with key change on reorder
|
|
679
690
|
key: createCellKey(cellKey),
|
|
680
691
|
itemOffset: itemOffset,
|
|
681
|
-
|
|
682
|
-
|
|
692
|
+
itemSize: itemSize,
|
|
693
|
+
dragXY: dragXY,
|
|
683
694
|
draggedIndex: draggedIndex,
|
|
684
695
|
animationDuration: animationDurationProp,
|
|
685
696
|
startDrag: startDrag
|
|
@@ -696,7 +707,6 @@ const ReorderableListCore = ({
|
|
|
696
707
|
onLayout: handleFlatListLayout,
|
|
697
708
|
onScroll: composedScrollHandler,
|
|
698
709
|
scrollEventThrottle: 1,
|
|
699
|
-
horizontal: false,
|
|
700
710
|
removeClippedSubviews: false,
|
|
701
711
|
numColumns: 1
|
|
702
712
|
// We force disable scroll or let the component prop control it.
|