@shopify/flash-list 1.8.0 → 2.0.0-alpha.10

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 (317) hide show
  1. package/README.md +179 -27
  2. package/dist/AnimatedFlashList.d.ts +0 -1
  3. package/dist/AnimatedFlashList.d.ts.map +1 -1
  4. package/dist/FlashList.js +2 -3
  5. package/dist/FlashList.js.map +1 -1
  6. package/dist/FlashListProps.d.ts +68 -5
  7. package/dist/FlashListProps.d.ts.map +1 -1
  8. package/dist/GridLayoutProviderWithProps.js +1 -2
  9. package/dist/GridLayoutProviderWithProps.js.map +1 -1
  10. package/dist/MasonryFlashList.d.ts +2 -2
  11. package/dist/MasonryFlashList.d.ts.map +1 -1
  12. package/dist/MasonryFlashList.js.map +1 -1
  13. package/dist/PureComponentWrapper.js +1 -1
  14. package/dist/PureComponentWrapper.js.map +1 -1
  15. package/dist/__tests__/AverageWindow.test.js +35 -0
  16. package/dist/__tests__/AverageWindow.test.js.map +1 -1
  17. package/dist/__tests__/ConsecutiveNumbers.test.d.ts +2 -0
  18. package/dist/__tests__/ConsecutiveNumbers.test.d.ts.map +1 -0
  19. package/dist/__tests__/ConsecutiveNumbers.test.js +224 -0
  20. package/dist/__tests__/ConsecutiveNumbers.test.js.map +1 -0
  21. package/dist/__tests__/FlashList.test.js.map +1 -1
  22. package/dist/__tests__/GridLayoutManager.test.d.ts +2 -0
  23. package/dist/__tests__/GridLayoutManager.test.d.ts.map +1 -0
  24. package/dist/__tests__/GridLayoutManager.test.js +69 -0
  25. package/dist/__tests__/GridLayoutManager.test.js.map +1 -0
  26. package/dist/__tests__/GridLayoutProviderWithProps.test.js.map +1 -1
  27. package/dist/__tests__/LinearLayoutManager.test.d.ts +2 -0
  28. package/dist/__tests__/LinearLayoutManager.test.d.ts.map +1 -0
  29. package/dist/__tests__/LinearLayoutManager.test.js +140 -0
  30. package/dist/__tests__/LinearLayoutManager.test.js.map +1 -0
  31. package/dist/__tests__/MasonryFlashList.test.js.map +1 -1
  32. package/dist/__tests__/MasonryLayoutManager.test.d.ts +2 -0
  33. package/dist/__tests__/MasonryLayoutManager.test.d.ts.map +1 -0
  34. package/dist/__tests__/MasonryLayoutManager.test.js +148 -0
  35. package/dist/__tests__/MasonryLayoutManager.test.js.map +1 -0
  36. package/dist/__tests__/RecycleKeyManager.test.d.ts +2 -0
  37. package/dist/__tests__/RecycleKeyManager.test.d.ts.map +1 -0
  38. package/dist/__tests__/RecycleKeyManager.test.js +210 -0
  39. package/dist/__tests__/RecycleKeyManager.test.js.map +1 -0
  40. package/dist/__tests__/RecyclerView.test.d.ts +2 -0
  41. package/dist/__tests__/RecyclerView.test.d.ts.map +1 -0
  42. package/dist/__tests__/RecyclerView.test.js +59 -0
  43. package/dist/__tests__/RecyclerView.test.js.map +1 -0
  44. package/dist/__tests__/ViewabilityHelper.test.js.map +1 -1
  45. package/dist/__tests__/findVisibleIndex.test.d.ts +2 -0
  46. package/dist/__tests__/findVisibleIndex.test.d.ts.map +1 -0
  47. package/dist/__tests__/findVisibleIndex.test.js +259 -0
  48. package/dist/__tests__/findVisibleIndex.test.js.map +1 -0
  49. package/dist/__tests__/helpers/createLayoutManager.d.ts +34 -0
  50. package/dist/__tests__/helpers/createLayoutManager.d.ts.map +1 -0
  51. package/dist/__tests__/helpers/createLayoutManager.js +111 -0
  52. package/dist/__tests__/helpers/createLayoutManager.js.map +1 -0
  53. package/dist/__tests__/helpers/mountFlashList.d.ts +2 -2
  54. package/dist/__tests__/helpers/mountFlashList.d.ts.map +1 -1
  55. package/dist/__tests__/helpers/mountFlashList.js +2 -2
  56. package/dist/__tests__/helpers/mountFlashList.js.map +1 -1
  57. package/dist/__tests__/helpers/mountMasonryFlashList.d.ts +2 -2
  58. package/dist/__tests__/helpers/mountMasonryFlashList.d.ts.map +1 -1
  59. package/dist/__tests__/helpers/mountMasonryFlashList.js +2 -2
  60. package/dist/__tests__/helpers/mountMasonryFlashList.js.map +1 -1
  61. package/dist/__tests__/useBlankAreaTracker.test.js.map +1 -1
  62. package/dist/__tests__/useUnmountAwareCallbacks.test.d.ts +2 -0
  63. package/dist/__tests__/useUnmountAwareCallbacks.test.d.ts.map +1 -0
  64. package/dist/__tests__/useUnmountAwareCallbacks.test.js +185 -0
  65. package/dist/__tests__/useUnmountAwareCallbacks.test.js.map +1 -0
  66. package/dist/benchmark/AutoScrollHelper.js +2 -2
  67. package/dist/benchmark/AutoScrollHelper.js.map +1 -1
  68. package/dist/benchmark/JSFPSMonitor.js.map +1 -1
  69. package/dist/benchmark/roundToDecimalPlaces.js +1 -2
  70. package/dist/benchmark/roundToDecimalPlaces.js.map +1 -1
  71. package/dist/benchmark/useBenchmark.js +2 -3
  72. package/dist/benchmark/useBenchmark.js.map +1 -1
  73. package/dist/benchmark/useBlankAreaTracker.js +1 -2
  74. package/dist/benchmark/useBlankAreaTracker.js.map +1 -1
  75. package/dist/benchmark/useDataMultiplier.js +1 -2
  76. package/dist/benchmark/useDataMultiplier.js.map +1 -1
  77. package/dist/benchmark/useFlatListBenchmark.d.ts +0 -1
  78. package/dist/benchmark/useFlatListBenchmark.d.ts.map +1 -1
  79. package/dist/benchmark/useFlatListBenchmark.js +1 -2
  80. package/dist/benchmark/useFlatListBenchmark.js.map +1 -1
  81. package/dist/enableNewCore.d.ts +3 -0
  82. package/dist/enableNewCore.d.ts.map +1 -0
  83. package/dist/enableNewCore.js +25 -0
  84. package/dist/enableNewCore.js.map +1 -0
  85. package/dist/errors/CustomError.js.map +1 -1
  86. package/dist/index.d.ts +6 -0
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +34 -8
  89. package/dist/index.js.map +1 -1
  90. package/dist/native/auto-layout/AutoLayoutView.d.ts +1 -1
  91. package/dist/native/auto-layout/AutoLayoutView.d.ts.map +1 -1
  92. package/dist/native/auto-layout/AutoLayoutView.js +1 -1
  93. package/dist/native/auto-layout/AutoLayoutView.js.map +1 -1
  94. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.d.ts.map +1 -1
  95. package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.d.ts +1 -1
  96. package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.d.ts.map +1 -1
  97. package/dist/native/config/PlatformHelper.android.d.ts +2 -0
  98. package/dist/native/config/PlatformHelper.android.d.ts.map +1 -1
  99. package/dist/native/config/PlatformHelper.android.js +2 -0
  100. package/dist/native/config/PlatformHelper.android.js.map +1 -1
  101. package/dist/native/config/PlatformHelper.d.ts +2 -0
  102. package/dist/native/config/PlatformHelper.d.ts.map +1 -1
  103. package/dist/native/config/PlatformHelper.ios.d.ts +2 -0
  104. package/dist/native/config/PlatformHelper.ios.d.ts.map +1 -1
  105. package/dist/native/config/PlatformHelper.ios.js +2 -0
  106. package/dist/native/config/PlatformHelper.ios.js.map +1 -1
  107. package/dist/native/config/PlatformHelper.js +2 -0
  108. package/dist/native/config/PlatformHelper.js.map +1 -1
  109. package/dist/native/config/PlatformHelper.web.d.ts +2 -0
  110. package/dist/native/config/PlatformHelper.web.d.ts.map +1 -1
  111. package/dist/native/config/PlatformHelper.web.js +3 -1
  112. package/dist/native/config/PlatformHelper.web.js.map +1 -1
  113. package/dist/recyclerview/RecycleKeyManager.d.ts +82 -0
  114. package/dist/recyclerview/RecycleKeyManager.d.ts.map +1 -0
  115. package/dist/recyclerview/RecycleKeyManager.js +135 -0
  116. package/dist/recyclerview/RecycleKeyManager.js.map +1 -0
  117. package/dist/recyclerview/RecyclerView.d.ts +12 -0
  118. package/dist/recyclerview/RecyclerView.d.ts.map +1 -0
  119. package/dist/recyclerview/RecyclerView.js +300 -0
  120. package/dist/recyclerview/RecyclerView.js.map +1 -0
  121. package/dist/recyclerview/RecyclerViewContextProvider.d.ts +11 -0
  122. package/dist/recyclerview/RecyclerViewContextProvider.d.ts.map +1 -0
  123. package/dist/recyclerview/RecyclerViewContextProvider.js +11 -0
  124. package/dist/recyclerview/RecyclerViewContextProvider.js.map +1 -0
  125. package/dist/recyclerview/RecyclerViewManager.d.ts +62 -0
  126. package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -0
  127. package/dist/recyclerview/RecyclerViewManager.js +371 -0
  128. package/dist/recyclerview/RecyclerViewManager.js.map +1 -0
  129. package/dist/recyclerview/RecyclerViewProps.d.ts +9 -0
  130. package/dist/recyclerview/RecyclerViewProps.d.ts.map +1 -0
  131. package/dist/recyclerview/RecyclerViewProps.js +3 -0
  132. package/dist/recyclerview/RecyclerViewProps.js.map +1 -0
  133. package/dist/recyclerview/ViewHolder.d.ts +45 -0
  134. package/dist/recyclerview/ViewHolder.d.ts.map +1 -0
  135. package/dist/recyclerview/ViewHolder.js +96 -0
  136. package/dist/recyclerview/ViewHolder.js.map +1 -0
  137. package/dist/recyclerview/ViewHolderCollection.d.ts +57 -0
  138. package/dist/recyclerview/ViewHolderCollection.d.ts.map +1 -0
  139. package/dist/recyclerview/ViewHolderCollection.js +74 -0
  140. package/dist/recyclerview/ViewHolderCollection.js.map +1 -0
  141. package/dist/recyclerview/components/CompatScroller.d.ts +7 -0
  142. package/dist/recyclerview/components/CompatScroller.d.ts.map +1 -0
  143. package/dist/recyclerview/components/CompatScroller.js +8 -0
  144. package/dist/recyclerview/components/CompatScroller.js.map +1 -0
  145. package/dist/recyclerview/components/CompatView.d.ts +7 -0
  146. package/dist/recyclerview/components/CompatView.d.ts.map +1 -0
  147. package/dist/recyclerview/components/CompatView.js +8 -0
  148. package/dist/recyclerview/components/CompatView.js.map +1 -0
  149. package/dist/recyclerview/components/ScrollAnchor.d.ts +29 -0
  150. package/dist/recyclerview/components/ScrollAnchor.d.ts.map +1 -0
  151. package/dist/recyclerview/components/ScrollAnchor.js +34 -0
  152. package/dist/recyclerview/components/ScrollAnchor.js.map +1 -0
  153. package/dist/recyclerview/components/StickyHeaders.d.ts +38 -0
  154. package/dist/recyclerview/components/StickyHeaders.d.ts.map +1 -0
  155. package/dist/recyclerview/components/StickyHeaders.js +111 -0
  156. package/dist/recyclerview/components/StickyHeaders.js.map +1 -0
  157. package/dist/recyclerview/helpers/ConsecutiveNumbers.d.ts +51 -0
  158. package/dist/recyclerview/helpers/ConsecutiveNumbers.d.ts.map +1 -0
  159. package/dist/recyclerview/helpers/ConsecutiveNumbers.js +122 -0
  160. package/dist/recyclerview/helpers/ConsecutiveNumbers.js.map +1 -0
  161. package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts +105 -0
  162. package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts.map +1 -0
  163. package/dist/recyclerview/helpers/EngagedIndicesTracker.js +209 -0
  164. package/dist/recyclerview/helpers/EngagedIndicesTracker.js.map +1 -0
  165. package/dist/recyclerview/helpers/RenderTimeTracker.d.ts +10 -0
  166. package/dist/recyclerview/helpers/RenderTimeTracker.d.ts.map +1 -0
  167. package/dist/recyclerview/helpers/RenderTimeTracker.js +39 -0
  168. package/dist/recyclerview/helpers/RenderTimeTracker.js.map +1 -0
  169. package/dist/recyclerview/helpers/VelocityTracker.d.ts +29 -0
  170. package/dist/recyclerview/helpers/VelocityTracker.d.ts.map +1 -0
  171. package/dist/recyclerview/helpers/VelocityTracker.js +70 -0
  172. package/dist/recyclerview/helpers/VelocityTracker.js.map +1 -0
  173. package/dist/recyclerview/hooks/useBoundDetection.d.ts +18 -0
  174. package/dist/recyclerview/hooks/useBoundDetection.d.ts.map +1 -0
  175. package/dist/recyclerview/hooks/useBoundDetection.js +101 -0
  176. package/dist/recyclerview/hooks/useBoundDetection.js.map +1 -0
  177. package/dist/recyclerview/hooks/useLayoutState.d.ts +12 -0
  178. package/dist/recyclerview/hooks/useLayoutState.d.ts.map +1 -0
  179. package/dist/recyclerview/hooks/useLayoutState.js +42 -0
  180. package/dist/recyclerview/hooks/useLayoutState.js.map +1 -0
  181. package/dist/recyclerview/hooks/useMappingHelper.d.ts +9 -0
  182. package/dist/recyclerview/hooks/useMappingHelper.d.ts.map +1 -0
  183. package/dist/recyclerview/hooks/useMappingHelper.js +19 -0
  184. package/dist/recyclerview/hooks/useMappingHelper.js.map +1 -0
  185. package/dist/recyclerview/hooks/useOnLoad.d.ts +25 -0
  186. package/dist/recyclerview/hooks/useOnLoad.d.ts.map +1 -0
  187. package/dist/recyclerview/hooks/useOnLoad.js +74 -0
  188. package/dist/recyclerview/hooks/useOnLoad.js.map +1 -0
  189. package/dist/recyclerview/hooks/useRecyclerViewController.d.ts +72 -0
  190. package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -0
  191. package/dist/recyclerview/hooks/useRecyclerViewController.js +424 -0
  192. package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -0
  193. package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts +8 -0
  194. package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts.map +1 -0
  195. package/dist/recyclerview/hooks/useRecyclerViewManager.js +30 -0
  196. package/dist/recyclerview/hooks/useRecyclerViewManager.js.map +1 -0
  197. package/dist/recyclerview/hooks/useRecyclingState.d.ts +16 -0
  198. package/dist/recyclerview/hooks/useRecyclingState.d.ts.map +1 -0
  199. package/dist/recyclerview/hooks/useRecyclingState.js +53 -0
  200. package/dist/recyclerview/hooks/useRecyclingState.js.map +1 -0
  201. package/dist/recyclerview/hooks/useSecondaryProps.d.ts +27 -0
  202. package/dist/recyclerview/hooks/useSecondaryProps.d.ts.map +1 -0
  203. package/dist/recyclerview/hooks/useSecondaryProps.js +96 -0
  204. package/dist/recyclerview/hooks/useSecondaryProps.js.map +1 -0
  205. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts +8 -0
  206. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts.map +1 -0
  207. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js +34 -0
  208. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js.map +1 -0
  209. package/dist/recyclerview/hooks/useUnmountFlag.d.ts +10 -0
  210. package/dist/recyclerview/hooks/useUnmountFlag.d.ts.map +1 -0
  211. package/dist/recyclerview/hooks/useUnmountFlag.js +28 -0
  212. package/dist/recyclerview/hooks/useUnmountFlag.js.map +1 -0
  213. package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts +65 -0
  214. package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts.map +1 -0
  215. package/dist/recyclerview/layout-managers/GridLayoutManager.js +201 -0
  216. package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -0
  217. package/dist/recyclerview/layout-managers/LayoutManager.d.ts +282 -0
  218. package/dist/recyclerview/layout-managers/LayoutManager.d.ts.map +1 -0
  219. package/dist/recyclerview/layout-managers/LayoutManager.js +254 -0
  220. package/dist/recyclerview/layout-managers/LayoutManager.js.map +1 -0
  221. package/dist/recyclerview/layout-managers/LinearLayoutManager.d.ts +51 -0
  222. package/dist/recyclerview/layout-managers/LinearLayoutManager.d.ts.map +1 -0
  223. package/dist/recyclerview/layout-managers/LinearLayoutManager.js +191 -0
  224. package/dist/recyclerview/layout-managers/LinearLayoutManager.js.map +1 -0
  225. package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts +73 -0
  226. package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts.map +1 -0
  227. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js +272 -0
  228. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js.map +1 -0
  229. package/dist/recyclerview/utils/adjustOffsetForRTL.d.ts +12 -0
  230. package/dist/recyclerview/utils/adjustOffsetForRTL.d.ts.map +1 -0
  231. package/dist/recyclerview/utils/adjustOffsetForRTL.js +17 -0
  232. package/dist/recyclerview/utils/adjustOffsetForRTL.js.map +1 -0
  233. package/dist/recyclerview/utils/componentUtils.d.ts +19 -0
  234. package/dist/recyclerview/utils/componentUtils.d.ts.map +1 -0
  235. package/dist/recyclerview/utils/componentUtils.js +32 -0
  236. package/dist/recyclerview/utils/componentUtils.js.map +1 -0
  237. package/dist/recyclerview/utils/findVisibleIndex.d.ts +24 -0
  238. package/dist/recyclerview/utils/findVisibleIndex.d.ts.map +1 -0
  239. package/dist/recyclerview/utils/findVisibleIndex.js +80 -0
  240. package/dist/recyclerview/utils/findVisibleIndex.js.map +1 -0
  241. package/dist/recyclerview/utils/measureLayout.d.ts +52 -0
  242. package/dist/recyclerview/utils/measureLayout.d.ts.map +1 -0
  243. package/dist/recyclerview/utils/measureLayout.js +107 -0
  244. package/dist/recyclerview/utils/measureLayout.js.map +1 -0
  245. package/dist/recyclerview/utils/measureLayout.web.d.ts +29 -0
  246. package/dist/recyclerview/utils/measureLayout.web.d.ts.map +1 -0
  247. package/dist/recyclerview/utils/measureLayout.web.js +89 -0
  248. package/dist/recyclerview/utils/measureLayout.web.js.map +1 -0
  249. package/dist/specs/AutoLayoutNativeComponent.d.ts +1 -2
  250. package/dist/specs/AutoLayoutNativeComponent.d.ts.map +1 -1
  251. package/dist/specs/CellContainerNativeComponent.d.ts +0 -1
  252. package/dist/specs/CellContainerNativeComponent.d.ts.map +1 -1
  253. package/dist/tsconfig.tsbuildinfo +1 -1
  254. package/dist/utils/AverageWindow.d.ts +13 -0
  255. package/dist/utils/AverageWindow.d.ts.map +1 -1
  256. package/dist/utils/AverageWindow.js +30 -1
  257. package/dist/utils/AverageWindow.js.map +1 -1
  258. package/dist/utils/ContentContainerUtils.d.ts.map +1 -1
  259. package/dist/utils/ContentContainerUtils.js.map +1 -1
  260. package/dist/viewability/ViewabilityHelper.js.map +1 -1
  261. package/dist/viewability/ViewabilityManager.d.ts.map +1 -1
  262. package/dist/viewability/ViewabilityManager.js.map +1 -1
  263. package/package.json +4 -3
  264. package/src/FlashList.tsx +1 -1
  265. package/src/FlashListProps.ts +73 -2
  266. package/src/__tests__/AverageWindow.test.ts +49 -1
  267. package/src/__tests__/ConsecutiveNumbers.test.ts +232 -0
  268. package/src/__tests__/GridLayoutManager.test.ts +113 -0
  269. package/src/__tests__/LinearLayoutManager.test.ts +227 -0
  270. package/src/__tests__/MasonryLayoutManager.test.ts +202 -0
  271. package/src/__tests__/RecycleKeyManager.test.ts +254 -0
  272. package/src/__tests__/RecyclerView.test.tsx +69 -0
  273. package/src/__tests__/findVisibleIndex.test.ts +369 -0
  274. package/src/__tests__/helpers/createLayoutManager.ts +142 -0
  275. package/src/__tests__/useUnmountAwareCallbacks.test.tsx +285 -0
  276. package/src/enableNewCore.ts +24 -0
  277. package/src/index.ts +27 -0
  278. package/src/native/config/PlatformHelper.android.ts +2 -0
  279. package/src/native/config/PlatformHelper.ios.ts +2 -0
  280. package/src/native/config/PlatformHelper.ts +2 -0
  281. package/src/native/config/PlatformHelper.web.ts +3 -1
  282. package/src/recyclerview/RecycleKeyManager.ts +185 -0
  283. package/src/recyclerview/RecyclerView.tsx +535 -0
  284. package/src/recyclerview/RecyclerViewContextProvider.ts +20 -0
  285. package/src/recyclerview/RecyclerViewManager.ts +434 -0
  286. package/src/recyclerview/RecyclerViewProps.ts +11 -0
  287. package/src/recyclerview/ViewHolder.tsx +178 -0
  288. package/src/recyclerview/ViewHolderCollection.tsx +165 -0
  289. package/src/recyclerview/components/CompatScroller.ts +9 -0
  290. package/src/recyclerview/components/CompatView.ts +9 -0
  291. package/src/recyclerview/components/ScrollAnchor.tsx +54 -0
  292. package/src/recyclerview/components/StickyHeaders.tsx +204 -0
  293. package/src/recyclerview/helpers/ConsecutiveNumbers.ts +120 -0
  294. package/src/recyclerview/helpers/EngagedIndicesTracker.ts +302 -0
  295. package/src/recyclerview/helpers/RenderTimeTracker.ts +38 -0
  296. package/src/recyclerview/helpers/VelocityTracker.ts +77 -0
  297. package/src/recyclerview/hooks/useBoundDetection.ts +127 -0
  298. package/src/recyclerview/hooks/useLayoutState.ts +46 -0
  299. package/src/recyclerview/hooks/useMappingHelper.ts +20 -0
  300. package/src/recyclerview/hooks/useOnLoad.ts +81 -0
  301. package/src/recyclerview/hooks/useRecyclerViewController.tsx +546 -0
  302. package/src/recyclerview/hooks/useRecyclerViewManager.ts +34 -0
  303. package/src/recyclerview/hooks/useRecyclingState.ts +63 -0
  304. package/src/recyclerview/hooks/useSecondaryProps.tsx +124 -0
  305. package/src/recyclerview/hooks/useUnmountAwareCallbacks.ts +37 -0
  306. package/src/recyclerview/hooks/useUnmountFlag.ts +26 -0
  307. package/src/recyclerview/layout-managers/GridLayoutManager.ts +212 -0
  308. package/src/recyclerview/layout-managers/LayoutManager.ts +498 -0
  309. package/src/recyclerview/layout-managers/LinearLayoutManager.ts +171 -0
  310. package/src/recyclerview/layout-managers/MasonryLayoutManager.ts +301 -0
  311. package/src/recyclerview/utils/adjustOffsetForRTL.ts +17 -0
  312. package/src/recyclerview/utils/componentUtils.ts +28 -0
  313. package/src/recyclerview/utils/findVisibleIndex.ts +93 -0
  314. package/src/recyclerview/utils/measureLayout.ts +128 -0
  315. package/src/recyclerview/utils/measureLayout.web.ts +104 -0
  316. package/src/utils/AverageWindow.ts +33 -0
  317. package/src/viewability/ViewToken.ts +1 -1
