@legendapp/list 2.1.0-beta.1 → 2.1.0-beta.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/index.mjs CHANGED
@@ -33,7 +33,10 @@ function StateProvider({ children }) {
33
33
  ["stylePaddingTop", 0],
34
34
  ["headerSize", 0],
35
35
  ["numContainers", 0],
36
- ["totalSize", 0]
36
+ ["activeStickyIndex", void 0],
37
+ ["totalSize", 0],
38
+ ["scrollAdjustPending", 0],
39
+ ["scrollingTo", void 0]
37
40
  ]),
38
41
  viewRefs: /* @__PURE__ */ new Map()
39
42
  }));
@@ -187,6 +190,47 @@ var ENABLE_DEBUG_VIEW = IS_DEV && false;
187
190
  var typedForwardRef = forwardRef;
188
191
  var typedMemo = memo;
189
192
 
193
+ // src/utils/helpers.ts
194
+ function isFunction(obj) {
195
+ return typeof obj === "function";
196
+ }
197
+ function isArray(obj) {
198
+ return Array.isArray(obj);
199
+ }
200
+ var warned = /* @__PURE__ */ new Set();
201
+ function warnDevOnce(id, text) {
202
+ if (IS_DEV && !warned.has(id)) {
203
+ warned.add(id);
204
+ console.warn(`[legend-list] ${text}`);
205
+ }
206
+ }
207
+ function roundSize(size) {
208
+ return Math.floor(size * 8) / 8;
209
+ }
210
+ function isNullOrUndefined(value) {
211
+ return value === null || value === void 0;
212
+ }
213
+ function comparatorDefault(a, b) {
214
+ return a - b;
215
+ }
216
+ function getPadding(s, type) {
217
+ var _a3, _b, _c;
218
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
219
+ }
220
+ function extractPadding(style, contentContainerStyle, type) {
221
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
222
+ }
223
+ function findContainerId(ctx, key) {
224
+ const numContainers = peek$(ctx, "numContainers");
225
+ for (let i = 0; i < numContainers; i++) {
226
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
227
+ if (itemKey === key) {
228
+ return i;
229
+ }
230
+ }
231
+ return -1;
232
+ }
233
+
190
234
  // src/components/PositionView.tsx
191
235
  var PositionViewState = typedMemo(function PositionView({
192
236
  id,
@@ -196,8 +240,11 @@ var PositionViewState = typedMemo(function PositionView({
196
240
  ...rest
197
241
  }) {
198
242
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
199
- const base = Array.isArray(style) ? Object.assign({}, ...style) : style;
200
- const combinedStyle = horizontal ? { ...base, left: position } : { ...base, top: position };
243
+ const base = {
244
+ contain: "paint layout style"
245
+ };
246
+ const composed = isArray(style) ? Object.assign({}, ...style) : style;
247
+ const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
201
248
  return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: combinedStyle, ...rest });
202
249
  });
203
250
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
@@ -206,19 +253,42 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
206
253
  style,
207
254
  refView,
208
255
  index,
256
+ stickyOffset,
257
+ animatedScrollY: _animatedScrollY,
258
+ children,
209
259
  ...rest
210
260
  }) {
211
- const [position = POSITION_OUT_OF_VIEW, _headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
261
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, activeStickyIndex] = useArr$([
262
+ `containerPosition${id}`,
263
+ "headerSize",
264
+ "activeStickyIndex"
265
+ ]);
266
+ const base = {
267
+ contain: "paint layout style"
268
+ };
269
+ const composed = React3.useMemo(
270
+ () => {
271
+ var _a3;
272
+ return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
273
+ },
274
+ [style]
275
+ );
212
276
  const viewStyle = React3.useMemo(() => {
213
- const base = Array.isArray(style) ? Object.assign({}, ...style) : style;
214
- const axisStyle = horizontal ? { transform: `translateX(${position}px)` } : { top: position };
215
- return {
216
- ...base,
217
- zIndex: index + 1e3,
218
- ...axisStyle
219
- };
220
- }, [style, position, horizontal, index]);
221
- return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: viewStyle, ...rest });
277
+ var _a3;
278
+ const styleBase = { ...base, ...composed };
279
+ delete styleBase.transform;
280
+ const offset = (_a3 = stickyOffset != null ? stickyOffset : headerSize) != null ? _a3 : 0;
281
+ const isActive = activeStickyIndex === index;
282
+ styleBase.position = isActive ? "sticky" : "absolute";
283
+ styleBase.zIndex = index + 1e3;
284
+ if (horizontal) {
285
+ styleBase.left = isActive ? offset : position;
286
+ } else {
287
+ styleBase.top = isActive ? offset : position;
288
+ }
289
+ return styleBase;
290
+ }, [composed, horizontal, position, index, stickyOffset, headerSize, activeStickyIndex]);
291
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
222
292
  });
223
293
  var PositionView2 = PositionViewState;
224
294
 
@@ -233,37 +303,6 @@ function useInit(cb) {
233
303
  return refValue.current;
234
304
  }
235
305
 
236
- // src/utils/helpers.ts
237
- function isFunction(obj) {
238
- return typeof obj === "function";
239
- }
240
- function isArray(obj) {
241
- return Array.isArray(obj);
242
- }
243
- var warned = /* @__PURE__ */ new Set();
244
- function warnDevOnce(id, text) {
245
- if (IS_DEV && !warned.has(id)) {
246
- warned.add(id);
247
- console.warn(`[legend-list] ${text}`);
248
- }
249
- }
250
- function roundSize(size) {
251
- return Math.floor(size * 8) / 8;
252
- }
253
- function isNullOrUndefined(value) {
254
- return value === null || value === void 0;
255
- }
256
- function comparatorDefault(a, b) {
257
- return a - b;
258
- }
259
- function getPadding(s, type) {
260
- var _a3, _b, _c;
261
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
262
- }
263
- function extractPadding(style, contentContainerStyle, type) {
264
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
265
- }
266
-
267
306
  // src/state/ContextContainer.ts
268
307
  var ContextContainer = createContext(null);
