@shopify/flash-list 2.2.3 → 2.3.1
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/FlashListProps.d.ts +11 -0
- package/dist/FlashListProps.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.android.d.ts +10 -0
- package/dist/native/config/PlatformHelper.android.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.android.js +7 -0
- package/dist/native/config/PlatformHelper.android.js.map +1 -1
- package/dist/native/config/PlatformHelper.d.ts +10 -0
- package/dist/native/config/PlatformHelper.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.ios.d.ts +10 -0
- package/dist/native/config/PlatformHelper.ios.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.ios.js +2 -0
- package/dist/native/config/PlatformHelper.ios.js.map +1 -1
- package/dist/native/config/PlatformHelper.js +2 -0
- package/dist/native/config/PlatformHelper.js.map +1 -1
- package/dist/native/config/PlatformHelper.web.d.ts +10 -0
- package/dist/native/config/PlatformHelper.web.d.ts.map +1 -1
- package/dist/native/config/PlatformHelper.web.js +2 -0
- package/dist/native/config/PlatformHelper.web.js.map +1 -1
- package/dist/recyclerview/RecyclerView.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerView.js +10 -3
- package/dist/recyclerview/RecyclerView.js.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.js +16 -18
- package/dist/recyclerview/RecyclerViewManager.js.map +1 -1
- package/dist/recyclerview/RecyclerViewProps.d.ts +1 -1
- package/dist/recyclerview/RecyclerViewProps.d.ts.map +1 -1
- package/dist/recyclerview/ViewHolder.d.ts +2 -0
- package/dist/recyclerview/ViewHolder.d.ts.map +1 -1
- package/dist/recyclerview/ViewHolder.js +8 -2
- package/dist/recyclerview/ViewHolder.js.map +1 -1
- package/dist/recyclerview/ViewHolderCollection.d.ts +2 -0
- package/dist/recyclerview/ViewHolderCollection.d.ts.map +1 -1
- package/dist/recyclerview/ViewHolderCollection.js +2 -2
- package/dist/recyclerview/ViewHolderCollection.js.map +1 -1
- package/dist/recyclerview/components/StickyHeaders.d.ts +3 -1
- package/dist/recyclerview/components/StickyHeaders.d.ts.map +1 -1
- package/dist/recyclerview/components/StickyHeaders.js +3 -2
- package/dist/recyclerview/components/StickyHeaders.js.map +1 -1
- package/dist/recyclerview/hooks/useBoundDetection.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useBoundDetection.js +7 -0
- package/dist/recyclerview/hooks/useBoundDetection.js.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.js +11 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -1
- package/dist/recyclerview/hooks/useSecondaryProps.d.ts +1 -1
- package/dist/recyclerview/hooks/useSecondaryProps.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useSecondaryProps.js +29 -13
- package/dist/recyclerview/hooks/useSecondaryProps.js.map +1 -1
- package/dist/recyclerview/utils/getInvertedTransformStyle.d.ts +10 -0
- package/dist/recyclerview/utils/getInvertedTransformStyle.d.ts.map +1 -0
- package/dist/recyclerview/utils/getInvertedTransformStyle.js +7 -0
- package/dist/recyclerview/utils/getInvertedTransformStyle.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/FlashListProps.ts +13 -0
- package/src/native/config/PlatformHelper.android.ts +7 -0
- package/src/native/config/PlatformHelper.ios.ts +2 -0
- package/src/native/config/PlatformHelper.ts +2 -0
- package/src/native/config/PlatformHelper.web.ts +2 -0
- package/src/recyclerview/RecyclerView.tsx +11 -0
- package/src/recyclerview/RecyclerViewManager.ts +17 -21
- package/src/recyclerview/RecyclerViewProps.ts +1 -1
- package/src/recyclerview/ViewHolder.tsx +11 -1
- package/src/recyclerview/ViewHolderCollection.tsx +4 -0
- package/src/recyclerview/components/StickyHeaders.tsx +5 -0
- package/src/recyclerview/hooks/useBoundDetection.ts +8 -0
- package/src/recyclerview/hooks/useRecyclerViewController.tsx +11 -1
- package/src/recyclerview/hooks/useSecondaryProps.tsx +36 -12
- package/src/recyclerview/utils/getInvertedTransformStyle.ts +7 -0
|
@@ -56,6 +56,8 @@ export interface ViewHolderCollectionProps<TItem> {
|
|
|
56
56
|
hideStickyHeaderRelatedCell: boolean;
|
|
57
57
|
/** Returns whether the item at the given index is in the last row of the layout */
|
|
58
58
|
isInLastRow: (index: number) => boolean;
|
|
59
|
+
/** Whether the list is inverted */
|
|
60
|
+
inverted: FlashListProps<TItem>["inverted"];
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
/**
|
|
@@ -93,6 +95,7 @@ export const ViewHolderCollection = <TItem,>(
|
|
|
93
95
|
currentStickyIndex,
|
|
94
96
|
hideStickyHeaderRelatedCell,
|
|
95
97
|
isInLastRow,
|
|
98
|
+
inverted,
|
|
96
99
|
} = props;
|
|
97
100
|
|
|
98
101
|
const [renderId, setRenderId] = React.useState(0);
|
|
@@ -202,6 +205,7 @@ export const ViewHolderCollection = <TItem,>(
|
|
|
202
205
|
hidden={
|
|
203
206
|
hideStickyHeaderRelatedCell && currentStickyIndex === index
|
|
204
207
|
}
|
|
208
|
+
inverted={inverted}
|
|
205
209
|
/>
|
|
206
210
|
);
|
|
207
211
|
})}
|
|
@@ -44,6 +44,8 @@ export interface StickyHeaderProps<TItem> {
|
|
|
44
44
|
recyclerViewManager: RecyclerViewManager<TItem>;
|
|
45
45
|
/** Additional data to trigger re-renders */
|
|
46
46
|
extraData: FlashListProps<TItem>["extraData"];
|
|
47
|
+
/** Whether the list is inverted */
|
|
48
|
+
inverted: FlashListProps<TItem>["inverted"];
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/**
|
|
@@ -69,6 +71,7 @@ export const StickyHeaders = <TItem,>({
|
|
|
69
71
|
data,
|
|
70
72
|
extraData,
|
|
71
73
|
onChangeStickyIndex,
|
|
74
|
+
inverted,
|
|
72
75
|
}: StickyHeaderProps<TItem>) => {
|
|
73
76
|
const [stickyHeaderState, setStickyHeaderState] = useState<StickyHeaderState>(
|
|
74
77
|
{
|
|
@@ -214,6 +217,7 @@ export const StickyHeaders = <TItem,>({
|
|
|
214
217
|
trailingItem={null}
|
|
215
218
|
target="StickyHeader"
|
|
216
219
|
hidden={false}
|
|
220
|
+
inverted={inverted}
|
|
217
221
|
/>
|
|
218
222
|
) : null}
|
|
219
223
|
</CompatAnimatedView>
|
|
@@ -227,6 +231,7 @@ export const StickyHeaders = <TItem,>({
|
|
|
227
231
|
refHolder,
|
|
228
232
|
extraData,
|
|
229
233
|
stickyHeaderOffset,
|
|
234
|
+
inverted,
|
|
230
235
|
]);
|
|
231
236
|
|
|
232
237
|
if (PlatformConfig.isRN083OrAbove && currentStickyIndex === -1) {
|
|
@@ -84,6 +84,14 @@ export function useBoundDetection<T>(
|
|
|
84
84
|
(isHorizontal ? contentSize.width : contentSize.height) +
|
|
85
85
|
recyclerViewManager.firstItemOffset;
|
|
86
86
|
|
|
87
|
+
// Skip bound detection if the window has no measurable size.
|
|
88
|
+
// This can happen when the list is mounted off-screen (e.g., in a
|
|
89
|
+
// background tab) and all measurements come back as 0, which would
|
|
90
|
+
// incorrectly trigger onEndReached/onStartReached.
|
|
91
|
+
if (visibleLength <= 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
87
95
|
// Check if we're near the end of the list
|
|
88
96
|
if (onEndReached) {
|
|
89
97
|
const onEndReachedThreshold = onEndReachedThresholdProp ?? 0.5;
|
|
@@ -136,6 +136,16 @@ export function useRecyclerViewController<T>(
|
|
|
136
136
|
const hasDataChanged = currentDataLength !== lastDataLengthRef.current;
|
|
137
137
|
// If we have a tracked first visible item, maintain its position
|
|
138
138
|
if (firstVisibleItemKey.current) {
|
|
139
|
+
// Try engaged indices first (fast O(n) over rendered items), then
|
|
140
|
+
// fall back to a full data search. The fallback is needed when:
|
|
141
|
+
// 1. Data just changed (hasDataChanged) — prepended items shift
|
|
142
|
+
// the anchor to a new index not yet in the engaged window.
|
|
143
|
+
// 2. A previous correction is still settling (ignoreScrollEvents
|
|
144
|
+
// is true) — after the first correction pass, unmeasured item
|
|
145
|
+
// heights may converge and push the anchor item outside the
|
|
146
|
+
// engaged window. ignoreScrollEvents stays true for 100ms
|
|
147
|
+
// after each correction, giving subsequent layout passes a
|
|
148
|
+
// chance to refine the scroll position.
|
|
139
149
|
const currentIndexOfFirstVisibleItem =
|
|
140
150
|
recyclerViewManager
|
|
141
151
|
.getEngagedIndices()
|
|
@@ -144,7 +154,7 @@ export function useRecyclerViewController<T>(
|
|
|
144
154
|
recyclerViewManager.getDataKey(index) ===
|
|
145
155
|
firstVisibleItemKey.current
|
|
146
156
|
) ??
|
|
147
|
-
(hasDataChanged
|
|
157
|
+
(hasDataChanged || recyclerViewManager.ignoreScrollEvents
|
|
148
158
|
? data?.findIndex(
|
|
149
159
|
(item, index) =>
|
|
150
160
|
recyclerViewManager.getDataKey(index) ===
|
|
@@ -5,6 +5,7 @@ import { RecyclerViewProps } from "../RecyclerViewProps";
|
|
|
5
5
|
import { getValidComponent, isComponentClass } from "../utils/componentUtils";
|
|
6
6
|
import { CompatView } from "../components/CompatView";
|
|
7
7
|
import { CompatAnimatedScroller } from "../components/CompatScroller";
|
|
8
|
+
import { getInvertedTransformStyle } from "../utils/getInvertedTransformStyle";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Hook that manages secondary props and components for the RecyclerView.
|
|
@@ -30,6 +31,7 @@ export function useSecondaryProps<T>(props: RecyclerViewProps<T>) {
|
|
|
30
31
|
ListFooterComponent,
|
|
31
32
|
ListFooterComponentStyle,
|
|
32
33
|
ListEmptyComponent,
|
|
34
|
+
ListEmptyComponentStyle,
|
|
33
35
|
renderScrollComponent,
|
|
34
36
|
refreshing,
|
|
35
37
|
progressViewOffset,
|
|
@@ -37,8 +39,14 @@ export function useSecondaryProps<T>(props: RecyclerViewProps<T>) {
|
|
|
37
39
|
data,
|
|
38
40
|
refreshControl: customRefreshControl,
|
|
39
41
|
stickyHeaderConfig,
|
|
42
|
+
inverted,
|
|
43
|
+
horizontal,
|
|
40
44
|
} = props;
|
|
41
45
|
|
|
46
|
+
const invertedTransformStyle = inverted
|
|
47
|
+
? getInvertedTransformStyle(horizontal)
|
|
48
|
+
: undefined;
|
|
49
|
+
|
|
42
50
|
/**
|
|
43
51
|
* Creates the refresh control component if onRefresh is provided.
|
|
44
52
|
*/
|
|
@@ -65,11 +73,11 @@ export function useSecondaryProps<T>(props: RecyclerViewProps<T>) {
|
|
|
65
73
|
return null;
|
|
66
74
|
}
|
|
67
75
|
return (
|
|
68
|
-
<CompatView style={ListHeaderComponentStyle}>
|
|
76
|
+
<CompatView style={[ListHeaderComponentStyle, invertedTransformStyle]}>
|
|
69
77
|
{getValidComponent(ListHeaderComponent)}
|
|
70
78
|
</CompatView>
|
|
71
79
|
);
|
|
72
|
-
}, [ListHeaderComponent, ListHeaderComponentStyle]);
|
|
80
|
+
}, [ListHeaderComponent, ListHeaderComponentStyle, invertedTransformStyle]);
|
|
73
81
|
|
|
74
82
|
/**
|
|
75
83
|
* Creates the footer component with optional styling.
|
|
@@ -79,11 +87,11 @@ export function useSecondaryProps<T>(props: RecyclerViewProps<T>) {
|
|
|
79
87
|
return null;
|
|
80
88
|
}
|
|
81
89
|
return (
|
|
82
|
-
<CompatView style={ListFooterComponentStyle}>
|
|
90
|
+
<CompatView style={[ListFooterComponentStyle, invertedTransformStyle]}>
|
|
83
91
|
{getValidComponent(ListFooterComponent)}
|
|
84
92
|
</CompatView>
|
|
85
93
|
);
|
|
86
|
-
}, [ListFooterComponent, ListFooterComponentStyle]);
|
|
94
|
+
}, [ListFooterComponent, ListFooterComponentStyle, invertedTransformStyle]);
|
|
87
95
|
|
|
88
96
|
/**
|
|
89
97
|
* Creates the empty state component when there's no data.
|
|
@@ -93,8 +101,21 @@ export function useSecondaryProps<T>(props: RecyclerViewProps<T>) {
|
|
|
93
101
|
if (!ListEmptyComponent || (data && data.length > 0)) {
|
|
94
102
|
return null;
|
|
95
103
|
}
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
const emptyContent = getValidComponent(ListEmptyComponent);
|
|
105
|
+
if (!invertedTransformStyle && !ListEmptyComponentStyle) {
|
|
106
|
+
return emptyContent;
|
|
107
|
+
}
|
|
108
|
+
return (
|
|
109
|
+
<CompatView style={[ListEmptyComponentStyle, invertedTransformStyle]}>
|
|
110
|
+
{emptyContent}
|
|
111
|
+
</CompatView>
|
|
112
|
+
);
|
|
113
|
+
}, [
|
|
114
|
+
ListEmptyComponent,
|
|
115
|
+
data,
|
|
116
|
+
invertedTransformStyle,
|
|
117
|
+
ListEmptyComponentStyle,
|
|
118
|
+
]);
|
|
98
119
|
|
|
99
120
|
/**
|
|
100
121
|
* Creates the sticky header backdrop component.
|
|
@@ -105,16 +126,19 @@ export function useSecondaryProps<T>(props: RecyclerViewProps<T>) {
|
|
|
105
126
|
}
|
|
106
127
|
return (
|
|
107
128
|
<CompatView
|
|
108
|
-
style={
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
129
|
+
style={[
|
|
130
|
+
{
|
|
131
|
+
position: "absolute",
|
|
132
|
+
inset: 0,
|
|
133
|
+
pointerEvents: "none",
|
|
134
|
+
},
|
|
135
|
+
invertedTransformStyle,
|
|
136
|
+
]}
|
|
113
137
|
>
|
|
114
138
|
{getValidComponent(stickyHeaderConfig?.backdropComponent)}
|
|
115
139
|
</CompatView>
|
|
116
140
|
);
|
|
117
|
-
}, [stickyHeaderConfig?.backdropComponent]);
|
|
141
|
+
}, [stickyHeaderConfig?.backdropComponent, invertedTransformStyle]);
|
|
118
142
|
|
|
119
143
|
/**
|
|
120
144
|
* Creates an animated scroll component based on the provided renderScrollComponent.
|