@@ -0,0 +1,301 @@
1
+ import {
2
+ LayoutParams,
3
+ RVDimension,
4
+ RVLayout,
5
+ RVLayoutInfo,
6
+ RVLayoutManager,
7
+ } from "./LayoutManager";
8
+
9
+ /**
10
+ * MasonryLayoutManager implementation that arranges items in a masonry/pinterest-style layout.
11
+ * Items are placed in columns, with support for items spanning multiple columns.
12
+ * Can optimize item placement to minimize column height differences.
13
+ */
14
+ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
15
+ /** The width of the bounded area for the masonry layout */
16
+ private boundedSize: number;
17
+ /** Array tracking the current height of each column */
18
+ private columnHeights: number[];
19
+ /** Current column index for sequential placement */
20
+ private currentColumn = 0;
21
+
22
+ constructor(params: LayoutParams, previousLayoutManager?: RVLayoutManager) {
23
+ super(params, previousLayoutManager);
24
+ this.boundedSize = params.windowSize.width;
25
+ this.optimizeItemArrangement = params.optimizeItemArrangement ?? false;
26
+ this.columnHeights = this.columnHeights ?? Array(this.maxColumns).fill(0);
27
+ }
28
+
29
+ /**
30
+ * Updates layout parameters and triggers recomputation if necessary.
31
+ * @param params New layout parameters
32
+ */
33
+ updateLayoutParams(params: LayoutParams) {
34
+ const prevMaxColumns = this.maxColumns;
35
+ const prevOptimizeItemArrangement = this.optimizeItemArrangement;
36
+ super.updateLayoutParams(params);
37
+ if (
38
+ this.boundedSize !== params.windowSize.width ||
39
+ prevMaxColumns !== params.maxColumns ||
40
+ prevOptimizeItemArrangement !== params.optimizeItemArrangement
41
+ ) {
42
+ this.boundedSize = params.windowSize.width;
43
+ if (this.layouts.length > 0) {
44
+ // console.log("-----> recomputeLayouts");
45
+
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
+ this.recomputeLayouts(0, this.layouts.length - 1);
52
+ this.requiresRepaint = true;
53
+ }
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Processes layout information for items, updating their dimensions.
59
+ * @param layoutInfo Array of layout information for items (real measurements)
60
+ * @param itemCount Total number of items in the list
61
+ */
62
+ processLayoutInfo(layoutInfo: RVLayoutInfo[], itemCount: number) {
63
+ // Update layout information
64
+ for (const info of layoutInfo) {
65
+ const { index, dimensions } = info;
66
+ const layout = this.layouts[index];
67
+ layout.height = dimensions.height;
68
+ layout.isHeightMeasured = true;
69
+ layout.isWidthMeasured = true;
70
+ this.layouts[index] = layout;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Estimates layout dimensions for an item at the given index.
76
+ * Can be called by base class if estimate is required.
77
+ * @param index Index of the item to estimate layout for
78
+ */
79
+ estimateLayout(index: number) {
80
+ const layout = this.layouts[index];
81
+
82
+ // Set width based on columns and span
83
+ layout.width = this.getWidth(index);
84
+ layout.height = this.getEstimatedHeight(index);
85
+
86
+ layout.isWidthMeasured = true;
87
+ layout.enforcedWidth = true;
88
+ }
89
+
90
+ /**
91
+ * Returns the total size of the layout area.
92
+ * @returns RVDimension containing width and height of the layout
93
+ */
94
+ getLayoutSize(): RVDimension {
95
+ if (this.layouts.length === 0) return { width: 0, height: 0 };
96
+
97
+ // Find the tallest column
98
+ const maxHeight = Math.max(...this.columnHeights);
99
+
100
+ return {
101
+ width: this.boundedSize,
102
+ height: maxHeight,
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Recomputes layouts for items in the given range.
108
+ * Uses different placement strategies based on optimization settings.
109
+ * @param startIndex Starting index of items to recompute
110
+ * @param endIndex Ending index of items to recompute
111
+ */
112
+ recomputeLayouts(startIndex: number, endIndex: number): void {
113
+ // Reset column heights if starting from the beginning
114
+ if (startIndex === 0) {
115
+ this.columnHeights = Array(this.maxColumns).fill(0);
116
+ this.currentColumn = 0;
117
+ } else {
118
+ // Find the y-position of the first item to recompute
119
+ // and adjust column heights accordingly
120
+ this.updateColumnHeightsToIndex(startIndex);
121
+ }
122
+
123
+ const itemCount = this.layouts.length;
124
+
125
+ for (let i = startIndex; i < itemCount; i++) {
126
+ const layout = this.getLayout(i);
127
+ const span = this.getSpanSizeInfo(i).span ?? 1;
128
+
129
+ if (this.optimizeItemArrangement) {
130
+ if (span === 1) {
131
+ // For single column items, place in the shortest column
132
+ this.placeSingleColumnItem(layout);
133
+ } else {
134
+ // For multi-column items, find the best position
135
+ this.placeOptimizedMultiColumnItem(layout, span);
136
+ }
137
+ } else {
138
+ // No optimization - place items sequentially
139
+ this.placeItemSequentially(layout, span);
140
+ }
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Calculates the width of an item based on its span.
146
+ * @param index Index of the item
147
+ * @returns Width of the item
148
+ */
149
+ private getWidth(index: number): number {
150
+ const span = this.getSpanSizeInfo(index).span ?? 1;
151
+ return (this.boundedSize / this.maxColumns) * span;
152
+ }
153
+
154
+ /**
155
+ * Places an item sequentially in the next available position.
156
+ * @param layout Layout information for the item
157
+ * @param span Number of columns the item spans
158
+ */
159
+ private placeItemSequentially(layout: RVLayout, span: number): void {
160
+ // Check if the item can fit in the current row
161
+ if (this.currentColumn + span > this.maxColumns) {
162
+ // Move to the next row
163
+ this.currentColumn = 0;
164
+ }
165
+
166
+ // Find the maximum height of the columns this item will span
167
+ let maxHeight = this.columnHeights[this.currentColumn];
168
+ for (
169
+ let col = this.currentColumn + 1;
170
+ col < this.currentColumn + span;
171
+ col++
172
+ ) {
173
+ if (col < this.maxColumns) {
174
+ maxHeight = Math.max(maxHeight, this.columnHeights[col]);
175
+ }
176
+ }
177
+
178
+ // Place the item
179
+ layout.x = (this.boundedSize / this.maxColumns) * this.currentColumn;
180
+ layout.y = maxHeight;
181
+
182
+ // Update column heights
183
+ for (let col = this.currentColumn; col < this.currentColumn + span; col++) {
184
+ if (col < this.maxColumns) {
185
+ this.columnHeights[col] = maxHeight + layout.height;
186
+ }
187
+ }
188
+
189
+ // Move to the next column
190
+ this.currentColumn += span;
191
+ if (this.currentColumn >= this.maxColumns) {
192
+ this.currentColumn = 0;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Places a single-column item in the shortest available column.
198
+ * @param layout Layout information for the item
199
+ */
200
+ private placeSingleColumnItem(layout: RVLayout): void {
201
+ // Find the shortest column
202
+ let shortestColumnIndex = 0;
203
+ let minHeight = this.columnHeights[0];
204
+
205
+ for (let i = 1; i < this.maxColumns; i++) {
206
+ if (this.columnHeights[i] < minHeight) {
207
+ minHeight = this.columnHeights[i];
208
+ shortestColumnIndex = i;
209
+ }
210
+ }
211
+
212
+ // Place the item in the shortest column
213
+ layout.x = (this.boundedSize / this.maxColumns) * shortestColumnIndex;
214
+ layout.y = this.columnHeights[shortestColumnIndex];
215
+
216
+ // Update the column height
217
+ this.columnHeights[shortestColumnIndex] += layout.height;
218
+ }
219
+
220
+ /**
221
+ * Places a multi-column item in the position that minimizes total column heights.
222
+ * @param layout Layout information for the item
223
+ * @param span Number of columns the item spans
224
+ */
225
+ private placeOptimizedMultiColumnItem(layout: RVLayout, span: number): void {
226
+ let bestStartColumn = 0;
227
+ let minTotalHeight = Number.MAX_VALUE;
228
+
229
+ // Try all possible positions
230
+ for (let startCol = 0; startCol <= this.maxColumns - span; startCol++) {
231
+ // Find the maximum height among the columns this item would span
232
+ let maxHeight = this.columnHeights[startCol];
233
+ for (let col = startCol + 1; col < startCol + span; col++) {
234
+ maxHeight = Math.max(maxHeight, this.columnHeights[col]);
235
+ }
236
+
237
+ // Calculate the total height after placing the item
238
+ let totalHeight = 0;
239
+ for (let col = 0; col < this.maxColumns; col++) {
240
+ if (col >= startCol && col < startCol + span) {
241
+ totalHeight += maxHeight + layout.height;
242
+ } else {
243
+ totalHeight += this.columnHeights[col];
244
+ }
245
+ }
246
+
247
+ // Update best position if this is better
248
+ if (totalHeight < minTotalHeight) {
249
+ minTotalHeight = totalHeight;
250
+ bestStartColumn = startCol;
251
+ }
252
+ }
253
+
254
+ // Place the item at the best position
255
+ const maxHeight = Math.max(
256
+ ...this.columnHeights.slice(bestStartColumn, bestStartColumn + span)
257
+ );
258
+ layout.x = (this.boundedSize / this.maxColumns) * bestStartColumn;
259
+ layout.y = maxHeight;
260
+
261
+ // Update column heights
262
+ for (let col = bestStartColumn; col < bestStartColumn + span; col++) {
263
+ this.columnHeights[col] = maxHeight + layout.height;
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Updates column heights up to a given index by recalculating item positions.
269
+ * @param index Index to update column heights up to
270
+ */
271
+ private updateColumnHeightsToIndex(index: number): void {
272
+ // Reset column heights
273
+ this.columnHeights = Array(this.maxColumns).fill(0);
274
+ this.currentColumn = 0;
275
+
276
+ // Recalculate column heights up to the given index
277
+ for (let i = 0; i < index; i++) {
278
+ const layout = this.layouts[i];
279
+ const itemWidth = layout.width;
280
+ const columnWidth = this.boundedSize / this.maxColumns;
281
+ const span = Math.round(itemWidth / columnWidth);
282
+
283
+ // Find which columns this item spans
284
+ const startColumn = Math.round(layout.x / columnWidth);
285
+ const endColumn = Math.min(startColumn + span, this.maxColumns);
286
+
287
+ // Update column heights
288
+ for (let col = startColumn; col < endColumn; col++) {
289
+ this.columnHeights[col] = Math.max(
290
+ this.columnHeights[col],
291
+ layout.y + layout.height
292
+ );
293
+ }
294
+
295
+ // Update current column for non-optimized layout
296
+ if (!this.optimizeItemArrangement) {
297
+ this.currentColumn = (startColumn + span) % this.maxColumns;
298
+ }
299
+ }
300
+ }
301
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Adjusts the scroll offset for Right-to-Left (RTL) layouts.
3
+ * offset it flipped when RTL is enabled.
4
+ * This function converts a left-to-right offset to its RTL equivalent.
5
+ *
6
+ * @param offset - The original scroll offset in LTR layout
7
+ * @param contentSize - The total size of the scrollable content
8
+ * @param windowSize - The size of the visible window/viewport
9
+ * @returns The adjusted offset for RTL layout
10
+ */
11
+ export function adjustOffsetForRTL(
12
+ offset: number,
13
+ contentSize: number,
14
+ windowSize: number
15
+ ) {
16
+ return contentSize - offset - windowSize;
17
+ }
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+
3
+ /**
4
+ * Helper function to handle both React components and React elements.
5
+ * This utility ensures proper rendering of components whether they are passed as
6
+ * component types or pre-rendered elements.
7
+ *
8
+ * @param component - Can be a React component type, React element, null, or undefined
9
+ * @returns A valid React element if the input is valid, null otherwise
10
+ *
11
+ * @example
12
+ * // With a component type
13
+ * getValidComponent(MyComponent)
14
+ *
15
+ * @example
16
+ * // With a pre-rendered element
17
+ * getValidComponent(<MyComponent />)
18
+ */
19
+ export const getValidComponent = (
20
+ component: React.ComponentType | React.ReactElement | null | undefined
21
+ ): React.ReactElement | null => {
22
+ if (React.isValidElement(component)) {
23
+ return component;
24
+ } else if (typeof component === "function") {
25
+ return React.createElement(component);
26
+ }
27
+ return null;
28
+ };
@@ -0,0 +1,93 @@
1
+ import { RVLayout } from "../layout-managers/LayoutManager";
2
+
3
+ /**
4
+ * A helper function to perform binary search for the first or last visible index.
5
+ * This function efficiently finds items that are visible within a viewport by using
6
+ * a binary search algorithm on sorted layouts.
7
+ *
8
+ * @param layouts - The sorted array of RVLayout objects, sorted by either x or y position
9
+ * @param threshold - The threshold value to determine visibility (viewport boundary)
10
+ * @param isSortedByX - A boolean indicating if the array is sorted by x (true) or y (false)
11
+ * @param findFirst - A boolean indicating whether to find the first (true) or last (false) visible index
12
+ * @returns The index of the visible layout or -1 if none are visible
13
+ *
14
+ * @remarks
15
+ * The binary search implementation ensures O(log n) time complexity for finding visible items.
16
+ * The function assumes the layouts array is pre-sorted by the relevant dimension (x or y).
17
+ */
18
+ function binarySearchVisibleIndex(
19
+ layouts: RVLayout[],
20
+ threshold: number,
21
+ isSortedByX: boolean,
22
+ findFirst: boolean
23
+ ): number {
24
+ let left = 0;
25
+ let right = layouts.length - 1;
26
+ let visibleIndex = -1;
27
+
28
+ while (left <= right) {
29
+ const mid = Math.floor((left + right) / 2);
30
+ const layout = layouts[mid];
31
+
32
+ // Check visibility based on the sorting criteria
33
+ const position = isSortedByX ? layout.x : layout.y;
34
+ const size = isSortedByX ? layout.width : layout.height;
35
+
36
+ if (findFirst) {
37
+ // Logic for finding the first visible index
38
+ if (position >= threshold || position + size > threshold) {
39
+ // Potential visible index found, continue searching left for earlier visible items
40
+ visibleIndex = mid;
41
+ right = mid - 1;
42
+ } else {
43
+ // Search in the right half for visible items
44
+ left = mid + 1;
45
+ }
46
+ } else if (position <= threshold) {
47
+ // Potential visible index found, continue searching right for later visible items
48
+ visibleIndex = mid;
49
+ left = mid + 1;
50
+ } else {
51
+ // Search in the left half for visible items
52
+ right = mid - 1;
53
+ }
54
+ }
55
+
56
+ return visibleIndex;
57
+ }
58
+
59
+ /**
60
+ * Finds the first visible index in a sorted array of RVLayout objects.
61
+ * This is a wrapper around binarySearchVisibleIndex that specifically finds
62
+ * the first item that becomes visible in the viewport.
63
+ *
64
+ * @param layouts - The sorted array of RVLayout objects
65
+ * @param threshold - The threshold value to determine visibility
66
+ * @param isSortedByX - A boolean indicating if the array is sorted by x (true) or y (false)
67
+ * @returns The index of the first visible layout or -1 if none are visible
68
+ */
69
+ export function findFirstVisibleIndex(
70
+ layouts: RVLayout[],
71
+ threshold: number,
72
+ isSortedByX: boolean
73
+ ): number {
74
+ return binarySearchVisibleIndex(layouts, threshold, isSortedByX, true);
75
+ }
76
+
77
+ /**
78
+ * Finds the last visible index in a sorted array of RVLayout objects.
79
+ * This is a wrapper around binarySearchVisibleIndex that specifically finds
80
+ * the last item that remains visible in the viewport.
81
+ *
82
+ * @param layouts - The sorted array of RVLayout objects
83
+ * @param threshold - The threshold value to determine visibility
84
+ * @param isSortedByX - A boolean indicating if the array is sorted by x (true) or y (false)
85
+ * @returns The index of the last visible layout or -1 if none are visible
86
+ */
87
+ export function findLastVisibleIndex(
88
+ layouts: RVLayout[],
89
+ threshold: number,
90
+ isSortedByX: boolean
91
+ ): number {
92
+ return binarySearchVisibleIndex(layouts, threshold, isSortedByX, false);
93
+ }
@@ -0,0 +1,128 @@
1
+ import { PixelRatio, View } from "react-native";
2
+
3
+ interface Layout {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ }
9
+
10
+ /**
11
+ * Measures the layout of a view relative to itselft.
12
+ * Using measure wasn't returing accurate values but this workaround does.
13
+ * Returns the x, y coordinates and dimensions of the view.
14
+ *
15
+ * @param view - The React Native View component to measure
16
+ * @returns An object containing x, y, width, and height measurements
17
+ */
18
+ function measureLayout(view: View, oldLayout: Layout | undefined) {
19
+ // const layout = view.unstable_getBoundingClientRect();
20
+ // layout.width = roundOffPixel(layout.width);
21
+ // layout.height = roundOffPixel(layout.height);
22
+ // return layout;
23
+ return measureLayoutRelative(view, view, oldLayout);
24
+ }
25
+
26
+ /**
27
+ * Measures the layout of a view relative to another view.
28
+ * Useful for measuring positions relative to a specific reference view.
29
+ *
30
+ * @param view - The React Native View component to measure
31
+ * @param relativeTo - The reference view to measure against
32
+ * @returns An object containing x, y, width, and height measurements
33
+ */
34
+ function measureLayoutRelative(
35
+ view: View,
36
+ relativeTo: View,
37
+ oldLayout: Layout | undefined
38
+ ) {
39
+ const layout = { x: 0, y: 0, width: 0, height: 0 };
40
+ view.measureLayout(relativeTo, (x, y, width, height) => {
41
+ layout.x = x;
42
+ layout.y = y;
43
+ layout.width = roundOffPixel(width);
44
+ layout.height = roundOffPixel(height);
45
+ });
46
+
47
+ if (oldLayout) {
48
+ if (areDimensionsEqual(layout.width, oldLayout.width)) {
49
+ layout.width = oldLayout.width;
50
+ }
51
+ if (areDimensionsEqual(layout.height, oldLayout.height)) {
52
+ layout.height = oldLayout.height;
53
+ }
54
+ }
55
+ return layout;
56
+ }
57
+
58
+ /**
59
+ * Checks if two dimension values are not equal, with a small tolerance.
60
+ * Used to handle floating-point precision issues in layout measurements.
61
+ *
62
+ * @param value1 - First dimension value to compare
63
+ * @param value2 - Second dimension value to compare
64
+ * @returns true if the values are significantly different, false otherwise
65
+ */
66
+ export function areDimensionsNotEqual(value1: number, value2: number): boolean {
67
+ return !areDimensionsEqual(value1, value2);
68
+ }
69
+
70
+ /**
71
+ * Checks if two dimension values are equal, with a small tolerance.
72
+ * Used to handle floating-point precision issues in layout measurements.
73
+ *
74
+ * @param value1 - First dimension value to compare
75
+ * @param value2 - Second dimension value to compare
76
+ * @returns true if the values are approximately equal, false otherwise
77
+ */
78
+ export function areDimensionsEqual(value1: number, value2: number): boolean {
79
+ return (
80
+ Math.abs(
81
+ PixelRatio.getPixelSizeForLayoutSize(value1) -
82
+ PixelRatio.getPixelSizeForLayoutSize(value2)
83
+ ) <= 1
84
+ );
85
+ }
86
+
87
+ export function roundOffPixel(value: number): number {
88
+ return PixelRatio.roundToNearestPixel(value);
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,104 @@
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
+ let depth = 0;
16
+
17
+ // Only check up to 3 parent elements
18
+ while (currentElement && currentElement !== stopAt && depth < 3) {
19
+ const htmlElement = currentElement as HTMLElement;
20
+ scrollX += htmlElement.scrollLeft ?? 0;
21
+ scrollY += htmlElement.scrollTop ?? 0;
22
+ currentElement = currentElement.parentElement;
23
+ depth++;
24
+ }
25
+
26
+ return { scrollX, scrollY };
27
+ }
28
+
29
+ /**
30
+ * Checks if two dimension values are not equal, with a small tolerance.
31
+ */
32
+ export function areDimensionsNotEqual(value1: number, value2: number): boolean {
33
+ return !areDimensionsEqual(value1, value2);
34
+ }
35
+
36
+ /**
37
+ * Checks if two dimension values are equal, with a small tolerance.
38
+ */
39
+ export function areDimensionsEqual(value1: number, value2: number): boolean {
40
+ return Math.abs(value1 - value2) <= 1;
41
+ }
42
+
43
+ export function roundOffPixel(value: number): number {
44
+ return value;
45
+ }
46
+
47
+ /**
48
+ * Measures the layout of parent of RecyclerView
49
+ */
50
+ export function measureParentSize(view: Element): Layout {
51
+ return {
52
+ x: 0,
53
+ y: 0,
54
+ width: view.clientWidth,
55
+ height: view.clientHeight,
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Measures the layout of child container of RecyclerView
61
+ */
62
+ export function measureFirstChildLayout(
63
+ childContainerView: Element,
64
+ parentView: Element
65
+ ): Layout {
66
+ const childRect = childContainerView.getBoundingClientRect();
67
+ const parentRect = parentView.getBoundingClientRect();
68
+
69
+ // Get scroll offsets for child container (max 3 parents)
70
+ const scrollOffsets = getScrollOffsets(childContainerView, parentView);
71
+
72
+ return {
73
+ x: childRect.left - parentRect.left + scrollOffsets.scrollX,
74
+ y: childRect.top - parentRect.top + scrollOffsets.scrollY,
75
+ width: roundOffPixel(childRect.width),
76
+ height: roundOffPixel(childRect.height),
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Measures the layout of items of RecyclerView
82
+ */
83
+ export function measureItemLayout(
84
+ item: Element,
85
+ oldLayout: Layout | undefined
86
+ ): Layout {
87
+ const layout = {
88
+ x: 0,
89
+ y: 0,
90
+ width: item.clientWidth,
91
+ height: item.clientHeight,
92
+ };
93
+
94
+ if (oldLayout) {
95
+ if (areDimensionsEqual(layout.width, oldLayout.width)) {
96
+ layout.width = oldLayout.width;
97
+ }
98
+ if (areDimensionsEqual(layout.height, oldLayout.height)) {
99
+ layout.height = oldLayout.height;
100
+ }
101
+ }
102
+
103
+ return layout;
104
+ }