@shopify/flash-list 2.0.0-alpha.9 → 2.0.0-rc.10
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/README.md +37 -97
- package/android/src/main/kotlin/com/shopify/reactnative/flash_list/BlankAreaEvent.kt +2 -2
- package/dist/AnimatedFlashList.d.ts.map +1 -1
- package/dist/AnimatedFlashList.js +3 -3
- package/dist/AnimatedFlashList.js.map +1 -1
- package/dist/FlashList.d.ts +9 -0
- package/dist/FlashList.d.ts.map +1 -1
- package/dist/FlashList.js +20 -0
- package/dist/FlashList.js.map +1 -1
- package/dist/FlashListProps.d.ts +30 -10
- package/dist/FlashListProps.d.ts.map +1 -1
- package/dist/FlashListProps.js.map +1 -1
- package/dist/FlashListRef.d.ts +305 -0
- package/dist/FlashListRef.d.ts.map +1 -0
- package/dist/FlashListRef.js +3 -0
- package/dist/FlashListRef.js.map +1 -0
- package/dist/MasonryFlashList.js.map +1 -1
- package/dist/__tests__/RecyclerView.test.js +72 -28
- package/dist/__tests__/RecyclerView.test.js.map +1 -1
- package/dist/__tests__/RenderStackManager.test.d.ts +2 -0
- package/dist/__tests__/RenderStackManager.test.d.ts.map +1 -0
- package/dist/__tests__/RenderStackManager.test.js +485 -0
- package/dist/__tests__/RenderStackManager.test.js.map +1 -0
- package/dist/__tests__/helpers/createLayoutManager.d.ts.map +1 -1
- package/dist/__tests__/helpers/createLayoutManager.js +3 -4
- package/dist/__tests__/helpers/createLayoutManager.js.map +1 -1
- package/dist/__tests__/useUnmountAwareCallbacks.test.js +1 -1
- package/dist/__tests__/useUnmountAwareCallbacks.test.js.map +1 -1
- package/dist/benchmark/useBenchmark.js +0 -25
- package/dist/benchmark/useBenchmark.js.map +1 -1
- package/dist/benchmark/useFlatListBenchmark.js +8 -7
- package/dist/benchmark/useFlatListBenchmark.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/native/config/PlatformHelper.android.d.ts +1 -0
- package/dist/native/config/PlatformHelper.android.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.android.js +1 -0
- package/dist/native/config/PlatformHelper.android.js.map +1 -1
- package/dist/native/config/PlatformHelper.d.ts +1 -0
- package/dist/native/config/PlatformHelper.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.ios.d.ts +1 -0
- package/dist/native/config/PlatformHelper.ios.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.ios.js +1 -0
- package/dist/native/config/PlatformHelper.ios.js.map +1 -1
- package/dist/native/config/PlatformHelper.js +1 -0
- package/dist/native/config/PlatformHelper.js.map +1 -1
- package/dist/native/config/PlatformHelper.web.d.ts +1 -0
- package/dist/native/config/PlatformHelper.web.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.web.js +1 -0
- package/dist/native/config/PlatformHelper.web.js.map +1 -1
- package/dist/recyclerview/RecyclerView.d.ts +2 -1
- package/dist/recyclerview/RecyclerView.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerView.js +104 -57
- package/dist/recyclerview/RecyclerView.js.map +1 -1
- package/dist/recyclerview/RecyclerViewContextProvider.d.ts +41 -6
- package/dist/recyclerview/RecyclerViewContextProvider.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewContextProvider.js +4 -0
- package/dist/recyclerview/RecyclerViewContextProvider.js.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.d.ts +24 -7
- package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.js +119 -113
- package/dist/recyclerview/RecyclerViewManager.js.map +1 -1
- package/dist/recyclerview/RenderStackManager.d.ts +86 -0
- package/dist/recyclerview/RenderStackManager.d.ts.map +1 -0
- package/dist/recyclerview/RenderStackManager.js +343 -0
- package/dist/recyclerview/RenderStackManager.js.map +1 -0
- package/dist/recyclerview/ViewHolder.d.ts.map +1 -1
- package/dist/recyclerview/ViewHolder.js +5 -3
- package/dist/recyclerview/ViewHolder.js.map +1 -1
- package/dist/recyclerview/ViewHolderCollection.d.ts +9 -3
- package/dist/recyclerview/ViewHolderCollection.d.ts.map +1 -1
- package/dist/recyclerview/ViewHolderCollection.js +26 -9
- package/dist/recyclerview/ViewHolderCollection.js.map +1 -1
- package/dist/recyclerview/components/ScrollAnchor.d.ts +2 -2
- package/dist/recyclerview/components/ScrollAnchor.d.ts.map +1 -1
- package/dist/recyclerview/components/ScrollAnchor.js +9 -5
- package/dist/recyclerview/components/ScrollAnchor.js.map +1 -1
- package/dist/recyclerview/components/StickyHeaders.d.ts +1 -1
- package/dist/recyclerview/components/StickyHeaders.d.ts.map +1 -1
- package/dist/recyclerview/components/StickyHeaders.js +40 -33
- package/dist/recyclerview/components/StickyHeaders.js.map +1 -1
- package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts +45 -1
- package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts.map +1 -1
- package/dist/recyclerview/helpers/EngagedIndicesTracker.js +77 -20
- package/dist/recyclerview/helpers/EngagedIndicesTracker.js.map +1 -1
- package/dist/recyclerview/helpers/RenderTimeTracker.d.ts +11 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.d.ts.map +1 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.js +42 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.js.map +1 -0
- package/dist/recyclerview/helpers/VelocityTracker.d.ts +29 -0
- package/dist/recyclerview/helpers/VelocityTracker.d.ts.map +1 -0
- package/dist/recyclerview/helpers/VelocityTracker.js +70 -0
- package/dist/recyclerview/helpers/VelocityTracker.js.map +1 -0
- package/dist/recyclerview/hooks/useBoundDetection.d.ts +1 -2
- package/dist/recyclerview/hooks/useBoundDetection.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useBoundDetection.js +56 -22
- package/dist/recyclerview/hooks/useBoundDetection.js.map +1 -1
- package/dist/recyclerview/hooks/useLayoutState.d.ts +3 -1
- package/dist/recyclerview/hooks/useLayoutState.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useLayoutState.js +5 -3
- package/dist/recyclerview/hooks/useLayoutState.js.map +1 -1
- package/dist/recyclerview/hooks/useMappingHelper.d.ts +1 -1
- package/dist/recyclerview/hooks/useMappingHelper.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useMappingHelper.js +1 -1
- package/dist/recyclerview/hooks/useMappingHelper.js.map +1 -1
- package/dist/recyclerview/hooks/useOnLoad.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useOnLoad.js +4 -6
- package/dist/recyclerview/hooks/useOnLoad.js.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.d.ts +5 -49
- package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.js +315 -204
- package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts +2 -0
- package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.js +11 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.js.map +1 -1
- package/dist/recyclerview/hooks/useRecyclingState.d.ts +4 -2
- package/dist/recyclerview/hooks/useRecyclingState.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclingState.js +2 -2
- package/dist/recyclerview/hooks/useRecyclingState.js.map +1 -1
- package/dist/recyclerview/hooks/useSecondaryProps.js +1 -1
- package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts +10 -3
- package/dist/recyclerview/hooks/useUnmountAwareCallbacks.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js +33 -4
- package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js.map +1 -1
- package/dist/recyclerview/hooks/useUnmountFlag.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useUnmountFlag.js +1 -0
- package/dist/recyclerview/hooks/useUnmountFlag.js.map +1 -1
- package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts +18 -4
- package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/GridLayoutManager.js +60 -21
- package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts +35 -21
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.js +92 -28
- package/dist/recyclerview/layout-managers/LayoutManager.js.map +1 -1
- package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts +9 -1
- package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/MasonryLayoutManager.js +28 -12
- package/dist/recyclerview/layout-managers/MasonryLayoutManager.js.map +1 -1
- package/dist/recyclerview/utils/measureLayout.web.d.ts.map +1 -1
- package/dist/recyclerview/utils/measureLayout.web.js +1 -3
- package/dist/recyclerview/utils/measureLayout.web.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/viewability/ViewToken.d.ts +2 -2
- package/dist/viewability/ViewToken.d.ts.map +1 -1
- package/dist/viewability/ViewabilityHelper.js +1 -1
- package/dist/viewability/ViewabilityHelper.js.map +1 -1
- package/dist/viewability/ViewabilityManager.d.ts.map +1 -1
- package/dist/viewability/ViewabilityManager.js +11 -5
- package/dist/viewability/ViewabilityManager.js.map +1 -1
- package/jestSetup.js +30 -11
- package/package.json +2 -1
- package/src/AnimatedFlashList.ts +3 -2
- package/src/FlashList.tsx +24 -0
- package/src/FlashListProps.ts +41 -10
- package/src/FlashListRef.ts +320 -0
- package/src/MasonryFlashList.tsx +2 -2
- package/src/__tests__/RecyclerView.test.tsx +106 -31
- package/src/__tests__/RenderStackManager.test.ts +574 -0
- package/src/__tests__/helpers/createLayoutManager.ts +2 -3
- package/src/__tests__/useUnmountAwareCallbacks.test.tsx +12 -12
- package/src/benchmark/useBenchmark.ts +0 -37
- package/src/benchmark/useFlatListBenchmark.ts +2 -2
- package/src/index.ts +2 -1
- package/src/native/config/PlatformHelper.android.ts +1 -0
- package/src/native/config/PlatformHelper.ios.ts +1 -0
- package/src/native/config/PlatformHelper.ts +1 -0
- package/src/native/config/PlatformHelper.web.ts +1 -0
- package/src/recyclerview/RecyclerView.tsx +139 -75
- package/src/recyclerview/RecyclerViewContextProvider.ts +52 -7
- package/src/recyclerview/RecyclerViewManager.ts +135 -98
- package/src/recyclerview/RenderStackManager.ts +317 -0
- package/src/recyclerview/ViewHolder.tsx +5 -3
- package/src/recyclerview/ViewHolderCollection.tsx +42 -14
- package/src/recyclerview/components/ScrollAnchor.tsx +21 -9
- package/src/recyclerview/components/StickyHeaders.tsx +63 -45
- package/src/recyclerview/helpers/EngagedIndicesTracker.ts +118 -23
- package/src/recyclerview/helpers/RenderTimeTracker.ts +42 -0
- package/src/recyclerview/helpers/VelocityTracker.ts +77 -0
- package/src/recyclerview/hooks/useBoundDetection.ts +72 -23
- package/src/recyclerview/hooks/useLayoutState.ts +15 -6
- package/src/recyclerview/hooks/useMappingHelper.ts +1 -1
- package/src/recyclerview/hooks/useOnLoad.ts +4 -6
- package/src/recyclerview/hooks/useRecyclerViewController.tsx +364 -254
- package/src/recyclerview/hooks/useRecyclerViewManager.ts +13 -1
- package/src/recyclerview/hooks/useRecyclingState.ts +11 -7
- package/src/recyclerview/hooks/useSecondaryProps.tsx +1 -1
- package/src/recyclerview/hooks/useUnmountAwareCallbacks.ts +39 -3
- package/src/recyclerview/hooks/useUnmountFlag.ts +1 -0
- package/src/recyclerview/layout-managers/GridLayoutManager.ts +67 -23
- package/src/recyclerview/layout-managers/LayoutManager.ts +110 -41
- package/src/recyclerview/layout-managers/MasonryLayoutManager.ts +30 -8
- package/src/recyclerview/utils/measureLayout.web.ts +1 -3
- package/src/viewability/ViewToken.ts +2 -2
- package/src/viewability/ViewabilityHelper.ts +1 -1
- package/src/viewability/ViewabilityManager.ts +16 -9
- package/dist/__tests__/RecycleKeyManager.test.d.ts +0 -2
- package/dist/__tests__/RecycleKeyManager.test.d.ts.map +0 -1
- package/dist/__tests__/RecycleKeyManager.test.js +0 -210
- package/dist/__tests__/RecycleKeyManager.test.js.map +0 -1
- package/dist/recyclerview/RecycleKeyManager.d.ts +0 -82
- package/dist/recyclerview/RecycleKeyManager.d.ts.map +0 -1
- package/dist/recyclerview/RecycleKeyManager.js +0 -135
- package/dist/recyclerview/RecycleKeyManager.js.map +0 -1
- package/src/__tests__/RecycleKeyManager.test.ts +0 -254
- package/src/recyclerview/RecycleKeyManager.ts +0 -185
|
@@ -2,15 +2,20 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { RecyclerViewProps } from "../RecyclerViewProps";
|
|
4
4
|
import { RecyclerViewManager } from "../RecyclerViewManager";
|
|
5
|
+
import { VelocityTracker } from "../helpers/VelocityTracker";
|
|
5
6
|
|
|
6
7
|
export const useRecyclerViewManager = <T>(props: RecyclerViewProps<T>) => {
|
|
7
8
|
const [recyclerViewManager] = useState<RecyclerViewManager<T>>(
|
|
8
9
|
() => new RecyclerViewManager(props)
|
|
9
10
|
);
|
|
11
|
+
const [velocityTracker] = useState(() => new VelocityTracker());
|
|
12
|
+
|
|
10
13
|
const { data } = props;
|
|
11
14
|
|
|
12
15
|
useMemo(() => {
|
|
13
16
|
recyclerViewManager.updateProps(props);
|
|
17
|
+
// used to update props so rule can be disabled
|
|
18
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
14
19
|
}, [props]);
|
|
15
20
|
|
|
16
21
|
/**
|
|
@@ -18,13 +23,20 @@ export const useRecyclerViewManager = <T>(props: RecyclerViewProps<T>) => {
|
|
|
18
23
|
*/
|
|
19
24
|
useMemo(() => {
|
|
20
25
|
recyclerViewManager.processDataUpdate();
|
|
26
|
+
// used to process data update so rule can be disabled
|
|
27
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
21
28
|
}, [data]);
|
|
22
29
|
|
|
23
30
|
useEffect(() => {
|
|
31
|
+
recyclerViewManager.restoreIfNeeded();
|
|
32
|
+
|
|
24
33
|
return () => {
|
|
25
34
|
recyclerViewManager.dispose();
|
|
35
|
+
velocityTracker.cleanUp();
|
|
26
36
|
};
|
|
37
|
+
// Used to perform cleanup on unmount
|
|
38
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
27
39
|
}, []);
|
|
28
40
|
|
|
29
|
-
return { recyclerViewManager };
|
|
41
|
+
return { recyclerViewManager, velocityTracker };
|
|
30
42
|
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
2
2
|
|
|
3
|
-
import { useLayoutState } from "./useLayoutState";
|
|
3
|
+
import { LayoutStateSetter, useLayoutState } from "./useLayoutState";
|
|
4
|
+
|
|
5
|
+
export type RecyclingStateSetter<T> = LayoutStateSetter<T>;
|
|
6
|
+
|
|
7
|
+
export type RecyclingStateInitialValue<T> = T | (() => T);
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
10
|
* A custom hook that provides state management with automatic reset functionality.
|
|
@@ -16,10 +20,10 @@ import { useLayoutState } from "./useLayoutState";
|
|
|
16
20
|
* - A setState function that works like useState's setState
|
|
17
21
|
*/
|
|
18
22
|
export function useRecyclingState<T>(
|
|
19
|
-
initialState: T
|
|
23
|
+
initialState: RecyclingStateInitialValue<T>,
|
|
20
24
|
deps: React.DependencyList,
|
|
21
25
|
onReset?: () => void
|
|
22
|
-
): [T,
|
|
26
|
+
): [T, RecyclingStateSetter<T>] {
|
|
23
27
|
// Store the current state value in a ref to persist between renders
|
|
24
28
|
const valueStore = useRef<T>();
|
|
25
29
|
// Use layoutState to trigger re-renders when state changes
|
|
@@ -42,8 +46,8 @@ export function useRecyclingState<T>(
|
|
|
42
46
|
* Proxy setState function that updates the stored value and triggers a re-render.
|
|
43
47
|
* Only triggers a re-render if the new value is different from the current value.
|
|
44
48
|
*/
|
|
45
|
-
const setStateProxy = useCallback(
|
|
46
|
-
(newValue
|
|
49
|
+
const setStateProxy: RecyclingStateSetter<T> = useCallback(
|
|
50
|
+
(newValue, skipParentLayout) => {
|
|
47
51
|
// Calculate next state value from function or direct value
|
|
48
52
|
const nextState =
|
|
49
53
|
typeof newValue === "function"
|
|
@@ -53,7 +57,7 @@ export function useRecyclingState<T>(
|
|
|
53
57
|
// Only update and trigger re-render if value has changed
|
|
54
58
|
if (nextState !== valueStore.current) {
|
|
55
59
|
valueStore.current = nextState;
|
|
56
|
-
setCounter((prev) => prev + 1);
|
|
60
|
+
setCounter((prev) => prev + 1, skipParentLayout);
|
|
57
61
|
}
|
|
58
62
|
},
|
|
59
63
|
[setCounter]
|
|
@@ -105,7 +105,7 @@ export function useSecondaryProps<T>(props: RecyclerViewProps<T>) {
|
|
|
105
105
|
const ForwardedScrollComponent = React.forwardRef((_props, ref) =>
|
|
106
106
|
(renderScrollComponent as any)({ ..._props, ref } as any)
|
|
107
107
|
);
|
|
108
|
-
ForwardedScrollComponent.displayName = "
|
|
108
|
+
ForwardedScrollComponent.displayName = "CustomScrollView";
|
|
109
109
|
scrollComponent = ForwardedScrollComponent as any;
|
|
110
110
|
} else if (renderScrollComponent) {
|
|
111
111
|
scrollComponent = renderScrollComponent;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Hook that provides
|
|
5
|
-
* Any timeouts created with
|
|
4
|
+
* Hook that provides a setTimeout which is aware of component unmount state.
|
|
5
|
+
* Any timeouts created with this hook will be automatically cleared when the component unmounts.
|
|
6
6
|
*/
|
|
7
|
-
export function
|
|
7
|
+
export function useUnmountAwareTimeout() {
|
|
8
8
|
// Store active timeout IDs in a Set for more efficient add/remove operations
|
|
9
9
|
const [timeoutIds] = useState<Set<NodeJS.Timeout>>(() => new Set());
|
|
10
10
|
|
|
@@ -35,3 +35,39 @@ export function useUnmountAwareCallbacks() {
|
|
|
35
35
|
setTimeout,
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Hook that provides a requestAnimationFrame which is aware of component unmount state.
|
|
41
|
+
* Any animation frames requested with this hook will be automatically canceled when the component unmounts.
|
|
42
|
+
*/
|
|
43
|
+
export function useUnmountAwareAnimationFrame() {
|
|
44
|
+
// Store active animation frame request IDs in a Set for more efficient add/remove operations
|
|
45
|
+
const [requestIds] = useState<Set<number>>(() => new Set());
|
|
46
|
+
|
|
47
|
+
// Cancel all animation frame requests on unmount
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
return () => {
|
|
50
|
+
requestIds.forEach((id) => cancelAnimationFrame(id));
|
|
51
|
+
requestIds.clear();
|
|
52
|
+
};
|
|
53
|
+
}, [requestIds]);
|
|
54
|
+
|
|
55
|
+
// Create a safe requestAnimationFrame that will be canceled on unmount
|
|
56
|
+
const requestAnimationFrame = useCallback(
|
|
57
|
+
(callback: FrameRequestCallback): void => {
|
|
58
|
+
const id = global.requestAnimationFrame((timestamp) => {
|
|
59
|
+
// Remove this request ID from the tracking set
|
|
60
|
+
requestIds.delete(id);
|
|
61
|
+
callback(timestamp);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Track this request ID
|
|
65
|
+
requestIds.add(id);
|
|
66
|
+
},
|
|
67
|
+
[requestIds]
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
requestAnimationFrame,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -16,6 +16,7 @@ export const useUnmountFlag = () => {
|
|
|
16
16
|
// Use layoutEffect to set up cleanup on unmount
|
|
17
17
|
// This ensures the flag is set before any other cleanup effects run
|
|
18
18
|
useLayoutEffect(() => {
|
|
19
|
+
isUnmounted.current = false;
|
|
19
20
|
// Cleanup function that runs when the component unmounts
|
|
20
21
|
return () => {
|
|
21
22
|
isUnmounted.current = true;
|
|
@@ -14,6 +14,9 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
14
14
|
/** The width of the bounded area for the grid */
|
|
15
15
|
private boundedSize: number;
|
|
16
16
|
|
|
17
|
+
/** If there's a span change for grid layout, we need to recompute all the widths */
|
|
18
|
+
private fullRelayoutRequired = false;
|
|
19
|
+
|
|
17
20
|
constructor(params: LayoutParams, previousLayoutManager?: RVLayoutManager) {
|
|
18
21
|
super(params, previousLayoutManager);
|
|
19
22
|
this.boundedSize = params.windowSize.width;
|
|
@@ -33,10 +36,7 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
33
36
|
this.boundedSize = params.windowSize.width;
|
|
34
37
|
if (this.layouts.length > 0) {
|
|
35
38
|
// update all widths
|
|
36
|
-
|
|
37
|
-
this.layouts[i].width = this.getWidth(i);
|
|
38
|
-
}
|
|
39
|
-
// console.log("-----> recomputeLayouts");
|
|
39
|
+
this.updateAllWidths();
|
|
40
40
|
|
|
41
41
|
this.recomputeLayouts(0, this.layouts.length - 1);
|
|
42
42
|
this.requiresRepaint = true;
|
|
@@ -57,6 +57,13 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
57
57
|
layout.isHeightMeasured = true;
|
|
58
58
|
layout.isWidthMeasured = true;
|
|
59
59
|
}
|
|
60
|
+
|
|
61
|
+
// TODO: Can be optimized
|
|
62
|
+
if (this.fullRelayoutRequired) {
|
|
63
|
+
this.updateAllWidths();
|
|
64
|
+
this.fullRelayoutRequired = false;
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
/**
|
|
@@ -72,18 +79,24 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
72
79
|
layout.enforcedWidth = true;
|
|
73
80
|
}
|
|
74
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Handles span change for an item.
|
|
84
|
+
* @param index Index of the item
|
|
85
|
+
*/
|
|
86
|
+
handleSpanChange(index: number) {
|
|
87
|
+
this.fullRelayoutRequired = true;
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
/**
|
|
76
91
|
* Returns the total size of the layout area.
|
|
77
92
|
* @returns RVDimension containing width and height of the layout
|
|
78
93
|
*/
|
|
79
94
|
getLayoutSize(): RVDimension {
|
|
80
95
|
if (this.layouts.length === 0) return { width: 0, height: 0 };
|
|
81
|
-
const
|
|
82
|
-
this.layouts.length - 1
|
|
83
|
-
);
|
|
96
|
+
const totalHeight = this.computeTotalHeightTillRow(this.layouts.length - 1);
|
|
84
97
|
return {
|
|
85
98
|
width: this.boundedSize,
|
|
86
|
-
height:
|
|
99
|
+
height: totalHeight,
|
|
87
100
|
};
|
|
88
101
|
}
|
|
89
102
|
|
|
@@ -93,7 +106,9 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
93
106
|
* @param endIndex Ending index of items to recompute
|
|
94
107
|
*/
|
|
95
108
|
recomputeLayouts(startIndex: number, endIndex: number): void {
|
|
96
|
-
const newStartIndex = this.
|
|
109
|
+
const newStartIndex = this.locateFirstIndexInRow(
|
|
110
|
+
Math.max(0, startIndex - 1)
|
|
111
|
+
);
|
|
97
112
|
const startVal = this.getLayout(newStartIndex);
|
|
98
113
|
|
|
99
114
|
let startX = startVal.x;
|
|
@@ -102,7 +117,7 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
102
117
|
for (let i = newStartIndex; i <= endIndex; i++) {
|
|
103
118
|
const layout = this.getLayout(i);
|
|
104
119
|
if (!this.checkBounds(startX, layout.width)) {
|
|
105
|
-
const tallestItem = this.processAndReturnTallestItemInRow(i);
|
|
120
|
+
const tallestItem = this.processAndReturnTallestItemInRow(i - 1);
|
|
106
121
|
startY = tallestItem.y + tallestItem.height;
|
|
107
122
|
startX = 0;
|
|
108
123
|
}
|
|
@@ -111,6 +126,9 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
111
126
|
layout.y = startY;
|
|
112
127
|
startX += layout.width;
|
|
113
128
|
}
|
|
129
|
+
if (endIndex === this.layouts.length - 1) {
|
|
130
|
+
this.processAndReturnTallestItemInRow(endIndex);
|
|
131
|
+
}
|
|
114
132
|
}
|
|
115
133
|
|
|
116
134
|
/**
|
|
@@ -119,25 +137,23 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
119
137
|
* @returns Width of the item
|
|
120
138
|
*/
|
|
121
139
|
private getWidth(index: number): number {
|
|
122
|
-
|
|
123
|
-
return (this.boundedSize / this.maxColumns) * span;
|
|
140
|
+
return (this.boundedSize / this.maxColumns) * this.getSpan(index);
|
|
124
141
|
}
|
|
125
142
|
|
|
126
143
|
/**
|
|
127
144
|
* Processes items in a row and returns the tallest item.
|
|
128
145
|
* Also handles height normalization for items in the same row.
|
|
129
146
|
* Tallest item per row helps in forcing tallest items height on neighbouring items.
|
|
130
|
-
* @param
|
|
147
|
+
* @param endIndex Index of the last item in the row
|
|
131
148
|
* @returns The tallest item in the row
|
|
132
149
|
*/
|
|
133
|
-
private processAndReturnTallestItemInRow(
|
|
134
|
-
const startIndex = this.
|
|
135
|
-
const y = this.layouts[startIndex].y;
|
|
150
|
+
private processAndReturnTallestItemInRow(endIndex: number): RVLayout {
|
|
151
|
+
const startIndex = this.locateFirstIndexInRow(endIndex);
|
|
136
152
|
let tallestItem: RVLayout | undefined;
|
|
137
153
|
let maxHeight = 0;
|
|
138
154
|
let i = startIndex;
|
|
139
155
|
let isMeasured = false;
|
|
140
|
-
while (
|
|
156
|
+
while (i <= endIndex) {
|
|
141
157
|
const layout = this.layouts[i];
|
|
142
158
|
isMeasured = isMeasured || Boolean(layout.isHeightMeasured);
|
|
143
159
|
maxHeight = Math.max(maxHeight, layout.height);
|
|
@@ -153,7 +169,9 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
153
169
|
break;
|
|
154
170
|
}
|
|
155
171
|
}
|
|
156
|
-
|
|
172
|
+
if (!tallestItem && maxHeight > 0) {
|
|
173
|
+
maxHeight = Number.MAX_SAFE_INTEGER;
|
|
174
|
+
}
|
|
157
175
|
tallestItem = tallestItem ?? this.layouts[startIndex];
|
|
158
176
|
|
|
159
177
|
if (!isMeasured) {
|
|
@@ -167,7 +185,7 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
167
185
|
this.requiresRepaint = true;
|
|
168
186
|
}
|
|
169
187
|
i = startIndex;
|
|
170
|
-
while (
|
|
188
|
+
while (i <= endIndex) {
|
|
171
189
|
this.layouts[i].minHeight = targetHeight;
|
|
172
190
|
if (targetHeight > 0) {
|
|
173
191
|
this.layouts[i].height = targetHeight;
|
|
@@ -182,6 +200,32 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
182
200
|
return tallestItem;
|
|
183
201
|
}
|
|
184
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Computes the total height of the layout.
|
|
205
|
+
* @param endIndex Index of the last item in the row
|
|
206
|
+
* @returns Total height of the layout
|
|
207
|
+
*/
|
|
208
|
+
private computeTotalHeightTillRow(endIndex: number): number {
|
|
209
|
+
const startIndex = this.locateFirstIndexInRow(endIndex);
|
|
210
|
+
const y = this.layouts[startIndex].y;
|
|
211
|
+
let maxHeight = 0;
|
|
212
|
+
let i = startIndex;
|
|
213
|
+
while (i <= endIndex) {
|
|
214
|
+
maxHeight = Math.max(maxHeight, this.layouts[i].height);
|
|
215
|
+
i++;
|
|
216
|
+
if (i >= this.layouts.length) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return y + maxHeight;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private updateAllWidths() {
|
|
224
|
+
for (let i = 0; i < this.layouts.length; i++) {
|
|
225
|
+
this.layouts[i].width = this.getWidth(i);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
185
229
|
/**
|
|
186
230
|
* Checks if an item can fit within the bounded width.
|
|
187
231
|
* @param itemX Starting X position of the item
|
|
@@ -194,14 +238,14 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
194
238
|
|
|
195
239
|
/**
|
|
196
240
|
* Locates the index of the first item in the current row.
|
|
197
|
-
* @param
|
|
241
|
+
* @param itemIndex Index to start searching from
|
|
198
242
|
* @returns Index of the first item in the row
|
|
199
243
|
*/
|
|
200
|
-
private
|
|
201
|
-
if (
|
|
244
|
+
private locateFirstIndexInRow(itemIndex: number): number {
|
|
245
|
+
if (itemIndex === 0) {
|
|
202
246
|
return 0;
|
|
203
247
|
}
|
|
204
|
-
let i =
|
|
248
|
+
let i = itemIndex;
|
|
205
249
|
for (; i >= 0; i--) {
|
|
206
250
|
if (this.layouts[i].x === 0) {
|
|
207
251
|
break;
|
|
@@ -20,12 +20,8 @@ export abstract class RVLayoutManager {
|
|
|
20
20
|
protected layouts: RVLayout[];
|
|
21
21
|
/** Dimensions of the visible window/viewport */
|
|
22
22
|
protected windowSize: RVDimension;
|
|
23
|
-
/** Information about item spans and sizes */
|
|
24
|
-
protected spanSizeInfo: SpanSizeInfo = {};
|
|
25
23
|
/** Maximum number of columns in the layout */
|
|
26
24
|
protected maxColumns: number;
|
|
27
|
-
/** Optional callback to override default item layout */
|
|
28
|
-
protected overrideItemLayout?: (index: number, layout: SpanSizeInfo) => void;
|
|
29
25
|
|
|
30
26
|
/** Whether to optimize item placement for better space utilization */
|
|
31
27
|
protected optimizeItemArrangement: boolean;
|
|
@@ -33,19 +29,34 @@ export abstract class RVLayoutManager {
|
|
|
33
29
|
/** Flag indicating if the layout requires repainting */
|
|
34
30
|
public requiresRepaint = false;
|
|
35
31
|
|
|
32
|
+
/** Optional callback to override default item layout */
|
|
33
|
+
private overrideItemLayout: (index: number, layout: SpanSizeInfo) => void;
|
|
36
34
|
/** Optional function to determine item type */
|
|
37
|
-
private
|
|
35
|
+
private getItemType: (index: number) => string;
|
|
38
36
|
/** Window for tracking average heights by item type */
|
|
39
37
|
private heightAverageWindow: MultiTypeAverageWindow;
|
|
40
38
|
/** Window for tracking average widths by item type */
|
|
41
39
|
private widthAverageWindow: MultiTypeAverageWindow;
|
|
42
40
|
/** Maximum number of items to process in a single layout pass */
|
|
43
|
-
private maxItemsToProcess = 250;
|
|
41
|
+
private maxItemsToProcess = 250;
|
|
42
|
+
/** Information about item spans and sizes */
|
|
43
|
+
private spanSizeInfo: SpanSizeInfo = {};
|
|
44
|
+
/** Span tracker for each item */
|
|
45
|
+
private spanTracker: (number | undefined)[] = [];
|
|
46
|
+
|
|
47
|
+
/** Current max index with changed layout */
|
|
48
|
+
private currentMaxIndexWithChangedLayout = -1;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Last index that was skipped during layout computation.
|
|
52
|
+
* Used to determine if a layout needs to be recomputed.
|
|
53
|
+
*/
|
|
54
|
+
private lastSkippedLayoutIndex = Number.MAX_VALUE;
|
|
44
55
|
|
|
45
56
|
constructor(params: LayoutParams, previousLayoutManager?: RVLayoutManager) {
|
|
46
57
|
this.heightAverageWindow = new MultiTypeAverageWindow(5, 200);
|
|
47
58
|
this.widthAverageWindow = new MultiTypeAverageWindow(5, 200);
|
|
48
|
-
this.
|
|
59
|
+
this.getItemType = params.getItemType;
|
|
49
60
|
this.overrideItemLayout = params.overrideItemLayout;
|
|
50
61
|
this.layouts = previousLayoutManager?.layouts ?? [];
|
|
51
62
|
if (previousLayoutManager) {
|
|
@@ -57,15 +68,6 @@ export abstract class RVLayoutManager {
|
|
|
57
68
|
}
|
|
58
69
|
}
|
|
59
70
|
|
|
60
|
-
/**
|
|
61
|
-
* Gets the type of an item at the given index.
|
|
62
|
-
* @param index Index of the item
|
|
63
|
-
* @returns Item type or "default" if not specified
|
|
64
|
-
*/
|
|
65
|
-
private getItemType(index: number): string | number {
|
|
66
|
-
return this._getItemType?.(index) ?? "default";
|
|
67
|
-
}
|
|
68
|
-
|
|
69
71
|
/**
|
|
70
72
|
* Gets the estimated width for an item based on its type.
|
|
71
73
|
* @param index Index of the item
|
|
@@ -114,8 +116,8 @@ export abstract class RVLayoutManager {
|
|
|
114
116
|
/**
|
|
115
117
|
* Gets indices of items currently visible in the viewport.
|
|
116
118
|
* Uses binary search for efficient lookup.
|
|
117
|
-
* @param unboundDimensionStart Start position of viewport
|
|
118
|
-
* @param unboundDimensionEnd End position of viewport
|
|
119
|
+
* @param unboundDimensionStart Start position of viewport (start X or start Y)
|
|
120
|
+
* @param unboundDimensionEnd End position of viewport (end X or end Y)
|
|
119
121
|
* @returns ConsecutiveNumbers containing visible indices
|
|
120
122
|
*/
|
|
121
123
|
getVisibleLayouts(
|
|
@@ -157,7 +159,7 @@ export abstract class RVLayoutManager {
|
|
|
157
159
|
}
|
|
158
160
|
const startIndex = Math.min(...indices);
|
|
159
161
|
// Recompute layouts starting from the smallest index in the original indices array
|
|
160
|
-
this.
|
|
162
|
+
this._recomputeLayouts(
|
|
161
163
|
this.getMinRecomputeIndex(startIndex),
|
|
162
164
|
this.getMaxRecomputeIndex(startIndex)
|
|
163
165
|
);
|
|
@@ -169,41 +171,49 @@ export abstract class RVLayoutManager {
|
|
|
169
171
|
* @param totalItemCount Total number of items in the list
|
|
170
172
|
*/
|
|
171
173
|
modifyLayout(layoutInfo: RVLayoutInfo[], totalItemCount: number): void {
|
|
174
|
+
this.maxItemsToProcess = Math.max(
|
|
175
|
+
this.maxItemsToProcess,
|
|
176
|
+
layoutInfo.length * 10
|
|
177
|
+
);
|
|
172
178
|
let minRecomputeIndex = Number.MAX_VALUE;
|
|
173
179
|
|
|
174
180
|
if (this.layouts.length > totalItemCount) {
|
|
175
181
|
this.layouts.length = totalItemCount;
|
|
182
|
+
this.spanTracker.length = totalItemCount;
|
|
176
183
|
minRecomputeIndex = totalItemCount - 1; // <0 gets skipped so it's safe to set to totalItemCount - 1
|
|
177
184
|
}
|
|
178
185
|
// update average windows
|
|
179
186
|
minRecomputeIndex = Math.min(
|
|
180
187
|
minRecomputeIndex,
|
|
181
|
-
this.
|
|
188
|
+
this.computeEstimatesAndMinMaxChangedLayout(layoutInfo)
|
|
182
189
|
);
|
|
183
190
|
|
|
184
191
|
if (this.layouts.length < totalItemCount && totalItemCount > 0) {
|
|
185
192
|
const startIndex = this.layouts.length;
|
|
186
193
|
this.layouts.length = totalItemCount;
|
|
194
|
+
this.spanTracker.length = totalItemCount;
|
|
187
195
|
for (let i = startIndex; i < totalItemCount; i++) {
|
|
188
196
|
this.getLayout(i);
|
|
197
|
+
this.getSpan(i);
|
|
189
198
|
}
|
|
190
199
|
this.recomputeLayouts(startIndex, totalItemCount - 1);
|
|
191
200
|
}
|
|
192
|
-
|
|
193
|
-
minRecomputeIndex,
|
|
194
|
-
this.processLayoutInfo(layoutInfo, totalItemCount) ?? minRecomputeIndex
|
|
195
|
-
);
|
|
201
|
+
|
|
196
202
|
// compute minRecomputeIndex
|
|
203
|
+
|
|
197
204
|
minRecomputeIndex = Math.min(
|
|
198
205
|
minRecomputeIndex,
|
|
199
|
-
this.
|
|
206
|
+
this.lastSkippedLayoutIndex,
|
|
207
|
+
this.computeMinIndexWithChangedSpan(layoutInfo),
|
|
208
|
+
this.processLayoutInfo(layoutInfo, totalItemCount) ?? minRecomputeIndex,
|
|
209
|
+
this.computeEstimatesAndMinMaxChangedLayout(layoutInfo)
|
|
200
210
|
);
|
|
211
|
+
|
|
201
212
|
if (minRecomputeIndex >= 0 && minRecomputeIndex < totalItemCount) {
|
|
202
|
-
this.
|
|
203
|
-
|
|
204
|
-
this.getMaxRecomputeIndex(minRecomputeIndex)
|
|
205
|
-
);
|
|
213
|
+
const maxRecomputeIndex = this.getMaxRecomputeIndex(minRecomputeIndex);
|
|
214
|
+
this._recomputeLayouts(minRecomputeIndex, maxRecomputeIndex);
|
|
206
215
|
}
|
|
216
|
+
this.currentMaxIndexWithChangedLayout = -1;
|
|
207
217
|
}
|
|
208
218
|
|
|
209
219
|
/**
|
|
@@ -269,16 +279,30 @@ export abstract class RVLayoutManager {
|
|
|
269
279
|
protected abstract estimateLayout(index: number): void;
|
|
270
280
|
|
|
271
281
|
/**
|
|
272
|
-
* Gets span
|
|
282
|
+
* Gets span for an item, applying any overrides.
|
|
283
|
+
* This is intended to be called during a relayout call. The value is tracked and used to determine if a span change has occurred.
|
|
284
|
+
* If skipTracking is true, the operation is not tracked. Can be useful if span is required outside of a relayout call.
|
|
285
|
+
* The tracker is used to call handleSpanChange if a span change has occurred before relayout call.
|
|
286
|
+
* // TODO: improve this contract.
|
|
273
287
|
* @param index Index of the item
|
|
274
|
-
* @returns
|
|
288
|
+
* @returns Span for the item
|
|
275
289
|
*/
|
|
276
|
-
protected
|
|
290
|
+
protected getSpan(index: number, skipTracking = false): number {
|
|
277
291
|
this.spanSizeInfo.span = undefined;
|
|
278
|
-
this.overrideItemLayout
|
|
279
|
-
|
|
292
|
+
this.overrideItemLayout(index, this.spanSizeInfo);
|
|
293
|
+
const span = Math.min(this.spanSizeInfo.span ?? 1, this.maxColumns);
|
|
294
|
+
if (!skipTracking) {
|
|
295
|
+
this.spanTracker[index] = span;
|
|
296
|
+
}
|
|
297
|
+
return span;
|
|
280
298
|
}
|
|
281
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Method to handle span change for an item. Can be overridden by subclasses.
|
|
302
|
+
* @param index Index of the item
|
|
303
|
+
*/
|
|
304
|
+
protected handleSpanChange(index: number) {}
|
|
305
|
+
|
|
282
306
|
/**
|
|
283
307
|
* Gets the maximum index to process in a single layout pass.
|
|
284
308
|
* @param startIndex Starting index
|
|
@@ -286,7 +310,8 @@ export abstract class RVLayoutManager {
|
|
|
286
310
|
*/
|
|
287
311
|
private getMaxRecomputeIndex(startIndex: number): number {
|
|
288
312
|
return Math.min(
|
|
289
|
-
startIndex
|
|
313
|
+
Math.max(startIndex, this.currentMaxIndexWithChangedLayout) +
|
|
314
|
+
this.maxItemsToProcess,
|
|
290
315
|
this.layouts.length - 1
|
|
291
316
|
);
|
|
292
317
|
}
|
|
@@ -300,12 +325,36 @@ export abstract class RVLayoutManager {
|
|
|
300
325
|
return startIndex;
|
|
301
326
|
}
|
|
302
327
|
|
|
328
|
+
private _recomputeLayouts(startIndex: number, endIndex: number): void {
|
|
329
|
+
this.recomputeLayouts(startIndex, endIndex);
|
|
330
|
+
if (
|
|
331
|
+
this.lastSkippedLayoutIndex >= startIndex &&
|
|
332
|
+
this.lastSkippedLayoutIndex <= endIndex
|
|
333
|
+
) {
|
|
334
|
+
this.lastSkippedLayoutIndex = Number.MAX_VALUE;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (endIndex + 1 < this.layouts.length) {
|
|
338
|
+
this.lastSkippedLayoutIndex = Math.min(
|
|
339
|
+
endIndex + 1,
|
|
340
|
+
this.lastSkippedLayoutIndex
|
|
341
|
+
);
|
|
342
|
+
const lastIndex = this.layouts.length - 1;
|
|
343
|
+
// Since layout managers derive height from last indices we need to make
|
|
344
|
+
// sure they're not too much out of sync.
|
|
345
|
+
if (this.layouts[lastIndex].y < this.layouts[endIndex].y) {
|
|
346
|
+
this.recomputeLayouts(this.lastSkippedLayoutIndex, lastIndex);
|
|
347
|
+
this.lastSkippedLayoutIndex = Number.MAX_VALUE;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
303
352
|
/**
|
|
304
353
|
* Computes size estimates and finds the minimum recompute index.
|
|
305
354
|
* @param layoutInfo Array of layout information for items
|
|
306
355
|
* @returns Minimum index that needs recomputation
|
|
307
356
|
*/
|
|
308
|
-
private
|
|
357
|
+
private computeEstimatesAndMinMaxChangedLayout(
|
|
309
358
|
layoutInfo: RVLayoutInfo[]
|
|
310
359
|
): number {
|
|
311
360
|
let minRecomputeIndex = Number.MAX_VALUE;
|
|
@@ -313,6 +362,7 @@ export abstract class RVLayoutManager {
|
|
|
313
362
|
const { index, dimensions } = info;
|
|
314
363
|
const storedLayout = this.layouts[index];
|
|
315
364
|
if (
|
|
365
|
+
index >= this.lastSkippedLayoutIndex ||
|
|
316
366
|
!storedLayout ||
|
|
317
367
|
!storedLayout.isHeightMeasured ||
|
|
318
368
|
!storedLayout.isWidthMeasured ||
|
|
@@ -320,6 +370,10 @@ export abstract class RVLayoutManager {
|
|
|
320
370
|
areDimensionsNotEqual(storedLayout.width, dimensions.width)
|
|
321
371
|
) {
|
|
322
372
|
minRecomputeIndex = Math.min(minRecomputeIndex, index);
|
|
373
|
+
this.currentMaxIndexWithChangedLayout = Math.max(
|
|
374
|
+
this.currentMaxIndexWithChangedLayout,
|
|
375
|
+
index
|
|
376
|
+
);
|
|
323
377
|
}
|
|
324
378
|
this.heightAverageWindow.addValue(
|
|
325
379
|
dimensions.height,
|
|
@@ -332,6 +386,21 @@ export abstract class RVLayoutManager {
|
|
|
332
386
|
}
|
|
333
387
|
return minRecomputeIndex;
|
|
334
388
|
}
|
|
389
|
+
|
|
390
|
+
private computeMinIndexWithChangedSpan(layoutInfo: RVLayoutInfo[]): number {
|
|
391
|
+
let minIndexWithChangedSpan = Number.MAX_VALUE;
|
|
392
|
+
for (const info of layoutInfo) {
|
|
393
|
+
const { index } = info;
|
|
394
|
+
const span = this.getSpan(index, true);
|
|
395
|
+
const storedSpan = this.spanTracker[index];
|
|
396
|
+
if (span !== storedSpan) {
|
|
397
|
+
this.spanTracker[index] = span;
|
|
398
|
+
this.handleSpanChange(index);
|
|
399
|
+
minIndexWithChangedSpan = Math.min(minIndexWithChangedSpan, index);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return minIndexWithChangedSpan;
|
|
403
|
+
}
|
|
335
404
|
}
|
|
336
405
|
|
|
337
406
|
/**
|
|
@@ -348,31 +417,31 @@ export interface LayoutParams {
|
|
|
348
417
|
* Determines if the list scrolls horizontally (true) or vertically (false)
|
|
349
418
|
* Affects how items are positioned and which dimension is used for scrolling
|
|
350
419
|
*/
|
|
351
|
-
horizontal
|
|
420
|
+
horizontal: boolean;
|
|
352
421
|
|
|
353
422
|
/**
|
|
354
423
|
* Maximum number of columns in a grid layout
|
|
355
424
|
* Controls how many items can be placed side by side
|
|
356
425
|
*/
|
|
357
|
-
maxColumns
|
|
426
|
+
maxColumns: number;
|
|
358
427
|
|
|
359
428
|
/**
|
|
360
429
|
* When true, attempts to optimize item placement for better space utilization
|
|
361
430
|
* May affect the ordering of items to minimize empty space
|
|
362
431
|
*/
|
|
363
|
-
optimizeItemArrangement
|
|
432
|
+
optimizeItemArrangement: boolean;
|
|
364
433
|
|
|
365
434
|
/**
|
|
366
435
|
* Callback to manually override layout properties for specific items
|
|
367
436
|
* Allows custom control over span and size for individual items
|
|
368
437
|
*/
|
|
369
|
-
overrideItemLayout
|
|
438
|
+
overrideItemLayout: (index: number, layout: SpanSizeInfo) => void;
|
|
370
439
|
|
|
371
440
|
/**
|
|
372
441
|
* Function to determine the type of an item at a specific index
|
|
373
442
|
* Used for size estimation and optimization based on item types
|
|
374
443
|
*/
|
|
375
|
-
getItemType
|
|
444
|
+
getItemType: (index: number) => string;
|
|
376
445
|
}
|
|
377
446
|
|
|
378
447
|
/**
|