@legendapp/list 2.0.0-next.1 → 2.0.0-next.2

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 (260) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.cursor/rules/changelog.mdc +60 -0
  3. package/.github/FUNDING.yml +15 -0
  4. package/.gitignore +5 -0
  5. package/.prettierrc.json +5 -0
  6. package/.vscode/settings.json +14 -0
  7. package/CLAUDE.md +126 -0
  8. package/biome.json +46 -0
  9. package/bun.lock +1289 -0
  10. package/bunfig.toml +2 -0
  11. package/dist/CHANGELOG.md +119 -0
  12. package/dist/LICENSE +21 -0
  13. package/dist/README.md +139 -0
  14. package/{animated.d.mts → dist/animated.d.mts} +1 -1
  15. package/{animated.d.ts → dist/animated.d.ts} +1 -1
  16. package/{index.d.mts → dist/index.d.mts} +16 -10
  17. package/{index.d.ts → dist/index.d.ts} +16 -10
  18. package/{index.js → dist/index.js} +52 -32
  19. package/{index.mjs → dist/index.mjs} +52 -32
  20. package/{keyboard-controller.d.mts → dist/keyboard-controller.d.mts} +4 -4
  21. package/{keyboard-controller.d.ts → dist/keyboard-controller.d.ts} +4 -4
  22. package/dist/package.json +35 -0
  23. package/example/README.md +40 -0
  24. package/example/api/data/genres.json +23 -0
  25. package/example/api/data/playlist/10402-10749.json +1 -0
  26. package/example/api/data/playlist/10402-10770.json +1 -0
  27. package/example/api/data/playlist/10402-37.json +1 -0
  28. package/example/api/data/playlist/10749-10752.json +1 -0
  29. package/example/api/data/playlist/10749-10770.json +1 -0
  30. package/example/api/data/playlist/10749-37.json +1 -0
  31. package/example/api/data/playlist/10749-878.json +1 -0
  32. package/example/api/data/playlist/10751-10402.json +1 -0
  33. package/example/api/data/playlist/10751-10752.json +1 -0
  34. package/example/api/data/playlist/10751-37.json +1 -0
  35. package/example/api/data/playlist/10751-53.json +1 -0
  36. package/example/api/data/playlist/10751-878.json +1 -0
  37. package/example/api/data/playlist/10751-9648.json +1 -0
  38. package/example/api/data/playlist/10752-37.json +1 -0
  39. package/example/api/data/playlist/12-10402.json +1 -0
  40. package/example/api/data/playlist/12-10749.json +1 -0
  41. package/example/api/data/playlist/12-18.json +1 -0
  42. package/example/api/data/playlist/12-27.json +1 -0
  43. package/example/api/data/playlist/12-35.json +1 -0
  44. package/example/api/data/playlist/14-36.json +1 -0
  45. package/example/api/data/playlist/14-878.json +1 -0
  46. package/example/api/data/playlist/16-10751.json +1 -0
  47. package/example/api/data/playlist/16-10770.json +1 -0
  48. package/example/api/data/playlist/16-35.json +1 -0
  49. package/example/api/data/playlist/16-36.json +1 -0
  50. package/example/api/data/playlist/16-53.json +1 -0
  51. package/example/api/data/playlist/18-10751.json +1 -0
  52. package/example/api/data/playlist/18-10752.json +1 -0
  53. package/example/api/data/playlist/18-37.json +1 -0
  54. package/example/api/data/playlist/18-53.json +1 -0
  55. package/example/api/data/playlist/18-878.json +1 -0
  56. package/example/api/data/playlist/27-10749.json +1 -0
  57. package/example/api/data/playlist/27-10770.json +1 -0
  58. package/example/api/data/playlist/28-10749.json +1 -0
  59. package/example/api/data/playlist/28-10751.json +1 -0
  60. package/example/api/data/playlist/28-10770.json +1 -0
  61. package/example/api/data/playlist/28-16.json +1 -0
  62. package/example/api/data/playlist/28-18.json +1 -0
  63. package/example/api/data/playlist/28-36.json +1 -0
  64. package/example/api/data/playlist/28-37.json +1 -0
  65. package/example/api/data/playlist/28-53.json +1 -0
  66. package/example/api/data/playlist/28-80.json +1 -0
  67. package/example/api/data/playlist/28-99.json +1 -0
  68. package/example/api/data/playlist/35-10749.json +1 -0
  69. package/example/api/data/playlist/35-10751.json +1 -0
  70. package/example/api/data/playlist/35-10752.json +1 -0
  71. package/example/api/data/playlist/35-27.json +1 -0
  72. package/example/api/data/playlist/35-36.json +1 -0
  73. package/example/api/data/playlist/35-53.json +1 -0
  74. package/example/api/data/playlist/35-80.json +1 -0
  75. package/example/api/data/playlist/36-37.json +1 -0
  76. package/example/api/data/playlist/36-878.json +1 -0
  77. package/example/api/data/playlist/36-9648.json +1 -0
  78. package/example/api/data/playlist/53-10752.json +1 -0
  79. package/example/api/data/playlist/80-10770.json +1 -0
  80. package/example/api/data/playlist/80-14.json +1 -0
  81. package/example/api/data/playlist/80-18.json +1 -0
  82. package/example/api/data/playlist/80-37.json +1 -0
  83. package/example/api/data/playlist/878-37.json +1 -0
  84. package/example/api/data/playlist/9648-10770.json +1 -0
  85. package/example/api/data/playlist/9648-37.json +1 -0
  86. package/example/api/data/playlist/9648-53.json +1 -0
  87. package/example/api/data/playlist/9648-878.json +1 -0
  88. package/example/api/data/playlist/99-10749.json +1 -0
  89. package/example/api/data/playlist/99-14.json +1 -0
  90. package/example/api/data/playlist/99-18.json +1 -0
  91. package/example/api/data/playlist/99-27.json +1 -0
  92. package/example/api/data/playlist/99-53.json +1 -0
  93. package/example/api/data/playlist/99-9648.json +1 -0
  94. package/example/api/data/playlist/index.ts +73 -0
  95. package/example/api/data/rows.json +1 -0
  96. package/example/api/index.ts +36 -0
  97. package/example/app/(tabs)/_layout.tsx +60 -0
  98. package/example/app/(tabs)/cards.tsx +81 -0
  99. package/example/app/(tabs)/index.tsx +205 -0
  100. package/example/app/(tabs)/moviesL.tsx +7 -0
  101. package/example/app/(tabs)/moviesLR.tsx +7 -0
  102. package/example/app/+not-found.tsx +32 -0
  103. package/example/app/_layout.tsx +34 -0
  104. package/example/app/accurate-scrollto/index.tsx +125 -0
  105. package/example/app/accurate-scrollto-2/index.tsx +52 -0
  106. package/example/app/accurate-scrollto-huge/index.tsx +128 -0
  107. package/example/app/add-to-end/index.tsx +82 -0
  108. package/example/app/ai-chat/index.tsx +236 -0
  109. package/example/app/bidirectional-infinite-list/index.tsx +133 -0
  110. package/example/app/cards-columns/index.tsx +37 -0
  111. package/example/app/cards-flashlist/index.tsx +122 -0
  112. package/example/app/cards-flatlist/index.tsx +94 -0
  113. package/example/app/cards-no-recycle/index.tsx +110 -0
  114. package/example/app/cards-renderItem.tsx +354 -0
  115. package/example/app/chat-example/index.tsx +167 -0
  116. package/example/app/chat-infinite/index.tsx +239 -0
  117. package/example/app/chat-keyboard/index.tsx +248 -0
  118. package/example/app/chat-resize-outer/index.tsx +247 -0
  119. package/example/app/columns/index.tsx +78 -0
  120. package/example/app/countries/index.tsx +182 -0
  121. package/example/app/countries-flashlist/index.tsx +163 -0
  122. package/example/app/countries-reorder/index.tsx +187 -0
  123. package/example/app/extra-data/index.tsx +86 -0
  124. package/example/app/filter-elements/filter-data-provider.tsx +55 -0
  125. package/example/app/filter-elements/index.tsx +118 -0
  126. package/example/app/initial-scroll-index/index.tsx +106 -0
  127. package/example/app/initial-scroll-index/renderFixedItem.tsx +215 -0
  128. package/example/app/initial-scroll-index-free-height/index.tsx +70 -0
  129. package/example/app/initial-scroll-index-keyed/index.tsx +62 -0
  130. package/example/app/lazy-list/index.tsx +123 -0
  131. package/example/app/movies-flashlist/index.tsx +7 -0
  132. package/example/app/mutable-cells/index.tsx +104 -0
  133. package/example/app/video-feed/index.tsx +119 -0
  134. package/example/app.config.js +22 -0
  135. package/example/app.json +45 -0
  136. package/example/assets/fonts/SpaceMono-Regular.ttf +0 -0
  137. package/example/assets/images/adaptive-icon.png +0 -0
  138. package/example/assets/images/favicon.png +0 -0
  139. package/example/assets/images/icon.png +0 -0
  140. package/example/assets/images/partial-react-logo.png +0 -0
  141. package/example/assets/images/react-logo.png +0 -0
  142. package/example/assets/images/react-logo@2x.png +0 -0
  143. package/example/assets/images/react-logo@3x.png +0 -0
  144. package/example/assets/images/splash-icon.png +0 -0
  145. package/example/autoscroll.sh +101 -0
  146. package/example/bun.lock +2266 -0
  147. package/example/bunfig.toml +2 -0
  148. package/example/components/Breathe.tsx +54 -0
  149. package/example/components/Circle.tsx +69 -0
  150. package/example/components/Collapsible.tsx +44 -0
  151. package/example/components/ExternalLink.tsx +24 -0
  152. package/example/components/HapticTab.tsx +18 -0
  153. package/example/components/HelloWave.tsx +37 -0
  154. package/example/components/Movies.tsx +179 -0
  155. package/example/components/ParallaxScrollView.tsx +81 -0
  156. package/example/components/ThemedText.tsx +60 -0
  157. package/example/components/ThemedView.tsx +14 -0
  158. package/example/components/__tests__/ThemedText-test.tsx +10 -0
  159. package/example/components/__tests__/__snapshots__/ThemedText-test.tsx.snap +24 -0
  160. package/example/components/ui/IconSymbol.ios.tsx +32 -0
  161. package/example/components/ui/IconSymbol.tsx +43 -0
  162. package/example/components/ui/TabBarBackground.ios.tsx +22 -0
  163. package/example/components/ui/TabBarBackground.tsx +6 -0
  164. package/example/constants/Colors.ts +26 -0
  165. package/example/constants/constants.ts +5 -0
  166. package/example/constants/useScrollTest.ts +19 -0
  167. package/example/hooks/useColorScheme.ts +1 -0
  168. package/example/hooks/useColorScheme.web.ts +8 -0
  169. package/example/hooks/useThemeColor.ts +22 -0
  170. package/example/ios/.xcode.env +11 -0
  171. package/example/ios/Podfile +64 -0
  172. package/example/ios/Podfile.lock +2767 -0
  173. package/example/ios/Podfile.properties.json +5 -0
  174. package/example/ios/listtest/AppDelegate.swift +70 -0
  175. package/example/ios/listtest/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
  176. package/example/ios/listtest/Images.xcassets/AppIcon.appiconset/Contents.json +14 -0
  177. package/example/ios/listtest/Images.xcassets/Contents.json +6 -0
  178. package/example/ios/listtest/Images.xcassets/SplashScreenBackground.colorset/Contents.json +20 -0
  179. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/Contents.json +23 -0
  180. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image.png +0 -0
  181. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image@2x.png +0 -0
  182. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image@3x.png +0 -0
  183. package/example/ios/listtest/Info.plist +85 -0
  184. package/example/ios/listtest/PrivacyInfo.xcprivacy +48 -0
  185. package/example/ios/listtest/SplashScreen.storyboard +42 -0
  186. package/example/ios/listtest/Supporting/Expo.plist +12 -0
  187. package/example/ios/listtest/listtest-Bridging-Header.h +3 -0
  188. package/example/ios/listtest/listtest.entitlements +5 -0
  189. package/example/ios/listtest.xcodeproj/project.pbxproj +547 -0
  190. package/example/ios/listtest.xcodeproj/xcshareddata/xcschemes/listtest.xcscheme +88 -0
  191. package/example/ios/listtest.xcworkspace/contents.xcworkspacedata +10 -0
  192. package/example/metro.config.js +16 -0
  193. package/example/package.json +73 -0
  194. package/example/scripts/reset-project.js +84 -0
  195. package/example/tsconfig.json +26 -0
  196. package/package.json +88 -34
  197. package/posttsup.ts +24 -0
  198. package/src/Container.tsx +176 -0
  199. package/src/Containers.tsx +85 -0
  200. package/src/ContextContainer.ts +145 -0
  201. package/src/DebugView.tsx +83 -0
  202. package/src/LazyLegendList.tsx +41 -0
  203. package/src/LeanView.tsx +18 -0
  204. package/src/LegendList.tsx +558 -0
  205. package/src/ListComponent.tsx +191 -0
  206. package/src/ScrollAdjust.tsx +24 -0
  207. package/src/ScrollAdjustHandler.ts +26 -0
  208. package/src/Separator.tsx +14 -0
  209. package/src/animated.tsx +6 -0
  210. package/src/calculateItemsInView.ts +363 -0
  211. package/src/calculateOffsetForIndex.ts +23 -0
  212. package/src/calculateOffsetWithOffsetPosition.ts +26 -0
  213. package/src/checkAllSizesKnown.ts +17 -0
  214. package/src/checkAtBottom.ts +36 -0
  215. package/src/checkAtTop.ts +27 -0
  216. package/src/checkThreshold.ts +30 -0
  217. package/src/constants.ts +11 -0
  218. package/src/createColumnWrapperStyle.ts +16 -0
  219. package/src/doInitialAllocateContainers.ts +40 -0
  220. package/src/doMaintainScrollAtEnd.ts +34 -0
  221. package/src/findAvailableContainers.ts +98 -0
  222. package/src/finishScrollTo.ts +8 -0
  223. package/src/getId.ts +21 -0
  224. package/src/getItemSize.ts +52 -0
  225. package/src/getRenderedItem.ts +34 -0
  226. package/src/getScrollVelocity.ts +47 -0
  227. package/src/handleLayout.ts +70 -0
  228. package/src/helpers.ts +39 -0
  229. package/src/index.ts +11 -0
  230. package/src/keyboard-controller.tsx +63 -0
  231. package/src/onScroll.ts +66 -0
  232. package/src/prepareMVCP.ts +50 -0
  233. package/src/reanimated.tsx +63 -0
  234. package/src/requestAdjust.ts +41 -0
  235. package/src/scrollTo.ts +40 -0
  236. package/src/scrollToIndex.ts +34 -0
  237. package/src/setDidLayout.ts +25 -0
  238. package/src/setPaddingTop.ts +28 -0
  239. package/src/state.tsx +304 -0
  240. package/src/types.ts +610 -0
  241. package/src/updateAlignItemsPaddingTop.ts +18 -0
  242. package/src/updateAllPositions.ts +130 -0
  243. package/src/updateItemSize.ts +203 -0
  244. package/src/updateTotalSize.ts +44 -0
  245. package/src/useAnimatedValue.ts +6 -0
  246. package/src/useCombinedRef.ts +22 -0
  247. package/src/useInit.ts +17 -0
  248. package/src/useSyncLayout.tsx +68 -0
  249. package/src/useValue$.ts +53 -0
  250. package/src/viewability.ts +279 -0
  251. package/tsconfig.json +59 -0
  252. package/tsup.config.ts +21 -0
  253. /package/{animated.js → dist/animated.js} +0 -0
  254. /package/{animated.mjs → dist/animated.mjs} +0 -0
  255. /package/{keyboard-controller.js → dist/keyboard-controller.js} +0 -0
  256. /package/{keyboard-controller.mjs → dist/keyboard-controller.mjs} +0 -0
  257. /package/{reanimated.d.mts → dist/reanimated.d.mts} +0 -0
  258. /package/{reanimated.d.ts → dist/reanimated.d.ts} +0 -0
  259. /package/{reanimated.js → dist/reanimated.js} +0 -0
  260. /package/{reanimated.mjs → dist/reanimated.mjs} +0 -0
