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

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/{dist/animated.d.mts → animated.d.mts} +7 -2
  2. package/{dist/animated.d.ts → animated.d.ts} +7 -2
  3. package/{dist/animated.js → animated.js} +2 -2
  4. package/{dist/animated.mjs → animated.mjs} +2 -2
  5. package/{dist/index.d.mts → index.d.mts} +106 -56
  6. package/{dist/index.d.ts → index.d.ts} +106 -56
  7. package/{dist/index.js → index.js} +1589 -1095
  8. package/{dist/index.mjs → index.mjs} +1591 -1097
  9. package/{dist/keyboard-controller.d.mts → keyboard-controller.d.mts} +28 -8
  10. package/{dist/keyboard-controller.d.ts → keyboard-controller.d.ts} +28 -8
  11. package/{dist/keyboard-controller.js → keyboard-controller.js} +4 -4
  12. package/{dist/keyboard-controller.mjs → keyboard-controller.mjs} +4 -4
  13. package/package.json +34 -88
  14. package/{dist/reanimated.d.mts → reanimated.d.mts} +2 -2
  15. package/{dist/reanimated.d.ts → reanimated.d.ts} +2 -2
  16. package/{dist/reanimated.js → reanimated.js} +7 -7
  17. package/{dist/reanimated.mjs → reanimated.mjs} +7 -7
  18. package/.claude/settings.local.json +0 -8
  19. package/.cursor/rules/changelog.mdc +0 -60
  20. package/.github/FUNDING.yml +0 -15
  21. package/.gitignore +0 -5
  22. package/.prettierrc.json +0 -5
  23. package/.vscode/settings.json +0 -14
  24. package/CLAUDE.md +0 -126
  25. package/biome.json +0 -46
  26. package/bun.lock +0 -1289
  27. package/bunfig.toml +0 -2
  28. package/dist/CHANGELOG.md +0 -119
  29. package/dist/LICENSE +0 -21
  30. package/dist/README.md +0 -139
  31. package/dist/package.json +0 -35
  32. package/example/README.md +0 -40
  33. package/example/api/data/genres.json +0 -23
  34. package/example/api/data/playlist/10402-10749.json +0 -1
  35. package/example/api/data/playlist/10402-10770.json +0 -1
  36. package/example/api/data/playlist/10402-37.json +0 -1
  37. package/example/api/data/playlist/10749-10752.json +0 -1
  38. package/example/api/data/playlist/10749-10770.json +0 -1
  39. package/example/api/data/playlist/10749-37.json +0 -1
  40. package/example/api/data/playlist/10749-878.json +0 -1
  41. package/example/api/data/playlist/10751-10402.json +0 -1
  42. package/example/api/data/playlist/10751-10752.json +0 -1
  43. package/example/api/data/playlist/10751-37.json +0 -1
  44. package/example/api/data/playlist/10751-53.json +0 -1
  45. package/example/api/data/playlist/10751-878.json +0 -1
  46. package/example/api/data/playlist/10751-9648.json +0 -1
  47. package/example/api/data/playlist/10752-37.json +0 -1
  48. package/example/api/data/playlist/12-10402.json +0 -1
  49. package/example/api/data/playlist/12-10749.json +0 -1
  50. package/example/api/data/playlist/12-18.json +0 -1
  51. package/example/api/data/playlist/12-27.json +0 -1
  52. package/example/api/data/playlist/12-35.json +0 -1
  53. package/example/api/data/playlist/14-36.json +0 -1
  54. package/example/api/data/playlist/14-878.json +0 -1
  55. package/example/api/data/playlist/16-10751.json +0 -1
  56. package/example/api/data/playlist/16-10770.json +0 -1
  57. package/example/api/data/playlist/16-35.json +0 -1
  58. package/example/api/data/playlist/16-36.json +0 -1
  59. package/example/api/data/playlist/16-53.json +0 -1
  60. package/example/api/data/playlist/18-10751.json +0 -1
  61. package/example/api/data/playlist/18-10752.json +0 -1
  62. package/example/api/data/playlist/18-37.json +0 -1
  63. package/example/api/data/playlist/18-53.json +0 -1
  64. package/example/api/data/playlist/18-878.json +0 -1
  65. package/example/api/data/playlist/27-10749.json +0 -1
  66. package/example/api/data/playlist/27-10770.json +0 -1
  67. package/example/api/data/playlist/28-10749.json +0 -1
  68. package/example/api/data/playlist/28-10751.json +0 -1
  69. package/example/api/data/playlist/28-10770.json +0 -1
  70. package/example/api/data/playlist/28-16.json +0 -1
  71. package/example/api/data/playlist/28-18.json +0 -1
  72. package/example/api/data/playlist/28-36.json +0 -1
  73. package/example/api/data/playlist/28-37.json +0 -1
  74. package/example/api/data/playlist/28-53.json +0 -1
  75. package/example/api/data/playlist/28-80.json +0 -1
  76. package/example/api/data/playlist/28-99.json +0 -1
  77. package/example/api/data/playlist/35-10749.json +0 -1
  78. package/example/api/data/playlist/35-10751.json +0 -1
  79. package/example/api/data/playlist/35-10752.json +0 -1
  80. package/example/api/data/playlist/35-27.json +0 -1
  81. package/example/api/data/playlist/35-36.json +0 -1
  82. package/example/api/data/playlist/35-53.json +0 -1
  83. package/example/api/data/playlist/35-80.json +0 -1
  84. package/example/api/data/playlist/36-37.json +0 -1
  85. package/example/api/data/playlist/36-878.json +0 -1
  86. package/example/api/data/playlist/36-9648.json +0 -1
  87. package/example/api/data/playlist/53-10752.json +0 -1
  88. package/example/api/data/playlist/80-10770.json +0 -1
  89. package/example/api/data/playlist/80-14.json +0 -1
  90. package/example/api/data/playlist/80-18.json +0 -1
  91. package/example/api/data/playlist/80-37.json +0 -1
  92. package/example/api/data/playlist/878-37.json +0 -1
  93. package/example/api/data/playlist/9648-10770.json +0 -1
  94. package/example/api/data/playlist/9648-37.json +0 -1
  95. package/example/api/data/playlist/9648-53.json +0 -1
  96. package/example/api/data/playlist/9648-878.json +0 -1
  97. package/example/api/data/playlist/99-10749.json +0 -1
  98. package/example/api/data/playlist/99-14.json +0 -1
  99. package/example/api/data/playlist/99-18.json +0 -1
  100. package/example/api/data/playlist/99-27.json +0 -1
  101. package/example/api/data/playlist/99-53.json +0 -1
  102. package/example/api/data/playlist/99-9648.json +0 -1
  103. package/example/api/data/playlist/index.ts +0 -73
  104. package/example/api/data/rows.json +0 -1
  105. package/example/api/index.ts +0 -36
  106. package/example/app/(tabs)/_layout.tsx +0 -60
  107. package/example/app/(tabs)/cards.tsx +0 -81
  108. package/example/app/(tabs)/index.tsx +0 -205
  109. package/example/app/(tabs)/moviesL.tsx +0 -7
  110. package/example/app/(tabs)/moviesLR.tsx +0 -7
  111. package/example/app/+not-found.tsx +0 -32
  112. package/example/app/_layout.tsx +0 -34
  113. package/example/app/accurate-scrollto/index.tsx +0 -125
  114. package/example/app/accurate-scrollto-2/index.tsx +0 -52
  115. package/example/app/accurate-scrollto-huge/index.tsx +0 -128
  116. package/example/app/add-to-end/index.tsx +0 -82
  117. package/example/app/ai-chat/index.tsx +0 -236
  118. package/example/app/bidirectional-infinite-list/index.tsx +0 -133
  119. package/example/app/cards-columns/index.tsx +0 -37
  120. package/example/app/cards-flashlist/index.tsx +0 -122
  121. package/example/app/cards-flatlist/index.tsx +0 -94
  122. package/example/app/cards-no-recycle/index.tsx +0 -110
  123. package/example/app/cards-renderItem.tsx +0 -354
  124. package/example/app/chat-example/index.tsx +0 -167
  125. package/example/app/chat-infinite/index.tsx +0 -239
  126. package/example/app/chat-keyboard/index.tsx +0 -248
  127. package/example/app/chat-resize-outer/index.tsx +0 -247
  128. package/example/app/columns/index.tsx +0 -78
  129. package/example/app/countries/index.tsx +0 -182
  130. package/example/app/countries-flashlist/index.tsx +0 -163
  131. package/example/app/countries-reorder/index.tsx +0 -187
  132. package/example/app/extra-data/index.tsx +0 -86
  133. package/example/app/filter-elements/filter-data-provider.tsx +0 -55
  134. package/example/app/filter-elements/index.tsx +0 -118
  135. package/example/app/initial-scroll-index/index.tsx +0 -106
  136. package/example/app/initial-scroll-index/renderFixedItem.tsx +0 -215
  137. package/example/app/initial-scroll-index-free-height/index.tsx +0 -70
  138. package/example/app/initial-scroll-index-keyed/index.tsx +0 -62
  139. package/example/app/lazy-list/index.tsx +0 -123
  140. package/example/app/movies-flashlist/index.tsx +0 -7
  141. package/example/app/mutable-cells/index.tsx +0 -104
  142. package/example/app/video-feed/index.tsx +0 -119
  143. package/example/app.config.js +0 -22
  144. package/example/app.json +0 -45
  145. package/example/assets/fonts/SpaceMono-Regular.ttf +0 -0
  146. package/example/assets/images/adaptive-icon.png +0 -0
  147. package/example/assets/images/favicon.png +0 -0
  148. package/example/assets/images/icon.png +0 -0
  149. package/example/assets/images/partial-react-logo.png +0 -0
  150. package/example/assets/images/react-logo.png +0 -0
  151. package/example/assets/images/react-logo@2x.png +0 -0
  152. package/example/assets/images/react-logo@3x.png +0 -0
  153. package/example/assets/images/splash-icon.png +0 -0
  154. package/example/autoscroll.sh +0 -101
  155. package/example/bun.lock +0 -2266
  156. package/example/bunfig.toml +0 -2
  157. package/example/components/Breathe.tsx +0 -54
  158. package/example/components/Circle.tsx +0 -69
  159. package/example/components/Collapsible.tsx +0 -44
  160. package/example/components/ExternalLink.tsx +0 -24
  161. package/example/components/HapticTab.tsx +0 -18
  162. package/example/components/HelloWave.tsx +0 -37
  163. package/example/components/Movies.tsx +0 -179
  164. package/example/components/ParallaxScrollView.tsx +0 -81
  165. package/example/components/ThemedText.tsx +0 -60
  166. package/example/components/ThemedView.tsx +0 -14
  167. package/example/components/__tests__/ThemedText-test.tsx +0 -10
  168. package/example/components/__tests__/__snapshots__/ThemedText-test.tsx.snap +0 -24
  169. package/example/components/ui/IconSymbol.ios.tsx +0 -32
  170. package/example/components/ui/IconSymbol.tsx +0 -43
  171. package/example/components/ui/TabBarBackground.ios.tsx +0 -22
  172. package/example/components/ui/TabBarBackground.tsx +0 -6
  173. package/example/constants/Colors.ts +0 -26
  174. package/example/constants/constants.ts +0 -5
  175. package/example/constants/useScrollTest.ts +0 -19
  176. package/example/hooks/useColorScheme.ts +0 -1
  177. package/example/hooks/useColorScheme.web.ts +0 -8
  178. package/example/hooks/useThemeColor.ts +0 -22
  179. package/example/ios/.xcode.env +0 -11
  180. package/example/ios/Podfile +0 -64
  181. package/example/ios/Podfile.lock +0 -2767
  182. package/example/ios/Podfile.properties.json +0 -5
  183. package/example/ios/listtest/AppDelegate.swift +0 -70
  184. package/example/ios/listtest/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
  185. package/example/ios/listtest/Images.xcassets/AppIcon.appiconset/Contents.json +0 -14
  186. package/example/ios/listtest/Images.xcassets/Contents.json +0 -6
  187. package/example/ios/listtest/Images.xcassets/SplashScreenBackground.colorset/Contents.json +0 -20
  188. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/Contents.json +0 -23
  189. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image.png +0 -0
  190. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image@2x.png +0 -0
  191. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image@3x.png +0 -0
  192. package/example/ios/listtest/Info.plist +0 -85
  193. package/example/ios/listtest/PrivacyInfo.xcprivacy +0 -48
  194. package/example/ios/listtest/SplashScreen.storyboard +0 -42
  195. package/example/ios/listtest/Supporting/Expo.plist +0 -12
  196. package/example/ios/listtest/listtest-Bridging-Header.h +0 -3
  197. package/example/ios/listtest/listtest.entitlements +0 -5
  198. package/example/ios/listtest.xcodeproj/project.pbxproj +0 -547
  199. package/example/ios/listtest.xcodeproj/xcshareddata/xcschemes/listtest.xcscheme +0 -88
  200. package/example/ios/listtest.xcworkspace/contents.xcworkspacedata +0 -10
  201. package/example/metro.config.js +0 -16
  202. package/example/package.json +0 -73
  203. package/example/scripts/reset-project.js +0 -84
  204. package/example/tsconfig.json +0 -26
  205. package/posttsup.ts +0 -24
  206. package/src/Container.tsx +0 -176
  207. package/src/Containers.tsx +0 -85
  208. package/src/ContextContainer.ts +0 -145
  209. package/src/DebugView.tsx +0 -83
  210. package/src/LazyLegendList.tsx +0 -41
  211. package/src/LeanView.tsx +0 -18
  212. package/src/LegendList.tsx +0 -558
  213. package/src/ListComponent.tsx +0 -191
  214. package/src/ScrollAdjust.tsx +0 -24
  215. package/src/ScrollAdjustHandler.ts +0 -26
  216. package/src/Separator.tsx +0 -14
  217. package/src/animated.tsx +0 -6
  218. package/src/calculateItemsInView.ts +0 -363
  219. package/src/calculateOffsetForIndex.ts +0 -23
  220. package/src/calculateOffsetWithOffsetPosition.ts +0 -26
  221. package/src/checkAllSizesKnown.ts +0 -17
  222. package/src/checkAtBottom.ts +0 -36
  223. package/src/checkAtTop.ts +0 -27
  224. package/src/checkThreshold.ts +0 -30
  225. package/src/constants.ts +0 -11
  226. package/src/createColumnWrapperStyle.ts +0 -16
  227. package/src/doInitialAllocateContainers.ts +0 -40
  228. package/src/doMaintainScrollAtEnd.ts +0 -34
  229. package/src/findAvailableContainers.ts +0 -98
  230. package/src/finishScrollTo.ts +0 -8
  231. package/src/getId.ts +0 -21
  232. package/src/getItemSize.ts +0 -52
  233. package/src/getRenderedItem.ts +0 -34
  234. package/src/getScrollVelocity.ts +0 -47
  235. package/src/handleLayout.ts +0 -70
  236. package/src/helpers.ts +0 -39
  237. package/src/index.ts +0 -11
  238. package/src/keyboard-controller.tsx +0 -63
  239. package/src/onScroll.ts +0 -66
  240. package/src/prepareMVCP.ts +0 -50
  241. package/src/reanimated.tsx +0 -63
  242. package/src/requestAdjust.ts +0 -41
  243. package/src/scrollTo.ts +0 -40
  244. package/src/scrollToIndex.ts +0 -34
  245. package/src/setDidLayout.ts +0 -25
  246. package/src/setPaddingTop.ts +0 -28
  247. package/src/state.tsx +0 -304
  248. package/src/types.ts +0 -610
  249. package/src/updateAlignItemsPaddingTop.ts +0 -18
  250. package/src/updateAllPositions.ts +0 -130
  251. package/src/updateItemSize.ts +0 -203
  252. package/src/updateTotalSize.ts +0 -44
  253. package/src/useAnimatedValue.ts +0 -6
  254. package/src/useCombinedRef.ts +0 -22
  255. package/src/useInit.ts +0 -17
  256. package/src/useSyncLayout.tsx +0 -68
  257. package/src/useValue$.ts +0 -53
  258. package/src/viewability.ts +0 -279
  259. package/tsconfig.json +0 -59
  260. package/tsup.config.ts +0 -21
