@shopify/flash-list 1.8.0 → 2.0.0-alpha.2

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 (170) hide show
  1. package/README.md +147 -26
  2. package/dist/FlashListProps.d.ts +65 -2
  3. package/dist/FlashListProps.d.ts.map +1 -1
  4. package/dist/__tests__/AverageWindow.test.js +35 -0
  5. package/dist/__tests__/AverageWindow.test.js.map +1 -1
  6. package/dist/enableNewCore.d.ts +3 -0
  7. package/dist/enableNewCore.d.ts.map +1 -0
  8. package/dist/enableNewCore.js +25 -0
  9. package/dist/enableNewCore.js.map +1 -0
  10. package/dist/index.d.ts +5 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +28 -8
  13. package/dist/index.js.map +1 -1
  14. package/dist/recyclerview/RecycleKeyManager.d.ts +82 -0
  15. package/dist/recyclerview/RecycleKeyManager.d.ts.map +1 -0
  16. package/dist/recyclerview/RecycleKeyManager.js +135 -0
  17. package/dist/recyclerview/RecycleKeyManager.js.map +1 -0
  18. package/dist/recyclerview/RecyclerView.d.ts +12 -0
  19. package/dist/recyclerview/RecyclerView.d.ts.map +1 -0
  20. package/dist/recyclerview/RecyclerView.js +283 -0
  21. package/dist/recyclerview/RecyclerView.js.map +1 -0
  22. package/dist/recyclerview/RecyclerViewContextProvider.d.ts +12 -0
  23. package/dist/recyclerview/RecyclerViewContextProvider.d.ts.map +1 -0
  24. package/dist/recyclerview/RecyclerViewContextProvider.js +11 -0
  25. package/dist/recyclerview/RecyclerViewContextProvider.js.map +1 -0
  26. package/dist/recyclerview/RecyclerViewManager.d.ts +52 -0
  27. package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -0
  28. package/dist/recyclerview/RecyclerViewManager.js +323 -0
  29. package/dist/recyclerview/RecyclerViewManager.js.map +1 -0
  30. package/dist/recyclerview/RecyclerViewProps.d.ts +9 -0
  31. package/dist/recyclerview/RecyclerViewProps.d.ts.map +1 -0
  32. package/dist/recyclerview/RecyclerViewProps.js +3 -0
  33. package/dist/recyclerview/RecyclerViewProps.js.map +1 -0
  34. package/dist/recyclerview/ViewHolder.d.ts +45 -0
  35. package/dist/recyclerview/ViewHolder.d.ts.map +1 -0
  36. package/dist/recyclerview/ViewHolder.js +96 -0
  37. package/dist/recyclerview/ViewHolder.js.map +1 -0
  38. package/dist/recyclerview/ViewHolderCollection.d.ts +57 -0
  39. package/dist/recyclerview/ViewHolderCollection.d.ts.map +1 -0
  40. package/dist/recyclerview/ViewHolderCollection.js +75 -0
  41. package/dist/recyclerview/ViewHolderCollection.js.map +1 -0
  42. package/dist/recyclerview/components/CompatScroller.d.ts +7 -0
  43. package/dist/recyclerview/components/CompatScroller.d.ts.map +1 -0
  44. package/dist/recyclerview/components/CompatScroller.js +8 -0
  45. package/dist/recyclerview/components/CompatScroller.js.map +1 -0
  46. package/dist/recyclerview/components/CompatView.d.ts +7 -0
  47. package/dist/recyclerview/components/CompatView.d.ts.map +1 -0
  48. package/dist/recyclerview/components/CompatView.js +8 -0
  49. package/dist/recyclerview/components/CompatView.js.map +1 -0
  50. package/dist/recyclerview/components/ScrollAnchor.d.ts +28 -0
  51. package/dist/recyclerview/components/ScrollAnchor.d.ts.map +1 -0
  52. package/dist/recyclerview/components/ScrollAnchor.js +35 -0
  53. package/dist/recyclerview/components/ScrollAnchor.js.map +1 -0
  54. package/dist/recyclerview/components/StickyHeaders.d.ts +38 -0
  55. package/dist/recyclerview/components/StickyHeaders.d.ts.map +1 -0
  56. package/dist/recyclerview/components/StickyHeaders.js +119 -0
  57. package/dist/recyclerview/components/StickyHeaders.js.map +1 -0
  58. package/dist/recyclerview/helpers/ConsecutiveNumbers.d.ts +51 -0
  59. package/dist/recyclerview/helpers/ConsecutiveNumbers.d.ts.map +1 -0
  60. package/dist/recyclerview/helpers/ConsecutiveNumbers.js +122 -0
  61. package/dist/recyclerview/helpers/ConsecutiveNumbers.js.map +1 -0
  62. package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts +59 -0
  63. package/dist/recyclerview/helpers/EngagedIndicesTracker.d.ts.map +1 -0
  64. package/dist/recyclerview/helpers/EngagedIndicesTracker.js +138 -0
  65. package/dist/recyclerview/helpers/EngagedIndicesTracker.js.map +1 -0
  66. package/dist/recyclerview/hooks/useBoundDetection.d.ts +19 -0
  67. package/dist/recyclerview/hooks/useBoundDetection.d.ts.map +1 -0
  68. package/dist/recyclerview/hooks/useBoundDetection.js +103 -0
  69. package/dist/recyclerview/hooks/useBoundDetection.js.map +1 -0
  70. package/dist/recyclerview/hooks/useLayoutState.d.ts +12 -0
  71. package/dist/recyclerview/hooks/useLayoutState.d.ts.map +1 -0
  72. package/dist/recyclerview/hooks/useLayoutState.js +43 -0
  73. package/dist/recyclerview/hooks/useLayoutState.js.map +1 -0
  74. package/dist/recyclerview/hooks/useOnLoad.d.ts +25 -0
  75. package/dist/recyclerview/hooks/useOnLoad.d.ts.map +1 -0
  76. package/dist/recyclerview/hooks/useOnLoad.js +73 -0
  77. package/dist/recyclerview/hooks/useOnLoad.js.map +1 -0
  78. package/dist/recyclerview/hooks/useRecyclerViewController.d.ts +72 -0
  79. package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -0
  80. package/dist/recyclerview/hooks/useRecyclerViewController.js +370 -0
  81. package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -0
  82. package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts +6 -0
  83. package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts.map +1 -0
  84. package/dist/recyclerview/hooks/useRecyclerViewManager.js +27 -0
  85. package/dist/recyclerview/hooks/useRecyclerViewManager.js.map +1 -0
  86. package/dist/recyclerview/hooks/useRecyclingState.d.ts +16 -0
  87. package/dist/recyclerview/hooks/useRecyclingState.d.ts.map +1 -0
  88. package/dist/recyclerview/hooks/useRecyclingState.js +54 -0
  89. package/dist/recyclerview/hooks/useRecyclingState.js.map +1 -0
  90. package/dist/recyclerview/hooks/useSecondaryProps.d.ts +27 -0
  91. package/dist/recyclerview/hooks/useSecondaryProps.d.ts.map +1 -0
  92. package/dist/recyclerview/hooks/useSecondaryProps.js +93 -0
  93. package/dist/recyclerview/hooks/useSecondaryProps.js.map +1 -0
  94. package/dist/recyclerview/hooks/useUnmountFlag.d.ts +11 -0
  95. package/dist/recyclerview/hooks/useUnmountFlag.d.ts.map +1 -0
  96. package/dist/recyclerview/hooks/useUnmountFlag.js +28 -0
  97. package/dist/recyclerview/hooks/useUnmountFlag.js.map +1 -0
  98. package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts +65 -0
  99. package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts.map +1 -0
  100. package/dist/recyclerview/layout-managers/GridLayoutManager.js +204 -0
  101. package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -0
  102. package/dist/recyclerview/layout-managers/LayoutManager.d.ts +281 -0
  103. package/dist/recyclerview/layout-managers/LayoutManager.d.ts.map +1 -0
  104. package/dist/recyclerview/layout-managers/LayoutManager.js +250 -0
  105. package/dist/recyclerview/layout-managers/LayoutManager.js.map +1 -0
  106. package/dist/recyclerview/layout-managers/LinearLayoutManager.d.ts +52 -0
  107. package/dist/recyclerview/layout-managers/LinearLayoutManager.d.ts.map +1 -0
  108. package/dist/recyclerview/layout-managers/LinearLayoutManager.js +191 -0
  109. package/dist/recyclerview/layout-managers/LinearLayoutManager.js.map +1 -0
  110. package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts +73 -0
  111. package/dist/recyclerview/layout-managers/MasonryLayoutManager.d.ts.map +1 -0
  112. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js +274 -0
  113. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js.map +1 -0
  114. package/dist/recyclerview/utils/adjustOffsetForRTL.d.ts +12 -0
  115. package/dist/recyclerview/utils/adjustOffsetForRTL.d.ts.map +1 -0
  116. package/dist/recyclerview/utils/adjustOffsetForRTL.js +18 -0
  117. package/dist/recyclerview/utils/adjustOffsetForRTL.js.map +1 -0
  118. package/dist/recyclerview/utils/componentUtils.d.ts +19 -0
  119. package/dist/recyclerview/utils/componentUtils.d.ts.map +1 -0
  120. package/dist/recyclerview/utils/componentUtils.js +32 -0
  121. package/dist/recyclerview/utils/componentUtils.js.map +1 -0
  122. package/dist/recyclerview/utils/findVisibleIndex.d.ts +24 -0
  123. package/dist/recyclerview/utils/findVisibleIndex.d.ts.map +1 -0
  124. package/dist/recyclerview/utils/findVisibleIndex.js +82 -0
  125. package/dist/recyclerview/utils/findVisibleIndex.js.map +1 -0
  126. package/dist/recyclerview/utils/measureLayout.d.ts +56 -0
  127. package/dist/recyclerview/utils/measureLayout.d.ts.map +1 -0
  128. package/dist/recyclerview/utils/measureLayout.js +77 -0
  129. package/dist/recyclerview/utils/measureLayout.js.map +1 -0
  130. package/dist/tsconfig.tsbuildinfo +1 -1
  131. package/dist/utils/AverageWindow.d.ts +13 -0
  132. package/dist/utils/AverageWindow.d.ts.map +1 -1
  133. package/dist/utils/AverageWindow.js +30 -1
  134. package/dist/utils/AverageWindow.js.map +1 -1
  135. package/package.json +1 -1
  136. package/src/FlashListProps.ts +73 -2
  137. package/src/__tests__/AverageWindow.test.ts +49 -1
  138. package/src/enableNewCore.ts +22 -0
  139. package/src/index.ts +21 -0
  140. package/src/recyclerview/RecycleKeyManager.ts +185 -0
  141. package/src/recyclerview/RecyclerView.tsx +500 -0
  142. package/src/recyclerview/RecyclerViewContextProvider.ts +19 -0
  143. package/src/recyclerview/RecyclerViewManager.ts +379 -0
  144. package/src/recyclerview/RecyclerViewProps.ts +10 -0
  145. package/src/recyclerview/ViewHolder.tsx +173 -0
  146. package/src/recyclerview/ViewHolderCollection.tsx +164 -0
  147. package/src/recyclerview/components/CompatScroller.ts +9 -0
  148. package/src/recyclerview/components/CompatView.ts +9 -0
  149. package/src/recyclerview/components/ScrollAnchor.tsx +53 -0
  150. package/src/recyclerview/components/StickyHeaders.tsx +210 -0
  151. package/src/recyclerview/helpers/ConsecutiveNumbers.ts +120 -0
  152. package/src/recyclerview/helpers/EngagedIndicesTracker.ts +191 -0
  153. package/src/recyclerview/hooks/useBoundDetection.ts +127 -0
  154. package/src/recyclerview/hooks/useLayoutState.ts +46 -0
  155. package/src/recyclerview/hooks/useOnLoad.ts +78 -0
  156. package/src/recyclerview/hooks/useRecyclerViewController.tsx +487 -0
  157. package/src/recyclerview/hooks/useRecyclerViewManager.ts +30 -0
  158. package/src/recyclerview/hooks/useRecyclingState.ts +63 -0
  159. package/src/recyclerview/hooks/useSecondaryProps.tsx +119 -0
  160. package/src/recyclerview/hooks/useUnmountFlag.ts +26 -0
  161. package/src/recyclerview/layout-managers/GridLayoutManager.ts +215 -0
  162. package/src/recyclerview/layout-managers/LayoutManager.ts +493 -0
  163. package/src/recyclerview/layout-managers/LinearLayoutManager.ts +167 -0
  164. package/src/recyclerview/layout-managers/MasonryLayoutManager.ts +302 -0
  165. package/src/recyclerview/utils/adjustOffsetForRTL.ts +17 -0
  166. package/src/recyclerview/utils/componentUtils.ts +28 -0
  167. package/src/recyclerview/utils/findVisibleIndex.ts +94 -0
  168. package/src/recyclerview/utils/measureLayout.ts +89 -0
  169. package/src/utils/AverageWindow.ts +33 -0
  170. package/src/viewability/ViewToken.ts +1 -1
