@shopify/flash-list 2.0.0-alpha.2 → 2.0.0-alpha.21

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 (329) hide show
  1. package/README.md +67 -96
  2. package/android/src/main/kotlin/com/shopify/reactnative/flash_list/BlankAreaEvent.kt +2 -2
  3. package/dist/AnimatedFlashList.d.ts +0 -1
  4. package/dist/AnimatedFlashList.d.ts.map +1 -1
  5. package/dist/AnimatedFlashList.js +3 -3
  6. package/dist/AnimatedFlashList.js.map +1 -1
  7. package/dist/FlashList.d.ts +9 -0
  8. package/dist/FlashList.d.ts.map +1 -1
  9. package/dist/FlashList.js +22 -3
  10. package/dist/FlashList.js.map +1 -1
  11. package/dist/FlashListProps.d.ts +33 -13
  12. package/dist/FlashListProps.d.ts.map +1 -1
  13. package/dist/FlashListProps.js.map +1 -1
  14. package/dist/FlashListRef.d.ts +305 -0
  15. package/dist/FlashListRef.d.ts.map +1 -0
  16. package/dist/FlashListRef.js +3 -0
  17. package/dist/FlashListRef.js.map +1 -0
  18. package/dist/GridLayoutProviderWithProps.js +1 -2
  19. package/dist/GridLayoutProviderWithProps.js.map +1 -1
  20. package/dist/MasonryFlashList.d.ts +2 -2
  21. package/dist/MasonryFlashList.d.ts.map +1 -1
  22. package/dist/MasonryFlashList.js.map +1 -1
  23. package/dist/PureComponentWrapper.js +1 -1
  24. package/dist/PureComponentWrapper.js.map +1 -1
  25. package/dist/__tests__/AverageWindow.test.js.map +1 -1
  26. package/dist/__tests__/ConsecutiveNumbers.test.d.ts +2 -0
  27. package/dist/__tests__/ConsecutiveNumbers.test.d.ts.map +1 -0
  28. package/dist/__tests__/ConsecutiveNumbers.test.js +224 -0
  29. package/dist/__tests__/ConsecutiveNumbers.test.js.map +1 -0
  30. package/dist/__tests__/FlashList.test.js.map +1 -1
  31. package/dist/__tests__/GridLayoutManager.test.d.ts +2 -0
  32. package/dist/__tests__/GridLayoutManager.test.d.ts.map +1 -0
  33. package/dist/__tests__/GridLayoutManager.test.js +69 -0
  34. package/dist/__tests__/GridLayoutManager.test.js.map +1 -0
  35. package/dist/__tests__/GridLayoutProviderWithProps.test.js.map +1 -1
  36. package/dist/__tests__/LayoutCommitObserver.test.d.ts +2 -0
  37. package/dist/__tests__/LayoutCommitObserver.test.d.ts.map +1 -0
  38. package/dist/__tests__/LayoutCommitObserver.test.js +35 -0
  39. package/dist/__tests__/LayoutCommitObserver.test.js.map +1 -0
  40. package/dist/__tests__/LinearLayoutManager.test.d.ts +2 -0
  41. package/dist/__tests__/LinearLayoutManager.test.d.ts.map +1 -0
  42. package/dist/__tests__/LinearLayoutManager.test.js +140 -0
  43. package/dist/__tests__/LinearLayoutManager.test.js.map +1 -0
  44. package/dist/__tests__/MasonryFlashList.test.js.map +1 -1
  45. package/dist/__tests__/MasonryLayoutManager.test.d.ts +2 -0
  46. package/dist/__tests__/MasonryLayoutManager.test.d.ts.map +1 -0
  47. package/dist/__tests__/MasonryLayoutManager.test.js +148 -0
  48. package/dist/__tests__/MasonryLayoutManager.test.js.map +1 -0
  49. package/dist/__tests__/RecyclerView.test.d.ts +2 -0
  50. package/dist/__tests__/RecyclerView.test.d.ts.map +1 -0
  51. package/dist/__tests__/RecyclerView.test.js +103 -0
  52. package/dist/__tests__/RecyclerView.test.js.map +1 -0
  53. package/dist/__tests__/RenderStackManager.test.d.ts +2 -0
  54. package/dist/__tests__/RenderStackManager.test.d.ts.map +1 -0
  55. package/dist/__tests__/RenderStackManager.test.js +485 -0
  56. package/dist/__tests__/RenderStackManager.test.js.map +1 -0
  57. package/dist/__tests__/ViewabilityHelper.test.js.map +1 -1
  58. package/dist/__tests__/findVisibleIndex.test.d.ts +2 -0
  59. package/dist/__tests__/findVisibleIndex.test.d.ts.map +1 -0
  60. package/dist/__tests__/findVisibleIndex.test.js +259 -0
  61. package/dist/__tests__/findVisibleIndex.test.js.map +1 -0
  62. package/dist/__tests__/helpers/createLayoutManager.d.ts +34 -0
  63. package/dist/__tests__/helpers/createLayoutManager.d.ts.map +1 -0
  64. package/dist/__tests__/helpers/createLayoutManager.js +110 -0
  65. package/dist/__tests__/helpers/createLayoutManager.js.map +1 -0
  66. package/dist/__tests__/helpers/mountFlashList.d.ts +2 -2
  67. package/dist/__tests__/helpers/mountFlashList.d.ts.map +1 -1
  68. package/dist/__tests__/helpers/mountFlashList.js +2 -2
  69. package/dist/__tests__/helpers/mountFlashList.js.map +1 -1
  70. package/dist/__tests__/helpers/mountMasonryFlashList.d.ts +2 -2
  71. package/dist/__tests__/helpers/mountMasonryFlashList.d.ts.map +1 -1
  72. package/dist/__tests__/helpers/mountMasonryFlashList.js +2 -2
  73. package/dist/__tests__/helpers/mountMasonryFlashList.js.map +1 -1
  74. package/dist/__tests__/useBlankAreaTracker.test.js.map +1 -1
  75. package/dist/__tests__/useUnmountAwareCallbacks.test.d.ts +2 -0
  76. package/dist/__tests__/useUnmountAwareCallbacks.test.d.ts.map +1 -0
  77. package/dist/__tests__/useUnmountAwareCallbacks.test.js +185 -0
  78. package/dist/__tests__/useUnmountAwareCallbacks.test.js.map +1 -0
  79. package/dist/benchmark/AutoScrollHelper.js +2 -2
  80. package/dist/benchmark/AutoScrollHelper.js.map +1 -1
  81. package/dist/benchmark/JSFPSMonitor.js.map +1 -1
  82. package/dist/benchmark/roundToDecimalPlaces.js +1 -2
  83. package/dist/benchmark/roundToDecimalPlaces.js.map +1 -1
  84. package/dist/benchmark/useBenchmark.js +2 -28
  85. package/dist/benchmark/useBenchmark.js.map +1 -1
  86. package/dist/benchmark/useBlankAreaTracker.js +1 -2
  87. package/dist/benchmark/useBlankAreaTracker.js.map +1 -1
  88. package/dist/benchmark/useDataMultiplier.js +1 -2
  89. package/dist/benchmark/useDataMultiplier.js.map +1 -1
  90. package/dist/benchmark/useFlatListBenchmark.d.ts +0 -1
  91. package/dist/benchmark/useFlatListBenchmark.d.ts.map +1 -1
  92. package/dist/benchmark/useFlatListBenchmark.js +9 -9
  93. package/dist/benchmark/useFlatListBenchmark.js.map +1 -1
  94. package/dist/enableNewCore.d.ts.map +1 -1
  95. package/dist/enableNewCore.js +4 -4
  96. package/dist/enableNewCore.js.map +1 -1
  97. package/dist/errors/CustomError.js.map +1 -1
  98. package/dist/index.d.ts +4 -1
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +11 -3
  101. package/dist/index.js.map +1 -1
  102. package/dist/native/auto-layout/AutoLayoutView.d.ts +1 -1
  103. package/dist/native/auto-layout/AutoLayoutView.d.ts.map +1 -1
  104. package/dist/native/auto-layout/AutoLayoutView.js +1 -1
  105. package/dist/native/auto-layout/AutoLayoutView.js.map +1 -1
  106. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.d.ts.map +1 -1
  107. package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.d.ts +1 -1
  108. package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.d.ts.map +1 -1
  109. package/dist/native/config/PlatformHelper.android.d.ts +2 -0
  110. package/dist/native/config/PlatformHelper.android.d.ts.map +1 -1
  111. package/dist/native/config/PlatformHelper.android.js +2 -0
  112. package/dist/native/config/PlatformHelper.android.js.map +1 -1
  113. package/dist/native/config/PlatformHelper.d.ts +2 -0
  114. package/dist/native/config/PlatformHelper.d.ts.map +1 -1
  115. package/dist/native/config/PlatformHelper.ios.d.ts +2 -0
  116. package/dist/native/config/PlatformHelper.ios.d.ts.map +1 -1
  117. package/dist/native/config/PlatformHelper.ios.js +2 -0
  118. package/dist/native/config/PlatformHelper.ios.js.map +1 -1
  119. package/dist/native/config/PlatformHelper.js +2 -0
  120. package/dist/native/config/PlatformHelper.js.map +1 -1
  121. package/dist/native/config/PlatformHelper.web.d.ts +2 -0
  122. package/dist/native/config/PlatformHelper.web.d.ts.map +1 -1
  123. package/dist/native/config/PlatformHelper.web.js +3 -1
  124. package/dist/native/config/PlatformHelper.web.js.map +1 -1
  125. package/dist/recyclerview/LayoutCommitObserver.d.ts +12 -0
  126. package/dist/recyclerview/LayoutCommitObserver.d.ts.map +1 -0
  127. package/dist/recyclerview/LayoutCommitObserver.js +62 -0
  128. package/dist/recyclerview/LayoutCommitObserver.js.map +1 -0
  129. package/dist/recyclerview/RecyclerView.d.ts +3 -2
  130. package/dist/recyclerview/RecyclerView.d.ts.map +1 -1
  131. package/dist/recyclerview/RecyclerView.js +133 -69
  132. package/dist/recyclerview/RecyclerView.js.map +1 -1
  133. package/dist/recyclerview/RecyclerViewContextProvider.d.ts +41 -7
  134. package/dist/recyclerview/RecyclerViewContextProvider.d.ts.map +1 -1
  135. package/dist/recyclerview/RecyclerViewContextProvider.js +6 -2
  136. package/dist/recyclerview/RecyclerViewContextProvider.js.map +1 -1
  137. package/dist/recyclerview/RecyclerViewManager.d.ts +31 -7
  138. package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -1
  139. package/dist/recyclerview/RecyclerViewManager.js +154 -117
  140. package/dist/recyclerview/RecyclerViewManager.js.map +1 -1
  141. package/dist/recyclerview/RecyclerViewProps.d.ts +1 -1
  142. package/dist/recyclerview/RecyclerViewProps.d.ts.map +1 -1
  143. package/dist/recyclerview/RenderStackManager.d.ts +86 -0
  144. package/dist/recyclerview/RenderStackManager.d.ts.map +1 -0
  145. package/dist/recyclerview/RenderStackManager.js +343 -0
  146. package/dist/recyclerview/RenderStackManager.js.map +1 -0
  147. package/dist/recyclerview/ViewHolder.d.ts.map +1 -1
  148. package/dist/recyclerview/ViewHolder.js +8 -6
  149. package/dist/recyclerview/ViewHolder.js.map +1 -1
  150. package/dist/recyclerview/ViewHolderCollection.d.ts +10 -4
  151. package/dist/recyclerview/ViewHolderCollection.d.ts.map +1 -1
  152. package/dist/recyclerview/ViewHolderCollection.js +26 -10
  153. package/dist/recyclerview/ViewHolderCollection.js.map +1 -1
  154. package/dist/recyclerview/components/ScrollAnchor.d.ts +2 -1
  155. package/dist/recyclerview/components/ScrollAnchor.d.ts.map +1 -1
  156. package/dist/recyclerview/components/ScrollAnchor.js +12 -9
  157. package/dist/recyclerview/components/ScrollAnchor.js.map +1 -1
  158. package/dist/recyclerview/components/StickyHeaders.d.ts +2 -2
  159. package/dist/recyclerview/components/StickyHeaders.d.ts.map +1 -1
  160. package/dist/recyclerview/components/StickyHeaders.js +44 -45
  161. package/dist/recyclerview/components/StickyHeaders.js.map +1 -1
  162. package/dist/recyclerview/helpers/ConsecutiveNumbers.d.ts +1 -1
  163. package/dist/recyclerview/helpers/ConsecutiveNumbers.d.ts.map +1 -1
  164. package/dist/recyclerview/helpers/ConsecutiveNumbers.js +2 -2
  165. package/dist/recyclerview/helpers/ConsecutiveNumbers.js.map +1 -1
  166. package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts +48 -2
  167. package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts.map +1 -1
  168. package/dist/recyclerview/helpers/EngagedIndicesTracker.js +89 -19
  169. package/dist/recyclerview/helpers/EngagedIndicesTracker.js.map +1 -1
  170. package/dist/recyclerview/helpers/RenderTimeTracker.d.ts +11 -0
  171. package/dist/recyclerview/helpers/RenderTimeTracker.d.ts.map +1 -0
  172. package/dist/recyclerview/helpers/RenderTimeTracker.js +42 -0
  173. package/dist/recyclerview/helpers/RenderTimeTracker.js.map +1 -0
  174. package/dist/recyclerview/helpers/VelocityTracker.d.ts +29 -0
  175. package/dist/recyclerview/helpers/VelocityTracker.d.ts.map +1 -0
  176. package/dist/recyclerview/helpers/VelocityTracker.js +70 -0
  177. package/dist/recyclerview/helpers/VelocityTracker.js.map +1 -0
  178. package/dist/recyclerview/hooks/useBoundDetection.d.ts +1 -3
  179. package/dist/recyclerview/hooks/useBoundDetection.d.ts.map +1 -1
  180. package/dist/recyclerview/hooks/useBoundDetection.js +60 -28
  181. package/dist/recyclerview/hooks/useBoundDetection.js.map +1 -1
  182. package/dist/recyclerview/hooks/useLayoutState.d.ts +3 -1
  183. package/dist/recyclerview/hooks/useLayoutState.d.ts.map +1 -1
  184. package/dist/recyclerview/hooks/useLayoutState.js +6 -5
  185. package/dist/recyclerview/hooks/useLayoutState.js.map +1 -1
  186. package/dist/recyclerview/hooks/useMappingHelper.d.ts +9 -0
  187. package/dist/recyclerview/hooks/useMappingHelper.d.ts.map +1 -0
  188. package/dist/recyclerview/hooks/useMappingHelper.js +19 -0
  189. package/dist/recyclerview/hooks/useMappingHelper.js.map +1 -0
  190. package/dist/recyclerview/hooks/useOnLoad.d.ts +2 -2
  191. package/dist/recyclerview/hooks/useOnLoad.d.ts.map +1 -1
  192. package/dist/recyclerview/hooks/useOnLoad.js +9 -10
  193. package/dist/recyclerview/hooks/useOnLoad.js.map +1 -1
  194. package/dist/recyclerview/hooks/useRecyclerViewController.d.ts +5 -49
  195. package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
  196. package/dist/recyclerview/hooks/useRecyclerViewController.js +343 -191
  197. package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -1
  198. package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts +2 -0
  199. package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts.map +1 -1
  200. package/dist/recyclerview/hooks/useRecyclerViewManager.js +11 -1
  201. package/dist/recyclerview/hooks/useRecyclerViewManager.js.map +1 -1
  202. package/dist/recyclerview/hooks/useRecyclingState.d.ts +4 -2
  203. package/dist/recyclerview/hooks/useRecyclingState.d.ts.map +1 -1
  204. package/dist/recyclerview/hooks/useRecyclingState.js +3 -4
  205. package/dist/recyclerview/hooks/useRecyclingState.js.map +1 -1
  206. package/dist/recyclerview/hooks/useSecondaryProps.d.ts +1 -1
  207. package/dist/recyclerview/hooks/useSecondaryProps.d.ts.map +1 -1
  208. package/dist/recyclerview/hooks/useSecondaryProps.js +15 -12
  209. package/dist/recyclerview/hooks/useSecondaryProps.js.map +1 -1
  210. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts +15 -0
  211. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts.map +1 -0
  212. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js +63 -0
  213. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js.map +1 -0
  214. package/dist/recyclerview/hooks/useUnmountFlag.d.ts +0 -1
  215. package/dist/recyclerview/hooks/useUnmountFlag.d.ts.map +1 -1
  216. package/dist/recyclerview/hooks/useUnmountFlag.js +1 -0
  217. package/dist/recyclerview/hooks/useUnmountFlag.js.map +1 -1
  218. package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts +18 -4
  219. package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts.map +1 -1
  220. package/dist/recyclerview/layout-managers/GridLayoutManager.js +61 -25
  221. package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -1
  222. package/dist/recyclerview/layout-managers/LayoutManager.d.ts +36 -21
  223. package/dist/recyclerview/layout-managers/LayoutManager.d.ts.map +1 -1
  224. package/dist/recyclerview/layout-managers/LayoutManager.js +96 -28
  225. package/dist/recyclerview/layout-managers/LayoutManager.js.map +1 -1
  226. package/dist/recyclerview/layout-managers/LinearLayoutManager.d.ts +1 -2
  227. package/dist/recyclerview/layout-managers/LinearLayoutManager.d.ts.map +1 -1
  228. package/dist/recyclerview/layout-managers/LinearLayoutManager.js +3 -3
  229. package/dist/recyclerview/layout-managers/LinearLayoutManager.js.map +1 -1
  230. package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts +9 -1
  231. package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts.map +1 -1
  232. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js +30 -16
  233. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js.map +1 -1
  234. package/dist/recyclerview/utils/adjustOffsetForRTL.js +1 -2
  235. package/dist/recyclerview/utils/adjustOffsetForRTL.js.map +1 -1
  236. package/dist/recyclerview/utils/componentUtils.d.ts +1 -1
  237. package/dist/recyclerview/utils/componentUtils.d.ts.map +1 -1
  238. package/dist/recyclerview/utils/componentUtils.js.map +1 -1
  239. package/dist/recyclerview/utils/findVisibleIndex.d.ts.map +1 -1
  240. package/dist/recyclerview/utils/findVisibleIndex.js +3 -5
  241. package/dist/recyclerview/utils/findVisibleIndex.js.map +1 -1
  242. package/dist/recyclerview/utils/measureLayout.d.ts +24 -28
  243. package/dist/recyclerview/utils/measureLayout.d.ts.map +1 -1
  244. package/dist/recyclerview/utils/measureLayout.js +36 -6
  245. package/dist/recyclerview/utils/measureLayout.js.map +1 -1
  246. package/dist/recyclerview/utils/measureLayout.web.d.ts +29 -0
  247. package/dist/recyclerview/utils/measureLayout.web.d.ts.map +1 -0
  248. package/dist/recyclerview/utils/measureLayout.web.js +87 -0
  249. package/dist/recyclerview/utils/measureLayout.web.js.map +1 -0
  250. package/dist/specs/AutoLayoutNativeComponent.d.ts +1 -2
  251. package/dist/specs/AutoLayoutNativeComponent.d.ts.map +1 -1
  252. package/dist/specs/CellContainerNativeComponent.d.ts +0 -1
  253. package/dist/specs/CellContainerNativeComponent.d.ts.map +1 -1
  254. package/dist/tsconfig.tsbuildinfo +1 -1
  255. package/dist/utils/AverageWindow.js.map +1 -1
  256. package/dist/utils/ContentContainerUtils.d.ts.map +1 -1
  257. package/dist/utils/ContentContainerUtils.js.map +1 -1
  258. package/dist/viewability/ViewToken.d.ts +2 -2
  259. package/dist/viewability/ViewToken.d.ts.map +1 -1
  260. package/dist/viewability/ViewabilityHelper.js +1 -1
  261. package/dist/viewability/ViewabilityHelper.js.map +1 -1
  262. package/dist/viewability/ViewabilityManager.d.ts.map +1 -1
  263. package/dist/viewability/ViewabilityManager.js +11 -5
  264. package/dist/viewability/ViewabilityManager.js.map +1 -1
  265. package/jestSetup.js +30 -11
  266. package/package.json +4 -3
  267. package/src/AnimatedFlashList.ts +3 -2
  268. package/src/FlashList.tsx +25 -1
  269. package/src/FlashListProps.ts +42 -11
  270. package/src/FlashListRef.ts +320 -0
  271. package/src/MasonryFlashList.tsx +2 -2
  272. package/src/__tests__/ConsecutiveNumbers.test.ts +232 -0
  273. package/src/__tests__/GridLayoutManager.test.ts +113 -0
  274. package/src/__tests__/LayoutCommitObserver.test.tsx +60 -0
  275. package/src/__tests__/LinearLayoutManager.test.ts +227 -0
  276. package/src/__tests__/MasonryLayoutManager.test.ts +202 -0
  277. package/src/__tests__/RecyclerView.test.tsx +144 -0
  278. package/src/__tests__/RenderStackManager.test.ts +574 -0
  279. package/src/__tests__/findVisibleIndex.test.ts +369 -0
  280. package/src/__tests__/helpers/createLayoutManager.ts +141 -0
  281. package/src/__tests__/useUnmountAwareCallbacks.test.tsx +285 -0
  282. package/src/benchmark/useBenchmark.ts +0 -37
  283. package/src/benchmark/useFlatListBenchmark.ts +2 -2
  284. package/src/enableNewCore.ts +3 -1
  285. package/src/index.ts +14 -3
  286. package/src/native/config/PlatformHelper.android.ts +2 -0
  287. package/src/native/config/PlatformHelper.ios.ts +2 -0
  288. package/src/native/config/PlatformHelper.ts +2 -0
  289. package/src/native/config/PlatformHelper.web.ts +3 -1
  290. package/src/recyclerview/LayoutCommitObserver.tsx +74 -0
  291. package/src/recyclerview/RecyclerView.tsx +178 -89
  292. package/src/recyclerview/RecyclerViewContextProvider.ts +53 -7
  293. package/src/recyclerview/RecyclerViewManager.ts +176 -97
  294. package/src/recyclerview/RecyclerViewProps.ts +2 -1
  295. package/src/recyclerview/RenderStackManager.ts +317 -0
  296. package/src/recyclerview/ViewHolder.tsx +13 -6
  297. package/src/recyclerview/ViewHolderCollection.tsx +45 -16
  298. package/src/recyclerview/components/ScrollAnchor.tsx +24 -11
  299. package/src/recyclerview/components/StickyHeaders.tsx +70 -58
  300. package/src/recyclerview/helpers/ConsecutiveNumbers.ts +2 -2
  301. package/src/recyclerview/helpers/EngagedIndicesTracker.ts +135 -25
  302. package/src/recyclerview/helpers/RenderTimeTracker.ts +42 -0
  303. package/src/recyclerview/helpers/VelocityTracker.ts +77 -0
  304. package/src/recyclerview/hooks/useBoundDetection.ts +74 -25
  305. package/src/recyclerview/hooks/useLayoutState.ts +15 -6
  306. package/src/recyclerview/hooks/useMappingHelper.ts +20 -0
  307. package/src/recyclerview/hooks/useOnLoad.ts +11 -10
  308. package/src/recyclerview/hooks/useRecyclerViewController.tsx +380 -241
  309. package/src/recyclerview/hooks/useRecyclerViewManager.ts +13 -1
  310. package/src/recyclerview/hooks/useRecyclingState.ts +11 -7
  311. package/src/recyclerview/hooks/useSecondaryProps.tsx +12 -7
  312. package/src/recyclerview/hooks/useUnmountAwareCallbacks.ts +73 -0
  313. package/src/recyclerview/hooks/useUnmountFlag.ts +1 -0
  314. package/src/recyclerview/layout-managers/GridLayoutManager.ts +68 -27
  315. package/src/recyclerview/layout-managers/LayoutManager.ts +116 -42
  316. package/src/recyclerview/layout-managers/LinearLayoutManager.ts +12 -8
  317. package/src/recyclerview/layout-managers/MasonryLayoutManager.ts +34 -13
  318. package/src/recyclerview/utils/componentUtils.ts +1 -1
  319. package/src/recyclerview/utils/findVisibleIndex.ts +1 -2
  320. package/src/recyclerview/utils/measureLayout.ts +41 -2
  321. package/src/recyclerview/utils/measureLayout.web.ts +102 -0
  322. package/src/viewability/ViewToken.ts +2 -2
  323. package/src/viewability/ViewabilityHelper.ts +1 -1
  324. package/src/viewability/ViewabilityManager.ts +16 -9
  325. package/dist/recyclerview/RecycleKeyManager.d.ts +0 -82
  326. package/dist/recyclerview/RecycleKeyManager.d.ts.map +0 -1
  327. package/dist/recyclerview/RecycleKeyManager.js +0 -135
  328. package/dist/recyclerview/RecycleKeyManager.js.map +0 -1
  329. package/src/recyclerview/RecycleKeyManager.ts +0 -185
