@legendapp/list 2.1.0-beta.1 → 2.1.0-beta.11

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.js CHANGED
@@ -54,7 +54,10 @@ function StateProvider({ children }) {
54
54
  ["stylePaddingTop", 0],
55
55
  ["headerSize", 0],
56
56
  ["numContainers", 0],
57
- ["totalSize", 0]
57
+ ["activeStickyIndex", void 0],
58
+ ["totalSize", 0],
59
+ ["scrollAdjustPending", 0],
60
+ ["scrollingTo", void 0]
58
61
  ]),
59
62
  viewRefs: /* @__PURE__ */ new Map()
60
63
  }));
@@ -123,11 +126,12 @@ function set$(ctx, signalName, value) {
123
126
  }
124
127
  }
125
128
  function getContentSize(ctx) {
129
+ var _a3, _b;
126
130
  const { values } = ctx;
127
131
  const stylePaddingTop = values.get("stylePaddingTop") || 0;
128
132
  const headerSize = values.get("headerSize") || 0;
129
133
  const footerSize = values.get("footerSize") || 0;
130
- const totalSize = values.get("totalSize");
134
+ const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
131
135
  return headerSize + footerSize + totalSize + stylePaddingTop;
132
136
  }
133
137
  function useArr$(signalNames) {
@@ -208,6 +212,47 @@ var ENABLE_DEBUG_VIEW = IS_DEV && false;
208
212
  var typedForwardRef = React3.forwardRef;
209
213
  var typedMemo = React3.memo;
210
214
 
215
+ // src/utils/helpers.ts
216
+ function isFunction(obj) {
217
+ return typeof obj === "function";
218
+ }
219
+ function isArray(obj) {
220
+ return Array.isArray(obj);
221
+ }
222
+ var warned = /* @__PURE__ */ new Set();
223
+ function warnDevOnce(id, text) {
224
+ if (IS_DEV && !warned.has(id)) {
225
+ warned.add(id);
226
+ console.warn(`[legend-list] ${text}`);
227
+ }
228
+ }
229
+ function roundSize(size) {
230
+ return Math.floor(size * 8) / 8;
231
+ }
232
+ function isNullOrUndefined(value) {
233
+ return value === null || value === void 0;
234
+ }
235
+ function comparatorDefault(a, b) {
236
+ return a - b;
237
+ }
238
+ function getPadding(s, type) {
239
+ var _a3, _b, _c;
240
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
241
+ }
242
+ function extractPadding(style, contentContainerStyle, type) {
243
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
244
+ }
245
+ function findContainerId(ctx, key) {
246
+ const numContainers = peek$(ctx, "numContainers");
247
+ for (let i = 0; i < numContainers; i++) {
248
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
249
+ if (itemKey === key) {
250
+ return i;
251
+ }
252
+ }
253
+ return -1;
254
+ }
255
+
211
256
  // src/components/PositionView.tsx
212
257
  var PositionViewState = typedMemo(function PositionView({
213
258
  id,
@@ -217,8 +262,11 @@ var PositionViewState = typedMemo(function PositionView({
217
262
  ...rest
218
263
  }) {
219
264
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
220
- const base = Array.isArray(style) ? Object.assign({}, ...style) : style;
221
- const combinedStyle = horizontal ? { ...base, left: position } : { ...base, top: position };
265
+ const base = {
266
+ contain: "paint layout style"
267
+ };
268
+ const composed = isArray(style) ? Object.assign({}, ...style) : style;
269
+ const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
222
270
  return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: combinedStyle, ...rest });
223
271
  });
224
272
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
@@ -227,19 +275,42 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
227
275
  style,
228
276
  refView,
229
277
  index,
278
+ stickyOffset,
279
+ animatedScrollY: _animatedScrollY,
280
+ children,
230
281
  ...rest
231
282
  }) {
232
- const [position = POSITION_OUT_OF_VIEW, _headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
283
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, activeStickyIndex] = useArr$([
284
+ `containerPosition${id}`,
285
+ "headerSize",
286
+ "activeStickyIndex"
287
+ ]);
288
+ const base = {
289
+ contain: "paint layout style"
290
+ };
291
+ const composed = React3__namespace.useMemo(
292
+ () => {
293
+ var _a3;
294
+ return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
295
+ },
296
+ [style]
297
+ );
233
298
  const viewStyle = React3__namespace.useMemo(() => {
234
- const base = Array.isArray(style) ? Object.assign({}, ...style) : style;
235
- const axisStyle = horizontal ? { transform: `translateX(${position}px)` } : { top: position };
236
- return {
237
- ...base,
238
- zIndex: index + 1e3,
239
- ...axisStyle
240
- };
241
- }, [style, position, horizontal, index]);
242
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: viewStyle, ...rest });
299
+ var _a3;
300
+ const styleBase = { ...base, ...composed };
301
+ delete styleBase.transform;
302
+ const offset = (_a3 = stickyOffset != null ? stickyOffset : headerSize) != null ? _a3 : 0;
303
+ const isActive = activeStickyIndex === index;
304
+ styleBase.position = isActive ? "sticky" : "absolute";
305
+ styleBase.zIndex = index + 1e3;
306
+ if (horizontal) {
307
+ styleBase.left = isActive ? offset : position;
308
+ } else {
309
+ styleBase.top = isActive ? offset : position;
310
+ }
311
+ return styleBase;
312
+ }, [composed, horizontal, position, index, stickyOffset, headerSize, activeStickyIndex]);
313
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
243
314
  });
244
315
  var PositionView2 = PositionViewState;
245
316
 
@@ -254,37 +325,6 @@ function useInit(cb) {
254
325
  return refValue.current;
255
326
  }
256
327
 
257
- // src/utils/helpers.ts
258
- function isFunction(obj) {
259
- return typeof obj === "function";
260
- }
261
- function isArray(obj) {
262
- return Array.isArray(obj);
263
- }
264
- var warned = /* @__PURE__ */ new Set();
265
- function warnDevOnce(id, text) {
266
- if (IS_DEV && !warned.has(id)) {
267
- warned.add(id);
268
- console.warn(`[legend-list] ${text}`);
269
- }
270
- }
271
- function roundSize(size) {
272
- return Math.floor(size * 8) / 8;
273
- }
274
- function isNullOrUndefined(value) {
275
- return value === null || value === void 0;
276
- }
277
- function comparatorDefault(a, b) {
278
- return a - b;
279
- }
280
- function getPadding(s, type) {
281
- var _a3, _b, _c;
282
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
283
- }
284
- function extractPadding(style, contentContainerStyle, type) {
285
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
286
- }
287
-
288
328
  // src/state/ContextContainer.ts
289
329
  var ContextContainer = React3.createContext(null);
