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

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/index.d.mts → index.d.mts} +28 -21
  2. package/{dist/index.d.ts → index.d.ts} +28 -21
  3. package/{dist/index.js → index.js} +808 -760
  4. package/{dist/index.mjs → index.mjs} +808 -760
  5. package/{dist → integrations}/animated.d.mts +2 -1
  6. package/{dist → integrations}/animated.d.ts +2 -1
  7. package/{dist → integrations}/animated.js +2 -2
  8. package/{dist → integrations}/animated.mjs +2 -2
  9. package/{dist → integrations}/keyboard-controller.d.mts +4 -0
  10. package/{dist → integrations}/keyboard-controller.d.ts +4 -0
  11. package/{dist → integrations}/keyboard-controller.js +4 -4
  12. package/{dist → integrations}/keyboard-controller.mjs +4 -4
  13. package/{dist → integrations}/reanimated.js +7 -7
  14. package/{dist → integrations}/reanimated.mjs +7 -7
  15. package/package.json +34 -88
  16. package/.claude/settings.local.json +0 -8
  17. package/.cursor/rules/changelog.mdc +0 -60
  18. package/.github/FUNDING.yml +0 -15
  19. package/.gitignore +0 -5
  20. package/.prettierrc.json +0 -5
  21. package/.vscode/settings.json +0 -14
  22. package/CLAUDE.md +0 -126
  23. package/biome.json +0 -46
  24. package/bun.lock +0 -1289
  25. package/bunfig.toml +0 -2
  26. package/dist/CHANGELOG.md +0 -119
  27. package/dist/LICENSE +0 -21
  28. package/dist/README.md +0 -139
  29. package/dist/package.json +0 -35
  30. package/example/README.md +0 -40
  31. package/example/api/data/genres.json +0 -23
  32. package/example/api/data/playlist/10402-10749.json +0 -1
  33. package/example/api/data/playlist/10402-10770.json +0 -1
  34. package/example/api/data/playlist/10402-37.json +0 -1
  35. package/example/api/data/playlist/10749-10752.json +0 -1
  36. package/example/api/data/playlist/10749-10770.json +0 -1
  37. package/example/api/data/playlist/10749-37.json +0 -1
  38. package/example/api/data/playlist/10749-878.json +0 -1
  39. package/example/api/data/playlist/10751-10402.json +0 -1
  40. package/example/api/data/playlist/10751-10752.json +0 -1
  41. package/example/api/data/playlist/10751-37.json +0 -1
  42. package/example/api/data/playlist/10751-53.json +0 -1
  43. package/example/api/data/playlist/10751-878.json +0 -1
  44. package/example/api/data/playlist/10751-9648.json +0 -1
  45. package/example/api/data/playlist/10752-37.json +0 -1
  46. package/example/api/data/playlist/12-10402.json +0 -1
  47. package/example/api/data/playlist/12-10749.json +0 -1
  48. package/example/api/data/playlist/12-18.json +0 -1
  49. package/example/api/data/playlist/12-27.json +0 -1
  50. package/example/api/data/playlist/12-35.json +0 -1
  51. package/example/api/data/playlist/14-36.json +0 -1
  52. package/example/api/data/playlist/14-878.json +0 -1
  53. package/example/api/data/playlist/16-10751.json +0 -1
  54. package/example/api/data/playlist/16-10770.json +0 -1
  55. package/example/api/data/playlist/16-35.json +0 -1
  56. package/example/api/data/playlist/16-36.json +0 -1
  57. package/example/api/data/playlist/16-53.json +0 -1
  58. package/example/api/data/playlist/18-10751.json +0 -1
  59. package/example/api/data/playlist/18-10752.json +0 -1
  60. package/example/api/data/playlist/18-37.json +0 -1
  61. package/example/api/data/playlist/18-53.json +0 -1
  62. package/example/api/data/playlist/18-878.json +0 -1
  63. package/example/api/data/playlist/27-10749.json +0 -1
  64. package/example/api/data/playlist/27-10770.json +0 -1
  65. package/example/api/data/playlist/28-10749.json +0 -1
  66. package/example/api/data/playlist/28-10751.json +0 -1
  67. package/example/api/data/playlist/28-10770.json +0 -1
  68. package/example/api/data/playlist/28-16.json +0 -1
  69. package/example/api/data/playlist/28-18.json +0 -1
  70. package/example/api/data/playlist/28-36.json +0 -1
  71. package/example/api/data/playlist/28-37.json +0 -1
  72. package/example/api/data/playlist/28-53.json +0 -1
  73. package/example/api/data/playlist/28-80.json +0 -1
  74. package/example/api/data/playlist/28-99.json +0 -1
  75. package/example/api/data/playlist/35-10749.json +0 -1
  76. package/example/api/data/playlist/35-10751.json +0 -1
  77. package/example/api/data/playlist/35-10752.json +0 -1
  78. package/example/api/data/playlist/35-27.json +0 -1
  79. package/example/api/data/playlist/35-36.json +0 -1
  80. package/example/api/data/playlist/35-53.json +0 -1
  81. package/example/api/data/playlist/35-80.json +0 -1
  82. package/example/api/data/playlist/36-37.json +0 -1
  83. package/example/api/data/playlist/36-878.json +0 -1
  84. package/example/api/data/playlist/36-9648.json +0 -1
  85. package/example/api/data/playlist/53-10752.json +0 -1
  86. package/example/api/data/playlist/80-10770.json +0 -1
  87. package/example/api/data/playlist/80-14.json +0 -1
  88. package/example/api/data/playlist/80-18.json +0 -1
  89. package/example/api/data/playlist/80-37.json +0 -1
  90. package/example/api/data/playlist/878-37.json +0 -1
  91. package/example/api/data/playlist/9648-10770.json +0 -1
  92. package/example/api/data/playlist/9648-37.json +0 -1
  93. package/example/api/data/playlist/9648-53.json +0 -1
  94. package/example/api/data/playlist/9648-878.json +0 -1
  95. package/example/api/data/playlist/99-10749.json +0 -1
  96. package/example/api/data/playlist/99-14.json +0 -1
  97. package/example/api/data/playlist/99-18.json +0 -1
  98. package/example/api/data/playlist/99-27.json +0 -1
  99. package/example/api/data/playlist/99-53.json +0 -1
  100. package/example/api/data/playlist/99-9648.json +0 -1
  101. package/example/api/data/playlist/index.ts +0 -73
  102. package/example/api/data/rows.json +0 -1
  103. package/example/api/index.ts +0 -36
  104. package/example/app/(tabs)/_layout.tsx +0 -60
  105. package/example/app/(tabs)/cards.tsx +0 -81
  106. package/example/app/(tabs)/index.tsx +0 -205
  107. package/example/app/(tabs)/moviesL.tsx +0 -7
  108. package/example/app/(tabs)/moviesLR.tsx +0 -7
  109. package/example/app/+not-found.tsx +0 -32
  110. package/example/app/_layout.tsx +0 -34
  111. package/example/app/accurate-scrollto/index.tsx +0 -125
  112. package/example/app/accurate-scrollto-2/index.tsx +0 -52
  113. package/example/app/accurate-scrollto-huge/index.tsx +0 -128
  114. package/example/app/add-to-end/index.tsx +0 -82
  115. package/example/app/ai-chat/index.tsx +0 -236
  116. package/example/app/bidirectional-infinite-list/index.tsx +0 -133
  117. package/example/app/cards-columns/index.tsx +0 -37
  118. package/example/app/cards-flashlist/index.tsx +0 -122
  119. package/example/app/cards-flatlist/index.tsx +0 -94
  120. package/example/app/cards-no-recycle/index.tsx +0 -110
  121. package/example/app/cards-renderItem.tsx +0 -354
  122. package/example/app/chat-example/index.tsx +0 -167
  123. package/example/app/chat-infinite/index.tsx +0 -239
  124. package/example/app/chat-keyboard/index.tsx +0 -248
  125. package/example/app/chat-resize-outer/index.tsx +0 -247
  126. package/example/app/columns/index.tsx +0 -78
  127. package/example/app/countries/index.tsx +0 -182
  128. package/example/app/countries-flashlist/index.tsx +0 -163
  129. package/example/app/countries-reorder/index.tsx +0 -187
  130. package/example/app/extra-data/index.tsx +0 -86
  131. package/example/app/filter-elements/filter-data-provider.tsx +0 -55
  132. package/example/app/filter-elements/index.tsx +0 -118
  133. package/example/app/initial-scroll-index/index.tsx +0 -106
  134. package/example/app/initial-scroll-index/renderFixedItem.tsx +0 -215
  135. package/example/app/initial-scroll-index-free-height/index.tsx +0 -70
  136. package/example/app/initial-scroll-index-keyed/index.tsx +0 -62
  137. package/example/app/lazy-list/index.tsx +0 -123
  138. package/example/app/movies-flashlist/index.tsx +0 -7
  139. package/example/app/mutable-cells/index.tsx +0 -104
  140. package/example/app/video-feed/index.tsx +0 -119
  141. package/example/app.config.js +0 -22
  142. package/example/app.json +0 -45
  143. package/example/assets/fonts/SpaceMono-Regular.ttf +0 -0
  144. package/example/assets/images/adaptive-icon.png +0 -0
  145. package/example/assets/images/favicon.png +0 -0
  146. package/example/assets/images/icon.png +0 -0
  147. package/example/assets/images/partial-react-logo.png +0 -0
  148. package/example/assets/images/react-logo.png +0 -0
  149. package/example/assets/images/react-logo@2x.png +0 -0
  150. package/example/assets/images/react-logo@3x.png +0 -0
  151. package/example/assets/images/splash-icon.png +0 -0
  152. package/example/autoscroll.sh +0 -101
  153. package/example/bun.lock +0 -2266
  154. package/example/bunfig.toml +0 -2
  155. package/example/components/Breathe.tsx +0 -54
  156. package/example/components/Circle.tsx +0 -69
  157. package/example/components/Collapsible.tsx +0 -44
  158. package/example/components/ExternalLink.tsx +0 -24
  159. package/example/components/HapticTab.tsx +0 -18
  160. package/example/components/HelloWave.tsx +0 -37
  161. package/example/components/Movies.tsx +0 -179
  162. package/example/components/ParallaxScrollView.tsx +0 -81
  163. package/example/components/ThemedText.tsx +0 -60
  164. package/example/components/ThemedView.tsx +0 -14
  165. package/example/components/__tests__/ThemedText-test.tsx +0 -10
  166. package/example/components/__tests__/__snapshots__/ThemedText-test.tsx.snap +0 -24
  167. package/example/components/ui/IconSymbol.ios.tsx +0 -32
  168. package/example/components/ui/IconSymbol.tsx +0 -43
  169. package/example/components/ui/TabBarBackground.ios.tsx +0 -22
  170. package/example/components/ui/TabBarBackground.tsx +0 -6
  171. package/example/constants/Colors.ts +0 -26
  172. package/example/constants/constants.ts +0 -5
  173. package/example/constants/useScrollTest.ts +0 -19
  174. package/example/hooks/useColorScheme.ts +0 -1
  175. package/example/hooks/useColorScheme.web.ts +0 -8
  176. package/example/hooks/useThemeColor.ts +0 -22
  177. package/example/ios/.xcode.env +0 -11
  178. package/example/ios/Podfile +0 -64
  179. package/example/ios/Podfile.lock +0 -2767
  180. package/example/ios/Podfile.properties.json +0 -5
  181. package/example/ios/listtest/AppDelegate.swift +0 -70
  182. package/example/ios/listtest/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
  183. package/example/ios/listtest/Images.xcassets/AppIcon.appiconset/Contents.json +0 -14
  184. package/example/ios/listtest/Images.xcassets/Contents.json +0 -6
  185. package/example/ios/listtest/Images.xcassets/SplashScreenBackground.colorset/Contents.json +0 -20
  186. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/Contents.json +0 -23
  187. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image.png +0 -0
  188. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image@2x.png +0 -0
  189. package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image@3x.png +0 -0
  190. package/example/ios/listtest/Info.plist +0 -85
  191. package/example/ios/listtest/PrivacyInfo.xcprivacy +0 -48
  192. package/example/ios/listtest/SplashScreen.storyboard +0 -42
  193. package/example/ios/listtest/Supporting/Expo.plist +0 -12
  194. package/example/ios/listtest/listtest-Bridging-Header.h +0 -3
  195. package/example/ios/listtest/listtest.entitlements +0 -5
  196. package/example/ios/listtest.xcodeproj/project.pbxproj +0 -547
  197. package/example/ios/listtest.xcodeproj/xcshareddata/xcschemes/listtest.xcscheme +0 -88
  198. package/example/ios/listtest.xcworkspace/contents.xcworkspacedata +0 -10
  199. package/example/metro.config.js +0 -16
  200. package/example/package.json +0 -73
  201. package/example/scripts/reset-project.js +0 -84
  202. package/example/tsconfig.json +0 -26
  203. package/posttsup.ts +0 -24
  204. package/src/Container.tsx +0 -176
  205. package/src/Containers.tsx +0 -85
  206. package/src/ContextContainer.ts +0 -145
  207. package/src/DebugView.tsx +0 -83
  208. package/src/LazyLegendList.tsx +0 -41
  209. package/src/LeanView.tsx +0 -18
  210. package/src/LegendList.tsx +0 -558
  211. package/src/ListComponent.tsx +0 -191
  212. package/src/ScrollAdjust.tsx +0 -24
  213. package/src/ScrollAdjustHandler.ts +0 -26
  214. package/src/Separator.tsx +0 -14
  215. package/src/animated.tsx +0 -6
  216. package/src/calculateItemsInView.ts +0 -363
  217. package/src/calculateOffsetForIndex.ts +0 -23
  218. package/src/calculateOffsetWithOffsetPosition.ts +0 -26
  219. package/src/checkAllSizesKnown.ts +0 -17
  220. package/src/checkAtBottom.ts +0 -36
  221. package/src/checkAtTop.ts +0 -27
  222. package/src/checkThreshold.ts +0 -30
  223. package/src/constants.ts +0 -11
  224. package/src/createColumnWrapperStyle.ts +0 -16
  225. package/src/doInitialAllocateContainers.ts +0 -40
  226. package/src/doMaintainScrollAtEnd.ts +0 -34
  227. package/src/findAvailableContainers.ts +0 -98
  228. package/src/finishScrollTo.ts +0 -8
  229. package/src/getId.ts +0 -21
  230. package/src/getItemSize.ts +0 -52
  231. package/src/getRenderedItem.ts +0 -34
  232. package/src/getScrollVelocity.ts +0 -47
  233. package/src/handleLayout.ts +0 -70
  234. package/src/helpers.ts +0 -39
  235. package/src/index.ts +0 -11
  236. package/src/keyboard-controller.tsx +0 -63
  237. package/src/onScroll.ts +0 -66
  238. package/src/prepareMVCP.ts +0 -50
  239. package/src/reanimated.tsx +0 -63
  240. package/src/requestAdjust.ts +0 -41
  241. package/src/scrollTo.ts +0 -40
  242. package/src/scrollToIndex.ts +0 -34
  243. package/src/setDidLayout.ts +0 -25
  244. package/src/setPaddingTop.ts +0 -28
  245. package/src/state.tsx +0 -304
  246. package/src/types.ts +0 -610
  247. package/src/updateAlignItemsPaddingTop.ts +0 -18
  248. package/src/updateAllPositions.ts +0 -130
  249. package/src/updateItemSize.ts +0 -203
  250. package/src/updateTotalSize.ts +0 -44
  251. package/src/useAnimatedValue.ts +0 -6
  252. package/src/useCombinedRef.ts +0 -22
  253. package/src/useInit.ts +0 -17
  254. package/src/useSyncLayout.tsx +0 -68
  255. package/src/useValue$.ts +0 -53
  256. package/src/viewability.ts +0 -279
  257. package/tsconfig.json +0 -59
  258. package/tsup.config.ts +0 -21
  259. package/{dist → integrations}/reanimated.d.mts +1 -1
  260. package/{dist → integrations}/reanimated.d.ts +1 -1
