@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,369 @@
1
+ import {
2
+ findFirstVisibleIndex,
3
+ findLastVisibleIndex,
4
+ } from "../recyclerview/utils/findVisibleIndex";
5
+ import { RVLayout } from "../recyclerview/layout-managers/LayoutManager";
6
+
7
+ import {
8
+ createPopulatedLayoutManager,
9
+ LayoutManagerType,
10
+ getAllLayouts,
11
+ } from "./helpers/createLayoutManager";
12
+
13
+ describe("findVisibleIndex", () => {
14
+ // Helper function to create mock layouts directly for precise control
15
+ function createMockLayouts(
16
+ count: number,
17
+ startPosition: number,
18
+ itemSize: number,
19
+ isHorizontal: boolean
20
+ ): RVLayout[] {
21
+ const layouts: RVLayout[] = [];
22
+ for (let i = 0; i < count; i++) {
23
+ const x = isHorizontal ? startPosition + i * itemSize : 0;
24
+ const y = isHorizontal ? 0 : startPosition + i * itemSize;
25
+ layouts.push({
26
+ x,
27
+ y,
28
+ width: isHorizontal ? itemSize : 100,
29
+ height: isHorizontal ? 100 : itemSize,
30
+ });
31
+ }
32
+ return layouts;
33
+ }
34
+
35
+ describe("findFirstVisibleIndex", () => {
36
+ // Test 1: Basic functionality - vertical layout
37
+ it("finds the first visible index in a vertical layout", () => {
38
+ const layoutManager = createPopulatedLayoutManager(
39
+ LayoutManagerType.LINEAR,
40
+ 20,
41
+ { horizontal: false }
42
+ );
43
+ const layouts = getAllLayouts(layoutManager);
44
+
45
+ // Viewport starts at y=150, so the second item (index 1) should be first visible
46
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 150, false);
47
+ expect(firstVisibleIndex).toBe(1);
48
+ });
49
+
50
+ // Test 2: Basic functionality - horizontal layout
51
+ it("finds the first visible index in a horizontal layout", () => {
52
+ const layoutManager = createPopulatedLayoutManager(
53
+ LayoutManagerType.LINEAR,
54
+ 20,
55
+ { horizontal: true }
56
+ );
57
+ const layouts = getAllLayouts(layoutManager);
58
+
59
+ // Viewport starts at x=150, so the second item (index 1) should be first visible
60
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 150, true);
61
+ expect(firstVisibleIndex).toBe(1);
62
+ });
63
+
64
+ // Test 3: Empty layouts array
65
+ it("returns -1 for empty layouts array", () => {
66
+ const firstVisibleIndex = findFirstVisibleIndex([], 100, false);
67
+ expect(firstVisibleIndex).toBe(-1);
68
+ });
69
+
70
+ // Test 4: All items are visible (threshold at 0)
71
+ it("returns 0 when all items are visible (threshold at 0)", () => {
72
+ const layoutManager = createPopulatedLayoutManager(
73
+ LayoutManagerType.LINEAR,
74
+ 10
75
+ );
76
+ const layouts = getAllLayouts(layoutManager);
77
+
78
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 0, false);
79
+ expect(firstVisibleIndex).toBe(0);
80
+ });
81
+
82
+ // Test 5: No items are visible (threshold beyond all items)
83
+ it("returns -1 when no items are visible", () => {
84
+ const layouts = createMockLayouts(10, 0, 100, false);
85
+
86
+ // Threshold is beyond all items (10 items * 100 height = 1000)
87
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 1100, false);
88
+ expect(firstVisibleIndex).toBe(-1);
89
+ });
90
+
91
+ // Test 6: Edge case - threshold exactly at item boundary
92
+ it("returns correct index when threshold is exactly at item boundary", () => {
93
+ const layouts = createMockLayouts(10, 0, 100, false);
94
+
95
+ // Threshold exactly at the start of the 5th item
96
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 400, false);
97
+ expect(firstVisibleIndex).toBe(4);
98
+ });
99
+
100
+ // Test 7: Edge case - threshold in the middle of an item
101
+ it("returns correct index when threshold is in the middle of an item", () => {
102
+ const layouts = createMockLayouts(10, 0, 100, false);
103
+
104
+ // Threshold in the middle of the 3rd item
105
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 250, false);
106
+ expect(firstVisibleIndex).toBe(2);
107
+ });
108
+
109
+ // Test 8: With grid layout - threshold crosses multiple columns
110
+ it("finds first visible index with grid layout", () => {
111
+ const layoutManager = createPopulatedLayoutManager(
112
+ LayoutManagerType.GRID,
113
+ 20,
114
+ { maxColumns: 2 }
115
+ );
116
+ const layouts = getAllLayouts(layoutManager);
117
+
118
+ // With 2 columns, items are positioned differently
119
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 150, false);
120
+
121
+ // Expected result depends on how grid layout positions items
122
+ // This test might need adjustment based on actual grid layout behavior
123
+ expect(firstVisibleIndex).not.toBe(-1);
124
+ });
125
+
126
+ // Test 9: With masonry layout - variable height items
127
+ it("finds first visible index with masonry layout and variable item sizes", () => {
128
+ const layoutManager = createPopulatedLayoutManager(
129
+ LayoutManagerType.MASONRY,
130
+ 20,
131
+ { maxColumns: 2 },
132
+ 100,
133
+ 100,
134
+ true // Variable size
135
+ );
136
+ const layouts = getAllLayouts(layoutManager);
137
+
138
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 200, false);
139
+ expect(firstVisibleIndex).not.toBe(-1);
140
+ });
141
+
142
+ // Test 10: Partial visibility - item just starting to appear
143
+ it("finds item that is just starting to become visible", () => {
144
+ const layouts = createMockLayouts(10, 0, 100, false);
145
+
146
+ // Threshold just 1px before item 4 ends
147
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 399, false);
148
+ expect(firstVisibleIndex).toBe(3);
149
+ });
150
+ });
151
+
152
+ describe("findLastVisibleIndex", () => {
153
+ // Test 11: Basic functionality - vertical layout
154
+ it("finds the last visible index in a vertical layout", () => {
155
+ const layoutManager = createPopulatedLayoutManager(
156
+ LayoutManagerType.LINEAR,
157
+ 20,
158
+ { horizontal: false }
159
+ );
160
+ const layouts = getAllLayouts(layoutManager);
161
+
162
+ // Viewport ends at y=250, so the third item (index 2) should be last visible
163
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 250, false);
164
+ expect(lastVisibleIndex).toBe(2);
165
+ });
166
+
167
+ // Test 12: Basic functionality - horizontal layout
168
+ it("finds the last visible index in a horizontal layout", () => {
169
+ const layoutManager = createPopulatedLayoutManager(
170
+ LayoutManagerType.LINEAR,
171
+ 20,
172
+ { horizontal: true }
173
+ );
174
+ const layouts = getAllLayouts(layoutManager);
175
+
176
+ // Viewport ends at x=250, so the third item (index 2) should be last visible
177
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 250, true);
178
+ expect(lastVisibleIndex).toBe(2);
179
+ });
180
+
181
+ // Test 13: Empty layouts array
182
+ it("returns -1 for empty layouts array", () => {
183
+ const lastVisibleIndex = findLastVisibleIndex([], 100, false);
184
+ expect(lastVisibleIndex).toBe(-1);
185
+ });
186
+
187
+ // Test 14: All items are within viewport
188
+ it("returns the last item index when all items are within viewport", () => {
189
+ const layouts = createMockLayouts(5, 0, 100, false);
190
+
191
+ // Viewport ends at y=1000, which includes all 5 items
192
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 1000, false);
193
+ expect(lastVisibleIndex).toBe(4); // Last item index is 4
194
+ });
195
+
196
+ // Test 15: No items are visible (threshold before all items)
197
+ it("returns -1 when no items are visible", () => {
198
+ const layouts = createMockLayouts(10, 100, 100, false);
199
+
200
+ // Threshold is before all items start
201
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 50, false);
202
+ expect(lastVisibleIndex).toBe(-1);
203
+ });
204
+
205
+ // Test 16: Edge case - threshold exactly at item boundary
206
+ it("returns correct index when threshold is exactly at item boundary", () => {
207
+ const layouts = createMockLayouts(10, 0, 100, false);
208
+
209
+ // Threshold exactly at the end of the 3rd item
210
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 300, false);
211
+ expect(lastVisibleIndex).toBe(3);
212
+ });
213
+
214
+ // Test 17: Edge case - threshold in the middle of an item
215
+ it("returns correct index when threshold is in the middle of an item", () => {
216
+ const layouts = createMockLayouts(10, 0, 100, false);
217
+
218
+ // Threshold in the middle of the 3rd item
219
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 250, false);
220
+ expect(lastVisibleIndex).toBe(2);
221
+ });
222
+
223
+ // Test 18: With grid layout
224
+ it("finds last visible index with grid layout", () => {
225
+ const layoutManager = createPopulatedLayoutManager(
226
+ LayoutManagerType.GRID,
227
+ 20,
228
+ { maxColumns: 2 }
229
+ );
230
+ const layouts = getAllLayouts(layoutManager);
231
+
232
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 350, false);
233
+ expect(lastVisibleIndex).not.toBe(-1);
234
+ });
235
+
236
+ // Test 19: With masonry layout - variable height items
237
+ it("finds last visible index with masonry layout and variable item sizes", () => {
238
+ const layoutManager = createPopulatedLayoutManager(
239
+ LayoutManagerType.MASONRY,
240
+ 20,
241
+ { maxColumns: 2 },
242
+ 100,
243
+ 100,
244
+ true // Variable size
245
+ );
246
+ const layouts = getAllLayouts(layoutManager);
247
+
248
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 400, false);
249
+ expect(lastVisibleIndex).not.toBe(-1);
250
+ });
251
+
252
+ // Test 20: Last item partially visible
253
+ it("includes last item when it's partially visible", () => {
254
+ const layouts = createMockLayouts(10, 0, 100, false);
255
+
256
+ // Threshold just 1px into the 5th item
257
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 401, false);
258
+ expect(lastVisibleIndex).toBe(4);
259
+ });
260
+ });
261
+
262
+ describe("Edge cases and complex scenarios", () => {
263
+ // Test 21: Single item layout
264
+ it("correctly handles single item layout for first visible", () => {
265
+ const layouts = createMockLayouts(1, 0, 100, false);
266
+
267
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 50, false);
268
+ expect(firstVisibleIndex).toBe(0);
269
+ });
270
+
271
+ // Test 22: Single item layout
272
+ it("correctly handles single item layout for last visible", () => {
273
+ const layouts = createMockLayouts(1, 0, 100, false);
274
+
275
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 50, false);
276
+ expect(lastVisibleIndex).toBe(0);
277
+ });
278
+
279
+ // Test 23: Variable size items for first visible index
280
+ it("correctly finds first visible with variable size items", () => {
281
+ const layouts: RVLayout[] = [
282
+ { x: 0, y: 0, width: 100, height: 50 },
283
+ { x: 0, y: 50, width: 100, height: 150 },
284
+ { x: 0, y: 200, width: 100, height: 75 },
285
+ { x: 0, y: 275, width: 100, height: 100 },
286
+ ];
287
+
288
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 175, false);
289
+ expect(firstVisibleIndex).toBe(1); // Second item is still visible at threshold 175
290
+ });
291
+
292
+ // Test 24: Variable size items for last visible index
293
+ it("correctly finds last visible with variable size items", () => {
294
+ const layouts: RVLayout[] = [
295
+ { x: 0, y: 0, width: 100, height: 50 },
296
+ { x: 0, y: 50, width: 100, height: 150 },
297
+ { x: 0, y: 200, width: 100, height: 75 },
298
+ { x: 0, y: 275, width: 100, height: 100 },
299
+ ];
300
+
301
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 225, false);
302
+ expect(lastVisibleIndex).toBe(2); // Third item is visible at threshold 225
303
+ });
304
+
305
+ // Test 25: Items with zero size
306
+ it("correctly handles items with zero size for first visible", () => {
307
+ const layouts: RVLayout[] = [
308
+ { x: 0, y: 0, width: 100, height: 0 },
309
+ { x: 0, y: 0, width: 100, height: 100 },
310
+ ];
311
+
312
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 0, false);
313
+ expect(firstVisibleIndex).toBe(0); // First item is at position but has zero height
314
+ });
315
+
316
+ // Test 26: Items with zero size
317
+ it("correctly handles items with zero size for last visible", () => {
318
+ const layouts: RVLayout[] = [
319
+ { x: 0, y: 0, width: 100, height: 100 },
320
+ { x: 0, y: 100, width: 100, height: 0 },
321
+ ];
322
+
323
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 100, false);
324
+ expect(lastVisibleIndex).toBe(1); // Second item is at threshold position but has zero height
325
+ });
326
+
327
+ // Test 27: Large number of items - performance test
328
+ it("efficiently finds first visible index in large dataset", () => {
329
+ const layouts = createMockLayouts(1000, 0, 100, false);
330
+
331
+ // Threshold in the middle of the list
332
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 50000, false);
333
+ expect(firstVisibleIndex).toBe(500);
334
+ });
335
+
336
+ // Test 28: Large number of items - performance test
337
+ it("efficiently finds last visible index in large dataset", () => {
338
+ const layouts = createMockLayouts(1000, 0, 100, false);
339
+
340
+ // Threshold in the middle of the list
341
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 50000, false);
342
+ expect(lastVisibleIndex).toBe(500);
343
+ });
344
+
345
+ // Test 29: Non-sequential indices
346
+ it("works with non-sequential indices for first visible", () => {
347
+ const layouts: RVLayout[] = [
348
+ { x: 0, y: 0, width: 100, height: 100 },
349
+ { x: 0, y: 100, width: 100, height: 100 },
350
+ { x: 0, y: 200, width: 100, height: 100 },
351
+ ];
352
+
353
+ const firstVisibleIndex = findFirstVisibleIndex(layouts, 150, false);
354
+ expect(firstVisibleIndex).toBe(1); // Second layout in the array, not index 1
355
+ });
356
+
357
+ // Test 30: Non-sequential indices
358
+ it("works with non-sequential indices for last visible", () => {
359
+ const layouts: RVLayout[] = [
360
+ { x: 0, y: 0, width: 100, height: 100 },
361
+ { x: 0, y: 100, width: 100, height: 100 },
362
+ { x: 0, y: 200, width: 100, height: 100 },
363
+ ];
364
+
365
+ const lastVisibleIndex = findLastVisibleIndex(layouts, 150, false);
366
+ expect(lastVisibleIndex).toBe(1); // Second layout in the array, not index 1
367
+ });
368
+ });
369
+ });
@@ -0,0 +1,141 @@
1
+ /* eslint-disable @shopify/typescript/prefer-pascal-case-enums */
2
+ import {
3
+ LayoutParams,
4
+ RVDimension,
5
+ RVLayout,
6
+ RVLayoutInfo,
7
+ RVLayoutManager,
8
+ } from "../../recyclerview/layout-managers/LayoutManager";
9
+ import { RVLinearLayoutManagerImpl } from "../../recyclerview/layout-managers/LinearLayoutManager";
10
+ import { RVGridLayoutManagerImpl } from "../../recyclerview/layout-managers/GridLayoutManager";
11
+ import { RVMasonryLayoutManagerImpl } from "../../recyclerview/layout-managers/MasonryLayoutManager";
12
+
13
+ /**
14
+ * Layout manager types available in the app
15
+ */
16
+ export enum LayoutManagerType {
17
+ LINEAR = "linear",
18
+ GRID = "grid",
19
+ MASONRY = "masonry",
20
+ }
21
+
22
+ /**
23
+ * Default window size for layout managers
24
+ */
25
+ const DEFAULT_WINDOW_SIZE: RVDimension = {
26
+ width: 400,
27
+ height: 900,
28
+ };
29
+
30
+ /**
31
+ * Create layout parameters with sensible defaults
32
+ */
33
+ export function createLayoutParams(
34
+ params: Partial<LayoutParams> = {}
35
+ ): LayoutParams {
36
+ return {
37
+ windowSize: params.windowSize || DEFAULT_WINDOW_SIZE,
38
+ horizontal: params.horizontal ?? false,
39
+ maxColumns: params.maxColumns ?? 1,
40
+ optimizeItemArrangement: params.optimizeItemArrangement ?? true,
41
+ overrideItemLayout: params.overrideItemLayout ?? (() => {}),
42
+ getItemType: params.getItemType ?? (() => "default"),
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Create a layout manager of the specified type
48
+ */
49
+ export function createLayoutManager(
50
+ type: LayoutManagerType,
51
+ params: Partial<LayoutParams> = {},
52
+ previousLayoutManager?: RVLayoutManager
53
+ ): RVLayoutManager {
54
+ const layoutParams = createLayoutParams(params);
55
+
56
+ switch (type) {
57
+ case LayoutManagerType.LINEAR:
58
+ return new RVLinearLayoutManagerImpl(layoutParams, previousLayoutManager);
59
+ case LayoutManagerType.GRID:
60
+ return new RVGridLayoutManagerImpl(layoutParams, previousLayoutManager);
61
+ case LayoutManagerType.MASONRY:
62
+ return new RVMasonryLayoutManagerImpl(
63
+ layoutParams,
64
+ previousLayoutManager
65
+ );
66
+ default:
67
+ throw new Error(`Unknown layout manager type: ${type}`);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Generate mock layout info for testing
73
+ */
74
+ export function createMockLayoutInfo(
75
+ index: number,
76
+ width: number,
77
+ height: number
78
+ ): RVLayoutInfo {
79
+ return {
80
+ index,
81
+ dimensions: {
82
+ width,
83
+ height,
84
+ },
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Populate layout data in a layout manager
90
+ */
91
+ export function populateLayouts(
92
+ layoutManager: RVLayoutManager,
93
+ itemCount: number,
94
+ itemWidth = 100,
95
+ itemHeight = 100,
96
+ variableSize = false
97
+ ): void {
98
+ const layoutInfos: RVLayoutInfo[] = [];
99
+
100
+ for (let i = 0; i < itemCount; i++) {
101
+ // If variableSize is true, add some randomness to the item dimensions
102
+ const width = variableSize ? itemWidth + (i % 3) * 20 : itemWidth;
103
+ const height = variableSize ? itemHeight + (i % 5) * 25 : itemHeight;
104
+
105
+ layoutInfos.push(createMockLayoutInfo(i, width, height));
106
+ }
107
+
108
+ layoutManager.modifyLayout(layoutInfos, itemCount);
109
+ }
110
+
111
+ /**
112
+ * Create and populate a layout manager in one step
113
+ */
114
+ export function createPopulatedLayoutManager(
115
+ type: LayoutManagerType,
116
+ itemCount: number,
117
+ params: Partial<LayoutParams> = {},
118
+ itemWidth = 100,
119
+ itemHeight = 100,
120
+ variableSize = false
121
+ ): RVLayoutManager {
122
+ const layoutManager = createLayoutManager(type, params);
123
+ populateLayouts(
124
+ layoutManager,
125
+ itemCount,
126
+ itemWidth,
127
+ itemHeight,
128
+ variableSize
129
+ );
130
+ return layoutManager;
131
+ }
132
+
133
+ /**
134
+ * Get all layouts from a layout manager
135
+ */
136
+ export function getAllLayouts(layoutManager: RVLayoutManager): RVLayout[] {
137
+ // Access the internal layouts array
138
+ return Array.from({ length: layoutManager.getLayoutCount() }, (_, index) =>
139
+ layoutManager.getLayout(index)
140
+ );
141
+ }