@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
@@ -20,32 +20,43 @@ export abstract class RVLayoutManager {
20
20
  protected layouts: RVLayout[];
21
21
  /** Dimensions of the visible window/viewport */
22
22
  protected windowSize: RVDimension;
23
- /** Information about item spans and sizes */
24
- protected spanSizeInfo: SpanSizeInfo = {};
25
23
  /** Maximum number of columns in the layout */
26
24
  protected maxColumns: number;
27
- /** Optional callback to override default item layout */
28
- protected overrideItemLayout?: (index: number, layout: SpanSizeInfo) => void;
29
25
 
30
26
  /** Whether to optimize item placement for better space utilization */
31
27
  protected optimizeItemArrangement: boolean;
32
28
 
33
29
  /** Flag indicating if the layout requires repainting */
34
- public requiresRepaint: boolean = false;
30
+ public requiresRepaint = false;
35
31
 
32
+ /** Optional callback to override default item layout */
33
+ private overrideItemLayout: (index: number, layout: SpanSizeInfo) => void;
36
34
  /** Optional function to determine item type */
37
- private _getItemType?: (index: number) => string | number;
35
+ private getItemType: (index: number) => string;
38
36
  /** Window for tracking average heights by item type */
39
37
  private heightAverageWindow: MultiTypeAverageWindow;
40
38
  /** Window for tracking average widths by item type */
41
39
  private widthAverageWindow: MultiTypeAverageWindow;
42
40
  /** Maximum number of items to process in a single layout pass */
43
- private maxItemsToProcess: number = 250; // TODO: make this dynamic
41
+ private maxItemsToProcess = 250;
42
+ /** Information about item spans and sizes */
43
+ private spanSizeInfo: SpanSizeInfo = {};
44
+ /** Span tracker for each item */
45
+ private spanTracker: (number | undefined)[] = [];
46
+
47
+ /** Current max index with changed layout */
48
+ private currentMaxIndexWithChangedLayout = -1;
49
+
50
+ /**
51
+ * Last index that was skipped during layout computation.
52
+ * Used to determine if a layout needs to be recomputed.
53
+ */
54
+ private lastSkippedLayoutIndex = Number.MAX_VALUE;
44
55
 
45
56
  constructor(params: LayoutParams, previousLayoutManager?: RVLayoutManager) {
46
57
  this.heightAverageWindow = new MultiTypeAverageWindow(5, 200);
47
58
  this.widthAverageWindow = new MultiTypeAverageWindow(5, 200);
48
- this._getItemType = params.getItemType;
59
+ this.getItemType = params.getItemType;
49
60
  this.overrideItemLayout = params.overrideItemLayout;
50
61
  this.layouts = previousLayoutManager?.layouts ?? [];
51
62
  if (previousLayoutManager) {
@@ -57,15 +68,6 @@ export abstract class RVLayoutManager {
57
68
  }
58
69
  }
59
70
 
60
- /**
61
- * Gets the type of an item at the given index.
62
- * @param index Index of the item
63
- * @returns Item type or "default" if not specified
64
- */
65
- private getItemType(index: number): string | number {
66
- return this._getItemType?.(index) ?? "default";
67
- }
68
-
69
71
  /**
70
72
  * Gets the estimated width for an item based on its type.
71
73
  * @param index Index of the item
@@ -114,8 +116,8 @@ export abstract class RVLayoutManager {
114
116
  /**
115
117
  * Gets indices of items currently visible in the viewport.
116
118
  * Uses binary search for efficient lookup.
117
- * @param unboundDimensionStart Start position of viewport
118
- * @param unboundDimensionEnd End position of viewport
119
+ * @param unboundDimensionStart Start position of viewport (start X or start Y)
120
+ * @param unboundDimensionEnd End position of viewport (end X or end Y)
119
121
  * @returns ConsecutiveNumbers containing visible indices
120
122
  */
121
123
  getVisibleLayouts(
@@ -157,7 +159,7 @@ export abstract class RVLayoutManager {
157
159
  }
158
160
  const startIndex = Math.min(...indices);
159
161
  // Recompute layouts starting from the smallest index in the original indices array
160
- this.recomputeLayouts(
162
+ this._recomputeLayouts(
161
163
  this.getMinRecomputeIndex(startIndex),
162
164
  this.getMaxRecomputeIndex(startIndex)
163
165
  );
@@ -169,41 +171,49 @@ export abstract class RVLayoutManager {
169
171
  * @param totalItemCount Total number of items in the list
170
172
  */
171
173
  modifyLayout(layoutInfo: RVLayoutInfo[], totalItemCount: number): void {
174
+ this.maxItemsToProcess = Math.max(
175
+ this.maxItemsToProcess,
176
+ layoutInfo.length * 10
177
+ );
172
178
  let minRecomputeIndex = Number.MAX_VALUE;
173
179
 
174
180
  if (this.layouts.length > totalItemCount) {
175
181
  this.layouts.length = totalItemCount;
182
+ this.spanTracker.length = totalItemCount;
176
183
  minRecomputeIndex = totalItemCount - 1; // <0 gets skipped so it's safe to set to totalItemCount - 1
177
184
  }
178
185
  // update average windows
179
186
  minRecomputeIndex = Math.min(
180
187
  minRecomputeIndex,
181
- this.computeEstimatesAndMinRecomputeIndex(layoutInfo)
188
+ this.computeEstimatesAndMinMaxChangedLayout(layoutInfo)
182
189
  );
183
190
 
184
191
  if (this.layouts.length < totalItemCount && totalItemCount > 0) {
185
192
  const startIndex = this.layouts.length;
186
193
  this.layouts.length = totalItemCount;
194
+ this.spanTracker.length = totalItemCount;
187
195
  for (let i = startIndex; i < totalItemCount; i++) {
188
196
  this.getLayout(i);
197
+ this.getSpan(i);
189
198
  }
190
199
  this.recomputeLayouts(startIndex, totalItemCount - 1);
191
200
  }
192
- minRecomputeIndex = Math.min(
193
- minRecomputeIndex,
194
- this.processLayoutInfo(layoutInfo, totalItemCount) ?? minRecomputeIndex
195
- );
201
+
196
202
  // compute minRecomputeIndex
203
+
197
204
  minRecomputeIndex = Math.min(
198
205
  minRecomputeIndex,
199
- this.computeEstimatesAndMinRecomputeIndex(layoutInfo)
206
+ this.lastSkippedLayoutIndex,
207
+ this.computeMinIndexWithChangedSpan(layoutInfo),
208
+ this.processLayoutInfo(layoutInfo, totalItemCount) ?? minRecomputeIndex,
209
+ this.computeEstimatesAndMinMaxChangedLayout(layoutInfo)
200
210
  );
211
+
201
212
  if (minRecomputeIndex >= 0 && minRecomputeIndex < totalItemCount) {
202
- this.recomputeLayouts(
203
- this.getMinRecomputeIndex(minRecomputeIndex),
204
- this.getMaxRecomputeIndex(minRecomputeIndex)
205
- );
213
+ const maxRecomputeIndex = this.getMaxRecomputeIndex(minRecomputeIndex);
214
+ this._recomputeLayouts(minRecomputeIndex, maxRecomputeIndex);
206
215
  }
216
+ this.currentMaxIndexWithChangedLayout = -1;
207
217
  }
208
218
 
209
219
  /**
@@ -245,6 +255,10 @@ export abstract class RVLayoutManager {
245
255
  params.optimizeItemArrangement ?? this.optimizeItemArrangement;
246
256
  }
247
257
 
258
+ getLayoutCount(): number {
259
+ return this.layouts.length;
260
+ }
261
+
248
262
  /**
249
263
  * Abstract method to recompute layouts for items in the given range.
250
264
  * @param startIndex Starting index of items to recompute
@@ -265,16 +279,30 @@ export abstract class RVLayoutManager {
265
279
  protected abstract estimateLayout(index: number): void;
266
280
 
267
281
  /**
268
- * Gets span size information for an item, applying any overrides.
282
+ * Gets span for an item, applying any overrides.
283
+ * This is intended to be called during a relayout call. The value is tracked and used to determine if a span change has occurred.
284
+ * If skipTracking is true, the operation is not tracked. Can be useful if span is required outside of a relayout call.
285
+ * The tracker is used to call handleSpanChange if a span change has occurred before relayout call.
286
+ * // TODO: improve this contract.
269
287
  * @param index Index of the item
270
- * @returns SpanSizeInfo for the item
288
+ * @returns Span for the item
271
289
  */
272
- protected getSpanSizeInfo(index: number): SpanSizeInfo {
290
+ protected getSpan(index: number, skipTracking = false): number {
273
291
  this.spanSizeInfo.span = undefined;
274
- this.overrideItemLayout?.(index, this.spanSizeInfo);
275
- return this.spanSizeInfo;
292
+ this.overrideItemLayout(index, this.spanSizeInfo);
293
+ const span = Math.min(this.spanSizeInfo.span ?? 1, this.maxColumns);
294
+ if (!skipTracking) {
295
+ this.spanTracker[index] = span;
296
+ }
297
+ return span;
276
298
  }
277
299
 
300
+ /**
301
+ * Method to handle span change for an item. Can be overridden by subclasses.
302
+ * @param index Index of the item
303
+ */
304
+ protected handleSpanChange(index: number) {}
305
+
278
306
  /**
279
307
  * Gets the maximum index to process in a single layout pass.
280
308
  * @param startIndex Starting index
@@ -282,7 +310,8 @@ export abstract class RVLayoutManager {
282
310
  */
283
311
  private getMaxRecomputeIndex(startIndex: number): number {
284
312
  return Math.min(
285
- startIndex + this.maxItemsToProcess,
313
+ Math.max(startIndex, this.currentMaxIndexWithChangedLayout) +
314
+ this.maxItemsToProcess,
286
315
  this.layouts.length - 1
287
316
  );
288
317
  }
@@ -296,12 +325,36 @@ export abstract class RVLayoutManager {
296
325
  return startIndex;
297
326
  }
298
327
 
328
+ private _recomputeLayouts(startIndex: number, endIndex: number): void {
329
+ this.recomputeLayouts(startIndex, endIndex);
330
+ if (
331
+ this.lastSkippedLayoutIndex >= startIndex &&
332
+ this.lastSkippedLayoutIndex <= endIndex
333
+ ) {
334
+ this.lastSkippedLayoutIndex = Number.MAX_VALUE;
335
+ }
336
+
337
+ if (endIndex + 1 < this.layouts.length) {
338
+ this.lastSkippedLayoutIndex = Math.min(
339
+ endIndex + 1,
340
+ this.lastSkippedLayoutIndex
341
+ );
342
+ const lastIndex = this.layouts.length - 1;
343
+ // Since layout managers derive height from last indices we need to make
344
+ // sure they're not too much out of sync.
345
+ if (this.layouts[lastIndex].y < this.layouts[endIndex].y) {
346
+ this.recomputeLayouts(this.lastSkippedLayoutIndex, lastIndex);
347
+ this.lastSkippedLayoutIndex = Number.MAX_VALUE;
348
+ }
349
+ }
350
+ }
351
+
299
352
  /**
300
353
  * Computes size estimates and finds the minimum recompute index.
301
354
  * @param layoutInfo Array of layout information for items
302
355
  * @returns Minimum index that needs recomputation
303
356
  */
304
- private computeEstimatesAndMinRecomputeIndex(
357
+ private computeEstimatesAndMinMaxChangedLayout(
305
358
  layoutInfo: RVLayoutInfo[]
306
359
  ): number {
307
360
  let minRecomputeIndex = Number.MAX_VALUE;
@@ -309,12 +362,18 @@ export abstract class RVLayoutManager {
309
362
  const { index, dimensions } = info;
310
363
  const storedLayout = this.layouts[index];
311
364
  if (
365
+ index >= this.lastSkippedLayoutIndex ||
366
+ !storedLayout ||
312
367
  !storedLayout.isHeightMeasured ||
313
368
  !storedLayout.isWidthMeasured ||
314
369
  areDimensionsNotEqual(storedLayout.height, dimensions.height) ||
315
370
  areDimensionsNotEqual(storedLayout.width, dimensions.width)
316
371
  ) {
317
372
  minRecomputeIndex = Math.min(minRecomputeIndex, index);
373
+ this.currentMaxIndexWithChangedLayout = Math.max(
374
+ this.currentMaxIndexWithChangedLayout,
375
+ index
376
+ );
318
377
  }
319
378
  this.heightAverageWindow.addValue(
320
379
  dimensions.height,
@@ -327,6 +386,21 @@ export abstract class RVLayoutManager {
327
386
  }
328
387
  return minRecomputeIndex;
329
388
  }
389
+
390
+ private computeMinIndexWithChangedSpan(layoutInfo: RVLayoutInfo[]): number {
391
+ let minIndexWithChangedSpan = Number.MAX_VALUE;
392
+ for (const info of layoutInfo) {
393
+ const { index } = info;
394
+ const span = this.getSpan(index, true);
395
+ const storedSpan = this.spanTracker[index];
396
+ if (span !== storedSpan) {
397
+ this.spanTracker[index] = span;
398
+ this.handleSpanChange(index);
399
+ minIndexWithChangedSpan = Math.min(minIndexWithChangedSpan, index);
400
+ }
401
+ }
402
+ return minIndexWithChangedSpan;
403
+ }
330
404
  }
331
405
 
332
406
  /**
@@ -343,31 +417,31 @@ export interface LayoutParams {
343
417
  * Determines if the list scrolls horizontally (true) or vertically (false)
344
418
  * Affects how items are positioned and which dimension is used for scrolling
345
419
  */
346
- horizontal?: boolean;
420
+ horizontal: boolean;
347
421
 
348
422
  /**
349
423
  * Maximum number of columns in a grid layout
350
424
  * Controls how many items can be placed side by side
351
425
  */
352
- maxColumns?: number;
426
+ maxColumns: number;
353
427
 
354
428
  /**
355
429
  * When true, attempts to optimize item placement for better space utilization
356
430
  * May affect the ordering of items to minimize empty space
357
431
  */
358
- optimizeItemArrangement?: boolean;
432
+ optimizeItemArrangement: boolean;
359
433
 
360
434
  /**
361
435
  * Callback to manually override layout properties for specific items
362
436
  * Allows custom control over span and size for individual items
363
437
  */
364
- overrideItemLayout?: (index: number, layout: SpanSizeInfo) => void;
438
+ overrideItemLayout: (index: number, layout: SpanSizeInfo) => void;
365
439
 
366
440
  /**
367
441
  * Function to determine the type of an item at a specific index
368
442
  * Used for size estimation and optimization based on item types
369
443
  */
370
- getItemType?: (index: number) => string | number;
444
+ getItemType: (index: number) => string;
371
445
  }
372
446
 
373
447
  /**
@@ -1,6 +1,10 @@
1
- import { LayoutParams, RVDimension, RVLayoutInfo } from "./LayoutManager";
2
- import { RVLayout } from "./LayoutManager";
3
- import { RVLayoutManager } from "./LayoutManager";
1
+ import {
2
+ LayoutParams,
3
+ RVDimension,
4
+ RVLayoutInfo,
5
+ RVLayout,
6
+ RVLayoutManager,
7
+ } from "./LayoutManager";
4
8
 
5
9
  /**
6
10
  * LinearLayoutManager implementation that arranges items in a single row or column.
@@ -10,12 +14,12 @@ export class RVLinearLayoutManagerImpl extends RVLayoutManager {
10
14
  /** The bounded size (width for vertical, height for horizontal) */
11
15
  private boundedSize: number;
12
16
  /** Whether the bounded size has been set */
13
- private hasSize: boolean = false;
17
+ private hasSize = false;
14
18
 
15
19
  /** Reference to the tallest item in the layout */
16
20
  private tallestItem?: RVLayout;
17
21
  /** Height of the tallest item */
18
- private tallestItemHeight: number = 0;
22
+ private tallestItemHeight = 0;
19
23
 
20
24
  constructor(params: LayoutParams, previousLayoutManager?: RVLayoutManager) {
21
25
  super(params, previousLayoutManager);
@@ -41,7 +45,7 @@ export class RVLinearLayoutManagerImpl extends RVLayoutManager {
41
45
  prevHorizontal !== this.horizontal
42
46
  ) {
43
47
  if (this.layouts.length > 0) {
44
- //console.log("-----> recomputeLayouts", this.horizontal);
48
+ // console.log("-----> recomputeLayouts", this.horizontal);
45
49
  this.recomputeLayouts(0, this.layouts.length - 1);
46
50
  this.requiresRepaint = true;
47
51
  }
@@ -96,7 +100,7 @@ export class RVLinearLayoutManagerImpl extends RVLayoutManager {
96
100
  ? lastLayout.x + lastLayout.width
97
101
  : this.boundedSize,
98
102
  height: this.horizontal
99
- ? this.tallestItem?.height ?? 0
103
+ ? this.tallestItem?.height ?? this.boundedSize
100
104
  : lastLayout.y + lastLayout.height,
101
105
  };
102
106
  }
@@ -123,7 +127,7 @@ export class RVLinearLayoutManagerImpl extends RVLayoutManager {
123
127
  this.requiresRepaint = true;
124
128
  targetMinHeight = 0;
125
129
  }
126
- //set minHeight for all layouts
130
+ // set minHeight for all layouts
127
131
  for (const layout of this.layouts) {
128
132
  if (targetMinHeight > 0) {
129
133
  layout.height = newTallestItem.height;
@@ -17,13 +17,16 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
17
17
  /** Array tracking the current height of each column */
18
18
  private columnHeights: number[];
19
19
  /** Current column index for sequential placement */
20
- private currentColumn: number = 0;
20
+ private currentColumn = 0;
21
+
22
+ /** If there's a span change for masonry layout, we need to recompute all the widths */
23
+ private fullRelayoutRequired = false;
21
24
 
22
25
  constructor(params: LayoutParams, previousLayoutManager?: RVLayoutManager) {
23
26
  super(params, previousLayoutManager);
24
27
  this.boundedSize = params.windowSize.width;
25
- this.optimizeItemArrangement = params.optimizeItemArrangement ?? false;
26
- this.columnHeights = Array(this.maxColumns).fill(0);
28
+ this.optimizeItemArrangement = params.optimizeItemArrangement;
29
+ this.columnHeights = this.columnHeights ?? Array(this.maxColumns).fill(0);
27
30
  }
28
31
 
29
32
  /**
@@ -41,14 +44,10 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
41
44
  ) {
42
45
  this.boundedSize = params.windowSize.width;
43
46
  if (this.layouts.length > 0) {
44
- //console.log("-----> recomputeLayouts");
47
+ // console.log("-----> recomputeLayouts");
45
48
 
46
- //update all widths
47
- for (let i = 0; i < this.layouts.length; i++) {
48
- this.layouts[i].width = this.getWidth(i);
49
- this.layouts[i].minHeight = undefined;
50
- }
51
- //TODO: Optimize masonry in general
49
+ // update all widths
50
+ this.updateAllWidths();
52
51
  this.recomputeLayouts(0, this.layouts.length - 1);
53
52
  this.requiresRepaint = true;
54
53
  }
@@ -70,6 +69,13 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
70
69
  layout.isWidthMeasured = true;
71
70
  this.layouts[index] = layout;
72
71
  }
72
+
73
+ // TODO: Can be optimized
74
+ if (this.fullRelayoutRequired) {
75
+ this.updateAllWidths();
76
+ this.fullRelayoutRequired = false;
77
+ return 0;
78
+ }
73
79
  }
74
80
 
75
81
  /**
@@ -88,6 +94,14 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
88
94
  layout.enforcedWidth = true;
89
95
  }
90
96
 
97
+ /**
98
+ * Handles span change for an item.
99
+ * @param index Index of the item
100
+ */
101
+ handleSpanChange(index: number) {
102
+ this.fullRelayoutRequired = true;
103
+ }
104
+
91
105
  /**
92
106
  * Returns the total size of the layout area.
93
107
  * @returns RVDimension containing width and height of the layout
@@ -125,7 +139,8 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
125
139
 
126
140
  for (let i = startIndex; i < itemCount; i++) {
127
141
  const layout = this.getLayout(i);
128
- const span = this.getSpanSizeInfo(i).span ?? 1;
142
+ // Skip tracking span because we're not changing widths
143
+ const span = this.getSpan(i, true);
129
144
 
130
145
  if (this.optimizeItemArrangement) {
131
146
  if (span === 1) {
@@ -148,8 +163,14 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
148
163
  * @returns Width of the item
149
164
  */
150
165
  private getWidth(index: number): number {
151
- const span = this.getSpanSizeInfo(index).span ?? 1;
152
- return (this.boundedSize / this.maxColumns) * span;
166
+ return (this.boundedSize / this.maxColumns) * this.getSpan(index);
167
+ }
168
+
169
+ private updateAllWidths() {
170
+ for (let i = 0; i < this.layouts.length; i++) {
171
+ this.layouts[i].width = this.getWidth(i);
172
+ this.layouts[i].minHeight = undefined;
173
+ }
153
174
  }
154
175
 
155
176
  /**
@@ -17,7 +17,7 @@ import React from "react";
17
17
  * getValidComponent(<MyComponent />)
18
18
  */
19
19
  export const getValidComponent = (
20
- component: React.ComponentType<any> | React.ReactElement | null | undefined
20
+ component: React.ComponentType | React.ReactElement | null | undefined
21
21
  ): React.ReactElement | null => {
22
22
  if (React.isValidElement(component)) {
23
23
  return component;
@@ -33,10 +33,9 @@ function binarySearchVisibleIndex(
33
33
  const position = isSortedByX ? layout.x : layout.y;
34
34
  const size = isSortedByX ? layout.width : layout.height;
35
35
 
36
- //TODO: Will this find item bigger than viewport
37
36
  if (findFirst) {
38
37
  // Logic for finding the first visible index
39
- if (position >= threshold || position + size >= threshold) {
38
+ if (position >= threshold || position + size > threshold) {
40
39
  // Potential visible index found, continue searching left for earlier visible items
41
40
  visibleIndex = mid;
42
41
  right = mid - 1;
@@ -15,7 +15,7 @@ interface Layout {
15
15
  * @param view - The React Native View component to measure
16
16
  * @returns An object containing x, y, width, and height measurements
17
17
  */
18
- export function measureLayout(view: View, oldLayout: Layout | undefined) {
18
+ function measureLayout(view: View, oldLayout: Layout | undefined) {
19
19
  // const layout = view.unstable_getBoundingClientRect();
20
20
  // layout.width = roundOffPixel(layout.width);
21
21
  // layout.height = roundOffPixel(layout.height);
@@ -31,7 +31,7 @@ export function measureLayout(view: View, oldLayout: Layout | undefined) {
31
31
  * @param relativeTo - The reference view to measure against
32
32
  * @returns An object containing x, y, width, and height measurements
33
33
  */
34
- export function measureLayoutRelative(
34
+ function measureLayoutRelative(
35
35
  view: View,
36
36
  relativeTo: View,
37
37
  oldLayout: Layout | undefined
@@ -87,3 +87,42 @@ export function areDimensionsEqual(value1: number, value2: number): boolean {
87
87
  export function roundOffPixel(value: number): number {
88
88
  return PixelRatio.roundToNearestPixel(value);
89
89
  }
90
+
91
+ /**
92
+ * Specific method for easier mocking
93
+ * Measures the layout of parent of RecyclerView
94
+ * Returns the x, y coordinates and dimensions of the view.
95
+ * @param view - The React Native View component to measure
96
+ * @returns An object containing x, y, width, and height measurements
97
+ */
98
+ export function measureParentSize(view: View): Layout {
99
+ return measureLayout(view, undefined);
100
+ }
101
+
102
+ /**
103
+ * Specific method for easier mocking
104
+ * Measures the layout of child container of RecyclerView
105
+ * @param childContainerView
106
+ * @param parentView
107
+ * @returns
108
+ */
109
+ export function measureFirstChildLayout(
110
+ childContainerView: View,
111
+ parentView: View
112
+ ): Layout {
113
+ return measureLayoutRelative(childContainerView, parentView, undefined);
114
+ }
115
+
116
+ /**
117
+ * Specific method for easier mocking
118
+ * Measures the layout of items of RecyclerView
119
+ * @param item
120
+ * @param oldLayout
121
+ * @returns
122
+ */
123
+ export function measureItemLayout(
124
+ item: View,
125
+ oldLayout: Layout | undefined
126
+ ): Layout {
127
+ return measureLayout(item, oldLayout);
128
+ }
@@ -0,0 +1,102 @@
1
+ interface Layout {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ }
7
+
8
+ /**
9
+ * Gets scroll offsets from up to 3 parent elements
10
+ */
11
+ function getScrollOffsets(element: Element, stopAt: Element) {
12
+ let scrollX = 0;
13
+ let scrollY = 0;
14
+ let currentElement: Element | null = element;
15
+
16
+ // Only check up to 3 parent elements
17
+ while (currentElement && currentElement !== stopAt) {
18
+ const htmlElement = currentElement as HTMLElement;
19
+ scrollX += htmlElement.scrollLeft ?? 0;
20
+ scrollY += htmlElement.scrollTop ?? 0;
21
+ currentElement = currentElement.parentElement;
22
+ }
23
+
24
+ return { scrollX, scrollY };
25
+ }
26
+
27
+ /**
28
+ * Checks if two dimension values are not equal, with a small tolerance.
29
+ */
30
+ export function areDimensionsNotEqual(value1: number, value2: number): boolean {
31
+ return !areDimensionsEqual(value1, value2);
32
+ }
33
+
34
+ /**
35
+ * Checks if two dimension values are equal, with a small tolerance.
36
+ */
37
+ export function areDimensionsEqual(value1: number, value2: number): boolean {
38
+ return Math.abs(value1 - value2) <= 1;
39
+ }
40
+
41
+ export function roundOffPixel(value: number): number {
42
+ return value;
43
+ }
44
+
45
+ /**
46
+ * Measures the layout of parent of RecyclerView
47
+ */
48
+ export function measureParentSize(view: Element): Layout {
49
+ return {
50
+ x: 0,
51
+ y: 0,
52
+ width: view.clientWidth,
53
+ height: view.clientHeight,
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Measures the layout of child container of RecyclerView
59
+ */
60
+ export function measureFirstChildLayout(
61
+ childContainerView: Element,
62
+ parentView: Element
63
+ ): Layout {
64
+ const childRect = childContainerView.getBoundingClientRect();
65
+ const parentRect = parentView.getBoundingClientRect();
66
+
67
+ // Get scroll offsets for child container (max 3 parents)
68
+ const scrollOffsets = getScrollOffsets(childContainerView, parentView);
69
+
70
+ return {
71
+ x: childRect.left - parentRect.left + scrollOffsets.scrollX,
72
+ y: childRect.top - parentRect.top + scrollOffsets.scrollY,
73
+ width: roundOffPixel(childRect.width),
74
+ height: roundOffPixel(childRect.height),
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Measures the layout of items of RecyclerView
80
+ */
81
+ export function measureItemLayout(
82
+ item: Element,
83
+ oldLayout: Layout | undefined
84
+ ): Layout {
85
+ const layout = {
86
+ x: 0,
87
+ y: 0,
88
+ width: item.clientWidth,
89
+ height: item.clientHeight,
90
+ };
91
+
92
+ if (oldLayout) {
93
+ if (areDimensionsEqual(layout.width, oldLayout.width)) {
94
+ layout.width = oldLayout.width;
95
+ }
96
+ if (areDimensionsEqual(layout.height, oldLayout.height)) {
97
+ layout.height = oldLayout.height;
98
+ }
99
+ }
100
+
101
+ return layout;
102
+ }
@@ -1,5 +1,5 @@
1
- export default interface ViewToken {
2
- item: any; //TODO: fix this type
1
+ export default interface ViewToken<T> {
2
+ item: T;
3
3
  key: string;
4
4
  index: number | null;
5
5
  isViewable: boolean;
@@ -89,8 +89,8 @@ class ViewabilityHelper {
89
89
  const timeoutId = setTimeout(() => {
90
90
  this.timers.delete(timeoutId);
91
91
  this.checkViewableIndicesChanges(newViewableIndices);
92
- this.timers.add(timeoutId);
93
92
  }, minimumViewTime);
93
+ this.timers.add(timeoutId);
94
94
  } else {
95
95
  this.checkViewableIndicesChanges(newViewableIndices);
96
96
  }