react-native-drax 0.11.0-alpha.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +390 -227
  3. package/lib/module/DebugOverlay.js +121 -0
  4. package/lib/module/DebugOverlay.js.map +1 -0
  5. package/lib/module/Drax.js +36 -0
  6. package/lib/module/Drax.js.map +1 -0
  7. package/lib/module/DraxContext.js +6 -0
  8. package/lib/module/DraxContext.js.map +1 -0
  9. package/lib/module/DraxHandle.js +47 -0
  10. package/lib/module/DraxHandle.js.map +1 -0
  11. package/lib/module/DraxHandleContext.js +11 -0
  12. package/lib/module/DraxHandleContext.js.map +1 -0
  13. package/lib/module/DraxList.js +108 -0
  14. package/lib/module/DraxList.js.map +1 -0
  15. package/lib/module/DraxProvider.js +203 -0
  16. package/lib/module/DraxProvider.js.map +1 -0
  17. package/lib/module/DraxScrollView.js +167 -0
  18. package/lib/module/DraxScrollView.js.map +1 -0
  19. package/lib/module/DraxSubprovider.js +21 -0
  20. package/lib/module/DraxSubprovider.js.map +1 -0
  21. package/lib/module/DraxView.js +348 -0
  22. package/lib/module/DraxView.js.map +1 -0
  23. package/lib/module/HoverLayer.js +152 -0
  24. package/lib/module/HoverLayer.js.map +1 -0
  25. package/lib/module/SortableBoardContainer.js +386 -0
  26. package/lib/module/SortableBoardContainer.js.map +1 -0
  27. package/lib/module/SortableBoardContext.js +6 -0
  28. package/lib/module/SortableBoardContext.js.map +1 -0
  29. package/lib/module/SortableContainer.js +571 -0
  30. package/lib/module/SortableContainer.js.map +1 -0
  31. package/lib/module/SortableItem.js +226 -0
  32. package/lib/module/SortableItem.js.map +1 -0
  33. package/lib/module/SortableItemContext.js +38 -0
  34. package/lib/module/SortableItemContext.js.map +1 -0
  35. package/lib/module/compat/detectVersion.js +19 -0
  36. package/lib/module/compat/detectVersion.js.map +1 -0
  37. package/lib/module/compat/index.js +5 -0
  38. package/lib/module/compat/index.js.map +1 -0
  39. package/lib/module/compat/types.js +4 -0
  40. package/lib/module/compat/types.js.map +1 -0
  41. package/lib/module/compat/useDraxPanGesture.js +94 -0
  42. package/lib/module/compat/useDraxPanGesture.js.map +1 -0
  43. package/lib/module/hooks/index.js +5 -0
  44. package/lib/module/hooks/index.js.map +1 -0
  45. package/lib/module/hooks/useCallbackDispatch.js +688 -0
  46. package/lib/module/hooks/useCallbackDispatch.js.map +1 -0
  47. package/lib/module/hooks/useDragGesture.js +240 -0
  48. package/lib/module/hooks/useDragGesture.js.map +1 -0
  49. package/lib/module/hooks/useDraxContext.js +12 -0
  50. package/lib/module/hooks/useDraxContext.js.map +1 -0
  51. package/lib/module/hooks/useDraxId.js +13 -0
  52. package/lib/module/hooks/useDraxId.js.map +1 -0
  53. package/lib/module/hooks/useDraxMethods.js +73 -0
  54. package/lib/module/hooks/useDraxMethods.js.map +1 -0
  55. package/lib/module/hooks/useDraxScrollHandler.js +97 -0
  56. package/lib/module/hooks/useDraxScrollHandler.js.map +1 -0
  57. package/lib/module/hooks/useSortableBoard.js +37 -0
  58. package/lib/module/hooks/useSortableBoard.js.map +1 -0
  59. package/lib/module/hooks/useSortableList.js +988 -0
  60. package/lib/module/hooks/useSortableList.js.map +1 -0
  61. package/lib/module/hooks/useSpatialIndex.js +283 -0
  62. package/lib/module/hooks/useSpatialIndex.js.map +1 -0
  63. package/lib/module/hooks/useViewStyles.js +158 -0
  64. package/lib/module/hooks/useViewStyles.js.map +1 -0
  65. package/lib/module/hooks/useWebScrollFreeze.js +52 -0
  66. package/lib/module/hooks/useWebScrollFreeze.js.map +1 -0
  67. package/lib/module/index.js +37 -0
  68. package/lib/module/index.js.map +1 -0
  69. package/lib/module/math.js +294 -0
  70. package/lib/module/math.js.map +1 -0
  71. package/lib/module/package.json +1 -0
  72. package/lib/module/params.js +88 -0
  73. package/lib/module/params.js.map +1 -0
  74. package/lib/module/types.js +215 -0
  75. package/lib/module/types.js.map +1 -0
  76. package/lib/typescript/package.json +1 -0
  77. package/lib/typescript/src/DebugOverlay.d.ts +17 -0
  78. package/lib/typescript/src/DebugOverlay.d.ts.map +1 -0
  79. package/lib/typescript/src/Drax.d.ts +28 -0
  80. package/lib/typescript/src/Drax.d.ts.map +1 -0
  81. package/lib/typescript/src/DraxContext.d.ts +3 -0
  82. package/lib/typescript/src/DraxContext.d.ts.map +1 -0
  83. package/lib/typescript/src/DraxHandle.d.ts +25 -0
  84. package/lib/typescript/src/DraxHandle.d.ts.map +1 -0
  85. package/lib/typescript/src/DraxHandleContext.d.ts +12 -0
  86. package/lib/typescript/src/DraxHandleContext.d.ts.map +1 -0
  87. package/lib/typescript/src/DraxList.d.ts +66 -0
  88. package/lib/typescript/src/DraxList.d.ts.map +1 -0
  89. package/lib/typescript/src/DraxProvider.d.ts +4 -0
  90. package/lib/typescript/src/DraxProvider.d.ts.map +1 -0
  91. package/lib/typescript/src/DraxScrollView.d.ts +7 -0
  92. package/lib/typescript/src/DraxScrollView.d.ts.map +1 -0
  93. package/lib/typescript/src/DraxSubprovider.d.ts +4 -0
  94. package/lib/typescript/src/DraxSubprovider.d.ts.map +1 -0
  95. package/lib/typescript/src/DraxView.d.ts +4 -0
  96. package/lib/typescript/src/DraxView.d.ts.map +1 -0
  97. package/lib/typescript/src/HoverLayer.d.ts +38 -0
  98. package/lib/typescript/src/HoverLayer.d.ts.map +1 -0
  99. package/lib/typescript/src/SortableBoardContainer.d.ts +11 -0
  100. package/lib/typescript/src/SortableBoardContainer.d.ts.map +1 -0
  101. package/lib/typescript/src/SortableBoardContext.d.ts +4 -0
  102. package/lib/typescript/src/SortableBoardContext.d.ts.map +1 -0
  103. package/lib/typescript/src/SortableContainer.d.ts +13 -0
  104. package/lib/typescript/src/SortableContainer.d.ts.map +1 -0
  105. package/lib/typescript/src/SortableItem.d.ts +14 -0
  106. package/lib/typescript/src/SortableItem.d.ts.map +1 -0
  107. package/lib/typescript/src/SortableItemContext.d.ts +37 -0
  108. package/lib/typescript/src/SortableItemContext.d.ts.map +1 -0
  109. package/lib/typescript/src/compat/detectVersion.d.ts +2 -0
  110. package/lib/typescript/src/compat/detectVersion.d.ts.map +1 -0
  111. package/lib/typescript/src/compat/index.d.ts +4 -0
  112. package/lib/typescript/src/compat/index.d.ts.map +1 -0
  113. package/lib/typescript/src/compat/types.d.ts +33 -0
  114. package/lib/typescript/src/compat/types.d.ts.map +1 -0
  115. package/lib/typescript/src/compat/useDraxPanGesture.d.ts +8 -0
  116. package/lib/typescript/src/compat/useDraxPanGesture.d.ts.map +1 -0
  117. package/lib/typescript/src/hooks/index.d.ts +3 -0
  118. package/lib/typescript/src/hooks/index.d.ts.map +1 -0
  119. package/lib/typescript/src/hooks/useCallbackDispatch.d.ts +40 -0
  120. package/lib/typescript/src/hooks/useCallbackDispatch.d.ts.map +1 -0
  121. package/lib/typescript/src/hooks/useDragGesture.d.ts +17 -0
  122. package/lib/typescript/src/hooks/useDragGesture.d.ts.map +1 -0
  123. package/lib/typescript/src/hooks/useDraxContext.d.ts +2 -0
  124. package/lib/typescript/src/hooks/useDraxContext.d.ts.map +1 -0
  125. package/{build → lib/typescript/src}/hooks/useDraxId.d.ts +1 -0
  126. package/lib/typescript/src/hooks/useDraxId.d.ts.map +1 -0
  127. package/lib/typescript/src/hooks/useDraxMethods.d.ts +13 -0
  128. package/lib/typescript/src/hooks/useDraxMethods.d.ts.map +1 -0
  129. package/lib/typescript/src/hooks/useDraxScrollHandler.d.ts +27 -0
  130. package/lib/typescript/src/hooks/useDraxScrollHandler.d.ts.map +1 -0
  131. package/lib/typescript/src/hooks/useSortableBoard.d.ts +10 -0
  132. package/lib/typescript/src/hooks/useSortableBoard.d.ts.map +1 -0
  133. package/lib/typescript/src/hooks/useSortableList.d.ts +11 -0
  134. package/lib/typescript/src/hooks/useSortableList.d.ts.map +1 -0
  135. package/lib/typescript/src/hooks/useSpatialIndex.d.ts +22 -0
  136. package/lib/typescript/src/hooks/useSpatialIndex.d.ts.map +1 -0
  137. package/lib/typescript/src/hooks/useViewStyles.d.ts +183 -0
  138. package/lib/typescript/src/hooks/useViewStyles.d.ts.map +1 -0
  139. package/lib/typescript/src/hooks/useWebScrollFreeze.d.ts +14 -0
  140. package/lib/typescript/src/hooks/useWebScrollFreeze.d.ts.map +1 -0
  141. package/lib/typescript/src/index.d.ts +25 -0
  142. package/lib/typescript/src/index.d.ts.map +1 -0
  143. package/lib/typescript/src/math.d.ts +76 -0
  144. package/lib/typescript/src/math.d.ts.map +1 -0
  145. package/{build → lib/typescript/src}/params.d.ts +13 -9
  146. package/lib/typescript/src/params.d.ts.map +1 -0
  147. package/lib/typescript/src/types.d.ts +756 -0
  148. package/lib/typescript/src/types.d.ts.map +1 -0
  149. package/package.json +164 -34
  150. package/src/DebugOverlay.tsx +140 -0
  151. package/src/Drax.ts +33 -0
  152. package/src/DraxContext.ts +8 -0
  153. package/src/DraxHandle.tsx +52 -0
  154. package/src/DraxHandleContext.ts +15 -0
  155. package/src/DraxList.tsx +181 -0
  156. package/src/DraxProvider.tsx +224 -0
  157. package/src/DraxScrollView.tsx +180 -0
  158. package/src/DraxSubprovider.tsx +22 -0
  159. package/src/DraxView.tsx +430 -0
  160. package/src/HoverLayer.tsx +167 -0
  161. package/src/SortableBoardContainer.tsx +439 -0
  162. package/src/SortableBoardContext.ts +6 -0
  163. package/src/SortableContainer.tsx +650 -0
  164. package/src/SortableItem.tsx +264 -0
  165. package/src/SortableItemContext.ts +46 -0
  166. package/src/compat/detectVersion.ts +17 -0
  167. package/src/compat/index.ts +7 -0
  168. package/src/compat/types.ts +35 -0
  169. package/src/compat/useDraxPanGesture.ts +112 -0
  170. package/src/hooks/index.ts +2 -0
  171. package/src/hooks/useCallbackDispatch.tsx +830 -0
  172. package/src/hooks/useDragGesture.ts +273 -0
  173. package/src/hooks/useDraxContext.ts +11 -0
  174. package/src/hooks/useDraxId.ts +11 -0
  175. package/src/hooks/useDraxMethods.ts +71 -0
  176. package/src/hooks/useDraxScrollHandler.ts +121 -0
  177. package/src/hooks/useSortableBoard.ts +44 -0
  178. package/src/hooks/useSortableList.ts +1063 -0
  179. package/src/hooks/useSpatialIndex.ts +336 -0
  180. package/src/hooks/useViewStyles.ts +180 -0
  181. package/src/hooks/useWebScrollFreeze.ts +60 -0
  182. package/src/index.ts +111 -0
  183. package/src/math.ts +333 -0
  184. package/src/params.ts +74 -0
  185. package/src/types.ts +933 -0
  186. package/.editorconfig +0 -15
  187. package/.eslintrc.js +0 -4
  188. package/.prettierrc +0 -16
  189. package/CHANGELOG.md +0 -270
  190. package/CODE-OF-CONDUCT.md +0 -85
  191. package/CONTRIBUTING.md +0 -15
  192. package/FUNDING.yml +0 -4
  193. package/build/AllHoverViews.d.ts +0 -0
  194. package/build/AllHoverViews.js +0 -30
  195. package/build/DraxContext.d.ts +0 -2
  196. package/build/DraxContext.js +0 -6
  197. package/build/DraxList.d.ts +0 -8
  198. package/build/DraxList.js +0 -512
  199. package/build/DraxListItem.d.ts +0 -7
  200. package/build/DraxListItem.js +0 -121
  201. package/build/DraxProvider.d.ts +0 -2
  202. package/build/DraxProvider.js +0 -704
  203. package/build/DraxScrollView.d.ts +0 -6
  204. package/build/DraxScrollView.js +0 -136
  205. package/build/DraxSubprovider.d.ts +0 -3
  206. package/build/DraxSubprovider.js +0 -18
  207. package/build/DraxView.d.ts +0 -8
  208. package/build/DraxView.js +0 -93
  209. package/build/HoverView.d.ts +0 -8
  210. package/build/HoverView.js +0 -40
  211. package/build/PanGestureDetector.d.ts +0 -3
  212. package/build/PanGestureDetector.js +0 -49
  213. package/build/hooks/index.d.ts +0 -4
  214. package/build/hooks/index.js +0 -11
  215. package/build/hooks/useContent.d.ts +0 -23
  216. package/build/hooks/useContent.js +0 -212
  217. package/build/hooks/useDraxContext.d.ts +0 -1
  218. package/build/hooks/useDraxContext.js +0 -13
  219. package/build/hooks/useDraxId.js +0 -13
  220. package/build/hooks/useDraxProtocol.d.ts +0 -5
  221. package/build/hooks/useDraxProtocol.js +0 -32
  222. package/build/hooks/useDraxRegistry.d.ts +0 -78
  223. package/build/hooks/useDraxRegistry.js +0 -714
  224. package/build/hooks/useDraxScrollHandler.d.ts +0 -25
  225. package/build/hooks/useDraxScrollHandler.js +0 -89
  226. package/build/hooks/useDraxState.d.ts +0 -10
  227. package/build/hooks/useDraxState.js +0 -132
  228. package/build/hooks/useMeasurements.d.ts +0 -9
  229. package/build/hooks/useMeasurements.js +0 -119
  230. package/build/hooks/useStatus.d.ts +0 -11
  231. package/build/hooks/useStatus.js +0 -96
  232. package/build/index.d.ts +0 -9
  233. package/build/index.js +0 -33
  234. package/build/math.d.ts +0 -22
  235. package/build/math.js +0 -68
  236. package/build/params.js +0 -27
  237. package/build/transform.d.ts +0 -11
  238. package/build/transform.js +0 -59
  239. package/build/types.d.ts +0 -807
  240. package/build/types.js +0 -46
  241. package/docs/concept.md +0 -79
  242. package/docs/images/color-drag-drop.gif +0 -0
  243. package/docs/images/deck-cards.gif +0 -0
  244. package/docs/images/drag-drop-events.jpg +0 -0
  245. package/docs/images/knight-moves.gif +0 -0
  246. package/docs/images/reorderable-list.gif +0 -0