@@ -1,36 +0,0 @@
1
- import { checkThreshold } from "./checkThreshold";
2
- import { getContentSize } from "./state";
3
- import type { StateContext } from "./state";
4
- import type { InternalState } from "./types";
5
-
6
- export function checkAtBottom(ctx: StateContext, state: InternalState) {
7
- if (!state) {
8
- return;
9
- }
10
- const {
11
- queuedInitialLayout,
12
- scrollLength,
13
- scroll,
14
- maintainingScrollAtEnd,
15
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold },
16
- } = state;
17
- const contentSize = getContentSize(ctx);
18
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
19
- // Check if at end
20
- const distanceFromEnd = contentSize - scroll - scrollLength;
21
- const isContentLess = contentSize < scrollLength;
22
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold!;
23
-
24
- state.isEndReached = checkThreshold(
25
- distanceFromEnd,
26
- isContentLess,
27
- onEndReachedThreshold! * scrollLength,
28
- state.isEndReached,
29
- state.endReachedBlockedByTimer,
30
- (distance) => state.props.onEndReached?.({ distanceFromEnd: distance }),
31
- (block) => {
32
- state.endReachedBlockedByTimer = block;
33
- },
34
- );
35
- }
36
- }
package/src/checkAtTop.ts DELETED
@@ -1,27 +0,0 @@
1
- import { checkThreshold } from "./checkThreshold";
2
- import type { InternalState } from "./types";
3
-
4
- export function checkAtTop(state: InternalState) {
5
- if (!state) {
6
- return;
7
- }
8
- const {
9
- scrollLength,
10
- scroll,
11
- props: { onStartReachedThreshold },
12
- } = state;
13
- const distanceFromTop = scroll;
14
- state.isAtStart = distanceFromTop <= 0;
15
-
16
- state.isStartReached = checkThreshold(
17
- distanceFromTop,
18
- false,
19
- onStartReachedThreshold! * scrollLength,
20
- state.isStartReached,
21
- state.startReachedBlockedByTimer,
22
- (distance) => state.props.onStartReached?.({ distanceFromStart: distance }),
23
- (block) => {
24
- state.startReachedBlockedByTimer = block;
25
- },
26
- );
27
- }
@@ -1,30 +0,0 @@
1
- export const checkThreshold = (
2
- distance: number,
3
- atThreshold: boolean,
4
- threshold: number,
5
- isReached: boolean,
6
- isBlockedByTimer: boolean,
7
- onReached?: (distance: number) => void,
8
- blockTimer?: (block: boolean) => void,
9
- ) => {
10
- const distanceAbs = Math.abs(distance);
11
- const isAtThreshold = atThreshold || distanceAbs < threshold;
12
-
13
- if (!isReached && !isBlockedByTimer) {
14
- if (isAtThreshold) {
15
- onReached?.(distance);
16
- blockTimer?.(true);
17
- setTimeout(() => {
18
- blockTimer?.(false);
19
- }, 700);
20
- return true;
21
- }
22
- } else {
23
- // reset flag when user scrolls back out of the threshold
24
- // add hysteresis to avoid multiple events triggered
25
- if (distance >= 1.3 * threshold) {
26
- return false;
27
- }
28
- }
29
- return isReached;
30
- };
package/src/constants.ts DELETED
@@ -1,11 +0,0 @@
1
- export const POSITION_OUT_OF_VIEW = -10000000;
2
-
3
- // use colorful overlays to visualize the padding and scroll adjustments
4
- // green means paddingTop (used for aligning elements at the bottom)
5
- // lightblue means scrollAdjust (used for maintainVisibleContentPosition) positive values
6
- // blue arrow at the rights means negative scrollAdjust (used for maintainVisibleContentPosition) negative values
7
- export const ENABLE_DEVMODE = __DEV__ && false;
8
- export const ENABLE_DEBUG_VIEW = __DEV__ && false;
9
-
10
- // @ts-expect-error nativeFabricUIManager is not defined in the global object types
11
- export const IsNewArchitecture = global.nativeFabricUIManager != null;
@@ -1,16 +0,0 @@
1
- import type { ViewStyle } from "react-native";
2
- import type { ColumnWrapperStyle } from "./types";
3
-
4
- export function createColumnWrapperStyle(contentContainerStyle: ViewStyle): ColumnWrapperStyle | undefined {
5
- const { gap, columnGap, rowGap } = contentContainerStyle;
6
- if (gap || columnGap || rowGap) {
7
- contentContainerStyle.gap = undefined;
8
- contentContainerStyle.columnGap = undefined;
9
- contentContainerStyle.rowGap = undefined;
10
- return {
11
- gap: gap as number,
12
- columnGap: columnGap as number,
13
- rowGap: rowGap as number,
14
- };
15
- }
16
- }
@@ -1,40 +0,0 @@
1
- import { calculateItemsInView } from "./calculateItemsInView";
2
- import { IsNewArchitecture, POSITION_OUT_OF_VIEW } from "./constants";
3
- import { type StateContext, peek$, set$ } from "./state";
4
- import type { InternalState } from "./types";
5
-
6
- export function doInitialAllocateContainers(ctx: StateContext, state: InternalState): boolean | undefined {
7
- // Allocate containers
8
- const { scrollLength } = state;
9
- const data = state.props.data;
10
- if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
11
- const averageItemSize = state.props.getEstimatedItemSize
12
- ? state.props.getEstimatedItemSize(0, data[0])
13
- : state.props.estimatedItemSize;
14
- const Extra = 1.5; // TODO make it a prop, experiment with whether it's faster with more containers
15
- const numContainers = Math.ceil(
16
- ((scrollLength + state.props.scrollBuffer * 2) / averageItemSize!) * state.props.numColumns * Extra,
17
- );
18
-
19
- for (let i = 0; i < numContainers; i++) {
20
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
21
- set$(ctx, `containerColumn${i}`, -1);
22
- }
23
-
24
- set$(ctx, "numContainers", numContainers);
25
- set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
26
-
27
- if (!IsNewArchitecture) {
28
- if (state.props.initialScroll) {
29
- requestAnimationFrame(() => {
30
- // immediate render causes issues with initial index position
31
- calculateItemsInView(ctx, state);
32
- });
33
- } else {
34
- calculateItemsInView(ctx, state);
35
- }
36
- }
37
-
38
- return true;
39
- }
40
- }
@@ -1,34 +0,0 @@
1
- import { peek$ } from "./state";
2
- import type { StateContext } from "./state";
3
- import type { InternalState } from "./types";
4
-
5
- export function doMaintainScrollAtEnd(ctx: StateContext, state: InternalState, animated: boolean) {
6
- const {
7
- refScroller,
8
- props: { maintainScrollAtEnd },
9
- } = state;
10
- // Run this only if scroll is at the bottom and after initial layout
11
- if (state?.isAtEnd && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
12
- // Set scroll to the bottom of the list so that checkAtTop/checkAtBottom is correct
13
- const paddingTop = peek$(ctx, "alignItemsPaddingTop");
14
- if (paddingTop > 0) {
15
- // if paddingTop exists, list is shorter then a screen, so scroll should be 0 anyways
16
- state.scroll = 0;
17
- }
18
-
19
- requestAnimationFrame(() => {
20
- state.maintainingScrollAtEnd = true;
21
- refScroller.current?.scrollToEnd({
22
- animated,
23
- });
24
- setTimeout(
25
- () => {
26
- state.maintainingScrollAtEnd = false;
27
- },
28
- animated ? 500 : 0,
29
- );
30
- });
31
-
32
- return true;
33
- }
34
- }
@@ -1,98 +0,0 @@
1
- import { comparatorDefault } from "./helpers";
2
- import { type StateContext, peek$ } from "./state";
3
- import type { InternalState } from "./types";
4
-
5
- export function findAvailableContainers(
6
- ctx: StateContext,
7
- state: InternalState,
8
- numNeeded: number,
9
- startBuffered: number,
10
- endBuffered: number,
11
- pendingRemoval: number[],
12
- ): number[] {
13
- const numContainers = peek$(ctx, "numContainers") as number;
14
-
15
- const result: number[] = [];
16
- const availableContainers: Array<{ index: number; distance: number }> = [];
17
-
18
- // First pass: collect unallocated containers (most efficient to use)
19
- for (let u = 0; u < numContainers; u++) {
20
- const key = peek$(ctx, `containerItemKey${u}`);
21
- let isOk = key === undefined;
22
- if (!isOk) {
23
- const index = pendingRemoval.indexOf(u);
24
- if (index !== -1) {
25
- pendingRemoval.splice(index, 1);
26
- isOk = true;
27
- }
28
- }
29
- // Hasn't been allocated yet or is pending removal, so use it
30
- if (isOk) {
31
- result.push(u);
32
- if (result.length >= numNeeded) {
33
- return result; // Early exit if we have enough unallocated containers
34
- }
35
- }
36
- }
37
-
38
- // Second pass: collect containers that are out of view
39
- for (let u = 0; u < numContainers; u++) {
40
- const key = peek$(ctx, `containerItemKey${u}`);
41
- if (key === undefined) continue; // Skip already collected containers
42
-
43
- const index = state.indexByKey.get(key)!;
44
- if (index < startBuffered) {
45
- availableContainers.push({ index: u, distance: startBuffered - index });
46
- } else if (index > endBuffered) {
47
- availableContainers.push({ index: u, distance: index - endBuffered });
48
- }
49
- }
50
-
51
- // If we need more containers than we have available so far
52
- const remaining = numNeeded - result.length;
53
- if (remaining > 0) {
54
- if (availableContainers.length > 0) {
55
- // Only sort if we need to
56
- if (availableContainers.length > remaining) {
57
- // Sort by distance (furthest first)
58
- availableContainers.sort(comparatorByDistance);
59
- // Take just what we need
60
- availableContainers.length = remaining;
61
- }
62
-
63
- // Add to result, keeping track of original indices
64
- for (const container of availableContainers) {
65
- result.push(container.index);
66
- }
67
- }
68
-
69
- // If we still need more, create new containers
70
- const stillNeeded = numNeeded - result.length;
71
- if (stillNeeded > 0) {
72
- for (let i = 0; i < stillNeeded; i++) {
73
- result.push(numContainers + i);
74
- }
75
-
76
- if (__DEV__ && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
77
- console.warn(
78
- "[legend-list] No unused container available, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize or increasing initialContainerPoolRatio.",
79
- {
80
- debugInfo: {
81
- numContainers,
82
- numNeeded,
83
- stillNeeded,
84
- numContainersPooled: peek$(ctx, "numContainersPooled"),
85
- },
86
- },
87
- );
88
- }
89
- }
90
- }
91
-
92
- // Sort by index for consistent ordering
93
- return result.sort(comparatorDefault);
94
- }
95
-
96
- function comparatorByDistance(a: { distance: number }, b: { distance: number }) {
97
- return b.distance - a.distance;
98
- }
@@ -1,8 +0,0 @@
1
- import type { InternalState } from "./types";
2
-
3
- export const finishScrollTo = (state: InternalState | null | undefined) => {
4
- if (state) {
5
- state.scrollingTo = undefined;
6
- state.scrollHistory.length = 0;
7
- }
8
- };
package/src/getId.ts DELETED
@@ -1,21 +0,0 @@
1
- import type { InternalState } from "./types";
2
-
3
- /**
4
- * Generates and caches a unique ID for a list item at the given index.
5
- *
6
- * @param state - The internal state containing data, keyExtractor, and ID cache
7
- * @param index - The index of the item to get the ID for
8
- * @returns The unique ID for the item, or empty string if data is not available
9
- */
10
- export function getId(state: InternalState, index: number): string {
11
- const { data, keyExtractor } = state.props;
12
- if (!data) {
13
- return "";
14
- }
15
-
16
- // Generate and cache the ID
17
- const ret = index < data.length ? (keyExtractor ? keyExtractor(data[index], index) : index) : null;
18
- const id = ret as string;
19
- state.idCache.set(index, id);
20
- return id;
21
- }
@@ -1,52 +0,0 @@
1
- import { IsNewArchitecture } from "./constants";
2
- import type { InternalState } from "./types";
3
-
4
- export function getItemSize(
5
- state: InternalState,
6
- key: string,
7
- index: number,
8
- data: any,
9
- useAverageSize?: number | undefined,
10
- ) {
11
- const {
12
- sizesKnown,
13
- sizes,
14
- scrollingTo,
15
- props: { estimatedItemSize, getEstimatedItemSize },
16
- } = state;
17
- const sizeKnown = sizesKnown.get(key)!;
18
- if (sizeKnown !== undefined) {
19
- return sizeKnown;
20
- }
21
-
22
- let size: number | undefined;
23
-
24
- // Using average size is not supported on old architecture because it can't layout immediately
25
- if (
26
- IsNewArchitecture &&
27
- useAverageSize !== undefined &&
28
- sizeKnown === undefined &&
29
- !getEstimatedItemSize &&
30
- !scrollingTo
31
- ) {
32
- // TODO: Hook this up to actual item type later once we have item types
33
- size = useAverageSize;
34
- }
35
-
36
- if (size === undefined) {
37
- size = sizes.get(key)!;
38
-
39
- if (size !== undefined) {
40
- return size;
41
- }
42
- }
43
-
44
- if (size === undefined) {
45
- // Get estimated size if we don't have an average or already cached size
46
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize!;
47
- }
48
-
49
- // Save to rendered sizes
50
- sizes.set(key, size);
51
- return size;
52
- }
@@ -1,34 +0,0 @@
1
- import React from "react";
2
- import { type StateContext, peek$ } from "./state";
3
- import type { InternalState } from "./types";
4
-
5
- export function getRenderedItem(ctx: StateContext, state: InternalState, key: string) {
6
- if (!state) {
7
- return null;
8
- }
9
-
10
- const {
11
- indexByKey,
12
- props: { data, renderItem },
13
- } = state;
14
-
15
- const index = indexByKey.get(key);
16
-
17
- if (index === undefined) {
18
- return null;
19
- }
20
-
21
- let renderedItem: React.ReactNode = null;
22
-
23
- if (renderItem) {
24
- const itemProps = {
25
- item: data[index],
26
- index,
27
- extraData: peek$(ctx, "extraData"),
28
- };
29
-
30
- renderedItem = React.createElement(renderItem, itemProps);
31
- }
32
-
33
- return { index, item: data[index], renderedItem };
34
- }
@@ -1,47 +0,0 @@
1
- import type { InternalState } from "./types";
2
-
3
- export const getScrollVelocity = (state: InternalState) => {
4
- const { scrollHistory } = state;
5
- let velocity = 0;
6
- if (scrollHistory.length >= 1) {
7
- const newest = scrollHistory[scrollHistory.length - 1];
8
- let oldest: (typeof scrollHistory)[0] | undefined;
9
- let start = 0;
10
-
11
- // If there's a change in direction, remove all entries before that point
12
- for (let i = 0; i < scrollHistory.length - 1; i++) {
13
- const entry = scrollHistory[i];
14
- const nextEntry = scrollHistory[i + 1];
15
-
16
- // Check if direction changes - if so, remove older entries
17
- if (i > 0) {
18
- const prevEntry = scrollHistory[i - 1];
19
- const prevDirection = entry.scroll - prevEntry.scroll;
20
- const currentDirection = nextEntry.scroll - entry.scroll;
21
-
22
- // If direction changed, remove all entries before this point
23
- if ((prevDirection > 0 && currentDirection < 0) || (prevDirection < 0 && currentDirection > 0)) {
24
- start = i;
25
- break;
26
- }
27
- }
28
- }
29
-
30
- // Find oldest recent event
31
- for (let i = start; i < scrollHistory.length - 1; i++) {
32
- const entry = scrollHistory[i];
33
- if (newest.time - entry.time <= 1000) {
34
- oldest = entry;
35
- break;
36
- }
37
- }
38
-
39
- if (oldest) {
40
- const scrollDiff = newest.scroll - oldest.scroll;
41
- const timeDiff = newest.time - oldest.time;
42
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
43
- }
44
- }
45
-
46
- return velocity;
47
- };
@@ -1,70 +0,0 @@
1
- import type { LayoutRectangle } from "react-native";
2
- import { calculateItemsInView } from "./calculateItemsInView";
3
- import { checkAtBottom } from "./checkAtBottom";
4
- import { checkAtTop } from "./checkAtTop";
5
- import { doInitialAllocateContainers } from "./doInitialAllocateContainers";
6
- import { doMaintainScrollAtEnd } from "./doMaintainScrollAtEnd";
7
- import { warnDevOnce } from "./helpers";
8
- import { type StateContext, set$ } from "./state";
9
- import type { InternalState, MaintainScrollAtEndOptions } from "./types";
10
- import { updateAlignItemsPaddingTop } from "./updateAlignItemsPaddingTop";
11
-
12
- export function handleLayout(
13
- ctx: StateContext,
14
- state: InternalState,
15
- layout: LayoutRectangle,
16
- setCanRender: (canRender: boolean) => void,
17
- ) {
18
- const { maintainScrollAtEnd } = state.props;
19
-
20
- const scrollLength = layout[state.props.horizontal ? "width" : "height"];
21
- const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
22
-
23
- const needsCalculate =
24
- !state.lastLayout ||
25
- scrollLength > state.scrollLength ||
26
- state.lastLayout.x !== layout.x ||
27
- state.lastLayout.y !== layout.y;
28
-
29
- state.lastLayout = layout;
30
-
31
- const didChange = scrollLength !== state.scrollLength;
32
- const prevOtherAxisSize = state.otherAxisSize;
33
- state.scrollLength = scrollLength;
34
- state.otherAxisSize = otherAxisSize;
35
- state.lastBatchingAction = Date.now();
36
- state.scrollForNextCalculateItemsInView = undefined;
37
-
38
- doInitialAllocateContainers(ctx, state);
39
-
40
- if (needsCalculate) {
41
- calculateItemsInView(ctx, state, { doMVCP: true });
42
- }
43
- if (didChange || otherAxisSize !== prevOtherAxisSize) {
44
- set$(ctx, "scrollSize", { width: layout.width, height: layout.height });
45
- }
46
-
47
- if (maintainScrollAtEnd === true || (maintainScrollAtEnd as MaintainScrollAtEndOptions).onLayout) {
48
- doMaintainScrollAtEnd(ctx, state, false);
49
- }
50
- updateAlignItemsPaddingTop(ctx, state);
51
- checkAtBottom(ctx, state);
52
- checkAtTop(state);
53
-
54
- if (state) {
55
- // If otherAxisSize minus padding is less than 10, we need to set the size of the other axis
56
- // from the item height. 10 is just a magic number to account for border/outline or rounding errors.
57
- state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
58
- }
59
-
60
- if (__DEV__ && scrollLength === 0) {
61
- warnDevOnce(
62
- "height0",
63
- `List ${
64
- state.props.horizontal ? "width" : "height"
65
- } is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`,
66
- );
67
- }
68
-
69
- setCanRender(true);
70
- }
package/src/helpers.ts DELETED
@@ -1,39 +0,0 @@
1
- import type { ViewStyle } from "react-native";
2
-
3
- export function isFunction(obj: unknown): obj is (...args: any[]) => any {
4
- return typeof obj === "function";
5
- }
6
- export function isArray(obj: unknown): obj is Array<any> {
7
- return Array.isArray(obj);
8
- }
9
-
10
- const warned = new Set<string>();
11
- export function warnDevOnce(id: string, text: string) {
12
- if (__DEV__ && !warned.has(id)) {
13
- warned.add(id);
14
- console.warn(`[legend-list] ${text}`);
15
- }
16
- }
17
-
18
- export function roundSize(size: number) {
19
- return Math.floor(size * 8) / 8; // Round to nearest quater pixel to avoid accumulating rounding errors
20
- }
21
-
22
- export function isNullOrUndefined(value: unknown) {
23
- return value === null || value === undefined;
24
- }
25
-
26
- export function comparatorDefault(a: number, b: number) {
27
- return a - b;
28
- }
29
-
30
- export function byIndex(a: { index: number }) {
31
- return a.index;
32
- }
33
-
34
- function getPadding(s: ViewStyle, type: "Top" | "Bottom") {
35
- return (s[`padding${type}`] ?? s.paddingVertical ?? s.padding ?? 0) as number;
36
- }
37
- export function extractPadding(style: ViewStyle, contentContainerStyle: ViewStyle, type: "Top" | "Bottom") {
38
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
39
- }
package/src/index.ts DELETED
@@ -1,11 +0,0 @@
1
- export { LegendList } from "./LegendList";
2
- export { LazyLegendList, type LazyLegendListProps } from "./LazyLegendList";
3
- export {
4
- useIsLastItem,
5
- useListScrollSize,
6
- useRecyclingEffect,
7
- useRecyclingState,
8
- useViewability,
9
- useViewabilityAmount,
10
- } from "./ContextContainer";
11
- export type * from "./types";
@@ -1,63 +0,0 @@
1
- import { LegendList as LegendListBase, type LegendListProps, type LegendListRef } from "@legendapp/list";
2
- import type { AnimatedLegendList } from "@legendapp/list/animated";
3
- import type { AnimatedLegendList as ReanimatedLegendList } from "@legendapp/list/reanimated";
4
- // biome-ignore lint/style/useImportType: Leaving this out makes it crash in some environments
5
- import * as React from "react";
6
- import { type ForwardedRef, forwardRef, useState } from "react";
7
- import { type Insets, StyleSheet } from "react-native";
8
- import { useKeyboardHandler } from "react-native-keyboard-controller";
9
- import { runOnJS } from "react-native-reanimated";
10
-
11
- // biome-ignore lint/complexity/noBannedTypes: This is a workaround for the fact that forwardRef is not typed
12
- type TypedForwardRef = <T, P = {}>(
13
- render: (props: P, ref: React.Ref<T>) => React.ReactNode,
14
- ) => (props: P & React.RefAttributes<T>) => React.ReactNode;
15
-
16
- const typedForwardRef = forwardRef as TypedForwardRef;
17
-
18
- export const LegendList = typedForwardRef(function LegendList<
19
- ItemT,
20
- ListT extends
21
- | typeof LegendListBase
22
- | typeof AnimatedLegendList
23
- | typeof ReanimatedLegendList = typeof LegendListBase,
24
- >(props: LegendListProps<ItemT> & { LegendList?: ListT }, forwardedRef: ForwardedRef<LegendListRef>) {
25
- const {
26
- LegendList: LegendListProp,
27
- contentContainerStyle: contentContainerStyleProp,
28
- scrollIndicatorInsets: scrollIndicatorInsetsProp,
29
- ...rest
30
- } = props;
31
- const [padding, setPadding] = useState(0);
32
-
33
- // Define this function outside the worklet
34
- const updatePadding = (height: number) => {
35
- setPadding(height);
36
- };
37
-
38
- useKeyboardHandler({
39
- onEnd: (e) => {
40
- "worklet";
41
- runOnJS(updatePadding)(e.height);
42
- },
43
- });
44
-
45
- const LegendListComponent = LegendListProp ?? LegendListBase;
46
-
47
- const contentContainerStyleFlattened = StyleSheet.flatten(contentContainerStyleProp) || {};
48
- const contentContainerStyle = { ...contentContainerStyleFlattened, paddingTop: padding };
49
- const scrollIndicatorInsets: Insets = scrollIndicatorInsetsProp ? { ...scrollIndicatorInsetsProp } : {};
50
- if (!props.horizontal) {
51
- scrollIndicatorInsets.top = (scrollIndicatorInsets?.top || 0) + padding;
52
- }
53
-
54
- return (
55
- // @ts-expect-error TODO: Fix this type
56
- <LegendListComponent
57
- {...rest}
58
- contentContainerStyle={contentContainerStyle}
59
- scrollIndicatorInsets={scrollIndicatorInsets}
60
- ref={forwardedRef}
61
- />
62
- );
63
- });