package/bunfig.toml ADDED
@@ -0,0 +1,2 @@
1
+ [install]
2
+ saveTextLockfile = true
@@ -0,0 +1,119 @@
1
+ ## 1.1.4
2
+ - Feat: Add sizes to getState()
3
+
4
+ ## 1.1.3
5
+ - Fix: scrollToEnd was not always setting `viewPosition: 1` correctly
6
+
7
+ ## 1.1.2
8
+ - Fix: Adding items in a list with item separators had a small layout jump as the previously last item re-rendered with a separator
9
+
10
+ ## 1.1.1
11
+ - Fix: scrollTo accuracy when paddingTop changes
12
+
13
+ ## 1.1.0
14
+ - Feat: Add LazyLegendList component for virtualizing regular children
15
+ - Feat: Support initialScrollIndex with viewOffset and viewPosition
16
+ - Feat: Add estimatedListSize prop for better initial size estimation
17
+
18
+ ## 1.0.20
19
+ - Types: Fix type of ref in Reanimated LegendList
20
+
21
+ ## 1.0.19
22
+ - Fix: scrollToEnd not including footerSize
23
+
24
+ ## 1.0.18
25
+ - Feat: Add a useListScrollSize hook
26
+ - Fix: Support renderItem being a function component
27
+ - Fix: scrollToEnd being incorrect by the amount of the bottom padding
28
+
29
+ ## 1.0.17
30
+ - Fix: initialScrollIndex not taking header component size into account
31
+ - Fix: PaddingAndAdjust for ListHeaderComponent
32
+ - Fix: ignore alignItemsAtEnd when the list is empty
33
+
34
+ ## 1.0.16
35
+ - Fix: isAtEnd was going to false when overscrolling
36
+ - Fix: refreshControl not being top padded correctly
37
+ - Fix: type of useLastItem hook
38
+ - Fix: header component was not displaying if a list had no data
39
+ - Fix: scrollToIndex logic that fixes scroll after items layout was not using viewPosition/viewOffset
40
+ - Fix: Improve scrollToIndex accuracy
41
+ - Fix: Improve scrollToEnd accuracy
42
+
43
+ ## 1.0.15
44
+ - Feat: Add a useIsLastItem hook
45
+ - Feat: Support horizontal lists without an intrinsic height, it takes the maximum height of list items
46
+ - Feat: Add onLoad prop
47
+ - Fix: maintainVisibleContentPosition not working on horizontal lists
48
+ - Perf: scrollForNextCalculateItemsInView was not taking drawDistance into account correctly
49
+ - Perf: Improved the algorithm for allocating containers to items
50
+ - Perf: Use useLayoutEffect in LegendList if available to get the outer ScrollView layout as soon as possible
51
+
52
+ ## 1.0.14
53
+ - Fix: A container changing size while inactive but not yet recycled could potentially overlap with elements onscreen if large enough
54
+
55
+ ## 1.0.13
56
+ - Fix: Missing React import in ListHeaderComponentContainer crashing some environments
57
+ - Fix: `initialScrollIndex` was off by padding if using "padding" or "paddingVertical" props
58
+
59
+ ## 1.0.12
60
+ - Fix: Initial scroll index and scrollTo were not compensating for top padding
61
+ - Fix: Removed an overly aggressive optimization that was sometimes causing blank spaces after scrolling
62
+ - Fix: Adding a lot of items to the end with maintainScrollAtEnd could result in a large blank space
63
+ - Fix: ListHeaderComponent sometimes not positioned correctly with maintainVisibleContentPosition
64
+ - Fix: Gap styles not working with maintainVisibleContentPosition
65
+
66
+ ## 1.0.11
67
+ - Fix: scrollTo was sometimes showing gaps at the bottom or bottom after reaching the destination
68
+
69
+ ## 1.0.10
70
+ - Fix: Removed an optimization that only checked newly visible items, which could sometimes cause gaps in lists
71
+ - Fix: Scroll history resets properly during scroll operations, which was causing gaps after scroll
72
+ - Fix: Made scroll buffer calculations and scroll jump handling more reliable
73
+
74
+ ## 1.0.9
75
+ - Fix: Use the `use-sync-external-store` shim to support older versions of react
76
+ - Fix: Lists sometimes leaving some gaps when reordering a list
77
+ - Fix: Sometimes precomputing next scroll position for calculation incorrectly
78
+
79
+ ## 1.0.8
80
+ - Perf: The scroll buffering algorithm is smarter and adjusts based on scroll direction for better performance
81
+ - Perf: The container-finding logic keeps index order, reducing gaps in rendering
82
+ - Perf: Combine multiple hooks in Container to a single `useArray$` hook
83
+
84
+ ## 1.0.7
85
+ - Fix: Containers that move out of view are handled better
86
+
87
+ ## 1.0.6
88
+ - Fix: Average item size calculations are more accurate while scrolling
89
+ - Fix: Items in view are handled better when data changes
90
+ - Fix: Scroll position is maintained more accurately during updates
91
+
92
+ ## 1.0.5
93
+ - Fix: Fast scrolling sometimes caused elements to disappear
94
+ - Fix: Out-of-range `scrollToIndex` calls are handled better
95
+
96
+ ## 1.0.4
97
+ - Fix: Container allocation is more efficient
98
+ - Fix: Bidirectional infinite lists scroll better on the old architecture
99
+ - Fix: Item size updates are handled more reliably
100
+ - Fix: Container reuse logic is more accurate
101
+ - Fix: Zero-size layouts are handled better in the old architecture
102
+
103
+ ## 1.0.3
104
+ - Fix: Items that are larger than the estimated size are handled correctly
105
+
106
+ ## 1.0.2
107
+ - Fix: Initial layout works better in the old architecture
108
+ - Fix: Average size calculations are more accurate for bidirectional scrolling
109
+ - Fix: Initial scroll index behavior is more precise
110
+ - Fix: Item size calculations are more accurate overall
111
+
112
+ ## 1.0.1
113
+ - Fix: Total size calculations are correct when using average sizes
114
+ - Fix: Keyboard avoiding behavior is improved for a smoother experience
115
+
116
+ ## 1.0.0
117
+ Initial release! Major changes if you're coming from a beta version:
118
+
119
+ - Item hooks like `useRecyclingState` are no longer render props, but can be imported directly from `@legendapp/list`.
package/dist/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Moo.do LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Legend List
2
+
3
+ **Legend List** is a high-performance list component for **React Native**, written purely in Typescript with no native dependencies. It is a drop-in replacement for `FlatList` and `FlashList` with better performance, especially when handling dynamically sized items.
4
+
5
+ <video src="https://github.com/user-attachments/assets/8641e305-ab06-4fb3-a96a-fd220df84985"></video>
6
+
7
+ ---
8
+
9
+ ## 🤔 Why Legend List?
10
+
11
+ * **Performance:** Designed from the ground up and heavily optimized for performance, it is faster than FlatList and other list libraries in most scenarios.
12
+ * **Dynamic Item Sizes:** Natively supports items with varying heights without performance hits.
13
+ * **Drop-in Replacement:** API compatibility with `FlatList` and `FlashList` for easier migration.
14
+ * **100% JS:** No native module linking required, ensuring easy integration and compatibility across platforms.
15
+ * **Lightweight:** Our goal is to keep LegendList as small of a dependency as possible. For more advanced use cases, we plan on supporting optional plugins. This ensures that we keep the package size as small as possible.
16
+ * **Bidirectional infinite lists:** Supports infinite scrolling in both directions with no flashes or scroll jumping
17
+ * **Chat UIs without inverted:** Chat UIs can align their content to the bottom and maintain scroll at end, so that the list doesn't need to be inverted, which causes weird behavior (in animations, etc...)
18
+
19
+ For more information, listen to the Legend List episode of the [React Native Radio Podcast](https://infinite.red/react-native-radio/rnr-325-legend-list-with-jay-meistrich) and the [livestream with Expo](https://www.youtube.com/watch?v=XpZMveUCke8).
20
+
21
+ ---
22
+ ## ✨ Additional Features
23
+
24
+ Beyond standard `FlatList` capabilities:
25
+
26
+ * `recycleItems`: (boolean) Toggles item component recycling.
27
+ * `true`: Reuses item components for optimal performance. Be cautious if your item components contain local state, as it might be reused unexpectedly.
28
+ * `false` (default): Creates new item components every time. Less performant but safer if items have complex internal state.
29
+ * `maintainScrollAtEnd`: (boolean) If `true` and the user is scrolled near the bottom (within `maintainScrollAtEndThreshold * screen height`), the list automatically scrolls to the end when items are added or heights change. Useful for chat interfaces.
30
+ * `alignItemsAtEnd`: (boolean) Useful for chat UIs, content smaller than the View will be aligned to the bottom of the list.
31
+
32
+ ---
33
+
34
+ ## 📚 Documentation
35
+
36
+ For comprehensive documentation, guides, and the full API reference, please visit:
37
+
38
+ ➡️ **[Legend List Documentation Site](https://www.legendapp.com/open-source/list)**
39
+
40
+ ---
41
+
42
+ ## 💻 Usage
43
+
44
+ ### Installation
45
+
46
+ ```bash
47
+ # Using Bun
48
+ bun add @legendapp/list
49
+
50
+ # Using npm
51
+ npm install @legendapp/list
52
+
53
+ # Using Yarn
54
+ yarn add @legendapp/list
55
+ ```
56
+
57
+ ### Example
58
+ ```tsx
59
+ import React, { useRef } from "react"
60
+ import { View, Image, Text, StyleSheet } from "react-native"
61
+ import { LegendList, LegendListRef, LegendListRenderItemProps } from "@legendapp/list"
62
+
63
+ // Define the type for your data items
64
+ interface UserData {
65
+ id: string;
66
+ name: string;
67
+ photoUri: string;
68
+ }
69
+
70
+ const LegendListExample = () => {
71
+ // Optional: Ref for accessing list methods (e.g., scrollTo)
72
+ const listRef = useRef<LegendListRef | null>(null)
73
+
74
+ const data = []
75
+
76
+ const renderItem = ({ item }: LegendListRenderItemProps<UserData>) => {
77
+ return (
78
+ <View>
79
+ <Image source={{ uri: item.photoUri }} />
80
+ <Text>{item.name}</Text>
81
+ </View>
82
+ )
83
+ }
84
+
85
+ return (
86
+ <LegendList
87
+ // Required Props
88
+ data={data}
89
+ renderItem={renderItem}
90
+
91
+ // Recommended props (Improves performance)
92
+ keyExtractor={(item) => item.id}
93
+ recycleItems={true}
94
+
95
+ // Recommended if data can change
96
+ maintainVisibleContentPosition
97
+
98
+ ref={listRef}
99
+ />
100
+ )
101
+ }
102
+
103
+ export default LegendListExample
104
+
105
+ ```
106
+
107
+ ---
108
+
109
+ ## How to Build
110
+
111
+ 1. `bun i`
112
+ 2. `bun run build` will build the package to the `dist` folder.
113
+
114
+ ## Running the Example
115
+
116
+ 1. `cd example`
117
+ 2. `bun i`
118
+ 3. `bun run ios`
119
+
120
+ ## PRs gladly accepted!
121
+
122
+ There's not a ton of code so hopefully it's easy to contribute. If you want to add a missing feature or fix a bug please post an issue to see if development is already in progress so we can make sure to not duplicate work 😀.
123
+
124
+ ## Upcoming Roadmap
125
+
126
+ - [] Column spans
127
+ - [] overrideItemLayout
128
+ - [] Sticky headers
129
+ - [] Masonry layout
130
+ - [] getItemType
131
+ - [] React DOM implementation
132
+
133
+ ## Community
134
+
135
+ Join us on [Discord](https://discord.gg/tuW2pAffjA) to get involved with the Legend community.
136
+
137
+ ## 👩‍⚖️ License
138
+
139
+ [MIT](LICENSE)
@@ -31,7 +31,7 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Om
31
31
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
32
32
  ListHeaderComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
33
33
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
34
- maintainScrollAtEnd?: boolean;
34
+ maintainScrollAtEnd?: boolean | _legendapp_list.MaintainScrollAtEndOptions;
35
35
  maintainScrollAtEndThreshold?: number;
36
36
  maintainVisibleContentPosition?: boolean;
37
37
  numColumns?: number;
@@ -31,7 +31,7 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Om
31
31
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
32
32
  ListHeaderComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
33
33
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
34
- maintainScrollAtEnd?: boolean;
34
+ maintainScrollAtEnd?: boolean | _legendapp_list.MaintainScrollAtEndOptions;
35
35
  maintainScrollAtEndThreshold?: number;
36
36
  maintainVisibleContentPosition?: boolean;
37
37
  numColumns?: number;
@@ -1,8 +1,8 @@
1
1
  import * as React$1 from 'react';
2
2
  import { ComponentProps, ReactNode, Dispatch, SetStateAction } from 'react';
3
3
  import * as react_native from 'react-native';
4
- import { View, ScrollView, StyleProp, ViewStyle, ScrollViewProps, NativeSyntheticEvent, NativeScrollEvent, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
5
- import Animated from 'react-native-reanimated';
4
+ import { View, ScrollView, Animated, StyleProp, ViewStyle, ScrollViewProps, LayoutRectangle, NativeSyntheticEvent, NativeScrollEvent, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
5
+ import Animated$1 from 'react-native-reanimated';
6
6
  import * as _legendapp_list_reanimated from '@legendapp/list/reanimated';
7
7
 
8
8
  type ListenerType = "numContainers" | "numContainersPooled" | `containerItemKey${number}` | `containerItemData${number}` | `containerPosition${number}` | `containerColumn${number}` | "containersDidLayout" | "extraData" | "numColumns" | "lastItemKeys" | "totalSize" | "alignItemsPaddingTop" | "stylePaddingTop" | "scrollAdjust" | "scrollAdjustUserOffset" | "headerSize" | "footerSize" | "maintainVisibleContentPosition" | "debugRawScroll" | "debugComputedScroll" | "otherAxisSize" | "scrollSize";
@@ -26,7 +26,7 @@ declare class ScrollAdjustHandler {
26
26
  setMounted(): void;
27
27
  }
28
28
 
29
- type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof ScrollView> | ComponentProps<typeof Animated.ScrollView>> = Omit<TScrollView, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews" | "children"> & {
29
+ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof ScrollView> | ComponentProps<typeof Animated.ScrollView> | ComponentProps<typeof Animated$1.ScrollView>> = Omit<TScrollView, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews" | "children"> & {
30
30
  /**
31
31
  * If true, aligns items at the end of the list.
32
32
  * @default false
@@ -122,7 +122,7 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
122
122
  * If true, auto-scrolls to end when new items are added.
123
123
  * @default false
124
124
  */
125
- maintainScrollAtEnd?: boolean;
125
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
126
126
  /**
127
127
  * Distance threshold in percentage of screen size to trigger maintainScrollAtEnd.
128
128
  * @default 0.1
@@ -233,6 +233,11 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
233
233
  elapsedTimeInMs: number;
234
234
  }) => void;
235
235
  };
236
+ interface MaintainScrollAtEndOptions {
237
+ onLayout?: boolean;
238
+ onItemLayout?: boolean;
239
+ onDataChange?: boolean;
240
+ }
236
241
  interface ColumnWrapperStyle {
237
242
  rowGap?: number;
238
243
  gap?: number;
@@ -309,6 +314,7 @@ interface InternalState {
309
314
  refScroller: React.RefObject<ScrollView>;
310
315
  loadStartTime: number;
311
316
  initialScroll: ScrollIndexWithOffsetPosition | undefined;
317
+ lastLayout: LayoutRectangle | undefined;
312
318
  props: {
313
319
  alignItemsAtEnd: boolean;
314
320
  data: readonly any[];
@@ -316,7 +322,7 @@ interface InternalState {
316
322
  getEstimatedItemSize: ((index: number, item: any) => number) | undefined;
317
323
  horizontal: boolean;
318
324
  keyExtractor: ((item: any, index: number) => string) | undefined;
319
- maintainScrollAtEnd: boolean;
325
+ maintainScrollAtEnd: boolean | MaintainScrollAtEndOptions;
320
326
  maintainScrollAtEndThreshold: number | undefined;
321
327
  maintainVisibleContentPosition: boolean;
322
328
  onEndReached: (((info: {
@@ -578,7 +584,7 @@ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "sc
578
584
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
579
585
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
580
586
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
581
- maintainScrollAtEnd?: boolean;
587
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
582
588
  maintainScrollAtEndThreshold?: number;
583
589
  maintainVisibleContentPosition?: boolean;
584
590
  numColumns?: number;
@@ -646,7 +652,7 @@ declare const LazyLegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_
646
652
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
647
653
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
648
654
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
649
- maintainScrollAtEnd?: boolean;
655
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
650
656
  maintainScrollAtEndThreshold?: number;
651
657
  maintainVisibleContentPosition?: boolean;
652
658
  numColumns?: number;
@@ -708,7 +714,7 @@ declare const LazyLegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_
708
714
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
709
715
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
710
716
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
711
- maintainScrollAtEnd?: boolean;
717
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
712
718
  maintainScrollAtEndThreshold?: number;
713
719
  maintainVisibleContentPosition?: boolean;
714
720
  numColumns?: number;
@@ -772,7 +778,7 @@ declare const LazyLegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_
772
778
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
773
779
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
774
780
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
775
- maintainScrollAtEnd?: boolean;
781
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
776
782
  maintainScrollAtEndThreshold?: number;
777
783
  maintainVisibleContentPosition?: boolean;
778
784
  numColumns?: number;
@@ -818,4 +824,4 @@ declare function useListScrollSize(): {
818
824
  height: number;
819
825
  };
820
826
 
821
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LazyLegendList, type LazyLegendListProps, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ScrollIndexWithOffsetPosition, type ScrollState, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
827
+ export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LazyLegendList, type LazyLegendListProps, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffsetPosition, type ScrollState, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
@@ -1,8 +1,8 @@
1
1
  import * as React$1 from 'react';
2
2
  import { ComponentProps, ReactNode, Dispatch, SetStateAction } from 'react';
3
3
  import * as react_native from 'react-native';
4
- import { View, ScrollView, StyleProp, ViewStyle, ScrollViewProps, NativeSyntheticEvent, NativeScrollEvent, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
5
- import Animated from 'react-native-reanimated';
4
+ import { View, ScrollView, Animated, StyleProp, ViewStyle, ScrollViewProps, LayoutRectangle, NativeSyntheticEvent, NativeScrollEvent, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
5
+ import Animated$1 from 'react-native-reanimated';
6
6
  import * as _legendapp_list_reanimated from '@legendapp/list/reanimated';
7
7
 
8
8
  type ListenerType = "numContainers" | "numContainersPooled" | `containerItemKey${number}` | `containerItemData${number}` | `containerPosition${number}` | `containerColumn${number}` | "containersDidLayout" | "extraData" | "numColumns" | "lastItemKeys" | "totalSize" | "alignItemsPaddingTop" | "stylePaddingTop" | "scrollAdjust" | "scrollAdjustUserOffset" | "headerSize" | "footerSize" | "maintainVisibleContentPosition" | "debugRawScroll" | "debugComputedScroll" | "otherAxisSize" | "scrollSize";
@@ -26,7 +26,7 @@ declare class ScrollAdjustHandler {
26
26
  setMounted(): void;
27
27
  }
28
28
 
29
- type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof ScrollView> | ComponentProps<typeof Animated.ScrollView>> = Omit<TScrollView, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews" | "children"> & {
29
+ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof ScrollView> | ComponentProps<typeof Animated.ScrollView> | ComponentProps<typeof Animated$1.ScrollView>> = Omit<TScrollView, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews" | "children"> & {
30
30
  /**
31
31
  * If true, aligns items at the end of the list.
32
32
  * @default false
@@ -122,7 +122,7 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
122
122
  * If true, auto-scrolls to end when new items are added.
123
123
  * @default false
124
124
  */
125
- maintainScrollAtEnd?: boolean;
125
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
126
126
  /**
127
127
  * Distance threshold in percentage of screen size to trigger maintainScrollAtEnd.
128
128
  * @default 0.1
@@ -233,6 +233,11 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
233
233
  elapsedTimeInMs: number;
234
234
  }) => void;
235
235
  };
236
+ interface MaintainScrollAtEndOptions {
237
+ onLayout?: boolean;
238
+ onItemLayout?: boolean;
239
+ onDataChange?: boolean;
240
+ }
236
241
  interface ColumnWrapperStyle {
237
242
  rowGap?: number;
238
243
  gap?: number;
@@ -309,6 +314,7 @@ interface InternalState {
309
314
  refScroller: React.RefObject<ScrollView>;
310
315
  loadStartTime: number;
311
316
  initialScroll: ScrollIndexWithOffsetPosition | undefined;
317
+ lastLayout: LayoutRectangle | undefined;
312
318
  props: {
313
319
  alignItemsAtEnd: boolean;
314
320
  data: readonly any[];
@@ -316,7 +322,7 @@ interface InternalState {
316
322
  getEstimatedItemSize: ((index: number, item: any) => number) | undefined;
317
323
  horizontal: boolean;
318
324
  keyExtractor: ((item: any, index: number) => string) | undefined;
319
- maintainScrollAtEnd: boolean;
325
+ maintainScrollAtEnd: boolean | MaintainScrollAtEndOptions;
320
326
  maintainScrollAtEndThreshold: number | undefined;
321
327
  maintainVisibleContentPosition: boolean;
322
328
  onEndReached: (((info: {
@@ -578,7 +584,7 @@ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "sc
578
584
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
579
585
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
580
586
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
581
- maintainScrollAtEnd?: boolean;
587
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
582
588
  maintainScrollAtEndThreshold?: number;
583
589
  maintainVisibleContentPosition?: boolean;
584
590
  numColumns?: number;
@@ -646,7 +652,7 @@ declare const LazyLegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_
646
652
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
647
653
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
648
654
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
649
- maintainScrollAtEnd?: boolean;
655
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
650
656
  maintainScrollAtEndThreshold?: number;
651
657
  maintainVisibleContentPosition?: boolean;
652
658
  numColumns?: number;
@@ -708,7 +714,7 @@ declare const LazyLegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_
708
714
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
709
715
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
710
716
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
711
- maintainScrollAtEnd?: boolean;
717
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
712
718
  maintainScrollAtEndThreshold?: number;
713
719
  maintainVisibleContentPosition?: boolean;
714
720
  numColumns?: number;
@@ -772,7 +778,7 @@ declare const LazyLegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_
772
778
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
773
779
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
774
780
  ListHeaderComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
775
- maintainScrollAtEnd?: boolean;
781
+ maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
776
782
  maintainScrollAtEndThreshold?: number;
777
783
  maintainVisibleContentPosition?: boolean;
778
784
  numColumns?: number;
@@ -818,4 +824,4 @@ declare function useListScrollSize(): {
818
824
  height: number;
819
825
  };
820
826
 
821
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LazyLegendList, type LazyLegendListProps, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ScrollIndexWithOffsetPosition, type ScrollState, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
827
+ export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LazyLegendList, type LazyLegendListProps, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffsetPosition, type ScrollState, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
@@ -932,26 +932,27 @@ function requestAdjust(ctx, state, positionDiff) {
932
932
  };
933
933
  state.scroll += positionDiff;
934
934
  state.scrollForNextCalculateItemsInView = void 0;
935
- if (peek$(ctx, "containersDidLayout")) {
935
+ const didLayout = peek$(ctx, "containersDidLayout");
936
+ if (didLayout) {
936
937
  doit();
938
+ const threshold = state.scroll - positionDiff / 2;
939
+ if (!state.ignoreScrollFromMVCP) {
940
+ state.ignoreScrollFromMVCP = {};
941
+ }
942
+ if (positionDiff > 0) {
943
+ state.ignoreScrollFromMVCP.lt = threshold;
944
+ } else {
945
+ state.ignoreScrollFromMVCP.gt = threshold;
946
+ }
947
+ if (state.ignoreScrollFromMVCPTimeout) {
948
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
949
+ }
950
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
951
+ state.ignoreScrollFromMVCP = void 0;
952
+ }, 100);
937
953
  } else {
938
954
  requestAnimationFrame(doit);
939
955
  }
940
- const threshold = state.scroll - positionDiff / 2;
941
- if (!state.ignoreScrollFromMVCP) {
942
- state.ignoreScrollFromMVCP = {};
943
- }
944
- if (positionDiff > 0) {
945
- state.ignoreScrollFromMVCP.lt = threshold;
946
- } else {
947
- state.ignoreScrollFromMVCP.gt = threshold;
948
- }
949
- if (state.ignoreScrollFromMVCPTimeout) {
950
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
951
- }
952
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
953
- state.ignoreScrollFromMVCP = void 0;
954
- }, 100);
955
956
  }
956
957
  }
957
958
 
@@ -1845,9 +1846,12 @@ function getRenderedItem(ctx, state, key) {
1845
1846
  }
1846
1847
 
1847
1848
  // src/handleLayout.ts
1848
- function handleLayout(ctx, state, size, setCanRender) {
1849
- const scrollLength = size[state.props.horizontal ? "width" : "height"];
1850
- const otherAxisSize = size[state.props.horizontal ? "height" : "width"];
1849
+ function handleLayout(ctx, state, layout, setCanRender) {
1850
+ const { maintainScrollAtEnd } = state.props;
1851
+ const scrollLength = layout[state.props.horizontal ? "width" : "height"];
1852
+ const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
1853
+ const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
1854
+ state.lastLayout = layout;
1851
1855
  const didChange = scrollLength !== state.scrollLength;
1852
1856
  const prevOtherAxisSize = state.otherAxisSize;
1853
1857
  state.scrollLength = scrollLength;
@@ -1855,13 +1859,15 @@ function handleLayout(ctx, state, size, setCanRender) {
1855
1859
  state.lastBatchingAction = Date.now();
1856
1860
  state.scrollForNextCalculateItemsInView = void 0;
1857
1861
  doInitialAllocateContainers(ctx, state);
1858
- if (didChange) {
1862
+ if (needsCalculate) {
1859
1863
  calculateItemsInView(ctx, state, { doMVCP: true });
1860
1864
  }
1861
1865
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
1862
- set$(ctx, "scrollSize", { width: size.width, height: size.height });
1866
+ set$(ctx, "scrollSize", { width: layout.width, height: layout.height });
1867
+ }
1868
+ if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
1869
+ doMaintainScrollAtEnd(ctx, state, false);
1863
1870
  }
1864
- doMaintainScrollAtEnd(ctx, state, false);
1865
1871
  updateAlignItemsPaddingTop(ctx, state);
1866
1872
  checkAtBottom(ctx, state);
1867
1873
  checkAtTop(state);
@@ -1874,7 +1880,6 @@ function handleLayout(ctx, state, size, setCanRender) {
1874
1880
  `List ${state.props.horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
1875
1881
  );
1876
1882
  }
1877
- calculateItemsInView(ctx, state, { doMVCP: true });
1878
1883
  setCanRender(true);
1879
1884
  }
1880
1885
 
@@ -1920,7 +1925,14 @@ function updateScroll(ctx, state, newScroll) {
1920
1925
  function updateItemSizes(ctx, state, itemUpdates) {
1921
1926
  var _a;
1922
1927
  const {
1923
- props: { horizontal, maintainVisibleContentPosition, suggestEstimatedItemSize, onItemSizeChanged, data }
1928
+ props: {
1929
+ horizontal,
1930
+ maintainVisibleContentPosition,
1931
+ suggestEstimatedItemSize,
1932
+ onItemSizeChanged,
1933
+ data,
1934
+ maintainScrollAtEnd
1935
+ }
1924
1936
  } = state;
1925
1937
  if (!data) return;
1926
1938
  let needsRecalculate = false;
@@ -1990,12 +2002,13 @@ function updateItemSizes(ctx, state, itemUpdates) {
1990
2002
  calculateItemsInView(ctx, state, { doMVCP: true });
1991
2003
  }
1992
2004
  if (shouldMaintainScrollAtEnd) {
1993
- doMaintainScrollAtEnd(ctx, state, false);
2005
+ if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
2006
+ doMaintainScrollAtEnd(ctx, state, false);
2007
+ }
1994
2008
  }
1995
2009
  }
1996
2010
  }
1997
2011
  function updateItemSize(ctx, state, itemKey, sizeObj) {
1998
- var _a, _b;
1999
2012
  if (IsNewArchitecture) {
2000
2013
  const { sizesKnown } = state;
2001
2014
  const numContainers = ctx.values.get("numContainers");
@@ -2006,8 +2019,11 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2006
2019
  changes.push({ itemKey, sizeObj });
2007
2020
  } else if (!sizesKnown.has(containerItemKey) && containerItemKey !== void 0) {
2008
2021
  const containerRef = ctx.viewRefs.get(i);
2009
- if (containerRef) {
2010
- const measured = (_b = (_a = containerRef.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
2022
+ if (containerRef == null ? void 0 : containerRef.current) {
2023
+ let measured;
2024
+ containerRef.current.measure((x, y, width, height) => {
2025
+ measured = { x, y, width, height };
2026
+ });
2011
2027
  if (measured) {
2012
2028
  changes.push({ itemKey: containerItemKey, sizeObj: measured });
2013
2029
  }
@@ -2177,7 +2193,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2177
2193
  props: {},
2178
2194
  refScroller: void 0,
2179
2195
  loadStartTime: Date.now(),
2180
- initialScroll
2196
+ initialScroll,
2197
+ lastLayout: void 0
2181
2198
  };
2182
2199
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
2183
2200
  set$(ctx, "extraData", extraData);
@@ -2219,7 +2236,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2219
2236
  state2.props.data = dataProp;
2220
2237
  if (!isFirst2) {
2221
2238
  calculateItemsInView(ctx, state2, { dataChanged: true, doMVCP: true });
2222
- const didMaintainScrollAtEnd = doMaintainScrollAtEnd(ctx, state2, false);
2239
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2240
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state2, false);
2223
2241
  if (!didMaintainScrollAtEnd && dataProp.length > state2.props.data.length) {
2224
2242
  state2.isEndReached = false;
2225
2243
  }
@@ -2273,9 +2291,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2273
2291
  }
2274
2292
  }
2275
2293
  React3.useLayoutEffect(() => {
2276
- var _a2, _b;
2277
2294
  if (IsNewArchitecture) {
2278
- const measured = (_b = (_a2 = refScroller.current) == null ? void 0 : _a2.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a2);
2295
+ let measured;
2296
+ refScroller.current.measure((x, y, width, height) => {
2297
+ measured = { x, y, width, height };
2298
+ });
2279
2299
  if (measured) {
2280
2300
  const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2281
2301
  if (size) {