@@ -0,0 +1,285 @@
1
+ import React from "react";
2
+ import { render } from "@quilted/react-testing";
3
+
4
+ import { useUnmountAwareTimeout } from "../recyclerview/hooks/useUnmountAwareCallbacks";
5
+
6
+ const TestComponent = ({
7
+ onRender,
8
+ }: {
9
+ onRender: (api: ReturnType<typeof useUnmountAwareTimeout>) => void;
10
+ }) => {
11
+ const api = useUnmountAwareTimeout();
12
+ onRender(api);
13
+ return null;
14
+ };
15
+
16
+ describe("useUnmountAwareCallbacks", () => {
17
+ beforeEach(() => {
18
+ jest.useFakeTimers();
19
+ });
20
+
21
+ afterEach(() => {
22
+ jest.clearAllTimers();
23
+ jest.clearAllMocks();
24
+ });
25
+
26
+ it("returns a setTimeout function", () => {
27
+ let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
28
+ render(
29
+ <TestComponent
30
+ onRender={(hookApi) => {
31
+ api = hookApi;
32
+ }}
33
+ />
34
+ );
35
+
36
+ expect(api).toBeDefined();
37
+ expect(api?.setTimeout).toBeDefined();
38
+ expect(typeof api?.setTimeout).toBe("function");
39
+ });
40
+
41
+ it("executes the callback after the specified delay", () => {
42
+ const callback = jest.fn();
43
+ let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
44
+
45
+ render(
46
+ <TestComponent
47
+ onRender={(hookApi) => {
48
+ api = hookApi;
49
+ }}
50
+ />
51
+ );
52
+
53
+ api?.setTimeout(callback, 1000);
54
+
55
+ expect(callback).not.toHaveBeenCalled();
56
+
57
+ // Fast-forward time
58
+ jest.advanceTimersByTime(1000);
59
+
60
+ expect(callback).toHaveBeenCalledTimes(1);
61
+ });
62
+
63
+ it("executes multiple callbacks after their respective delays", () => {
64
+ const callback1 = jest.fn();
65
+ const callback2 = jest.fn();
66
+ let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
67
+
68
+ render(
69
+ <TestComponent
70
+ onRender={(hookApi) => {
71
+ api = hookApi;
72
+ }}
73
+ />
74
+ );
75
+
76
+ api?.setTimeout(callback1, 1000);
77
+ api?.setTimeout(callback2, 2000);
78
+
79
+ expect(callback1).not.toHaveBeenCalled();
80
+ expect(callback2).not.toHaveBeenCalled();
81
+
82
+ // Fast-forward time by 1000ms
83
+ jest.advanceTimersByTime(1000);
84
+
85
+ expect(callback1).toHaveBeenCalledTimes(1);
86
+ expect(callback2).not.toHaveBeenCalled();
87
+
88
+ // Fast-forward time by another 1000ms
89
+ jest.advanceTimersByTime(1000);
90
+
91
+ expect(callback1).toHaveBeenCalledTimes(1);
92
+ expect(callback2).toHaveBeenCalledTimes(1);
93
+ });
94
+
95
+ it("clears all timeouts when the component unmounts", () => {
96
+ const callback = jest.fn();
97
+ let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
98
+
99
+ const component = render(
100
+ <TestComponent
101
+ onRender={(hookApi) => {
102
+ api = hookApi;
103
+ }}
104
+ />
105
+ );
106
+
107
+ api?.setTimeout(callback, 1000);
108
+ api?.setTimeout(callback, 2000);
109
+
110
+ // Spy on clearTimeout to verify it's called during unmount
111
+ const clearTimeoutSpy = jest.spyOn(global, "clearTimeout");
112
+
113
+ // Unmount the component
114
+ component.unmount();
115
+
116
+ // Fast-forward time
117
+ jest.advanceTimersByTime(2000);
118
+
119
+ // Expect callbacks not to be called because timeouts were cleared
120
+ expect(callback).not.toHaveBeenCalled();
121
+ expect(clearTimeoutSpy).toHaveBeenCalled();
122
+ });
123
+
124
+ it("removes timeout from tracking set once it executes", () => {
125
+ const callback = jest.fn();
126
+ let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
127
+
128
+ const component = render(
129
+ <TestComponent
130
+ onRender={(hookApi) => {
131
+ api = hookApi;
132
+ }}
133
+ />
134
+ );
135
+
136
+ api?.setTimeout(callback, 1000);
137
+
138
+ // Fast-forward time
139
+ jest.advanceTimersByTime(1000);
140
+
141
+ // Verify callback was called
142
+ expect(callback).toHaveBeenCalledTimes(1);
143
+
144
+ // We can't directly check the timeoutIds Set, so we'll verify indirectly
145
+ // by making sure no clearTimeout calls happen on unmount (since the timeout was already cleared)
146
+ const clearTimeoutSpy = jest.spyOn(global, "clearTimeout");
147
+ clearTimeoutSpy.mockClear(); // Reset the mock calls before unmount
148
+
149
+ // Unmount the component
150
+ component.unmount();
151
+
152
+ // If the timeout was properly removed from the set, clearTimeout won't be called on unmount
153
+ expect(clearTimeoutSpy).not.toHaveBeenCalled();
154
+ });
155
+
156
+ it("handles multiple timeouts correctly", () => {
157
+ const callback1 = jest.fn();
158
+ const callback2 = jest.fn();
159
+ const callback3 = jest.fn();
160
+ let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
161
+
162
+ const component = render(
163
+ <TestComponent
164
+ onRender={(hookApi) => {
165
+ api = hookApi;
166
+ }}
167
+ />
168
+ );
169
+
170
+ // Set up three timeouts with different delays
171
+ api?.setTimeout(callback1, 1000);
172
+ api?.setTimeout(callback2, 2000);
173
+ api?.setTimeout(callback3, 3000);
174
+
175
+ // Fast-forward time by 1500ms (should trigger only the first callback)
176
+ jest.advanceTimersByTime(1500);
177
+
178
+ expect(callback1).toHaveBeenCalledTimes(1);
179
+ expect(callback2).not.toHaveBeenCalled();
180
+ expect(callback3).not.toHaveBeenCalled();
181
+
182
+ // Unmount the component (should clear remaining timeouts)
183
+ component.unmount();
184
+
185
+ // Fast-forward time to when all callbacks would have been called
186
+ jest.advanceTimersByTime(2000);
187
+
188
+ // Only the first callback should have been called
189
+ expect(callback1).toHaveBeenCalledTimes(1);
190
+ expect(callback2).not.toHaveBeenCalled();
191
+ expect(callback3).not.toHaveBeenCalled();
192
+ });
193
+
194
+ it("handles callbacks that trigger new timeouts", () => {
195
+ const finalCallback = jest.fn();
196
+ let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
197
+
198
+ render(
199
+ <TestComponent
200
+ onRender={(hookApi) => {
201
+ api = hookApi;
202
+ }}
203
+ />
204
+ );
205
+
206
+ const firstCallback = () => {
207
+ api?.setTimeout(finalCallback, 1000);
208
+ };
209
+
210
+ api?.setTimeout(firstCallback, 1000);
211
+
212
+ // Fast-forward time to trigger first callback
213
+ jest.advanceTimersByTime(1000);
214
+
215
+ expect(finalCallback).not.toHaveBeenCalled();
216
+
217
+ // Fast-forward time to trigger second callback
218
+ jest.advanceTimersByTime(1000);
219
+
220
+ expect(finalCallback).toHaveBeenCalledTimes(1);
221
+ });
222
+
223
+ it("handles zero delay timeouts", () => {
224
+ const callback = jest.fn();
225
+ let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
226
+
227
+ render(
228
+ <TestComponent
229
+ onRender={(hookApi) => {
230
+ api = hookApi;
231
+ }}
232
+ />
233
+ );
234
+
235
+ api?.setTimeout(callback, 0);
236
+
237
+ expect(callback).not.toHaveBeenCalled();
238
+
239
+ // Even with zero delay, we need to advance the timer to execute
240
+ jest.advanceTimersByTime(0);
241
+
242
+ expect(callback).toHaveBeenCalledTimes(1);
243
+ });
244
+
245
+ it("handles errors in callbacks without affecting other timeouts", () => {
246
+ const errorCallback = jest.fn(() => {
247
+ throw new Error("Test error");
248
+ });
249
+ const successCallback = jest.fn();
250
+ let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
251
+
252
+ // Suppress error log during test
253
+ const originalConsoleError = console.error;
254
+ console.error = jest.fn();
255
+
256
+ render(
257
+ <TestComponent
258
+ onRender={(hookApi) => {
259
+ api = hookApi;
260
+ }}
261
+ />
262
+ );
263
+
264
+ api?.setTimeout(errorCallback, 1000);
265
+ api?.setTimeout(successCallback, 2000);
266
+
267
+ // Fast-forward time to trigger error callback
268
+ try {
269
+ jest.advanceTimersByTime(1000);
270
+ } catch (error) {
271
+ // Expected error
272
+ }
273
+
274
+ expect(errorCallback).toHaveBeenCalledTimes(1);
275
+ expect(successCallback).not.toHaveBeenCalled();
276
+
277
+ // Fast-forward time to trigger success callback
278
+ jest.advanceTimersByTime(1000);
279
+
280
+ expect(successCallback).toHaveBeenCalledTimes(1);
281
+
282
+ // Restore console.error
283
+ console.error = originalConsoleError;
284
+ });
285
+ });
@@ -199,42 +199,5 @@ function computeSuggestions(
199
199
  `Data count is low. Try to increase it to a large number (e.g 200) using the 'useDataMultiplier' hook.`
200
200
  );