@@ -0,0 +1,500 @@
1
+ /**
2
+ * RecyclerView is a high-performance list component that efficiently renders and recycles list items.
3
+ * It's designed to handle large lists with optimal memory usage and smooth scrolling.
4
+ */
5
+ import React, {
6
+ RefObject,
7
+ useCallback,
8
+ useLayoutEffect,
9
+ useMemo,
10
+ useRef,
11
+ forwardRef,
12
+ useState,
13
+ useId,
14
+ } from "react";
15
+ import {
16
+ Animated,
17
+ I18nManager,
18
+ NativeScrollEvent,
19
+ NativeSyntheticEvent,
20
+ } from "react-native";
21
+
22
+ import { RVDimension } from "./layout-managers/LayoutManager";
23
+ import {
24
+ areDimensionsNotEqual,
25
+ measureLayout,
26
+ measureLayoutRelative,
27
+ } from "./utils/measureLayout";
28
+ import {
29
+ RecyclerViewContext,
30
+ RecyclerViewContextProvider,
31
+ useRecyclerViewContext,
32
+ } from "./RecyclerViewContextProvider";
33
+ import { useLayoutState } from "./hooks/useLayoutState";
34
+ import { useRecyclerViewManager } from "./hooks/useRecyclerViewManager";
35
+ import { RecyclerViewProps } from "./RecyclerViewProps";
36
+ import { useOnListLoad } from "./hooks/useOnLoad";
37
+ import {
38
+ ViewHolderCollection,
39
+ ViewHolderCollectionRef,
40
+ } from "./ViewHolderCollection";
41
+ import { CompatView } from "./components/CompatView";
42
+ import { CompatScroller } from "./components/CompatScroller";
43
+ import { useBoundDetection } from "./hooks/useBoundDetection";
44
+ import { adjustOffsetForRTL } from "./utils/adjustOffsetForRTL";
45
+ import { useSecondaryProps } from "./hooks/useSecondaryProps";
46
+ import { StickyHeaders, StickyHeaderRef } from "./components/StickyHeaders";
47
+ import { ScrollAnchor, ScrollAnchorRef } from "./components/ScrollAnchor";
48
+ import { useRecyclerViewController } from "./hooks/useRecyclerViewController";
49
+
50
+ /**
51
+ * Main RecyclerView component that handles list rendering, scrolling, and item recycling.
52
+ * @template T - The type of items in the list
53
+ */
54
+ const RecyclerViewComponent = <T,>(
55
+ props: RecyclerViewProps<T>,
56
+ ref: React.Ref<any>
57
+ ) => {
58
+ // Destructure props and initialize refs
59
+ const {
60
+ horizontal,
61
+ renderItem,
62
+ data,
63
+ extraData,
64
+ onLoad,
65
+ CellRendererComponent,
66
+ overrideProps,
67
+ refreshing,
68
+ onRefresh,
69
+ progressViewOffset,
70
+ ListEmptyComponent,
71
+ ListHeaderComponent,
72
+ ListHeaderComponentStyle,
73
+ ListFooterComponent,
74
+ ListFooterComponentStyle,
75
+ ItemSeparatorComponent,
76
+ renderScrollComponent,
77
+ onScroll,
78
+ disableRecycling,
79
+ style,
80
+ stickyHeaderIndices,
81
+ maintainVisibleContentPosition,
82
+ onCommitLayoutEffect,
83
+ ...rest
84
+ } = props;
85
+
86
+ // Core refs for managing scroll view, internal view, and child container
87
+ const scrollViewRef = useRef<CompatScroller>(null);
88
+ const internalViewRef = useRef<CompatView>(null);
89
+ const childContainerViewRef = useRef<CompatView>(null);
90
+ const containerViewSizeRef = useRef<RVDimension | undefined>(undefined);
91
+ const pendingChildIds = useRef<Set<string>>(new Set()).current;
92
+
93
+ // Track scroll position
94
+ const scrollY = useRef(new Animated.Value(0)).current;
95
+
96
+ // Refs for sticky headers and scroll anchoring
97
+ const stickyHeaderRef = useRef<StickyHeaderRef>(null);
98
+ const scrollAnchorRef = useRef<ScrollAnchorRef>(null);
99
+
100
+ // State for managing layout and render updates
101
+ const [_, setLayoutTreeId] = useLayoutState(0);
102
+ const [__, setRenderId] = useState(0);
103
+
104
+ // Map to store refs for each item in the list
105
+ const refHolder = useMemo(
106
+ () => new Map<number, RefObject<CompatView | null>>(),
107
+ []
108
+ );
109
+
110
+ // Initialize core RecyclerView manager and content offset management
111
+ const { recyclerViewManager } = useRecyclerViewManager(props);
112
+ const { applyContentOffset, applyInitialScrollIndex } =
113
+ useRecyclerViewController(
114
+ recyclerViewManager,
115
+ ref,
116
+ scrollViewRef,
117
+ scrollAnchorRef,
118
+ props
119
+ );
120
+
121
+ // Initialize view holder collection ref
122
+ const viewHolderCollectionRef = useRef<ViewHolderCollectionRef>(null);
123
+
124
+ // Hook to handle list loading
125
+ useOnListLoad(recyclerViewManager, onLoad);
126
+
127
+ // Hook to detect when scrolling reaches list bounds
128
+ const { checkBounds } = useBoundDetection(
129
+ recyclerViewManager,
130
+ props,
131
+ scrollViewRef
132
+ );
133
+
134
+ /**
135
+ * Initialize the RecyclerView by measuring and setting up the window size
136
+ * This effect runs when the component mounts or when layout changes
137
+ */
138
+ useLayoutEffect(() => {
139
+ if (internalViewRef.current && childContainerViewRef.current) {
140
+ // Measure the outer and inner container layouts
141
+ const outerViewLayout = measureLayout(internalViewRef.current, undefined);
142
+ const childViewLayout = measureLayoutRelative(
143
+ childContainerViewRef.current,
144
+ internalViewRef.current,
145
+ undefined
146
+ );
147
+
148
+ containerViewSizeRef.current = outerViewLayout;
149
+
150
+ // Calculate offset of first item
151
+ const firstItemOffset = horizontal
152
+ ? childViewLayout.x - outerViewLayout.x
153
+ : childViewLayout.y - outerViewLayout.y;
154
+
155
+ // Update the RecyclerView manager with window dimensions
156
+ recyclerViewManager.updateLayoutParams(
157
+ {
158
+ width: horizontal ? outerViewLayout.width : childViewLayout.width,
159
+ height: horizontal ? childViewLayout.height : outerViewLayout.height,
160
+ },
161
+ firstItemOffset
162
+ );
163
+ }
164
+ });
165
+
166
+ /**
167
+ * Effect to handle layout updates for list items
168
+ * This ensures proper positioning and recycling of items
169
+ */
170
+ useLayoutEffect(() => {
171
+ if (pendingChildIds.size > 0) {
172
+ return;
173
+ }
174
+ const layoutInfo = Array.from(refHolder, ([index, viewHolderRef]) => {
175
+ const layout = measureLayout(
176
+ viewHolderRef.current!,
177
+ recyclerViewManager.getLayout(index)
178
+ );
179
+
180
+ //comapre height with stored layout
181
+ //const storedLayout = recyclerViewManager.getLayout(index);
182
+ // if (
183
+ // storedLayout.height !== layout.height &&
184
+ // storedLayout.isHeightMeasured
185
+ // ) {
186
+ // console.log(
187
+ // "height changed",
188
+ // index,
189
+ // layout.height,
190
+ // storedLayout.height
191
+ // );
192
+ // }
193
+ return { index, dimensions: layout };
194
+ });
195
+
196
+ if (
197
+ recyclerViewManager.modifyChildrenLayout(layoutInfo, data?.length ?? 0)
198
+ ) {
199
+ // Trigger re-render if layout modifications were made
200
+ setRenderId((prev) => prev + 1);
201
+ } else {
202
+ //console.log("commitLayout");
203
+ // TODO: reduce perf impact of commitLayout
204
+ viewHolderCollectionRef.current?.commitLayout();
205
+ applyContentOffset();
206
+ }
207
+ });
208
+
209
+ /**
210
+ * Scroll event handler that manages scroll position, velocity, and RTL support
211
+ */
212
+ const onScrollHandler = useCallback(
213
+ (event: NativeSyntheticEvent<NativeScrollEvent>) => {
214
+ let velocity = event.nativeEvent.velocity;
215
+ let scrollOffset = horizontal
216
+ ? event.nativeEvent.contentOffset.x
217
+ : event.nativeEvent.contentOffset.y;
218
+
219
+ // Handle RTL (Right-to-Left) layout adjustments
220
+ if (I18nManager.isRTL && horizontal) {
221
+ scrollOffset = adjustOffsetForRTL(
222
+ scrollOffset,
223
+ event.nativeEvent.contentSize.width,
224
+ event.nativeEvent.layoutMeasurement.width
225
+ );
226
+ if (velocity) {
227
+ velocity = {
228
+ x: -velocity.x,
229
+ y: velocity.y,
230
+ };
231
+ }
232
+ }
233
+
234
+ // Update scroll position and trigger re-render if needed
235
+ if (recyclerViewManager.updateScrollOffset(scrollOffset, velocity)) {
236
+ setRenderId((prev) => prev + 1);
237
+ }
238
+
239
+ // Update sticky headers and check bounds
240
+ stickyHeaderRef.current?.reportScrollEvent(event.nativeEvent);
241
+ checkBounds();
242
+
243
+ // Record interaction and compute item visibility
244
+ recyclerViewManager.recordInteraction();
245
+ recyclerViewManager.computeItemViewability();
246
+
247
+ // Call user-provided onScroll handler
248
+ onScroll?.(event);
249
+ },
250
+ [horizontal, recyclerViewManager]
251
+ );
252
+
253
+ // Create context for child components
254
+ const recyclerViewContext: RecyclerViewContext = useMemo(() => {
255
+ return {
256
+ layout: () => {
257
+ setLayoutTreeId((prev) => prev + 1);
258
+ },
259
+ getRef: () => {
260
+ return ref;
261
+ },
262
+ getScrollViewRef: () => {
263
+ return scrollViewRef;
264
+ },
265
+ markChildLayoutAsPending: (id: string) => {
266
+ pendingChildIds.add(id);
267
+ },
268
+ unmarkChildLayoutAsPending: (id: string) => {
269
+ if (pendingChildIds.has(id)) {
270
+ pendingChildIds.delete(id);
271
+ recyclerViewContext.layout();
272
+ }
273
+ },
274
+ };
275
+ }, [setLayoutTreeId]);
276
+
277
+ const parentRecyclerViewContext = useRecyclerViewContext();
278
+ const recyclerViewId = useId();
279
+
280
+ /**
281
+ * Validates that item dimensions match the expected layout
282
+ */
283
+ const validateItemSize = useCallback(
284
+ (index: number, size: RVDimension) => {
285
+ const layout = recyclerViewManager.getLayout(index);
286
+ const width = Math.max(
287
+ Math.min(layout.width, layout.maxWidth ?? Infinity),
288
+ layout.minWidth ?? 0
289
+ );
290
+ const height = Math.max(
291
+ Math.min(layout.height, layout.maxHeight ?? Infinity),
292
+ layout.minHeight ?? 0
293
+ );
294
+ if (
295
+ areDimensionsNotEqual(width, size.width) ||
296
+ areDimensionsNotEqual(height, size.height)
297
+ ) {
298
+ // console.log(
299
+ // "invalid size",
300
+ // index,
301
+ // width,
302
+ // size.width,
303
+ // height,
304
+ // size.height
305
+ // );
306
+ // TODO: Add a warning for missing useLayoutState
307
+ recyclerViewContext.layout();
308
+ }
309
+ },
310
+ [recyclerViewManager]
311
+ );
312
+
313
+ // Get secondary props and components
314
+ const {
315
+ refreshControl,
316
+ renderHeader,
317
+ renderFooter,
318
+ renderEmpty,
319
+ CompatScrollView,
320
+ } = useSecondaryProps(props);
321
+
322
+ if (
323
+ !recyclerViewManager.getIsFirstLayoutComplete() &&
324
+ recyclerViewManager.getDataLength() > 0
325
+ ) {
326
+ parentRecyclerViewContext?.markChildLayoutAsPending(recyclerViewId);
327
+ }
328
+
329
+ // Render sticky headers if configured
330
+ const stickyHeaders = useMemo(() => {
331
+ if (
332
+ data &&
333
+ data.length > 0 &&
334
+ stickyHeaderIndices &&
335
+ stickyHeaderIndices.length > 0
336
+ ) {
337
+ return (
338
+ <StickyHeaders
339
+ stickyHeaderIndices={stickyHeaderIndices}
340
+ data={data}
341
+ renderItem={renderItem}
342
+ scrollY={scrollY}
343
+ stickyHeaderRef={stickyHeaderRef}
344
+ recyclerViewManager={recyclerViewManager}
345
+ extraData={extraData}
346
+ />
347
+ );
348
+ }
349
+ return null;
350
+ }, [data, stickyHeaderIndices, renderItem, extraData]);
351
+
352
+ // Set up scroll event handling with animation support for sticky headers
353
+ const animatedEvent = useMemo(() => {
354
+ if (stickyHeaders) {
355
+ return Animated.event(
356
+ [{ nativeEvent: { contentOffset: { y: scrollY } } }],
357
+ { useNativeDriver: true, listener: onScrollHandler }
358
+ );
359
+ }
360
+ return onScrollHandler;
361
+ }, [onScrollHandler, stickyHeaders]);
362
+
363
+ const maintainVisibleContentPositionInternal = useMemo(() => {
364
+ if (maintainVisibleContentPosition?.disabled || horizontal) {
365
+ return undefined;
366
+ } else {
367
+ return {
368
+ ...maintainVisibleContentPosition,
369
+ minIndexForVisible: 0,
370
+ };
371
+ }
372
+ }, [maintainVisibleContentPosition]);
373
+
374
+ const shouldRenderFromBottom =
375
+ maintainVisibleContentPositionInternal?.startRenderingFromBottom ?? false;
376
+
377
+ // Calculate minimum height adjustment for bottom rendering
378
+ const adjustmentMinHeight = recyclerViewManager.hasLayout()
379
+ ? Math.max(
380
+ 0,
381
+ recyclerViewManager.getWindowSize().height -
382
+ recyclerViewManager.getChildContainerDimensions().height -
383
+ recyclerViewManager.firstItemOffset
384
+ )
385
+ : 0;
386
+
387
+ // Create view for measuring bounded size
388
+ const viewToMeasureBoundedSize = useMemo(() => {
389
+ return (
390
+ <CompatView
391
+ style={{
392
+ height: horizontal ? undefined : 0,
393
+ width: horizontal ? 0 : undefined,
394
+ minHeight: shouldRenderFromBottom ? adjustmentMinHeight : undefined,
395
+ }}
396
+ ref={childContainerViewRef}
397
+ />
398
+ );
399
+ }, [horizontal, shouldRenderFromBottom, adjustmentMinHeight]);
400
+
401
+ //console.log("render");
402
+
403
+ // Render the main RecyclerView structure
404
+ return (
405
+ <RecyclerViewContextProvider value={recyclerViewContext}>
406
+ <CompatView
407
+ style={{ flex: horizontal ? undefined : 1, ...style }}
408
+ ref={internalViewRef}
409
+ collapsable={false}
410
+ onLayout={(event) => {
411
+ if (
412
+ areDimensionsNotEqual(
413
+ event.nativeEvent.layout.width,
414
+ containerViewSizeRef.current?.width ?? 0
415
+ ) ||
416
+ areDimensionsNotEqual(
417
+ event.nativeEvent.layout.height,
418
+ containerViewSizeRef.current?.height ?? 0
419
+ )
420
+ ) {
421
+ // console.log(
422
+ // "onLayout",
423
+
424
+ // recyclerViewManager.getWindowSize(),
425
+ // event.nativeEvent.layout
426
+ // );
427
+ recyclerViewContext.layout();
428
+ }
429
+ }}
430
+ >
431
+ <CompatScrollView
432
+ {...rest}
433
+ horizontal={horizontal}
434
+ ref={scrollViewRef}
435
+ onScroll={animatedEvent}
436
+ // TODO: evaluate perf
437
+ maintainVisibleContentPosition={
438
+ maintainVisibleContentPositionInternal
439
+ }
440
+ refreshControl={refreshControl}
441
+ {...overrideProps}
442
+ >
443
+ {/* Scroll anchor for maintaining content position */}
444
+ {maintainVisibleContentPositionInternal && (
445
+ <ScrollAnchor scrollAnchorRef={scrollAnchorRef} />
446
+ )}
447
+ {renderHeader}
448
+ {viewToMeasureBoundedSize}
449
+ {/* Main list content */}
450
+ <ViewHolderCollection
451
+ viewHolderCollectionRef={viewHolderCollectionRef}
452
+ data={data}
453
+ horizontal={horizontal}
454
+ renderStack={recyclerViewManager.getRenderStack()}
455
+ getLayout={(index) => recyclerViewManager.getLayout(index)}
456
+ refHolder={refHolder}
457
+ onSizeChanged={validateItemSize}
458
+ renderItem={renderItem}
459
+ extraData={extraData}
460
+ onCommitLayoutEffect={() => {
461
+ applyInitialScrollIndex();
462
+ parentRecyclerViewContext?.unmarkChildLayoutAsPending(
463
+ recyclerViewId
464
+ );
465
+ onCommitLayoutEffect?.();
466
+ }}
467
+ onCommitEffect={() => {
468
+ applyInitialScrollIndex();
469
+ checkBounds();
470
+ recyclerViewManager.computeItemViewability();
471
+ recyclerViewManager.disableRecycling = Boolean(disableRecycling);
472
+ }}
473
+ CellRendererComponent={CellRendererComponent}
474
+ ItemSeparatorComponent={ItemSeparatorComponent}
475
+ getChildContainerLayout={() =>
476
+ recyclerViewManager.hasLayout()
477
+ ? recyclerViewManager.getChildContainerDimensions()
478
+ : undefined
479
+ }
480
+ />
481
+ {renderEmpty}
482
+ {renderFooter}
483
+ </CompatScrollView>
484
+ {stickyHeaders}
485
+ </CompatView>
486
+ </RecyclerViewContextProvider>
487
+ );
488
+ };
489
+
490
+ // Type definition for the RecyclerView component
491
+ type RecyclerViewType = <T>(
492
+ props: RecyclerViewProps<T> & { ref?: React.Ref<any> }
493
+ ) => React.JSX.Element;
494
+
495
+ // Create and export the memoized, forwarded ref component
496
+ const RecyclerView = React.memo(
497
+ forwardRef(RecyclerViewComponent)
498
+ ) as RecyclerViewType;
499
+
500
+ export { RecyclerView };
@@ -0,0 +1,19 @@
1
+ import { createContext, useContext } from "react";
2
+ import { CompatScroller } from "./components/CompatScroller";
3
+
4
+ export interface RecyclerViewContext {
5
+ layout: () => void;
6
+ getRef: () => React.Ref<any>;
7
+ getScrollViewRef: () => React.RefObject<CompatScroller | null>;
8
+ markChildLayoutAsPending: (id: string) => void;
9
+ unmarkChildLayoutAsPending: (id: string) => void;
10
+ }
11
+
12
+ const RecyclerViewContextInstance = createContext<
13
+ RecyclerViewContext | undefined
14
+ >(undefined);
15
+
16
+ export const RecyclerViewContextProvider = RecyclerViewContextInstance.Provider;
17
+ export function useRecyclerViewContext() {
18
+ return useContext(RecyclerViewContextInstance);
19
+ }