269
308
  function useViewability(callback, configId) {
@@ -392,6 +431,10 @@ function getGlobalResizeObserver() {
392
431
  }
393
432
  var callbackMap = /* @__PURE__ */ new WeakMap();
394
433
  function createResizeObserver(element, callback) {
434
+ if (typeof ResizeObserver === "undefined") {
435
+ return () => {
436
+ };
437
+ }
395
438
  if (!element) {
396
439
  return () => {
397
440
  };
@@ -427,7 +470,7 @@ function useOnLayoutSync({
427
470
  const current = ref.current;
428
471
  const scrollableNode = (_b = (_a3 = current == null ? void 0 : current.getScrollableNode) == null ? void 0 : _a3.call(current)) != null ? _b : null;
429
472
  const element = scrollableNode || current;
430
- if (!element || !(element instanceof HTMLElement)) {
473
+ if (!element) {
431
474
  return;
432
475
  }
433
476
  const emit = (layout, fromLayoutEffect) => {
@@ -449,6 +492,9 @@ function useOnLayoutSync({
449
492
  return {};
450
493
  }
451
494
  function toLayout(rect) {
495
+ if (!rect) {
496
+ return { height: 0, width: 0, x: 0, y: 0 };
497
+ }
452
498
  return {
453
499
  height: rect.height,
454
500
  width: rect.width,
@@ -550,7 +596,7 @@ var Container = typedMemo(function Container2({
550
596
  }
551
597
  didLayoutRef.current = true;
552
598
  let layout = rectangle;
553
- Math.floor(rectangle[currentHorizontal ? "width" : "height"] * 8) / 8;
599
+ roundSize(rectangle[currentHorizontal ? "width" : "height"]);
554
600
  const doUpdate = () => {
555
601
  itemLayoutRef.current.lastSize = { height: layout.height, width: layout.width };
556
602
  updateItemSizeFn(currentItemKey, layout);
@@ -708,7 +754,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
708
754
  const columnWrapperStyle = ctx.columnWrapperStyle;
709
755
  const [totalSize, otherAxisSize] = useArr$(["totalSize", "otherAxisSize"]);
710
756
  useDOMOrder(ref);
711
- const style = horizontal ? { minHeight: otherAxisSize, width: totalSize } : { height: totalSize, minWidth: otherAxisSize };
757
+ const style = horizontal ? { minHeight: otherAxisSize, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, position: "relative" };
712
758
  if (columnWrapperStyle && numColumns > 1) {
713
759
  const { columnGap, rowGap, gap } = columnWrapperStyle;
714
760
  const gapX = columnGap || gap || 0;
@@ -780,7 +826,7 @@ function DevNumbers() {
780
826
 
781
827
  // src/platform/StyleSheet.tsx
782
828
  function flattenStyles(styles) {
783
- if (Array.isArray(styles)) {
829
+ if (isArray(styles)) {
784
830
  return Object.assign({}, ...styles.filter(Boolean));
785
831
  }
786
832
  return styles;
@@ -970,10 +1016,11 @@ function PaddingDevMode() {
970
1016
  function useValueListener$(key, callback) {
971
1017
  const ctx = useStateContext();
972
1018
  useLayoutEffect(() => {
973
- listen$(ctx, key, (value) => {
1019
+ const unsubscribe = listen$(ctx, key, (value) => {
974
1020
  callback(value);
975
1021
  });
976
- }, []);
1022
+ return unsubscribe;
1023
+ }, [callback, ctx, key]);
977
1024
  }
978
1025
 
979
1026
  // src/components/ScrollAdjust.tsx
@@ -1138,13 +1185,100 @@ function calculateOffsetForIndex(ctx, state, index) {
1138
1185
  return position;
1139
1186
  }
1140
1187
 
1188
+ // src/utils/setPaddingTop.ts
1189
+ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1190
+ if (stylePaddingTop !== void 0) {
1191
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1192
+ if (stylePaddingTop < prevStylePaddingTop) {
1193
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
1194
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1195
+ state.timeoutSetPaddingTop = setTimeout(() => {
1196
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
1197
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1198
+ }, 16);
1199
+ }
1200
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
1201
+ }
1202
+ if (alignItemsPaddingTop !== void 0) {
1203
+ set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1204
+ }
1205
+ }
1206
+
1207
+ // src/utils/updateAlignItemsPaddingTop.ts
1208
+ function updateAlignItemsPaddingTop(ctx, state) {
1209
+ const {
1210
+ scrollLength,
1211
+ props: { alignItemsAtEnd, data }
1212
+ } = state;
1213
+ if (alignItemsAtEnd) {
1214
+ let alignItemsPaddingTop = 0;
1215
+ if ((data == null ? void 0 : data.length) > 0) {
1216
+ const contentSize = getContentSize(ctx);
1217
+ alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1218
+ }
1219
+ setPaddingTop(ctx, state, { alignItemsPaddingTop });
1220
+ }
1221
+ }
1222
+
1223
+ // src/core/updateTotalSize.ts
1224
+ function updateTotalSize(ctx, state) {
1225
+ const {
1226
+ positions,
1227
+ props: { data }
1228
+ } = state;
1229
+ if (data.length === 0) {
1230
+ addTotalSize(ctx, state, null, 0);
1231
+ } else {
1232
+ const lastId = getId(state, data.length - 1);
1233
+ if (lastId !== void 0) {
1234
+ const lastPosition = positions.get(lastId);
1235
+ if (lastPosition !== void 0) {
1236
+ const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1237
+ if (lastSize !== void 0) {
1238
+ const totalSize = lastPosition + lastSize;
1239
+ addTotalSize(ctx, state, null, totalSize);
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+ function addTotalSize(ctx, state, key, add) {
1246
+ const { alignItemsAtEnd } = state.props;
1247
+ const prevTotalSize = state.totalSize;
1248
+ if (key === null) {
1249
+ state.totalSize = add;
1250
+ if (state.timeoutSetPaddingTop) {
1251
+ clearTimeout(state.timeoutSetPaddingTop);
1252
+ state.timeoutSetPaddingTop = void 0;
1253
+ }
1254
+ } else {
1255
+ state.totalSize += add;
1256
+ }
1257
+ if (prevTotalSize !== state.totalSize) {
1258
+ set$(ctx, "totalSize", state.totalSize);
1259
+ if (alignItemsAtEnd) {
1260
+ updateAlignItemsPaddingTop(ctx, state);
1261
+ }
1262
+ }
1263
+ }
1264
+
1265
+ // src/core/setSize.ts
1266
+ function setSize(ctx, state, itemKey, size) {
1267
+ const { sizes } = state;
1268
+ const previousSize = sizes.get(itemKey);
1269
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1270
+ if (diff !== 0) {
1271
+ addTotalSize(ctx, state, itemKey, diff);
1272
+ }
1273
+ sizes.set(itemKey, size);
1274
+ }
1275
+
1141
1276
  // src/utils/getItemSize.ts
1142
- function getItemSize(state, key, index, data, useAverageSize) {
1277
+ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1143
1278
  var _a3, _b;
1144
1279
  const {
1145
1280
  sizesKnown,
1146
1281
  sizes,
1147
- scrollingTo,
1148
1282
  averageSizes,
1149
1283
  props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1150
1284
  } = state;
@@ -1154,6 +1288,13 @@ function getItemSize(state, key, index, data, useAverageSize) {
1154
1288
  }
1155
1289
  let size;
1156
1290
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1291
+ const scrollingTo = peek$(ctx, "scrollingTo");
1292
+ if (preferCachedSize) {
1293
+ const cachedSize = sizes.get(key);
1294
+ if (cachedSize !== void 0) {
1295
+ return cachedSize;
1296
+ }
1297
+ }
1157
1298
  if (getFixedItemSize) {
1158
1299
  size = getFixedItemSize(index, data, itemType);
1159
1300
  if (size !== void 0) {
@@ -1175,56 +1316,235 @@ function getItemSize(state, key, index, data, useAverageSize) {
1175
1316
  if (size === void 0) {
1176
1317
  size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1177
1318
  }
1178
- sizes.set(key, size);
1319
+ setSize(ctx, state, key, size);
1179
1320
  return size;
1180
1321
  }
1181
1322
 
1182
1323
  // src/core/calculateOffsetWithOffsetPosition.ts
1183
- function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
1324
+ function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1184
1325
  const { index, viewOffset, viewPosition } = params;
1185
1326
  let offset = offsetParam;
1186
1327
  if (viewOffset) {
1187
1328
  offset -= viewOffset;
1188
1329
  }
1189
1330
  if (viewPosition !== void 0 && index !== void 0) {
1190
- offset -= viewPosition * (state.scrollLength - getItemSize(state, getId(state, index), index, state.props.data[index]));
1331
+ offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1191
1332
  }
1192
1333
  return offset;
1193
1334
  }
1194
1335
 
1195
- // src/core/finishScrollTo.ts
1196
- var finishScrollTo = (state) => {
1197
- if (state) {
1198
- state.scrollingTo = void 0;
1199
- state.scrollHistory.length = 0;
1336
+ // src/utils/checkThreshold.ts
1337
+ var HYSTERESIS_MULTIPLIER = 1.3;
1338
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1339
+ const absDistance = Math.abs(distance);
1340
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
1341
+ const updateSnapshot = () => {
1342
+ setSnapshot == null ? void 0 : setSnapshot({
1343
+ atThreshold,
1344
+ contentSize: context.contentSize,
1345
+ dataLength: context.dataLength,
1346
+ scrollPosition: context.scrollPosition
1347
+ });
1348
+ };
1349
+ if (!wasReached) {
1350
+ if (!within) {
1351
+ return false;
1352
+ }
1353
+ onReached == null ? void 0 : onReached(distance);
1354
+ updateSnapshot();
1355
+ return true;
1356
+ }
1357
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1358
+ if (reset) {
1359
+ setSnapshot == null ? void 0 : setSnapshot(void 0);
1360
+ return false;
1361
+ }
1362
+ if (within) {
1363
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1364
+ if (changed) {
1365
+ onReached == null ? void 0 : onReached(distance);
1366
+ updateSnapshot();
1367
+ }
1200
1368
  }
1369
+ return true;
1201
1370
  };
1202
1371
 
1203
- // src/core/scrollTo.ts
1204
- function scrollTo(state, params = {}) {
1372
+ // src/utils/checkAtBottom.ts
1373
+ function checkAtBottom(ctx, state) {
1205
1374
  var _a3;
1206
- const { animated, noScrollingTo, isInitialScroll } = params;
1375
+ if (!state) {
1376
+ return;
1377
+ }
1207
1378
  const {
1208
- refScroller,
1209
- props: { horizontal }
1379
+ queuedInitialLayout,
1380
+ scrollLength,
1381
+ scroll,
1382
+ maintainingScrollAtEnd,
1383
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1210
1384
  } = state;
1211
- const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
1212
- state.scrollHistory.length = 0;
1213
- if (!noScrollingTo) {
1214
- state.scrollingTo = params;
1215
- }
1216
- state.scrollPending = offset;
1217
- if (!params.isInitialScroll || Platform.OS === "android") {
1218
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1219
- animated: !!animated,
1220
- x: horizontal ? offset : 0,
1221
- y: horizontal ? 0 : offset
1222
- });
1385
+ const contentSize = getContentSize(ctx);
1386
+ if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1387
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1388
+ const isContentLess = contentSize < scrollLength;
1389
+ state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1390
+ state.isEndReached = checkThreshold(
1391
+ distanceFromEnd,
1392
+ isContentLess,
1393
+ onEndReachedThreshold * scrollLength,
1394
+ state.isEndReached,
1395
+ state.endReachedSnapshot,
1396
+ {
1397
+ contentSize,
1398
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1399
+ scrollPosition: scroll
1400
+ },
1401
+ (distance) => {
1402
+ var _a4, _b;
1403
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1404
+ },
1405
+ (snapshot) => {
1406
+ state.endReachedSnapshot = snapshot;
1407
+ }
1408
+ );
1223
1409
  }
1224
- if (!animated) {
1225
- state.scroll = offset;
1226
- setTimeout(() => finishScrollTo(state), 100);
1227
- if (isInitialScroll) {
1410
+ }
1411
+
1412
+ // src/utils/checkAtTop.ts
1413
+ function checkAtTop(state) {
1414
+ var _a3;
1415
+ if (!state) {
1416
+ return;
1417
+ }
1418
+ const {
1419
+ scrollLength,
1420
+ scroll,
1421
+ props: { onStartReachedThreshold }
1422
+ } = state;
1423
+ const distanceFromTop = scroll;
1424
+ state.isAtStart = distanceFromTop <= 0;
1425
+ state.isStartReached = checkThreshold(
1426
+ distanceFromTop,
1427
+ false,
1428
+ onStartReachedThreshold * scrollLength,
1429
+ state.isStartReached,
1430
+ state.startReachedSnapshot,
1431
+ {
1432
+ contentSize: state.totalSize,
1433
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1434
+ scrollPosition: scroll
1435
+ },
1436
+ (distance) => {
1437
+ var _a4, _b;
1438
+ return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
1439
+ },
1440
+ (snapshot) => {
1441
+ state.startReachedSnapshot = snapshot;
1442
+ }
1443
+ );
1444
+ }
1445
+
1446
+ // src/core/onScroll.ts
1447
+ function onScroll(ctx, state, event) {
1448
+ var _a3, _b, _c;
1449
+ const {
1450
+ scrollProcessingEnabled,
1451
+ props: { onScroll: onScrollProp }
1452
+ } = state;
1453
+ if (scrollProcessingEnabled === false) {
1454
+ return;
1455
+ }
1456
+ if (((_b = (_a3 = event.nativeEvent) == null ? void 0 : _a3.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
1457
+ return;
1458
+ }
1459
+ const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
1460
+ state.scrollPending = newScroll;
1461
+ updateScroll(ctx, state, newScroll);
1462
+ onScrollProp == null ? void 0 : onScrollProp(event);
1463
+ }
1464
+ function updateScroll(ctx, state, newScroll, forceUpdate) {
1465
+ const scrollingTo = peek$(ctx, "scrollingTo");
1466
+ state.hasScrolled = true;
1467
+ state.lastBatchingAction = Date.now();
1468
+ const currentTime = Date.now();
1469
+ const adjust = state.scrollAdjustHandler.getAdjust();
1470
+ const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1471
+ const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1472
+ if (adjustChanged) {
1473
+ state.scrollHistory.length = 0;
1474
+ }
1475
+ state.lastScrollAdjustForHistory = adjust;
1476
+ if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1477
+ if (!adjustChanged) {
1478
+ state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1479
+ }
1480
+ }
1481
+ if (state.scrollHistory.length > 5) {
1482
+ state.scrollHistory.shift();
1483
+ }
1484
+ state.scrollPrev = state.scroll;
1485
+ state.scrollPrevTime = state.scrollTime;
1486
+ state.scroll = newScroll;
1487
+ state.scrollTime = currentTime;
1488
+ const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
1489
+ if (ignoreScrollFromMVCP && !scrollingTo) {
1490
+ const { lt, gt } = ignoreScrollFromMVCP;
1491
+ if (lt && newScroll < lt || gt && newScroll > gt) {
1492
+ state.ignoreScrollFromMVCPIgnored = true;
1493
+ return;
1494
+ }
1495
+ }
1496
+ if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1497
+ state.ignoreScrollFromMVCPIgnored = false;
1498
+ calculateItemsInView(ctx, state, { doMVCP: scrollingTo !== void 0 });
1499
+ checkAtBottom(ctx, state);
1500
+ checkAtTop(state);
1501
+ state.dataChangeNeedsScrollUpdate = false;
1502
+ }
1503
+ }
1504
+
1505
+ // src/core/finishScrollTo.ts
1506
+ function finishScrollTo(ctx, state) {
1507
+ var _a3, _b;
1508
+ if (state) {
1509
+ state.scrollHistory.length = 0;
1510
+ state.initialScroll = void 0;
1511
+ set$(ctx, "scrollingTo", void 0);
1512
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1513
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1514
+ }
1515
+ }
1516
+ }
1517
+
1518
+ // src/core/scrollTo.ts
1519
+ function scrollTo(ctx, state, params) {
1520
+ var _a3;
1521
+ const { noScrollingTo, ...scrollTarget } = params;
1522
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1523
+ const {
1524
+ refScroller,
1525
+ props: { horizontal }
1526
+ } = state;
1527
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1528
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1529
+ const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1530
+ offset = Math.min(offset, maxOffset);
1531
+ }
1532
+ state.scrollHistory.length = 0;
1533
+ if (!noScrollingTo) {
1534
+ set$(ctx, "scrollingTo", scrollTarget);
1535
+ }
1536
+ state.scrollPending = offset;
1537
+ if (!isInitialScroll || Platform.OS === "android") {
1538
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1539
+ animated: !!animated,
1540
+ x: horizontal ? offset : 0,
1541
+ y: horizontal ? 0 : offset
1542
+ });
1543
+ }
1544
+ if (!animated) {
1545
+ state.scroll = offset;
1546
+ setTimeout(() => finishScrollTo(ctx, state), 100);
1547
+ if (isInitialScroll) {
1228
1548
  setTimeout(() => {
1229
1549
  state.initialScroll = void 0;
1230
1550
  }, 500);
@@ -1245,24 +1565,6 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1245
1565
  const didLayout = peek$(ctx, "containersDidLayout");
1246
1566
  if (didLayout) {
1247
1567
  doit();
1248
- const threshold = state.scroll - positionDiff / 2;
1249
- if (!state.ignoreScrollFromMVCP) {
1250
- state.ignoreScrollFromMVCP = {};
1251
- }
1252
- if (positionDiff > 0) {
1253
- state.ignoreScrollFromMVCP.lt = threshold;
1254
- } else {
1255
- state.ignoreScrollFromMVCP.gt = threshold;
1256
- }
1257
- if (state.ignoreScrollFromMVCPTimeout) {
1258
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
1259
- }
1260
- state.ignoreScrollFromMVCPTimeout = setTimeout(
1261
- () => {
1262
- state.ignoreScrollFromMVCP = void 0;
1263
- },
1264
- 100
1265
- );
1266
1568
  } else {
1267
1569
  requestAnimationFrame(doit);
1268
1570
  }
@@ -1274,9 +1576,9 @@ function prepareMVCP(ctx, state, dataChanged) {
1274
1576
  const {
1275
1577
  idsInView,
1276
1578
  positions,
1277
- scrollingTo,
1278
1579
  props: { maintainVisibleContentPosition }
1279
1580
  } = state;
1581
+ const scrollingTo = peek$(ctx, "scrollingTo");
1280
1582
  let prevPosition;
1281
1583
  let targetId;
1282
1584
  const idsInViewWithPositions = [];
@@ -1352,7 +1654,7 @@ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1352
1654
  const prevId = state.idCache[prevIndex];
1353
1655
  const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1354
1656
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1355
- const prevRowHeight = calculateRowMaxSize(state, prevRowStart, prevIndex, useAverageSize);
1657
+ const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1356
1658
  currentRowTop = prevPosition + prevRowHeight;
1357
1659
  }
1358
1660
  return {
@@ -1375,7 +1677,7 @@ function findRowStartIndex(state, numColumns, index) {
1375
1677
  }
1376
1678
  return rowStart;
1377
1679
  }
1378
- function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1680
+ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1379
1681
  if (endIndex < startIndex) {
1380
1682
  return 0;
1381
1683
  }
@@ -1389,7 +1691,7 @@ function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1389
1691
  continue;
1390
1692
  }
1391
1693
  const id = state.idCache[i];
1392
- const size = getItemSize(state, id, i, data[i], useAverageSize);
1694
+ const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1393
1695
  if (size > maxSize) {
1394
1696
  maxSize = size;
1395
1697
  }
@@ -1397,82 +1699,43 @@ function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1397
1699
  return maxSize;
1398
1700
  }
1399
1701
 
1400
- // src/utils/setPaddingTop.ts
1401
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1402
- if (stylePaddingTop !== void 0) {
1403
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1404
- if (stylePaddingTop < prevStylePaddingTop) {
1405
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
1406
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1407
- state.timeoutSetPaddingTop = setTimeout(() => {
1408
- prevTotalSize = peek$(ctx, "totalSize") || 0;
1409
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1410
- }, 16);
1411
- }
1412
- set$(ctx, "stylePaddingTop", stylePaddingTop);
1413
- }
1414
- if (alignItemsPaddingTop !== void 0) {
1415
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1416
- }
1417
- }
1418
-
1419
- // src/utils/updateAlignItemsPaddingTop.ts
1420
- function updateAlignItemsPaddingTop(ctx, state) {
1421
- const {
1422
- scrollLength,
1423
- props: { alignItemsAtEnd, data }
1424
- } = state;
1425
- if (alignItemsAtEnd) {
1426
- let alignItemsPaddingTop = 0;
1427
- if ((data == null ? void 0 : data.length) > 0) {
1428
- const contentSize = getContentSize(ctx);
1429
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1430
- }
1431
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1432
- }
1433
- }
1434
-
1435
- // src/core/updateTotalSize.ts
1436
- function updateTotalSize(ctx, state) {
1437
- const {
1438
- positions,
1439
- props: { data }
1440
- } = state;
1441
- if (data.length === 0) {
1442
- addTotalSize(ctx, state, null, 0);
1443
- } else {
1444
- const lastId = getId(state, data.length - 1);
1445
- if (lastId !== void 0) {
1446
- const lastPosition = positions.get(lastId);
1447
- if (lastPosition !== void 0) {
1448
- const lastSize = getItemSize(state, lastId, data.length - 1, data[data.length - 1]);
1449
- if (lastSize !== void 0) {
1450
- const totalSize = lastPosition + lastSize;
1451
- addTotalSize(ctx, state, null, totalSize);
1702
+ // src/utils/getScrollVelocity.ts
1703
+ var getScrollVelocity = (state) => {
1704
+ const { scrollHistory } = state;
1705
+ let velocity = 0;
1706
+ if (scrollHistory.length >= 1) {
1707
+ const newest = scrollHistory[scrollHistory.length - 1];
1708
+ let oldest;
1709
+ let start = 0;
1710
+ const now = Date.now();
1711
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1712
+ const entry = scrollHistory[i];
1713
+ const nextEntry = scrollHistory[i + 1];
1714
+ if (i > 0) {
1715
+ const prevEntry = scrollHistory[i - 1];
1716
+ const prevDirection = entry.scroll - prevEntry.scroll;
1717
+ const currentDirection = nextEntry.scroll - entry.scroll;
1718
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1719
+ start = i;
1720
+ break;
1452
1721
  }
1453
1722
  }
1454
1723
  }
1455
- }
1456
- }
1457
- function addTotalSize(ctx, state, key, add) {
1458
- const { alignItemsAtEnd } = state.props;
1459
- const prevTotalSize = state.totalSize;
1460
- if (key === null) {
1461
- state.totalSize = add;
1462
- if (state.timeoutSetPaddingTop) {
1463
- clearTimeout(state.timeoutSetPaddingTop);
1464
- state.timeoutSetPaddingTop = void 0;
1724
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1725
+ const entry = scrollHistory[i];
1726
+ if (now - entry.time <= 1e3) {
1727
+ oldest = entry;
1728
+ break;
1729
+ }
1465
1730
  }
1466
- } else {
1467
- state.totalSize += add;
1468
- }
1469
- if (prevTotalSize !== state.totalSize) {
1470
- set$(ctx, "totalSize", state.totalSize);
1471
- if (alignItemsAtEnd) {
1472
- updateAlignItemsPaddingTop(ctx, state);
1731
+ if (oldest && oldest !== newest) {
1732
+ const scrollDiff = newest.scroll - oldest.scroll;
1733
+ const timeDiff = newest.time - oldest.time;
1734
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1473
1735
  }
1474
1736
  }
1475
- }
1737
+ return velocity;
1738
+ };
1476
1739
 
1477
1740
  // src/utils/updateSnapToOffsets.ts
1478
1741
  function updateSnapToOffsets(ctx, state) {
@@ -1490,23 +1753,30 @@ function updateSnapToOffsets(ctx, state) {
1490
1753
  }
1491
1754
 
1492
1755
  // src/core/updateItemPositions.ts
1493
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered } = { scrollBottomBuffered: -1, startIndex: 0 }) {
1494
- var _a3, _b, _c, _d;
1756
+ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false } = {
1757
+ forceFullUpdate: false,
1758
+ scrollBottomBuffered: -1,
1759
+ startIndex: 0
1760
+ }) {
1761
+ var _a3, _b, _c, _d, _e;
1495
1762
  const {
1496
1763
  columns,
1497
1764
  indexByKey,
1498
1765
  positions,
1499
1766
  idCache,
1500
1767
  sizesKnown,
1501
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
1768
+ props: { getEstimatedItemSize, snapToIndices, enableAverages, maintainVisibleContentPosition }
1502
1769
  } = state;
1503
1770
  const data = state.props.data;
1504
1771
  const dataLength = data.length;
1505
1772
  const numColumns = peek$(ctx, "numColumns");
1773
+ const scrollingTo = peek$(ctx, "scrollingTo");
1506
1774
  const hasColumns = numColumns > 1;
1507
1775
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1776
+ const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1508
1777
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1509
1778
  const useAverageSize = enableAverages && !getEstimatedItemSize;
1779
+ const preferCachedSize = maintainVisibleContentPosition && (dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0);
1510
1780
  let currentRowTop = 0;
1511
1781
  let column = 1;
1512
1782
  let maxSizeInRow = 0;
@@ -1523,8 +1793,8 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1523
1793
  } else if (startIndex < dataLength) {
1524
1794
  const prevIndex = startIndex - 1;
1525
1795
  const prevId = getId(state, prevIndex);
1526
- const prevPosition = (_a3 = positions.get(prevId)) != null ? _a3 : 0;
1527
- const prevSize = (_b = sizesKnown.get(prevId)) != null ? _b : getItemSize(state, prevId, prevIndex, data[prevIndex], useAverageSize);
1796
+ const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1797
+ const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1528
1798
  currentRowTop = prevPosition + prevSize;
1529
1799
  }
1530
1800
  }
@@ -1532,16 +1802,16 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1532
1802
  let didBreakEarly = false;
1533
1803
  let breakAt;
1534
1804
  for (let i = startIndex; i < dataLength; i++) {
1535
- if (breakAt && i > breakAt) {
1805
+ if (shouldOptimize && breakAt !== void 0 && i > breakAt) {
1536
1806
  didBreakEarly = true;
1537
1807
  break;
1538
1808
  }
1539
- if (breakAt === void 0 && !dataChanged && currentRowTop > maxVisibleArea) {
1809
+ if (shouldOptimize && breakAt === void 0 && !scrollingTo && !dataChanged && currentRowTop > maxVisibleArea) {
1540
1810
  const itemsPerRow = hasColumns ? numColumns : 1;
1541
1811
  breakAt = i + itemsPerRow + 10;
1542
1812
  }
1543
- const id = (_c = idCache[i]) != null ? _c : getId(state, i);
1544
- const size = (_d = sizesKnown.get(id)) != null ? _d : getItemSize(state, id, i, data[i], useAverageSize);
1813
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1814
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
1545
1815
  if (IS_DEV && needsIndexByKey) {
1546
1816
  if (indexByKeyForChecking.has(id)) {
1547
1817
  console.error(
@@ -1759,16 +2029,6 @@ function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize,
1759
2029
  const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
1760
2030
  return value.isViewable;
1761
2031
  }
1762
- function findContainerId(ctx, key) {
1763
- const numContainers = peek$(ctx, "numContainers");
1764
- for (let i = 0; i < numContainers; i++) {
1765
- const itemKey = peek$(ctx, `containerItemKey${i}`);
1766
- if (itemKey === key) {
1767
- return i;
1768
- }
1769
- }
1770
- return -1;
1771
- }
1772
2032
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1773
2033
  const key = containerId + configId;
1774
2034
  ctx.mapViewabilityValues.set(key, viewToken);
@@ -1817,7 +2077,7 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1817
2077
  for (const containerIndex of stickyContainerPool) {
1818
2078
  const key = peek$(ctx, `containerItemKey${containerIndex}`);
1819
2079
  const isPendingRemoval = pendingRemovalSet.has(containerIndex);
1820
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
2080
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType) && !result.includes(containerIndex)) {
1821
2081
  result.push(containerIndex);
1822
2082
  if (isPendingRemoval && pendingRemovalSet.delete(containerIndex)) {
1823
2083
  pendingRemovalChanged = true;
@@ -1905,149 +2165,35 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1905
2165
  if (pendingRemovalChanged) {
1906
2166
  pendingRemoval.length = 0;
1907
2167
  for (const value of pendingRemovalSet) {
1908
- pendingRemoval.push(value);
1909
- }
1910
- }
1911
- return result.sort(comparatorDefault);
1912
- }
1913
- function comparatorByDistance(a, b) {
1914
- return b.distance - a.distance;
1915
- }
1916
-
1917
- // src/utils/getScrollVelocity.ts
1918
- var getScrollVelocity = (state) => {
1919
- const { scrollHistory } = state;
1920
- let velocity = 0;
1921
- if (scrollHistory.length >= 1) {
1922
- const newest = scrollHistory[scrollHistory.length - 1];
1923
- let oldest;
1924
- let start = 0;
1925
- const now = Date.now();
1926
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1927
- const entry = scrollHistory[i];
1928
- const nextEntry = scrollHistory[i + 1];
1929
- if (i > 0) {
1930
- const prevEntry = scrollHistory[i - 1];
1931
- const prevDirection = entry.scroll - prevEntry.scroll;
1932
- const currentDirection = nextEntry.scroll - entry.scroll;
1933
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1934
- start = i;
1935
- break;
1936
- }
1937
- }
1938
- }
1939
- for (let i = start; i < scrollHistory.length - 1; i++) {
1940
- const entry = scrollHistory[i];
1941
- if (now - entry.time <= 1e3) {
1942
- oldest = entry;
1943
- break;
1944
- }
1945
- }
1946
- if (oldest && oldest !== newest) {
1947
- const scrollDiff = newest.scroll - oldest.scroll;
1948
- const timeDiff = newest.time - oldest.time;
1949
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1950
- }
1951
- }
1952
- return velocity;
1953
- };
1954
-
1955
- // src/core/scrollToIndex.ts
1956
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
1957
- if (index >= state.props.data.length) {
1958
- index = state.props.data.length - 1;
1959
- } else if (index < 0) {
1960
- index = 0;
1961
- }
1962
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
1963
- const isLast = index === state.props.data.length - 1;
1964
- if (isLast && viewPosition === void 0) {
1965
- viewPosition = 1;
1966
- }
1967
- state.scrollForNextCalculateItemsInView = void 0;
1968
- scrollTo(state, {
1969
- animated,
1970
- index,
1971
- offset: firstIndexOffset,
1972
- viewOffset,
1973
- viewPosition: viewPosition != null ? viewPosition : 0
1974
- });
1975
- }
1976
-
1977
- // src/utils/checkThreshold.ts
1978
- var HYSTERESIS_MULTIPLIER = 1.3;
1979
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1980
- const absDistance = Math.abs(distance);
1981
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
1982
- const updateSnapshot = () => {
1983
- setSnapshot == null ? void 0 : setSnapshot({
1984
- atThreshold,
1985
- contentSize: context.contentSize,
1986
- dataLength: context.dataLength,
1987
- scrollPosition: context.scrollPosition
1988
- });
1989
- };
1990
- if (!wasReached) {
1991
- if (!within) {
1992
- return false;
1993
- }
1994
- onReached == null ? void 0 : onReached(distance);
1995
- updateSnapshot();
1996
- return true;
1997
- }
1998
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1999
- if (reset) {
2000
- setSnapshot == null ? void 0 : setSnapshot(void 0);
2001
- return false;
2002
- }
2003
- if (within) {
2004
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
2005
- if (changed) {
2006
- onReached == null ? void 0 : onReached(distance);
2007
- updateSnapshot();
2168
+ pendingRemoval.push(value);
2008
2169
  }
2009
2170
  }
2010
- return true;
2011
- };
2171
+ return result.sort(comparatorDefault);
2172
+ }
2173
+ function comparatorByDistance(a, b) {
2174
+ return b.distance - a.distance;
2175
+ }
2012
2176
 
2013
- // src/utils/checkAtBottom.ts
2014
- function checkAtBottom(ctx, state) {
2015
- var _a3;
2016
- if (!state) {
2017
- return;
2177
+ // src/core/scrollToIndex.ts
2178
+ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2179
+ if (index >= state.props.data.length) {
2180
+ index = state.props.data.length - 1;
2181
+ } else if (index < 0) {
2182
+ index = 0;
2018
2183
  }
2019
- const {
2020
- queuedInitialLayout,
2021
- scrollLength,
2022
- scroll,
2023
- maintainingScrollAtEnd,
2024
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
2025
- } = state;
2026
- const contentSize = getContentSize(ctx);
2027
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
2028
- const distanceFromEnd = contentSize - scroll - scrollLength;
2029
- const isContentLess = contentSize < scrollLength;
2030
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
2031
- state.isEndReached = checkThreshold(
2032
- distanceFromEnd,
2033
- isContentLess,
2034
- onEndReachedThreshold * scrollLength,
2035
- state.isEndReached,
2036
- state.endReachedSnapshot,
2037
- {
2038
- scrollPosition: scroll,
2039
- contentSize,
2040
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length
2041
- },
2042
- (distance) => {
2043
- var _a4, _b;
2044
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
2045
- },
2046
- (snapshot) => {
2047
- state.endReachedSnapshot = snapshot;
2048
- }
2049
- );
2184
+ const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2185
+ const isLast = index === state.props.data.length - 1;
2186
+ if (isLast && viewPosition === void 0) {
2187
+ viewPosition = 1;
2050
2188
  }
2189
+ state.scrollForNextCalculateItemsInView = void 0;
2190
+ scrollTo(ctx, state, {
2191
+ animated,
2192
+ index,
2193
+ offset: firstIndexOffset,
2194
+ viewOffset,
2195
+ viewPosition: viewPosition != null ? viewPosition : 0
2196
+ });
2051
2197
  }
2052
2198
 
2053
2199
  // src/utils/setDidLayout.ts
@@ -2129,7 +2275,7 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2129
2275
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2130
2276
  if (currentId) {
2131
2277
  const currentPos = state.positions.get(currentId);
2132
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
2278
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2133
2279
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2134
2280
  }
2135
2281
  }
@@ -2140,21 +2286,22 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2140
2286
  }
2141
2287
  function calculateItemsInView(ctx, state, params = {}) {
2142
2288
  unstable_batchedUpdates(() => {
2143
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
2289
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2144
2290
  const {
2145
2291
  columns,
2146
2292
  containerItemKeys,
2147
2293
  enableScrollForNextCalculateItemsInView,
2148
2294
  idCache,
2149
2295
  indexByKey,
2296
+ initialScroll,
2150
2297
  minIndexSizeChanged,
2151
2298
  positions,
2299
+ props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2152
2300
  scrollForNextCalculateItemsInView,
2153
2301
  scrollLength,
2154
2302
  sizes,
2155
2303
  startBufferedId: startBufferedIdOrig,
2156
- viewabilityConfigCallbackPairs,
2157
- props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer }
2304
+ viewabilityConfigCallbackPairs
2158
2305
  } = state;
2159
2306
  const { data } = state.props;
2160
2307
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
@@ -2166,20 +2313,22 @@ function calculateItemsInView(ctx, state, params = {}) {
2166
2313
  const totalSize = peek$(ctx, "totalSize");
2167
2314
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2168
2315
  const numColumns = peek$(ctx, "numColumns");
2169
- const { dataChanged, doMVCP } = params;
2316
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
2170
2317
  const speed = getScrollVelocity(state);
2171
2318
  const scrollExtra = 0;
2172
2319
  const { queuedInitialLayout } = state;
2173
2320
  let { scroll: scrollState } = state;
2174
2321
  if (!queuedInitialLayout && initialScroll) {
2175
2322
  const updatedOffset = calculateOffsetWithOffsetPosition(
2323
+ ctx,
2176
2324
  state,
2177
2325
  calculateOffsetForIndex(ctx, state, initialScroll.index),
2178
2326
  initialScroll
2179
2327
  );
2180
2328
  scrollState = updatedOffset;
2181
2329
  }
2182
- const scrollAdjustPad = -topPad;
2330
+ const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2331
+ const scrollAdjustPad = scrollAdjustPending - topPad;
2183
2332
  let scroll = scrollState + scrollExtra + scrollAdjustPad;
2184
2333
  if (scroll + scrollLength > totalSize) {
2185
2334
  scroll = Math.max(0, totalSize - scrollLength);
@@ -2192,6 +2341,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2192
2341
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2193
2342
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2194
2343
  state.activeStickyIndex = nextActiveStickyIndex;
2344
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2195
2345
  let scrollBufferTop = scrollBuffer;
2196
2346
  let scrollBufferBottom = scrollBuffer;
2197
2347
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2216,8 +2366,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2216
2366
  idCache.length = 0;
2217
2367
  positions.clear();
2218
2368
  }
2219
- const startIndex = dataChanged ? 0 : (_a3 = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _a3 : 0;
2220
- updateItemPositions(ctx, state, dataChanged, { scrollBottomBuffered, startIndex });
2369
+ const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2370
+ updateItemPositions(ctx, state, dataChanged, {
2371
+ forceFullUpdate: !!forceFullItemPositions,
2372
+ scrollBottomBuffered,
2373
+ startIndex
2374
+ });
2221
2375
  if (minIndexSizeChanged !== void 0) {
2222
2376
  state.minIndexSizeChanged = void 0;
2223
2377
  }
@@ -2229,9 +2383,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2229
2383
  let endBuffered = null;
2230
2384
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2231
2385
  for (let i = loopStart; i >= 0; i--) {
2232
- const id = (_b = idCache[i]) != null ? _b : getId(state, i);
2386
+ const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2233
2387
  const top = positions.get(id);
2234
- const size = (_c = sizes.get(id)) != null ? _c : getItemSize(state, id, i, data[i]);
2388
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2235
2389
  const bottom = top + size;
2236
2390
  if (bottom > scroll - scrollBuffer) {
2237
2391
  loopStart = i;
@@ -2257,8 +2411,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2257
2411
  let firstFullyOnScreenIndex;
2258
2412
  const dataLength = data.length;
2259
2413
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2260
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
2261
- const size = (_e = sizes.get(id)) != null ? _e : getItemSize(state, id, i, data[i]);
2414
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2415
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2262
2416
  const top = positions.get(id);
2263
2417
  if (!foundEnd) {
2264
2418
  if (startNoBuffer === null && top + size > scroll) {
@@ -2287,7 +2441,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2287
2441
  }
2288
2442
  const idsInView = [];
2289
2443
  for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2290
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2444
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2291
2445
  idsInView.push(id);
2292
2446
  }
2293
2447
  Object.assign(state, {
@@ -2319,7 +2473,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2319
2473
  let numContainers2 = prevNumContainers;
2320
2474
  const needNewContainers = [];
2321
2475
  for (let i = startBuffered; i <= endBuffered; i++) {
2322
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2476
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2323
2477
  if (!containerItemKeys.has(id)) {
2324
2478
  needNewContainers.push(i);
2325
2479
  }
@@ -2337,6 +2491,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2337
2491
  );
2338
2492
  } else {
2339
2493
  state.activeStickyIndex = void 0;
2494
+ set$(ctx, "activeStickyIndex", void 0);
2340
2495
  }
2341
2496
  if (needNewContainers.length > 0) {
2342
2497
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
@@ -2356,7 +2511,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2356
2511
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2357
2512
  const i = needNewContainers[idx];
2358
2513
  const containerIndex = availableContainers[idx];
2359
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2514
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2360
2515
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2361
2516
  if (oldKey && oldKey !== id) {
2362
2517
  containerItemKeys.delete(oldKey);
@@ -2395,7 +2550,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2395
2550
  for (let i = 0; i < numContainers; i++) {
2396
2551
  const itemKey = peek$(ctx, `containerItemKey${i}`);
2397
2552
  if (pendingRemoval.includes(i)) {
2398
- if (itemKey) {
2553
+ if (itemKey !== void 0) {
2399
2554
  containerItemKeys.delete(itemKey);
2400
2555
  }
2401
2556
  state.containerItemTypes.delete(i);
@@ -2412,11 +2567,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2412
2567
  const itemIndex = indexByKey.get(itemKey);
2413
2568
  const item = data[itemIndex];
2414
2569
  if (item !== void 0) {
2415
- const id = (_i = idCache[itemIndex]) != null ? _i : getId(state, itemIndex);
2416
- const position = positions.get(id);
2417
- if (position === void 0) {
2570
+ const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2571
+ const positionValue = positions.get(id);
2572
+ if (positionValue === void 0) {
2418
2573
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2419
2574
  } else {
2575
+ const position = (positionValue || 0) - scrollAdjustPending;
2420
2576
  const column = columns.get(id) || 1;
2421
2577
  const prevPos = peek$(ctx, `containerPosition${i}`);
2422
2578
  const prevColumn = peek$(ctx, `containerColumn${i}`);
@@ -2455,6 +2611,26 @@ function calculateItemsInView(ctx, state, params = {}) {
2455
2611
  });
2456
2612
  }
2457
2613
 
2614
+ // src/core/checkActualChange.ts
2615
+ function checkActualChange(state, dataProp, previousData) {
2616
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
2617
+ return true;
2618
+ }
2619
+ const {
2620
+ idCache,
2621
+ props: { keyExtractor }
2622
+ } = state;
2623
+ for (let i = 0; i < dataProp.length; i++) {
2624
+ if (dataProp[i] !== previousData[i]) {
2625
+ return true;
2626
+ }
2627
+ if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
2628
+ return true;
2629
+ }
2630
+ }
2631
+ return false;
2632
+ }
2633
+
2458
2634
  // src/core/doMaintainScrollAtEnd.ts
2459
2635
  function doMaintainScrollAtEnd(ctx, state, animated) {
2460
2636
  const {
@@ -2485,40 +2661,6 @@ function doMaintainScrollAtEnd(ctx, state, animated) {
2485
2661
  }
2486
2662
  }
2487
2663
 
2488
- // src/utils/checkAtTop.ts
2489
- function checkAtTop(state) {
2490
- var _a3;
2491
- if (!state) {
2492
- return;
2493
- }
2494
- const {
2495
- scrollLength,
2496
- scroll,
2497
- props: { onStartReachedThreshold }
2498
- } = state;
2499
- const distanceFromTop = scroll;
2500
- state.isAtStart = distanceFromTop <= 0;
2501
- state.isStartReached = checkThreshold(
2502
- distanceFromTop,
2503
- false,
2504
- onStartReachedThreshold * scrollLength,
2505
- state.isStartReached,
2506
- state.startReachedSnapshot,
2507
- {
2508
- scrollPosition: scroll,
2509
- contentSize: state.totalSize,
2510
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length
2511
- },
2512
- (distance) => {
2513
- var _a4, _b;
2514
- return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
2515
- },
2516
- (snapshot) => {
2517
- state.startReachedSnapshot = snapshot;
2518
- }
2519
- );
2520
- }
2521
-
2522
2664
  // src/utils/updateAveragesOnDataChange.ts
2523
2665
  function updateAveragesOnDataChange(state, oldData, newData) {
2524
2666
  var _a3;
@@ -2572,25 +2714,23 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2572
2714
  }
2573
2715
 
2574
2716
  // src/core/checkResetContainers.ts
2575
- function checkResetContainers(ctx, state, isFirst, dataProp) {
2576
- if (state) {
2577
- if (!isFirst && state.props.data !== dataProp) {
2578
- updateAveragesOnDataChange(state, state.props.data, dataProp);
2579
- }
2580
- const { maintainScrollAtEnd } = state.props;
2581
- if (!isFirst) {
2582
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2583
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2584
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2585
- if (!didMaintainScrollAtEnd && dataProp.length > state.props.data.length) {
2586
- state.isEndReached = false;
2587
- }
2588
- if (!didMaintainScrollAtEnd) {
2589
- checkAtTop(state);
2590
- checkAtBottom(ctx, state);
2591
- }
2592
- }
2717
+ function checkResetContainers(ctx, state, dataProp) {
2718
+ const { previousData } = state;
2719
+ if (previousData) {
2720
+ updateAveragesOnDataChange(state, previousData, dataProp);
2721
+ }
2722
+ const { maintainScrollAtEnd } = state.props;
2723
+ calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2724
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2725
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2726
+ if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2727
+ state.isEndReached = false;
2593
2728
  }
2729
+ if (!didMaintainScrollAtEnd) {
2730
+ checkAtTop(state);
2731
+ checkAtBottom(ctx, state);
2732
+ }
2733
+ delete state.previousData;
2594
2734
  }
2595
2735
 
2596
2736
  // src/core/doInitialAllocateContainers.ts
@@ -2631,7 +2771,7 @@ function doInitialAllocateContainers(ctx, state) {
2631
2771
  set$(ctx, "numContainers", numContainers);
2632
2772
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2633
2773
  if (state.lastLayout) {
2634
- if (state.props.initialScroll) {
2774
+ if (state.initialScroll) {
2635
2775
  requestAnimationFrame(() => {
2636
2776
  calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2637
2777
  });
@@ -2687,78 +2827,47 @@ function handleLayout(ctx, state, layout, setCanRender) {
2687
2827
  setCanRender(true);
2688
2828
  }
2689
2829
 
2690
- // src/core/onScroll.ts
2691
- function onScroll(ctx, state, event) {
2692
- var _a3, _b, _c;
2693
- const {
2694
- scrollProcessingEnabled,
2695
- props: { onScroll: onScrollProp }
2696
- } = state;
2697
- if (scrollProcessingEnabled === false) {
2698
- return;
2699
- }
2700
- if (((_b = (_a3 = event.nativeEvent) == null ? void 0 : _a3.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
2701
- return;
2702
- }
2703
- const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2704
- state.scrollPending = newScroll;
2705
- updateScroll(ctx, state, newScroll);
2706
- onScrollProp == null ? void 0 : onScrollProp(event);
2707
- }
2708
- function updateScroll(ctx, state, newScroll) {
2709
- const scrollingTo = state.scrollingTo;
2710
- state.hasScrolled = true;
2711
- state.lastBatchingAction = Date.now();
2712
- const currentTime = Date.now();
2713
- const adjust = state.scrollAdjustHandler.getAdjust();
2714
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
2715
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
2716
- if (adjustChanged) {
2717
- state.scrollHistory.length = 0;
2718
- }
2719
- state.lastScrollAdjustForHistory = adjust;
2720
- if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
2721
- if (!adjustChanged) {
2722
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
2723
- }
2724
- }
2725
- if (state.scrollHistory.length > 5) {
2726
- state.scrollHistory.shift();
2727
- }
2728
- state.scrollPrev = state.scroll;
2729
- state.scrollPrevTime = state.scrollTime;
2730
- state.scroll = newScroll;
2731
- state.scrollTime = currentTime;
2732
- const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
2733
- if (ignoreScrollFromMVCP && !state.scrollingTo) {
2734
- const { lt, gt } = ignoreScrollFromMVCP;
2735
- if (lt && newScroll < lt || gt && newScroll > gt) {
2736
- return;
2737
- }
2738
- }
2739
- if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
2740
- calculateItemsInView(ctx, state, { doMVCP: state.scrollingTo !== void 0 });
2741
- checkAtBottom(ctx, state);
2742
- checkAtTop(state);
2743
- state.dataChangeNeedsScrollUpdate = false;
2744
- }
2745
- }
2746
-
2747
2830
  // src/core/ScrollAdjustHandler.ts
2748
2831
  var ScrollAdjustHandler = class {
2749
2832
  constructor(ctx) {
2750
2833
  this.appliedAdjust = 0;
2834
+ this.pendingAdjust = 0;
2751
2835
  this.mounted = false;
2752
2836
  this.context = ctx;
2837
+ {
2838
+ const commitPendingAdjust = () => {
2839
+ const state = this.context.internalState;
2840
+ const pending = this.pendingAdjust;
2841
+ if (pending !== 0) {
2842
+ this.pendingAdjust = 0;
2843
+ this.appliedAdjust += pending;
2844
+ state.scroll += pending;
2845
+ state.scrollForNextCalculateItemsInView = void 0;
2846
+ set$(this.context, "scrollAdjustPending", 0);
2847
+ set$(this.context, "scrollAdjust", this.appliedAdjust);
2848
+ calculateItemsInView(this.context, this.context.internalState);
2849
+ }
2850
+ };
2851
+ listen$(this.context, "scrollingTo", (value) => {
2852
+ if (value === void 0) {
2853
+ commitPendingAdjust();
2854
+ }
2855
+ });
2856
+ }
2753
2857
  }
2754
2858
  requestAdjust(add) {
2755
- const oldAdjustTop = this.appliedAdjust;
2756
- this.appliedAdjust = add + oldAdjustTop;
2757
- const set = () => set$(this.context, "scrollAdjust", this.appliedAdjust);
2758
- if (this.mounted) {
2759
- set();
2859
+ const scrollingTo = peek$(this.context, "scrollingTo");
2860
+ if ((scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2861
+ this.pendingAdjust += add;
2862
+ set$(this.context, "scrollAdjustPending", this.pendingAdjust);
2760
2863
  } else {
2761
- requestAnimationFrame(set);
2864
+ this.appliedAdjust += add;
2865
+ const setter = () => set$(this.context, "scrollAdjust", this.appliedAdjust);
2866
+ if (this.mounted) {
2867
+ setter();
2868
+ } else {
2869
+ requestAnimationFrame(setter);
2870
+ }
2762
2871
  }
2763
2872
  }
2764
2873
  setMounted() {
@@ -2806,8 +2915,8 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2806
2915
  let minIndexSizeChanged;
2807
2916
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2808
2917
  const prevSizeKnown = state.sizesKnown.get(itemKey);
2809
- const diff = updateOneItemSize(state, itemKey, sizeObj);
2810
- const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2918
+ const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
2919
+ const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
2811
2920
  if (diff !== 0) {
2812
2921
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2813
2922
  const { startBuffered, endBuffered } = state;
@@ -2828,7 +2937,6 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2828
2937
  if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2829
2938
  shouldMaintainScrollAtEnd = true;
2830
2939
  }
2831
- addTotalSize(ctx, state, itemKey, diff);
2832
2940
  onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2833
2941
  index,
2834
2942
  itemData: state.props.data[index],
@@ -2868,7 +2976,7 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2868
2976
  }
2869
2977
  }
2870
2978
  }
2871
- function updateOneItemSize(state, itemKey, sizeObj) {
2979
+ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2872
2980
  var _a3;
2873
2981
  const {
2874
2982
  sizes,
@@ -2879,7 +2987,7 @@ function updateOneItemSize(state, itemKey, sizeObj) {
2879
2987
  } = state;
2880
2988
  if (!data) return 0;
2881
2989
  const index = indexByKey.get(itemKey);
2882
- const prevSize = getItemSize(state, itemKey, index, data[index]);
2990
+ const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
2883
2991
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
2884
2992
  const size = Math.round(rawSize) ;
2885
2993
  sizesKnown.set(itemKey, size);
@@ -2893,7 +3001,7 @@ function updateOneItemSize(state, itemKey, sizeObj) {
2893
3001
  averages.num++;
2894
3002
  }
2895
3003
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2896
- sizes.set(itemKey, size);
3004
+ setSize(ctx, state, itemKey, size);
2897
3005
  return size - prevSize;
2898
3006
  }
2899
3007
  return 0;
@@ -2938,6 +3046,91 @@ function createColumnWrapperStyle(contentContainerStyle) {
2938
3046
  };
2939
3047
  }
2940
3048
  }
3049
+
3050
+ // src/utils/createImperativeHandle.ts
3051
+ function createImperativeHandle(ctx, state) {
3052
+ const scrollIndexIntoView = (options) => {
3053
+ if (state) {
3054
+ const { index, ...rest } = options;
3055
+ const { startNoBuffer, endNoBuffer } = state;
3056
+ if (index < startNoBuffer || index > endNoBuffer) {
3057
+ const viewPosition = index < startNoBuffer ? 0 : 1;
3058
+ scrollToIndex(ctx, state, {
3059
+ ...rest,
3060
+ index,
3061
+ viewPosition
3062
+ });
3063
+ }
3064
+ }
3065
+ };
3066
+ const refScroller = state.refScroller;
3067
+ return {
3068
+ flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
3069
+ getNativeScrollRef: () => refScroller.current,
3070
+ getScrollableNode: () => refScroller.current.getScrollableNode(),
3071
+ getScrollResponder: () => refScroller.current.getScrollResponder(),
3072
+ getState: () => ({
3073
+ activeStickyIndex: state.activeStickyIndex,
3074
+ contentLength: state.totalSize,
3075
+ data: state.props.data,
3076
+ elementAtIndex: (index) => {
3077
+ var _a3;
3078
+ return (_a3 = ctx.viewRefs.get(findContainerId(ctx, getId(state, index)))) == null ? void 0 : _a3.current;
3079
+ },
3080
+ end: state.endNoBuffer,
3081
+ endBuffered: state.endBuffered,
3082
+ isAtEnd: state.isAtEnd,
3083
+ isAtStart: state.isAtStart,
3084
+ positionAtIndex: (index) => state.positions.get(getId(state, index)),
3085
+ positions: state.positions,
3086
+ scroll: state.scroll,
3087
+ scrollLength: state.scrollLength,
3088
+ sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
3089
+ sizes: state.sizesKnown,
3090
+ start: state.startNoBuffer,
3091
+ startBuffered: state.startBuffered
3092
+ }),
3093
+ scrollIndexIntoView,
3094
+ scrollItemIntoView: ({ item, ...props }) => {
3095
+ const data = state.props.data;
3096
+ const index = data.indexOf(item);
3097
+ if (index !== -1) {
3098
+ scrollIndexIntoView({ index, ...props });
3099
+ }
3100
+ },
3101
+ scrollToEnd: (options) => {
3102
+ const data = state.props.data;
3103
+ const stylePaddingBottom = state.props.stylePaddingBottom;
3104
+ const index = data.length - 1;
3105
+ if (index !== -1) {
3106
+ const paddingBottom = stylePaddingBottom || 0;
3107
+ const footerSize = peek$(ctx, "footerSize") || 0;
3108
+ scrollToIndex(ctx, state, {
3109
+ index,
3110
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3111
+ viewPosition: 1,
3112
+ ...options
3113
+ });
3114
+ }
3115
+ },
3116
+ scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3117
+ scrollToItem: ({ item, ...props }) => {
3118
+ const data = state.props.data;
3119
+ const index = data.indexOf(item);
3120
+ if (index !== -1) {
3121
+ scrollToIndex(ctx, state, { index, ...props });
3122
+ }
3123
+ },
3124
+ scrollToOffset: (params) => scrollTo(ctx, state, params),
3125
+ setScrollProcessingEnabled: (enabled) => {
3126
+ state.scrollProcessingEnabled = enabled;
3127
+ },
3128
+ setVisibleContentAnchorOffset: (value) => {
3129
+ const val = isFunction(value) ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
3130
+ set$(ctx, "scrollAdjustUserOffset", val);
3131
+ }
3132
+ };
3133
+ }
2941
3134
  function getRenderedItem(ctx, state, key) {
2942
3135
  var _a3;
2943
3136
  if (!state) {
@@ -3038,6 +3231,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3038
3231
  columnWrapperStyle,
3039
3232
  contentContainerStyle: contentContainerStyleProp,
3040
3233
  data: dataProp = [],
3234
+ dataVersion,
3041
3235
  drawDistance = 250,
3042
3236
  enableAverages = true,
3043
3237
  estimatedItemSize: estimatedItemSizeProp,
@@ -3048,6 +3242,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3048
3242
  getItemType,
3049
3243
  horizontal,
3050
3244
  initialContainerPoolRatio = 2,
3245
+ initialScrollAtEnd = false,
3051
3246
  initialScrollIndex: initialScrollIndexProp,
3052
3247
  initialScrollOffset: initialScrollOffsetProp,
3053
3248
  itemsAreEqual,
@@ -3086,13 +3281,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3086
3281
  waitForInitialLayout = true,
3087
3282
  ...rest
3088
3283
  } = props;
3089
- const [renderNum, setRenderNum] = useState(0);
3090
- const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3091
- const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
3092
3284
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3093
3285
  const style = { ...StyleSheet.flatten(styleProp) };
3094
3286
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3095
3287
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3288
+ const [renderNum, setRenderNum] = useState(0);
3289
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3290
+ const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
3096
3291
  const ctx = useStateContext();
3097
3292
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
3098
3293
  const refScroller = useRef(null);
@@ -3111,6 +3306,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3111
3306
  containerItemKeys: /* @__PURE__ */ new Set(),
3112
3307
  containerItemTypes: /* @__PURE__ */ new Map(),
3113
3308
  dataChangeNeedsScrollUpdate: false,
3309
+ didColumnsChange: false,
3310
+ didDataChange: false,
3114
3311
  enableScrollForNextCalculateItemsInView: true,
3115
3312
  endBuffered: -1,
3116
3313
  endNoBuffer: -1,
@@ -3119,10 +3316,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3119
3316
  idCache: [],
3120
3317
  idsInView: [],
3121
3318
  indexByKey: /* @__PURE__ */ new Map(),
3122
- initialScroll,
3319
+ initialScroll: initialScrollProp,
3123
3320
  isAtEnd: false,
3124
3321
  isAtStart: false,
3125
3322
  isEndReached: false,
3323
+ isFirst: true,
3126
3324
  isStartReached: false,
3127
3325
  lastBatchingAction: Date.now(),
3128
3326
  lastLayout: void 0,
@@ -3155,21 +3353,27 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3155
3353
  totalSize: 0,
3156
3354
  viewabilityConfigCallbackPairs: void 0
3157
3355
  };
3356
+ const internalState = ctx.internalState;
3357
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3158
3358
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3159
3359
  set$(ctx, "extraData", extraData);
3160
3360
  }
3161
3361
  refState.current = ctx.internalState;
3162
3362
  }
3163
3363
  const state = refState.current;
3164
- const isFirst = !state.props.renderItem;
3165
- const didDataChange = state.props.data !== dataProp;
3166
- if (didDataChange) {
3364
+ const isFirstLocal = state.isFirst;
3365
+ state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3366
+ const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3367
+ if (didDataChangeLocal) {
3167
3368
  state.dataChangeNeedsScrollUpdate = true;
3369
+ state.didDataChange = true;
3370
+ state.previousData = state.props.data;
3168
3371
  }
3169
3372
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3170
3373
  state.props = {
3171
3374
  alignItemsAtEnd,
3172
3375
  data: dataProp,
3376
+ dataVersion,
3173
3377
  enableAverages,
3174
3378
  estimatedItemSize,
3175
3379
  getEstimatedItemSize,
@@ -3177,7 +3381,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3177
3381
  getItemType,
3178
3382
  horizontal: !!horizontal,
3179
3383
  initialContainerPoolRatio,
3180
- initialScroll,
3181
3384
  itemsAreEqual,
3182
3385
  keyExtractor,
3183
3386
  maintainScrollAtEnd,
@@ -3209,7 +3412,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3209
3412
  { length: Math.min(numColumnsProp, dataProp.length) },
3210
3413
  (_, i) => getId(state, dataProp.length - 1 - i)
3211
3414
  );
3212
- }, [dataProp, numColumnsProp]);
3415
+ }, [dataProp, dataVersion, numColumnsProp]);
3213
3416
  const initializeStateVars = () => {
3214
3417
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3215
3418
  set$(ctx, "numColumns", numColumnsProp);
@@ -3224,7 +3427,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3224
3427
  requestAdjust(ctx, state, paddingDiff);
3225
3428
  }
3226
3429
  };
3227
- if (isFirst) {
3430
+ if (isFirstLocal) {
3228
3431
  initializeStateVars();
3229
3432
  updateItemPositions(
3230
3433
  ctx,
@@ -3234,38 +3437,42 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3234
3437
  );
3235
3438
  }
3236
3439
  const initialContentOffset = useMemo(() => {
3237
- if (initialScroll) {
3238
- const { index, viewOffset } = initialScroll;
3239
- let initialContentOffset2 = viewOffset || 0;
3240
- if (index !== void 0) {
3241
- initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
3242
- }
3243
- refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
3244
- if (initialContentOffset2 > 0) {
3245
- scrollTo(state, {
3246
- animated: false,
3247
- index,
3248
- isInitialScroll: true,
3249
- offset: initialContentOffset2,
3250
- viewPosition: index === dataProp.length - 1 ? 1 : 0
3251
- });
3252
- }
3253
- return initialContentOffset2;
3254
- }
3255
- return 0;
3256
- }, [renderNum]);
3257
- if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
3440
+ const { initialScroll } = refState.current;
3441
+ if (!initialScroll) {
3442
+ return 0;
3443
+ }
3444
+ if (initialScroll.contentOffset !== void 0) {
3445
+ return initialScroll.contentOffset;
3446
+ }
3447
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3448
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3449
+ let clampedOffset = resolvedOffset;
3450
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3451
+ const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3452
+ clampedOffset = Math.min(clampedOffset, maxOffset);
3453
+ }
3454
+ clampedOffset = Math.max(0, clampedOffset);
3455
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3456
+ refState.current.initialScroll = updatedInitialScroll;
3457
+ state.initialScroll = updatedInitialScroll;
3458
+ refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3459
+ return clampedOffset;
3460
+ }, [renderNum, state.initialScroll]);
3461
+ if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3258
3462
  refState.current.lastBatchingAction = Date.now();
3259
- if (!keyExtractorProp && !isFirst && didDataChange) {
3463
+ if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
3260
3464
  IS_DEV && warnDevOnce(
3261
3465
  "keyExtractor",
3262
3466
  "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
3263
3467
  );
3264
3468
  refState.current.sizes.clear();
3265
3469
  refState.current.positions.clear();
3470
+ refState.current.totalSize = 0;
3471
+ set$(ctx, "totalSize", 0);
3266
3472
  }
3267
3473
  }
3268
3474
  const onLayoutHeader = useCallback((rect, fromLayoutEffect) => {
3475
+ const { initialScroll } = refState.current;
3269
3476
  const size = rect[horizontal ? "width" : "height"];
3270
3477
  set$(ctx, "headerSize", size);
3271
3478
  if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
@@ -3276,31 +3483,57 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3276
3483
  }
3277
3484
  }
3278
3485
  }, []);
3486
+ const doInitialScroll = useCallback(() => {
3487
+ var _a4;
3488
+ const initialScroll = state.initialScroll;
3489
+ if (initialScroll) {
3490
+ scrollTo(ctx, state, {
3491
+ animated: false,
3492
+ index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3493
+ offset: initialContentOffset,
3494
+ precomputedWithViewOffset: true
3495
+ });
3496
+ }
3497
+ }, [initialContentOffset, state.initialScroll]);
3498
+ const onLayoutChange = useCallback((layout) => {
3499
+ doInitialScroll();
3500
+ handleLayout(ctx, state, layout, setCanRender);
3501
+ }, []);
3502
+ const { onLayout } = useOnLayoutSync({
3503
+ onLayoutChange,
3504
+ onLayoutProp,
3505
+ ref: refScroller
3506
+ // the type of ScrollView doesn't include measure?
3507
+ });
3279
3508
  useLayoutEffect(() => {
3280
3509
  if (snapToIndices) {
3281
3510
  updateSnapToOffsets(ctx, state);
3282
3511
  }
3283
3512
  }, [snapToIndices]);
3284
3513
  useLayoutEffect(() => {
3285
- const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainers(ctx, state);
3286
- if (!didAllocateContainers) {
3287
- checkResetContainers(
3288
- ctx,
3289
- state,
3290
- /*isFirst*/
3291
- isFirst,
3292
- dataProp
3293
- );
3294
- }
3295
- }, [dataProp, numColumnsProp]);
3514
+ const {
3515
+ didColumnsChange,
3516
+ didDataChange,
3517
+ isFirst,
3518
+ props: { data }
3519
+ } = state;
3520
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3521
+ if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3522
+ checkResetContainers(ctx, state, data);
3523
+ }
3524
+ state.didColumnsChange = false;
3525
+ state.didDataChange = false;
3526
+ state.isFirst = false;
3527
+ }, [dataProp, dataVersion, numColumnsProp]);
3296
3528
  useLayoutEffect(() => {
3297
3529
  set$(ctx, "extraData", extraData);
3298
3530
  }, [extraData]);
3299
3531
  useLayoutEffect(initializeStateVars, [
3532
+ dataVersion,
3300
3533
  memoizedLastItemKeys.join(","),
3301
3534
  numColumnsProp,
3302
- stylePaddingTopState,
3303
- stylePaddingBottomState
3535
+ stylePaddingBottomState,
3536
+ stylePaddingTopState
3304
3537
  ]);
3305
3538
  useEffect(() => {
3306
3539
  const viewability = setupViewability({
@@ -3311,103 +3544,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3311
3544
  state.viewabilityConfigCallbackPairs = viewability;
3312
3545
  state.enableScrollForNextCalculateItemsInView = !viewability;
3313
3546
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3314
- const onLayoutChange = useCallback((layout) => {
3315
- handleLayout(ctx, state, layout, setCanRender);
3316
- }, []);
3317
- const { onLayout } = useOnLayoutSync({
3318
- onLayoutChange,
3319
- onLayoutProp,
3320
- ref: refScroller
3321
- // the type of ScrollView doesn't include measure?
3322
- });
3323
- useImperativeHandle(forwardedRef, () => {
3324
- const scrollIndexIntoView = (options) => {
3325
- const state2 = refState.current;
3326
- if (state2) {
3327
- const { index, ...rest2 } = options;
3328
- const { startNoBuffer, endNoBuffer } = state2;
3329
- if (index < startNoBuffer || index > endNoBuffer) {
3330
- const viewPosition = index < startNoBuffer ? 0 : 1;
3331
- scrollToIndex(ctx, state2, {
3332
- ...rest2,
3333
- index,
3334
- viewPosition
3335
- });
3336
- }
3337
- }
3338
- };
3339
- return {
3340
- flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
3341
- getNativeScrollRef: () => refScroller.current,
3342
- getScrollableNode: () => refScroller.current.getScrollableNode(),
3343
- getScrollResponder: () => refScroller.current.getScrollResponder(),
3344
- getState: () => {
3345
- const state2 = refState.current;
3346
- return state2 ? {
3347
- activeStickyIndex: state2.activeStickyIndex,
3348
- contentLength: state2.totalSize,
3349
- data: state2.props.data,
3350
- end: state2.endNoBuffer,
3351
- endBuffered: state2.endBuffered,
3352
- isAtEnd: state2.isAtEnd,
3353
- isAtStart: state2.isAtStart,
3354
- positionAtIndex: (index) => state2.positions.get(getId(state2, index)),
3355
- positions: state2.positions,
3356
- scroll: state2.scroll,
3357
- scrollLength: state2.scrollLength,
3358
- sizeAtIndex: (index) => state2.sizesKnown.get(getId(state2, index)),
3359
- sizes: state2.sizesKnown,
3360
- start: state2.startNoBuffer,
3361
- startBuffered: state2.startBuffered
3362
- } : {};
3363
- },
3364
- scrollIndexIntoView,
3365
- scrollItemIntoView: ({ item, ...props2 }) => {
3366
- const data = refState.current.props.data;
3367
- const index = data.indexOf(item);
3368
- if (index !== -1) {
3369
- scrollIndexIntoView({ index, ...props2 });
3370
- }
3371
- },
3372
- scrollToEnd: (options) => {
3373
- const data = refState.current.props.data;
3374
- const stylePaddingBottom = refState.current.props.stylePaddingBottom;
3375
- const index = data.length - 1;
3376
- if (index !== -1) {
3377
- const paddingBottom = stylePaddingBottom || 0;
3378
- const footerSize = peek$(ctx, "footerSize") || 0;
3379
- scrollToIndex(ctx, state, {
3380
- index,
3381
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3382
- viewPosition: 1,
3383
- ...options
3384
- });
3385
- }
3386
- },
3387
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3388
- scrollToItem: ({ item, ...props2 }) => {
3389
- const data = refState.current.props.data;
3390
- const index = data.indexOf(item);
3391
- if (index !== -1) {
3392
- scrollToIndex(ctx, state, { index, ...props2 });
3393
- }
3394
- },
3395
- scrollToOffset: (params) => scrollTo(state, params),
3396
- setScrollProcessingEnabled: (enabled) => {
3397
- refState.current.scrollProcessingEnabled = enabled;
3398
- },
3399
- setVisibleContentAnchorOffset: (value) => {
3400
- const val = typeof value === "function" ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
3401
- set$(ctx, "scrollAdjustUserOffset", val);
3402
- }
3403
- };
3404
- }, []);
3547
+ useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
3405
3548
  {
3406
- useEffect(() => {
3407
- if (initialContentOffset) {
3408
- scrollTo(state, { animated: false, offset: initialContentOffset, ...initialScroll || {} });
3409
- }
3410
- }, []);
3549
+ useEffect(doInitialScroll, []);
3411
3550
  }
3412
3551
  const fns = useMemo(
3413
3552
  () => ({
@@ -3436,7 +3575,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3436
3575
  onMomentumScrollEnd: (event) => {
3437
3576
  {
3438
3577
  requestAnimationFrame(() => {
3439
- finishScrollTo(refState.current);
3578
+ finishScrollTo(ctx, refState.current);
3440
3579
  });
3441
3580
  }
3442
3581
  if (onMomentumScrollEnd) {