201
201
  }
202
- const distanceFromWindow = roundToDecimalPlaces(
203
- flashListRef.current.firstItemOffset,
204
- 0
205
- );
206
- if (
207
- (flashListRef.current.props.estimatedFirstItemOffset || 0) !==
208
- distanceFromWindow
209
- ) {
210
- suggestions.push(
211
- `estimatedFirstItemOffset can be set to ${distanceFromWindow}`
212
- );
213
- }
214
- const rlv = flashListRef.current.recyclerlistview_unsafe;
215
- const horizontal = flashListRef.current.props.horizontal;
216
- if (rlv) {
217
- const sizeArray = rlv.props.dataProvider
218
- .getAllData()
219
- .map((_, index) =>
220
- horizontal
221
- ? rlv.getLayout?.(index)?.width || 0
222
- : rlv.getLayout?.(index)?.height || 0
223
- );
224
- const averageSize = Math.round(
225
- sizeArray.reduce((prev, current) => prev + current, 0) /
226
- sizeArray.length
227
- );
228
- if (
229
- Math.abs(
230
- averageSize -
231
- (flashListRef.current.props.estimatedItemSize ??
232
- flashListRef.current.state.layoutProvider
233
- .defaultEstimatedItemSize)
234
- ) > 5
235
- ) {
236
- suggestions.push(`estimatedItemSize can be set to ${averageSize}`);
237
- }
238
- }
239
202
  }
