@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.
- package/{dist/index.d.mts → index.d.mts} +28 -21
- package/{dist/index.d.ts → index.d.ts} +28 -21
- package/{dist/index.js → index.js} +808 -760
- package/{dist/index.mjs → index.mjs} +808 -760
- package/{dist → integrations}/animated.d.mts +2 -1
- package/{dist → integrations}/animated.d.ts +2 -1
- package/{dist → integrations}/animated.js +2 -2
- package/{dist → integrations}/animated.mjs +2 -2
- package/{dist → integrations}/keyboard-controller.d.mts +4 -0
- package/{dist → integrations}/keyboard-controller.d.ts +4 -0
- package/{dist → integrations}/keyboard-controller.js +4 -4
- package/{dist → integrations}/keyboard-controller.mjs +4 -4
- package/{dist → integrations}/reanimated.js +7 -7
- package/{dist → integrations}/reanimated.mjs +7 -7
- package/package.json +34 -88
- package/.claude/settings.local.json +0 -8
- package/.cursor/rules/changelog.mdc +0 -60
- package/.github/FUNDING.yml +0 -15
- package/.gitignore +0 -5
- package/.prettierrc.json +0 -5
- package/.vscode/settings.json +0 -14
- package/CLAUDE.md +0 -126
- package/biome.json +0 -46
- package/bun.lock +0 -1289
- package/bunfig.toml +0 -2
- package/dist/CHANGELOG.md +0 -119
- package/dist/LICENSE +0 -21
- package/dist/README.md +0 -139
- package/dist/package.json +0 -35
- package/example/README.md +0 -40
- package/example/api/data/genres.json +0 -23
- package/example/api/data/playlist/10402-10749.json +0 -1
- package/example/api/data/playlist/10402-10770.json +0 -1
- package/example/api/data/playlist/10402-37.json +0 -1
- package/example/api/data/playlist/10749-10752.json +0 -1
- package/example/api/data/playlist/10749-10770.json +0 -1
- package/example/api/data/playlist/10749-37.json +0 -1
- package/example/api/data/playlist/10749-878.json +0 -1
- package/example/api/data/playlist/10751-10402.json +0 -1
- package/example/api/data/playlist/10751-10752.json +0 -1
- package/example/api/data/playlist/10751-37.json +0 -1
- package/example/api/data/playlist/10751-53.json +0 -1
- package/example/api/data/playlist/10751-878.json +0 -1
- package/example/api/data/playlist/10751-9648.json +0 -1
- package/example/api/data/playlist/10752-37.json +0 -1
- package/example/api/data/playlist/12-10402.json +0 -1
- package/example/api/data/playlist/12-10749.json +0 -1
- package/example/api/data/playlist/12-18.json +0 -1
- package/example/api/data/playlist/12-27.json +0 -1
- package/example/api/data/playlist/12-35.json +0 -1
- package/example/api/data/playlist/14-36.json +0 -1
- package/example/api/data/playlist/14-878.json +0 -1
- package/example/api/data/playlist/16-10751.json +0 -1
- package/example/api/data/playlist/16-10770.json +0 -1
- package/example/api/data/playlist/16-35.json +0 -1
- package/example/api/data/playlist/16-36.json +0 -1
- package/example/api/data/playlist/16-53.json +0 -1
- package/example/api/data/playlist/18-10751.json +0 -1
- package/example/api/data/playlist/18-10752.json +0 -1
- package/example/api/data/playlist/18-37.json +0 -1
- package/example/api/data/playlist/18-53.json +0 -1
- package/example/api/data/playlist/18-878.json +0 -1
- package/example/api/data/playlist/27-10749.json +0 -1
- package/example/api/data/playlist/27-10770.json +0 -1
- package/example/api/data/playlist/28-10749.json +0 -1
- package/example/api/data/playlist/28-10751.json +0 -1
- package/example/api/data/playlist/28-10770.json +0 -1
- package/example/api/data/playlist/28-16.json +0 -1
- package/example/api/data/playlist/28-18.json +0 -1
- package/example/api/data/playlist/28-36.json +0 -1
- package/example/api/data/playlist/28-37.json +0 -1
- package/example/api/data/playlist/28-53.json +0 -1
- package/example/api/data/playlist/28-80.json +0 -1
- package/example/api/data/playlist/28-99.json +0 -1
- package/example/api/data/playlist/35-10749.json +0 -1
- package/example/api/data/playlist/35-10751.json +0 -1
- package/example/api/data/playlist/35-10752.json +0 -1
- package/example/api/data/playlist/35-27.json +0 -1
- package/example/api/data/playlist/35-36.json +0 -1
- package/example/api/data/playlist/35-53.json +0 -1
- package/example/api/data/playlist/35-80.json +0 -1
- package/example/api/data/playlist/36-37.json +0 -1
- package/example/api/data/playlist/36-878.json +0 -1
- package/example/api/data/playlist/36-9648.json +0 -1
- package/example/api/data/playlist/53-10752.json +0 -1
- package/example/api/data/playlist/80-10770.json +0 -1
- package/example/api/data/playlist/80-14.json +0 -1
- package/example/api/data/playlist/80-18.json +0 -1
- package/example/api/data/playlist/80-37.json +0 -1
- package/example/api/data/playlist/878-37.json +0 -1
- package/example/api/data/playlist/9648-10770.json +0 -1
- package/example/api/data/playlist/9648-37.json +0 -1
- package/example/api/data/playlist/9648-53.json +0 -1
- package/example/api/data/playlist/9648-878.json +0 -1
- package/example/api/data/playlist/99-10749.json +0 -1
- package/example/api/data/playlist/99-14.json +0 -1
- package/example/api/data/playlist/99-18.json +0 -1
- package/example/api/data/playlist/99-27.json +0 -1
- package/example/api/data/playlist/99-53.json +0 -1
- package/example/api/data/playlist/99-9648.json +0 -1
- package/example/api/data/playlist/index.ts +0 -73
- package/example/api/data/rows.json +0 -1
- package/example/api/index.ts +0 -36
- package/example/app/(tabs)/_layout.tsx +0 -60
- package/example/app/(tabs)/cards.tsx +0 -81
- package/example/app/(tabs)/index.tsx +0 -205
- package/example/app/(tabs)/moviesL.tsx +0 -7
- package/example/app/(tabs)/moviesLR.tsx +0 -7
- package/example/app/+not-found.tsx +0 -32
- package/example/app/_layout.tsx +0 -34
- package/example/app/accurate-scrollto/index.tsx +0 -125
- package/example/app/accurate-scrollto-2/index.tsx +0 -52
- package/example/app/accurate-scrollto-huge/index.tsx +0 -128
- package/example/app/add-to-end/index.tsx +0 -82
- package/example/app/ai-chat/index.tsx +0 -236
- package/example/app/bidirectional-infinite-list/index.tsx +0 -133
- package/example/app/cards-columns/index.tsx +0 -37
- package/example/app/cards-flashlist/index.tsx +0 -122
- package/example/app/cards-flatlist/index.tsx +0 -94
- package/example/app/cards-no-recycle/index.tsx +0 -110
- package/example/app/cards-renderItem.tsx +0 -354
- package/example/app/chat-example/index.tsx +0 -167
- package/example/app/chat-infinite/index.tsx +0 -239
- package/example/app/chat-keyboard/index.tsx +0 -248
- package/example/app/chat-resize-outer/index.tsx +0 -247
- package/example/app/columns/index.tsx +0 -78
- package/example/app/countries/index.tsx +0 -182
- package/example/app/countries-flashlist/index.tsx +0 -163
- package/example/app/countries-reorder/index.tsx +0 -187
- package/example/app/extra-data/index.tsx +0 -86
- package/example/app/filter-elements/filter-data-provider.tsx +0 -55
- package/example/app/filter-elements/index.tsx +0 -118
- package/example/app/initial-scroll-index/index.tsx +0 -106
- package/example/app/initial-scroll-index/renderFixedItem.tsx +0 -215
- package/example/app/initial-scroll-index-free-height/index.tsx +0 -70
- package/example/app/initial-scroll-index-keyed/index.tsx +0 -62
- package/example/app/lazy-list/index.tsx +0 -123
- package/example/app/movies-flashlist/index.tsx +0 -7
- package/example/app/mutable-cells/index.tsx +0 -104
- package/example/app/video-feed/index.tsx +0 -119
- package/example/app.config.js +0 -22
- package/example/app.json +0 -45
- package/example/assets/fonts/SpaceMono-Regular.ttf +0 -0
- package/example/assets/images/adaptive-icon.png +0 -0
- package/example/assets/images/favicon.png +0 -0
- package/example/assets/images/icon.png +0 -0
- package/example/assets/images/partial-react-logo.png +0 -0
- package/example/assets/images/react-logo.png +0 -0
- package/example/assets/images/react-logo@2x.png +0 -0
- package/example/assets/images/react-logo@3x.png +0 -0
- package/example/assets/images/splash-icon.png +0 -0
- package/example/autoscroll.sh +0 -101
- package/example/bun.lock +0 -2266
- package/example/bunfig.toml +0 -2
- package/example/components/Breathe.tsx +0 -54
- package/example/components/Circle.tsx +0 -69
- package/example/components/Collapsible.tsx +0 -44
- package/example/components/ExternalLink.tsx +0 -24
- package/example/components/HapticTab.tsx +0 -18
- package/example/components/HelloWave.tsx +0 -37
- package/example/components/Movies.tsx +0 -179
- package/example/components/ParallaxScrollView.tsx +0 -81
- package/example/components/ThemedText.tsx +0 -60
- package/example/components/ThemedView.tsx +0 -14
- package/example/components/__tests__/ThemedText-test.tsx +0 -10
- package/example/components/__tests__/__snapshots__/ThemedText-test.tsx.snap +0 -24
- package/example/components/ui/IconSymbol.ios.tsx +0 -32
- package/example/components/ui/IconSymbol.tsx +0 -43
- package/example/components/ui/TabBarBackground.ios.tsx +0 -22
- package/example/components/ui/TabBarBackground.tsx +0 -6
- package/example/constants/Colors.ts +0 -26
- package/example/constants/constants.ts +0 -5
- package/example/constants/useScrollTest.ts +0 -19
- package/example/hooks/useColorScheme.ts +0 -1
- package/example/hooks/useColorScheme.web.ts +0 -8
- package/example/hooks/useThemeColor.ts +0 -22
- package/example/ios/.xcode.env +0 -11
- package/example/ios/Podfile +0 -64
- package/example/ios/Podfile.lock +0 -2767
- package/example/ios/Podfile.properties.json +0 -5
- package/example/ios/listtest/AppDelegate.swift +0 -70
- package/example/ios/listtest/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
- package/example/ios/listtest/Images.xcassets/AppIcon.appiconset/Contents.json +0 -14
- package/example/ios/listtest/Images.xcassets/Contents.json +0 -6
- package/example/ios/listtest/Images.xcassets/SplashScreenBackground.colorset/Contents.json +0 -20
- package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/Contents.json +0 -23
- package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image.png +0 -0
- package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image@2x.png +0 -0
- package/example/ios/listtest/Images.xcassets/SplashScreenLogo.imageset/image@3x.png +0 -0
- package/example/ios/listtest/Info.plist +0 -85
- package/example/ios/listtest/PrivacyInfo.xcprivacy +0 -48
- package/example/ios/listtest/SplashScreen.storyboard +0 -42
- package/example/ios/listtest/Supporting/Expo.plist +0 -12
- package/example/ios/listtest/listtest-Bridging-Header.h +0 -3
- package/example/ios/listtest/listtest.entitlements +0 -5
- package/example/ios/listtest.xcodeproj/project.pbxproj +0 -547
- package/example/ios/listtest.xcodeproj/xcshareddata/xcschemes/listtest.xcscheme +0 -88
- package/example/ios/listtest.xcworkspace/contents.xcworkspacedata +0 -10
- package/example/metro.config.js +0 -16
- package/example/package.json +0 -73
- package/example/scripts/reset-project.js +0 -84
- package/example/tsconfig.json +0 -26
- package/posttsup.ts +0 -24
- package/src/Container.tsx +0 -176
- package/src/Containers.tsx +0 -85
- package/src/ContextContainer.ts +0 -145
- package/src/DebugView.tsx +0 -83
- package/src/LazyLegendList.tsx +0 -41
- package/src/LeanView.tsx +0 -18
- package/src/LegendList.tsx +0 -558
- package/src/ListComponent.tsx +0 -191
- package/src/ScrollAdjust.tsx +0 -24
- package/src/ScrollAdjustHandler.ts +0 -26
- package/src/Separator.tsx +0 -14
- package/src/animated.tsx +0 -6
- package/src/calculateItemsInView.ts +0 -363
- package/src/calculateOffsetForIndex.ts +0 -23
- package/src/calculateOffsetWithOffsetPosition.ts +0 -26
- package/src/checkAllSizesKnown.ts +0 -17
- package/src/checkAtBottom.ts +0 -36
- package/src/checkAtTop.ts +0 -27
- package/src/checkThreshold.ts +0 -30
- package/src/constants.ts +0 -11
- package/src/createColumnWrapperStyle.ts +0 -16
- package/src/doInitialAllocateContainers.ts +0 -40
- package/src/doMaintainScrollAtEnd.ts +0 -34
- package/src/findAvailableContainers.ts +0 -98
- package/src/finishScrollTo.ts +0 -8
- package/src/getId.ts +0 -21
- package/src/getItemSize.ts +0 -52
- package/src/getRenderedItem.ts +0 -34
- package/src/getScrollVelocity.ts +0 -47
- package/src/handleLayout.ts +0 -70
- package/src/helpers.ts +0 -39
- package/src/index.ts +0 -11
- package/src/keyboard-controller.tsx +0 -63
- package/src/onScroll.ts +0 -66
- package/src/prepareMVCP.ts +0 -50
- package/src/reanimated.tsx +0 -63
- package/src/requestAdjust.ts +0 -41
- package/src/scrollTo.ts +0 -40
- package/src/scrollToIndex.ts +0 -34
- package/src/setDidLayout.ts +0 -25
- package/src/setPaddingTop.ts +0 -28
- package/src/state.tsx +0 -304
- package/src/types.ts +0 -610
- package/src/updateAlignItemsPaddingTop.ts +0 -18
- package/src/updateAllPositions.ts +0 -130
- package/src/updateItemSize.ts +0 -203
- package/src/updateTotalSize.ts +0 -44
- package/src/useAnimatedValue.ts +0 -6
- package/src/useCombinedRef.ts +0 -22
- package/src/useInit.ts +0 -17
- package/src/useSyncLayout.tsx +0 -68
- package/src/useValue$.ts +0 -53
- package/src/viewability.ts +0 -279
- package/tsconfig.json +0 -59
- package/tsup.config.ts +0 -21
- package/{dist → integrations}/reanimated.d.mts +1 -1
- package/{dist → integrations}/reanimated.d.ts +1 -1
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { getId } from "./getId";
|
|
2
|
-
import { getItemSize } from "./getItemSize";
|
|
3
|
-
import { getScrollVelocity } from "./getScrollVelocity";
|
|
4
|
-
import { roundSize } from "./helpers";
|
|
5
|
-
import { type StateContext, peek$ } from "./state";
|
|
6
|
-
import type { InternalState } from "./types";
|
|
7
|
-
import { updateTotalSize } from "./updateTotalSize";
|
|
8
|
-
|
|
9
|
-
export function updateAllPositions(ctx: StateContext, state: InternalState, dataChanged?: boolean) {
|
|
10
|
-
const { averageSizes, columns, indexByKey, positions, firstFullyOnScreenIndex, idCache, sizesKnown } = state;
|
|
11
|
-
const data = state.props.data;
|
|
12
|
-
const numColumns = peek$(ctx, "numColumns");
|
|
13
|
-
const indexByKeyForChecking = __DEV__ ? new Map() : undefined;
|
|
14
|
-
const scrollVelocity = getScrollVelocity(state);
|
|
15
|
-
|
|
16
|
-
if (dataChanged) {
|
|
17
|
-
indexByKey.clear();
|
|
18
|
-
idCache.clear();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// TODO: Hook this up to actual item types later once we have item types
|
|
22
|
-
const itemType = "";
|
|
23
|
-
let averageSize = averageSizes[itemType]?.avg;
|
|
24
|
-
if (averageSize !== undefined) {
|
|
25
|
-
averageSize = roundSize(averageSize);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Check if we should use backwards optimization when scrolling up
|
|
29
|
-
const shouldUseBackwards =
|
|
30
|
-
!dataChanged && scrollVelocity < 0 && firstFullyOnScreenIndex > 5 && firstFullyOnScreenIndex < data!.length;
|
|
31
|
-
|
|
32
|
-
if (shouldUseBackwards && firstFullyOnScreenIndex !== undefined) {
|
|
33
|
-
// Get the current position of firstFullyOnScreenIndex as anchor
|
|
34
|
-
const anchorId = getId(state, firstFullyOnScreenIndex)!;
|
|
35
|
-
const anchorPosition = positions.get(anchorId);
|
|
36
|
-
|
|
37
|
-
// If we don't have the anchor position, fall back to regular behavior
|
|
38
|
-
if (anchorPosition !== undefined) {
|
|
39
|
-
// Start from the anchor and go backwards
|
|
40
|
-
let currentRowTop = anchorPosition;
|
|
41
|
-
let maxSizeInRow = 0;
|
|
42
|
-
let bailout = false;
|
|
43
|
-
|
|
44
|
-
// Process items backwards from firstFullyOnScreenIndex - 1 to 0
|
|
45
|
-
for (let i = firstFullyOnScreenIndex - 1; i >= 0; i--) {
|
|
46
|
-
const id = idCache.get(i) ?? getId(state, i)!;
|
|
47
|
-
const size = sizesKnown.get(id) ?? getItemSize(state, id, i, data[i], averageSize);
|
|
48
|
-
const itemColumn = columns.get(id)!;
|
|
49
|
-
|
|
50
|
-
maxSizeInRow = Math.max(maxSizeInRow, size);
|
|
51
|
-
|
|
52
|
-
// When we reach column 1, we're at the start of a new row going backwards
|
|
53
|
-
if (itemColumn === 1) {
|
|
54
|
-
currentRowTop -= maxSizeInRow;
|
|
55
|
-
maxSizeInRow = 0;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Check if position goes too low - bail if so
|
|
59
|
-
if (currentRowTop < -2000) {
|
|
60
|
-
bailout = true;
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Update position for this item (columns and indexByKey already set)
|
|
65
|
-
positions.set(id, currentRowTop);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!bailout) {
|
|
69
|
-
// We successfully processed backwards, we're done
|
|
70
|
-
updateTotalSize(ctx, state);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Regular ascending behavior (either not scrolling up or bailed out)
|
|
77
|
-
let currentRowTop = 0;
|
|
78
|
-
let column = 1;
|
|
79
|
-
let maxSizeInRow = 0;
|
|
80
|
-
|
|
81
|
-
const hasColumns = numColumns > 1;
|
|
82
|
-
const needsIndexByKey = dataChanged || indexByKey.size === 0;
|
|
83
|
-
|
|
84
|
-
// Note that this loop is micro-optimized because it's a hot path
|
|
85
|
-
const dataLength = data!.length;
|
|
86
|
-
for (let i = 0; i < dataLength; i++) {
|
|
87
|
-
// Inline the map get calls to avoid the overhead of the function call
|
|
88
|
-
const id = idCache.get(i) ?? getId(state, i)!;
|
|
89
|
-
const size = sizesKnown.get(id) ?? getItemSize(state, id, i, data[i], averageSize);
|
|
90
|
-
|
|
91
|
-
// Set index mapping for this item
|
|
92
|
-
if (__DEV__ && needsIndexByKey) {
|
|
93
|
-
if (indexByKeyForChecking!.has(id)) {
|
|
94
|
-
console.error(
|
|
95
|
-
`[legend-list] Error: Detected overlapping key (${id}) which causes missing items and gaps and other terrrible things. Check that keyExtractor returns unique values.`,
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
indexByKeyForChecking!.set(id, i);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Set position for this item
|
|
102
|
-
positions.set(id, currentRowTop);
|
|
103
|
-
|
|
104
|
-
// Update indexByKey if needed
|
|
105
|
-
if (needsIndexByKey) {
|
|
106
|
-
indexByKey.set(id, i);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Set column for this item
|
|
110
|
-
columns.set(id, column);
|
|
111
|
-
|
|
112
|
-
if (hasColumns) {
|
|
113
|
-
if (size > maxSizeInRow) {
|
|
114
|
-
maxSizeInRow = size;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
column++;
|
|
118
|
-
if (column > numColumns) {
|
|
119
|
-
// Move to next row
|
|
120
|
-
currentRowTop += maxSizeInRow;
|
|
121
|
-
column = 1;
|
|
122
|
-
maxSizeInRow = 0;
|
|
123
|
-
}
|
|
124
|
-
} else {
|
|
125
|
-
currentRowTop += size;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
updateTotalSize(ctx, state);
|
|
130
|
-
}
|
package/src/updateItemSize.ts
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import type { LayoutRectangle } from "react-native";
|
|
2
|
-
import { calculateItemsInView } from "./calculateItemsInView";
|
|
3
|
-
import { checkAllSizesKnown } from "./checkAllSizesKnown";
|
|
4
|
-
import { IsNewArchitecture } from "./constants";
|
|
5
|
-
import { doMaintainScrollAtEnd } from "./doMaintainScrollAtEnd";
|
|
6
|
-
import { getItemSize } from "./getItemSize";
|
|
7
|
-
import { requestAdjust } from "./requestAdjust";
|
|
8
|
-
import { type StateContext, peek$, set$ } from "./state";
|
|
9
|
-
import type { InternalState, MaintainScrollAtEndOptions } from "./types";
|
|
10
|
-
|
|
11
|
-
export function updateItemSizes(
|
|
12
|
-
ctx: StateContext,
|
|
13
|
-
state: InternalState,
|
|
14
|
-
itemUpdates: { itemKey: string; sizeObj: { width: number; height: number } }[],
|
|
15
|
-
) {
|
|
16
|
-
const {
|
|
17
|
-
props: {
|
|
18
|
-
horizontal,
|
|
19
|
-
maintainVisibleContentPosition,
|
|
20
|
-
suggestEstimatedItemSize,
|
|
21
|
-
onItemSizeChanged,
|
|
22
|
-
data,
|
|
23
|
-
maintainScrollAtEnd,
|
|
24
|
-
},
|
|
25
|
-
} = state;
|
|
26
|
-
|
|
27
|
-
if (!data) return;
|
|
28
|
-
|
|
29
|
-
let needsRecalculate = false;
|
|
30
|
-
let shouldMaintainScrollAtEnd = false;
|
|
31
|
-
let minIndexSizeChanged: number | undefined;
|
|
32
|
-
let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
|
|
33
|
-
|
|
34
|
-
for (const { itemKey, sizeObj } of itemUpdates) {
|
|
35
|
-
const index = state.indexByKey.get(itemKey)!;
|
|
36
|
-
const prevSizeKnown = state.sizesKnown.get(itemKey);
|
|
37
|
-
|
|
38
|
-
const diff = updateOneItemSize(state, itemKey, sizeObj);
|
|
39
|
-
const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
|
|
40
|
-
|
|
41
|
-
if (diff !== 0) {
|
|
42
|
-
minIndexSizeChanged = minIndexSizeChanged !== undefined ? Math.min(minIndexSizeChanged, index) : index;
|
|
43
|
-
|
|
44
|
-
// Handle scrolling adjustments
|
|
45
|
-
if (
|
|
46
|
-
state.scrollingTo?.viewPosition &&
|
|
47
|
-
maintainVisibleContentPosition &&
|
|
48
|
-
index === state.scrollingTo.index
|
|
49
|
-
) {
|
|
50
|
-
requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Check if item is in view
|
|
54
|
-
const { startBuffered, endBuffered } = state;
|
|
55
|
-
needsRecalculate ||= index >= startBuffered && index <= endBuffered;
|
|
56
|
-
if (!needsRecalculate) {
|
|
57
|
-
const numContainers = ctx.values.get("numContainers") as number;
|
|
58
|
-
for (let i = 0; i < numContainers; i++) {
|
|
59
|
-
if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
|
|
60
|
-
needsRecalculate = true;
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Handle other axis size
|
|
67
|
-
if (state.needsOtherAxisSize) {
|
|
68
|
-
const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
|
|
69
|
-
maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Check if we should maintain scroll at end
|
|
73
|
-
if (prevSizeKnown !== undefined && Math.abs(prevSizeKnown - size) > 5) {
|
|
74
|
-
shouldMaintainScrollAtEnd = true;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Call onItemSizeChanged callback
|
|
78
|
-
onItemSizeChanged?.({
|
|
79
|
-
size,
|
|
80
|
-
previous: size - diff,
|
|
81
|
-
index,
|
|
82
|
-
itemKey,
|
|
83
|
-
itemData: state.props.data[index],
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Update state with minimum changed index
|
|
89
|
-
if (minIndexSizeChanged !== undefined) {
|
|
90
|
-
state.minIndexSizeChanged =
|
|
91
|
-
state.minIndexSizeChanged !== undefined
|
|
92
|
-
? Math.min(state.minIndexSizeChanged, minIndexSizeChanged)
|
|
93
|
-
: minIndexSizeChanged;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Handle dev warning about estimated size
|
|
97
|
-
if (__DEV__ && suggestEstimatedItemSize && minIndexSizeChanged !== undefined) {
|
|
98
|
-
if (state.timeoutSizeMessage) clearTimeout(state.timeoutSizeMessage);
|
|
99
|
-
state.timeoutSizeMessage = setTimeout(() => {
|
|
100
|
-
state.timeoutSizeMessage = undefined;
|
|
101
|
-
const num = state.sizesKnown.size;
|
|
102
|
-
const avg = state.averageSizes[""]?.avg;
|
|
103
|
-
console.warn(
|
|
104
|
-
`[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`,
|
|
105
|
-
);
|
|
106
|
-
}, 1000);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const cur = peek$(ctx, "otherAxisSize");
|
|
110
|
-
if (!cur || maxOtherAxisSize > cur) {
|
|
111
|
-
set$(ctx, "otherAxisSize", maxOtherAxisSize);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const containersDidLayout = peek$(ctx, "containersDidLayout");
|
|
115
|
-
|
|
116
|
-
if (containersDidLayout || checkAllSizesKnown(state)) {
|
|
117
|
-
if (needsRecalculate) {
|
|
118
|
-
state.scrollForNextCalculateItemsInView = undefined;
|
|
119
|
-
|
|
120
|
-
calculateItemsInView(ctx, state, { doMVCP: true });
|
|
121
|
-
}
|
|
122
|
-
if (shouldMaintainScrollAtEnd) {
|
|
123
|
-
if (maintainScrollAtEnd === true || (maintainScrollAtEnd as MaintainScrollAtEndOptions).onItemLayout) {
|
|
124
|
-
doMaintainScrollAtEnd(ctx, state, false);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function updateItemSize(
|
|
131
|
-
ctx: StateContext,
|
|
132
|
-
state: InternalState,
|
|
133
|
-
itemKey: string,
|
|
134
|
-
sizeObj: { width: number; height: number },
|
|
135
|
-
) {
|
|
136
|
-
if (IsNewArchitecture) {
|
|
137
|
-
const { sizesKnown } = state;
|
|
138
|
-
const numContainers = ctx.values.get("numContainers") as number;
|
|
139
|
-
const changes: { itemKey: string; sizeObj: { width: number; height: number } }[] = [];
|
|
140
|
-
|
|
141
|
-
// Run through all containers and if we don't already have a known size then measure the item
|
|
142
|
-
// This is useful because when multiple items render in one frame, the first container fires a
|
|
143
|
-
// useLayoutEffect and we can measure all containers before their useLayoutEffects fire after a delay.
|
|
144
|
-
// This lets use fix any gaps/overlaps that might be visible before the useLayoutEffects fire for each container.
|
|
145
|
-
for (let i = 0; i < numContainers; i++) {
|
|
146
|
-
const containerItemKey = peek$(ctx, `containerItemKey${i}`);
|
|
147
|
-
if (itemKey === containerItemKey) {
|
|
148
|
-
// If it's this item just use the param
|
|
149
|
-
changes.push({ itemKey, sizeObj });
|
|
150
|
-
} else if (!sizesKnown.has(containerItemKey) && containerItemKey !== undefined) {
|
|
151
|
-
const containerRef = ctx.viewRefs.get(i);
|
|
152
|
-
if (containerRef?.current) {
|
|
153
|
-
let measured: LayoutRectangle;
|
|
154
|
-
containerRef.current.measure((x, y, width, height) => {
|
|
155
|
-
measured = { x, y, width, height };
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
if (measured!) {
|
|
159
|
-
changes.push({ itemKey: containerItemKey, sizeObj: measured });
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (changes.length > 0) {
|
|
166
|
-
updateItemSizes(ctx, state, changes);
|
|
167
|
-
}
|
|
168
|
-
} else {
|
|
169
|
-
updateItemSizes(ctx, state, [{ itemKey, sizeObj }]);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function updateOneItemSize(state: InternalState, itemKey: string, sizeObj: { width: number; height: number }) {
|
|
174
|
-
const {
|
|
175
|
-
sizes,
|
|
176
|
-
indexByKey,
|
|
177
|
-
sizesKnown,
|
|
178
|
-
averageSizes,
|
|
179
|
-
props: { data, horizontal },
|
|
180
|
-
} = state;
|
|
181
|
-
if (!data) return 0;
|
|
182
|
-
|
|
183
|
-
const index = indexByKey.get(itemKey)!;
|
|
184
|
-
const prevSize = getItemSize(state, itemKey, index, data as any);
|
|
185
|
-
const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
|
|
186
|
-
|
|
187
|
-
sizesKnown.set(itemKey, size);
|
|
188
|
-
|
|
189
|
-
// Update averages
|
|
190
|
-
const itemType = "";
|
|
191
|
-
let averages = averageSizes[itemType];
|
|
192
|
-
if (!averages) {
|
|
193
|
-
averages = averageSizes[itemType] = { num: 0, avg: 0 };
|
|
194
|
-
}
|
|
195
|
-
averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
|
|
196
|
-
averages.num++;
|
|
197
|
-
|
|
198
|
-
if (!prevSize || Math.abs(prevSize - size) > 0.1) {
|
|
199
|
-
sizes.set(itemKey, size);
|
|
200
|
-
return size - prevSize;
|
|
201
|
-
}
|
|
202
|
-
return 0;
|
|
203
|
-
}
|
package/src/updateTotalSize.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { getId } from "./getId";
|
|
2
|
-
import { getItemSize } from "./getItemSize";
|
|
3
|
-
import { type StateContext, set$ } from "./state";
|
|
4
|
-
import type { InternalState } from "./types";
|
|
5
|
-
import { updateAlignItemsPaddingTop } from "./updateAlignItemsPaddingTop";
|
|
6
|
-
|
|
7
|
-
export function updateTotalSize(ctx: StateContext, state: InternalState) {
|
|
8
|
-
const {
|
|
9
|
-
positions,
|
|
10
|
-
props: { data },
|
|
11
|
-
} = state;
|
|
12
|
-
|
|
13
|
-
if (data.length === 0) {
|
|
14
|
-
addTotalSize(ctx, state, null, 0);
|
|
15
|
-
} else {
|
|
16
|
-
const lastId = getId(state, data.length - 1);
|
|
17
|
-
if (lastId !== undefined) {
|
|
18
|
-
const lastPosition = positions.get(lastId);
|
|
19
|
-
if (lastPosition !== undefined) {
|
|
20
|
-
const lastSize = getItemSize(state, lastId, data.length - 1, data[data.length - 1]);
|
|
21
|
-
// TODO: This is likely incorrect for columns with rows having different heights, need to get max size of the last row
|
|
22
|
-
if (lastSize !== undefined) {
|
|
23
|
-
const totalSize = lastPosition + lastSize;
|
|
24
|
-
addTotalSize(ctx, state, null, totalSize);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function addTotalSize(ctx: StateContext, state: InternalState, key: string | null, add: number) {
|
|
32
|
-
const { alignItemsAtEnd } = state.props;
|
|
33
|
-
if (key === null) {
|
|
34
|
-
state.totalSize = add;
|
|
35
|
-
} else {
|
|
36
|
-
state.totalSize += add;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
set$(ctx, "totalSize", state.totalSize);
|
|
40
|
-
|
|
41
|
-
if (alignItemsAtEnd) {
|
|
42
|
-
updateAlignItemsPaddingTop(ctx, state);
|
|
43
|
-
}
|
|
44
|
-
}
|
package/src/useAnimatedValue.ts
DELETED
package/src/useCombinedRef.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { useCallback } from "react";
|
|
2
|
-
import { isFunction } from "./helpers";
|
|
3
|
-
|
|
4
|
-
type RefItem<T> = ((element: T | null) => void) | React.MutableRefObject<T | null> | null | undefined;
|
|
5
|
-
|
|
6
|
-
export const useCombinedRef = <T>(...refs: RefItem<T>[]) => {
|
|
7
|
-
const callback = useCallback((element: T | null) => {
|
|
8
|
-
for (const ref of refs) {
|
|
9
|
-
if (!ref) {
|
|
10
|
-
continue;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if (isFunction(ref)) {
|
|
14
|
-
ref(element);
|
|
15
|
-
} else {
|
|
16
|
-
ref.current = element;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}, refs);
|
|
20
|
-
|
|
21
|
-
return callback;
|
|
22
|
-
};
|
package/src/useInit.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { useRef } from "react";
|
|
2
|
-
|
|
3
|
-
const symbolFirst = Symbol();
|
|
4
|
-
// A hook that runs a callback only once during the first render.
|
|
5
|
-
// It should happen during render, not in useEffect, so that any setState calls during the callback
|
|
6
|
-
// will trigger a re-render immediately rather than waiting for a next render.
|
|
7
|
-
// See https://react.dev/reference/react/useState#storing-information-from-previous-renders
|
|
8
|
-
export function useInit<T>(cb: () => T) {
|
|
9
|
-
const refValue = useRef<T | typeof symbolFirst>(symbolFirst);
|
|
10
|
-
|
|
11
|
-
// Run inline during first render only
|
|
12
|
-
if (refValue.current === symbolFirst) {
|
|
13
|
-
refValue.current = cb();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return refValue.current;
|
|
17
|
-
}
|
package/src/useSyncLayout.tsx
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
-
import type { LayoutChangeEvent, LayoutRectangle, View } from "react-native";
|
|
3
|
-
|
|
4
|
-
export function useSyncLayoutState<T extends View = View>({
|
|
5
|
-
getValue,
|
|
6
|
-
debounce,
|
|
7
|
-
onChange: onChangeProp,
|
|
8
|
-
}: {
|
|
9
|
-
getValue: (rectangle: LayoutRectangle) => number;
|
|
10
|
-
debounce?: number | undefined;
|
|
11
|
-
onChange: (rectangle: LayoutRectangle, fromLayoutEffect: boolean) => void;
|
|
12
|
-
}) {
|
|
13
|
-
const debounceTimeoutRef = useRef<any>(null);
|
|
14
|
-
const [value, setValue] = useState(0);
|
|
15
|
-
|
|
16
|
-
const onChange = useCallback(
|
|
17
|
-
(rectangle: LayoutRectangle, fromLayoutEffect: boolean) => {
|
|
18
|
-
const height = getValue(rectangle);
|
|
19
|
-
|
|
20
|
-
if (debounce === undefined) {
|
|
21
|
-
setValue(height);
|
|
22
|
-
} else {
|
|
23
|
-
// Clear previous timeout if it exists
|
|
24
|
-
if (debounceTimeoutRef.current) {
|
|
25
|
-
clearTimeout(debounceTimeoutRef.current);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Debounce the setViewHeight call
|
|
29
|
-
debounceTimeoutRef.current = setTimeout(() => {
|
|
30
|
-
debounceTimeoutRef.current = null;
|
|
31
|
-
setValue(height);
|
|
32
|
-
}, debounce);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
onChangeProp?.(rectangle, fromLayoutEffect);
|
|
36
|
-
},
|
|
37
|
-
[getValue, debounce],
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const { onLayout, ref } = useSyncLayout<T>({ onChange });
|
|
41
|
-
|
|
42
|
-
return { value, onLayout, ref };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function useSyncLayout<T extends View = View>({
|
|
46
|
-
onChange,
|
|
47
|
-
}: {
|
|
48
|
-
onChange: (rectangle: LayoutRectangle, fromLayoutEffect: boolean) => void;
|
|
49
|
-
}) {
|
|
50
|
-
const ref = useRef<T | null>(null);
|
|
51
|
-
|
|
52
|
-
const onLayout = useCallback(
|
|
53
|
-
(event: LayoutChangeEvent) => {
|
|
54
|
-
onChange(event.nativeEvent.layout, false);
|
|
55
|
-
},
|
|
56
|
-
[onChange],
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
useLayoutEffect(() => {
|
|
60
|
-
if (ref.current) {
|
|
61
|
-
ref.current.measure((x, y, width, height) => {
|
|
62
|
-
onChange({ x, y, width, height }, true);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}, []);
|
|
66
|
-
|
|
67
|
-
return { onLayout, ref };
|
|
68
|
-
}
|
package/src/useValue$.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import { listen$, peek$, useStateContext } from "./state";
|
|
3
|
-
import type { ListenerType } from "./state";
|
|
4
|
-
import { useAnimatedValue } from "./useAnimatedValue";
|
|
5
|
-
|
|
6
|
-
export function useValue$(
|
|
7
|
-
key: ListenerType,
|
|
8
|
-
params?: {
|
|
9
|
-
getValue?: (value: number) => number;
|
|
10
|
-
delay?: number | ((value: number, prevValue: number | undefined) => number);
|
|
11
|
-
},
|
|
12
|
-
) {
|
|
13
|
-
const { getValue, delay } = params || {};
|
|
14
|
-
const ctx = useStateContext();
|
|
15
|
-
const animValue = useAnimatedValue((getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) ?? 0);
|
|
16
|
-
useMemo(() => {
|
|
17
|
-
let newValue: number | undefined = undefined;
|
|
18
|
-
let prevValue: number | undefined = undefined;
|
|
19
|
-
let didQueueTask = false;
|
|
20
|
-
listen$(ctx, key, (v) => {
|
|
21
|
-
newValue = getValue ? getValue(v) : v;
|
|
22
|
-
|
|
23
|
-
if (delay !== undefined) {
|
|
24
|
-
// Queue into a microtask because setting the value immediately was making the value
|
|
25
|
-
// not actually set. I think it has to do with setting during useLayoutEffect, but I'm not sure.
|
|
26
|
-
// This seems to be an optimization for setting totalSize because that can happen multiple times per frame
|
|
27
|
-
// so we skip setting the value immediately if using the microtask version.
|
|
28
|
-
const fn = () => {
|
|
29
|
-
didQueueTask = false;
|
|
30
|
-
if (newValue !== undefined) {
|
|
31
|
-
animValue.setValue(newValue!);
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
const delayValue = typeof delay === "function" ? delay(newValue!, prevValue) : delay;
|
|
35
|
-
prevValue = newValue;
|
|
36
|
-
if (!didQueueTask) {
|
|
37
|
-
didQueueTask = true;
|
|
38
|
-
if (delayValue === 0) {
|
|
39
|
-
queueMicrotask(fn);
|
|
40
|
-
} else {
|
|
41
|
-
// We're not clearing the timeout because we want it to run in the timeout from the first change
|
|
42
|
-
// but just not run multiple times.
|
|
43
|
-
setTimeout(fn, delayValue);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
animValue.setValue(newValue!);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}, []);
|
|
51
|
-
|
|
52
|
-
return animValue;
|
|
53
|
-
}
|