@souscheflabs/reanimated-flashlist 0.1.7
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 +282 -0
- package/lib/AnimatedFlashList.d.ts +6 -0
- package/lib/AnimatedFlashList.d.ts.map +1 -0
- package/lib/AnimatedFlashList.js +207 -0
- package/lib/AnimatedFlashListItem.d.ts +33 -0
- package/lib/AnimatedFlashListItem.d.ts.map +1 -0
- package/lib/AnimatedFlashListItem.js +155 -0
- package/lib/__tests__/utils/test-utils.d.ts +82 -0
- package/lib/__tests__/utils/test-utils.d.ts.map +1 -0
- package/lib/__tests__/utils/test-utils.js +115 -0
- package/lib/constants/animations.d.ts +39 -0
- package/lib/constants/animations.d.ts.map +1 -0
- package/lib/constants/animations.js +100 -0
- package/lib/constants/drag.d.ts +11 -0
- package/lib/constants/drag.d.ts.map +1 -0
- package/lib/constants/drag.js +47 -0
- package/lib/constants/index.d.ts +3 -0
- package/lib/constants/index.d.ts.map +1 -0
- package/lib/constants/index.js +18 -0
- package/lib/contexts/DragStateContext.d.ts +73 -0
- package/lib/contexts/DragStateContext.d.ts.map +1 -0
- package/lib/contexts/DragStateContext.js +148 -0
- package/lib/contexts/ListAnimationContext.d.ts +104 -0
- package/lib/contexts/ListAnimationContext.d.ts.map +1 -0
- package/lib/contexts/ListAnimationContext.js +184 -0
- package/lib/contexts/index.d.ts +5 -0
- package/lib/contexts/index.d.ts.map +1 -0
- package/lib/contexts/index.js +10 -0
- package/lib/hooks/animations/index.d.ts +9 -0
- package/lib/hooks/animations/index.d.ts.map +1 -0
- package/lib/hooks/animations/index.js +13 -0
- package/lib/hooks/animations/useListEntryAnimation.d.ts +38 -0
- package/lib/hooks/animations/useListEntryAnimation.d.ts.map +1 -0
- package/lib/hooks/animations/useListEntryAnimation.js +90 -0
- package/lib/hooks/animations/useListExitAnimation.d.ts +67 -0
- package/lib/hooks/animations/useListExitAnimation.d.ts.map +1 -0
- package/lib/hooks/animations/useListExitAnimation.js +146 -0
- package/lib/hooks/drag/index.d.ts +20 -0
- package/lib/hooks/drag/index.d.ts.map +1 -0
- package/lib/hooks/drag/index.js +26 -0
- package/lib/hooks/drag/useDragAnimatedStyle.d.ts +33 -0
- package/lib/hooks/drag/useDragAnimatedStyle.d.ts.map +1 -0
- package/lib/hooks/drag/useDragAnimatedStyle.js +61 -0
- package/lib/hooks/drag/useDragGesture.d.ts +30 -0
- package/lib/hooks/drag/useDragGesture.d.ts.map +1 -0
- package/lib/hooks/drag/useDragGesture.js +189 -0
- package/lib/hooks/drag/useDragShift.d.ts +21 -0
- package/lib/hooks/drag/useDragShift.d.ts.map +1 -0
- package/lib/hooks/drag/useDragShift.js +85 -0
- package/lib/hooks/drag/useDropCompensation.d.ts +27 -0
- package/lib/hooks/drag/useDropCompensation.d.ts.map +1 -0
- package/lib/hooks/drag/useDropCompensation.js +90 -0
- package/lib/hooks/index.d.ts +8 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/index.js +18 -0
- package/lib/index.d.ts +42 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +69 -0
- package/lib/types/animations.d.ts +71 -0
- package/lib/types/animations.d.ts.map +1 -0
- package/lib/types/animations.js +2 -0
- package/lib/types/drag.d.ts +94 -0
- package/lib/types/drag.d.ts.map +1 -0
- package/lib/types/drag.js +2 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +19 -0
- package/lib/types/list.d.ts +136 -0
- package/lib/types/list.d.ts.map +1 -0
- package/lib/types/list.js +2 -0
- package/package.json +73 -0
- package/src/AnimatedFlashList.tsx +411 -0
- package/src/AnimatedFlashListItem.tsx +212 -0
- package/src/__tests__/components/AnimatedFlashList.test.tsx +365 -0
- package/src/__tests__/components/AnimatedFlashListItem.test.tsx +371 -0
- package/src/__tests__/contexts/DragStateContext.test.tsx +169 -0
- package/src/__tests__/contexts/ListAnimationContext.test.tsx +324 -0
- package/src/__tests__/hooks/useDragAnimatedStyle.test.tsx +118 -0
- package/src/__tests__/hooks/useDragGesture.test.tsx +169 -0
- package/src/__tests__/hooks/useDragShift.test.tsx +94 -0
- package/src/__tests__/hooks/useDropCompensation.test.tsx +182 -0
- package/src/__tests__/hooks/useListEntryAnimation.test.tsx +135 -0
- package/src/__tests__/hooks/useListExitAnimation.test.tsx +175 -0
- package/src/__tests__/utils/test-utils.tsx +159 -0
- package/src/constants/animations.ts +107 -0
- package/src/constants/drag.ts +51 -0
- package/src/constants/index.ts +2 -0
- package/src/contexts/DragStateContext.tsx +197 -0
- package/src/contexts/ListAnimationContext.tsx +302 -0
- package/src/contexts/index.ts +9 -0
- package/src/hooks/animations/index.ts +9 -0
- package/src/hooks/animations/useListEntryAnimation.ts +108 -0
- package/src/hooks/animations/useListExitAnimation.ts +197 -0
- package/src/hooks/drag/index.ts +20 -0
- package/src/hooks/drag/useDragAnimatedStyle.ts +80 -0
- package/src/hooks/drag/useDragGesture.ts +267 -0
- package/src/hooks/drag/useDragShift.ts +119 -0
- package/src/hooks/drag/useDropCompensation.ts +120 -0
- package/src/hooks/index.ts +16 -0
- package/src/index.ts +105 -0
- package/src/types/animations.ts +76 -0
- package/src/types/drag.ts +101 -0
- package/src/types/index.ts +3 -0
- package/src/types/list.ts +178 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useSharedValue,
|
|
3
|
+
useDerivedValue,
|
|
4
|
+
useAnimatedReaction,
|
|
5
|
+
withTiming,
|
|
6
|
+
Easing,
|
|
7
|
+
} from 'react-native-reanimated';
|
|
8
|
+
import { useDragState } from '../../contexts/DragStateContext';
|
|
9
|
+
import type { UseDragShiftConfig, UseDragShiftResult } from '../../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Hook that calculates shift animation for non-dragged items.
|
|
13
|
+
*
|
|
14
|
+
* When an item is being dragged, other items need to shift up or down
|
|
15
|
+
* to make room for the dragged item at its new position. This hook
|
|
16
|
+
* calculates the target shift based on:
|
|
17
|
+
* - The current drag position (from context)
|
|
18
|
+
* - The scroll delta (accounting for autoscroll)
|
|
19
|
+
* - The item's index relative to the dragged item
|
|
20
|
+
*
|
|
21
|
+
* The shift is animated with timing for smooth transitions.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* const { shiftY } = useDragShift({ itemId: item.id, index });
|
|
26
|
+
* // Use shiftY.value in animated style
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function useDragShift(config: UseDragShiftConfig): UseDragShiftResult {
|
|
30
|
+
const { itemId, index } = config;
|
|
31
|
+
|
|
32
|
+
// Global drag state for coordinating animations across all items
|
|
33
|
+
const {
|
|
34
|
+
isDragging: globalIsDragging,
|
|
35
|
+
draggedIndex,
|
|
36
|
+
draggedItemId,
|
|
37
|
+
currentTranslateY,
|
|
38
|
+
scrollOffset,
|
|
39
|
+
dragStartScrollOffset,
|
|
40
|
+
dragUpdateTrigger,
|
|
41
|
+
measuredItemHeight,
|
|
42
|
+
isDropping,
|
|
43
|
+
config: dragConfig,
|
|
44
|
+
} = useDragState();
|
|
45
|
+
|
|
46
|
+
// Shift animation SharedValue
|
|
47
|
+
const shiftY = useSharedValue(0);
|
|
48
|
+
|
|
49
|
+
// Calculate target shift using useDerivedValue
|
|
50
|
+
const targetShiftY = useDerivedValue(() => {
|
|
51
|
+
'worklet';
|
|
52
|
+
// Force re-evaluation on every drag state change
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
54
|
+
dragUpdateTrigger.value;
|
|
55
|
+
|
|
56
|
+
// During drop transition, freeze shift values
|
|
57
|
+
if (isDropping.value) {
|
|
58
|
+
return shiftY.value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const currentDraggedIndex = draggedIndex.value;
|
|
62
|
+
const isDraggingNow = globalIsDragging.value;
|
|
63
|
+
const translateYNow = currentTranslateY.value;
|
|
64
|
+
const currentDraggedItemId = draggedItemId.value;
|
|
65
|
+
|
|
66
|
+
// If I'm the dragged item, no shift needed
|
|
67
|
+
if (currentDraggedItemId === itemId) return 0;
|
|
68
|
+
|
|
69
|
+
// If not dragging, reset to 0
|
|
70
|
+
if (!isDraggingNow) return 0;
|
|
71
|
+
|
|
72
|
+
// Use dynamically measured height + margins, or fall back to config
|
|
73
|
+
const itemHeight =
|
|
74
|
+
measuredItemHeight.value > 0
|
|
75
|
+
? measuredItemHeight.value + dragConfig.itemVerticalMargin
|
|
76
|
+
: dragConfig.itemHeight;
|
|
77
|
+
|
|
78
|
+
// Calculate effective translateY including scroll delta
|
|
79
|
+
const scrollDelta = scrollOffset.value - dragStartScrollOffset.value;
|
|
80
|
+
const effectiveTranslateY = translateYNow + scrollDelta;
|
|
81
|
+
|
|
82
|
+
// Calculate which index the dragged item is hovering over
|
|
83
|
+
const offset =
|
|
84
|
+
effectiveTranslateY > 0 ? 0.2 : effectiveTranslateY < 0 ? -0.2 : 0;
|
|
85
|
+
const hoveredIndex =
|
|
86
|
+
currentDraggedIndex + Math.round(effectiveTranslateY / itemHeight + offset);
|
|
87
|
+
|
|
88
|
+
// Moving DOWN: items between original and hovered positions shift UP
|
|
89
|
+
if (hoveredIndex > currentDraggedIndex) {
|
|
90
|
+
if (index > currentDraggedIndex && index <= hoveredIndex) {
|
|
91
|
+
return -itemHeight;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Moving UP: items between hovered and original positions shift DOWN
|
|
95
|
+
else if (hoveredIndex < currentDraggedIndex) {
|
|
96
|
+
if (index < currentDraggedIndex && index >= hoveredIndex) {
|
|
97
|
+
return itemHeight;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return 0;
|
|
102
|
+
}, [index, itemId]);
|
|
103
|
+
|
|
104
|
+
// Animate shift when target changes
|
|
105
|
+
useAnimatedReaction(
|
|
106
|
+
() => targetShiftY.value,
|
|
107
|
+
(target, prev) => {
|
|
108
|
+
'worklet';
|
|
109
|
+
if (target !== prev) {
|
|
110
|
+
shiftY.value = withTiming(target, {
|
|
111
|
+
duration: 100,
|
|
112
|
+
easing: Easing.out(Easing.ease),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return { shiftY };
|
|
119
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { useLayoutEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
useSharedValue,
|
|
4
|
+
useAnimatedReaction,
|
|
5
|
+
withTiming,
|
|
6
|
+
Easing,
|
|
7
|
+
} from 'react-native-reanimated';
|
|
8
|
+
import { useRecyclingState } from '@shopify/flash-list';
|
|
9
|
+
import { useDragState } from '../../contexts/DragStateContext';
|
|
10
|
+
import type { UseDropCompensationConfig } from '../../types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Hook that handles index change compensation after drag reorder.
|
|
14
|
+
*
|
|
15
|
+
* When an item is dropped and the data updates, React re-renders with new indices.
|
|
16
|
+
* This causes a visual jump because items suddenly have different positions.
|
|
17
|
+
* This hook compensates by:
|
|
18
|
+
* 1. Detecting index changes (via useRecyclingState for FlashList recycling)
|
|
19
|
+
* 2. Adjusting translateY to compensate for the position delta
|
|
20
|
+
* 3. Animating the dragged item back to 0 (settle animation)
|
|
21
|
+
* 4. Resetting global drag state after animation completes
|
|
22
|
+
* 5. Resetting shiftY on UI thread via useAnimatedReaction
|
|
23
|
+
*
|
|
24
|
+
* useLayoutEffect runs synchronously before paint to prevent visual flash.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* useDropCompensation({
|
|
29
|
+
* itemId: item.id,
|
|
30
|
+
* index,
|
|
31
|
+
* translateY,
|
|
32
|
+
* shiftY,
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function useDropCompensation(config: UseDropCompensationConfig): void {
|
|
37
|
+
const { itemId, index, translateY, shiftY } = config;
|
|
38
|
+
|
|
39
|
+
// Track index on UI thread for synchronized shift reset
|
|
40
|
+
const indexShared = useSharedValue(index);
|
|
41
|
+
|
|
42
|
+
// Global drag state
|
|
43
|
+
const {
|
|
44
|
+
isDragging: globalIsDragging,
|
|
45
|
+
draggedIndex,
|
|
46
|
+
draggedItemId,
|
|
47
|
+
measuredItemHeight,
|
|
48
|
+
isDropping,
|
|
49
|
+
dragUpdateTrigger,
|
|
50
|
+
config: dragConfig,
|
|
51
|
+
} = useDragState();
|
|
52
|
+
|
|
53
|
+
// Use FlashList's useRecyclingState for automatic reset on view recycling
|
|
54
|
+
const [prevIndex, setPrevIndex] = useRecyclingState(index, [itemId]);
|
|
55
|
+
|
|
56
|
+
// Handle index changes after data updates (drop compensation)
|
|
57
|
+
useLayoutEffect(() => {
|
|
58
|
+
if (index !== prevIndex) {
|
|
59
|
+
// Compensate for index change by adjusting translateY
|
|
60
|
+
const indexDelta = index - prevIndex;
|
|
61
|
+
const heightDelta = indexDelta * dragConfig.itemHeight;
|
|
62
|
+
|
|
63
|
+
// Check if this is the dragged item completing its drop
|
|
64
|
+
const isTheDraggedItem = draggedItemId.value === itemId;
|
|
65
|
+
|
|
66
|
+
// Compensate translateY for position change
|
|
67
|
+
const compensatedY = translateY.value - heightDelta;
|
|
68
|
+
|
|
69
|
+
if (isTheDraggedItem) {
|
|
70
|
+
// Dragged item: animate smoothly to 0 from compensated position
|
|
71
|
+
translateY.value = compensatedY;
|
|
72
|
+
translateY.value = withTiming(
|
|
73
|
+
0,
|
|
74
|
+
{ duration: 150, easing: Easing.out(Easing.ease) },
|
|
75
|
+
finished => {
|
|
76
|
+
'worklet';
|
|
77
|
+
if (finished) {
|
|
78
|
+
// Reset ALL global state after settle animation
|
|
79
|
+
isDropping.value = false;
|
|
80
|
+
globalIsDragging.value = false;
|
|
81
|
+
draggedIndex.value = -1;
|
|
82
|
+
draggedItemId.value = '';
|
|
83
|
+
measuredItemHeight.value = 0;
|
|
84
|
+
dragUpdateTrigger.value = withTiming(dragUpdateTrigger.value + 1, {
|
|
85
|
+
duration: 1,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
} else if (Math.abs(translateY.value) > 1) {
|
|
91
|
+
// Non-dragged item: just compensate without animation
|
|
92
|
+
translateY.value = compensatedY;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Update tracked index
|
|
96
|
+
setPrevIndex(index);
|
|
97
|
+
}
|
|
98
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
99
|
+
}, [index, prevIndex, translateY, setPrevIndex, itemId]);
|
|
100
|
+
|
|
101
|
+
// Sync JS index to SharedValue for UI thread access
|
|
102
|
+
useLayoutEffect(() => {
|
|
103
|
+
indexShared.value = index;
|
|
104
|
+
}, [index, indexShared]);
|
|
105
|
+
|
|
106
|
+
// Reset shiftY on UI thread when index changes
|
|
107
|
+
useAnimatedReaction(
|
|
108
|
+
() => indexShared.value,
|
|
109
|
+
(currentIndex, prevIdx) => {
|
|
110
|
+
'worklet';
|
|
111
|
+
if (
|
|
112
|
+
prevIdx !== null &&
|
|
113
|
+
currentIndex !== prevIdx &&
|
|
114
|
+
Math.abs(shiftY.value) > 1
|
|
115
|
+
) {
|
|
116
|
+
shiftY.value = 0;
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks for AnimatedFlashList
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all hooks for both drag and animation functionality.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Drag hooks
|
|
8
|
+
export {
|
|
9
|
+
useDragGesture,
|
|
10
|
+
useDragShift,
|
|
11
|
+
useDragAnimatedStyle,
|
|
12
|
+
useDropCompensation,
|
|
13
|
+
} from './drag';
|
|
14
|
+
|
|
15
|
+
// Animation hooks
|
|
16
|
+
export { useListExitAnimation, useListEntryAnimation } from './animations';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @souschef/reanimated-flashlist
|
|
3
|
+
*
|
|
4
|
+
* A high-performance animated FlashList with drag-to-reorder and entry/exit animations.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import {
|
|
9
|
+
* AnimatedFlashList,
|
|
10
|
+
* type AnimatedListItem,
|
|
11
|
+
* } from '@souschef/reanimated-flashlist';
|
|
12
|
+
*
|
|
13
|
+
* interface MyItem extends AnimatedListItem {
|
|
14
|
+
* title: string;
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* <AnimatedFlashList<MyItem>
|
|
18
|
+
* data={items}
|
|
19
|
+
* keyExtractor={(item) => item.id}
|
|
20
|
+
* renderItem={({ item, dragHandleProps, triggerExitAnimation }) => (
|
|
21
|
+
* <View>
|
|
22
|
+
* <Text>{item.title}</Text>
|
|
23
|
+
* {dragHandleProps && (
|
|
24
|
+
* <GestureDetector gesture={dragHandleProps.gesture}>
|
|
25
|
+
* <DragIcon />
|
|
26
|
+
* </GestureDetector>
|
|
27
|
+
* )}
|
|
28
|
+
* </View>
|
|
29
|
+
* )}
|
|
30
|
+
* dragEnabled
|
|
31
|
+
* onReorder={(itemId, from, to) => handleReorder(itemId, from, to)}
|
|
32
|
+
* />
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
// Main component
|
|
37
|
+
export { AnimatedFlashList } from './AnimatedFlashList';
|
|
38
|
+
export { AnimatedFlashListItem } from './AnimatedFlashListItem';
|
|
39
|
+
|
|
40
|
+
// Contexts (for advanced usage)
|
|
41
|
+
export {
|
|
42
|
+
DragStateProvider,
|
|
43
|
+
useDragState,
|
|
44
|
+
ListAnimationProvider,
|
|
45
|
+
useListAnimation,
|
|
46
|
+
useListAnimationOptional,
|
|
47
|
+
} from './contexts';
|
|
48
|
+
|
|
49
|
+
// Hooks (for advanced/custom implementations)
|
|
50
|
+
export {
|
|
51
|
+
// Drag hooks
|
|
52
|
+
useDragGesture,
|
|
53
|
+
useDragShift,
|
|
54
|
+
useDragAnimatedStyle,
|
|
55
|
+
useDropCompensation,
|
|
56
|
+
// Animation hooks
|
|
57
|
+
useListExitAnimation,
|
|
58
|
+
useListEntryAnimation,
|
|
59
|
+
} from './hooks';
|
|
60
|
+
|
|
61
|
+
// Constants (for customization)
|
|
62
|
+
export {
|
|
63
|
+
DEFAULT_DRAG_CONFIG,
|
|
64
|
+
createDragConfig,
|
|
65
|
+
DEFAULT_EXIT_ANIMATION,
|
|
66
|
+
FAST_EXIT_ANIMATION,
|
|
67
|
+
DEFAULT_ENTRY_ANIMATION,
|
|
68
|
+
getExitAnimationConfig,
|
|
69
|
+
createEntryAnimationConfig,
|
|
70
|
+
standardEasing,
|
|
71
|
+
} from './constants';
|
|
72
|
+
|
|
73
|
+
// Types
|
|
74
|
+
export type {
|
|
75
|
+
// List types
|
|
76
|
+
AnimatedListItem,
|
|
77
|
+
AnimatedFlashListProps,
|
|
78
|
+
AnimatedFlashListRef,
|
|
79
|
+
AnimatedFlashListConfig,
|
|
80
|
+
AnimatedRenderItemInfo,
|
|
81
|
+
DragHandleProps,
|
|
82
|
+
// Drag types
|
|
83
|
+
DragConfig,
|
|
84
|
+
UseDragGestureConfig,
|
|
85
|
+
UseDragGestureCallbacks,
|
|
86
|
+
UseDragGestureResult,
|
|
87
|
+
UseDragShiftConfig,
|
|
88
|
+
UseDragShiftResult,
|
|
89
|
+
UseDropCompensationConfig,
|
|
90
|
+
UseDragAnimatedStyleResult,
|
|
91
|
+
// Animation types
|
|
92
|
+
AnimationDirection,
|
|
93
|
+
ExitAnimationPreset,
|
|
94
|
+
ExitAnimationConfig,
|
|
95
|
+
EntryAnimationConfig,
|
|
96
|
+
PendingEntryAnimation,
|
|
97
|
+
ExitAnimationTrigger,
|
|
98
|
+
HapticFeedbackType,
|
|
99
|
+
} from './types';
|
|
100
|
+
|
|
101
|
+
// Context types
|
|
102
|
+
export type {
|
|
103
|
+
DragStateContextValue,
|
|
104
|
+
ListAnimationContextValue,
|
|
105
|
+
} from './contexts';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation direction for entry/exit animations
|
|
3
|
+
* 1 = forward/right, -1 = backward/left
|
|
4
|
+
*/
|
|
5
|
+
export type AnimationDirection = 1 | -1;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Animation preset type for exit animations
|
|
9
|
+
* - 'default': Standard exit animation (for swipe/delete actions)
|
|
10
|
+
* - 'fast': Quick exit animation (for checkbox toggles)
|
|
11
|
+
*/
|
|
12
|
+
export type ExitAnimationPreset = 'default' | 'fast';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Haptic feedback types that can be triggered during animations
|
|
16
|
+
*/
|
|
17
|
+
export type HapticFeedbackType = 'light' | 'medium' | 'heavy' | 'selection';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Configuration for exit animations
|
|
21
|
+
*/
|
|
22
|
+
export interface ExitAnimationConfig {
|
|
23
|
+
slide: {
|
|
24
|
+
duration: number;
|
|
25
|
+
distance: number;
|
|
26
|
+
};
|
|
27
|
+
fade: {
|
|
28
|
+
delay: number;
|
|
29
|
+
duration: number;
|
|
30
|
+
};
|
|
31
|
+
scale: {
|
|
32
|
+
delay: number;
|
|
33
|
+
duration: number;
|
|
34
|
+
toValue: number;
|
|
35
|
+
};
|
|
36
|
+
removalDelay: number;
|
|
37
|
+
layoutAnimation: {
|
|
38
|
+
duration: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Configuration for entry animations
|
|
44
|
+
*/
|
|
45
|
+
export interface EntryAnimationConfig {
|
|
46
|
+
fade: { duration: number };
|
|
47
|
+
slide: { distance: number; duration: number };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Pending entry animation info
|
|
52
|
+
*/
|
|
53
|
+
export interface PendingEntryAnimation {
|
|
54
|
+
itemId: string;
|
|
55
|
+
direction: AnimationDirection;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Exit animation trigger function type
|
|
61
|
+
*/
|
|
62
|
+
export type ExitAnimationTrigger = (
|
|
63
|
+
direction: AnimationDirection,
|
|
64
|
+
onComplete: () => void,
|
|
65
|
+
preset?: ExitAnimationPreset,
|
|
66
|
+
) => void;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Info about an item currently exiting (for layout compensation)
|
|
70
|
+
*/
|
|
71
|
+
export interface ExitingItemInfo {
|
|
72
|
+
itemId: string;
|
|
73
|
+
index: number;
|
|
74
|
+
height: number;
|
|
75
|
+
timestamp: number;
|
|
76
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { SharedValue, AnimatedRef } from 'react-native-reanimated';
|
|
2
|
+
import type { GestureType } from 'react-native-gesture-handler';
|
|
3
|
+
import type Animated from 'react-native-reanimated';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for drag behavior
|
|
7
|
+
*/
|
|
8
|
+
export interface DragConfig {
|
|
9
|
+
/** Fixed height for list items (used for drag calculations) */
|
|
10
|
+
itemHeight: number;
|
|
11
|
+
/** Scale factor applied to dragged item */
|
|
12
|
+
dragScale: number;
|
|
13
|
+
/** Shadow opacity for dragged item */
|
|
14
|
+
dragShadowOpacity: number;
|
|
15
|
+
/** Vertical margin per item */
|
|
16
|
+
itemVerticalMargin: number;
|
|
17
|
+
/** Duration (ms) to hold drag handle before drag activates */
|
|
18
|
+
longPressDuration: number;
|
|
19
|
+
/** Pixels from viewport edge to trigger autoscroll */
|
|
20
|
+
edgeThreshold: number;
|
|
21
|
+
/** Maximum scroll speed in pixels per frame */
|
|
22
|
+
maxScrollSpeed: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for useDragGesture hook
|
|
27
|
+
*/
|
|
28
|
+
export interface UseDragGestureConfig {
|
|
29
|
+
/** Item ID for stable identity */
|
|
30
|
+
itemId: string;
|
|
31
|
+
/** Current index in list */
|
|
32
|
+
index: number;
|
|
33
|
+
/** Total number of items in the list */
|
|
34
|
+
totalItems: number;
|
|
35
|
+
/** Whether drag is enabled for this item */
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
/** Ref to measure item container */
|
|
38
|
+
containerRef: AnimatedRef<Animated.View>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Callbacks for drag gesture events
|
|
43
|
+
*/
|
|
44
|
+
export interface UseDragGestureCallbacks {
|
|
45
|
+
/** Called when reorder should occur (with index delta) */
|
|
46
|
+
onReorderByDelta?: (itemId: string, delta: number) => void;
|
|
47
|
+
/** Optional haptic feedback callback */
|
|
48
|
+
onHapticFeedback?: (type: 'light' | 'medium') => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Return value from useDragGesture hook
|
|
53
|
+
*/
|
|
54
|
+
export interface UseDragGestureResult {
|
|
55
|
+
/** The pan gesture to attach to drag handle */
|
|
56
|
+
panGesture: GestureType;
|
|
57
|
+
/** Whether this item is currently being dragged */
|
|
58
|
+
isDragging: SharedValue<boolean>;
|
|
59
|
+
/** Current translateY of this item */
|
|
60
|
+
translateY: SharedValue<number>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Configuration for useDragShift hook
|
|
65
|
+
*/
|
|
66
|
+
export interface UseDragShiftConfig {
|
|
67
|
+
/** Item ID for identity check (to skip shift for the dragged item) */
|
|
68
|
+
itemId: string;
|
|
69
|
+
/** Current index in list */
|
|
70
|
+
index: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Return value from useDragShift hook
|
|
75
|
+
*/
|
|
76
|
+
export interface UseDragShiftResult {
|
|
77
|
+
/** Animated shift value for non-dragged items */
|
|
78
|
+
shiftY: SharedValue<number>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Configuration for useDropCompensation hook
|
|
83
|
+
*/
|
|
84
|
+
export interface UseDropCompensationConfig {
|
|
85
|
+
/** Item ID for stable identity check */
|
|
86
|
+
itemId: string;
|
|
87
|
+
/** Current index in list */
|
|
88
|
+
index: number;
|
|
89
|
+
/** This item's translateY SharedValue (from useDragGesture) */
|
|
90
|
+
translateY: SharedValue<number>;
|
|
91
|
+
/** This item's shiftY SharedValue (from useDragShift) */
|
|
92
|
+
shiftY: SharedValue<number>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Return value from useDragAnimatedStyle hook
|
|
97
|
+
*/
|
|
98
|
+
export interface UseDragAnimatedStyleResult {
|
|
99
|
+
/** Animated style for the dragged item (transform, zIndex, shadow) */
|
|
100
|
+
dragAnimatedStyle: ReturnType<typeof import('react-native-reanimated').useAnimatedStyle>;
|
|
101
|
+
}
|