package/src/onScroll.ts DELETED
@@ -1,66 +0,0 @@
1
- import type { NativeScrollEvent, NativeSyntheticEvent } from "react-native";
2
- import { calculateItemsInView } from "./calculateItemsInView";
3
- import { checkAtBottom } from "./checkAtBottom";
4
- import { checkAtTop } from "./checkAtTop";
5
- import type { StateContext } from "./state";
6
- import type { InternalState } from "./types";
7
-
8
- export function onScroll(
9
- ctx: StateContext,
10
- state: InternalState,
11
- event: {
12
- nativeEvent: NativeScrollEvent;
13
- },
14
- ) {
15
- if (event.nativeEvent?.contentSize?.height === 0 && event.nativeEvent.contentSize?.width === 0) {
16
- return;
17
- }
18
-
19
- const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
20
-
21
- // Ignore scroll events that are too close to the previous scroll position
22
- // after adjusting for MVCP
23
- const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
24
- if (ignoreScrollFromMVCP && !state.scrollingTo) {
25
- const { lt, gt } = ignoreScrollFromMVCP;
26
- if ((lt && newScroll < lt) || (gt && newScroll > gt)) {
27
- return;
28
- }
29
- }
30
-
31
- state.scrollPending = newScroll;
32
-
33
- updateScroll(ctx, state, newScroll);
34
-
35
- state.props.onScroll?.(event as NativeSyntheticEvent<NativeScrollEvent>);
36
- }
37
-
38
- function updateScroll(ctx: StateContext, state: InternalState, newScroll: number) {
39
- const scrollingTo = state.scrollingTo;
40
-
41
- state.hasScrolled = true;
42
- state.lastBatchingAction = Date.now();
43
- const currentTime = performance.now();
44
-
45
- // Don't add to the history if it's initial scroll event otherwise invalid velocity will be calculated
46
- // Don't add to the history if we are scrolling to an offset
47
- if (scrollingTo === undefined && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
48
- // Update scroll history
49
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
50
- }
51
-
52
- // Keep only last 5 entries
53
- if (state.scrollHistory.length > 5) {
54
- state.scrollHistory.shift();
55
- }
56
-
57
- // Update current scroll state
58
- state.scrollPrev = state.scroll;
59
- state.scrollPrevTime = state.scrollTime;
60
- state.scroll = newScroll;
61
- state.scrollTime = currentTime;
62
- // Use velocity to predict scroll position
63
- calculateItemsInView(ctx, state);
64
- checkAtBottom(ctx, state);
65
- checkAtTop(state);
66
- }
@@ -1,50 +0,0 @@
1
- import { getId } from "./getId";
2
- import { requestAdjust } from "./requestAdjust";
3
- import { type StateContext, peek$ } from "./state";
4
- import type { InternalState } from "./types";
5
-
6
- export function prepareMVCP(ctx: StateContext, state: InternalState): () => void {
7
- const {
8
- positions,
9
- scrollingTo,
10
- props: { maintainVisibleContentPosition },
11
- } = state;
12
-
13
- let prevPosition: number;
14
- let targetId: string | undefined;
15
- let targetIndex: number | undefined;
16
- const scrollTarget = scrollingTo?.index;
17
-
18
- if (maintainVisibleContentPosition) {
19
- const indexByKey = state.indexByKey;
20
-
21
- if (scrollTarget !== undefined) {
22
- // If we're currently scrolling to a target index, do MVCP for its position
23
- targetId = getId(state, scrollTarget);
24
- targetIndex = scrollTarget;
25
- } else if (state.idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
26
- // Do MVCP for the first item fully in view
27
- targetId = state.idsInView.find((id) => indexByKey.get(id) !== undefined);
28
- targetIndex = indexByKey.get(targetId!);
29
- }
30
-
31
- if (targetId !== undefined && targetIndex !== undefined) {
32
- prevPosition = positions.get(targetId)!;
33
- }
34
- }
35
-
36
- // Return a function to do MVCP based on the prepared values
37
- return () => {
38
- if (targetId !== undefined && prevPosition !== undefined) {
39
- const newPosition = positions.get(targetId);
40
-
41
- if (newPosition !== undefined) {
42
- const positionDiff = newPosition - prevPosition;
43
-
44
- if (Math.abs(positionDiff) > 0.1) {
45
- requestAdjust(ctx, state, positionDiff);
46
- }
47
- }
48
- }
49
- };
50
- }
@@ -1,63 +0,0 @@
1
- import { LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRef } from "@legendapp/list";
2
- import React, { type ComponentProps } from "react";
3
- import Animated from "react-native-reanimated";
4
- import { useCombinedRef } from "./useCombinedRef";
5
-
6
- type KeysToOmit =
7
- | "getEstimatedItemSize"
8
- | "keyExtractor"
9
- | "animatedProps"
10
- | "renderItem"
11
- | "onItemSizeChanged"
12
- | "ItemSeparatorComponent";
13
-
14
- type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Animated.ScrollView>>;
15
-
16
- export interface AnimatedLegendListPropsBase<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit> {
17
- refScrollView?: React.Ref<Animated.ScrollView>;
18
- }
19
-
20
- type OtherAnimatedLegendListProps<ItemT> = Pick<PropsBase<ItemT>, KeysToOmit>;
21
-
22
- // A component that receives a ref for the Animated.ScrollView and passes it to the LegendList
23
- const LegendListForwardedRef = React.forwardRef(function LegendListForwardedRef<ItemT>(
24
- props: LegendListProps<ItemT> & { refLegendList: (r: LegendListRef | null) => void },
25
- ref: React.Ref<Animated.ScrollView>,
26
- ) {
27
- const { refLegendList, ...rest } = props;
28
-
29
- return (
30
- <LegendList
31
- refScrollView={ref}
32
- ref={(r) => {
33
- refLegendList(r);
34
- }}
35
- {...rest}
36
- />
37
- );
38
- });
39
-
40
- const AnimatedLegendListComponent = Animated.createAnimatedComponent(LegendListForwardedRef);
41
-
42
- type AnimatedLegendListProps<ItemT> = Omit<AnimatedLegendListPropsBase<ItemT>, "refLegendList" | "ref"> &
43
- OtherAnimatedLegendListProps<ItemT>;
44
-
45
- type AnimatedLegendListDefinition = <ItemT>(
46
- props: AnimatedLegendListProps<ItemT> & { ref?: React.Ref<LegendListRef> },
47
- ) => React.ReactElement | null;
48
-
49
- // A component that has the shape of LegendList which passes the ref down as refLegendList
50
- const AnimatedLegendList = React.forwardRef(function AnimatedLegendList<ItemT>(
51
- props: AnimatedLegendListProps<ItemT>,
52
- ref: React.Ref<LegendListRef>,
53
- ) {
54
- const { refScrollView, ...rest } = props as AnimatedLegendListPropsBase<ItemT>;
55
-
56
- const refLegendList = React.useRef<LegendListRef | null>(null);
57
-
58
- const combinedRef = useCombinedRef(refLegendList, ref);
59
-
60
- return <AnimatedLegendListComponent refLegendList={combinedRef} ref={refScrollView} {...rest} />;
61
- }) as AnimatedLegendListDefinition;
62
-
63
- export { AnimatedLegendList, type AnimatedLegendListProps };
@@ -1,41 +0,0 @@
1
- import { type StateContext, peek$ } from "./state";
2
- import type { InternalState } from "./types";
3
-
4
- export function requestAdjust(ctx: StateContext, state: InternalState, positionDiff: number) {
5
- if (Math.abs(positionDiff) > 0.1) {
6
- const doit = () => {
7
- state.scrollAdjustHandler.requestAdjust(positionDiff);
8
- };
9
- state.scroll += positionDiff;
10
- state.scrollForNextCalculateItemsInView = undefined;
11
-
12
- const didLayout = peek$(ctx, "containersDidLayout");
13
-
14
- if (didLayout) {
15
- doit();
16
-
17
- // Calculate a threshold to ignore scroll jumps for a short period of time
18
- // This is to avoid the case where a scroll event comes in that was relevant from before
19
- // the requestAdjust. So we ignore scroll events that are closer to the previous
20
- // scroll position than the target position.
21
- const threshold = state.scroll - positionDiff / 2;
22
- if (!state.ignoreScrollFromMVCP) {
23
- state.ignoreScrollFromMVCP = {};
24
- }
25
- if (positionDiff > 0) {
26
- state.ignoreScrollFromMVCP.lt = threshold;
27
- } else {
28
- state.ignoreScrollFromMVCP.gt = threshold;
29
- }
30
-
31
- if (state.ignoreScrollFromMVCPTimeout) {
32
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
33
- }
34
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
35
- state.ignoreScrollFromMVCP = undefined;
36
- }, 100);
37
- } else {
38
- requestAnimationFrame(doit);
39
- }
40
- }
41
- }
package/src/scrollTo.ts DELETED
@@ -1,40 +0,0 @@
1
- import { calculateOffsetWithOffsetPosition } from "./calculateOffsetWithOffsetPosition";
2
- import { finishScrollTo } from "./finishScrollTo";
3
- import type { InternalState } from "./types";
4
-
5
- export function scrollTo(
6
- state: InternalState,
7
- params: {
8
- animated?: boolean;
9
- index?: number;
10
- offset: number;
11
- viewOffset?: number;
12
- viewPosition?: number;
13
- } = {} as any,
14
- ) {
15
- const { animated } = params;
16
- const {
17
- refScroller,
18
- props: { horizontal },
19
- } = state;
20
-
21
- const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
22
-
23
- // Disable scroll adjust while scrolling so that it doesn't do extra work affecting the target offset
24
- state.scrollHistory.length = 0;
25
- state.scrollingTo = params;
26
- state.scrollPending = offset;
27
- // Do the scroll
28
- refScroller.current?.scrollTo({
29
- x: horizontal ? offset : 0,
30
- y: horizontal ? 0 : offset,
31
- animated: !!animated,
32
- });
33
-
34
- if (!animated) {
35
- state.scroll = offset;
36
- // TODO: Should this not be a timeout, and instead wait for all item layouts to settle?
37
- // It's used for mvcp for when items change size above scroll.
38
- setTimeout(() => finishScrollTo(state), 100);
39
- }
40
- }
@@ -1,34 +0,0 @@
1
- import { calculateOffsetForIndex } from "./calculateOffsetForIndex";
2
- import { scrollTo } from "./scrollTo";
3
- import type { StateContext } from "./state";
4
- import type { InternalState, LegendListRef } from "./types";
5
-
6
- export function scrollToIndex(
7
- ctx: StateContext,
8
- state: InternalState,
9
- { index, viewOffset = 0, animated = true, viewPosition }: Parameters<LegendListRef["scrollToIndex"]>[0],
10
- ) {
11
- if (index >= state.props.data.length) {
12
- index = state.props.data.length - 1;
13
- } else if (index < 0) {
14
- index = 0;
15
- }
16
-
17
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
18
-
19
- const isLast = index === state.props.data.length - 1;
20
- if (isLast && viewPosition === undefined) {
21
- viewPosition = 1;
22
- }
23
- const firstIndexScrollPostion = firstIndexOffset - viewOffset;
24
-
25
- state.scrollForNextCalculateItemsInView = undefined;
26
-
27
- scrollTo(state, {
28
- offset: firstIndexScrollPostion,
29
- animated,
30
- index,
31
- viewPosition: viewPosition ?? 0,
32
- viewOffset,
33
- });
34
- }
@@ -1,25 +0,0 @@
1
- import { checkAtBottom } from "./checkAtBottom";
2
- import { IsNewArchitecture } from "./constants";
3
- import { scrollToIndex } from "./scrollToIndex";
4
- import { type StateContext, set$ } from "./state";
5
- import type { InternalState } from "./types";
6
-
7
- export function setDidLayout(ctx: StateContext, state: InternalState) {
8
- const {
9
- loadStartTime,
10
- initialScroll,
11
- props: { onLoad },
12
- } = state;
13
- state.queuedInitialLayout = true;
14
- checkAtBottom(ctx, state);
15
-
16
- if (!IsNewArchitecture && initialScroll) {
17
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
18
- }
19
-
20
- set$(ctx, "containersDidLayout", true);
21
-
22
- if (onLoad) {
23
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
24
- }
25
- }
@@ -1,28 +0,0 @@
1
- import { type StateContext, peek$, set$ } from "./state";
2
-
3
- export function setPaddingTop(
4
- ctx: StateContext,
5
- { stylePaddingTop, alignItemsPaddingTop }: { stylePaddingTop?: number; alignItemsPaddingTop?: number },
6
- ) {
7
- if (stylePaddingTop !== undefined) {
8
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
9
- if (stylePaddingTop < prevStylePaddingTop) {
10
- // If reducing top padding then we need to make sure the ScrollView doesn't
11
- // scroll itself because the height reduced.
12
- // First add the padding to the total size so that the total height in the ScrollView
13
- // doesn't change
14
- const prevTotalSize = peek$(ctx, "totalSize") || 0;
15
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
16
- setTimeout(() => {
17
- // Then reset it back to how it was
18
- set$(ctx, "totalSize", prevTotalSize);
19
- }, 16);
20
- }
21
-
22
- // Now set the padding
23
- set$(ctx, "stylePaddingTop", stylePaddingTop);
24
- }
25
- if (alignItemsPaddingTop !== undefined) {
26
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
27
- }
28
- }
package/src/state.tsx DELETED
@@ -1,304 +0,0 @@
1
- import * as React from "react";
2
- import type { View } from "react-native";
3
- import { useSyncExternalStore } from "use-sync-external-store/shim";
4
- import type {
5
- ColumnWrapperStyle,
6
- ViewAmountToken,
7
- ViewToken,
8
- ViewabilityAmountCallback,
9
- ViewabilityCallback,
10
- } from "./types";
11
-
12
- // This is an implementation of a simple state management system, inspired by Legend State.
13
- // It stores values and listeners in Maps, with peek$ and set$ functions to get and set values.
14
- // The set$ function also triggers the listeners.
15
- //
16
- // This is definitely not general purpose and has one big optimization/caveat: use$ is only ever called
17
- // once for each unique name. So we don't need to manage a Set of listeners or dispose them,
18
- // which saves needing useEffect hooks or managing listeners in a Set.
19
-
20
- export type ListenerType =
21
- | "numContainers"
22
- | "numContainersPooled"
23
- | `containerItemKey${number}`
24
- | `containerItemData${number}`
25
- | `containerPosition${number}`
26
- | `containerColumn${number}`
27
- | "containersDidLayout"
28
- | "extraData"
29
- | "numColumns"
30
- | "lastItemKeys"
31
- | "totalSize"
32
- | "alignItemsPaddingTop"
33
- | "stylePaddingTop"
34
- | "scrollAdjust"
35
- | "scrollAdjustUserOffset"
36
- | "headerSize"
37
- | "footerSize"
38
- | "maintainVisibleContentPosition"
39
- | "debugRawScroll"
40
- | "debugComputedScroll"
41
- | "otherAxisSize"
42
- | "scrollSize";
43
-
44
- export type ListenerTypeValueMap = {
45
- numContainers: number;
46
- numContainersPooled: number;
47
- containersDidLayout: boolean;
48
- extraData: any;
49
- numColumns: number;
50
- lastItemKeys: string[];
51
- totalSize: number;
52
- alignItemsPaddingTop: number;
53
- stylePaddingTop: number;
54
- scrollAdjust: number;
55
- scrollAdjustUserOffset: number;
56
- headerSize: number;
57
- footerSize: number;
58
- maintainVisibleContentPosition: boolean;
59
- debugRawScroll: number;
60
- debugComputedScroll: number;
61
- otherAxisSize: number;
62
- scrollSize: { width: number; height: number };
63
- } & {
64
- [K in ListenerType as K extends `containerItemKey${number}` ? K : never]: string;
65
- } & {
66
- [K in ListenerType as K extends `containerItemData${number}` ? K : never]: any;
67
- } & {
68
- [K in ListenerType as K extends `containerPosition${number}` ? K : never]: number;
69
- } & {
70
- [K in ListenerType as K extends `containerColumn${number}` ? K : never]: number;
71
- };
72
-
73
- export interface StateContext {
74
- listeners: Map<ListenerType, Set<(value: any) => void>>;
75
- values: Map<ListenerType, any>;
76
- mapViewabilityCallbacks: Map<string, ViewabilityCallback>;
77
- mapViewabilityValues: Map<string, ViewToken>;
78
- mapViewabilityAmountCallbacks: Map<number, ViewabilityAmountCallback>;
79
- mapViewabilityAmountValues: Map<number, ViewAmountToken>;
80
- columnWrapperStyle: ColumnWrapperStyle | undefined;
81
- viewRefs: Map<number, React.RefObject<View>>;
82
- }
83
-
84
- const ContextState = React.createContext<StateContext | null>(null);
85
-
86
- export function StateProvider({ children }: { children: React.ReactNode }) {
87
- const [value] = React.useState<StateContext>(() => ({
88
- listeners: new Map(),
89
- values: new Map<ListenerType, any>([
90
- ["alignItemsPaddingTop", 0],
91
- ["stylePaddingTop", 0],
92
- ["headerSize", 0],
93
- ]),
94
- mapViewabilityCallbacks: new Map<string, ViewabilityCallback>(),
95
- mapViewabilityValues: new Map<string, ViewToken>(),
96
- mapViewabilityAmountCallbacks: new Map<number, ViewabilityAmountCallback>(),
97
- mapViewabilityAmountValues: new Map<number, ViewAmountToken>(),
98
- columnWrapperStyle: undefined,
99
- viewRefs: new Map<number, React.RefObject<View>>(),
100
- }));
101
- return <ContextState.Provider value={value}>{children}</ContextState.Provider>;
102
- }
103
-
104
- export function useStateContext() {
105
- return React.useContext(ContextState)!;
106
- }
107
-
108
- function createSelectorFunctionsArr(ctx: StateContext, signalNames: ListenerType[]) {
109
- let lastValues: any[] = [];
110
- let lastSignalValues: any[] = [];
111
-
112
- return {
113
- subscribe: (cb: (value: any) => void) => {
114
- const listeners: (() => void)[] = [];
115
- for (const signalName of signalNames) {
116
- listeners.push(listen$(ctx, signalName, cb));
117
- }
118
- return () => {
119
- for (const listener of listeners) {
120
- listener();
121
- }
122
- };
123
- },
124
- get: () => {
125
- const currentValues: any[] = [];
126
- let hasChanged = false;
127
-
128
- for (let i = 0; i < signalNames.length; i++) {
129
- const value = peek$(ctx, signalNames[i]);
130
- currentValues.push(value);
131
-
132
- // Check if this value has changed from last time
133
- if (value !== lastSignalValues[i]) {
134
- hasChanged = true;
135
- }
136
- }
137
-
138
- // Update our cached signal values regardless
139
- lastSignalValues = currentValues;
140
-
141
- // Only create a new array reference if something changed
142
- if (hasChanged) {
143
- lastValues = currentValues;
144
- }
145
-
146
- return lastValues;
147
- },
148
- };
149
- }
150
-
151
- export function listen$<T extends ListenerType>(
152
- ctx: StateContext,
153
- signalName: T,
154
- cb: (value: ListenerTypeValueMap[T]) => void,
155
- ): () => void {
156
- const { listeners } = ctx;
157
- let setListeners = listeners.get(signalName);
158
- if (!setListeners) {
159
- setListeners = new Set();
160
- listeners.set(signalName, setListeners);
161
- }
162
- setListeners!.add(cb);
163
-
164
- return () => setListeners!.delete(cb);
165
- }
166
-
167
- // Function to get value based on ListenerType without requiring generic type
168
- export function peek$<T extends ListenerType>(ctx: StateContext, signalName: T): ListenerTypeValueMap[T] {
169
- const { values } = ctx;
170
- return values.get(signalName);
171
- }
172
-
173
- export function set$<T extends ListenerType>(
174
- ctx: StateContext,
175
- signalName: T,
176
- value: ListenerTypeValueMap[T] | undefined,
177
- ) {
178
- const { listeners, values } = ctx;
179
- if (values.get(signalName) !== value) {
180
- values.set(signalName, value);
181
- const setListeners = listeners.get(signalName);
182
- if (setListeners) {
183
- for (const listener of setListeners) {
184
- listener(value);
185
- }
186
- }
187
- }
188
- }
189
-
190
- export function getContentSize(ctx: StateContext) {
191
- const { values } = ctx;
192
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
193
- const headerSize = values.get("headerSize") || 0;
194
- const footerSize = values.get("footerSize") || 0;
195
- const totalSize = values.get("totalSize") || 0;
196
- return headerSize + footerSize + totalSize + stylePaddingTop;
197
- }
198
-
199
- export function useArr$<T extends ListenerType>(signalNames: [T]): [ListenerTypeValueMap[T]];
200
- export function useArr$<T1 extends ListenerType, T2 extends ListenerType>(
201
- signalNames: [T1, T2],
202
- ): [ListenerTypeValueMap[T1], ListenerTypeValueMap[T2]];
203
- export function useArr$<T1 extends ListenerType, T2 extends ListenerType, T3 extends ListenerType>(
204
- signalNames: [T1, T2, T3],
205
- ): [ListenerTypeValueMap[T1], ListenerTypeValueMap[T2], ListenerTypeValueMap[T3]];
206
- export function useArr$<
207
- T1 extends ListenerType,
208
- T2 extends ListenerType,
209
- T3 extends ListenerType,
210
- T4 extends ListenerType,
211
- >(
212
- signalNames: [T1, T2, T3, T4],
213
- ): [ListenerTypeValueMap[T1], ListenerTypeValueMap[T2], ListenerTypeValueMap[T3], ListenerTypeValueMap[T4]];
214
- export function useArr$<
215
- T1 extends ListenerType,
216
- T2 extends ListenerType,
217
- T3 extends ListenerType,
218
- T4 extends ListenerType,
219
- T5 extends ListenerType,
220
- >(
221
- signalNames: [T1, T2, T3, T4, T5],
222
- ): [
223
- ListenerTypeValueMap[T1],
224
- ListenerTypeValueMap[T2],
225
- ListenerTypeValueMap[T3],
226
- ListenerTypeValueMap[T4],
227
- ListenerTypeValueMap[T5],
228
- ];
229
- export function useArr$<
230
- T1 extends ListenerType,
231
- T2 extends ListenerType,
232
- T3 extends ListenerType,
233
- T4 extends ListenerType,
234
- T5 extends ListenerType,
235
- T6 extends ListenerType,
236
- >(
237
- signalNames: [T1, T2, T3, T4, T5, T6],
238
- ): [
239
- ListenerTypeValueMap[T1],
240
- ListenerTypeValueMap[T2],
241
- ListenerTypeValueMap[T3],
242
- ListenerTypeValueMap[T4],
243
- ListenerTypeValueMap[T5],
244
- ListenerTypeValueMap[T6],
245
- ];
246
- export function useArr$<
247
- T1 extends ListenerType,
248
- T2 extends ListenerType,
249
- T3 extends ListenerType,
250
- T4 extends ListenerType,
251
- T5 extends ListenerType,
252
- T6 extends ListenerType,
253
- T7 extends ListenerType,
254
- >(
255
- signalNames: [T1, T2, T3, T4, T5, T6, T7],
256
- ): [
257
- ListenerTypeValueMap[T1],
258
- ListenerTypeValueMap[T2],
259
- ListenerTypeValueMap[T3],
260
- ListenerTypeValueMap[T4],
261
- ListenerTypeValueMap[T5],
262
- ListenerTypeValueMap[T6],
263
- ListenerTypeValueMap[T7],
264
- ];
265
- export function useArr$<
266
- T1 extends ListenerType,
267
- T2 extends ListenerType,
268
- T3 extends ListenerType,
269
- T4 extends ListenerType,
270
- T5 extends ListenerType,
271
- T6 extends ListenerType,
272
- T7 extends ListenerType,
273
- T8 extends ListenerType,
274
- >(
275
- signalNames: [T1, T2, T3, T4, T5, T6, T7, T8],
276
- ): [
277
- ListenerTypeValueMap[T1],
278
- ListenerTypeValueMap[T2],
279
- ListenerTypeValueMap[T3],
280
- ListenerTypeValueMap[T4],
281
- ListenerTypeValueMap[T5],
282
- ListenerTypeValueMap[T6],
283
- ListenerTypeValueMap[T7],
284
- ListenerTypeValueMap[T8],
285
- ];
286
- export function useArr$<T extends ListenerType>(signalNames: T[]): ListenerTypeValueMap[T][] {
287
- const ctx = React.useContext(ContextState)!;
288
- const { subscribe, get } = React.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
289
- const value = useSyncExternalStore(subscribe, get);
290
-
291
- return value;
292
- }
293
- export function useSelector$<T extends ListenerType, T2>(
294
- signalName: T,
295
- selector: (value: ListenerTypeValueMap[T]) => T2,
296
- ): T2 {
297
- const ctx = React.useContext(ContextState)!;
298
- const { subscribe, get } = React.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
299
-
300
- // Return a selected value based on the signal name, so it only re-renders when the selected value changes
301
- const value = useSyncExternalStore(subscribe, () => selector(get()[0]));
302
-
303
- return value;
304
- }