@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,26 @@
1
+ import { useRef, useLayoutEffect } from "react";
2
+
3
+ /**
4
+ * Hook that provides a way to track component unmounting state.
5
+ * This hook is particularly useful for preventing state updates or side effects
6
+ * after a component has unmounted, helping to avoid memory leaks and race conditions.
7
+ *
8
+ * @returns A ref containing a boolean flag that indicates whether the component is unmounted
9
+ * (true) or mounted (false)
10
+ */
11
+ export const useUnmountFlag = () => {
12
+ // Create a ref to store the unmount state
13
+ // Using ref ensures the value persists between renders without causing re-renders
14
+ const isUnmounted = useRef(false);
15
+
16
+ // Use layoutEffect to set up cleanup on unmount
17
+ // This ensures the flag is set before any other cleanup effects run
18
+ useLayoutEffect(() => {
19
+ // Cleanup function that runs when the component unmounts
20
+ return () => {
21
+ isUnmounted.current = true;
22
+ };
23
+ }, []);
24
+
25
+ return isUnmounted;
26
+ };
@@ -0,0 +1,215 @@
1
+ import {
2
+ LayoutParams,
3
+ RVDimension,
4
+ RVLayout,
5
+ RVLayoutInfo,
6
+ RVLayoutManager,
7
+ } from "./LayoutManager";
8
+
9
+ /**
10
+ * GridLayoutManager implementation that arranges items in a grid pattern.
11
+ * Items are placed in rows and columns, with support for items spanning multiple columns.
12
+ */
13
+ export class RVGridLayoutManagerImpl extends RVLayoutManager {
14
+ /** The width of the bounded area for the grid */
15
+ private boundedSize: number;
16
+
17
+ constructor(params: LayoutParams, previousLayoutManager?: RVLayoutManager) {
18
+ super(params, previousLayoutManager);
19
+ this.boundedSize = params.windowSize.width;
20
+ }
21
+
22
+ /**
23
+ * Updates layout parameters and triggers recomputation if necessary.
24
+ * @param params New layout parameters
25
+ */
26
+ updateLayoutParams(params: LayoutParams): void {
27
+ const prevNumColumns = this.maxColumns;
28
+ super.updateLayoutParams(params);
29
+ if (
30
+ this.boundedSize !== params.windowSize.width ||
31
+ prevNumColumns !== params.maxColumns
32
+ ) {
33
+ this.boundedSize = params.windowSize.width;
34
+ if (this.layouts.length > 0) {
35
+ //update all widths
36
+ for (let i = 0; i < this.layouts.length; i++) {
37
+ this.layouts[i].width = this.getWidth(i);
38
+ }
39
+ //console.log("-----> recomputeLayouts");
40
+
41
+ this.recomputeLayouts(0, this.layouts.length - 1);
42
+ this.requiresRepaint = true;
43
+ }
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Processes layout information for items, updating their dimensions.
49
+ * @param layoutInfo Array of layout information for items
50
+ * @param itemCount Total number of items in the list
51
+ */
52
+ processLayoutInfo(layoutInfo: RVLayoutInfo[], itemCount: number) {
53
+ for (const info of layoutInfo) {
54
+ const { index, dimensions } = info;
55
+ const layout = this.layouts[index];
56
+ layout.height = dimensions.height;
57
+ layout.isHeightMeasured = true;
58
+ layout.isWidthMeasured = true;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Estimates layout dimensions for an item at the given index.
64
+ * @param index Index of the item to estimate layout for
65
+ */
66
+ estimateLayout(index: number) {
67
+ const layout = this.layouts[index];
68
+ layout.width = this.getWidth(index);
69
+ layout.height = this.getEstimatedHeight(index);
70
+
71
+ layout.isWidthMeasured = true;
72
+ layout.enforcedWidth = true;
73
+ }
74
+
75
+ /**
76
+ * Returns the total size of the layout area.
77
+ * @returns RVDimension containing width and height of the layout
78
+ */
79
+ getLayoutSize(): RVDimension {
80
+ if (this.layouts.length === 0) return { width: 0, height: 0 };
81
+ const lastRowTallestItem = this.processAndReturnTallestItemInRow(
82
+ this.layouts.length - 1
83
+ );
84
+ return {
85
+ width: this.boundedSize,
86
+ height: lastRowTallestItem.y + lastRowTallestItem.height,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Recomputes layouts for items in the given range.
92
+ * @param startIndex Starting index of items to recompute
93
+ * @param endIndex Ending index of items to recompute
94
+ */
95
+ recomputeLayouts(startIndex: number, endIndex: number): void {
96
+ const newStartIndex = this.locateFirstNeighbourIndex(startIndex);
97
+ const startVal = this.getLayout(newStartIndex);
98
+
99
+ let startX = startVal.x;
100
+ let startY = startVal.y;
101
+
102
+ for (let i = newStartIndex; i <= endIndex; i++) {
103
+ const layout = this.getLayout(i);
104
+ if (!this.checkBounds(startX, layout.width)) {
105
+ const tallestItem = this.processAndReturnTallestItemInRow(i);
106
+ startY = tallestItem.y + tallestItem.height;
107
+ startX = 0;
108
+ }
109
+
110
+ layout.x = startX;
111
+ layout.y = startY;
112
+ startX += layout.width;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Calculates the width of an item based on its span.
118
+ * @param index Index of the item
119
+ * @returns Width of the item
120
+ */
121
+ private getWidth(index: number): number {
122
+ const span = this.getSpanSizeInfo(index).span ?? 1;
123
+ return (this.boundedSize / this.maxColumns) * span;
124
+ }
125
+
126
+ /**
127
+ * Processes items in a row and returns the tallest item.
128
+ * Also handles height normalization for items in the same row.
129
+ * Tallest item per row helps in forcing tallest items height on neighbouring items.
130
+ * @param index Index of the last item in the row
131
+ * @returns The tallest item in the row
132
+ */
133
+ private processAndReturnTallestItemInRow(index: number): RVLayout {
134
+ const startIndex = this.locateFirstNeighbourIndex(index);
135
+ const y = this.layouts[startIndex].y;
136
+ let tallestItem: RVLayout | undefined;
137
+ let maxHeight = 0;
138
+ let i = startIndex;
139
+ let isMeasured = false;
140
+ // TODO: Manage precision problems
141
+ while (Math.ceil(this.layouts[i].y) === Math.ceil(y)) {
142
+ const layout = this.layouts[i];
143
+ isMeasured = isMeasured || Boolean(layout.isHeightMeasured);
144
+ maxHeight = Math.max(maxHeight, layout.height);
145
+ if (
146
+ layout.height > (layout.minHeight ?? 0) &&
147
+ layout.height > (tallestItem?.height ?? 0)
148
+ ) {
149
+ tallestItem = layout;
150
+ }
151
+
152
+ i++;
153
+ if (i >= this.layouts.length) {
154
+ break;
155
+ }
156
+ }
157
+
158
+ tallestItem = tallestItem ?? this.layouts[startIndex];
159
+
160
+ if (!isMeasured) {
161
+ return tallestItem;
162
+ }
163
+
164
+ if (tallestItem) {
165
+ let targetHeight = tallestItem.height;
166
+ if (maxHeight - tallestItem.height > 1) {
167
+ targetHeight = 0;
168
+ this.requiresRepaint = true;
169
+ }
170
+ i = startIndex;
171
+ // TODO: Manage precision problems
172
+ while (Math.ceil(this.layouts[i].y) === Math.ceil(y)) {
173
+ this.layouts[i].minHeight = targetHeight;
174
+ if (targetHeight > 0) {
175
+ this.layouts[i].height = targetHeight;
176
+ }
177
+ i++;
178
+ if (i >= this.layouts.length) {
179
+ break;
180
+ }
181
+ }
182
+ tallestItem.minHeight = 0;
183
+ }
184
+ return tallestItem;
185
+ }
186
+
187
+ /**
188
+ * Checks if an item can fit within the bounded width.
189
+ * @param itemX Starting X position of the item
190
+ * @param width Width of the item
191
+ * @returns True if the item fits within bounds
192
+ */
193
+ private checkBounds(itemX: number, width: number): boolean {
194
+ // TODO: Manage precision problems
195
+ return itemX + width <= this.boundedSize + 0.9;
196
+ }
197
+
198
+ /**
199
+ * Locates the index of the first item in the current row.
200
+ * @param startIndex Index to start searching from
201
+ * @returns Index of the first item in the row
202
+ */
203
+ private locateFirstNeighbourIndex(startIndex: number): number {
204
+ if (startIndex === 0) {
205
+ return 0;
206
+ }
207
+ let i = startIndex - 1;
208
+ for (; i >= 0; i--) {
209
+ if (this.layouts[i].x === 0) {
210
+ break;
211
+ }
212
+ }
213
+ return Math.max(i, 0);
214
+ }
215
+ }