react-native-drax 0.10.3 → 0.11.0-alpha.2
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/.eslintrc.js +2 -73
- package/.prettierrc +16 -0
- package/README.md +81 -1
- package/build/AllHoverViews.d.ts +0 -0
- package/build/AllHoverViews.js +30 -0
- package/build/DraxContext.d.ts +0 -1
- package/build/DraxList.d.ts +3 -3
- package/build/DraxList.js +231 -216
- package/build/DraxListItem.d.ts +7 -0
- package/build/DraxListItem.js +121 -0
- package/build/DraxProvider.d.ts +1 -3
- package/build/DraxProvider.js +322 -259
- package/build/DraxScrollView.d.ts +1 -1
- package/build/DraxScrollView.js +29 -90
- package/build/DraxSubprovider.d.ts +2 -2
- package/build/DraxSubprovider.js +1 -1
- package/build/DraxView.d.ts +7 -2
- package/build/DraxView.js +60 -383
- package/build/HoverView.d.ts +8 -0
- package/build/HoverView.js +40 -0
- package/build/PanGestureDetector.d.ts +3 -0
- package/build/PanGestureDetector.js +49 -0
- package/build/hooks/useContent.d.ts +23 -0
- package/build/hooks/useContent.js +212 -0
- package/build/hooks/useDraxProtocol.d.ts +5 -0
- package/build/hooks/useDraxProtocol.js +32 -0
- package/build/hooks/useDraxRegistry.d.ts +21 -20
- package/build/hooks/useDraxRegistry.js +195 -182
- package/build/hooks/useDraxScrollHandler.d.ts +25 -0
- package/build/hooks/useDraxScrollHandler.js +89 -0
- package/build/hooks/useDraxState.d.ts +1 -1
- package/build/hooks/useDraxState.js +9 -6
- package/build/hooks/useMeasurements.d.ts +9 -0
- package/build/hooks/useMeasurements.js +119 -0
- package/build/hooks/useStatus.d.ts +11 -0
- package/build/hooks/useStatus.js +96 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +4 -1
- package/build/math.d.ts +2 -2
- package/build/math.js +10 -6
- package/build/params.d.ts +9 -0
- package/build/params.js +7 -1
- package/build/transform.d.ts +11 -4
- package/build/transform.js +50 -8
- package/build/types.d.ts +238 -87
- package/build/types.js +10 -7
- package/package.json +43 -45
package/build/DraxList.js
CHANGED
|
@@ -22,51 +22,40 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
25
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
29
|
exports.DraxList = void 0;
|
|
30
|
+
const lodash_throttle_1 = __importDefault(require("lodash.throttle"));
|
|
27
31
|
const react_1 = __importStar(require("react"));
|
|
28
32
|
const react_native_1 = require("react-native");
|
|
29
|
-
const
|
|
33
|
+
const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
|
|
30
34
|
const DraxSubprovider_1 = require("./DraxSubprovider");
|
|
31
|
-
const
|
|
32
|
-
const
|
|
35
|
+
const DraxView_1 = require("./DraxView");
|
|
36
|
+
const useDraxScrollHandler_1 = require("./hooks/useDraxScrollHandler");
|
|
33
37
|
const params_1 = require("./params");
|
|
34
|
-
const
|
|
35
|
-
draggingStyle: { opacity: 0 },
|
|
36
|
-
dragReleasedStyle: { opacity: 0.5 },
|
|
37
|
-
});
|
|
38
|
+
const types_1 = require("./types");
|
|
38
39
|
const DraxListUnforwarded = (props, forwardedRef) => {
|
|
39
|
-
const { data, style,
|
|
40
|
+
const { data, style, onItemDragStart, onItemDragPositionChange, onItemDragEnd, onItemReorder, reorderable: reorderableProp, onScroll: onScrollProp, lockItemDragsToMainAxis = false, longPressDelay = params_1.defaultListItemLongPressDelay, parentDraxViewProps, monitoringExternalDragStyle, ...flatListProps } = props;
|
|
40
41
|
// Copy the value of the horizontal property for internal use.
|
|
41
42
|
const horizontal = flatListProps.horizontal ?? false;
|
|
42
43
|
// Get the item count for internal use.
|
|
43
44
|
const itemCount = data?.length ?? 0;
|
|
44
45
|
// Set a sensible default for reorderable prop.
|
|
45
|
-
const reorderable = reorderableProp ??
|
|
46
|
-
// The unique identifer for this list's Drax view.
|
|
47
|
-
const id = (0, hooks_1.useDraxId)(idProp);
|
|
48
|
-
// FlatList, used for scrolling.
|
|
49
|
-
const flatListRef = (0, react_1.useRef)(null);
|
|
50
|
-
// FlatList node handle, used for measuring children.
|
|
51
|
-
const nodeHandleRef = (0, react_1.useRef)(null);
|
|
52
|
-
// Container view measurements, for scrolling by percentage.
|
|
53
|
-
const containerMeasurementsRef = (0, react_1.useRef)(undefined);
|
|
54
|
-
// Content size, for scrolling by percentage.
|
|
55
|
-
const contentSizeRef = (0, react_1.useRef)(undefined);
|
|
56
|
-
// Scroll position, for Drax bounds checking and auto-scrolling.
|
|
57
|
-
const scrollPositionRef = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
46
|
+
const reorderable = reorderableProp ?? onItemReorder !== undefined;
|
|
58
47
|
// Original index of the currently dragged list item, if any.
|
|
59
|
-
const
|
|
48
|
+
const draggedItem = (0, react_native_reanimated_1.useSharedValue)(undefined);
|
|
60
49
|
// Auto-scrolling state.
|
|
61
50
|
const scrollStateRef = (0, react_1.useRef)(types_1.AutoScrollDirection.None);
|
|
62
|
-
// Auto-scrolling interval.
|
|
63
|
-
const scrollIntervalRef = (0, react_1.useRef)(undefined);
|
|
64
51
|
// List item measurements, for determining shift.
|
|
65
52
|
const itemMeasurementsRef = (0, react_1.useRef)([]);
|
|
53
|
+
const prevItemMeasurementsRef = (0, react_1.useRef)([]);
|
|
66
54
|
// Drax view registrations, for remeasuring after reorder.
|
|
67
55
|
const registrationsRef = (0, react_1.useRef)([]);
|
|
68
56
|
// Shift offsets.
|
|
69
|
-
const shiftsRef = (0,
|
|
57
|
+
const shiftsRef = (0, react_native_reanimated_1.useSharedValue)([]);
|
|
58
|
+
const previousShiftsRef = (0, react_native_reanimated_1.useSharedValue)([]);
|
|
70
59
|
// Maintain cache of reordered list indexes until data updates.
|
|
71
60
|
const [originalIndexes, setOriginalIndexes] = (0, react_1.useState)([]);
|
|
72
61
|
// Maintain the index the item is currently dragged to.
|
|
@@ -75,7 +64,7 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
75
64
|
(0, react_1.useEffect)(() => {
|
|
76
65
|
const itemMeasurements = itemMeasurementsRef.current;
|
|
77
66
|
const registrations = registrationsRef.current;
|
|
78
|
-
const shifts = shiftsRef.
|
|
67
|
+
const shifts = shiftsRef.value;
|
|
79
68
|
if (itemMeasurements.length > itemCount) {
|
|
80
69
|
itemMeasurements.splice(itemCount - itemMeasurements.length);
|
|
81
70
|
}
|
|
@@ -97,109 +86,21 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
97
86
|
}
|
|
98
87
|
else {
|
|
99
88
|
while (shifts.length < itemCount) {
|
|
100
|
-
shifts.push({
|
|
101
|
-
targetValue: 0,
|
|
102
|
-
animatedValue: new react_native_1.Animated.Value(0),
|
|
103
|
-
});
|
|
89
|
+
shifts.push({ x: 0, y: 0 });
|
|
104
90
|
}
|
|
105
91
|
}
|
|
92
|
+
shiftsRef.value = shifts;
|
|
106
93
|
}, [itemCount]);
|
|
107
94
|
// Clear reorders when data changes.
|
|
108
95
|
(0, react_1.useLayoutEffect)(() => {
|
|
109
96
|
// console.log('clear reorders');
|
|
110
97
|
setOriginalIndexes(data ? [...Array(data.length).keys()] : []);
|
|
111
98
|
}, [data]);
|
|
112
|
-
// Apply the reorder cache to the data.
|
|
113
|
-
const reorderedData = (0, react_1.useMemo)(() => {
|
|
114
|
-
// console.log('refresh sorted data');
|
|
115
|
-
if (!id || !data) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
if (data.length !== originalIndexes.length) {
|
|
119
|
-
return data;
|
|
120
|
-
}
|
|
121
|
-
return originalIndexes.map((index) => data[index]);
|
|
122
|
-
}, [id, data, originalIndexes]);
|
|
123
|
-
// Get shift transform for list item at index.
|
|
124
|
-
const getShiftTransform = (0, react_1.useCallback)((index) => {
|
|
125
|
-
const shift = shiftsRef.current[index]?.animatedValue ?? 0;
|
|
126
|
-
return horizontal
|
|
127
|
-
? [{ translateX: shift }]
|
|
128
|
-
: [{ translateY: shift }];
|
|
129
|
-
}, [horizontal]);
|
|
130
|
-
// Set the currently dragged list item.
|
|
131
|
-
const setDraggedItem = (0, react_1.useCallback)((originalIndex) => {
|
|
132
|
-
draggedItemRef.current = originalIndex;
|
|
133
|
-
}, []);
|
|
134
|
-
// Clear the currently dragged list item.
|
|
135
|
-
const resetDraggedItem = (0, react_1.useCallback)(() => {
|
|
136
|
-
draggedItemRef.current = undefined;
|
|
137
|
-
}, []);
|
|
138
|
-
// Drax view renderItem wrapper.
|
|
139
|
-
const renderItem = (0, react_1.useCallback)((info) => {
|
|
140
|
-
const { index, item } = info;
|
|
141
|
-
const originalIndex = originalIndexes[index];
|
|
142
|
-
const { style: itemStyle, draggingStyle = defaultStyles.draggingStyle, dragReleasedStyle = defaultStyles.dragReleasedStyle, ...otherStyleProps } = itemStyles ?? {};
|
|
143
|
-
return (react_1.default.createElement(DraxView_1.DraxView, { style: [itemStyle, { transform: getShiftTransform(originalIndex) }], draggingStyle: draggingStyle, dragReleasedStyle: dragReleasedStyle, ...otherStyleProps, longPressDelay: longPressDelay, lockDragXPosition: lockItemDragsToMainAxis && !horizontal, lockDragYPosition: lockItemDragsToMainAxis && horizontal, draggable: itemsDraggable, payload: { index, originalIndex }, ...(viewPropsExtractor?.(item) ?? {}), onDragEnd: resetDraggedItem, onDragDrop: resetDraggedItem, onMeasure: (measurements) => {
|
|
144
|
-
if (originalIndex !== undefined) {
|
|
145
|
-
// console.log(`measuring [${index}, ${originalIndex}]: (${measurements?.x}, ${measurements?.y})`);
|
|
146
|
-
itemMeasurementsRef.current[originalIndex] = measurements;
|
|
147
|
-
}
|
|
148
|
-
}, registration: (registration) => {
|
|
149
|
-
if (registration && originalIndex !== undefined) {
|
|
150
|
-
// console.log(`registering [${index}, ${originalIndex}], ${registration.id}`);
|
|
151
|
-
registrationsRef.current[originalIndex] = registration;
|
|
152
|
-
registration.measure();
|
|
153
|
-
}
|
|
154
|
-
}, renderContent: (contentProps) => renderItemContent(info, contentProps), renderHoverContent: renderItemHoverContent
|
|
155
|
-
&& ((hoverContentProps) => renderItemHoverContent(info, hoverContentProps)) }));
|
|
156
|
-
}, [
|
|
157
|
-
originalIndexes,
|
|
158
|
-
itemStyles,
|
|
159
|
-
viewPropsExtractor,
|
|
160
|
-
getShiftTransform,
|
|
161
|
-
resetDraggedItem,
|
|
162
|
-
itemsDraggable,
|
|
163
|
-
renderItemContent,
|
|
164
|
-
renderItemHoverContent,
|
|
165
|
-
longPressDelay,
|
|
166
|
-
lockItemDragsToMainAxis,
|
|
167
|
-
horizontal,
|
|
168
|
-
]);
|
|
169
|
-
// Track the size of the container view.
|
|
170
|
-
const onMeasureContainer = (0, react_1.useCallback)((measurements) => {
|
|
171
|
-
containerMeasurementsRef.current = measurements;
|
|
172
|
-
}, []);
|
|
173
|
-
// Track the size of the content.
|
|
174
|
-
const onContentSizeChange = (0, react_1.useCallback)((width, height) => {
|
|
175
|
-
contentSizeRef.current = { x: width, y: height };
|
|
176
|
-
}, []);
|
|
177
|
-
// Set FlatList and node handle refs.
|
|
178
|
-
const setFlatListRefs = (0, react_1.useCallback)((ref) => {
|
|
179
|
-
flatListRef.current = ref;
|
|
180
|
-
nodeHandleRef.current = ref && (0, react_native_1.findNodeHandle)(ref);
|
|
181
|
-
if (forwardedRef) {
|
|
182
|
-
if (typeof forwardedRef === 'function') {
|
|
183
|
-
forwardedRef(ref);
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
// eslint-disable-next-line no-param-reassign
|
|
187
|
-
forwardedRef.current = ref;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}, [forwardedRef]);
|
|
191
|
-
// Update tracked scroll position when list is scrolled.
|
|
192
|
-
const onScroll = (0, react_1.useCallback)((event) => {
|
|
193
|
-
const { nativeEvent: { contentOffset } } = event;
|
|
194
|
-
scrollPositionRef.current = { ...contentOffset };
|
|
195
|
-
onScrollProp?.(event);
|
|
196
|
-
}, [onScrollProp]);
|
|
197
99
|
// Handle auto-scrolling on interval.
|
|
198
100
|
const doScroll = (0, react_1.useCallback)(() => {
|
|
199
|
-
const flatList = flatListRef.current;
|
|
200
101
|
const containerMeasurements = containerMeasurementsRef.current;
|
|
201
102
|
const contentSize = contentSizeRef.current;
|
|
202
|
-
if (!
|
|
103
|
+
if (!flatListRef.current || !containerMeasurements || !contentSize) {
|
|
203
104
|
return;
|
|
204
105
|
}
|
|
205
106
|
let containerLength;
|
|
@@ -208,12 +109,12 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
208
109
|
if (horizontal) {
|
|
209
110
|
containerLength = containerMeasurements.width;
|
|
210
111
|
contentLength = contentSize.x;
|
|
211
|
-
prevOffset =
|
|
112
|
+
prevOffset = scrollPosition.value.x;
|
|
212
113
|
}
|
|
213
114
|
else {
|
|
214
115
|
containerLength = containerMeasurements.height;
|
|
215
116
|
contentLength = contentSize.y;
|
|
216
|
-
prevOffset =
|
|
117
|
+
prevOffset = scrollPosition.value.y;
|
|
217
118
|
}
|
|
218
119
|
const jumpLength = containerLength * 0.2;
|
|
219
120
|
let offset;
|
|
@@ -229,63 +130,126 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
229
130
|
}
|
|
230
131
|
}
|
|
231
132
|
if (offset !== undefined) {
|
|
232
|
-
|
|
233
|
-
|
|
133
|
+
flatListRef.current.scrollToOffset({ offset });
|
|
134
|
+
flatListRef.current.flashScrollIndicators();
|
|
234
135
|
}
|
|
235
136
|
}, [horizontal]);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
137
|
+
const { id, containerMeasurementsRef, contentSizeRef, onContentSizeChange, onMeasureContainer, onScroll, scrollRef: flatListRef, scrollPosition, setScrollRefs, startScroll, stopScroll, } = (0, useDraxScrollHandler_1.useDraxScrollHandler)({
|
|
138
|
+
idProp: parentDraxViewProps?.id,
|
|
139
|
+
onContentSizeChangeProp: props.onContentSizeChange,
|
|
140
|
+
onScrollProp,
|
|
141
|
+
forwardedRef,
|
|
142
|
+
doScroll,
|
|
143
|
+
});
|
|
144
|
+
// Apply the reorder cache to the data.
|
|
145
|
+
const reorderedData = (0, react_1.useMemo)(() => {
|
|
146
|
+
// console.log('refresh sorted data');
|
|
147
|
+
if (!id || !data) {
|
|
148
|
+
return null;
|
|
240
149
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}, [doScroll]);
|
|
244
|
-
// Stop the auto-scrolling interval.
|
|
245
|
-
const stopScroll = (0, react_1.useCallback)(() => {
|
|
246
|
-
if (scrollIntervalRef.current) {
|
|
247
|
-
clearInterval(scrollIntervalRef.current);
|
|
248
|
-
scrollIntervalRef.current = undefined;
|
|
150
|
+
if (data.length !== originalIndexes.length) {
|
|
151
|
+
return data;
|
|
249
152
|
}
|
|
153
|
+
return originalIndexes.map(index => data[index]).filter(Boolean);
|
|
154
|
+
}, [id, data, originalIndexes]);
|
|
155
|
+
// Set the currently dragged list item.
|
|
156
|
+
const setDraggedItem = (0, react_1.useCallback)((originalIndex) => {
|
|
157
|
+
draggedItem.value = originalIndex;
|
|
250
158
|
}, []);
|
|
251
|
-
//
|
|
252
|
-
(0, react_1.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
159
|
+
// Clear the currently dragged list item.
|
|
160
|
+
const resetDraggedItem = (0, react_1.useCallback)(() => {
|
|
161
|
+
draggedItem.value = undefined;
|
|
162
|
+
}, []);
|
|
163
|
+
// Drax view renderItem wrapper.
|
|
164
|
+
const renderItem = (0, react_1.useCallback)((info) => {
|
|
165
|
+
const { index, item } = info;
|
|
166
|
+
const originalIndex = originalIndexes[index];
|
|
167
|
+
const itemProps = {
|
|
168
|
+
index,
|
|
169
|
+
item,
|
|
170
|
+
originalIndex,
|
|
171
|
+
horizontal,
|
|
172
|
+
longPressDelay,
|
|
173
|
+
lockItemDragsToMainAxis,
|
|
174
|
+
draggedItem,
|
|
175
|
+
shiftsRef,
|
|
176
|
+
itemMeasurementsRef,
|
|
177
|
+
prevItemMeasurementsRef,
|
|
178
|
+
resetDraggedItem,
|
|
179
|
+
keyExtractor: props?.keyExtractor,
|
|
180
|
+
previousShiftsRef,
|
|
181
|
+
registrationsRef,
|
|
182
|
+
info,
|
|
183
|
+
data,
|
|
184
|
+
};
|
|
185
|
+
return props?.renderItem?.(info, itemProps);
|
|
186
|
+
}, [
|
|
187
|
+
originalIndexes,
|
|
188
|
+
resetDraggedItem,
|
|
189
|
+
longPressDelay,
|
|
190
|
+
lockItemDragsToMainAxis,
|
|
191
|
+
horizontal,
|
|
192
|
+
draggedItem,
|
|
193
|
+
shiftsRef,
|
|
194
|
+
itemMeasurementsRef,
|
|
195
|
+
prevItemMeasurementsRef,
|
|
196
|
+
previousShiftsRef,
|
|
197
|
+
registrationsRef,
|
|
198
|
+
props?.keyExtractor,
|
|
199
|
+
data,
|
|
200
|
+
]);
|
|
258
201
|
// Reset all shift values.
|
|
259
202
|
const resetShifts = (0, react_1.useCallback)(() => {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
shift.animatedValue.setValue(0);
|
|
264
|
-
});
|
|
203
|
+
previousShiftsRef.value = shiftsRef.value;
|
|
204
|
+
prevItemMeasurementsRef.current = [...itemMeasurementsRef.current];
|
|
205
|
+
shiftsRef.value = shiftsRef.value.map(() => ({ x: 0, y: 0 }));
|
|
265
206
|
}, []);
|
|
266
207
|
// Update shift values in response to a drag.
|
|
267
|
-
const updateShifts = (0, react_1.useCallback)(({ index: fromIndex, originalIndex: fromOriginalIndex }, { index: toIndex }) => {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if
|
|
274
|
-
|
|
208
|
+
const updateShifts = (0, react_1.useCallback)(({ index: fromIndex, originalIndex: fromOriginalIndex }, { index: toIndex }, dragged) => {
|
|
209
|
+
const isForward = fromIndex < toIndex;
|
|
210
|
+
shiftsRef.value = originalIndexes.map(index => {
|
|
211
|
+
// Don't shift the dragged item
|
|
212
|
+
if (index === fromIndex)
|
|
213
|
+
return { x: 0, y: 0 };
|
|
214
|
+
// Reset shift if item is no longer in the affected range
|
|
215
|
+
const shouldShift = isForward
|
|
216
|
+
? index > fromIndex && index <= toIndex
|
|
217
|
+
: index >= toIndex && index < fromIndex;
|
|
218
|
+
if (!shouldShift)
|
|
219
|
+
return { x: 0, y: 0 };
|
|
220
|
+
// Get measurements for current item and the item we're shifting to
|
|
221
|
+
const currentMeasurements = itemMeasurementsRef.current[index];
|
|
222
|
+
const draggedMeasurements = itemMeasurementsRef.current[fromOriginalIndex] ||
|
|
223
|
+
/** If no measurements, it must be an external dragged item */
|
|
224
|
+
dragged.data.hoverMeasurements;
|
|
225
|
+
const targetIndex = isForward ? index - 1 : index + 1;
|
|
226
|
+
const targetMeasurements = itemMeasurementsRef.current[targetIndex] ||
|
|
227
|
+
/** If no measurements, it must be last item. Fallback to list contentSize */
|
|
228
|
+
contentSizeRef.current;
|
|
229
|
+
if (!currentMeasurements || !draggedMeasurements || !targetMeasurements) {
|
|
230
|
+
return { x: 0, y: 0 };
|
|
231
|
+
}
|
|
232
|
+
// Calculate gaps between items in both directions
|
|
233
|
+
const xGap = isForward
|
|
234
|
+
? currentMeasurements.x - (targetMeasurements.x + targetMeasurements.width)
|
|
235
|
+
: targetMeasurements.x - (currentMeasurements.x + currentMeasurements.width);
|
|
236
|
+
const yGap = isForward
|
|
237
|
+
? currentMeasurements.y - (targetMeasurements.y + targetMeasurements.height)
|
|
238
|
+
: targetMeasurements.y - (currentMeasurements.y + currentMeasurements.height);
|
|
239
|
+
// Calculated new shifts
|
|
240
|
+
const x = isForward ? -(draggedMeasurements.width + xGap) : draggedMeasurements.width + xGap;
|
|
241
|
+
const y = isForward ? -(draggedMeasurements.height + yGap) : draggedMeasurements.height + yGap;
|
|
242
|
+
if ((props?.numColumns || 1) > 1) {
|
|
243
|
+
return { x, y };
|
|
275
244
|
}
|
|
276
|
-
|
|
277
|
-
|
|
245
|
+
if (horizontal) {
|
|
246
|
+
return { x, y: 0 };
|
|
278
247
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
react_native_1.Animated.timing(shift.animatedValue, {
|
|
282
|
-
duration: 200,
|
|
283
|
-
toValue: newTargetValue,
|
|
284
|
-
useNativeDriver: true,
|
|
285
|
-
}).start();
|
|
248
|
+
else {
|
|
249
|
+
return { x: 0, y };
|
|
286
250
|
}
|
|
287
251
|
});
|
|
288
|
-
}, [originalIndexes
|
|
252
|
+
}, [originalIndexes]);
|
|
289
253
|
// Calculate absolute position of list item for snapback.
|
|
290
254
|
const calculateSnapbackTarget = (0, react_1.useCallback)(({ index: fromIndex, originalIndex: fromOriginalIndex }, { index: toIndex, originalIndex: toOriginalIndex }) => {
|
|
291
255
|
const containerMeasurements = containerMeasurementsRef.current;
|
|
@@ -300,37 +264,51 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
300
264
|
// toIndex + 1 is in the list. We can measure the position of the next item.
|
|
301
265
|
const nextMeasurements = itemMeasurements[originalIndexes[nextIndex]];
|
|
302
266
|
if (nextMeasurements) {
|
|
303
|
-
nextPos = {
|
|
267
|
+
nextPos = {
|
|
268
|
+
x: nextMeasurements.x,
|
|
269
|
+
y: nextMeasurements.y,
|
|
270
|
+
};
|
|
304
271
|
}
|
|
305
272
|
}
|
|
306
273
|
else {
|
|
307
274
|
// toIndex is the last item of the list. We can use the list content size.
|
|
308
275
|
const contentSize = contentSizeRef.current;
|
|
309
276
|
if (contentSize) {
|
|
310
|
-
nextPos = horizontal
|
|
311
|
-
? { x: contentSize.x, y: 0 }
|
|
312
|
-
: { x: 0, y: contentSize.y };
|
|
277
|
+
nextPos = horizontal ? { x: contentSize.x, y: 0 } : { x: 0, y: contentSize.y };
|
|
313
278
|
}
|
|
314
279
|
}
|
|
315
280
|
const fromMeasurements = itemMeasurements[fromOriginalIndex];
|
|
316
281
|
if (nextPos && fromMeasurements) {
|
|
282
|
+
const flattenedStyles = react_native_1.StyleSheet.flatten(flatListProps.contentContainerStyle) || {};
|
|
283
|
+
//@ts-ignore
|
|
284
|
+
const rowGap = flattenedStyles.rowGap ?? flattenedStyles.gap ?? 0;
|
|
285
|
+
//@ts-ignore
|
|
286
|
+
const columnGap = flattenedStyles.columnGap ?? flattenedStyles.gap ?? 0;
|
|
317
287
|
targetPos = horizontal
|
|
318
|
-
? {
|
|
319
|
-
|
|
288
|
+
? {
|
|
289
|
+
x: nextPos.x - fromMeasurements.width - rowGap,
|
|
290
|
+
y: nextPos.y,
|
|
291
|
+
}
|
|
292
|
+
: {
|
|
293
|
+
x: nextPos.x,
|
|
294
|
+
y: nextPos.y - fromMeasurements.height - columnGap,
|
|
295
|
+
};
|
|
320
296
|
}
|
|
321
297
|
}
|
|
322
298
|
else {
|
|
323
299
|
// Target pos(toIndex)
|
|
324
300
|
const toMeasurements = itemMeasurements[toOriginalIndex];
|
|
325
301
|
if (toMeasurements) {
|
|
326
|
-
targetPos = {
|
|
302
|
+
targetPos = {
|
|
303
|
+
x: toMeasurements.x,
|
|
304
|
+
y: toMeasurements.y,
|
|
305
|
+
};
|
|
327
306
|
}
|
|
328
307
|
}
|
|
329
308
|
if (targetPos) {
|
|
330
|
-
const scrollPosition = scrollPositionRef.current;
|
|
331
309
|
return {
|
|
332
|
-
x: containerMeasurements.x - scrollPosition.x + targetPos.x,
|
|
333
|
-
y: containerMeasurements.y - scrollPosition.y + targetPos.y,
|
|
310
|
+
x: containerMeasurements.x - scrollPosition.value.x + targetPos.x,
|
|
311
|
+
y: containerMeasurements.y - scrollPosition.value.y + targetPos.y,
|
|
334
312
|
};
|
|
335
313
|
}
|
|
336
314
|
}
|
|
@@ -342,16 +320,24 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
342
320
|
scrollStateRef.current = types_1.AutoScrollDirection.None;
|
|
343
321
|
stopScroll();
|
|
344
322
|
const { dragged, receiver } = eventData;
|
|
345
|
-
|
|
346
|
-
if
|
|
347
|
-
|
|
348
|
-
const fromPayload =
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
323
|
+
const isExternalDrag = dragged.parentId !== id || typeof dragged.payload.originalIndex !== 'number';
|
|
324
|
+
// First, check if we need to shift items.
|
|
325
|
+
if (reorderable) {
|
|
326
|
+
const fromPayload = {
|
|
327
|
+
/**
|
|
328
|
+
* Indexing should start from zero and stop at `itemCount - 1`, but
|
|
329
|
+
* we're also handling for external drag by adding a fake item index,
|
|
330
|
+
* resulting to `itemCount`.
|
|
331
|
+
*/
|
|
332
|
+
index: !isExternalDrag ? dragged.payload.index : itemCount,
|
|
333
|
+
originalIndex: !isExternalDrag ? dragged.payload.originalIndex : itemCount,
|
|
334
|
+
};
|
|
335
|
+
const toPayload = receiver?.parentId === id ? receiver.payload : undefined;
|
|
352
336
|
const { index: fromIndex, originalIndex: fromOriginalIndex } = fromPayload;
|
|
353
337
|
const { index: toIndex, originalIndex: toOriginalIndex } = toPayload ?? {};
|
|
354
|
-
const toItem =
|
|
338
|
+
const toItem = toOriginalIndex !== undefined ? data?.[toOriginalIndex] : undefined;
|
|
339
|
+
const fromItem = data?.[fromOriginalIndex] || dragged.payload;
|
|
340
|
+
throttledSetIsExternalDrag(false);
|
|
355
341
|
// Reset all shifts and call callback, regardless of whether toPayload exists.
|
|
356
342
|
resetShifts();
|
|
357
343
|
if (totalDragEnd) {
|
|
@@ -361,7 +347,8 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
361
347
|
toItem,
|
|
362
348
|
cancelled: (0, types_1.isWithCancelledFlag)(eventData) ? eventData.cancelled : false,
|
|
363
349
|
index: fromIndex,
|
|
364
|
-
item:
|
|
350
|
+
item: fromItem,
|
|
351
|
+
isExternalDrag,
|
|
365
352
|
});
|
|
366
353
|
}
|
|
367
354
|
// Reset currently dragged over position index to undefined.
|
|
@@ -370,9 +357,10 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
370
357
|
onItemDragPositionChange?.({
|
|
371
358
|
...eventData,
|
|
372
359
|
index: fromIndex,
|
|
373
|
-
item:
|
|
360
|
+
item: fromItem,
|
|
374
361
|
toIndex: undefined,
|
|
375
362
|
previousIndex: draggedToIndex.current,
|
|
363
|
+
isExternalDrag,
|
|
376
364
|
});
|
|
377
365
|
}
|
|
378
366
|
draggedToIndex.current = undefined;
|
|
@@ -387,9 +375,10 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
387
375
|
setOriginalIndexes(newOriginalIndexes);
|
|
388
376
|
onItemReorder?.({
|
|
389
377
|
fromIndex,
|
|
390
|
-
fromItem
|
|
378
|
+
fromItem,
|
|
391
379
|
toIndex: toIndex,
|
|
392
|
-
toItem
|
|
380
|
+
toItem,
|
|
381
|
+
isExternalDrag,
|
|
393
382
|
});
|
|
394
383
|
}
|
|
395
384
|
return snapbackTarget;
|
|
@@ -410,6 +399,7 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
410
399
|
]);
|
|
411
400
|
// Monitor drag starts to handle callbacks.
|
|
412
401
|
const onMonitorDragStart = (0, react_1.useCallback)((eventData) => {
|
|
402
|
+
parentDraxViewProps?.onMonitorDragStart?.(eventData);
|
|
413
403
|
const { dragged } = eventData;
|
|
414
404
|
// First, check if we need to do anything.
|
|
415
405
|
if (reorderable && dragged.parentId === id) {
|
|
@@ -420,26 +410,34 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
420
410
|
...eventData,
|
|
421
411
|
index,
|
|
422
412
|
item: data?.[originalIndex],
|
|
413
|
+
isExternalDrag: false,
|
|
423
414
|
});
|
|
424
415
|
}
|
|
425
|
-
}, [
|
|
426
|
-
id,
|
|
427
|
-
reorderable,
|
|
428
|
-
data,
|
|
429
|
-
setDraggedItem,
|
|
430
|
-
onItemDragStart,
|
|
431
|
-
]);
|
|
416
|
+
}, [id, reorderable, data, setDraggedItem, onItemDragStart]);
|
|
432
417
|
// Monitor drags to react with item shifts and auto-scrolling.
|
|
433
418
|
const onMonitorDragOver = (0, react_1.useCallback)((eventData) => {
|
|
419
|
+
parentDraxViewProps?.onMonitorDragOver?.(eventData);
|
|
434
420
|
const { dragged, receiver, monitorOffsetRatio } = eventData;
|
|
421
|
+
const isExternalDrag = dragged.parentId !== id || typeof dragged.payload.originalIndex !== 'number';
|
|
435
422
|
// First, check if we need to shift items.
|
|
436
|
-
if (reorderable
|
|
437
|
-
|
|
438
|
-
|
|
423
|
+
if (reorderable) {
|
|
424
|
+
const fromPayload = {
|
|
425
|
+
/**
|
|
426
|
+
* Indexing should start from zero and stop at `itemCount - 1`, but
|
|
427
|
+
* we're also handling for external drag by adding a fake item index,
|
|
428
|
+
* resulting to `itemCount`.
|
|
429
|
+
*/
|
|
430
|
+
index: !isExternalDrag ? dragged.payload.index : itemCount,
|
|
431
|
+
originalIndex: !isExternalDrag ? dragged.payload.originalIndex : itemCount,
|
|
432
|
+
};
|
|
433
|
+
if (typeof draggedItem.value !== 'number') {
|
|
434
|
+
/** DraxList is receiving external drag */
|
|
435
|
+
setDraggedItem(itemCount);
|
|
436
|
+
}
|
|
437
|
+
monitoringExternalDragStyle && draggedItem.value === itemCount && throttledSetIsExternalDrag(true);
|
|
438
|
+
const fromItem = data?.[fromPayload.originalIndex] || dragged.payload;
|
|
439
439
|
// Find its current position index in the list, if any.
|
|
440
|
-
const toPayload = receiver?.parentId === id
|
|
441
|
-
? receiver.payload
|
|
442
|
-
: undefined;
|
|
440
|
+
const toPayload = receiver?.parentId === id ? receiver.payload : undefined;
|
|
443
441
|
// Check and update currently dragged over position index.
|
|
444
442
|
const toIndex = toPayload?.index;
|
|
445
443
|
if (toIndex !== draggedToIndex.current) {
|
|
@@ -447,13 +445,14 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
447
445
|
...eventData,
|
|
448
446
|
toIndex,
|
|
449
447
|
index: fromPayload.index,
|
|
450
|
-
item:
|
|
448
|
+
item: fromItem,
|
|
451
449
|
previousIndex: draggedToIndex.current,
|
|
450
|
+
isExternalDrag,
|
|
452
451
|
});
|
|
453
452
|
draggedToIndex.current = toIndex;
|
|
454
453
|
}
|
|
455
454
|
// Update shift transforms for items in the list.
|
|
456
|
-
updateShifts(fromPayload, toPayload ?? fromPayload);
|
|
455
|
+
updateShifts(fromPayload, toPayload ?? fromPayload, dragged);
|
|
457
456
|
}
|
|
458
457
|
// Next, see if we need to auto-scroll.
|
|
459
458
|
const ratio = horizontal ? monitorOffsetRatio.x : monitorOffsetRatio.y;
|
|
@@ -470,28 +469,44 @@ const DraxListUnforwarded = (props, forwardedRef) => {
|
|
|
470
469
|
}
|
|
471
470
|
startScroll();
|
|
472
471
|
}
|
|
473
|
-
}, [
|
|
474
|
-
id,
|
|
475
|
-
reorderable,
|
|
476
|
-
data,
|
|
477
|
-
updateShifts,
|
|
478
|
-
horizontal,
|
|
479
|
-
stopScroll,
|
|
480
|
-
startScroll,
|
|
481
|
-
onItemDragPositionChange,
|
|
482
|
-
]);
|
|
472
|
+
}, [id, reorderable, data, updateShifts, horizontal, stopScroll, startScroll, onItemDragPositionChange]);
|
|
483
473
|
// Monitor drag exits to stop scrolling, update shifts, and update draggedToIndex.
|
|
484
|
-
const onMonitorDragExit = (0, react_1.useCallback)((eventData) =>
|
|
474
|
+
const onMonitorDragExit = (0, react_1.useCallback)((eventData) => {
|
|
475
|
+
handleInternalDragEnd(eventData, false);
|
|
476
|
+
parentDraxViewProps?.onMonitorDragExit?.(eventData);
|
|
477
|
+
}, [handleInternalDragEnd]);
|
|
485
478
|
/*
|
|
486
479
|
* Monitor drag ends to stop scrolling, update shifts, and possibly reorder.
|
|
487
480
|
* This addresses the Android case where if we drag a list item and auto-scroll
|
|
488
481
|
* too far, the drag gets cancelled.
|
|
489
482
|
*/
|
|
490
|
-
const onMonitorDragEnd = (0, react_1.useCallback)((eventData) =>
|
|
483
|
+
const onMonitorDragEnd = (0, react_1.useCallback)((eventData) => {
|
|
484
|
+
const defaultSnapbackTarget = handleInternalDragEnd(eventData, true);
|
|
485
|
+
const providedSnapTarget = parentDraxViewProps?.onMonitorDragEnd?.(eventData);
|
|
486
|
+
resetDraggedItem();
|
|
487
|
+
return providedSnapTarget ?? defaultSnapbackTarget;
|
|
488
|
+
}, [handleInternalDragEnd]);
|
|
491
489
|
// Monitor drag drops to stop scrolling, update shifts, and possibly reorder.
|
|
492
|
-
const onMonitorDragDrop = (0, react_1.useCallback)((eventData) =>
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
490
|
+
const onMonitorDragDrop = (0, react_1.useCallback)((eventData) => {
|
|
491
|
+
const defaultSnapbackTarget = handleInternalDragEnd(eventData, true);
|
|
492
|
+
const providedSnapTarget = parentDraxViewProps?.onMonitorDragDrop?.(eventData);
|
|
493
|
+
draggedItem.value === itemCount && resetDraggedItem();
|
|
494
|
+
return providedSnapTarget ?? defaultSnapbackTarget;
|
|
495
|
+
}, [handleInternalDragEnd]);
|
|
496
|
+
const [isExternalDrag, setIsExternalDrag] = (0, react_1.useState)(false);
|
|
497
|
+
const throttledSetIsExternalDrag = (0, react_1.useMemo)(() => (0, lodash_throttle_1.default)((position) => {
|
|
498
|
+
setIsExternalDrag(position);
|
|
499
|
+
}, 300), []);
|
|
500
|
+
return (react_1.default.createElement(DraxView_1.DraxView, { ...parentDraxViewProps, style: [parentDraxViewProps?.style, isExternalDrag && monitoringExternalDragStyle], id: id, scrollPosition: scrollPosition, onMeasure: event => {
|
|
501
|
+
parentDraxViewProps?.onMeasure?.(event);
|
|
502
|
+
return onMeasureContainer(event);
|
|
503
|
+
}, onMonitorDragStart: onMonitorDragStart, onMonitorDragOver: onMonitorDragOver, onMonitorDragExit: onMonitorDragExit, onMonitorDragEnd: onMonitorDragEnd, onMonitorDragDrop: onMonitorDragDrop },
|
|
504
|
+
react_1.default.createElement(DraxSubprovider_1.DraxSubprovider, { parent: {
|
|
505
|
+
id,
|
|
506
|
+
viewRef: {
|
|
507
|
+
current: flatListRef?.current?.getNativeScrollRef?.(),
|
|
508
|
+
},
|
|
509
|
+
} },
|
|
510
|
+
react_1.default.createElement(react_native_reanimated_1.default.FlatList, { ...flatListProps, style: style, ref: setScrollRefs, renderItem: renderItem, onScroll: onScroll, onContentSizeChange: onContentSizeChange, data: reorderedData }))));
|
|
496
511
|
};
|
|
497
512
|
exports.DraxList = (0, react_1.forwardRef)(DraxListUnforwarded);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DraxListItemProps, DraxViewProps } from './types';
|
|
3
|
+
declare const RenderItemComponent: <T extends unknown>({ itemProps: { index, item, originalIndex, horizontal, lockItemDragsToMainAxis, draggedItem, shiftsRef, itemMeasurementsRef, prevItemMeasurementsRef, resetDraggedItem, keyExtractor, previousShiftsRef, registrationsRef, data, }, ...draxViewProps }: {
|
|
4
|
+
itemProps: DraxListItemProps<T>;
|
|
5
|
+
} & DraxViewProps) => React.JSX.Element;
|
|
6
|
+
export declare const DraxListItem: typeof RenderItemComponent;
|
|
7
|
+
export {};
|