@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.
Files changed (104) hide show
  1. package/README.md +282 -0
  2. package/lib/AnimatedFlashList.d.ts +6 -0
  3. package/lib/AnimatedFlashList.d.ts.map +1 -0
  4. package/lib/AnimatedFlashList.js +207 -0
  5. package/lib/AnimatedFlashListItem.d.ts +33 -0
  6. package/lib/AnimatedFlashListItem.d.ts.map +1 -0
  7. package/lib/AnimatedFlashListItem.js +155 -0
  8. package/lib/__tests__/utils/test-utils.d.ts +82 -0
  9. package/lib/__tests__/utils/test-utils.d.ts.map +1 -0
  10. package/lib/__tests__/utils/test-utils.js +115 -0
  11. package/lib/constants/animations.d.ts +39 -0
  12. package/lib/constants/animations.d.ts.map +1 -0
  13. package/lib/constants/animations.js +100 -0
  14. package/lib/constants/drag.d.ts +11 -0
  15. package/lib/constants/drag.d.ts.map +1 -0
  16. package/lib/constants/drag.js +47 -0
  17. package/lib/constants/index.d.ts +3 -0
  18. package/lib/constants/index.d.ts.map +1 -0
  19. package/lib/constants/index.js +18 -0
  20. package/lib/contexts/DragStateContext.d.ts +73 -0
  21. package/lib/contexts/DragStateContext.d.ts.map +1 -0
  22. package/lib/contexts/DragStateContext.js +148 -0
  23. package/lib/contexts/ListAnimationContext.d.ts +104 -0
  24. package/lib/contexts/ListAnimationContext.d.ts.map +1 -0
  25. package/lib/contexts/ListAnimationContext.js +184 -0
  26. package/lib/contexts/index.d.ts +5 -0
  27. package/lib/contexts/index.d.ts.map +1 -0
  28. package/lib/contexts/index.js +10 -0
  29. package/lib/hooks/animations/index.d.ts +9 -0
  30. package/lib/hooks/animations/index.d.ts.map +1 -0
  31. package/lib/hooks/animations/index.js +13 -0
  32. package/lib/hooks/animations/useListEntryAnimation.d.ts +38 -0
  33. package/lib/hooks/animations/useListEntryAnimation.d.ts.map +1 -0
  34. package/lib/hooks/animations/useListEntryAnimation.js +90 -0
  35. package/lib/hooks/animations/useListExitAnimation.d.ts +67 -0
  36. package/lib/hooks/animations/useListExitAnimation.d.ts.map +1 -0
  37. package/lib/hooks/animations/useListExitAnimation.js +146 -0
  38. package/lib/hooks/drag/index.d.ts +20 -0
  39. package/lib/hooks/drag/index.d.ts.map +1 -0
  40. package/lib/hooks/drag/index.js +26 -0
  41. package/lib/hooks/drag/useDragAnimatedStyle.d.ts +33 -0
  42. package/lib/hooks/drag/useDragAnimatedStyle.d.ts.map +1 -0
  43. package/lib/hooks/drag/useDragAnimatedStyle.js +61 -0
  44. package/lib/hooks/drag/useDragGesture.d.ts +30 -0
  45. package/lib/hooks/drag/useDragGesture.d.ts.map +1 -0
  46. package/lib/hooks/drag/useDragGesture.js +189 -0
  47. package/lib/hooks/drag/useDragShift.d.ts +21 -0
  48. package/lib/hooks/drag/useDragShift.d.ts.map +1 -0
  49. package/lib/hooks/drag/useDragShift.js +85 -0
  50. package/lib/hooks/drag/useDropCompensation.d.ts +27 -0
  51. package/lib/hooks/drag/useDropCompensation.d.ts.map +1 -0
  52. package/lib/hooks/drag/useDropCompensation.js +90 -0
  53. package/lib/hooks/index.d.ts +8 -0
  54. package/lib/hooks/index.d.ts.map +1 -0
  55. package/lib/hooks/index.js +18 -0
  56. package/lib/index.d.ts +42 -0
  57. package/lib/index.d.ts.map +1 -0
  58. package/lib/index.js +69 -0
  59. package/lib/types/animations.d.ts +71 -0
  60. package/lib/types/animations.d.ts.map +1 -0
  61. package/lib/types/animations.js +2 -0
  62. package/lib/types/drag.d.ts +94 -0
  63. package/lib/types/drag.d.ts.map +1 -0
  64. package/lib/types/drag.js +2 -0
  65. package/lib/types/index.d.ts +4 -0
  66. package/lib/types/index.d.ts.map +1 -0
  67. package/lib/types/index.js +19 -0
  68. package/lib/types/list.d.ts +136 -0
  69. package/lib/types/list.d.ts.map +1 -0
  70. package/lib/types/list.js +2 -0
  71. package/package.json +73 -0
  72. package/src/AnimatedFlashList.tsx +411 -0
  73. package/src/AnimatedFlashListItem.tsx +212 -0
  74. package/src/__tests__/components/AnimatedFlashList.test.tsx +365 -0
  75. package/src/__tests__/components/AnimatedFlashListItem.test.tsx +371 -0
  76. package/src/__tests__/contexts/DragStateContext.test.tsx +169 -0
  77. package/src/__tests__/contexts/ListAnimationContext.test.tsx +324 -0
  78. package/src/__tests__/hooks/useDragAnimatedStyle.test.tsx +118 -0
  79. package/src/__tests__/hooks/useDragGesture.test.tsx +169 -0
  80. package/src/__tests__/hooks/useDragShift.test.tsx +94 -0
  81. package/src/__tests__/hooks/useDropCompensation.test.tsx +182 -0
  82. package/src/__tests__/hooks/useListEntryAnimation.test.tsx +135 -0
  83. package/src/__tests__/hooks/useListExitAnimation.test.tsx +175 -0
  84. package/src/__tests__/utils/test-utils.tsx +159 -0
  85. package/src/constants/animations.ts +107 -0
  86. package/src/constants/drag.ts +51 -0
  87. package/src/constants/index.ts +2 -0
  88. package/src/contexts/DragStateContext.tsx +197 -0
  89. package/src/contexts/ListAnimationContext.tsx +302 -0
  90. package/src/contexts/index.ts +9 -0
  91. package/src/hooks/animations/index.ts +9 -0
  92. package/src/hooks/animations/useListEntryAnimation.ts +108 -0
  93. package/src/hooks/animations/useListExitAnimation.ts +197 -0
  94. package/src/hooks/drag/index.ts +20 -0
  95. package/src/hooks/drag/useDragAnimatedStyle.ts +80 -0
  96. package/src/hooks/drag/useDragGesture.ts +267 -0
  97. package/src/hooks/drag/useDragShift.ts +119 -0
  98. package/src/hooks/drag/useDropCompensation.ts +120 -0
  99. package/src/hooks/index.ts +16 -0
  100. package/src/index.ts +105 -0
  101. package/src/types/animations.ts +76 -0
  102. package/src/types/drag.ts +101 -0
  103. package/src/types/index.ts +3 -0
  104. package/src/types/list.ts +178 -0
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AnimatedFlashListItem = void 0;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
39
+ const hooks_1 = require("./hooks");
40
+ const contexts_1 = require("./contexts");
41
+ /**
42
+ * Internal item wrapper that provides all animation functionality.
43
+ *
44
+ * This component:
45
+ * 1. Sets up drag gesture and shift animations
46
+ * 2. Sets up entry/exit animations
47
+ * 3. Combines all animated styles
48
+ * 4. Passes everything to the consumer's renderItem function
49
+ *
50
+ * @internal
51
+ */
52
+ function AnimatedFlashListItemInner({ item, index, totalItems, isDragEnabled, renderItem, onReorderByDelta, onHapticFeedback, }) {
53
+ // Animated ref for measuring item height on drag start
54
+ const containerRef = (0, react_native_reanimated_1.useAnimatedRef)();
55
+ // Track measured height for layout compensation
56
+ const measuredHeightRef = (0, react_1.useRef)(0);
57
+ // List animation context for subscription-triggered animations and layout compensation
58
+ const animationContext = (0, contexts_1.useListAnimationOptional)();
59
+ // === DRAG HOOKS ===
60
+ // Pan gesture for drag-to-reorder
61
+ const { panGesture, isDragging, translateY } = (0, hooks_1.useDragGesture)({
62
+ itemId: item.id,
63
+ index,
64
+ totalItems,
65
+ enabled: isDragEnabled,
66
+ containerRef,
67
+ }, {
68
+ onReorderByDelta,
69
+ onHapticFeedback,
70
+ });
71
+ // Shift animation for non-dragged items
72
+ const { shiftY } = (0, hooks_1.useDragShift)({ itemId: item.id, index });
73
+ // Handle index changes after cache updates (drop compensation)
74
+ (0, hooks_1.useDropCompensation)({ itemId: item.id, index, translateY, shiftY });
75
+ // Animated style for drag transforms
76
+ const { dragAnimatedStyle } = (0, hooks_1.useDragAnimatedStyle)(item.id, isDragging, translateY, shiftY);
77
+ // === ANIMATION HOOKS ===
78
+ // Callbacks for layout compensation (register/unregister exiting items)
79
+ const onExitStart = (0, react_1.useCallback)((exitIndex, height) => {
80
+ animationContext?.registerExitingItem(item.id, exitIndex, height);
81
+ }, [animationContext, item.id]);
82
+ const onExitComplete = (0, react_1.useCallback)(() => {
83
+ animationContext?.unregisterExitingItem(item.id);
84
+ }, [animationContext, item.id]);
85
+ // Exit animation for smooth slide-out (with layout compensation callbacks)
86
+ const { exitAnimatedStyle, triggerExit, resetAnimation } = (0, hooks_1.useListExitAnimation)(item.id, {
87
+ index,
88
+ measuredHeight: measuredHeightRef.current,
89
+ onExitStart,
90
+ onExitComplete,
91
+ });
92
+ // Entry animation for items appearing
93
+ const { entryAnimatedStyle } = (0, hooks_1.useListEntryAnimation)(item.id);
94
+ // Register exit animation trigger (O(1) direct calls from subscriptions)
95
+ (0, react_1.useLayoutEffect)(() => {
96
+ if (!animationContext)
97
+ return;
98
+ animationContext.registerAnimationTrigger(item.id, triggerExit);
99
+ return () => animationContext.unregisterAnimationTrigger(item.id);
100
+ }, [item.id, triggerExit, animationContext]);
101
+ // Track measured height for layout compensation
102
+ const handleLayout = (0, react_1.useCallback)((event) => {
103
+ measuredHeightRef.current = event.nativeEvent.layout.height;
104
+ }, []);
105
+ // Create combined animated style
106
+ const combinedAnimatedStyle = (0, react_1.useMemo)(() => {
107
+ // We can't directly combine animated styles here since they're worklet-based
108
+ // Instead, we'll let the consumer apply them via the render prop
109
+ return {};
110
+ }, []);
111
+ // Create drag handle props
112
+ const dragHandleProps = (0, react_1.useMemo)(() => isDragEnabled
113
+ ? {
114
+ gesture: panGesture,
115
+ isDragging,
116
+ }
117
+ : null, [isDragEnabled, panGesture, isDragging]);
118
+ // Trigger exit animation wrapper
119
+ const triggerExitAnimation = (0, react_1.useCallback)((direction, onComplete, preset) => {
120
+ triggerExit(direction, onComplete, preset);
121
+ }, [triggerExit]);
122
+ // Create render info
123
+ const renderInfo = (0, react_1.useMemo)(() => ({
124
+ item,
125
+ index,
126
+ totalItems,
127
+ animatedStyle: combinedAnimatedStyle,
128
+ dragHandleProps,
129
+ isDragging: false, // This is a SharedValue, consumer should use dragHandleProps.isDragging
130
+ isDragEnabled,
131
+ triggerExitAnimation,
132
+ resetExitAnimation: resetAnimation,
133
+ }), [
134
+ item,
135
+ index,
136
+ totalItems,
137
+ combinedAnimatedStyle,
138
+ dragHandleProps,
139
+ isDragEnabled,
140
+ triggerExitAnimation,
141
+ resetAnimation,
142
+ ]);
143
+ // Render the item with animations applied
144
+ // The consumer's renderItem gets wrapped in our animated container
145
+ const renderedItem = renderItem(renderInfo);
146
+ return (<react_native_reanimated_1.default.View ref={containerRef} onLayout={handleLayout} style={[
147
+ exitAnimatedStyle,
148
+ entryAnimatedStyle,
149
+ isDragEnabled && dragAnimatedStyle,
150
+ ]}>
151
+ {renderedItem}
152
+ </react_native_reanimated_1.default.View>);
153
+ }
154
+ // Memoize to prevent unnecessary re-renders
155
+ exports.AnimatedFlashListItem = react_1.default.memo(AnimatedFlashListItemInner);
@@ -0,0 +1,82 @@
1
+ import { ReactElement } from 'react';
2
+ import { render, RenderOptions } from '@testing-library/react-native';
3
+ import type { DragConfig } from '../../types';
4
+ /**
5
+ * Custom render options with context configuration
6
+ */
7
+ interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
8
+ dragConfig?: Partial<DragConfig>;
9
+ entryAnimationTimeout?: number;
10
+ layoutAnimationDuration?: number;
11
+ }
12
+ /**
13
+ * Custom render function that wraps components with all necessary providers
14
+ */
15
+ declare const customRender: (ui: ReactElement, options?: CustomRenderOptions) => ReturnType<typeof render>;
16
+ export * from '@testing-library/react-native';
17
+ export { customRender as render };
18
+ /**
19
+ * Create a mock list item with required id field
20
+ */
21
+ export interface MockListItem {
22
+ id: string;
23
+ title?: string;
24
+ [key: string]: unknown;
25
+ }
26
+ /**
27
+ * Factory function to create a single mock item
28
+ */
29
+ export declare const createMockItem: (id: string, overrides?: Partial<MockListItem>) => MockListItem;
30
+ /**
31
+ * Factory function to create multiple mock items
32
+ */
33
+ export declare const createMockItems: (count: number) => MockListItem[];
34
+ /**
35
+ * Create a mock SharedValue-like object for testing
36
+ */
37
+ export declare const createMockSharedValue: <T>(initialValue: T) => {
38
+ value: T;
39
+ addListener: jest.Mock<any, any, any>;
40
+ removeListener: jest.Mock<any, any, any>;
41
+ modify: jest.Mock<T, [modifier: (val: T) => T], any>;
42
+ };
43
+ /**
44
+ * Create a mock animated ref for testing
45
+ */
46
+ export declare const createMockAnimatedRef: <T>(current?: T | null) => {
47
+ current: T | null;
48
+ };
49
+ /**
50
+ * Wait for animations to complete (mock version)
51
+ */
52
+ export declare const waitForAnimations: (ms?: number) => Promise<void>;
53
+ /**
54
+ * Mock gesture event data for testing drag operations
55
+ */
56
+ export declare const createMockGestureEvent: (overrides?: Record<string, unknown>) => {
57
+ translationX: number;
58
+ translationY: number;
59
+ velocityX: number;
60
+ velocityY: number;
61
+ absoluteX: number;
62
+ absoluteY: number;
63
+ x: number;
64
+ y: number;
65
+ numberOfPointers: number;
66
+ state: number;
67
+ };
68
+ /**
69
+ * Mock FlashList ref for testing
70
+ */
71
+ export declare const createMockFlashListRef: () => {
72
+ scrollToOffset: jest.Mock<any, any, any>;
73
+ scrollToIndex: jest.Mock<any, any, any>;
74
+ scrollToItem: jest.Mock<any, any, any>;
75
+ scrollToEnd: jest.Mock<any, any, any>;
76
+ getScrollOffset: jest.Mock<number, [], any>;
77
+ getScrollableNode: jest.Mock<null, [], any>;
78
+ recordInteraction: jest.Mock<any, any, any>;
79
+ flashScrollIndicators: jest.Mock<any, any, any>;
80
+ prepareForLayoutAnimationRender: jest.Mock<any, any, any>;
81
+ };
82
+ //# sourceMappingURL=test-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/test-utils.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAGtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA+B9C;;GAEG;AACH,UAAU,mBAAoB,SAAQ,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC;IAClE,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;GAEG;AACH,QAAA,MAAM,YAAY,GAChB,IAAI,YAAY,EAChB,UAAU,mBAAmB,KAC5B,UAAU,CAAC,OAAO,MAAM,CAmB1B,CAAC;AAGF,cAAc,+BAA+B,CAAC;AAG9C,OAAO,EAAE,YAAY,IAAI,MAAM,EAAE,CAAC;AAElC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,GACzB,IAAI,MAAM,EACV,YAAW,OAAO,CAAC,YAAY,CAAM,KACpC,YAID,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,KAAG,YAAY,EACc,CAAC;AAE3E;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EAAG,cAAc,CAAC;;;;0CAItB,CAAC,KAAK,CAAC;CACxC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EAAG,UAAS,CAAC,GAAG,IAAW;;CAEhE,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAU,WAAQ,KAAG,OAAO,CAAC,IAAI,CAE9D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GAAI,YAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;;;;;;CAY5E,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;CAUjC,CAAC"}
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.createMockFlashListRef = exports.createMockGestureEvent = exports.waitForAnimations = exports.createMockAnimatedRef = exports.createMockSharedValue = exports.createMockItems = exports.createMockItem = exports.render = void 0;
21
+ const react_1 = __importDefault(require("react"));
22
+ const react_native_1 = require("@testing-library/react-native");
23
+ const DragStateContext_1 = require("../../contexts/DragStateContext");
24
+ const ListAnimationContext_1 = require("../../contexts/ListAnimationContext");
25
+ /**
26
+ * Wrapper component that provides all required contexts for testing
27
+ */
28
+ const AllProviders = ({ children, dragConfig, entryAnimationTimeout, layoutAnimationDuration, }) => {
29
+ return (<ListAnimationContext_1.ListAnimationProvider entryAnimationTimeout={entryAnimationTimeout} layoutAnimationDuration={layoutAnimationDuration}>
30
+ <DragStateContext_1.DragStateProvider config={dragConfig}>{children}</DragStateContext_1.DragStateProvider>
31
+ </ListAnimationContext_1.ListAnimationProvider>);
32
+ };
33
+ /**
34
+ * Custom render function that wraps components with all necessary providers
35
+ */
36
+ const customRender = (ui, options) => {
37
+ const { dragConfig, entryAnimationTimeout, layoutAnimationDuration, ...renderOptions } = options ?? {};
38
+ const Wrapper = ({ children }) => (<AllProviders dragConfig={dragConfig} entryAnimationTimeout={entryAnimationTimeout} layoutAnimationDuration={layoutAnimationDuration}>
39
+ {children}
40
+ </AllProviders>);
41
+ return (0, react_native_1.render)(ui, { wrapper: Wrapper, ...renderOptions });
42
+ };
43
+ exports.render = customRender;
44
+ // Re-export everything
45
+ __exportStar(require("@testing-library/react-native"), exports);
46
+ /**
47
+ * Factory function to create a single mock item
48
+ */
49
+ const createMockItem = (id, overrides = {}) => ({
50
+ id,
51
+ title: `Item ${id}`,
52
+ ...overrides,
53
+ });
54
+ exports.createMockItem = createMockItem;
55
+ /**
56
+ * Factory function to create multiple mock items
57
+ */
58
+ const createMockItems = (count) => Array.from({ length: count }, (_, i) => (0, exports.createMockItem)(`item-${i + 1}`));
59
+ exports.createMockItems = createMockItems;
60
+ /**
61
+ * Create a mock SharedValue-like object for testing
62
+ */
63
+ const createMockSharedValue = (initialValue) => ({
64
+ value: initialValue,
65
+ addListener: jest.fn(),
66
+ removeListener: jest.fn(),
67
+ modify: jest.fn((modifier) => modifier(initialValue)),
68
+ });
69
+ exports.createMockSharedValue = createMockSharedValue;
70
+ /**
71
+ * Create a mock animated ref for testing
72
+ */
73
+ const createMockAnimatedRef = (current = null) => ({
74
+ current,
75
+ });
76
+ exports.createMockAnimatedRef = createMockAnimatedRef;
77
+ /**
78
+ * Wait for animations to complete (mock version)
79
+ */
80
+ const waitForAnimations = async (ms = 300) => {
81
+ await new Promise((resolve) => setTimeout(resolve, ms));
82
+ };
83
+ exports.waitForAnimations = waitForAnimations;
84
+ /**
85
+ * Mock gesture event data for testing drag operations
86
+ */
87
+ const createMockGestureEvent = (overrides = {}) => ({
88
+ translationX: 0,
89
+ translationY: 0,
90
+ velocityX: 0,
91
+ velocityY: 0,
92
+ absoluteX: 0,
93
+ absoluteY: 0,
94
+ x: 0,
95
+ y: 0,
96
+ numberOfPointers: 1,
97
+ state: 4, // ACTIVE
98
+ ...overrides,
99
+ });
100
+ exports.createMockGestureEvent = createMockGestureEvent;
101
+ /**
102
+ * Mock FlashList ref for testing
103
+ */
104
+ const createMockFlashListRef = () => ({
105
+ scrollToOffset: jest.fn(),
106
+ scrollToIndex: jest.fn(),
107
+ scrollToItem: jest.fn(),
108
+ scrollToEnd: jest.fn(),
109
+ getScrollOffset: jest.fn(() => 0),
110
+ getScrollableNode: jest.fn(() => null),
111
+ recordInteraction: jest.fn(),
112
+ flashScrollIndicators: jest.fn(),
113
+ prepareForLayoutAnimationRender: jest.fn(),
114
+ });
115
+ exports.createMockFlashListRef = createMockFlashListRef;
@@ -0,0 +1,39 @@
1
+ import type { ExitAnimationConfig, EntryAnimationConfig } from '../types';
2
+ /**
3
+ * Standard cubic bezier easing function for smooth animations
4
+ * Equivalent to CSS ease-in-out with custom curve
5
+ */
6
+ export declare const standardEasing: import("react-native-reanimated").EasingFunctionFactory;
7
+ /**
8
+ * Default exit animation configuration
9
+ * Used when items are removed/toggled from the list
10
+ *
11
+ * Timeline (300ms total):
12
+ * - 0-300ms: Slide (300px in direction)
13
+ * - 50-300ms: Scale to 0.95
14
+ * - 100-300ms: Fade out
15
+ */
16
+ export declare const DEFAULT_EXIT_ANIMATION: ExitAnimationConfig;
17
+ /**
18
+ * Fast exit animation for quick actions (checkbox toggles)
19
+ *
20
+ * Timeline (200ms total):
21
+ * - 0-200ms: Slide (200px in direction)
22
+ * - 0-200ms: Fade out (starts immediately)
23
+ * - 0-200ms: Scale to 0.97
24
+ */
25
+ export declare const FAST_EXIT_ANIMATION: ExitAnimationConfig;
26
+ /**
27
+ * Default entry animation configuration
28
+ * Used when items appear in the list
29
+ */
30
+ export declare const DEFAULT_ENTRY_ANIMATION: EntryAnimationConfig;
31
+ /**
32
+ * Get exit animation config by preset
33
+ */
34
+ export declare function getExitAnimationConfig(preset?: 'default' | 'fast', overrides?: Partial<ExitAnimationConfig>): ExitAnimationConfig;
35
+ /**
36
+ * Create a merged entry animation config from defaults and overrides
37
+ */
38
+ export declare function createEntryAnimationConfig(overrides?: Partial<EntryAnimationConfig>): EntryAnimationConfig;
39
+ //# sourceMappingURL=animations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animations.d.ts","sourceRoot":"","sources":["../../src/constants/animations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,cAAc,yDAAoC,CAAC;AAEhE;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,mBAkBpC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,mBAkBjC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,oBAGrC,CAAC;AAEF;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,GAAE,SAAS,GAAG,MAAe,EACnC,SAAS,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GACvC,mBAAmB,CAWrB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GACxC,oBAAoB,CAOtB"}
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_ENTRY_ANIMATION = exports.FAST_EXIT_ANIMATION = exports.DEFAULT_EXIT_ANIMATION = exports.standardEasing = void 0;
4
+ exports.getExitAnimationConfig = getExitAnimationConfig;
5
+ exports.createEntryAnimationConfig = createEntryAnimationConfig;
6
+ const react_native_reanimated_1 = require("react-native-reanimated");
7
+ /**
8
+ * Standard cubic bezier easing function for smooth animations
9
+ * Equivalent to CSS ease-in-out with custom curve
10
+ */
11
+ exports.standardEasing = react_native_reanimated_1.Easing.bezier(0.25, 0.1, 0.25, 1);
12
+ /**
13
+ * Default exit animation configuration
14
+ * Used when items are removed/toggled from the list
15
+ *
16
+ * Timeline (300ms total):
17
+ * - 0-300ms: Slide (300px in direction)
18
+ * - 50-300ms: Scale to 0.95
19
+ * - 100-300ms: Fade out
20
+ */
21
+ exports.DEFAULT_EXIT_ANIMATION = {
22
+ slide: {
23
+ duration: 300,
24
+ distance: 300,
25
+ },
26
+ fade: {
27
+ delay: 100,
28
+ duration: 200,
29
+ },
30
+ scale: {
31
+ delay: 50,
32
+ duration: 250,
33
+ toValue: 0.95,
34
+ },
35
+ removalDelay: 300,
36
+ layoutAnimation: {
37
+ duration: 200,
38
+ },
39
+ };
40
+ /**
41
+ * Fast exit animation for quick actions (checkbox toggles)
42
+ *
43
+ * Timeline (200ms total):
44
+ * - 0-200ms: Slide (200px in direction)
45
+ * - 0-200ms: Fade out (starts immediately)
46
+ * - 0-200ms: Scale to 0.97
47
+ */
48
+ exports.FAST_EXIT_ANIMATION = {
49
+ slide: {
50
+ duration: 200,
51
+ distance: 200,
52
+ },
53
+ fade: {
54
+ delay: 0,
55
+ duration: 200,
56
+ },
57
+ scale: {
58
+ delay: 0,
59
+ duration: 200,
60
+ toValue: 0.97,
61
+ },
62
+ removalDelay: 200,
63
+ layoutAnimation: {
64
+ duration: 150,
65
+ },
66
+ };
67
+ /**
68
+ * Default entry animation configuration
69
+ * Used when items appear in the list
70
+ */
71
+ exports.DEFAULT_ENTRY_ANIMATION = {
72
+ fade: { duration: 250 },
73
+ slide: { distance: 50, duration: 300 },
74
+ };
75
+ /**
76
+ * Get exit animation config by preset
77
+ */
78
+ function getExitAnimationConfig(preset = 'fast', overrides) {
79
+ const base = preset === 'fast' ? exports.FAST_EXIT_ANIMATION : exports.DEFAULT_EXIT_ANIMATION;
80
+ if (!overrides)
81
+ return base;
82
+ return {
83
+ slide: { ...base.slide, ...overrides.slide },
84
+ fade: { ...base.fade, ...overrides.fade },
85
+ scale: { ...base.scale, ...overrides.scale },
86
+ removalDelay: overrides.removalDelay ?? base.removalDelay,
87
+ layoutAnimation: { ...base.layoutAnimation, ...overrides.layoutAnimation },
88
+ };
89
+ }
90
+ /**
91
+ * Create a merged entry animation config from defaults and overrides
92
+ */
93
+ function createEntryAnimationConfig(overrides) {
94
+ if (!overrides)
95
+ return exports.DEFAULT_ENTRY_ANIMATION;
96
+ return {
97
+ fade: { ...exports.DEFAULT_ENTRY_ANIMATION.fade, ...overrides.fade },
98
+ slide: { ...exports.DEFAULT_ENTRY_ANIMATION.slide, ...overrides.slide },
99
+ };
100
+ }
@@ -0,0 +1,11 @@
1
+ import type { DragConfig } from '../types';
2
+ /**
3
+ * Default drag configuration
4
+ * All values can be overridden via AnimatedFlashList config prop
5
+ */
6
+ export declare const DEFAULT_DRAG_CONFIG: DragConfig;
7
+ /**
8
+ * Create a merged drag config from defaults and overrides
9
+ */
10
+ export declare function createDragConfig(overrides?: Partial<DragConfig>): DragConfig;
11
+ //# sourceMappingURL=drag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drag.d.ts","sourceRoot":"","sources":["../../src/constants/drag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,UAoCjC,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU,CAG5E"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_DRAG_CONFIG = void 0;
4
+ exports.createDragConfig = createDragConfig;
5
+ /**
6
+ * Default drag configuration
7
+ * All values can be overridden via AnimatedFlashList config prop
8
+ */
9
+ exports.DEFAULT_DRAG_CONFIG = {
10
+ /**
11
+ * Fixed height for list items used in drag calculations.
12
+ * Override this to match your item height + margins
13
+ */
14
+ itemHeight: 95,
15
+ /**
16
+ * Scale factor applied to dragged item for visual feedback
17
+ */
18
+ dragScale: 1.03,
19
+ /**
20
+ * Shadow opacity for dragged item (increases from 0.1 to this value)
21
+ */
22
+ dragShadowOpacity: 0.25,
23
+ /**
24
+ * Vertical margin per item (total margin = 2 * itemVerticalMargin)
25
+ */
26
+ itemVerticalMargin: 8,
27
+ /**
28
+ * Duration (ms) to hold drag handle before drag activates
29
+ */
30
+ longPressDuration: 200,
31
+ /**
32
+ * Pixels from viewport edge to trigger autoscroll
33
+ */
34
+ edgeThreshold: 80,
35
+ /**
36
+ * Maximum scroll speed in pixels per frame at edge
37
+ */
38
+ maxScrollSpeed: 10,
39
+ };
40
+ /**
41
+ * Create a merged drag config from defaults and overrides
42
+ */
43
+ function createDragConfig(overrides) {
44
+ if (!overrides)
45
+ return exports.DEFAULT_DRAG_CONFIG;
46
+ return { ...exports.DEFAULT_DRAG_CONFIG, ...overrides };
47
+ }
@@ -0,0 +1,3 @@
1
+ export * from './drag';
2
+ export * from './animations';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./drag"), exports);
18
+ __exportStar(require("./animations"), exports);
@@ -0,0 +1,73 @@
1
+ import React, { type ReactNode } from 'react';
2
+ import { type SharedValue } from 'react-native-reanimated';
3
+ import type { FlashListRef } from '@shopify/flash-list';
4
+ import type { DragConfig } from '../types';
5
+ /**
6
+ * Centralized drag state for coordinating animations across list items.
7
+ * All animation values are Reanimated SharedValues for 60fps UI thread performance.
8
+ *
9
+ * Architecture:
10
+ * - Single source of truth for all drag state (no per-item local state)
11
+ * - Dragged item identified by draggedIndex, reads/writes centralized values
12
+ * - Non-dragged items read draggedIndex/currentTranslateY to calculate shift
13
+ * - Scroll state enables viewport-aware hover calculations and autoscroll
14
+ */
15
+ export interface DragStateContextValue {
16
+ /** Is any item currently being dragged? */
17
+ isDragging: SharedValue<boolean>;
18
+ /** Original index of the item being dragged (-1 if not dragging) */
19
+ draggedIndex: SharedValue<number>;
20
+ /** ID of the item being dragged (for stable identity across FlashList recycling) */
21
+ draggedItemId: SharedValue<string>;
22
+ /** Current Y translation of the dragged item (for calculating hover position) */
23
+ currentTranslateY: SharedValue<number>;
24
+ /** Scale of the dragged item (1.0 = normal, configured = dragging) */
25
+ draggedScale: SharedValue<number>;
26
+ /** Current scroll offset of the list (updated via onScroll) */
27
+ scrollOffset: SharedValue<number>;
28
+ /** Scroll offset when drag started (for scroll delta calculation during autoscroll) */
29
+ dragStartScrollOffset: SharedValue<number>;
30
+ /** Total content height of the list (updated via onContentSizeChange) */
31
+ contentHeight: SharedValue<number>;
32
+ /** Visible viewport height (updated via onLayout) */
33
+ visibleHeight: SharedValue<number>;
34
+ /** Y position of FlashList top on screen (for autoscroll coordinate conversion) */
35
+ listTopY: SharedValue<number>;
36
+ /** Counter incremented on every drag state change to force useDerivedValue re-evaluation */
37
+ dragUpdateTrigger: SharedValue<number>;
38
+ /** Measured height of the dragged item (for dynamic height calculations) */
39
+ measuredItemHeight: SharedValue<number>;
40
+ /** Flag to freeze shift values during drop transition */
41
+ isDropping: SharedValue<boolean>;
42
+ /** Register the FlashList ref for autoscroll operations */
43
+ setListRef: (ref: FlashListRef<unknown> | null) => void;
44
+ /** Scroll the list to a specific offset (for autoscroll during drag) */
45
+ scrollToOffset: (offset: number, animated?: boolean) => void;
46
+ /** Reset drag state after drop animation completes */
47
+ resetDragState: () => void;
48
+ /** Current drag configuration */
49
+ config: DragConfig;
50
+ }
51
+ /**
52
+ * Hook to access shared drag state from context.
53
+ * Must be used within DragStateProvider.
54
+ */
55
+ export declare const useDragState: () => DragStateContextValue;
56
+ interface DragStateProviderProps {
57
+ children: ReactNode;
58
+ /** Optional drag configuration overrides */
59
+ config?: Partial<DragConfig>;
60
+ }
61
+ /**
62
+ * Provider that creates shared Reanimated values for drag state.
63
+ *
64
+ * These values are shared across all list items:
65
+ * - The dragged item writes to them during drag gestures
66
+ * - Non-dragged items read them to calculate their shift offset
67
+ * - Scroll state enables viewport-aware hover calculations
68
+ *
69
+ * Using SharedValues ensures animations run on the UI thread at 60fps.
70
+ */
71
+ export declare const DragStateProvider: React.FC<DragStateProviderProps>;
72
+ export {};
73
+ //# sourceMappingURL=DragStateContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DragStateContext.d.ts","sourceRoot":"","sources":["../../src/contexts/DragStateContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAMZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC,2CAA2C;IAC3C,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,oEAAoE;IACpE,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,oFAAoF;IACpF,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,iFAAiF;IACjF,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,sEAAsE;IACtE,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,+DAA+D;IAC/D,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,uFAAuF;IACvF,qBAAqB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3C,yEAAyE;IACzE,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,qDAAqD;IACrD,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,mFAAmF;IACnF,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9B,4FAA4F;IAC5F,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,4EAA4E;IAC5E,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC,yDAAyD;IACzD,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,2DAA2D;IAC3D,UAAU,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACxD,wEAAwE;IACxE,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7D,sDAAsD;IACtD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,iCAAiC;IACjC,MAAM,EAAE,UAAU,CAAC;CACpB;AAID;;;GAGG;AACH,eAAO,MAAM,YAAY,QAAO,qBAM/B,CAAC;AAEF,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CA0G9D,CAAC"}