290
330
  function useViewability(callback, configId) {
@@ -413,6 +453,10 @@ function getGlobalResizeObserver() {
413
453
  }
414
454
  var callbackMap = /* @__PURE__ */ new WeakMap();
415
455
  function createResizeObserver(element, callback) {
456
+ if (typeof ResizeObserver === "undefined") {
457
+ return () => {
458
+ };
459
+ }
416
460
  if (!element) {
417
461
  return () => {
418
462
  };
@@ -448,7 +492,7 @@ function useOnLayoutSync({
448
492
  const current = ref.current;
449
493
  const scrollableNode = (_b = (_a3 = current == null ? void 0 : current.getScrollableNode) == null ? void 0 : _a3.call(current)) != null ? _b : null;
450
494
  const element = scrollableNode || current;
451
- if (!element || !(element instanceof HTMLElement)) {
495
+ if (!element) {
452
496
  return;
453
497
  }
454
498
  const emit = (layout, fromLayoutEffect) => {
@@ -470,6 +514,9 @@ function useOnLayoutSync({
470
514
  return {};
471
515
  }
472
516
  function toLayout(rect) {
517
+ if (!rect) {
518
+ return { height: 0, width: 0, x: 0, y: 0 };
519
+ }
473
520
  return {
474
521
  height: rect.height,
475
522
  width: rect.width,
@@ -571,7 +618,7 @@ var Container = typedMemo(function Container2({
571
618
  }
572
619
  didLayoutRef.current = true;
573
620
  let layout = rectangle;
574
- Math.floor(rectangle[currentHorizontal ? "width" : "height"] * 8) / 8;
621
+ roundSize(rectangle[currentHorizontal ? "width" : "height"]);
575
622
  const doUpdate = () => {
576
623
  itemLayoutRef.current.lastSize = { height: layout.height, width: layout.width };
577
624
  updateItemSizeFn(currentItemKey, layout);
@@ -729,7 +776,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
729
776
  const columnWrapperStyle = ctx.columnWrapperStyle;
730
777
  const [totalSize, otherAxisSize] = useArr$(["totalSize", "otherAxisSize"]);
731
778
  useDOMOrder(ref);
732
- const style = horizontal ? { minHeight: otherAxisSize, width: totalSize } : { height: totalSize, minWidth: otherAxisSize };
779
+ const style = horizontal ? { minHeight: otherAxisSize, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, position: "relative" };
733
780
  if (columnWrapperStyle && numColumns > 1) {
734
781
  const { columnGap, rowGap, gap } = columnWrapperStyle;
735
782
  const gapX = columnGap || gap || 0;
@@ -801,7 +848,7 @@ function DevNumbers() {
801
848
 
802
849
  // src/platform/StyleSheet.tsx
803
850
  function flattenStyles(styles) {
804
- if (Array.isArray(styles)) {
851
+ if (isArray(styles)) {
805
852
  return Object.assign({}, ...styles.filter(Boolean));
806
853
  }
807
854
  return styles;
@@ -991,10 +1038,11 @@ function PaddingDevMode() {
991
1038
  function useValueListener$(key, callback) {
992
1039
  const ctx = useStateContext();
993
1040
  React3.useLayoutEffect(() => {
994
- listen$(ctx, key, (value) => {
1041
+ const unsubscribe = listen$(ctx, key, (value) => {
995
1042
  callback(value);
996
1043
  });
997
- }, []);
1044
+ return unsubscribe;
1045
+ }, [callback, ctx, key]);
998
1046
  }
999
1047
 
1000
1048
  // src/components/ScrollAdjust.tsx
@@ -1159,13 +1207,84 @@ function calculateOffsetForIndex(ctx, state, index) {
1159
1207
  return position;
1160
1208
  }
1161
1209
 
1210
+ // src/utils/setPaddingTop.ts
1211
+ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1212
+ if (stylePaddingTop !== void 0) {
1213
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1214
+ if (stylePaddingTop < prevStylePaddingTop) {
1215
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
1216
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1217
+ state.timeoutSetPaddingTop = setTimeout(() => {
1218
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
1219
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1220
+ }, 16);
1221
+ }
1222
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
1223
+ }
1224
+ if (alignItemsPaddingTop !== void 0) {
1225
+ set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1226
+ }
1227
+ }
1228
+
1229
+ // src/utils/updateAlignItemsPaddingTop.ts
1230
+ function updateAlignItemsPaddingTop(ctx, state) {
1231
+ const {
1232
+ scrollLength,
1233
+ props: { alignItemsAtEnd, data }
1234
+ } = state;
1235
+ if (alignItemsAtEnd) {
1236
+ let alignItemsPaddingTop = 0;
1237
+ if ((data == null ? void 0 : data.length) > 0) {
1238
+ const contentSize = getContentSize(ctx);
1239
+ alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1240
+ }
1241
+ setPaddingTop(ctx, state, { alignItemsPaddingTop });
1242
+ }
1243
+ }
1244
+
1245
+ // src/core/addTotalSize.ts
1246
+ function addTotalSize(ctx, state, key, add) {
1247
+ const { alignItemsAtEnd } = state.props;
1248
+ const prevTotalSize = state.totalSize;
1249
+ let totalSize = state.totalSize;
1250
+ if (key === null) {
1251
+ totalSize = add;
1252
+ if (state.timeoutSetPaddingTop) {
1253
+ clearTimeout(state.timeoutSetPaddingTop);
1254
+ state.timeoutSetPaddingTop = void 0;
1255
+ }
1256
+ } else {
1257
+ totalSize += add;
1258
+ }
1259
+ if (prevTotalSize !== totalSize) {
1260
+ {
1261
+ state.pendingTotalSize = void 0;
1262
+ state.totalSize = totalSize;
1263
+ set$(ctx, "totalSize", totalSize);
1264
+ if (alignItemsAtEnd) {
1265
+ updateAlignItemsPaddingTop(ctx, state);
1266
+ }
1267
+ }
1268
+ }
1269
+ }
1270
+
1271
+ // src/core/setSize.ts
1272
+ function setSize(ctx, state, itemKey, size) {
1273
+ const { sizes } = state;
1274
+ const previousSize = sizes.get(itemKey);
1275
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1276
+ if (diff !== 0) {
1277
+ addTotalSize(ctx, state, itemKey, diff);
1278
+ }
1279
+ sizes.set(itemKey, size);
1280
+ }
1281
+
1162
1282
  // src/utils/getItemSize.ts
1163
- function getItemSize(state, key, index, data, useAverageSize) {
1283
+ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1164
1284
  var _a3, _b;
1165
1285
  const {
1166
1286
  sizesKnown,
1167
1287
  sizes,
1168
- scrollingTo,
1169
1288
  averageSizes,
1170
1289
  props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1171
1290
  } = state;
@@ -1175,6 +1294,13 @@ function getItemSize(state, key, index, data, useAverageSize) {
1175
1294
  }
1176
1295
  let size;
1177
1296
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1297
+ const scrollingTo = peek$(ctx, "scrollingTo");
1298
+ if (preferCachedSize) {
1299
+ const cachedSize = sizes.get(key);
1300
+ if (cachedSize !== void 0) {
1301
+ return cachedSize;
1302
+ }
1303
+ }
1178
1304
  if (getFixedItemSize) {
1179
1305
  size = getFixedItemSize(index, data, itemType);
1180
1306
  if (size !== void 0) {
@@ -1196,108 +1322,326 @@ function getItemSize(state, key, index, data, useAverageSize) {
1196
1322
  if (size === void 0) {
1197
1323
  size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1198
1324
  }
1199
- sizes.set(key, size);
1325
+ setSize(ctx, state, key, size);
1200
1326
  return size;
1201
1327
  }
1202
1328
 
1203
1329
  // src/core/calculateOffsetWithOffsetPosition.ts
1204
- function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
1330
+ function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1205
1331
  const { index, viewOffset, viewPosition } = params;
1206
1332
  let offset = offsetParam;
1207
1333
  if (viewOffset) {
1208
1334
  offset -= viewOffset;
1209
1335
  }
1210
1336
  if (viewPosition !== void 0 && index !== void 0) {
1211
- offset -= viewPosition * (state.scrollLength - getItemSize(state, getId(state, index), index, state.props.data[index]));
1337
+ offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1212
1338
  }
1213
1339
  return offset;
1214
1340
  }
1215
1341
 
1216
- // src/core/finishScrollTo.ts
1217
- var finishScrollTo = (state) => {
1218
- if (state) {
1219
- state.scrollingTo = void 0;
1220
- state.scrollHistory.length = 0;
1342
+ // src/utils/checkThreshold.ts
1343
+ var HYSTERESIS_MULTIPLIER = 1.3;
1344
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1345
+ const absDistance = Math.abs(distance);
1346
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
1347
+ const updateSnapshot = () => {
1348
+ setSnapshot == null ? void 0 : setSnapshot({
1349
+ atThreshold,
1350
+ contentSize: context.contentSize,
1351
+ dataLength: context.dataLength,
1352
+ scrollPosition: context.scrollPosition
1353
+ });
1354
+ };
1355
+ if (!wasReached) {
1356
+ if (!within) {
1357
+ return false;
1358
+ }
1359
+ onReached == null ? void 0 : onReached(distance);
1360
+ updateSnapshot();
1361
+ return true;
1362
+ }
1363
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1364
+ if (reset) {
1365
+ setSnapshot == null ? void 0 : setSnapshot(void 0);
1366
+ return false;
1367
+ }
1368
+ if (within) {
1369
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1370
+ if (changed) {
1371
+ onReached == null ? void 0 : onReached(distance);
1372
+ updateSnapshot();
1373
+ }
1221
1374
  }
1375
+ return true;
1222
1376
  };
1223
1377
 
1224
- // src/core/scrollTo.ts
1225
- function scrollTo(state, params = {}) {
1378
+ // src/utils/checkAtBottom.ts
1379
+ function checkAtBottom(ctx, state) {
1226
1380
  var _a3;
1227
- const { animated, noScrollingTo, isInitialScroll } = params;
1381
+ if (!state) {
1382
+ return;
1383
+ }
1228
1384
  const {
1229
- refScroller,
1230
- props: { horizontal }
1385
+ queuedInitialLayout,
1386
+ scrollLength,
1387
+ scroll,
1388
+ maintainingScrollAtEnd,
1389
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1231
1390
  } = state;
1232
- const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
1233
- state.scrollHistory.length = 0;
1234
- if (!noScrollingTo) {
1235
- state.scrollingTo = params;
1236
- }
1237
- state.scrollPending = offset;
1238
- if (!params.isInitialScroll || Platform.OS === "android") {
1239
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1240
- animated: !!animated,
1241
- x: horizontal ? offset : 0,
1242
- y: horizontal ? 0 : offset
1243
- });
1244
- }
1245
- if (!animated) {
1246
- state.scroll = offset;
1247
- setTimeout(() => finishScrollTo(state), 100);
1248
- if (isInitialScroll) {
1249
- setTimeout(() => {
1250
- state.initialScroll = void 0;
1251
- }, 500);
1252
- }
1391
+ const contentSize = getContentSize(ctx);
1392
+ if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1393
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1394
+ const isContentLess = contentSize < scrollLength;
1395
+ state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1396
+ state.isEndReached = checkThreshold(
1397
+ distanceFromEnd,
1398
+ isContentLess,
1399
+ onEndReachedThreshold * scrollLength,
1400
+ state.isEndReached,
1401
+ state.endReachedSnapshot,
1402
+ {
1403
+ contentSize,
1404
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1405
+ scrollPosition: scroll
1406
+ },
1407
+ (distance) => {
1408
+ var _a4, _b;
1409
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1410
+ },
1411
+ (snapshot) => {
1412
+ state.endReachedSnapshot = snapshot;
1413
+ }
1414
+ );
1253
1415
  }
1254
1416
  }
1255
1417
 
1256
- // src/utils/requestAdjust.ts
1257
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1258
- if (Math.abs(positionDiff) > 0.1) {
1259
- const doit = () => {
1260
- {
1261
- state.scrollAdjustHandler.requestAdjust(positionDiff);
1262
- }
1263
- };
1264
- state.scroll += positionDiff;
1265
- state.scrollForNextCalculateItemsInView = void 0;
1266
- const didLayout = peek$(ctx, "containersDidLayout");
1267
- if (didLayout) {
1268
- doit();
1269
- const threshold = state.scroll - positionDiff / 2;
1270
- if (!state.ignoreScrollFromMVCP) {
1271
- state.ignoreScrollFromMVCP = {};
1272
- }
1273
- if (positionDiff > 0) {
1274
- state.ignoreScrollFromMVCP.lt = threshold;
1275
- } else {
1276
- state.ignoreScrollFromMVCP.gt = threshold;
1277
- }
1278
- if (state.ignoreScrollFromMVCPTimeout) {
1279
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
1280
- }
1281
- state.ignoreScrollFromMVCPTimeout = setTimeout(
1282
- () => {
1283
- state.ignoreScrollFromMVCP = void 0;
1284
- },
1285
- 100
1286
- );
1418
+ // src/utils/checkAtTop.ts
1419
+ function checkAtTop(state) {
1420
+ var _a3;
1421
+ if (!state) {
1422
+ return;
1423
+ }
1424
+ const {
1425
+ scrollLength,
1426
+ scroll,
1427
+ props: { onStartReachedThreshold }
1428
+ } = state;
1429
+ const distanceFromTop = scroll;
1430
+ state.isAtStart = distanceFromTop <= 0;
1431
+ state.isStartReached = checkThreshold(
1432
+ distanceFromTop,
1433
+ false,
1434
+ onStartReachedThreshold * scrollLength,
1435
+ state.isStartReached,
1436
+ state.startReachedSnapshot,
1437
+ {
1438
+ contentSize: state.totalSize,
1439
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1440
+ scrollPosition: scroll
1441
+ },
1442
+ (distance) => {
1443
+ var _a4, _b;
1444
+ return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
1445
+ },
1446
+ (snapshot) => {
1447
+ state.startReachedSnapshot = snapshot;
1448
+ }
1449
+ );
1450
+ }
1451
+
1452
+ // src/core/onScroll.ts
1453
+ function onScroll(ctx, state, event) {
1454
+ var _a3, _b, _c;
1455
+ const {
1456
+ scrollProcessingEnabled,
1457
+ props: { onScroll: onScrollProp }
1458
+ } = state;
1459
+ if (scrollProcessingEnabled === false) {
1460
+ return;
1461
+ }
1462
+ 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) {
1463
+ return;
1464
+ }
1465
+ const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
1466
+ state.scrollPending = newScroll;
1467
+ updateScroll(ctx, state, newScroll);
1468
+ onScrollProp == null ? void 0 : onScrollProp(event);
1469
+ }
1470
+ function updateScroll(ctx, state, newScroll, forceUpdate) {
1471
+ var _a3;
1472
+ const scrollingTo = peek$(ctx, "scrollingTo");
1473
+ state.hasScrolled = true;
1474
+ state.lastBatchingAction = Date.now();
1475
+ const currentTime = Date.now();
1476
+ const adjust = state.scrollAdjustHandler.getAdjust();
1477
+ const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1478
+ const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1479
+ if (adjustChanged) {
1480
+ state.scrollHistory.length = 0;
1481
+ }
1482
+ state.lastScrollAdjustForHistory = adjust;
1483
+ if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1484
+ if (!adjustChanged) {
1485
+ state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1486
+ }
1487
+ }
1488
+ if (state.scrollHistory.length > 5) {
1489
+ state.scrollHistory.shift();
1490
+ }
1491
+ state.scrollPrev = state.scroll;
1492
+ state.scrollPrevTime = state.scrollTime;
1493
+ state.scroll = newScroll;
1494
+ state.scrollTime = currentTime;
1495
+ const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
1496
+ if (ignoreScrollFromMVCP && !scrollingTo) {
1497
+ const { lt, gt } = ignoreScrollFromMVCP;
1498
+ if (lt && newScroll < lt || gt && newScroll > gt) {
1499
+ state.ignoreScrollFromMVCPIgnored = true;
1500
+ return;
1501
+ }
1502
+ }
1503
+ if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1504
+ state.ignoreScrollFromMVCPIgnored = false;
1505
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1506
+ checkAtBottom(ctx, state);
1507
+ checkAtTop(state);
1508
+ state.dataChangeNeedsScrollUpdate = false;
1509
+ }
1510
+ }
1511
+
1512
+ // src/core/finishScrollTo.ts
1513
+ function finishScrollTo(ctx, state) {
1514
+ var _a3, _b;
1515
+ if (state) {
1516
+ state.scrollHistory.length = 0;
1517
+ state.initialScroll = void 0;
1518
+ state.initialAnchor = void 0;
1519
+ set$(ctx, "scrollingTo", void 0);
1520
+ if (state.pendingTotalSize !== void 0) {
1521
+ addTotalSize(ctx, state, null, state.pendingTotalSize);
1522
+ }
1523
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1524
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1525
+ }
1526
+ }
1527
+ }
1528
+
1529
+ // src/core/scrollTo.ts
1530
+ function scrollTo(ctx, state, params) {
1531
+ var _a3;
1532
+ const { noScrollingTo, ...scrollTarget } = params;
1533
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1534
+ const {
1535
+ refScroller,
1536
+ props: { horizontal }
1537
+ } = state;
1538
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1539
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1540
+ const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1541
+ offset = Math.min(offset, maxOffset);
1542
+ }
1543
+ state.scrollHistory.length = 0;
1544
+ if (!noScrollingTo) {
1545
+ set$(ctx, "scrollingTo", scrollTarget);
1546
+ }
1547
+ state.scrollPending = offset;
1548
+ if (!isInitialScroll || Platform.OS === "android") {
1549
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1550
+ animated: !!animated,
1551
+ x: horizontal ? offset : 0,
1552
+ y: horizontal ? 0 : offset
1553
+ });
1554
+ }
1555
+ if (!animated) {
1556
+ state.scroll = offset;
1557
+ setTimeout(() => finishScrollTo(ctx, state), 100);
1558
+ if (isInitialScroll) {
1559
+ setTimeout(() => {
1560
+ state.initialScroll = void 0;
1561
+ }, 500);
1562
+ }
1563
+ }
1564
+ }
1565
+
1566
+ // src/utils/requestAdjust.ts
1567
+ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1568
+ if (Math.abs(positionDiff) > 0.1) {
1569
+ const doit = () => {
1570
+ {
1571
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
1572
+ }
1573
+ };
1574
+ state.scroll += positionDiff;
1575
+ state.scrollForNextCalculateItemsInView = void 0;
1576
+ const didLayout = peek$(ctx, "containersDidLayout");
1577
+ if (didLayout) {
1578
+ doit();
1287
1579
  } else {
1288
1580
  requestAnimationFrame(doit);
1289
1581
  }
1290
1582
  }
1291
1583
  }
1292
1584
 
1585
+ // src/core/ensureInitialAnchor.ts
1586
+ var INITIAL_ANCHOR_TOLERANCE = 0.5;
1587
+ var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1588
+ var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1589
+ function ensureInitialAnchor(ctx, state) {
1590
+ var _a3, _b, _c, _d, _e;
1591
+ const anchor = state.initialAnchor;
1592
+ const item = state.props.data[anchor.index];
1593
+ const containersDidLayout = peek$(ctx, "containersDidLayout");
1594
+ if (!containersDidLayout) {
1595
+ return;
1596
+ }
1597
+ const id = getId(state, anchor.index);
1598
+ if (state.positions.get(id) === void 0) {
1599
+ return;
1600
+ }
1601
+ const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1602
+ if (size === void 0) {
1603
+ return;
1604
+ }
1605
+ const availableSpace = Math.max(0, state.scrollLength - size);
1606
+ const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1607
+ const contentSize = getContentSize(ctx);
1608
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1609
+ const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1610
+ const delta = clampedDesiredOffset - state.scroll;
1611
+ if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1612
+ const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1613
+ if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1614
+ state.initialAnchor = void 0;
1615
+ } else {
1616
+ anchor.settledTicks = settledTicks;
1617
+ }
1618
+ return;
1619
+ }
1620
+ if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1621
+ state.initialAnchor = void 0;
1622
+ return;
1623
+ }
1624
+ const lastDelta = anchor.lastDelta;
1625
+ if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1626
+ state.initialAnchor = void 0;
1627
+ return;
1628
+ }
1629
+ Object.assign(anchor, {
1630
+ attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1631
+ lastDelta: delta,
1632
+ settledTicks: 0
1633
+ });
1634
+ requestAdjust(ctx, state, delta);
1635
+ }
1636
+
1293
1637
  // src/core/mvcp.ts
1294
1638
  function prepareMVCP(ctx, state, dataChanged) {
1295
1639
  const {
1296
1640
  idsInView,
1297
1641
  positions,
1298
- scrollingTo,
1299
1642
  props: { maintainVisibleContentPosition }
1300
1643
  } = state;
1644
+ const scrollingTo = peek$(ctx, "scrollingTo");
1301
1645
  let prevPosition;
1302
1646
  let targetId;
1303
1647
  const idsInViewWithPositions = [];
@@ -1338,7 +1682,7 @@ function prepareMVCP(ctx, state, dataChanged) {
1338
1682
  if (targetId !== void 0 && prevPosition !== void 0) {
1339
1683
  const newPosition = positions.get(targetId);
1340
1684
  if (newPosition !== void 0) {
1341
- const totalSize = peek$(ctx, "totalSize");
1685
+ const totalSize = getContentSize(ctx);
1342
1686
  let diff = newPosition - prevPosition;
1343
1687
  if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
1344
1688
  if (diff > 0) {
@@ -1373,7 +1717,7 @@ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1373
1717
  const prevId = state.idCache[prevIndex];
1374
1718
  const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1375
1719
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1376
- const prevRowHeight = calculateRowMaxSize(state, prevRowStart, prevIndex, useAverageSize);
1720
+ const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1377
1721
  currentRowTop = prevPosition + prevRowHeight;
1378
1722
  }
1379
1723
  return {
@@ -1396,7 +1740,7 @@ function findRowStartIndex(state, numColumns, index) {
1396
1740
  }
1397
1741
  return rowStart;
1398
1742
  }
1399
- function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1743
+ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1400
1744
  if (endIndex < startIndex) {
1401
1745
  return 0;
1402
1746
  }
@@ -1410,7 +1754,7 @@ function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1410
1754
  continue;
1411
1755
  }
1412
1756
  const id = state.idCache[i];
1413
- const size = getItemSize(state, id, i, data[i], useAverageSize);
1757
+ const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1414
1758
  if (size > maxSize) {
1415
1759
  maxSize = size;
1416
1760
  }
@@ -1418,41 +1762,6 @@ function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1418
1762
  return maxSize;
1419
1763
  }
1420
1764
 
1421
- // src/utils/setPaddingTop.ts
1422
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1423
- if (stylePaddingTop !== void 0) {
1424
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1425
- if (stylePaddingTop < prevStylePaddingTop) {
1426
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
1427
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1428
- state.timeoutSetPaddingTop = setTimeout(() => {
1429
- prevTotalSize = peek$(ctx, "totalSize") || 0;
1430
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1431
- }, 16);
1432
- }
1433
- set$(ctx, "stylePaddingTop", stylePaddingTop);
1434
- }
1435
- if (alignItemsPaddingTop !== void 0) {
1436
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1437
- }
1438
- }
1439
-
1440
- // src/utils/updateAlignItemsPaddingTop.ts
1441
- function updateAlignItemsPaddingTop(ctx, state) {
1442
- const {
1443
- scrollLength,
1444
- props: { alignItemsAtEnd, data }
1445
- } = state;
1446
- if (alignItemsAtEnd) {
1447
- let alignItemsPaddingTop = 0;
1448
- if ((data == null ? void 0 : data.length) > 0) {
1449
- const contentSize = getContentSize(ctx);
1450
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1451
- }
1452
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1453
- }
1454
- }
1455
-
1456
1765
  // src/core/updateTotalSize.ts
1457
1766
  function updateTotalSize(ctx, state) {
1458
1767
  const {
@@ -1466,7 +1775,7 @@ function updateTotalSize(ctx, state) {
1466
1775
  if (lastId !== void 0) {
1467
1776
  const lastPosition = positions.get(lastId);
1468
1777
  if (lastPosition !== void 0) {
1469
- const lastSize = getItemSize(state, lastId, data.length - 1, data[data.length - 1]);
1778
+ const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1470
1779
  if (lastSize !== void 0) {
1471
1780
  const totalSize = lastPosition + lastSize;
1472
1781
  addTotalSize(ctx, state, null, totalSize);
@@ -1475,25 +1784,44 @@ function updateTotalSize(ctx, state) {
1475
1784
  }
1476
1785
  }
1477
1786
  }
1478
- function addTotalSize(ctx, state, key, add) {
1479
- const { alignItemsAtEnd } = state.props;
1480
- const prevTotalSize = state.totalSize;
1481
- if (key === null) {
1482
- state.totalSize = add;
1483
- if (state.timeoutSetPaddingTop) {
1484
- clearTimeout(state.timeoutSetPaddingTop);
1485
- state.timeoutSetPaddingTop = void 0;
1787
+
1788
+ // src/utils/getScrollVelocity.ts
1789
+ var getScrollVelocity = (state) => {
1790
+ const { scrollHistory } = state;
1791
+ let velocity = 0;
1792
+ if (scrollHistory.length >= 1) {
1793
+ const newest = scrollHistory[scrollHistory.length - 1];
1794
+ let oldest;
1795
+ let start = 0;
1796
+ const now = Date.now();
1797
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1798
+ const entry = scrollHistory[i];
1799
+ const nextEntry = scrollHistory[i + 1];
1800
+ if (i > 0) {
1801
+ const prevEntry = scrollHistory[i - 1];
1802
+ const prevDirection = entry.scroll - prevEntry.scroll;
1803
+ const currentDirection = nextEntry.scroll - entry.scroll;
1804
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1805
+ start = i;
1806
+ break;
1807
+ }
1808
+ }
1486
1809
  }
1487
- } else {
1488
- state.totalSize += add;
1489
- }
1490
- if (prevTotalSize !== state.totalSize) {
1491
- set$(ctx, "totalSize", state.totalSize);
1492
- if (alignItemsAtEnd) {
1493
- updateAlignItemsPaddingTop(ctx, state);
1810
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1811
+ const entry = scrollHistory[i];
1812
+ if (now - entry.time <= 1e3) {
1813
+ oldest = entry;
1814
+ break;
1815
+ }
1816
+ }
1817
+ if (oldest && oldest !== newest) {
1818
+ const scrollDiff = newest.scroll - oldest.scroll;
1819
+ const timeDiff = newest.time - oldest.time;
1820
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1494
1821
  }
1495
1822
  }
1496
- }
1823
+ return velocity;
1824
+ };
1497
1825
 
1498
1826
  // src/utils/updateSnapToOffsets.ts
1499
1827
  function updateSnapToOffsets(ctx, state) {
@@ -1511,23 +1839,30 @@ function updateSnapToOffsets(ctx, state) {
1511
1839
  }
1512
1840
 
1513
1841
  // src/core/updateItemPositions.ts
1514
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered } = { scrollBottomBuffered: -1, startIndex: 0 }) {
1515
- var _a3, _b, _c, _d;
1842
+ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false } = {
1843
+ forceFullUpdate: false,
1844
+ scrollBottomBuffered: -1,
1845
+ startIndex: 0
1846
+ }) {
1847
+ var _a3, _b, _c, _d, _e;
1516
1848
  const {
1517
1849
  columns,
1518
1850
  indexByKey,
1519
1851
  positions,
1520
1852
  idCache,
1521
1853
  sizesKnown,
1522
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
1854
+ props: { getEstimatedItemSize, snapToIndices, enableAverages, maintainVisibleContentPosition }
1523
1855
  } = state;
1524
1856
  const data = state.props.data;
1525
1857
  const dataLength = data.length;
1526
1858
  const numColumns = peek$(ctx, "numColumns");
1859
+ const scrollingTo = peek$(ctx, "scrollingTo");
1527
1860
  const hasColumns = numColumns > 1;
1528
1861
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1862
+ const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1529
1863
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1530
1864
  const useAverageSize = enableAverages && !getEstimatedItemSize;
1865
+ const preferCachedSize = maintainVisibleContentPosition && (dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0);
1531
1866
  let currentRowTop = 0;
1532
1867
  let column = 1;
1533
1868
  let maxSizeInRow = 0;
@@ -1544,8 +1879,8 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1544
1879
  } else if (startIndex < dataLength) {
1545
1880
  const prevIndex = startIndex - 1;
1546
1881
  const prevId = getId(state, prevIndex);
1547
- const prevPosition = (_a3 = positions.get(prevId)) != null ? _a3 : 0;
1548
- const prevSize = (_b = sizesKnown.get(prevId)) != null ? _b : getItemSize(state, prevId, prevIndex, data[prevIndex], useAverageSize);
1882
+ const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1883
+ const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1549
1884
  currentRowTop = prevPosition + prevSize;
1550
1885
  }
1551
1886
  }
@@ -1553,16 +1888,16 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1553
1888
  let didBreakEarly = false;
1554
1889
  let breakAt;
1555
1890
  for (let i = startIndex; i < dataLength; i++) {
1556
- if (breakAt && i > breakAt) {
1891
+ if (shouldOptimize && breakAt !== void 0 && i > breakAt) {
1557
1892
  didBreakEarly = true;
1558
1893
  break;
1559
1894
  }
1560
- if (breakAt === void 0 && !dataChanged && currentRowTop > maxVisibleArea) {
1895
+ if (shouldOptimize && breakAt === void 0 && !scrollingTo && !dataChanged && currentRowTop > maxVisibleArea) {
1561
1896
  const itemsPerRow = hasColumns ? numColumns : 1;
1562
1897
  breakAt = i + itemsPerRow + 10;
1563
1898
  }
1564
- const id = (_c = idCache[i]) != null ? _c : getId(state, i);
1565
- const size = (_d = sizesKnown.get(id)) != null ? _d : getItemSize(state, id, i, data[i], useAverageSize);
1899
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1900
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
1566
1901
  if (IS_DEV && needsIndexByKey) {
1567
1902
  if (indexByKeyForChecking.has(id)) {
1568
1903
  console.error(
@@ -1780,16 +2115,6 @@ function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize,
1780
2115
  const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
1781
2116
  return value.isViewable;
1782
2117
  }
1783
- function findContainerId(ctx, key) {
1784
- const numContainers = peek$(ctx, "numContainers");
1785
- for (let i = 0; i < numContainers; i++) {
1786
- const itemKey = peek$(ctx, `containerItemKey${i}`);
1787
- if (itemKey === key) {
1788
- return i;
1789
- }
1790
- }
1791
- return -1;
1792
- }
1793
2118
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1794
2119
  const key = containerId + configId;
1795
2120
  ctx.mapViewabilityValues.set(key, viewToken);
@@ -1838,7 +2163,7 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1838
2163
  for (const containerIndex of stickyContainerPool) {
1839
2164
  const key = peek$(ctx, `containerItemKey${containerIndex}`);
1840
2165
  const isPendingRemoval = pendingRemovalSet.has(containerIndex);
1841
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
2166
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType) && !result.includes(containerIndex)) {
1842
2167
  result.push(containerIndex);
1843
2168
  if (isPendingRemoval && pendingRemovalSet.delete(containerIndex)) {
1844
2169
  pendingRemovalChanged = true;
@@ -1931,144 +2256,30 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1931
2256
  }
1932
2257
  return result.sort(comparatorDefault);
1933
2258
  }
1934
- function comparatorByDistance(a, b) {
1935
- return b.distance - a.distance;
1936
- }
1937
-
1938
- // src/utils/getScrollVelocity.ts
1939
- var getScrollVelocity = (state) => {
1940
- const { scrollHistory } = state;
1941
- let velocity = 0;
1942
- if (scrollHistory.length >= 1) {
1943
- const newest = scrollHistory[scrollHistory.length - 1];
1944
- let oldest;
1945
- let start = 0;
1946
- const now = Date.now();
1947
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1948
- const entry = scrollHistory[i];
1949
- const nextEntry = scrollHistory[i + 1];
1950
- if (i > 0) {
1951
- const prevEntry = scrollHistory[i - 1];
1952
- const prevDirection = entry.scroll - prevEntry.scroll;
1953
- const currentDirection = nextEntry.scroll - entry.scroll;
1954
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1955
- start = i;
1956
- break;
1957
- }
1958
- }
1959
- }
1960
- for (let i = start; i < scrollHistory.length - 1; i++) {
1961
- const entry = scrollHistory[i];
1962
- if (now - entry.time <= 1e3) {
1963
- oldest = entry;
1964
- break;
1965
- }
1966
- }
1967
- if (oldest && oldest !== newest) {
1968
- const scrollDiff = newest.scroll - oldest.scroll;
1969
- const timeDiff = newest.time - oldest.time;
1970
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1971
- }
1972
- }
1973
- return velocity;
1974
- };
1975
-
1976
- // src/core/scrollToIndex.ts
1977
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
1978
- if (index >= state.props.data.length) {
1979
- index = state.props.data.length - 1;
1980
- } else if (index < 0) {
1981
- index = 0;
1982
- }
1983
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
1984
- const isLast = index === state.props.data.length - 1;
1985
- if (isLast && viewPosition === void 0) {
1986
- viewPosition = 1;
1987
- }
1988
- state.scrollForNextCalculateItemsInView = void 0;
1989
- scrollTo(state, {
1990
- animated,
1991
- index,
1992
- offset: firstIndexOffset,
1993
- viewOffset,
1994
- viewPosition: viewPosition != null ? viewPosition : 0
1995
- });
1996
- }
1997
-
1998
- // src/utils/checkThreshold.ts
1999
- var HYSTERESIS_MULTIPLIER = 1.3;
2000
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
2001
- const absDistance = Math.abs(distance);
2002
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
2003
- const updateSnapshot = () => {
2004
- setSnapshot == null ? void 0 : setSnapshot({
2005
- atThreshold,
2006
- contentSize: context.contentSize,
2007
- dataLength: context.dataLength,
2008
- scrollPosition: context.scrollPosition
2009
- });
2010
- };
2011
- if (!wasReached) {
2012
- if (!within) {
2013
- return false;
2014
- }
2015
- onReached == null ? void 0 : onReached(distance);
2016
- updateSnapshot();
2017
- return true;
2018
- }
2019
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
2020
- if (reset) {
2021
- setSnapshot == null ? void 0 : setSnapshot(void 0);
2022
- return false;
2023
- }
2024
- if (within) {
2025
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
2026
- if (changed) {
2027
- onReached == null ? void 0 : onReached(distance);
2028
- updateSnapshot();
2029
- }
2030
- }
2031
- return true;
2032
- };
2259
+ function comparatorByDistance(a, b) {
2260
+ return b.distance - a.distance;
2261
+ }
2033
2262
 
2034
- // src/utils/checkAtBottom.ts
2035
- function checkAtBottom(ctx, state) {
2036
- var _a3;
2037
- if (!state) {
2038
- return;
2263
+ // src/core/scrollToIndex.ts
2264
+ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2265
+ if (index >= state.props.data.length) {
2266
+ index = state.props.data.length - 1;
2267
+ } else if (index < 0) {
2268
+ index = 0;
2039
2269
  }
2040
- const {
2041
- queuedInitialLayout,
2042
- scrollLength,
2043
- scroll,
2044
- maintainingScrollAtEnd,
2045
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
2046
- } = state;
2047
- const contentSize = getContentSize(ctx);
2048
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
2049
- const distanceFromEnd = contentSize - scroll - scrollLength;
2050
- const isContentLess = contentSize < scrollLength;
2051
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
2052
- state.isEndReached = checkThreshold(
2053
- distanceFromEnd,
2054
- isContentLess,
2055
- onEndReachedThreshold * scrollLength,
2056
- state.isEndReached,
2057
- state.endReachedSnapshot,
2058
- {
2059
- scrollPosition: scroll,
2060
- contentSize,
2061
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length
2062
- },
2063
- (distance) => {
2064
- var _a4, _b;
2065
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
2066
- },
2067
- (snapshot) => {
2068
- state.endReachedSnapshot = snapshot;
2069
- }
2070
- );
2270
+ const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2271
+ const isLast = index === state.props.data.length - 1;
2272
+ if (isLast && viewPosition === void 0) {
2273
+ viewPosition = 1;
2071
2274
  }
2275
+ state.scrollForNextCalculateItemsInView = void 0;
2276
+ scrollTo(ctx, state, {
2277
+ animated,
2278
+ index,
2279
+ offset: firstIndexOffset,
2280
+ viewOffset,
2281
+ viewPosition: viewPosition != null ? viewPosition : 0
2282
+ });
2072
2283
  }
2073
2284
 
2074
2285
  // src/utils/setDidLayout.ts
@@ -2150,7 +2361,7 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2150
2361
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2151
2362
  if (currentId) {
2152
2363
  const currentPos = state.positions.get(currentId);
2153
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
2364
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2154
2365
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2155
2366
  }
2156
2367
  }
@@ -2161,46 +2372,52 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2161
2372
  }
2162
2373
  function calculateItemsInView(ctx, state, params = {}) {
2163
2374
  reactDom.unstable_batchedUpdates(() => {
2164
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
2375
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2165
2376
  const {
2166
2377
  columns,
2167
2378
  containerItemKeys,
2168
2379
  enableScrollForNextCalculateItemsInView,
2169
2380
  idCache,
2170
2381
  indexByKey,
2382
+ initialScroll,
2171
2383
  minIndexSizeChanged,
2172
2384
  positions,
2385
+ props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2173
2386
  scrollForNextCalculateItemsInView,
2174
2387
  scrollLength,
2175
2388
  sizes,
2176
2389
  startBufferedId: startBufferedIdOrig,
2177
- viewabilityConfigCallbackPairs,
2178
- props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer }
2390
+ viewabilityConfigCallbackPairs
2179
2391
  } = state;
2180
2392
  const { data } = state.props;
2181
2393
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2182
2394
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2183
2395
  const prevNumContainers = peek$(ctx, "numContainers");
2184
2396
  if (!data || scrollLength === 0 || !prevNumContainers) {
2397
+ if (state.initialAnchor) {
2398
+ ensureInitialAnchor(ctx, state);
2399
+ }
2185
2400
  return;
2186
2401
  }
2187
- const totalSize = peek$(ctx, "totalSize");
2402
+ const totalSize = getContentSize(ctx);
2188
2403
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2189
2404
  const numColumns = peek$(ctx, "numColumns");
2190
- const { dataChanged, doMVCP } = params;
2405
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
2191
2406
  const speed = getScrollVelocity(state);
2192
2407
  const scrollExtra = 0;
2193
2408
  const { queuedInitialLayout } = state;
2194
2409
  let { scroll: scrollState } = state;
2195
2410
  if (!queuedInitialLayout && initialScroll) {
2196
2411
  const updatedOffset = calculateOffsetWithOffsetPosition(
2412
+ ctx,
2197
2413
  state,
2198
2414
  calculateOffsetForIndex(ctx, state, initialScroll.index),
2199
2415
  initialScroll
2200
2416
  );
2201
2417
  scrollState = updatedOffset;
2202
2418
  }
2203
- const scrollAdjustPad = -topPad;
2419
+ const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2420
+ const scrollAdjustPad = scrollAdjustPending - topPad;
2204
2421
  let scroll = scrollState + scrollExtra + scrollAdjustPad;
2205
2422
  if (scroll + scrollLength > totalSize) {
2206
2423
  scroll = Math.max(0, totalSize - scrollLength);
@@ -2213,6 +2430,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2213
2430
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2214
2431
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2215
2432
  state.activeStickyIndex = nextActiveStickyIndex;
2433
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2216
2434
  let scrollBufferTop = scrollBuffer;
2217
2435
  let scrollBufferBottom = scrollBuffer;
2218
2436
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2228,6 +2446,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2228
2446
  if (!dataChanged && scrollForNextCalculateItemsInView) {
2229
2447
  const { top, bottom } = scrollForNextCalculateItemsInView;
2230
2448
  if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2449
+ if (state.initialAnchor) {
2450
+ ensureInitialAnchor(ctx, state);
2451
+ }
2231
2452
  return;
2232
2453
  }
2233
2454
  }
@@ -2237,8 +2458,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2237
2458
  idCache.length = 0;
2238
2459
  positions.clear();
2239
2460
  }
2240
- const startIndex = dataChanged ? 0 : (_a3 = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _a3 : 0;
2241
- updateItemPositions(ctx, state, dataChanged, { scrollBottomBuffered, startIndex });
2461
+ const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2462
+ updateItemPositions(ctx, state, dataChanged, {
2463
+ forceFullUpdate: !!forceFullItemPositions,
2464
+ scrollBottomBuffered,
2465
+ startIndex
2466
+ });
2242
2467
  if (minIndexSizeChanged !== void 0) {
2243
2468
  state.minIndexSizeChanged = void 0;
2244
2469
  }
@@ -2250,9 +2475,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2250
2475
  let endBuffered = null;
2251
2476
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2252
2477
  for (let i = loopStart; i >= 0; i--) {
2253
- const id = (_b = idCache[i]) != null ? _b : getId(state, i);
2478
+ const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2254
2479
  const top = positions.get(id);
2255
- const size = (_c = sizes.get(id)) != null ? _c : getItemSize(state, id, i, data[i]);
2480
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2256
2481
  const bottom = top + size;
2257
2482
  if (bottom > scroll - scrollBuffer) {
2258
2483
  loopStart = i;
@@ -2278,8 +2503,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2278
2503
  let firstFullyOnScreenIndex;
2279
2504
  const dataLength = data.length;
2280
2505
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2281
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
2282
- const size = (_e = sizes.get(id)) != null ? _e : getItemSize(state, id, i, data[i]);
2506
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2507
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2283
2508
  const top = positions.get(id);
2284
2509
  if (!foundEnd) {
2285
2510
  if (startNoBuffer === null && top + size > scroll) {
@@ -2308,7 +2533,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2308
2533
  }
2309
2534
  const idsInView = [];
2310
2535
  for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2311
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2536
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2312
2537
  idsInView.push(id);
2313
2538
  }
2314
2539
  Object.assign(state, {
@@ -2340,7 +2565,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2340
2565
  let numContainers2 = prevNumContainers;
2341
2566
  const needNewContainers = [];
2342
2567
  for (let i = startBuffered; i <= endBuffered; i++) {
2343
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2568
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2344
2569
  if (!containerItemKeys.has(id)) {
2345
2570
  needNewContainers.push(i);
2346
2571
  }
@@ -2358,6 +2583,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2358
2583
  );
2359
2584
  } else {
2360
2585
  state.activeStickyIndex = void 0;
2586
+ set$(ctx, "activeStickyIndex", void 0);
2361
2587
  }
2362
2588
  if (needNewContainers.length > 0) {
2363
2589
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
@@ -2377,7 +2603,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2377
2603
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2378
2604
  const i = needNewContainers[idx];
2379
2605
  const containerIndex = availableContainers[idx];
2380
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2606
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2381
2607
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2382
2608
  if (oldKey && oldKey !== id) {
2383
2609
  containerItemKeys.delete(oldKey);
@@ -2416,7 +2642,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2416
2642
  for (let i = 0; i < numContainers; i++) {
2417
2643
  const itemKey = peek$(ctx, `containerItemKey${i}`);
2418
2644
  if (pendingRemoval.includes(i)) {
2419
- if (itemKey) {
2645
+ if (itemKey !== void 0) {
2420
2646
  containerItemKeys.delete(itemKey);
2421
2647
  }
2422
2648
  state.containerItemTypes.delete(i);
@@ -2433,11 +2659,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2433
2659
  const itemIndex = indexByKey.get(itemKey);
2434
2660
  const item = data[itemIndex];
2435
2661
  if (item !== void 0) {
2436
- const id = (_i = idCache[itemIndex]) != null ? _i : getId(state, itemIndex);
2437
- const position = positions.get(id);
2438
- if (position === void 0) {
2662
+ const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2663
+ const positionValue = positions.get(id);
2664
+ if (positionValue === void 0) {
2439
2665
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2440
2666
  } else {
2667
+ const position = (positionValue || 0) - scrollAdjustPending;
2441
2668
  const column = columns.get(id) || 1;
2442
2669
  const prevPos = peek$(ctx, `containerPosition${i}`);
2443
2670
  const prevColumn = peek$(ctx, `containerColumn${i}`);
@@ -2474,6 +2701,29 @@ function calculateItemsInView(ctx, state, params = {}) {
2474
2701
  }
2475
2702
  }
2476
2703
  });
2704
+ if (state.initialAnchor) {
2705
+ ensureInitialAnchor(ctx, state);
2706
+ }
2707
+ }
2708
+
2709
+ // src/core/checkActualChange.ts
2710
+ function checkActualChange(state, dataProp, previousData) {
2711
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
2712
+ return true;
2713
+ }
2714
+ const {
2715
+ idCache,
2716
+ props: { keyExtractor }
2717
+ } = state;
2718
+ for (let i = 0; i < dataProp.length; i++) {
2719
+ if (dataProp[i] !== previousData[i]) {
2720
+ return true;
2721
+ }
2722
+ if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
2723
+ return true;
2724
+ }
2725
+ }
2726
+ return false;
2477
2727
  }
2478
2728
 
2479
2729
  // src/core/doMaintainScrollAtEnd.ts
@@ -2506,40 +2756,6 @@ function doMaintainScrollAtEnd(ctx, state, animated) {
2506
2756
  }
2507
2757
  }
2508
2758
 
2509
- // src/utils/checkAtTop.ts
2510
- function checkAtTop(state) {
2511
- var _a3;
2512
- if (!state) {
2513
- return;
2514
- }
2515
- const {
2516
- scrollLength,
2517
- scroll,
2518
- props: { onStartReachedThreshold }
2519
- } = state;
2520
- const distanceFromTop = scroll;
2521
- state.isAtStart = distanceFromTop <= 0;
2522
- state.isStartReached = checkThreshold(
2523
- distanceFromTop,
2524
- false,
2525
- onStartReachedThreshold * scrollLength,
2526
- state.isStartReached,
2527
- state.startReachedSnapshot,
2528
- {
2529
- scrollPosition: scroll,
2530
- contentSize: state.totalSize,
2531
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length
2532
- },
2533
- (distance) => {
2534
- var _a4, _b;
2535
- return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
2536
- },
2537
- (snapshot) => {
2538
- state.startReachedSnapshot = snapshot;
2539
- }
2540
- );
2541
- }
2542
-
2543
2759
  // src/utils/updateAveragesOnDataChange.ts
2544
2760
  function updateAveragesOnDataChange(state, oldData, newData) {
2545
2761
  var _a3;
@@ -2593,25 +2809,23 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2593
2809
  }
2594
2810
 
2595
2811
  // src/core/checkResetContainers.ts
2596
- function checkResetContainers(ctx, state, isFirst, dataProp) {
2597
- if (state) {
2598
- if (!isFirst && state.props.data !== dataProp) {
2599
- updateAveragesOnDataChange(state, state.props.data, dataProp);
2600
- }
2601
- const { maintainScrollAtEnd } = state.props;
2602
- if (!isFirst) {
2603
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2604
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2605
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2606
- if (!didMaintainScrollAtEnd && dataProp.length > state.props.data.length) {
2607
- state.isEndReached = false;
2608
- }
2609
- if (!didMaintainScrollAtEnd) {
2610
- checkAtTop(state);
2611
- checkAtBottom(ctx, state);
2612
- }
2613
- }
2812
+ function checkResetContainers(ctx, state, dataProp) {
2813
+ const { previousData } = state;
2814
+ if (previousData) {
2815
+ updateAveragesOnDataChange(state, previousData, dataProp);
2816
+ }
2817
+ const { maintainScrollAtEnd } = state.props;
2818
+ calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2819
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2820
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2821
+ if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2822
+ state.isEndReached = false;
2823
+ }
2824
+ if (!didMaintainScrollAtEnd) {
2825
+ checkAtTop(state);
2826
+ checkAtBottom(ctx, state);
2614
2827
  }
2828
+ delete state.previousData;
2615
2829
  }
2616
2830
 
2617
2831
  // src/core/doInitialAllocateContainers.ts
@@ -2652,7 +2866,7 @@ function doInitialAllocateContainers(ctx, state) {
2652
2866
  set$(ctx, "numContainers", numContainers);
2653
2867
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2654
2868
  if (state.lastLayout) {
2655
- if (state.props.initialScroll) {
2869
+ if (state.initialScroll) {
2656
2870
  requestAnimationFrame(() => {
2657
2871
  calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2658
2872
  });
@@ -2708,78 +2922,47 @@ function handleLayout(ctx, state, layout, setCanRender) {
2708
2922
  setCanRender(true);
2709
2923
  }
2710
2924
 
2711
- // src/core/onScroll.ts
2712
- function onScroll(ctx, state, event) {
2713
- var _a3, _b, _c;
2714
- const {
2715
- scrollProcessingEnabled,
2716
- props: { onScroll: onScrollProp }
2717
- } = state;
2718
- if (scrollProcessingEnabled === false) {
2719
- return;
2720
- }
2721
- 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) {
2722
- return;
2723
- }
2724
- const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2725
- state.scrollPending = newScroll;
2726
- updateScroll(ctx, state, newScroll);
2727
- onScrollProp == null ? void 0 : onScrollProp(event);
2728
- }
2729
- function updateScroll(ctx, state, newScroll) {
2730
- const scrollingTo = state.scrollingTo;
2731
- state.hasScrolled = true;
2732
- state.lastBatchingAction = Date.now();
2733
- const currentTime = Date.now();
2734
- const adjust = state.scrollAdjustHandler.getAdjust();
2735
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
2736
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
2737
- if (adjustChanged) {
2738
- state.scrollHistory.length = 0;
2739
- }
2740
- state.lastScrollAdjustForHistory = adjust;
2741
- if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
2742
- if (!adjustChanged) {
2743
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
2744
- }
2745
- }
2746
- if (state.scrollHistory.length > 5) {
2747
- state.scrollHistory.shift();
2748
- }
2749
- state.scrollPrev = state.scroll;
2750
- state.scrollPrevTime = state.scrollTime;
2751
- state.scroll = newScroll;
2752
- state.scrollTime = currentTime;
2753
- const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
2754
- if (ignoreScrollFromMVCP && !state.scrollingTo) {
2755
- const { lt, gt } = ignoreScrollFromMVCP;
2756
- if (lt && newScroll < lt || gt && newScroll > gt) {
2757
- return;
2758
- }
2759
- }
2760
- if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
2761
- calculateItemsInView(ctx, state, { doMVCP: state.scrollingTo !== void 0 });
2762
- checkAtBottom(ctx, state);
2763
- checkAtTop(state);
2764
- state.dataChangeNeedsScrollUpdate = false;
2765
- }
2766
- }
2767
-
2768
2925
  // src/core/ScrollAdjustHandler.ts
2769
2926
  var ScrollAdjustHandler = class {
2770
2927
  constructor(ctx) {
2771
2928
  this.appliedAdjust = 0;
2929
+ this.pendingAdjust = 0;
2772
2930
  this.mounted = false;
2773
2931
  this.context = ctx;
2932
+ {
2933
+ const commitPendingAdjust = () => {
2934
+ const state = this.context.internalState;
2935
+ const pending = this.pendingAdjust;
2936
+ if (pending !== 0) {
2937
+ this.pendingAdjust = 0;
2938
+ this.appliedAdjust += pending;
2939
+ state.scroll += pending;
2940
+ state.scrollForNextCalculateItemsInView = void 0;
2941
+ set$(this.context, "scrollAdjustPending", 0);
2942
+ set$(this.context, "scrollAdjust", this.appliedAdjust);
2943
+ calculateItemsInView(this.context, this.context.internalState);
2944
+ }
2945
+ };
2946
+ listen$(this.context, "scrollingTo", (value) => {
2947
+ if (value === void 0) {
2948
+ commitPendingAdjust();
2949
+ }
2950
+ });
2951
+ }
2774
2952
  }
2775
2953
  requestAdjust(add) {
2776
- const oldAdjustTop = this.appliedAdjust;
2777
- this.appliedAdjust = add + oldAdjustTop;
2778
- const set = () => set$(this.context, "scrollAdjust", this.appliedAdjust);
2779
- if (this.mounted) {
2780
- set();
2954
+ const scrollingTo = peek$(this.context, "scrollingTo");
2955
+ if ((scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2956
+ this.pendingAdjust += add;
2957
+ set$(this.context, "scrollAdjustPending", this.pendingAdjust);
2781
2958
  } else {
2782
- requestAnimationFrame(set);
2959
+ this.appliedAdjust += add;
2960
+ const setter = () => set$(this.context, "scrollAdjust", this.appliedAdjust);
2961
+ if (this.mounted) {
2962
+ setter();
2963
+ } else {
2964
+ requestAnimationFrame(setter);
2965
+ }
2783
2966
  }
2784
2967
  }
2785
2968
  setMounted() {
@@ -2827,8 +3010,8 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2827
3010
  let minIndexSizeChanged;
2828
3011
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2829
3012
  const prevSizeKnown = state.sizesKnown.get(itemKey);
2830
- const diff = updateOneItemSize(state, itemKey, sizeObj);
2831
- const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
3013
+ const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3014
+ const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
2832
3015
  if (diff !== 0) {
2833
3016
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2834
3017
  const { startBuffered, endBuffered } = state;
@@ -2849,7 +3032,6 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2849
3032
  if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2850
3033
  shouldMaintainScrollAtEnd = true;
2851
3034
  }
2852
- addTotalSize(ctx, state, itemKey, diff);
2853
3035
  onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2854
3036
  index,
2855
3037
  itemData: state.props.data[index],
@@ -2889,7 +3071,7 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2889
3071
  }
2890
3072
  }
2891
3073
  }
2892
- function updateOneItemSize(state, itemKey, sizeObj) {
3074
+ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2893
3075
  var _a3;
2894
3076
  const {
2895
3077
  sizes,
@@ -2900,7 +3082,7 @@ function updateOneItemSize(state, itemKey, sizeObj) {
2900
3082
  } = state;
2901
3083
  if (!data) return 0;
2902
3084
  const index = indexByKey.get(itemKey);
2903
- const prevSize = getItemSize(state, itemKey, index, data[index]);
3085
+ const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
2904
3086
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
2905
3087
  const size = Math.round(rawSize) ;
2906
3088
  sizesKnown.set(itemKey, size);
@@ -2914,7 +3096,7 @@ function updateOneItemSize(state, itemKey, sizeObj) {
2914
3096
  averages.num++;
2915
3097
  }
2916
3098
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2917
- sizes.set(itemKey, size);
3099
+ setSize(ctx, state, itemKey, size);
2918
3100
  return size - prevSize;
2919
3101
  }
2920
3102
  return 0;
@@ -2959,6 +3141,91 @@ function createColumnWrapperStyle(contentContainerStyle) {
2959
3141
  };
2960
3142
  }
2961
3143
  }
3144
+
3145
+ // src/utils/createImperativeHandle.ts
3146
+ function createImperativeHandle(ctx, state) {
3147
+ const scrollIndexIntoView = (options) => {
3148
+ if (state) {
3149
+ const { index, ...rest } = options;
3150
+ const { startNoBuffer, endNoBuffer } = state;
3151
+ if (index < startNoBuffer || index > endNoBuffer) {
3152
+ const viewPosition = index < startNoBuffer ? 0 : 1;
3153
+ scrollToIndex(ctx, state, {
3154
+ ...rest,
3155
+ index,
3156
+ viewPosition
3157
+ });
3158
+ }
3159
+ }
3160
+ };
3161
+ const refScroller = state.refScroller;
3162
+ return {
3163
+ flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
3164
+ getNativeScrollRef: () => refScroller.current,
3165
+ getScrollableNode: () => refScroller.current.getScrollableNode(),
3166
+ getScrollResponder: () => refScroller.current.getScrollResponder(),
3167
+ getState: () => ({
3168
+ activeStickyIndex: state.activeStickyIndex,
3169
+ contentLength: state.totalSize,
3170
+ data: state.props.data,
3171
+ elementAtIndex: (index) => {
3172
+ var _a3;
3173
+ return (_a3 = ctx.viewRefs.get(findContainerId(ctx, getId(state, index)))) == null ? void 0 : _a3.current;
3174
+ },
3175
+ end: state.endNoBuffer,
3176
+ endBuffered: state.endBuffered,
3177
+ isAtEnd: state.isAtEnd,
3178
+ isAtStart: state.isAtStart,
3179
+ positionAtIndex: (index) => state.positions.get(getId(state, index)),
3180
+ positions: state.positions,
3181
+ scroll: state.scroll,
3182
+ scrollLength: state.scrollLength,
3183
+ sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
3184
+ sizes: state.sizesKnown,
3185
+ start: state.startNoBuffer,
3186
+ startBuffered: state.startBuffered
3187
+ }),
3188
+ scrollIndexIntoView,
3189
+ scrollItemIntoView: ({ item, ...props }) => {
3190
+ const data = state.props.data;
3191
+ const index = data.indexOf(item);
3192
+ if (index !== -1) {
3193
+ scrollIndexIntoView({ index, ...props });
3194
+ }
3195
+ },
3196
+ scrollToEnd: (options) => {
3197
+ const data = state.props.data;
3198
+ const stylePaddingBottom = state.props.stylePaddingBottom;
3199
+ const index = data.length - 1;
3200
+ if (index !== -1) {
3201
+ const paddingBottom = stylePaddingBottom || 0;
3202
+ const footerSize = peek$(ctx, "footerSize") || 0;
3203
+ scrollToIndex(ctx, state, {
3204
+ index,
3205
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3206
+ viewPosition: 1,
3207
+ ...options
3208
+ });
3209
+ }
3210
+ },
3211
+ scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3212
+ scrollToItem: ({ item, ...props }) => {
3213
+ const data = state.props.data;
3214
+ const index = data.indexOf(item);
3215
+ if (index !== -1) {
3216
+ scrollToIndex(ctx, state, { index, ...props });
3217
+ }
3218
+ },
3219
+ scrollToOffset: (params) => scrollTo(ctx, state, params),
3220
+ setScrollProcessingEnabled: (enabled) => {
3221
+ state.scrollProcessingEnabled = enabled;
3222
+ },
3223
+ setVisibleContentAnchorOffset: (value) => {
3224
+ const val = isFunction(value) ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
3225
+ set$(ctx, "scrollAdjustUserOffset", val);
3226
+ }
3227
+ };
3228
+ }
2962
3229
  function getRenderedItem(ctx, state, key) {
2963
3230
  var _a3;
2964
3231
  if (!state) {
@@ -3053,12 +3320,13 @@ var LegendList = typedMemo(
3053
3320
  })
3054
3321
  );
3055
3322
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3056
- var _a3;
3323
+ var _a3, _b;
3057
3324
  const {
3058
3325
  alignItemsAtEnd = false,
3059
3326
  columnWrapperStyle,
3060
3327
  contentContainerStyle: contentContainerStyleProp,
3061
3328
  data: dataProp = [],
3329
+ dataVersion,
3062
3330
  drawDistance = 250,
3063
3331
  enableAverages = true,
3064
3332
  estimatedItemSize: estimatedItemSizeProp,
@@ -3069,6 +3337,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3069
3337
  getItemType,
3070
3338
  horizontal,
3071
3339
  initialContainerPoolRatio = 2,
3340
+ initialScrollAtEnd = false,
3072
3341
  initialScrollIndex: initialScrollIndexProp,
3073
3342
  initialScrollOffset: initialScrollOffsetProp,
3074
3343
  itemsAreEqual,
@@ -3107,13 +3376,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3107
3376
  waitForInitialLayout = true,
3108
3377
  ...rest
3109
3378
  } = props;
3110
- const [renderNum, setRenderNum] = React3.useState(0);
3111
- const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3112
- const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
3113
3379
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3114
3380
  const style = { ...StyleSheet.flatten(styleProp) };
3115
3381
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3116
3382
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3383
+ const [renderNum, setRenderNum] = React3.useState(0);
3384
+ 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;
3385
+ const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
3117
3386
  const ctx = useStateContext();
3118
3387
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
3119
3388
  const refScroller = React3.useRef(null);
@@ -3132,6 +3401,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3132
3401
  containerItemKeys: /* @__PURE__ */ new Set(),
3133
3402
  containerItemTypes: /* @__PURE__ */ new Map(),
3134
3403
  dataChangeNeedsScrollUpdate: false,
3404
+ didColumnsChange: false,
3405
+ didDataChange: false,
3135
3406
  enableScrollForNextCalculateItemsInView: true,
3136
3407
  endBuffered: -1,
3137
3408
  endNoBuffer: -1,
@@ -3140,10 +3411,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3140
3411
  idCache: [],
3141
3412
  idsInView: [],
3142
3413
  indexByKey: /* @__PURE__ */ new Map(),
3143
- initialScroll,
3414
+ initialAnchor: (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
3415
+ attempts: 0,
3416
+ index: initialScrollProp.index,
3417
+ settledTicks: 0,
3418
+ viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3419
+ viewPosition: initialScrollProp.viewPosition
3420
+ } : void 0,
3421
+ initialScroll: initialScrollProp,
3144
3422
  isAtEnd: false,
3145
3423
  isAtStart: false,
3146
3424
  isEndReached: false,
3425
+ isFirst: true,
3147
3426
  isStartReached: false,
3148
3427
  lastBatchingAction: Date.now(),
3149
3428
  lastLayout: void 0,
@@ -3176,21 +3455,27 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3176
3455
  totalSize: 0,
3177
3456
  viewabilityConfigCallbackPairs: void 0
3178
3457
  };
3458
+ const internalState = ctx.internalState;
3459
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3179
3460
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3180
3461
  set$(ctx, "extraData", extraData);
3181
3462
  }
3182
3463
  refState.current = ctx.internalState;
3183
3464
  }
3184
3465
  const state = refState.current;
3185
- const isFirst = !state.props.renderItem;
3186
- const didDataChange = state.props.data !== dataProp;
3187
- if (didDataChange) {
3466
+ const isFirstLocal = state.isFirst;
3467
+ state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3468
+ const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3469
+ if (didDataChangeLocal) {
3188
3470
  state.dataChangeNeedsScrollUpdate = true;
3471
+ state.didDataChange = true;
3472
+ state.previousData = state.props.data;
3189
3473
  }
3190
3474
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3191
3475
  state.props = {
3192
3476
  alignItemsAtEnd,
3193
3477
  data: dataProp,
3478
+ dataVersion,
3194
3479
  enableAverages,
3195
3480
  estimatedItemSize,
3196
3481
  getEstimatedItemSize,
@@ -3198,7 +3483,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3198
3483
  getItemType,
3199
3484
  horizontal: !!horizontal,
3200
3485
  initialContainerPoolRatio,
3201
- initialScroll,
3202
3486
  itemsAreEqual,
3203
3487
  keyExtractor,
3204
3488
  maintainScrollAtEnd,
@@ -3230,7 +3514,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3230
3514
  { length: Math.min(numColumnsProp, dataProp.length) },
3231
3515
  (_, i) => getId(state, dataProp.length - 1 - i)
3232
3516
  );
3233
- }, [dataProp, numColumnsProp]);
3517
+ }, [dataProp, dataVersion, numColumnsProp]);
3234
3518
  const initializeStateVars = () => {
3235
3519
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3236
3520
  set$(ctx, "numColumns", numColumnsProp);
@@ -3245,7 +3529,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3245
3529
  requestAdjust(ctx, state, paddingDiff);
3246
3530
  }
3247
3531
  };
3248
- if (isFirst) {
3532
+ if (isFirstLocal) {
3249
3533
  initializeStateVars();
3250
3534
  updateItemPositions(
3251
3535
  ctx,
@@ -3255,38 +3539,53 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3255
3539
  );
3256
3540
  }
3257
3541
  const initialContentOffset = React3.useMemo(() => {
3258
- if (initialScroll) {
3259
- const { index, viewOffset } = initialScroll;
3260
- let initialContentOffset2 = viewOffset || 0;
3261
- if (index !== void 0) {
3262
- initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
3263
- }
3264
- refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
3265
- if (initialContentOffset2 > 0) {
3266
- scrollTo(state, {
3267
- animated: false,
3268
- index,
3269
- isInitialScroll: true,
3270
- offset: initialContentOffset2,
3271
- viewPosition: index === dataProp.length - 1 ? 1 : 0
3272
- });
3273
- }
3274
- return initialContentOffset2;
3542
+ var _a4, _b2;
3543
+ const { initialScroll } = refState.current;
3544
+ if (!initialScroll) {
3545
+ refState.current.initialAnchor = void 0;
3546
+ return 0;
3547
+ }
3548
+ if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3549
+ refState.current.initialAnchor = {
3550
+ attempts: 0,
3551
+ index: initialScroll.index,
3552
+ settledTicks: 0,
3553
+ viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3554
+ viewPosition: initialScroll.viewPosition
3555
+ };
3275
3556
  }
3276
- return 0;
3277
- }, [renderNum]);
3278
- if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
3557
+ if (initialScroll.contentOffset !== void 0) {
3558
+ return initialScroll.contentOffset;
3559
+ }
3560
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3561
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3562
+ let clampedOffset = resolvedOffset;
3563
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3564
+ const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3565
+ clampedOffset = Math.min(clampedOffset, maxOffset);
3566
+ }
3567
+ clampedOffset = Math.max(0, clampedOffset);
3568
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3569
+ refState.current.initialScroll = updatedInitialScroll;
3570
+ state.initialScroll = updatedInitialScroll;
3571
+ refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3572
+ return clampedOffset;
3573
+ }, [renderNum, state.initialScroll]);
3574
+ if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3279
3575
  refState.current.lastBatchingAction = Date.now();
3280
- if (!keyExtractorProp && !isFirst && didDataChange) {
3576
+ if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
3281
3577
  IS_DEV && warnDevOnce(
3282
3578
  "keyExtractor",
3283
3579
  "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."
3284
3580
  );
3285
3581
  refState.current.sizes.clear();
3286
3582
  refState.current.positions.clear();
3583
+ refState.current.totalSize = 0;
3584
+ set$(ctx, "totalSize", 0);
3287
3585
  }
3288
3586
  }
3289
3587
  const onLayoutHeader = React3.useCallback((rect, fromLayoutEffect) => {
3588
+ const { initialScroll } = refState.current;
3290
3589
  const size = rect[horizontal ? "width" : "height"];
3291
3590
  set$(ctx, "headerSize", size);
3292
3591
  if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
@@ -3297,31 +3596,58 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3297
3596
  }
3298
3597
  }
3299
3598
  }, []);
3599
+ const doInitialScroll = React3.useCallback(() => {
3600
+ var _a4;
3601
+ const initialScroll = state.initialScroll;
3602
+ if (initialScroll) {
3603
+ scrollTo(ctx, state, {
3604
+ animated: false,
3605
+ index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3606
+ isInitialScroll: true,
3607
+ offset: initialContentOffset,
3608
+ precomputedWithViewOffset: true
3609
+ });
3610
+ }
3611
+ }, [initialContentOffset, state.initialScroll]);
3612
+ const onLayoutChange = React3.useCallback((layout) => {
3613
+ doInitialScroll();
3614
+ handleLayout(ctx, state, layout, setCanRender);
3615
+ }, []);
3616
+ const { onLayout } = useOnLayoutSync({
3617
+ onLayoutChange,
3618
+ onLayoutProp,
3619
+ ref: refScroller
3620
+ // the type of ScrollView doesn't include measure?
3621
+ });
3300
3622
  React3.useLayoutEffect(() => {
3301
3623
  if (snapToIndices) {
3302
3624
  updateSnapToOffsets(ctx, state);
3303
3625
  }
3304
3626
  }, [snapToIndices]);
3305
3627
  React3.useLayoutEffect(() => {
3306
- const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainers(ctx, state);
3307
- if (!didAllocateContainers) {
3308
- checkResetContainers(
3309
- ctx,
3310
- state,
3311
- /*isFirst*/
3312
- isFirst,
3313
- dataProp
3314
- );
3315
- }
3316
- }, [dataProp, numColumnsProp]);
3628
+ const {
3629
+ didColumnsChange,
3630
+ didDataChange,
3631
+ isFirst,
3632
+ props: { data }
3633
+ } = state;
3634
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3635
+ if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3636
+ checkResetContainers(ctx, state, data);
3637
+ }
3638
+ state.didColumnsChange = false;
3639
+ state.didDataChange = false;
3640
+ state.isFirst = false;
3641
+ }, [dataProp, dataVersion, numColumnsProp]);
3317
3642
  React3.useLayoutEffect(() => {
3318
3643
  set$(ctx, "extraData", extraData);
3319
3644
  }, [extraData]);
3320
3645
  React3.useLayoutEffect(initializeStateVars, [
3646
+ dataVersion,
3321
3647
  memoizedLastItemKeys.join(","),
3322
3648
  numColumnsProp,
3323
- stylePaddingTopState,
3324
- stylePaddingBottomState
3649
+ stylePaddingBottomState,
3650
+ stylePaddingTopState
3325
3651
  ]);
3326
3652
  React3.useEffect(() => {
3327
3653
  const viewability = setupViewability({
@@ -3332,103 +3658,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3332
3658
  state.viewabilityConfigCallbackPairs = viewability;
3333
3659
  state.enableScrollForNextCalculateItemsInView = !viewability;
3334
3660
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3335
- const onLayoutChange = React3.useCallback((layout) => {
3336
- handleLayout(ctx, state, layout, setCanRender);
3337
- }, []);
3338
- const { onLayout } = useOnLayoutSync({
3339
- onLayoutChange,
3340
- onLayoutProp,
3341
- ref: refScroller
3342
- // the type of ScrollView doesn't include measure?
3343
- });
3344
- React3.useImperativeHandle(forwardedRef, () => {
3345
- const scrollIndexIntoView = (options) => {
3346
- const state2 = refState.current;
3347
- if (state2) {
3348
- const { index, ...rest2 } = options;
3349
- const { startNoBuffer, endNoBuffer } = state2;
3350
- if (index < startNoBuffer || index > endNoBuffer) {
3351
- const viewPosition = index < startNoBuffer ? 0 : 1;
3352
- scrollToIndex(ctx, state2, {
3353
- ...rest2,
3354
- index,
3355
- viewPosition
3356
- });
3357
- }
3358
- }
3359
- };
3360
- return {
3361
- flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
3362
- getNativeScrollRef: () => refScroller.current,
3363
- getScrollableNode: () => refScroller.current.getScrollableNode(),
3364
- getScrollResponder: () => refScroller.current.getScrollResponder(),
3365
- getState: () => {
3366
- const state2 = refState.current;
3367
- return state2 ? {
3368
- activeStickyIndex: state2.activeStickyIndex,
3369
- contentLength: state2.totalSize,
3370
- data: state2.props.data,
3371
- end: state2.endNoBuffer,
3372
- endBuffered: state2.endBuffered,
3373
- isAtEnd: state2.isAtEnd,
3374
- isAtStart: state2.isAtStart,
3375
- positionAtIndex: (index) => state2.positions.get(getId(state2, index)),
3376
- positions: state2.positions,
3377
- scroll: state2.scroll,
3378
- scrollLength: state2.scrollLength,
3379
- sizeAtIndex: (index) => state2.sizesKnown.get(getId(state2, index)),
3380
- sizes: state2.sizesKnown,
3381
- start: state2.startNoBuffer,
3382
- startBuffered: state2.startBuffered
3383
- } : {};
3384
- },
3385
- scrollIndexIntoView,
3386
- scrollItemIntoView: ({ item, ...props2 }) => {
3387
- const data = refState.current.props.data;
3388
- const index = data.indexOf(item);
3389
- if (index !== -1) {
3390
- scrollIndexIntoView({ index, ...props2 });
3391
- }
3392
- },
3393
- scrollToEnd: (options) => {
3394
- const data = refState.current.props.data;
3395
- const stylePaddingBottom = refState.current.props.stylePaddingBottom;
3396
- const index = data.length - 1;
3397
- if (index !== -1) {
3398
- const paddingBottom = stylePaddingBottom || 0;
3399
- const footerSize = peek$(ctx, "footerSize") || 0;
3400
- scrollToIndex(ctx, state, {
3401
- index,
3402
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3403
- viewPosition: 1,
3404
- ...options
3405
- });
3406
- }
3407
- },
3408
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3409
- scrollToItem: ({ item, ...props2 }) => {
3410
- const data = refState.current.props.data;
3411
- const index = data.indexOf(item);
3412
- if (index !== -1) {
3413
- scrollToIndex(ctx, state, { index, ...props2 });
3414
- }
3415
- },
3416
- scrollToOffset: (params) => scrollTo(state, params),
3417
- setScrollProcessingEnabled: (enabled) => {
3418
- refState.current.scrollProcessingEnabled = enabled;
3419
- },
3420
- setVisibleContentAnchorOffset: (value) => {
3421
- const val = typeof value === "function" ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
3422
- set$(ctx, "scrollAdjustUserOffset", val);
3423
- }
3424
- };
3425
- }, []);
3661
+ React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
3426
3662
  {
3427
- React3.useEffect(() => {
3428
- if (initialContentOffset) {
3429
- scrollTo(state, { animated: false, offset: initialContentOffset, ...initialScroll || {} });
3430
- }
3431
- }, []);
3663
+ React3.useEffect(doInitialScroll, []);
3432
3664
  }
3433
3665
  const fns = React3.useMemo(
3434
3666
  () => ({
@@ -3457,7 +3689,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3457
3689
  onMomentumScrollEnd: (event) => {
3458
3690
  {
3459
3691
  requestAnimationFrame(() => {
3460
- finishScrollTo(refState.current);
3692
+ finishScrollTo(ctx, refState.current);
3461
3693
  });
3462
3694
  }
3463
3695
  if (onMomentumScrollEnd) {
@@ -3477,7 +3709,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3477
3709
  }
3478
3710
  ),
3479
3711
  refScrollView: combinedRef,
3480
- scrollAdjustHandler: (_a3 = refState.current) == null ? void 0 : _a3.scrollAdjustHandler,
3712
+ scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3481
3713
  scrollEventThrottle: 16 ,
3482
3714
  snapToIndices,
3483
3715
  stickyIndices,