240
203
  }
@@ -25,7 +25,7 @@ export function useFlatListBenchmark(
25
25
  ) {
26
26
  useEffect(() => {
27
27
  const cancellable = new Cancellable();
28
- if (flatListRef.current) {
28
+ if (flatListRef.current && flatListRef.current.props) {
29
29
  if (!(Number(flatListRef.current.props.data?.length) > 0)) {
30
30
  throw new Error("Data is empty, cannot run benchmark");
31
31
  }
@@ -71,7 +71,7 @@ async function runScrollBenchmark(
71
71
  scrollSpeedMultiplier: number
72
72
  ): Promise<void> {
73
73
  if (flatListRef.current) {
74
- const horizontal = flatListRef.current.props.horizontal;
74
+ const horizontal = Boolean(flatListRef.current.props?.horizontal);
75
75
 
76
76
  const fromX = 0;
77
77
  const fromY = 0;
@@ -1,3 +1,5 @@
1
+ import { Platform } from "react-native";
2
+
1
3
  let useNewCore: boolean | undefined;
2
4
  export function enableNewCore(enable: boolean) {
3
5
  useNewCore = enable;
@@ -15,7 +17,7 @@ function isReactNativeNewArchitecture(): boolean {
15
17
  // Check for TurboModule system
16
18
  const hasTurboModule = Boolean((global as any)?.__turboModuleProxy);
17
19
 
18
- return hasFabricUIManager || hasTurboModule;
20
+ return hasFabricUIManager || hasTurboModule || Platform.OS === "web";
19
21
  } catch {
20
22
  return false;
21
23
  }
package/src/index.ts CHANGED
@@ -1,9 +1,11 @@
1
+ // eslint-disable-next-line import/no-named-default
1
2
  import { default as OriginalFlashList } from "./FlashList";
2
3
  import { isNewCoreEnabled } from "./enableNewCore";
3
4
  import { RecyclerView } from "./recyclerview/RecyclerView";
4
5
 
5
6
  // Keep this unmodified for TS type checking
6
7
  export { default as FlashList } from "./FlashList";
8
+ export { FlashListRef } from "./FlashListRef";
7
9
  export {
8
10
  FlashListProps,
9
11
  ContentStyle,
@@ -44,19 +46,28 @@ export {
44
46
  } from "./MasonryFlashList";
45
47
  export { useLayoutState } from "./recyclerview/hooks/useLayoutState";
46
48
  export { useRecyclingState } from "./recyclerview/hooks/useRecyclingState";
49
+ export { useMappingHelper } from "./recyclerview/hooks/useMappingHelper";
47
50
  export { JSFPSMonitor, JSFPSResult } from "./benchmark/JSFPSMonitor";
48
51
  export { autoScroll, Cancellable } from "./benchmark/AutoScrollHelper";
49
52
  export { default as ViewToken } from "./viewability/ViewToken";
50
53
  export { default as CellContainer } from "./native/cell-container/CellContainer";
51
54
  export { RecyclerView } from "./recyclerview/RecyclerView";
52
55
  export { RecyclerViewProps } from "./recyclerview/RecyclerViewProps";
53
- export { useRecyclerViewContext } from "./recyclerview/RecyclerViewContextProvider";
56
+ export { useFlashListContext } from "./recyclerview/RecyclerViewContextProvider";
57
+ export {
58
+ LayoutCommitObserver,
59
+ LayoutCommitObserverProps,
60
+ } from "./recyclerview/LayoutCommitObserver";
54
61
 
55
62
  // @ts-ignore - This is ignored by TypeScript but will be present in the compiled JS
56
63
  // In the compiled JS, this will override the previous FlashList export with a conditional one
57
- if (typeof module !== "undefined" && module.exports) {
64
+ if (
65
+ typeof module !== "undefined" &&
66
+ module.exports &&
67
+ process?.env?.NODE_ENV !== "test"
68
+ ) {
58
69
  Object.defineProperty(module.exports, "FlashList", {
59
- get: function () {
70
+ get() {
60
71
  return isNewCoreEnabled() ? RecyclerView : OriginalFlashList;
61
72
  },
62
73
  configurable: true,
@@ -2,6 +2,8 @@ import { BaseItemAnimator } from "recyclerlistview";
2
2
 
3
3
  const PlatformConfig = {
4
4
  defaultDrawDistance: 250,
5
+ supportsOffsetCorrection: true,
6
+ trackAverageRenderTimeForOffsetProjection: true,
5
7
  // Using rotate instead of scaleY on Android to avoid performance issues. Issue: https://github.com/Shopify/flash-list/issues/751
6
8
  invertedTransformStyle: { transform: [{ rotate: "180deg" }] },
7
9
  invertedTransformStyleHorizontal: { transform: [{ rotate: "180deg" }] },
@@ -2,6 +2,8 @@ import { BaseItemAnimator } from "recyclerlistview";
2
2
 
3
3
  const PlatformConfig = {
4
4
  defaultDrawDistance: 250,
5
+ supportsOffsetCorrection: true,
6
+ trackAverageRenderTimeForOffsetProjection: false,
5
7
  invertedTransformStyle: { transform: [{ scaleY: -1 }] },
6
8
  invertedTransformStyleHorizontal: { transform: [{ scaleX: -1 }] },
7
9
  };
@@ -3,6 +3,8 @@ import { DefaultJSItemAnimator } from "recyclerlistview/dist/reactnative/platfor
3
3
 
4
4
  const PlatformConfig = {
5
5
  defaultDrawDistance: 250,
6
+ supportsOffsetCorrection: false,
7
+ trackAverageRenderTimeForOffsetProjection: false,
6
8
  invertedTransformStyle: { transform: [{ scaleY: -1 }] },
7
9
  invertedTransformStyleHorizontal: { transform: [{ scaleX: -1 }] },
8
10
  };
@@ -4,7 +4,9 @@ import { BaseItemAnimator } from "recyclerlistview";
4
4
  import { DefaultJSItemAnimator } from "recyclerlistview/dist/reactnative/platform/reactnative/itemanimators/defaultjsanimator/DefaultJSItemAnimator";
5
5
 
6
6
  const PlatformConfig = {
7
- defaultDrawDistance: 2000,
7
+ defaultDrawDistance: 500,
8
+ supportsOffsetCorrection: false,
9
+ trackAverageRenderTimeForOffsetProjection: false,
8
10
  invertedTransformStyle: { transform: [{ scaleY: -1 }] },
9
11
  invertedTransformStyleHorizontal: { transform: [{ scaleX: -1 }] },
10
12
  };
@@ -0,0 +1,74 @@
1
+ import React, { useLayoutEffect, useMemo, useRef } from "react";
2
+
3
+ import {
4
+ RecyclerViewContext,
5
+ RecyclerViewContextProvider,
6
+ useRecyclerViewContext,
7
+ } from "./RecyclerViewContextProvider";
8
+ import { useLayoutState } from "./hooks/useLayoutState";
9
+
10
+ export interface LayoutCommitObserverProps {
11
+ children: React.ReactNode;
12
+ onCommitLayoutEffect?: () => void;
13
+ }
14
+
15
+ /**
16
+ * LayoutCommitObserver can be used to observe when FlashList commits a layout.
17
+ * It is useful when your component has one or more FlashLists somewhere down the tree.
18
+ * LayoutCommitObserver will trigger `onCommitLayoutEffect` when all of the FlashLists in the tree have finished their first commit.
19
+ */
20
+ export const LayoutCommitObserver = React.memo(
21
+ (props: LayoutCommitObserverProps) => {
22
+ const { children, onCommitLayoutEffect } = props;
23
+ const parentRecyclerViewContext = useRecyclerViewContext();
24
+ const [_, setRenderId] = useLayoutState(0);
25
+ const pendingChildIds = useRef<Set<string>>(new Set()).current;
26
+
27
+ useLayoutEffect(() => {
28
+ if (pendingChildIds.size > 0) {
29
+ return;
30
+ }
31
+ onCommitLayoutEffect?.();
32
+ });
33
+
34
+ // Create context for child components
35
+ const recyclerViewContext: RecyclerViewContext<unknown> = useMemo(() => {
36
+ return {
37
+ layout: () => {
38
+ setRenderId((prev) => prev + 1);
39
+ },
40
+ getRef: () => {
41
+ return parentRecyclerViewContext?.getRef() ?? null;
42
+ },
43
+ getParentRef: () => {
44
+ return parentRecyclerViewContext?.getParentRef() ?? null;
45
+ },
46
+ getParentScrollViewRef: () => {
47
+ return parentRecyclerViewContext?.getParentScrollViewRef() ?? null;
48
+ },
49
+ getScrollViewRef: () => {
50
+ return parentRecyclerViewContext?.getScrollViewRef() ?? null;
51
+ },
52
+ markChildLayoutAsPending: (id: string) => {
53
+ parentRecyclerViewContext?.markChildLayoutAsPending(id);
54
+ pendingChildIds.add(id);
55
+ },
56
+ unmarkChildLayoutAsPending: (id: string) => {
57
+ parentRecyclerViewContext?.unmarkChildLayoutAsPending(id);
58
+ if (pendingChildIds.has(id)) {
59
+ pendingChildIds.delete(id);
60
+ recyclerViewContext.layout();
61
+ }
62
+ },
63
+ };
64
+ }, [parentRecyclerViewContext, pendingChildIds, setRenderId]);
65
+
66
+ return (
67
+ <RecyclerViewContextProvider value={recyclerViewContext}>
68
+ {children}
69
+ </RecyclerViewContextProvider>
70
+ );
71
+ }
72
+ );
73
+
74
+ LayoutCommitObserver.displayName = "LayoutCommitObserver";