package/src/types.ts ADDED
@@ -0,0 +1,933 @@
1
+ import type { ReactNode, RefObject } from 'react';
2
+ import type {
3
+ NativeScrollEvent,
4
+ NativeSyntheticEvent,
5
+ ScrollViewProps,
6
+ StyleProp,
7
+ ViewProps,
8
+ ViewStyle,
9
+ } from 'react-native';
10
+ import type { HostInstance } from 'react-native';
11
+ import type {
12
+ AnimatedStyle,
13
+ EntryOrExitLayoutType,
14
+ SharedValue,
15
+ } from 'react-native-reanimated';
16
+
17
+ // ─── Core Geometry Types ───────────────────────────────────────────────────
18
+
19
+ /** An xy-coordinate position value */
20
+ export interface Position {
21
+ // Index signature required for Reanimated AnimatableValue compatibility
22
+ [k: string]: number;
23
+ x: number;
24
+ y: number;
25
+ }
26
+
27
+ /** Predicate for checking if something is a Position */
28
+ export const isPosition = (something: unknown): something is Position =>
29
+ typeof something === 'object' &&
30
+ something !== null &&
31
+ 'x' in something &&
32
+ 'y' in something &&
33
+ typeof something.x === 'number' &&
34
+ typeof something.y === 'number';
35
+
36
+ /** Dimensions of a view */
37
+ export interface ViewDimensions {
38
+ width: number;
39
+ height: number;
40
+ }
41
+
42
+ /** Grid span for a sortable item (columns and rows it occupies) */
43
+ export interface GridItemSpan {
44
+ /** Number of columns this item spans. @default 1 */
45
+ colSpan: number;
46
+ /** Number of rows this item spans. @default 1 */
47
+ rowSpan: number;
48
+ }
49
+
50
+ /** Measurements of a Drax view for bounds checking purposes */
51
+ export interface DraxViewMeasurements extends Position, ViewDimensions {
52
+ /** 1 when DraxView auto-detected transform-based positioning
53
+ * (e.g., LegendList) and used visual measurement instead of Yoga layout. 0 otherwise. */
54
+ _transformDetected: number;
55
+ }
56
+
57
+ // ─── Drag Phase & Status Types ─────────────────────────────────────────────
58
+
59
+ /** Phase of a drag operation — drives all animated styles */
60
+ export type DragPhase = 'idle' | 'dragging' | 'releasing';
61
+
62
+ /** The states a dragged view can be in */
63
+ export enum DraxViewDragStatus {
64
+ Inactive,
65
+ Dragging,
66
+ Released,
67
+ }
68
+
69
+ /** The states a receiver view can be in */
70
+ export enum DraxViewReceiveStatus {
71
+ Inactive,
72
+ Receiving,
73
+ }
74
+
75
+ // ─── Collision Algorithm ────────────────────────────────────────────────────
76
+
77
+ /** Algorithm used to determine if a dragged view is over a receiver */
78
+ export type CollisionAlgorithm = 'center' | 'intersect' | 'contain';
79
+
80
+ // ─── Spatial Index (SharedValue, UI Thread) ────────────────────────────────
81
+
82
+ /** Entry in the spatial index SharedValue, accessed from worklets for hit-testing */
83
+ export interface SpatialEntry {
84
+ /** View unique identifier */
85
+ id: string;
86
+ /** Position relative to parent */
87
+ x: number;
88
+ y: number;
89
+ width: number;
90
+ height: number;
91
+ /** Index of parent in the spatial index array, -1 if root */
92
+ parentIndex: number;
93
+ /** Can this view receive drags? */
94
+ receptive: boolean;
95
+ /** Can this view monitor drags? */
96
+ monitoring: boolean;
97
+ /** Can this view be dragged? */
98
+ draggable: boolean;
99
+ /** If true, this view will not receive drags from its own children */
100
+ rejectOwnChildren: boolean;
101
+ /** Collision algorithm for receiving: 'center' (default), 'intersect', or 'contain' */
102
+ collisionAlgorithm: CollisionAlgorithm;
103
+ }
104
+
105
+ /** Result of a UI-thread hit test */
106
+ export interface HitTestResult {
107
+ receiverId: string;
108
+ monitorIds: string[];
109
+ }
110
+
111
+ // ─── Event Data Types (Public API) ─────────────────────────────────────────
112
+
113
+ /** Data about a view involved in a Drax event */
114
+ export interface DraxEventViewData {
115
+ id: string;
116
+ parentId?: string;
117
+ payload: unknown;
118
+ measurements?: DraxViewMeasurements;
119
+ }
120
+
121
+ /** Data about a dragged view involved in a Drax event */
122
+ export interface DraxEventDraggedViewData extends DraxEventViewData {
123
+ dragTranslationRatio: Position;
124
+ dragOffset: Position;
125
+ grabOffset: Position;
126
+ grabOffsetRatio: Position;
127
+ hoverPosition: Position;
128
+ }
129
+
130
+ /** Data about a receiver view involved in a Drax event */
131
+ export interface DraxEventReceiverViewData extends DraxEventViewData {
132
+ receiveOffset: Position;
133
+ receiveOffsetRatio: Position;
134
+ }
135
+
136
+ /** Data about a Drax drag event */
137
+ export interface DraxDragEventData {
138
+ dragAbsolutePosition: Position;
139
+ dragTranslation: Position;
140
+ dragged: DraxEventDraggedViewData;
141
+ }
142
+
143
+ /** Supplemental type for adding a cancelled flag */
144
+ export interface WithCancelledFlag {
145
+ cancelled: boolean;
146
+ }
147
+
148
+ /** Predicate for checking if something has a cancelled flag */
149
+ export const isWithCancelledFlag = (
150
+ something: unknown
151
+ ): something is WithCancelledFlag =>
152
+ typeof something === 'object' &&
153
+ something !== null &&
154
+ 'cancelled' in something &&
155
+ typeof something.cancelled === 'boolean';
156
+
157
+ /** Data about a Drax drag end event */
158
+ export interface DraxDragEndEventData
159
+ extends DraxDragEventData, WithCancelledFlag {}
160
+
161
+ /** Data about a Drax drag event that involves a receiver */
162
+ export interface DraxDragWithReceiverEventData extends DraxDragEventData {
163
+ receiver: DraxEventReceiverViewData;
164
+ }
165
+
166
+ /** Data about a Drax drag/receive end event */
167
+ export interface DraxDragWithReceiverEndEventData
168
+ extends DraxDragWithReceiverEventData, WithCancelledFlag {}
169
+
170
+ /** Data about a Drax monitor event */
171
+ export interface DraxMonitorEventData extends DraxDragEventData {
172
+ receiver?: DraxEventReceiverViewData;
173
+ monitorOffset: Position;
174
+ monitorOffsetRatio: Position;
175
+ }
176
+
177
+ /** Data about a Drax monitor drag end event */
178
+ export interface DraxMonitorEndEventData
179
+ extends DraxMonitorEventData, WithCancelledFlag {}
180
+
181
+ /** Data about a Drax monitor drag-drop event */
182
+ export interface DraxMonitorDragDropEventData extends Required<DraxMonitorEventData> {}
183
+
184
+ // ─── Snap Types ────────────────────────────────────────────────────────────
185
+
186
+ /** Preset values for specifying snap targets without a Position */
187
+ export enum DraxSnapbackTargetPreset {
188
+ Default,
189
+ None,
190
+ }
191
+
192
+ /** Target for snap hover view release animation: none, default, or specified Position */
193
+ export type DraxSnapbackTarget = DraxSnapbackTargetPreset | Position;
194
+
195
+ /** Response type for drag end callbacks, allowing override of default release snap behavior */
196
+ export type DraxProtocolDragEndResponse = void | DraxSnapbackTarget;
197
+
198
+ /** Data about a Drax snap, used for custom animations */
199
+ export interface DraxSnapData {
200
+ hoverPosition: SharedValue<Position>;
201
+ toValue: Position;
202
+ delay: number;
203
+ duration: number;
204
+ scrollPosition?: SharedValue<Position>;
205
+ finishedCallback: (finished: boolean) => void;
206
+ }
207
+
208
+ /** Data passed to onSnapEnd and onReceiveSnapEnd callbacks */
209
+ export interface DraxSnapEndEventData {
210
+ dragged: { id: string; parentId?: string; payload: unknown };
211
+ receiver?: { id: string; parentId?: string; payload: unknown };
212
+ }
213
+
214
+ // ─── Render Content Props ──────────────────────────────────────────────────
215
+
216
+ /** Simplified view state for render content props */
217
+ export interface DraxViewState {
218
+ dragStatus: DraxViewDragStatus;
219
+ receiveStatus: DraxViewReceiveStatus;
220
+ dragAbsolutePosition?: Position;
221
+ dragTranslation?: Position;
222
+ dragTranslationRatio?: Position;
223
+ dragOffset?: Position;
224
+ grabOffset?: Position;
225
+ grabOffsetRatio?: Position;
226
+ draggingOverReceiver?: DraxEventViewData;
227
+ receiveOffset?: Position;
228
+ receiveOffsetRatio?: Position;
229
+ receivingDrag?: DraxEventViewData;
230
+ }
231
+
232
+ /** Tracking status indicating whether anything is being dragged/received */
233
+ export interface DraxTrackingStatus {
234
+ dragging: boolean;
235
+ receiving: boolean;
236
+ }
237
+
238
+ /** Props provided to a render function for a Drax view */
239
+ export interface DraxRenderContentProps {
240
+ viewState?: DraxViewState;
241
+ trackingStatus?: DraxTrackingStatus;
242
+ hover: boolean;
243
+ children: ReactNode;
244
+ dimensions?: ViewDimensions;
245
+ }
246
+
247
+ /** Props provided to a render function for a hovering copy of a Drax view */
248
+ export interface DraxRenderHoverContentProps extends DraxRenderContentProps {}
249
+
250
+ // ─── Style Types ───────────────────────────────────────────────────────────
251
+
252
+ /** Style prop for DraxView drag/receive states (flattened for worklets) */
253
+ export type DraxStyleProp = StyleProp<ViewStyle>;
254
+
255
+ /** Style prop for hover views (supports animated styles) */
256
+ export type AnimatedViewStylePropWithoutLayout =
257
+ | StyleProp<ViewStyle>
258
+ | StyleProp<AnimatedStyle<StyleProp<ViewStyle>>>;
259
+
260
+ /** Style-related props for a Drax view */
261
+ export interface DraxViewStyleProps {
262
+ style?: DraxStyleProp;
263
+ dragInactiveStyle?: DraxStyleProp;
264
+ draggingStyle?: DraxStyleProp;
265
+ draggingWithReceiverStyle?: DraxStyleProp;
266
+ draggingWithoutReceiverStyle?: DraxStyleProp;
267
+ dragReleasedStyle?: DraxStyleProp;
268
+ hoverStyle?: AnimatedViewStylePropWithoutLayout;
269
+ hoverDraggingStyle?: AnimatedViewStylePropWithoutLayout;
270
+ hoverDraggingWithReceiverStyle?: AnimatedViewStylePropWithoutLayout;
271
+ hoverDraggingWithoutReceiverStyle?: AnimatedViewStylePropWithoutLayout;
272
+ hoverDragReleasedStyle?: AnimatedViewStylePropWithoutLayout;
273
+ receiverInactiveStyle?: DraxStyleProp;
274
+ receivingStyle?: DraxStyleProp;
275
+ otherDraggingStyle?: DraxStyleProp;
276
+ otherDraggingWithReceiverStyle?: DraxStyleProp;
277
+ otherDraggingWithoutReceiverStyle?: DraxStyleProp;
278
+ }
279
+
280
+ // ─── Custom render functions ───────────────────────────────────────────────
281
+
282
+ /** Custom render function for content of a DraxView */
283
+ export interface DraxViewRenderContent {
284
+ (props: DraxRenderContentProps): ReactNode;
285
+ }
286
+
287
+ /** Custom render function for content of hovering copy of a DraxView */
288
+ export interface DraxViewRenderHoverContent {
289
+ (props: DraxRenderHoverContentProps): ReactNode;
290
+ }
291
+
292
+ // ─── View Props ────────────────────────────────────────────────────────────
293
+
294
+ /** Props for a DraxView */
295
+ export interface DraxViewProps
296
+ extends Omit<ViewProps, 'style'>, DraxViewStyleProps {
297
+ /** Custom render function for content of this view */
298
+ renderContent?: DraxViewRenderContent;
299
+ /** Custom render function for content of hovering copy of this view, defaults to renderContent */
300
+ renderHoverContent?: DraxViewRenderHoverContent;
301
+ /** If true, do not render hover view copies for this view when dragging */
302
+ noHover?: boolean;
303
+ /** For external registration of this view, to access internal methods */
304
+ registration?: (registration: DraxViewRegistration | undefined) => void;
305
+ /** For receiving view measurements externally */
306
+ onMeasure?: DraxViewMeasurementHandler;
307
+ /** Unique Drax view id, auto-generated if omitted */
308
+ id?: string;
309
+ /** Drax parent view, if nesting */
310
+ parent?: DraxParentView;
311
+ /** If true, treat this view as a Drax parent view for nested children */
312
+ isParent?: boolean;
313
+ /** Used internally - The view's scroll position, if it is a scrollable parent view */
314
+ scrollPosition?: SharedValue<Position>;
315
+ /** Time in milliseconds view needs to be pressed before drag starts */
316
+ longPressDelay?: number;
317
+ /** Cancel drag activation if finger moves more than this distance (px).
318
+ * Prevents accidental drags when the user is trying to scroll.
319
+ * Can be a number (symmetric) or [min, max] tuple per axis. */
320
+ dragActivationFailOffset?: number;
321
+
322
+ /** Hint that this view is inside a horizontal scroll container.
323
+ * On mobile web, sets `touch-action: pan-x` so the browser allows
324
+ * native horizontal scrolling before the long-press activates drag.
325
+ * Without this, items in horizontal lists default to `pan-y` which
326
+ * blocks horizontal scrolling on touch devices. */
327
+ scrollHorizontal?: boolean;
328
+
329
+ // ─── Callback props (formerly in DraxProtocol) ─────────────────────
330
+
331
+ /** A function that can be used to conditionally enable or disable receiving */
332
+ dynamicReceptiveCallback?: (data: {
333
+ targetId: string;
334
+ targetMeasurements: DraxViewMeasurements;
335
+ draggedId: string;
336
+ draggedPayload: unknown;
337
+ }) => boolean;
338
+
339
+ /** Simpler convenience prop for conditional drop acceptance based on payload */
340
+ acceptsDrag?: (draggedPayload: unknown) => boolean;
341
+ /** Maximum number of items this view can receive. Drops are auto-rejected
342
+ * when at capacity. Requires DraxProvider to track dropped items centrally. */
343
+ capacity?: number;
344
+
345
+ /** Called in the dragged view when a drag action begins */
346
+ onDragStart?: (data: DraxDragEventData) => void;
347
+ /** Called in the dragged view repeatedly while dragged, not over any receiver */
348
+ onDrag?: (data: DraxDragEventData) => void;
349
+ /** Called in the dragged view when initially dragged over a new receiver */
350
+ onDragEnter?: (data: DraxDragWithReceiverEventData) => void;
351
+ /** Called in the dragged view repeatedly while dragged over a receiver */
352
+ onDragOver?: (data: DraxDragWithReceiverEventData) => void;
353
+ /** Called in the dragged view when dragged off of a receiver */
354
+ onDragExit?: (data: DraxDragWithReceiverEventData) => void;
355
+ /** Called in the dragged view when drag ends not over any receiver or is cancelled */
356
+ onDragEnd?: (data: DraxDragEndEventData) => DraxProtocolDragEndResponse;
357
+ /** Called in the dragged view when drag ends over a receiver */
358
+ onDragDrop?: (
359
+ data: DraxDragWithReceiverEventData
360
+ ) => DraxProtocolDragEndResponse;
361
+ /** Called in the dragged view when drag release snap ends */
362
+ onSnapEnd?: (data: DraxSnapEndEventData) => void;
363
+ /** Called in the receiver view when drag release snap ends */
364
+ onReceiveSnapEnd?: (data: DraxSnapEndEventData) => void;
365
+ /** Called in the receiver view each time an item is initially dragged over it */
366
+ onReceiveDragEnter?: (data: DraxDragWithReceiverEventData) => void;
367
+ /** Called in the receiver view repeatedly while an item is dragged over it */
368
+ onReceiveDragOver?: (data: DraxDragWithReceiverEventData) => void;
369
+ /** Called in the receiver view when item is dragged off of it or drag is cancelled */
370
+ onReceiveDragExit?: (data: DraxDragWithReceiverEndEventData) => void;
371
+ /** Called in the receiver view when drag ends over it */
372
+ onReceiveDragDrop?: (
373
+ data: DraxDragWithReceiverEventData
374
+ ) => DraxProtocolDragEndResponse;
375
+ /** Called in the monitor view when a drag action begins over it */
376
+ onMonitorDragStart?: (data: DraxMonitorEventData) => void;
377
+ /** Called in the monitor view each time an item is initially dragged over it */
378
+ onMonitorDragEnter?: (data: DraxMonitorEventData) => void;
379
+ /** Called in the monitor view repeatedly while an item is dragged over it */
380
+ onMonitorDragOver?: (data: DraxMonitorEventData) => void;
381
+ /** Called in the monitor view when item is dragged off of it */
382
+ onMonitorDragExit?: (data: DraxMonitorEventData) => void;
383
+ /** Called in the monitor view when drag ends over it while not over any receiver or drag is cancelled */
384
+ onMonitorDragEnd?: (
385
+ data: DraxMonitorEndEventData
386
+ ) => DraxProtocolDragEndResponse;
387
+ /** Called in the monitor view when drag ends over it while over a receiver */
388
+ onMonitorDragDrop?: (
389
+ data: DraxMonitorDragDropEventData
390
+ ) => DraxProtocolDragEndResponse;
391
+
392
+ /** Whether or not to animate hover view snap after drag release, defaults to true */
393
+ animateSnap?: boolean;
394
+ /** Delay in ms before hover view snap begins after drag is released */
395
+ snapDelay?: number;
396
+ /** Duration in ms for hover view snap to complete */
397
+ snapDuration?: number;
398
+ /** Function returning custom hover view snap animation */
399
+ snapAnimator?: (data: DraxSnapData) => void;
400
+
401
+ /** Payload that will be delivered to receiver views when this view is dragged; overrides `payload` */
402
+ dragPayload?: unknown;
403
+ /** Payload that will be delivered to dragged views when this view receives them; overrides `payload` */
404
+ receiverPayload?: unknown;
405
+ /** Convenience prop to provide one value for both `dragPayload` and `receiverPayload` */
406
+ payload?: unknown;
407
+
408
+ /** Whether the view can be dragged */
409
+ draggable?: boolean;
410
+ /** Whether the view can receive drags */
411
+ receptive?: boolean;
412
+ /** Whether the view can monitor drags */
413
+ monitoring?: boolean;
414
+ /** If true, this view will not receive drags from its own children */
415
+ rejectOwnChildren?: boolean;
416
+ /** @deprecated No longer needed — hover measurements are handled automatically */
417
+ disableHoverViewMeasurementsOnLayout?: boolean;
418
+ /** If true, lock drag's x-position */
419
+ lockDragXPosition?: boolean;
420
+ /** If true, lock drag's y position */
421
+ lockDragYPosition?: boolean;
422
+ /** When true, drag is only activated via a descendant DraxHandle component */
423
+ dragHandle?: boolean;
424
+ /** Collision algorithm for receiving drags: 'center' (default), 'intersect', or 'contain' */
425
+ collisionAlgorithm?: CollisionAlgorithm;
426
+ /** Ref to a View that constrains the drag area. The dragged view will be clamped within these bounds. */
427
+ dragBoundsRef?: RefObject<any>;
428
+ }
429
+
430
+ // ─── View Registry (JS Thread) ─────────────────────────────────────────────
431
+
432
+ /** Entry in the JS-thread view registry Map */
433
+ export interface ViewRegistryEntry {
434
+ id: string;
435
+ parentId?: string;
436
+ /** Index in the spatialIndexSV array */
437
+ spatialIndex: number;
438
+ /** Scroll position SharedValue, for scrollable parent views */
439
+ scrollPosition?: SharedValue<Position>;
440
+ /** Current measurements */
441
+ measurements?: DraxViewMeasurements;
442
+ /** All props from DraxView (callbacks, styles, payload, etc.) */
443
+ props: DraxViewProps;
444
+ }
445
+
446
+ // ─── Context Value ─────────────────────────────────────────────────────────
447
+
448
+ /** Context value used internally by Drax provider */
449
+ export interface DraxContextValue {
450
+ // ── Split SharedValues (by update frequency) ───────────────────────
451
+ /** Changes ~2x per drag. Read by all DraxView useAnimatedStyle. */
452
+ draggedIdSV: SharedValue<string>;
453
+ /** Changes ~3-5x per drag. Read by all DraxView useAnimatedStyle. */
454
+ receiverIdSV: SharedValue<string>;
455
+ /** Changes ~3x per drag. Read by all DraxView useAnimatedStyle. */
456
+ dragPhaseSV: SharedValue<DragPhase>;
457
+ /** Changes every frame during drag. Read ONLY by HoverLayer. */
458
+ hoverPositionSV: SharedValue<Position>;
459
+ /** Changes every frame during drag. Used by gesture worklet for hit-testing. */
460
+ dragAbsolutePositionSV: SharedValue<Position>;
461
+ /** ID of the most recently rejected receiver (cleared when drag leaves its bounds).
462
+ * Read by gesture worklet to skip re-detecting the same rejected receiver. */
463
+ rejectedReceiverIdSV: SharedValue<string>;
464
+ /** Changes on view mount/layout. Read by gesture worklet for hit-testing. */
465
+ spatialIndexSV: SharedValue<SpatialEntry[]>;
466
+ /** Changes during scroll. Indexed parallel to spatialIndex. */
467
+ scrollOffsetsSV: SharedValue<Position[]>;
468
+ /** Set once on drag start. */
469
+ grabOffsetSV: SharedValue<Position>;
470
+ /** Absolute position where drag started. */
471
+ startPositionSV: SharedValue<Position>;
472
+ /** Screen offset of the DraxProvider root view (for coordinate conversion). */
473
+ rootOffsetSV: SharedValue<Position>;
474
+ /** True after hover content is committed to DOM (set in HoverLayer useLayoutEffect).
475
+ * False after snap completes. Used by SortableItem for blink-free visibility. */
476
+ hoverReadySV: SharedValue<boolean>;
477
+ /** Set to true by SortableContainer.finalizeDrag when a reorder commit is in-flight.
478
+ * Checked by onSnapComplete to skip immediate hover clearing — the clearing is
479
+ * deferred to useSortableList's useLayoutEffect (after FlatList re-render). */
480
+ hoverClearDeferredRef: { current: boolean };
481
+ /** Animated hover content dimensions for cross-container transfer.
482
+ * x = width, y = height. {0,0} = no constraint (natural size). */
483
+ hoverDimsSV: SharedValue<Position>;
484
+
485
+ // ── Registry methods (JS thread) ───────────────────────────────────
486
+ registerView: (payload: RegisterViewPayload) => void;
487
+ unregisterView: (id: string) => void;
488
+ updateMeasurements: (id: string, measurements: DraxViewMeasurements) => void;
489
+ updateScrollOffset: (id: string, offset: Position) => void;
490
+ updateViewProps: (id: string, props: DraxViewProps) => void;
491
+ getViewEntry: (id: string) => ViewRegistryEntry | undefined;
492
+
493
+ // ── Callback dispatch (JS thread, called via runOnJS from gesture) ─
494
+ handleDragStart: (
495
+ draggedId: string,
496
+ absolutePosition: Position,
497
+ grabOffset: Position
498
+ ) => void;
499
+ handleReceiverChange: (
500
+ oldReceiverId: string,
501
+ newReceiverId: string,
502
+ absolutePosition: Position,
503
+ monitorIds?: string[]
504
+ ) => void;
505
+ handleDragEnd: (
506
+ draggedId: string,
507
+ receiverId: string,
508
+ cancelled: boolean,
509
+ finalMonitorIds?: string[]
510
+ ) => void;
511
+
512
+ // ── Hover content ──────────────────────────────────────────────────
513
+ setHoverContent: (content: ReactNode | null) => void;
514
+
515
+ // ── Dropped items tracking ─────────────────────────────────────────
516
+ /** Map of receiverId → Set of draggedIds that have been dropped on it */
517
+ droppedItemsRef: RefObject<Map<string, Set<string>>>;
518
+
519
+ // ── View refs ──────────────────────────────────────────────────────
520
+ rootViewRef: { current: HostInstance | null };
521
+ parent?: DraxParentView;
522
+ }
523
+
524
+ /** Payload for registering a Drax view */
525
+ export interface RegisterViewPayload {
526
+ id: string;
527
+ parentId?: string;
528
+ scrollPosition?: SharedValue<Position>;
529
+ props: DraxViewProps;
530
+ }
531
+
532
+ // ─── Provider / Subprovider Props ──────────────────────────────────────────
533
+
534
+ /** Event data for provider-level drag callbacks */
535
+ export interface DraxProviderDragEvent {
536
+ draggedId: string;
537
+ receiverId?: string;
538
+ position: Position;
539
+ }
540
+
541
+ /** Optional props that can be passed to a DraxProvider */
542
+ export interface DraxProviderProps {
543
+ style?: StyleProp<ViewStyle>;
544
+ debug?: boolean;
545
+ /** Called when any drag starts */
546
+ onDragStart?: (event: DraxProviderDragEvent) => void;
547
+ /** Called on every gesture update during any drag */
548
+ onDrag?: (event: DraxProviderDragEvent) => void;
549
+ /** Called when any drag ends (drop or cancel) */
550
+ onDragEnd?: (event: DraxProviderDragEvent & { cancelled: boolean }) => void;
551
+ children?: ReactNode;
552
+ }
553
+
554
+ /** Props that are passed to a DraxSubprovider */
555
+ export interface DraxSubproviderProps {
556
+ parent: DraxParentView;
557
+ }
558
+
559
+ // ─── External Registration ─────────────────────────────────────────────────
560
+
561
+ /** Methods provided by a DraxView when registered externally */
562
+ export interface DraxViewRegistration {
563
+ id: string;
564
+ measure: (measurementHandler?: DraxViewMeasurementHandler) => void;
565
+ }
566
+
567
+ /** Information about the parent of a nested DraxView */
568
+ export interface DraxParentView {
569
+ id: string;
570
+ /** Any ref-like object with a .current holding a native view instance.
571
+ * Accepts both React.RefObject and Reanimated.AnimatedRef. */
572
+ viewRef: { current: any };
573
+ /** When true, measureLayout returns content-relative positions on native
574
+ * (scroll offset should NOT be added). */
575
+ isScrollContainer?: boolean;
576
+ }
577
+
578
+ /** Function that receives a Drax view measurement */
579
+ export interface DraxViewMeasurementHandler {
580
+ (measurements: DraxViewMeasurements | undefined): void;
581
+ }
582
+
583
+ // ─── Auto-scroll Types ─────────────────────────────────────────────────────
584
+
585
+ /** Auto-scroll direction used internally by DraxScrollView and DraxList */
586
+ export enum AutoScrollDirection {
587
+ Back = -1,
588
+ None = 0,
589
+ Forward = 1,
590
+ }
591
+
592
+ /** Auto-scroll state used internally by DraxScrollView */
593
+ export interface AutoScrollState {
594
+ x: AutoScrollDirection;
595
+ y: AutoScrollDirection;
596
+ }
597
+
598
+ /** Props for auto-scroll options */
599
+ export interface DraxAutoScrollProps {
600
+ autoScrollIntervalLength?: number;
601
+ autoScrollJumpRatio?: number;
602
+ autoScrollBackThreshold?: number;
603
+ autoScrollForwardThreshold?: number;
604
+ }
605
+
606
+ // ─── ScrollView Props ──────────────────────────────────────────────────────
607
+
608
+ /** Props for a DraxScrollView */
609
+ export interface DraxScrollViewProps
610
+ extends ScrollViewProps, DraxAutoScrollProps {
611
+ id?: string;
612
+ }
613
+
614
+ // ─── Sortable Types (List-Agnostic) ─────────────────────────────────────────
615
+
616
+ /** Reorder strategy for sortable lists */
617
+ export type SortableReorderStrategy = 'insert' | 'swap';
618
+
619
+ /** Named animation preset for sortable item shift animations */
620
+ export type SortableAnimationPreset = 'default' | 'spring' | 'gentle' | 'snappy' | 'none';
621
+
622
+ /** Custom animation configuration for sortable item shifts */
623
+ export interface SortableAnimationCustomConfig {
624
+ /** Duration in ms for timing-based animations. Ignored when useSpring is true. @default 200 */
625
+ shiftDuration?: number;
626
+ /** Use spring physics instead of timing. @default false */
627
+ useSpring?: boolean;
628
+ /** Spring damping. @default 15 */
629
+ springDamping?: number;
630
+ /** Spring stiffness. @default 150 */
631
+ springStiffness?: number;
632
+ /** Spring mass. @default 1 */
633
+ springMass?: number;
634
+ }
635
+
636
+ /** Animation configuration: a preset name or custom config object */
637
+ export type SortableAnimationConfig = SortableAnimationPreset | SortableAnimationCustomConfig;
638
+
639
+ /** Measurement for a single sortable item, keyed by item key */
640
+ export interface SortableItemMeasurement {
641
+ x: number;
642
+ y: number;
643
+ width: number;
644
+ height: number;
645
+ key: string;
646
+ /** Current display index (updated on reorder) */
647
+ index: number;
648
+ /** Scroll offset at the time this measurement was taken */
649
+ scrollAtMeasure: Position;
650
+ }
651
+
652
+ /** Internal payload attached to each SortableItem's DraxView */
653
+ export interface SortableItemPayload {
654
+ index: number;
655
+ originalIndex: number;
656
+ }
657
+
658
+ /** Type guard for SortableItemPayload */
659
+ export function isSortableItemPayload(value: unknown): value is SortableItemPayload {
660
+ return (
661
+ typeof value === 'object' &&
662
+ value !== null &&
663
+ 'index' in value &&
664
+ 'originalIndex' in value &&
665
+ typeof value.index === 'number' &&
666
+ typeof value.originalIndex === 'number'
667
+ );
668
+ }
669
+
670
+ /** Event data for sortable drag start */
671
+ export interface SortableDragStartEvent<T> {
672
+ index: number;
673
+ item: T;
674
+ }
675
+
676
+ /** Event data for sortable drag position change */
677
+ export interface SortableDragPositionChangeEvent<T> {
678
+ index: number;
679
+ item: T;
680
+ toIndex: number | undefined;
681
+ previousIndex: number | undefined;
682
+ }
683
+
684
+ /** Event data for sortable drag end */
685
+ export interface SortableDragEndEvent<T> {
686
+ index: number;
687
+ item: T;
688
+ toIndex: number | undefined;
689
+ cancelled: boolean;
690
+ }
691
+
692
+ /** Event data for sortable item reorder */
693
+ export interface SortableReorderEvent<T> {
694
+ data: T[];
695
+ fromIndex: number;
696
+ toIndex: number;
697
+ fromItem: T;
698
+ toItem: T;
699
+ isExternalDrag: boolean;
700
+ }
701
+
702
+ /** Props for rendering a drop indicator in a sortable container */
703
+ export interface DropIndicatorProps {
704
+ /** Whether the indicator should be visible */
705
+ visible: boolean;
706
+ /** Whether the list is horizontal */
707
+ horizontal: boolean;
708
+ }
709
+
710
+ /** Options for useSortableList hook */
711
+ export interface UseSortableListOptions<T> {
712
+ /** Optional explicit DraxView id for the container */
713
+ id?: string;
714
+ data: T[];
715
+ keyExtractor: (item: T, index: number) => string;
716
+ onReorder: (event: SortableReorderEvent<T>) => void;
717
+ /** List layout direction. @default false */
718
+ horizontal?: boolean;
719
+ /** Number of columns for grid layout. @default 1 */
720
+ numColumns?: number;
721
+ /** Reorder strategy. @default 'insert' */
722
+ reorderStrategy?: SortableReorderStrategy;
723
+ /** Long press delay before drag starts in ms. @default 250 */
724
+ longPressDelay?: number;
725
+ /** Lock item drags to the list's main axis. @default false */
726
+ lockToMainAxis?: boolean;
727
+ /** Auto-scroll jump distance as fraction of container size. @default 0.2 */
728
+ autoScrollJumpRatio?: number;
729
+ /** Drag position threshold for back auto-scroll. @default 0.1 */
730
+ autoScrollBackThreshold?: number;
731
+ /** Drag position threshold for forward auto-scroll. @default 0.9 */
732
+ autoScrollForwardThreshold?: number;
733
+ /** Animation config for item shift animations. @default 'default' */
734
+ animationConfig?: SortableAnimationConfig;
735
+ /** Returns the grid span for an item. Enables non-uniform grid layout
736
+ * where items can span multiple columns and/or rows.
737
+ * Only used when numColumns > 1. */
738
+ getItemSpan?: (item: T, index: number) => GridItemSpan;
739
+ /** Style applied to all non-dragged items while a drag is active.
740
+ * Use for dimming/scaling inactive items (e.g., `{ opacity: 0.5 }`). */
741
+ inactiveItemStyle?: ViewStyle;
742
+ /** Reanimated layout animation for items entering the list (e.g., `FadeIn`). */
743
+ itemEntering?: EntryOrExitLayoutType;
744
+ /** Reanimated layout animation for items exiting the list (e.g., `FadeOut`). */
745
+ itemExiting?: EntryOrExitLayoutType;
746
+ /** Callback when drag starts */
747
+ onDragStart?: (event: SortableDragStartEvent<T>) => void;
748
+ /** Callback when drag position (index) changes */
749
+ onDragPositionChange?: (event: SortableDragPositionChangeEvent<T>) => void;
750
+ /** Callback when drag ends */
751
+ onDragEnd?: (event: SortableDragEndEvent<T>) => void;
752
+ }
753
+
754
+ /** Handle returned by useSortableList — pass to SortableContainer and SortableItem */
755
+ export interface SortableListHandle<T> {
756
+ /** Reordered data to pass to your list component */
757
+ data: T[];
758
+ /** Wire to your list's onScroll prop */
759
+ onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
760
+ /** Wire to your list's onContentSizeChange prop */
761
+ onContentSizeChange: (width: number, height: number) => void;
762
+ /** Stable index-based keyExtractor — prevents FlatList cell unmounting on reorder */
763
+ stableKeyExtractor: (item: T, index: number) => string;
764
+ /** Internal state — consumed by SortableContainer and SortableItem */
765
+ _internal: SortableListInternal<T>;
766
+ }
767
+
768
+ /** Internal state of the sortable list (not part of public API contract) */
769
+ export interface SortableListInternal<T> {
770
+ id: string;
771
+ horizontal: boolean;
772
+ numColumns: number;
773
+ reorderStrategy: SortableReorderStrategy;
774
+ longPressDelay: number;
775
+ lockToMainAxis: boolean;
776
+ animationConfig: SortableAnimationConfig;
777
+ /** Returns the grid span for an item (non-uniform grid layout) */
778
+ getItemSpan?: (item: T, index: number) => GridItemSpan;
779
+ inactiveItemStyle?: ViewStyle;
780
+ itemEntering?: EntryOrExitLayoutType;
781
+ itemExiting?: EntryOrExitLayoutType;
782
+ /** Set of item keys that are fixed (cannot be dragged or displaced) */
783
+ fixedKeys: RefObject<Set<string>>;
784
+ draggedItem: SharedValue<number | undefined>;
785
+ itemMeasurements: RefObject<Map<string, SortableItemMeasurement>>;
786
+ originalIndexes: number[];
787
+ keyExtractor: (item: T, index: number) => string;
788
+ data: T[];
789
+ rawData: T[];
790
+ /** Move the dragged item to a new display index (live reorder during drag) */
791
+ moveDraggedItem: (toDisplayIndex: number) => void;
792
+ /** Get the snapback target for the dragged item's current position */
793
+ getSnapbackTarget: () => DraxSnapbackTarget;
794
+ setDraggedItem: (index: number) => void;
795
+ resetDraggedItem: () => void;
796
+ scrollPosition: SharedValue<Position>;
797
+ containerMeasurementsRef: RefObject<DraxViewMeasurements | undefined>;
798
+ contentSizeRef: RefObject<Position | undefined>;
799
+ autoScrollJumpRatio: number;
800
+ autoScrollBackThreshold: number;
801
+ autoScrollForwardThreshold: number;
802
+ /** Callbacks from options, stored for SortableContainer to invoke */
803
+ onDragStart?: (event: SortableDragStartEvent<T>) => void;
804
+ onDragPositionChange?: (event: SortableDragPositionChangeEvent<T>) => void;
805
+ onDragEnd?: (event: SortableDragEndEvent<T>) => void;
806
+ onReorder: (event: SortableReorderEvent<T>) => void;
807
+ getMeasurementByOriginalIndex: (originalIndex: number) => SortableItemMeasurement | undefined;
808
+ /** Position of the drop indicator (animated) */
809
+ dropTargetPositionSV: SharedValue<Position>;
810
+ /** Whether the drop indicator is visible (animated) */
811
+ dropTargetVisibleSV: SharedValue<boolean>;
812
+ /** Called by SortableItem's onSnapEnd to finalize the drag.
813
+ * Stored as a ref so the latest finalizeDrag is always called,
814
+ * even if SortableItem has a stale _internal reference. */
815
+ onItemSnapEnd?: () => void;
816
+ /** Current display index of the dragged item (updated during live reorder) */
817
+ draggedDisplayIndexRef: RefObject<number | undefined>;
818
+ /** Original display index where the drag started */
819
+ dragStartIndexRef: RefObject<number | undefined>;
820
+ /** Per-item shift transforms keyed by item key (UI thread) */
821
+ shiftsRef: SharedValue<Record<string, Position>>;
822
+ /** When true, SortableItem clears shift transforms instantly (no animation) */
823
+ instantClearSV: SharedValue<boolean>;
824
+ /** When false, SortableItem ignores shifts entirely (treats as 0).
825
+ * Set to false SYNCHRONOUSLY in useLayoutEffect when rawData changes,
826
+ * so the animated style reads it in the same UI frame as the Fabric commit.
827
+ * This prevents the 1-frame blink where cells show new content but stale shifts. */
828
+ shiftsValidSV: SharedValue<boolean>;
829
+ /** Initialize pending order from current originalIndexes at drag start */
830
+ initPendingOrder: () => void;
831
+ /** Store committed visual order after drag (permanent shifts, no data change) */
832
+ commitVisualOrder: () => void;
833
+ /** Flush permanent shifts: sync stableData to rawData and clear shifts.
834
+ * Restores touch hit testing after permanent shifts. */
835
+ flushVisualOrder: () => void;
836
+ /** Compute shifts for a given order. Returns undefined if measurements missing. */
837
+ computeShiftsForOrder: (
838
+ order: number[],
839
+ skipIndex?: number,
840
+ phantom?: SortablePhantomSlot,
841
+ ) => Record<string, Position> | undefined;
842
+ /** Committed visual order from last completed drag (indices into rawData) */
843
+ committedOrderRef: RefObject<number[]>;
844
+ /** Pending order ref (indices into rawData) */
845
+ pendingOrderRef: RefObject<number[]>;
846
+ /** Cancel drag without reorder — reverts to committed shifts */
847
+ cancelDrag: () => void;
848
+ /** Compute target display index from a container-local content position */
849
+ getSlotFromPosition: (contentPos: Position) => number;
850
+ /** Current phantom slot (cross-container drag) */
851
+ phantomRef: RefObject<SortablePhantomSlot | undefined>;
852
+ /** Reserve space for an incoming item at the given display index */
853
+ setPhantomSlot: (atDisplayIndex: number, width: number, height: number) => void;
854
+ /** Remove phantom slot, items shift back */
855
+ clearPhantomSlot: () => void;
856
+ /** Remove the dragged item from pending order — items close the gap */
857
+ ejectDraggedItem: () => void;
858
+ /** Re-add a previously ejected item into pending order at the given display index */
859
+ reinjectDraggedItem: (displayIndex: number, originalIndex: number) => void;
860
+ /** Get snap target position for the current phantom slot */
861
+ getPhantomSnapTarget: () => DraxSnapbackTarget;
862
+ /** Off-screen shifts for transferred items (persist across shift recalculations) */
863
+ ghostShiftsRef: RefObject<Record<string, Position>>;
864
+ /** Committed shifts from last completed drag (for cancel revert) */
865
+ committedShiftsRef: RefObject<Record<string, Position>>;
866
+ /** When true, the next useLayoutEffect RESET skips sync shiftsValidSV=false */
867
+ skipShiftsInvalidationRef: RefObject<boolean>;
868
+ }
869
+
870
+ // ─── Board Types (Cross-Container Sortable) ──────────────────────────────
871
+
872
+ /** Phantom slot for cross-container drag: virtual space in target column */
873
+ export interface SortablePhantomSlot {
874
+ atDisplayIndex: number;
875
+ width: number;
876
+ height: number;
877
+ }
878
+
879
+ /** Event data for cross-container item transfer */
880
+ export interface SortableBoardTransferEvent<TItem> {
881
+ item: TItem;
882
+ fromContainerId: string;
883
+ fromIndex: number;
884
+ toContainerId: string;
885
+ toIndex: number;
886
+ }
887
+
888
+ /** Options for useSortableBoard hook */
889
+ export interface UseSortableBoardOptions<TItem> {
890
+ keyExtractor: (item: TItem) => string;
891
+ onTransfer: (event: SortableBoardTransferEvent<TItem>) => void;
892
+ }
893
+
894
+ /** Handle returned by useSortableBoard — pass to SortableBoardContainer */
895
+ export interface SortableBoardHandle<TItem> {
896
+ _internal: SortableBoardInternal<TItem>;
897
+ }
898
+
899
+ /** Transfer state during cross-container drag */
900
+ export interface SortableBoardTransferState {
901
+ sourceId: string;
902
+ sourceOriginalIndex: number;
903
+ itemKey: string;
904
+ itemDimensions: ViewDimensions;
905
+ dragStartIndex: number;
906
+ targetId?: string;
907
+ targetSlot?: number;
908
+ }
909
+
910
+ /** Internal state of the sortable board (not part of public API contract) */
911
+ export interface SortableBoardInternal<TItem> {
912
+ keyExtractor: (item: TItem) => string;
913
+ onTransfer: (event: SortableBoardTransferEvent<TItem>) => void;
914
+ columns: Map<string, SortableListInternal<unknown>>;
915
+ registerColumn: (id: string, internal: SortableListInternal<unknown>) => void;
916
+ unregisterColumn: (id: string) => void;
917
+ transferState: RefObject<SortableBoardTransferState | undefined>;
918
+ /** Set by SortableBoardContainer — handles cross-container transfer finalization */
919
+ finalizeTransfer?: () => void;
920
+ }
921
+
922
+ /** Context value for board coordination.
923
+ * Uses Pick to avoid generic variance issues — consumers only need
924
+ * transferState and finalizeTransfer, not typed item fields. */
925
+ export interface SortableBoardContextValue {
926
+ registerColumn: (id: string, internal: SortableListInternal<unknown>) => void;
927
+ unregisterColumn: (id: string) => void;
928
+ boardInternal: SortableBoardInternal<unknown>;
929
+ }
930
+
931
+ // ─── Utility Types ─────────────────────────────────────────────────────────
932
+
933
+