@shopify/flash-list 2.0.0-rc.2 → 2.0.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FlashListProps.d.ts +10 -2
- package/dist/FlashListProps.d.ts.map +1 -1
- package/dist/FlashListProps.js.map +1 -1
- package/dist/__tests__/RenderStackManager.test.js +1 -2
- package/dist/__tests__/RenderStackManager.test.js.map +1 -1
- package/dist/recyclerview/RecyclerView.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerView.js +11 -3
- package/dist/recyclerview/RecyclerView.js.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.d.ts +1 -0
- package/dist/recyclerview/RecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/RecyclerViewManager.js +5 -0
- package/dist/recyclerview/RecyclerViewManager.js.map +1 -1
- package/dist/recyclerview/RenderStackManager.d.ts +1 -0
- package/dist/recyclerview/RenderStackManager.d.ts.map +1 -1
- package/dist/recyclerview/RenderStackManager.js +26 -7
- package/dist/recyclerview/RenderStackManager.js.map +1 -1
- package/dist/recyclerview/helpers/RenderTimeTracker.d.ts +1 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.d.ts.map +1 -1
- package/dist/recyclerview/helpers/RenderTimeTracker.js +3 -0
- package/dist/recyclerview/helpers/RenderTimeTracker.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/useRecyclerViewController.d.ts +2 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewController.js +237 -190
- package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.d.ts.map +1 -1
- package/dist/recyclerview/hooks/useRecyclerViewManager.js +2 -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/layout-managers/GridLayoutManager.d.ts +14 -6
- package/dist/recyclerview/layout-managers/GridLayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/GridLayoutManager.js +40 -23
- package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts +26 -6
- package/dist/recyclerview/layout-managers/LayoutManager.d.ts.map +1 -1
- package/dist/recyclerview/layout-managers/LayoutManager.js +89 -15
- 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/tsconfig.tsbuildinfo +1 -1
- package/dist/viewability/ViewabilityManager.d.ts.map +1 -1
- package/dist/viewability/ViewabilityManager.js +10 -3
- package/dist/viewability/ViewabilityManager.js.map +1 -1
- package/package.json +1 -1
- package/src/FlashListProps.ts +16 -2
- package/src/__tests__/RenderStackManager.test.ts +1 -2
- package/src/recyclerview/RecyclerView.tsx +27 -14
- package/src/recyclerview/RecyclerViewManager.ts +6 -0
- package/src/recyclerview/RenderStackManager.ts +32 -6
- package/src/recyclerview/helpers/RenderTimeTracker.ts +4 -0
- package/src/recyclerview/hooks/useLayoutState.ts +15 -6
- package/src/recyclerview/hooks/useRecyclerViewController.tsx +240 -168
- package/src/recyclerview/hooks/useRecyclerViewManager.ts +3 -1
- package/src/recyclerview/hooks/useRecyclingState.ts +11 -7
- package/src/recyclerview/layout-managers/GridLayoutManager.ts +44 -23
- package/src/recyclerview/layout-managers/LayoutManager.ts +98 -20
- package/src/recyclerview/layout-managers/MasonryLayoutManager.ts +30 -8
- package/src/viewability/ViewabilityManager.ts +10 -6
|
@@ -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,13 +79,21 @@ 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 totalHeight = this.
|
|
96
|
+
const totalHeight = this.computeTotalHeightTillRow(this.layouts.length - 1);
|
|
82
97
|
return {
|
|
83
98
|
width: this.boundedSize,
|
|
84
99
|
height: totalHeight,
|
|
@@ -91,7 +106,7 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
91
106
|
* @param endIndex Ending index of items to recompute
|
|
92
107
|
*/
|
|
93
108
|
recomputeLayouts(startIndex: number, endIndex: number): void {
|
|
94
|
-
const newStartIndex = this.
|
|
109
|
+
const newStartIndex = this.locateFirstIndexInRow(
|
|
95
110
|
Math.max(0, startIndex - 1)
|
|
96
111
|
);
|
|
97
112
|
const startVal = this.getLayout(newStartIndex);
|
|
@@ -122,25 +137,23 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
122
137
|
* @returns Width of the item
|
|
123
138
|
*/
|
|
124
139
|
private getWidth(index: number): number {
|
|
125
|
-
|
|
126
|
-
return (this.boundedSize / this.maxColumns) * span;
|
|
140
|
+
return (this.boundedSize / this.maxColumns) * this.getSpan(index);
|
|
127
141
|
}
|
|
128
142
|
|
|
129
143
|
/**
|
|
130
144
|
* Processes items in a row and returns the tallest item.
|
|
131
145
|
* Also handles height normalization for items in the same row.
|
|
132
146
|
* Tallest item per row helps in forcing tallest items height on neighbouring items.
|
|
133
|
-
* @param
|
|
147
|
+
* @param endIndex Index of the last item in the row
|
|
134
148
|
* @returns The tallest item in the row
|
|
135
149
|
*/
|
|
136
|
-
private processAndReturnTallestItemInRow(
|
|
137
|
-
const startIndex = this.
|
|
138
|
-
const y = this.layouts[startIndex].y;
|
|
150
|
+
private processAndReturnTallestItemInRow(endIndex: number): RVLayout {
|
|
151
|
+
const startIndex = this.locateFirstIndexInRow(endIndex);
|
|
139
152
|
let tallestItem: RVLayout | undefined;
|
|
140
153
|
let maxHeight = 0;
|
|
141
154
|
let i = startIndex;
|
|
142
155
|
let isMeasured = false;
|
|
143
|
-
while (
|
|
156
|
+
while (i <= endIndex) {
|
|
144
157
|
const layout = this.layouts[i];
|
|
145
158
|
isMeasured = isMeasured || Boolean(layout.isHeightMeasured);
|
|
146
159
|
maxHeight = Math.max(maxHeight, layout.height);
|
|
@@ -156,7 +169,9 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
156
169
|
break;
|
|
157
170
|
}
|
|
158
171
|
}
|
|
159
|
-
|
|
172
|
+
if (!tallestItem && maxHeight > 0) {
|
|
173
|
+
maxHeight = Number.MAX_SAFE_INTEGER;
|
|
174
|
+
}
|
|
160
175
|
tallestItem = tallestItem ?? this.layouts[startIndex];
|
|
161
176
|
|
|
162
177
|
if (!isMeasured) {
|
|
@@ -170,7 +185,7 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
170
185
|
this.requiresRepaint = true;
|
|
171
186
|
}
|
|
172
187
|
i = startIndex;
|
|
173
|
-
while (
|
|
188
|
+
while (i <= endIndex) {
|
|
174
189
|
this.layouts[i].minHeight = targetHeight;
|
|
175
190
|
if (targetHeight > 0) {
|
|
176
191
|
this.layouts[i].height = targetHeight;
|
|
@@ -187,15 +202,15 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
187
202
|
|
|
188
203
|
/**
|
|
189
204
|
* Computes the total height of the layout.
|
|
190
|
-
* @param
|
|
205
|
+
* @param endIndex Index of the last item in the row
|
|
191
206
|
* @returns Total height of the layout
|
|
192
207
|
*/
|
|
193
|
-
private
|
|
194
|
-
const startIndex = this.
|
|
208
|
+
private computeTotalHeightTillRow(endIndex: number): number {
|
|
209
|
+
const startIndex = this.locateFirstIndexInRow(endIndex);
|
|
195
210
|
const y = this.layouts[startIndex].y;
|
|
196
211
|
let maxHeight = 0;
|
|
197
212
|
let i = startIndex;
|
|
198
|
-
while (
|
|
213
|
+
while (i <= endIndex) {
|
|
199
214
|
maxHeight = Math.max(maxHeight, this.layouts[i].height);
|
|
200
215
|
i++;
|
|
201
216
|
if (i >= this.layouts.length) {
|
|
@@ -205,6 +220,12 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
205
220
|
return y + maxHeight;
|
|
206
221
|
}
|
|
207
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
|
+
|
|
208
229
|
/**
|
|
209
230
|
* Checks if an item can fit within the bounded width.
|
|
210
231
|
* @param itemX Starting X position of the item
|
|
@@ -217,14 +238,14 @@ export class RVGridLayoutManagerImpl extends RVLayoutManager {
|
|
|
217
238
|
|
|
218
239
|
/**
|
|
219
240
|
* Locates the index of the first item in the current row.
|
|
220
|
-
* @param
|
|
241
|
+
* @param itemIndex Index to start searching from
|
|
221
242
|
* @returns Index of the first item in the row
|
|
222
243
|
*/
|
|
223
|
-
private
|
|
224
|
-
if (
|
|
244
|
+
private locateFirstIndexInRow(itemIndex: number): number {
|
|
245
|
+
if (itemIndex === 0) {
|
|
225
246
|
return 0;
|
|
226
247
|
}
|
|
227
|
-
let i =
|
|
248
|
+
let i = itemIndex;
|
|
228
249
|
for (; i >= 0; i--) {
|
|
229
250
|
if (this.layouts[i].x === 0) {
|
|
230
251
|
break;
|
|
@@ -20,8 +20,6 @@ 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
25
|
|
|
@@ -40,7 +38,20 @@ export abstract class RVLayoutManager {
|
|
|
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);
|
|
@@ -148,7 +159,7 @@ export abstract class RVLayoutManager {
|
|
|
148
159
|
}
|
|
149
160
|
const startIndex = Math.min(...indices);
|
|
150
161
|
// Recompute layouts starting from the smallest index in the original indices array
|
|
151
|
-
this.
|
|
162
|
+
this._recomputeLayouts(
|
|
152
163
|
this.getMinRecomputeIndex(startIndex),
|
|
153
164
|
this.getMaxRecomputeIndex(startIndex)
|
|
154
165
|
);
|
|
@@ -160,41 +171,49 @@ export abstract class RVLayoutManager {
|
|
|
160
171
|
* @param totalItemCount Total number of items in the list
|
|
161
172
|
*/
|
|
162
173
|
modifyLayout(layoutInfo: RVLayoutInfo[], totalItemCount: number): void {
|
|
174
|
+
this.maxItemsToProcess = Math.max(
|
|
175
|
+
this.maxItemsToProcess,
|
|
176
|
+
layoutInfo.length * 10
|
|
177
|
+
);
|
|
163
178
|
let minRecomputeIndex = Number.MAX_VALUE;
|
|
164
179
|
|
|
165
180
|
if (this.layouts.length > totalItemCount) {
|
|
166
181
|
this.layouts.length = totalItemCount;
|
|
182
|
+
this.spanTracker.length = totalItemCount;
|
|
167
183
|
minRecomputeIndex = totalItemCount - 1; // <0 gets skipped so it's safe to set to totalItemCount - 1
|
|
168
184
|
}
|
|
169
185
|
// update average windows
|
|
170
186
|
minRecomputeIndex = Math.min(
|
|
171
187
|
minRecomputeIndex,
|
|
172
|
-
this.
|
|
188
|
+
this.computeEstimatesAndMinMaxChangedLayout(layoutInfo)
|
|
173
189
|
);
|
|
174
190
|
|
|
175
191
|
if (this.layouts.length < totalItemCount && totalItemCount > 0) {
|
|
176
192
|
const startIndex = this.layouts.length;
|
|
177
193
|
this.layouts.length = totalItemCount;
|
|
194
|
+
this.spanTracker.length = totalItemCount;
|
|
178
195
|
for (let i = startIndex; i < totalItemCount; i++) {
|
|
179
196
|
this.getLayout(i);
|
|
197
|
+
this.getSpan(i);
|
|
180
198
|
}
|
|
181
199
|
this.recomputeLayouts(startIndex, totalItemCount - 1);
|
|
182
200
|
}
|
|
183
|
-
|
|
184
|
-
minRecomputeIndex,
|
|
185
|
-
this.processLayoutInfo(layoutInfo, totalItemCount) ?? minRecomputeIndex
|
|
186
|
-
);
|
|
201
|
+
|
|
187
202
|
// compute minRecomputeIndex
|
|
203
|
+
|
|
188
204
|
minRecomputeIndex = Math.min(
|
|
189
205
|
minRecomputeIndex,
|
|
190
|
-
this.
|
|
206
|
+
this.lastSkippedLayoutIndex,
|
|
207
|
+
this.computeMinIndexWithChangedSpan(layoutInfo),
|
|
208
|
+
this.processLayoutInfo(layoutInfo, totalItemCount) ?? minRecomputeIndex,
|
|
209
|
+
this.computeEstimatesAndMinMaxChangedLayout(layoutInfo)
|
|
191
210
|
);
|
|
211
|
+
|
|
192
212
|
if (minRecomputeIndex >= 0 && minRecomputeIndex < totalItemCount) {
|
|
193
|
-
this.
|
|
194
|
-
|
|
195
|
-
this.getMaxRecomputeIndex(minRecomputeIndex)
|
|
196
|
-
);
|
|
213
|
+
const maxRecomputeIndex = this.getMaxRecomputeIndex(minRecomputeIndex);
|
|
214
|
+
this._recomputeLayouts(minRecomputeIndex, maxRecomputeIndex);
|
|
197
215
|
}
|
|
216
|
+
this.currentMaxIndexWithChangedLayout = -1;
|
|
198
217
|
}
|
|
199
218
|
|
|
200
219
|
/**
|
|
@@ -260,16 +279,30 @@ export abstract class RVLayoutManager {
|
|
|
260
279
|
protected abstract estimateLayout(index: number): void;
|
|
261
280
|
|
|
262
281
|
/**
|
|
263
|
-
* 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.
|
|
264
287
|
* @param index Index of the item
|
|
265
|
-
* @returns
|
|
288
|
+
* @returns Span for the item
|
|
266
289
|
*/
|
|
267
|
-
protected
|
|
290
|
+
protected getSpan(index: number, skipTracking = false): number {
|
|
268
291
|
this.spanSizeInfo.span = undefined;
|
|
269
292
|
this.overrideItemLayout(index, this.spanSizeInfo);
|
|
270
|
-
|
|
293
|
+
const span = Math.min(this.spanSizeInfo.span ?? 1, this.maxColumns);
|
|
294
|
+
if (!skipTracking) {
|
|
295
|
+
this.spanTracker[index] = span;
|
|
296
|
+
}
|
|
297
|
+
return span;
|
|
271
298
|
}
|
|
272
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
|
+
|
|
273
306
|
/**
|
|
274
307
|
* Gets the maximum index to process in a single layout pass.
|
|
275
308
|
* @param startIndex Starting index
|
|
@@ -277,7 +310,8 @@ export abstract class RVLayoutManager {
|
|
|
277
310
|
*/
|
|
278
311
|
private getMaxRecomputeIndex(startIndex: number): number {
|
|
279
312
|
return Math.min(
|
|
280
|
-
startIndex
|
|
313
|
+
Math.max(startIndex, this.currentMaxIndexWithChangedLayout) +
|
|
314
|
+
this.maxItemsToProcess,
|
|
281
315
|
this.layouts.length - 1
|
|
282
316
|
);
|
|
283
317
|
}
|
|
@@ -291,12 +325,36 @@ export abstract class RVLayoutManager {
|
|
|
291
325
|
return startIndex;
|
|
292
326
|
}
|
|
293
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
|
+
|
|
294
352
|
/**
|
|
295
353
|
* Computes size estimates and finds the minimum recompute index.
|
|
296
354
|
* @param layoutInfo Array of layout information for items
|
|
297
355
|
* @returns Minimum index that needs recomputation
|
|
298
356
|
*/
|
|
299
|
-
private
|
|
357
|
+
private computeEstimatesAndMinMaxChangedLayout(
|
|
300
358
|
layoutInfo: RVLayoutInfo[]
|
|
301
359
|
): number {
|
|
302
360
|
let minRecomputeIndex = Number.MAX_VALUE;
|
|
@@ -304,6 +362,7 @@ export abstract class RVLayoutManager {
|
|
|
304
362
|
const { index, dimensions } = info;
|
|
305
363
|
const storedLayout = this.layouts[index];
|
|
306
364
|
if (
|
|
365
|
+
index >= this.lastSkippedLayoutIndex ||
|
|
307
366
|
!storedLayout ||
|
|
308
367
|
!storedLayout.isHeightMeasured ||
|
|
309
368
|
!storedLayout.isWidthMeasured ||
|
|
@@ -311,6 +370,10 @@ export abstract class RVLayoutManager {
|
|
|
311
370
|
areDimensionsNotEqual(storedLayout.width, dimensions.width)
|
|
312
371
|
) {
|
|
313
372
|
minRecomputeIndex = Math.min(minRecomputeIndex, index);
|
|
373
|
+
this.currentMaxIndexWithChangedLayout = Math.max(
|
|
374
|
+
this.currentMaxIndexWithChangedLayout,
|
|
375
|
+
index
|
|
376
|
+
);
|
|
314
377
|
}
|
|
315
378
|
this.heightAverageWindow.addValue(
|
|
316
379
|
dimensions.height,
|
|
@@ -323,6 +386,21 @@ export abstract class RVLayoutManager {
|
|
|
323
386
|
}
|
|
324
387
|
return minRecomputeIndex;
|
|
325
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
|
+
}
|
|
326
404
|
}
|
|
327
405
|
|
|
328
406
|
/**
|
|
@@ -19,10 +19,13 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
|
|
|
19
19
|
/** Current column index for sequential placement */
|
|
20
20
|
private currentColumn = 0;
|
|
21
21
|
|
|
22
|
+
/** If there's a span change for masonry layout, we need to recompute all the widths */
|
|
23
|
+
private fullRelayoutRequired = false;
|
|
24
|
+
|
|
22
25
|
constructor(params: LayoutParams, previousLayoutManager?: RVLayoutManager) {
|
|
23
26
|
super(params, previousLayoutManager);
|
|
24
27
|
this.boundedSize = params.windowSize.width;
|
|
25
|
-
this.optimizeItemArrangement = params.optimizeItemArrangement
|
|
28
|
+
this.optimizeItemArrangement = params.optimizeItemArrangement;
|
|
26
29
|
this.columnHeights = this.columnHeights ?? Array(this.maxColumns).fill(0);
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -44,10 +47,7 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
|
|
|
44
47
|
// console.log("-----> recomputeLayouts");
|
|
45
48
|
|
|
46
49
|
// update all widths
|
|
47
|
-
|
|
48
|
-
this.layouts[i].width = this.getWidth(i);
|
|
49
|
-
this.layouts[i].minHeight = undefined;
|
|
50
|
-
}
|
|
50
|
+
this.updateAllWidths();
|
|
51
51
|
this.recomputeLayouts(0, this.layouts.length - 1);
|
|
52
52
|
this.requiresRepaint = true;
|
|
53
53
|
}
|
|
@@ -69,6 +69,13 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
|
|
|
69
69
|
layout.isWidthMeasured = true;
|
|
70
70
|
this.layouts[index] = layout;
|
|
71
71
|
}
|
|
72
|
+
|
|
73
|
+
// TODO: Can be optimized
|
|
74
|
+
if (this.fullRelayoutRequired) {
|
|
75
|
+
this.updateAllWidths();
|
|
76
|
+
this.fullRelayoutRequired = false;
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
/**
|
|
@@ -87,6 +94,14 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
|
|
|
87
94
|
layout.enforcedWidth = true;
|
|
88
95
|
}
|
|
89
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Handles span change for an item.
|
|
99
|
+
* @param index Index of the item
|
|
100
|
+
*/
|
|
101
|
+
handleSpanChange(index: number) {
|
|
102
|
+
this.fullRelayoutRequired = true;
|
|
103
|
+
}
|
|
104
|
+
|
|
90
105
|
/**
|
|
91
106
|
* Returns the total size of the layout area.
|
|
92
107
|
* @returns RVDimension containing width and height of the layout
|
|
@@ -124,7 +139,8 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
|
|
|
124
139
|
|
|
125
140
|
for (let i = startIndex; i < itemCount; i++) {
|
|
126
141
|
const layout = this.getLayout(i);
|
|
127
|
-
|
|
142
|
+
// Skip tracking span because we're not changing widths
|
|
143
|
+
const span = this.getSpan(i, true);
|
|
128
144
|
|
|
129
145
|
if (this.optimizeItemArrangement) {
|
|
130
146
|
if (span === 1) {
|
|
@@ -147,8 +163,14 @@ export class RVMasonryLayoutManagerImpl extends RVLayoutManager {
|
|
|
147
163
|
* @returns Width of the item
|
|
148
164
|
*/
|
|
149
165
|
private getWidth(index: number): number {
|
|
150
|
-
|
|
151
|
-
|
|
166
|
+
return (this.boundedSize / this.maxColumns) * this.getSpan(index);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private updateAllWidths() {
|
|
170
|
+
for (let i = 0; i < this.layouts.length; i++) {
|
|
171
|
+
this.layouts[i].width = this.getWidth(i);
|
|
172
|
+
this.layouts[i].minHeight = undefined;
|
|
173
|
+
}
|
|
152
174
|
}
|
|
153
175
|
|
|
154
176
|
/**
|
|
@@ -22,17 +22,21 @@ export default class ViewabilityManager<T> {
|
|
|
22
22
|
this.viewabilityHelpers.push(
|
|
23
23
|
this.createViewabilityHelper(
|
|
24
24
|
flashListRef.props.viewabilityConfig,
|
|
25
|
-
|
|
25
|
+
(info) => {
|
|
26
|
+
flashListRef.props.onViewableItemsChanged?.(info);
|
|
27
|
+
}
|
|
26
28
|
)
|
|
27
29
|
);
|
|
28
30
|
}
|
|
29
31
|
(flashListRef.props.viewabilityConfigCallbackPairs ?? []).forEach(
|
|
30
|
-
(pair) => {
|
|
32
|
+
(pair, index) => {
|
|
31
33
|
this.viewabilityHelpers.push(
|
|
32
|
-
this.createViewabilityHelper(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
this.createViewabilityHelper(pair.viewabilityConfig, (info) => {
|
|
35
|
+
const callback =
|
|
36
|
+
flashListRef.props.viewabilityConfigCallbackPairs?.[index]
|
|
37
|
+
?.onViewableItemsChanged;
|
|
38
|
+
callback?.(info);
|
|
39
|
+
})
|
|
36
40
|
);
|
|
37
41
|
}
|
|
38
42
|
);
|