@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,232 @@
1
+ /* eslint-disable id-length */
2
+ import { ConsecutiveNumbers } from "../recyclerview/helpers/ConsecutiveNumbers";
3
+
4
+ describe("ConsecutiveNumbers", () => {
5
+ describe("constructor", () => {
6
+ it("should initialize with start and end indices", () => {
7
+ const numbers = new ConsecutiveNumbers(5, 10);
8
+ expect(numbers.startIndex).toBe(5);
9
+ expect(numbers.endIndex).toBe(10);
10
+ });
11
+ });
12
+
13
+ describe("EMPTY", () => {
14
+ it("should have correct values for EMPTY constant", () => {
15
+ expect(ConsecutiveNumbers.EMPTY.startIndex).toBe(-1);
16
+ expect(ConsecutiveNumbers.EMPTY.endIndex).toBe(-2);
17
+ expect(ConsecutiveNumbers.EMPTY.length).toBe(0);
18
+ });
19
+ });
20
+
21
+ describe("length", () => {
22
+ it("should return correct length for valid range", () => {
23
+ expect(new ConsecutiveNumbers(5, 10).length).toBe(6);
24
+ expect(new ConsecutiveNumbers(0, 0).length).toBe(1);
25
+ });
26
+
27
+ it("should return 0 for invalid range", () => {
28
+ expect(new ConsecutiveNumbers(5, 4).length).toBe(0);
29
+ expect(new ConsecutiveNumbers(10, 5).length).toBe(0);
30
+ });
31
+ });
32
+
33
+ describe("at", () => {
34
+ it("should return correct value at index", () => {
35
+ const numbers = new ConsecutiveNumbers(5, 10);
36
+ expect(numbers.at(0)).toBe(5);
37
+ expect(numbers.at(3)).toBe(8);
38
+ expect(numbers.at(5)).toBe(10);
39
+ });
40
+
41
+ it("should work with negative ranges", () => {
42
+ const numbers = new ConsecutiveNumbers(-3, 2);
43
+ expect(numbers.at(0)).toBe(-3);
44
+ expect(numbers.at(3)).toBe(0);
45
+ expect(numbers.at(5)).toBe(2);
46
+ });
47
+ });
48
+
49
+ describe("equals", () => {
50
+ it("should return true for identical ranges", () => {
51
+ const a = new ConsecutiveNumbers(5, 10);
52
+ const b = new ConsecutiveNumbers(5, 10);
53
+ expect(a.equals(b)).toBe(true);
54
+ });
55
+
56
+ it("should return false for different ranges", () => {
57
+ const a = new ConsecutiveNumbers(5, 10);
58
+ const b = new ConsecutiveNumbers(5, 11);
59
+ const c = new ConsecutiveNumbers(6, 10);
60
+ expect(a.equals(b)).toBe(false);
61
+ expect(a.equals(c)).toBe(false);
62
+ });
63
+
64
+ it("should handle empty ranges", () => {
65
+ expect(ConsecutiveNumbers.EMPTY.equals(ConsecutiveNumbers.EMPTY)).toBe(
66
+ true
67
+ );
68
+ expect(
69
+ ConsecutiveNumbers.EMPTY.equals(new ConsecutiveNumbers(1, 5))
70
+ ).toBe(false);
71
+ });
72
+ });
73
+
74
+ describe("toArray", () => {
75
+ it("should return correct array for valid range", () => {
76
+ expect(new ConsecutiveNumbers(5, 8).toArray()).toEqual([5, 6, 7, 8]);
77
+ expect(new ConsecutiveNumbers(0, 3).toArray()).toEqual([0, 1, 2, 3]);
78
+ expect(new ConsecutiveNumbers(-2, 1).toArray()).toEqual([-2, -1, 0, 1]);
79
+ });
80
+
81
+ it("should return empty array for invalid ranges", () => {
82
+ expect(new ConsecutiveNumbers(5, 4).toArray()).toEqual([]);
83
+ expect(ConsecutiveNumbers.EMPTY.toArray()).toEqual([]);
84
+ });
85
+ });
86
+
87
+ describe("includes", () => {
88
+ it("should return true for values in range", () => {
89
+ const numbers = new ConsecutiveNumbers(5, 10);
90
+ expect(numbers.includes(5)).toBe(true);
91
+ expect(numbers.includes(7)).toBe(true);
92
+ expect(numbers.includes(10)).toBe(true);
93
+ });
94
+
95
+ it("should return false for values outside range", () => {
96
+ const numbers = new ConsecutiveNumbers(5, 10);
97
+ expect(numbers.includes(4)).toBe(false);
98
+ expect(numbers.includes(11)).toBe(false);
99
+ });
100
+
101
+ it("should handle empty ranges", () => {
102
+ expect(ConsecutiveNumbers.EMPTY.includes(0)).toBe(false);
103
+ });
104
+ });
105
+
106
+ describe("indexOf", () => {
107
+ it("should return correct index for values in range", () => {
108
+ const numbers = new ConsecutiveNumbers(5, 10);
109
+ expect(numbers.indexOf(5)).toBe(0);
110
+ expect(numbers.indexOf(7)).toBe(2);
111
+ expect(numbers.indexOf(10)).toBe(5);
112
+ });
113
+
114
+ it("should return -1 for values outside range", () => {
115
+ const numbers = new ConsecutiveNumbers(5, 10);
116
+ expect(numbers.indexOf(4)).toBe(-1);
117
+ expect(numbers.indexOf(11)).toBe(-1);
118
+ });
119
+ });
120
+
121
+ describe("findValue", () => {
122
+ it("should find values matching predicate", () => {
123
+ const numbers = new ConsecutiveNumbers(5, 10);
124
+ expect(numbers.findValue((v) => v % 2 === 0)).toBe(6);
125
+ expect(numbers.findValue((v) => v > 8)).toBe(9);
126
+ });
127
+
128
+ it("should return undefined when no match found", () => {
129
+ const numbers = new ConsecutiveNumbers(5, 10);
130
+ expect(numbers.findValue((v) => v > 100)).toBe(undefined);
131
+ });
132
+
133
+ it("should provide index and array to predicate", () => {
134
+ const numbers = new ConsecutiveNumbers(5, 7);
135
+ const mockFn = jest.fn().mockReturnValue(false);
136
+ numbers.findValue(mockFn);
137
+ expect(mockFn).toHaveBeenCalledTimes(3);
138
+ expect(mockFn).toHaveBeenNthCalledWith(1, 5, 0, numbers);
139
+ expect(mockFn).toHaveBeenNthCalledWith(2, 6, 1, numbers);
140
+ expect(mockFn).toHaveBeenNthCalledWith(3, 7, 2, numbers);
141
+ });
142
+ });
143
+
144
+ describe("every", () => {
145
+ it("should return true when all values match predicate", () => {
146
+ const numbers = new ConsecutiveNumbers(5, 10);
147
+ expect(numbers.every((v) => v >= 5)).toBe(true);
148
+ expect(numbers.every((v) => v <= 10)).toBe(true);
149
+ });
150
+
151
+ it("should return false when some values do not match predicate", () => {
152
+ const numbers = new ConsecutiveNumbers(5, 10);
153
+ expect(numbers.every((v) => v % 2 === 0)).toBe(false);
154
+ expect(numbers.every((v) => v > 7)).toBe(false);
155
+ });
156
+
157
+ it("should provide index and array to predicate", () => {
158
+ const numbers = new ConsecutiveNumbers(5, 7);
159
+ const mockFn = jest.fn().mockReturnValue(true);
160
+ numbers.every(mockFn);
161
+ expect(mockFn).toHaveBeenCalledTimes(3);
162
+ expect(mockFn).toHaveBeenNthCalledWith(1, 5, 0, numbers);
163
+ expect(mockFn).toHaveBeenNthCalledWith(2, 6, 1, numbers);
164
+ expect(mockFn).toHaveBeenNthCalledWith(3, 7, 2, numbers);
165
+ });
166
+
167
+ it("should short-circuit when predicate returns false", () => {
168
+ const numbers = new ConsecutiveNumbers(5, 10);
169
+ const mockFn = jest.fn().mockImplementation((v) => v < 7);
170
+ numbers.every(mockFn);
171
+ expect(mockFn).toHaveBeenCalledTimes(3); // Should stop after v=7
172
+ });
173
+ });
174
+
175
+ describe("slice", () => {
176
+ it("should slice with both start and end", () => {
177
+ const numbers = new ConsecutiveNumbers(5, 10);
178
+ const sliced = numbers.slice(1, 4);
179
+ expect(sliced.startIndex).toBe(6);
180
+ expect(sliced.endIndex).toBe(8);
181
+ expect(sliced.length).toBe(3);
182
+ });
183
+
184
+ it("should slice with only start", () => {
185
+ const numbers = new ConsecutiveNumbers(5, 10);
186
+ const sliced = numbers.slice(2);
187
+ expect(sliced.startIndex).toBe(7);
188
+ expect(sliced.endIndex).toBe(10);
189
+ expect(sliced.length).toBe(4);
190
+ });
191
+
192
+ it("should handle out of bounds slices", () => {
193
+ const numbers = new ConsecutiveNumbers(5, 10);
194
+ const sliced = numbers.slice(0, 100);
195
+ expect(sliced.length).toBe(6);
196
+ expect(sliced.startIndex).toBe(5);
197
+ expect(sliced.endIndex).toBe(10);
198
+ });
199
+
200
+ it("should handle invalid slices", () => {
201
+ const numbers = new ConsecutiveNumbers(5, 10);
202
+ const sliced = numbers.slice(4, 3);
203
+ expect(sliced.length).toBe(0);
204
+ expect(sliced.startIndex).toBe(9);
205
+ expect(sliced.endIndex).toBe(8);
206
+ });
207
+ });
208
+
209
+ describe("iterator", () => {
210
+ it("should iterate over all values", () => {
211
+ const numbers = new ConsecutiveNumbers(5, 8);
212
+ const result = [];
213
+ for (const num of numbers) {
214
+ result.push(num);
215
+ }
216
+ expect(result).toEqual([5, 6, 7, 8]);
217
+ });
218
+
219
+ it("should handle empty ranges", () => {
220
+ const result = [];
221
+ for (const num of ConsecutiveNumbers.EMPTY) {
222
+ result.push(num);
223
+ }
224
+ expect(result).toEqual([]);
225
+ });
226
+
227
+ it("should work with spread operator", () => {
228
+ const numbers = new ConsecutiveNumbers(5, 8);
229
+ expect([...numbers]).toEqual([5, 6, 7, 8]);
230
+ });
231
+ });
232
+ });
@@ -0,0 +1,113 @@
1
+ import {
2
+ createPopulatedLayoutManager,
3
+ getAllLayouts,
4
+ LayoutManagerType,
5
+ createLayoutParams,
6
+ } from "./helpers/createLayoutManager";
7
+
8
+ describe("GridLayoutManager", () => {
9
+ const windowSize = { width: 400, height: 900 };
10
+ const defaultParams = { windowSize, maxColumns: 2 };
11
+
12
+ describe("Basic grid layout", () => {
13
+ it("should arrange items in rows with equal widths", () => {
14
+ const manager = createPopulatedLayoutManager(
15
+ LayoutManagerType.GRID,
16
+ 4,
17
+ defaultParams
18
+ );
19
+ const layouts = getAllLayouts(manager);
20
+
21
+ expect(layouts[0].x).toBe(0);
22
+ expect(layouts[0].width).toBe(200);
23
+ expect(layouts[1].x).toBe(200);
24
+
25
+ expect(layouts[2].y).toBe(layouts[0].height);
26
+ expect(layouts[3].x).toBe(200);
27
+ });
28
+
29
+ it("should respect maxColumns configuration", () => {
30
+ const manager = createPopulatedLayoutManager(LayoutManagerType.GRID, 6, {
31
+ ...defaultParams,
32
+ maxColumns: 3,
33
+ });
34
+ const layouts = getAllLayouts(manager);
35
+
36
+ expect(layouts[0].width).toBeCloseTo(400 / 3);
37
+ expect(layouts[3].x).toBe(0);
38
+ expect(layouts[3].y).toBe(layouts[0].height);
39
+ });
40
+ });
41
+
42
+ describe("Multi-column items", () => {
43
+ it("should handle items spanning multiple columns", () => {
44
+ const manager = createPopulatedLayoutManager(LayoutManagerType.GRID, 3, {
45
+ ...defaultParams,
46
+ maxColumns: 3,
47
+ overrideItemLayout: (index, layout) => {
48
+ layout.span = index === 0 ? 2 : undefined;
49
+ },
50
+ });
51
+ const layouts = getAllLayouts(manager);
52
+
53
+ // First item spans 2 columns
54
+ expect(layouts[0].width).toBeCloseTo((400 / 3) * 2);
55
+ // Next item starts in third column
56
+ expect(layouts[1].x).toBeCloseTo((400 / 3) * 2);
57
+ });
58
+
59
+ it("should wrap items that exceed column count", () => {
60
+ const manager = createPopulatedLayoutManager(LayoutManagerType.GRID, 4, {
61
+ ...defaultParams,
62
+ overrideItemLayout: (index, layout) => {
63
+ layout.span = index % 2 === 0 ? 2 : 1;
64
+ },
65
+ });
66
+ const layouts = getAllLayouts(manager);
67
+
68
+ // Row 1: 2 columns (span 2 + span 2 would exceed 2 columns)
69
+ expect(layouts[0].width).toBe(400);
70
+ expect(layouts[1].x).toBe(0);
71
+ expect(layouts[1].y).toBe(layouts[0].height);
72
+ expect(layouts[2].x).toBe(0);
73
+ expect(layouts[2].y).toBe(layouts[1].height + layouts[0].height);
74
+ });
75
+ });
76
+
77
+ describe("Layout recalculations", () => {
78
+ it("should adjust layout when window size changes", () => {
79
+ const manager = createPopulatedLayoutManager(
80
+ LayoutManagerType.GRID,
81
+ 4,
82
+ defaultParams
83
+ );
84
+
85
+ // Update window size
86
+ manager.updateLayoutParams(
87
+ createLayoutParams({
88
+ ...defaultParams,
89
+ windowSize: { width: 600, height: 900 },
90
+ })
91
+ );
92
+
93
+ const updatedWidth = getAllLayouts(manager)[0].width;
94
+ expect(updatedWidth).toBe(300); // 600 / 2 columns
95
+ });
96
+
97
+ it("should maintain positions when adding new items", () => {
98
+ const manager = createPopulatedLayoutManager(
99
+ LayoutManagerType.GRID,
100
+ 2,
101
+ defaultParams
102
+ );
103
+ const initialLayouts = getAllLayouts(manager);
104
+
105
+ // Add two more items
106
+ manager.modifyLayout([], 4);
107
+
108
+ const updatedLayouts = getAllLayouts(manager);
109
+ expect(updatedLayouts[0]).toEqual(initialLayouts[0]);
110
+ expect(updatedLayouts[3].y).toBe(initialLayouts[0].height);
111
+ });
112
+ });
113
+ });
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import { render } from "@quilted/react-testing";
3
+
4
+ import { RecyclerView } from "../recyclerview/RecyclerView";
5
+ import { useFlashListContext } from "../recyclerview/RecyclerViewContextProvider";
6
+ import { LayoutCommitObserver } from "../recyclerview/LayoutCommitObserver";
7
+
8
+ describe("LayoutCommitObserver", () => {
9
+ it("should not alter ref captured by child", () => {
10
+ const ChildComponent = () => {
11
+ const context = useFlashListContext();
12
+ expect(context?.getRef()?.props.testID).toBe("child");
13
+ expect(context?.getParentRef()?.props.testID).toBe("parent");
14
+ expect(context?.getScrollViewRef()?.props.testID).toBe("child");
15
+ expect(context?.getParentScrollViewRef()?.props.testID).toBe("parent");
16
+
17
+ return null;
18
+ };
19
+
20
+ let commitLayoutEffectCount = 0;
21
+
22
+ const content = (
23
+ <RecyclerView
24
+ testID="parent"
25
+ data={[1]}
26
+ renderItem={() => (
27
+ <LayoutCommitObserver
28
+ onCommitLayoutEffect={() => {
29
+ commitLayoutEffectCount++;
30
+ }}
31
+ >
32
+ <RecyclerView
33
+ testID="child"
34
+ data={[1]}
35
+ renderItem={() => (
36
+ <LayoutCommitObserver
37
+ onCommitLayoutEffect={() => {
38
+ commitLayoutEffectCount++;
39
+ }}
40
+ >
41
+ <LayoutCommitObserver
42
+ onCommitLayoutEffect={() => {
43
+ commitLayoutEffectCount++;
44
+ }}
45
+ >
46
+ <ChildComponent />
47
+ </LayoutCommitObserver>
48
+ </LayoutCommitObserver>
49
+ )}
50
+ />
51
+ </LayoutCommitObserver>
52
+ )}
53
+ />
54
+ );
55
+
56
+ render(content);
57
+
58
+ expect(commitLayoutEffectCount).toBe(3);
59
+ });
60
+ });
@@ -0,0 +1,227 @@
1
+ import {
2
+ createPopulatedLayoutManager,
3
+ getAllLayouts,
4
+ LayoutManagerType,
5
+ createLayoutParams,
6
+ createLayoutManager,
7
+ createMockLayoutInfo,
8
+ } from "./helpers/createLayoutManager";
9
+
10
+ describe("LinearLayoutManager", () => {
11
+ const windowSize = { width: 400, height: 900 };
12
+ const defaultParams = { windowSize, horizontal: false };
13
+ const horizontalParams = { windowSize, horizontal: true };
14
+
15
+ describe("Vertical layout", () => {
16
+ it("should stack items vertically", () => {
17
+ const manager = createPopulatedLayoutManager(
18
+ LayoutManagerType.LINEAR,
19
+ 3,
20
+ defaultParams,
21
+ 100, // itemWidth
22
+ 100 // itemHeight
23
+ );
24
+ const layouts = getAllLayouts(manager);
25
+
26
+ expect(layouts.length).toBe(3);
27
+ expect(layouts[0].y).toBe(0);
28
+ expect(layouts[1].y).toBe(100);
29
+ expect(layouts[2].y).toBe(200);
30
+ expect(layouts[0].x).toBe(0);
31
+ expect(layouts[0].width).toBe(400); // Should take full width
32
+ });
33
+
34
+ it("should handle variable item heights", () => {
35
+ const manager = createLayoutManager(
36
+ LayoutManagerType.LINEAR,
37
+ defaultParams
38
+ );
39
+ const layoutInfos = [
40
+ createMockLayoutInfo(0, 400, 100),
41
+ createMockLayoutInfo(1, 400, 150),
42
+ createMockLayoutInfo(2, 400, 50),
43
+ ];
44
+ manager.modifyLayout(layoutInfos, 3);
45
+ const layouts = getAllLayouts(manager);
46
+
47
+ expect(layouts[0].height).toBe(100);
48
+ expect(layouts[1].height).toBe(150);
49
+ expect(layouts[2].height).toBe(50);
50
+
51
+ expect(layouts[0].y).toBe(0);
52
+ expect(layouts[1].y).toBe(100); // 0 + 100
53
+ expect(layouts[2].y).toBe(250); // 100 + 150
54
+ });
55
+
56
+ it("should calculate total layout size correctly", () => {
57
+ const manager = createPopulatedLayoutManager(
58
+ LayoutManagerType.LINEAR,
59
+ 3,
60
+ defaultParams,
61
+ 100,
62
+ 100
63
+ );
64
+ const layoutSize = manager.getLayoutSize();
65
+ expect(layoutSize.width).toBe(400);
66
+ expect(layoutSize.height).toBe(300); // 3 items * 100 height
67
+ });
68
+ });
69
+
70
+ describe("Horizontal layout", () => {
71
+ it("should stack items horizontally", () => {
72
+ const manager = createPopulatedLayoutManager(
73
+ LayoutManagerType.LINEAR,
74
+ 3,
75
+ horizontalParams,
76
+ 100, // itemWidth
77
+ 100 // itemHeight - should take full height
78
+ );
79
+ const layouts = getAllLayouts(manager);
80
+
81
+ expect(layouts.length).toBe(3);
82
+ expect(layouts[0].x).toBe(0);
83
+ expect(layouts[1].x).toBe(100);
84
+ expect(layouts[2].x).toBe(200);
85
+ expect(layouts[0].y).toBe(0);
86
+ expect(layouts[0].minHeight).toBe(900); // Should take full height
87
+ });
88
+
89
+ it("should handle variable item widths", () => {
90
+ const manager = createLayoutManager(
91
+ LayoutManagerType.LINEAR,
92
+ horizontalParams
93
+ );
94
+ const layoutInfos = [
95
+ createMockLayoutInfo(0, 100, 900),
96
+ createMockLayoutInfo(1, 150, 900),
97
+ createMockLayoutInfo(2, 50, 900),
98
+ ];
99
+ manager.modifyLayout(layoutInfos, 3);
100
+ const layouts = getAllLayouts(manager);
101
+
102
+ expect(layouts[0].width).toBe(100);
103
+ expect(layouts[1].width).toBe(150);
104
+ expect(layouts[2].width).toBe(50);
105
+
106
+ expect(layouts[0].x).toBe(0);
107
+ expect(layouts[1].x).toBe(100); // 0 + 100
108
+ expect(layouts[2].x).toBe(250); // 100 + 150
109
+ });
110
+
111
+ it("should calculate total layout size correctly in horizontal mode", () => {
112
+ const manager = createPopulatedLayoutManager(
113
+ LayoutManagerType.LINEAR,
114
+ 3,
115
+ horizontalParams,
116
+ 100,
117
+ 100
118
+ );
119
+ const layoutSize = manager.getLayoutSize();
120
+ expect(layoutSize.width).toBe(300); // 3 items * 100 width
121
+ expect(layoutSize.height).toBe(900);
122
+ });
123
+ });
124
+
125
+ describe("Layout modifications", () => {
126
+ it("should update layout when items are added", () => {
127
+ const manager = createPopulatedLayoutManager(
128
+ LayoutManagerType.LINEAR,
129
+ 2,
130
+ defaultParams,
131
+ 100,
132
+ 100
133
+ );
134
+ const initialLayouts = getAllLayouts(manager);
135
+ expect(initialLayouts.length).toBe(2);
136
+
137
+ // Add one more item
138
+ const newLayoutInfo = [createMockLayoutInfo(2, 400, 120)];
139
+ manager.modifyLayout(newLayoutInfo, 3);
140
+
141
+ const updatedLayouts = getAllLayouts(manager);
142
+ expect(updatedLayouts.length).toBe(3);
143
+ expect(updatedLayouts[2].y).toBe(200); // 100 + 100
144
+ expect(updatedLayouts[2].height).toBe(120);
145
+ expect(manager.getLayoutSize().height).toBe(320); // 100 + 100 + 120
146
+ });
147
+
148
+ it("should update layout when items are removed", () => {
149
+ const manager = createPopulatedLayoutManager(
150
+ LayoutManagerType.LINEAR,
151
+ 3,
152
+ defaultParams,
153
+ 100,
154
+ 100
155
+ );
156
+ expect(getAllLayouts(manager).length).toBe(3);
157
+ expect(manager.getLayoutSize().height).toBe(300);
158
+
159
+ // Remove the last item
160
+ manager.modifyLayout([], 2);
161
+
162
+ const updatedLayouts = getAllLayouts(manager);
163
+ expect(updatedLayouts.length).toBe(2);
164
+ expect(manager.getLayoutSize().height).toBe(200);
165
+ });
166
+
167
+ it("should handle replacing all items", () => {
168
+ const manager = createPopulatedLayoutManager(
169
+ LayoutManagerType.LINEAR,
170
+ 3,
171
+ defaultParams,
172
+ 100,
173
+ 100
174
+ );
175
+
176
+ const newLayoutInfos = [
177
+ createMockLayoutInfo(0, 400, 50),
178
+ createMockLayoutInfo(1, 400, 60),
179
+ ];
180
+ manager.modifyLayout(newLayoutInfos, 2);
181
+
182
+ const layouts = getAllLayouts(manager);
183
+ expect(layouts.length).toBe(2);
184
+ expect(layouts[0].height).toBe(50);
185
+ expect(layouts[1].height).toBe(60);
186
+ expect(layouts[1].y).toBe(50);
187
+ expect(manager.getLayoutSize().height).toBe(110); // 50 + 60
188
+ });
189
+
190
+ it("should recalculate layout when window size changes", () => {
191
+ const manager = createPopulatedLayoutManager(
192
+ LayoutManagerType.LINEAR,
193
+ 3,
194
+ defaultParams,
195
+ 100,
196
+ 100
197
+ );
198
+ const initialLayouts = getAllLayouts(manager);
199
+ expect(initialLayouts[0].width).toBe(400);
200
+
201
+ manager.updateLayoutParams(
202
+ createLayoutParams({
203
+ ...defaultParams,
204
+ windowSize: { width: 600, height: 900 },
205
+ })
206
+ );
207
+
208
+ const updatedLayouts = getAllLayouts(manager);
209
+ expect(updatedLayouts[0].width).toBe(600); // Width should adapt
210
+ expect(updatedLayouts[1].y).toBe(initialLayouts[1].y); // Vertical position shouldn't change
211
+ });
212
+ });
213
+
214
+ describe("Empty layout", () => {
215
+ it("should return zero size for empty layout", () => {
216
+ const manager = createLayoutManager(
217
+ LayoutManagerType.LINEAR,
218
+ defaultParams
219
+ );
220
+ manager.modifyLayout([], 0);
221
+ const layoutSize = manager.getLayoutSize();
222
+ expect(layoutSize.width).toBe(0);
223
+ expect(layoutSize.height).toBe(0);
224
+ expect(getAllLayouts(manager).length).toBe(0);
225
+ });
226
+ });
227
+ });