@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.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
  }));
@@ -102,11 +105,12 @@ function set$(ctx, signalName, value) {
102
105
  }
103
106
  }
104
107
  function getContentSize(ctx) {
108
+ var _a3, _b;
105
109
  const { values } = ctx;
106
110
  const stylePaddingTop = values.get("stylePaddingTop") || 0;
107
111
  const headerSize = values.get("headerSize") || 0;
108
112
  const footerSize = values.get("footerSize") || 0;
109
- const totalSize = values.get("totalSize");
113
+ const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
110
114
  return headerSize + footerSize + totalSize + stylePaddingTop;
111
115
  }
112
116
  function useArr$(signalNames) {
@@ -187,6 +191,47 @@ var ENABLE_DEBUG_VIEW = IS_DEV && false;
187
191
  var typedForwardRef = forwardRef;
188
192
  var typedMemo = memo;
189
193
 
194
+ // src/utils/helpers.ts
195
+ function isFunction(obj) {
196
+ return typeof obj === "function";
197
+ }
198
+ function isArray(obj) {
199
+ return Array.isArray(obj);
200
+ }
201
+ var warned = /* @__PURE__ */ new Set();
202
+ function warnDevOnce(id, text) {
203
+ if (IS_DEV && !warned.has(id)) {
204
+ warned.add(id);
205
+ console.warn(`[legend-list] ${text}`);
206
+ }
207
+ }
208
+ function roundSize(size) {
209
+ return Math.floor(size * 8) / 8;
210
+ }
211
+ function isNullOrUndefined(value) {
212
+ return value === null || value === void 0;
213
+ }
214
+ function comparatorDefault(a, b) {
215
+ return a - b;
216
+ }
217
+ function getPadding(s, type) {
218
+ var _a3, _b, _c;
219
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
220
+ }
221
+ function extractPadding(style, contentContainerStyle, type) {
222
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
223
+ }
224
+ function findContainerId(ctx, key) {
225
+ const numContainers = peek$(ctx, "numContainers");
226
+ for (let i = 0; i < numContainers; i++) {
227
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
228
+ if (itemKey === key) {
229
+ return i;
230
+ }
231
+ }
232
+ return -1;
233
+ }
234
+
190
235
  // src/components/PositionView.tsx
191
236
  var PositionViewState = typedMemo(function PositionView({
192
237
  id,
@@ -196,8 +241,11 @@ var PositionViewState = typedMemo(function PositionView({
196
241
  ...rest
197
242
  }) {
198
243
  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 };
244
+ const base = {
245
+ contain: "paint layout style"
246
+ };
247
+ const composed = isArray(style) ? Object.assign({}, ...style) : style;
248
+ const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
201
249
  return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: combinedStyle, ...rest });
202
250
  });
203
251
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
@@ -206,19 +254,42 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
206
254
  style,
207
255
  refView,
208
256
  index,
257
+ stickyOffset,
258
+ animatedScrollY: _animatedScrollY,
259
+ children,
209
260
  ...rest
210
261
  }) {
211
- const [position = POSITION_OUT_OF_VIEW, _headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
262
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, activeStickyIndex] = useArr$([
263
+ `containerPosition${id}`,
264
+ "headerSize",
265
+ "activeStickyIndex"
266
+ ]);
267
+ const base = {
268
+ contain: "paint layout style"
269
+ };
270
+ const composed = React3.useMemo(
271
+ () => {
272
+ var _a3;
273
+ return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
274
+ },
275
+ [style]
276
+ );
212
277
  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 });
278
+ var _a3;
279
+ const styleBase = { ...base, ...composed };
280
+ delete styleBase.transform;
281
+ const offset = (_a3 = stickyOffset != null ? stickyOffset : headerSize) != null ? _a3 : 0;
282
+ const isActive = activeStickyIndex === index;
283
+ styleBase.position = isActive ? "sticky" : "absolute";
284
+ styleBase.zIndex = index + 1e3;
285
+ if (horizontal) {
286
+ styleBase.left = isActive ? offset : position;
287
+ } else {
288
+ styleBase.top = isActive ? offset : position;
289
+ }
290
+ return styleBase;
291
+ }, [composed, horizontal, position, index, stickyOffset, headerSize, activeStickyIndex]);
292
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
222
293
  });
223
294
  var PositionView2 = PositionViewState;
224
295
 
@@ -233,37 +304,6 @@ function useInit(cb) {
233
304
  return refValue.current;
234
305
  }
235
306
 
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
307
  // src/state/ContextContainer.ts
268
308
  var ContextContainer = createContext(null);
269
309
  function useViewability(callback, configId) {
@@ -392,6 +432,10 @@ function getGlobalResizeObserver() {
392
432
  }
393
433
  var callbackMap = /* @__PURE__ */ new WeakMap();
394
434
  function createResizeObserver(element, callback) {
435
+ if (typeof ResizeObserver === "undefined") {
436
+ return () => {
437
+ };
438
+ }
395
439
  if (!element) {
396
440
  return () => {
397
441
  };
@@ -427,7 +471,7 @@ function useOnLayoutSync({
427
471
  const current = ref.current;
428
472
  const scrollableNode = (_b = (_a3 = current == null ? void 0 : current.getScrollableNode) == null ? void 0 : _a3.call(current)) != null ? _b : null;
429
473
  const element = scrollableNode || current;
430
- if (!element || !(element instanceof HTMLElement)) {
474
+ if (!element) {
431
475
  return;
432
476
  }
433
477
  const emit = (layout, fromLayoutEffect) => {
@@ -449,6 +493,9 @@ function useOnLayoutSync({
449
493
  return {};
450
494
  }
451
495
  function toLayout(rect) {
496
+ if (!rect) {
497
+ return { height: 0, width: 0, x: 0, y: 0 };
498
+ }
452
499
  return {
453
500
  height: rect.height,
454
501
  width: rect.width,
@@ -550,7 +597,7 @@ var Container = typedMemo(function Container2({
550
597
  }
551
598
  didLayoutRef.current = true;
552
599
  let layout = rectangle;
553
- Math.floor(rectangle[currentHorizontal ? "width" : "height"] * 8) / 8;
600
+ roundSize(rectangle[currentHorizontal ? "width" : "height"]);
554
601
  const doUpdate = () => {
555
602
  itemLayoutRef.current.lastSize = { height: layout.height, width: layout.width };
556
603
  updateItemSizeFn(currentItemKey, layout);
@@ -708,7 +755,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
708
755
  const columnWrapperStyle = ctx.columnWrapperStyle;
709
756
  const [totalSize, otherAxisSize] = useArr$(["totalSize", "otherAxisSize"]);
710
757
  useDOMOrder(ref);
711
- const style = horizontal ? { minHeight: otherAxisSize, width: totalSize } : { height: totalSize, minWidth: otherAxisSize };
758
+ const style = horizontal ? { minHeight: otherAxisSize, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, position: "relative" };
712
759
  if (columnWrapperStyle && numColumns > 1) {
713
760
  const { columnGap, rowGap, gap } = columnWrapperStyle;
714
761
  const gapX = columnGap || gap || 0;
@@ -780,7 +827,7 @@ function DevNumbers() {
780
827
 
781
828
  // src/platform/StyleSheet.tsx
782
829
  function flattenStyles(styles) {
783
- if (Array.isArray(styles)) {
830
+ if (isArray(styles)) {
784
831
  return Object.assign({}, ...styles.filter(Boolean));
785
832
  }
786
833
  return styles;
@@ -970,10 +1017,11 @@ function PaddingDevMode() {
970
1017
  function useValueListener$(key, callback) {
971
1018
  const ctx = useStateContext();
972
1019
  useLayoutEffect(() => {
973
- listen$(ctx, key, (value) => {
1020
+ const unsubscribe = listen$(ctx, key, (value) => {
974
1021
  callback(value);
975
1022
  });
976
- }, []);
1023
+ return unsubscribe;
1024
+ }, [callback, ctx, key]);
977
1025
  }
978
1026
 
979
1027
  // src/components/ScrollAdjust.tsx
@@ -1138,13 +1186,84 @@ function calculateOffsetForIndex(ctx, state, index) {
1138
1186
  return position;
1139
1187
  }
1140
1188
 
1189
+ // src/utils/setPaddingTop.ts
1190
+ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1191
+ if (stylePaddingTop !== void 0) {
1192
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1193
+ if (stylePaddingTop < prevStylePaddingTop) {
1194
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
1195
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1196
+ state.timeoutSetPaddingTop = setTimeout(() => {
1197
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
1198
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1199
+ }, 16);
1200
+ }
1201
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
1202
+ }
1203
+ if (alignItemsPaddingTop !== void 0) {
1204
+ set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1205
+ }
1206
+ }
1207
+
1208
+ // src/utils/updateAlignItemsPaddingTop.ts
1209
+ function updateAlignItemsPaddingTop(ctx, state) {
1210
+ const {
1211
+ scrollLength,
1212
+ props: { alignItemsAtEnd, data }
1213
+ } = state;
1214
+ if (alignItemsAtEnd) {
1215
+ let alignItemsPaddingTop = 0;
1216
+ if ((data == null ? void 0 : data.length) > 0) {
1217
+ const contentSize = getContentSize(ctx);
1218
+ alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1219
+ }
1220
+ setPaddingTop(ctx, state, { alignItemsPaddingTop });
1221
+ }
1222
+ }
1223
+
1224
+ // src/core/addTotalSize.ts
1225
+ function addTotalSize(ctx, state, key, add) {
1226
+ const { alignItemsAtEnd } = state.props;
1227
+ const prevTotalSize = state.totalSize;
1228
+ let totalSize = state.totalSize;
1229
+ if (key === null) {
1230
+ totalSize = add;
1231
+ if (state.timeoutSetPaddingTop) {
1232
+ clearTimeout(state.timeoutSetPaddingTop);
1233
+ state.timeoutSetPaddingTop = void 0;
1234
+ }
1235
+ } else {
1236
+ totalSize += add;
1237
+ }
1238
+ if (prevTotalSize !== totalSize) {
1239
+ {
1240
+ state.pendingTotalSize = void 0;
1241
+ state.totalSize = totalSize;
1242
+ set$(ctx, "totalSize", totalSize);
1243
+ if (alignItemsAtEnd) {
1244
+ updateAlignItemsPaddingTop(ctx, state);
1245
+ }
1246
+ }
1247
+ }
1248
+ }
1249
+
1250
+ // src/core/setSize.ts
1251
+ function setSize(ctx, state, itemKey, size) {
1252
+ const { sizes } = state;
1253
+ const previousSize = sizes.get(itemKey);
1254
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1255
+ if (diff !== 0) {
1256
+ addTotalSize(ctx, state, itemKey, diff);
1257
+ }
1258
+ sizes.set(itemKey, size);
1259
+ }
1260
+
1141
1261
  // src/utils/getItemSize.ts
1142
- function getItemSize(state, key, index, data, useAverageSize) {
1262
+ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1143
1263
  var _a3, _b;
1144
1264
  const {
1145
1265
  sizesKnown,
1146
1266
  sizes,
1147
- scrollingTo,
1148
1267
  averageSizes,
1149
1268
  props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1150
1269
  } = state;
@@ -1154,6 +1273,13 @@ function getItemSize(state, key, index, data, useAverageSize) {
1154
1273
  }
1155
1274
  let size;
1156
1275
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1276
+ const scrollingTo = peek$(ctx, "scrollingTo");
1277
+ if (preferCachedSize) {
1278
+ const cachedSize = sizes.get(key);
1279
+ if (cachedSize !== void 0) {
1280
+ return cachedSize;
1281
+ }
1282
+ }
1157
1283
  if (getFixedItemSize) {
1158
1284
  size = getFixedItemSize(index, data, itemType);
1159
1285
  if (size !== void 0) {
@@ -1175,108 +1301,326 @@ function getItemSize(state, key, index, data, useAverageSize) {
1175
1301
  if (size === void 0) {
1176
1302
  size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1177
1303
  }
1178
- sizes.set(key, size);
1304
+ setSize(ctx, state, key, size);
1179
1305
  return size;
1180
1306
  }
1181
1307
 
1182
1308
  // src/core/calculateOffsetWithOffsetPosition.ts
1183
- function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
1309
+ function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1184
1310
  const { index, viewOffset, viewPosition } = params;
1185
1311
  let offset = offsetParam;
1186
1312
  if (viewOffset) {
1187
1313
  offset -= viewOffset;
1188
1314
  }
1189
1315
  if (viewPosition !== void 0 && index !== void 0) {
1190
- offset -= viewPosition * (state.scrollLength - getItemSize(state, getId(state, index), index, state.props.data[index]));
1316
+ offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1191
1317
  }
1192
1318
  return offset;
1193
1319
  }
1194
1320
 
1195
- // src/core/finishScrollTo.ts
1196
- var finishScrollTo = (state) => {
1197
- if (state) {
1198
- state.scrollingTo = void 0;
1199
- state.scrollHistory.length = 0;
1321
+ // src/utils/checkThreshold.ts
1322
+ var HYSTERESIS_MULTIPLIER = 1.3;
1323
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1324
+ const absDistance = Math.abs(distance);
1325
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
1326
+ const updateSnapshot = () => {
1327
+ setSnapshot == null ? void 0 : setSnapshot({
1328
+ atThreshold,
1329
+ contentSize: context.contentSize,
1330
+ dataLength: context.dataLength,
1331
+ scrollPosition: context.scrollPosition
1332
+ });
1333
+ };
1334
+ if (!wasReached) {
1335
+ if (!within) {
1336
+ return false;
1337
+ }
1338
+ onReached == null ? void 0 : onReached(distance);
1339
+ updateSnapshot();
1340
+ return true;
1341
+ }
1342
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1343
+ if (reset) {
1344
+ setSnapshot == null ? void 0 : setSnapshot(void 0);
1345
+ return false;
1346
+ }
1347
+ if (within) {
1348
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1349
+ if (changed) {
1350
+ onReached == null ? void 0 : onReached(distance);
1351
+ updateSnapshot();
1352
+ }
1200
1353
  }
1354
+ return true;
1201
1355
  };
1202
1356
 
1203
- // src/core/scrollTo.ts
1204
- function scrollTo(state, params = {}) {
1357
+ // src/utils/checkAtBottom.ts
1358
+ function checkAtBottom(ctx, state) {
1205
1359
  var _a3;
1206
- const { animated, noScrollingTo, isInitialScroll } = params;
1360
+ if (!state) {
1361
+ return;
1362
+ }
1207
1363
  const {
1208
- refScroller,
1209
- props: { horizontal }
1364
+ queuedInitialLayout,
1365
+ scrollLength,
1366
+ scroll,
1367
+ maintainingScrollAtEnd,
1368
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1210
1369
  } = 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
- });
1223
- }
1224
- if (!animated) {
1225
- state.scroll = offset;
1226
- setTimeout(() => finishScrollTo(state), 100);
1227
- if (isInitialScroll) {
1228
- setTimeout(() => {
1229
- state.initialScroll = void 0;
1230
- }, 500);
1231
- }
1370
+ const contentSize = getContentSize(ctx);
1371
+ if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1372
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1373
+ const isContentLess = contentSize < scrollLength;
1374
+ state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1375
+ state.isEndReached = checkThreshold(
1376
+ distanceFromEnd,
1377
+ isContentLess,
1378
+ onEndReachedThreshold * scrollLength,
1379
+ state.isEndReached,
1380
+ state.endReachedSnapshot,
1381
+ {
1382
+ contentSize,
1383
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1384
+ scrollPosition: scroll
1385
+ },
1386
+ (distance) => {
1387
+ var _a4, _b;
1388
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1389
+ },
1390
+ (snapshot) => {
1391
+ state.endReachedSnapshot = snapshot;
1392
+ }
1393
+ );
1232
1394
  }
1233
1395
  }
1234
1396
 
1235
- // src/utils/requestAdjust.ts
1236
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1237
- if (Math.abs(positionDiff) > 0.1) {
1238
- const doit = () => {
1239
- {
1240
- state.scrollAdjustHandler.requestAdjust(positionDiff);
1241
- }
1242
- };
1243
- state.scroll += positionDiff;
1244
- state.scrollForNextCalculateItemsInView = void 0;
1245
- const didLayout = peek$(ctx, "containersDidLayout");
1246
- if (didLayout) {
1247
- 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
- );
1397
+ // src/utils/checkAtTop.ts
1398
+ function checkAtTop(state) {
1399
+ var _a3;
1400
+ if (!state) {
1401
+ return;
1402
+ }
1403
+ const {
1404
+ scrollLength,
1405
+ scroll,
1406
+ props: { onStartReachedThreshold }
1407
+ } = state;
1408
+ const distanceFromTop = scroll;
1409
+ state.isAtStart = distanceFromTop <= 0;
1410
+ state.isStartReached = checkThreshold(
1411
+ distanceFromTop,
1412
+ false,
1413
+ onStartReachedThreshold * scrollLength,
1414
+ state.isStartReached,
1415
+ state.startReachedSnapshot,
1416
+ {
1417
+ contentSize: state.totalSize,
1418
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1419
+ scrollPosition: scroll
1420
+ },
1421
+ (distance) => {
1422
+ var _a4, _b;
1423
+ return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
1424
+ },
1425
+ (snapshot) => {
1426
+ state.startReachedSnapshot = snapshot;
1427
+ }
1428
+ );
1429
+ }
1430
+
1431
+ // src/core/onScroll.ts
1432
+ function onScroll(ctx, state, event) {
1433
+ var _a3, _b, _c;
1434
+ const {
1435
+ scrollProcessingEnabled,
1436
+ props: { onScroll: onScrollProp }
1437
+ } = state;
1438
+ if (scrollProcessingEnabled === false) {
1439
+ return;
1440
+ }
1441
+ 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) {
1442
+ return;
1443
+ }
1444
+ const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
1445
+ state.scrollPending = newScroll;
1446
+ updateScroll(ctx, state, newScroll);
1447
+ onScrollProp == null ? void 0 : onScrollProp(event);
1448
+ }
1449
+ function updateScroll(ctx, state, newScroll, forceUpdate) {
1450
+ var _a3;
1451
+ const scrollingTo = peek$(ctx, "scrollingTo");
1452
+ state.hasScrolled = true;
1453
+ state.lastBatchingAction = Date.now();
1454
+ const currentTime = Date.now();
1455
+ const adjust = state.scrollAdjustHandler.getAdjust();
1456
+ const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1457
+ const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1458
+ if (adjustChanged) {
1459
+ state.scrollHistory.length = 0;
1460
+ }
1461
+ state.lastScrollAdjustForHistory = adjust;
1462
+ if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1463
+ if (!adjustChanged) {
1464
+ state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1465
+ }
1466
+ }
1467
+ if (state.scrollHistory.length > 5) {
1468
+ state.scrollHistory.shift();
1469
+ }
1470
+ state.scrollPrev = state.scroll;
1471
+ state.scrollPrevTime = state.scrollTime;
1472
+ state.scroll = newScroll;
1473
+ state.scrollTime = currentTime;
1474
+ const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
1475
+ if (ignoreScrollFromMVCP && !scrollingTo) {
1476
+ const { lt, gt } = ignoreScrollFromMVCP;
1477
+ if (lt && newScroll < lt || gt && newScroll > gt) {
1478
+ state.ignoreScrollFromMVCPIgnored = true;
1479
+ return;
1480
+ }
1481
+ }
1482
+ if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1483
+ state.ignoreScrollFromMVCPIgnored = false;
1484
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1485
+ checkAtBottom(ctx, state);
1486
+ checkAtTop(state);
1487
+ state.dataChangeNeedsScrollUpdate = false;
1488
+ }
1489
+ }
1490
+
1491
+ // src/core/finishScrollTo.ts
1492
+ function finishScrollTo(ctx, state) {
1493
+ var _a3, _b;
1494
+ if (state) {
1495
+ state.scrollHistory.length = 0;
1496
+ state.initialScroll = void 0;
1497
+ state.initialAnchor = void 0;
1498
+ set$(ctx, "scrollingTo", void 0);
1499
+ if (state.pendingTotalSize !== void 0) {
1500
+ addTotalSize(ctx, state, null, state.pendingTotalSize);
1501
+ }
1502
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1503
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1504
+ }
1505
+ }
1506
+ }
1507
+
1508
+ // src/core/scrollTo.ts
1509
+ function scrollTo(ctx, state, params) {
1510
+ var _a3;
1511
+ const { noScrollingTo, ...scrollTarget } = params;
1512
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1513
+ const {
1514
+ refScroller,
1515
+ props: { horizontal }
1516
+ } = state;
1517
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1518
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1519
+ const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1520
+ offset = Math.min(offset, maxOffset);
1521
+ }
1522
+ state.scrollHistory.length = 0;
1523
+ if (!noScrollingTo) {
1524
+ set$(ctx, "scrollingTo", scrollTarget);
1525
+ }
1526
+ state.scrollPending = offset;
1527
+ if (!isInitialScroll || Platform.OS === "android") {
1528
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1529
+ animated: !!animated,
1530
+ x: horizontal ? offset : 0,
1531
+ y: horizontal ? 0 : offset
1532
+ });
1533
+ }
1534
+ if (!animated) {
1535
+ state.scroll = offset;
1536
+ setTimeout(() => finishScrollTo(ctx, state), 100);
1537
+ if (isInitialScroll) {
1538
+ setTimeout(() => {
1539
+ state.initialScroll = void 0;
1540
+ }, 500);
1541
+ }
1542
+ }
1543
+ }
1544
+
1545
+ // src/utils/requestAdjust.ts
1546
+ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1547
+ if (Math.abs(positionDiff) > 0.1) {
1548
+ const doit = () => {
1549
+ {
1550
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
1551
+ }
1552
+ };
1553
+ state.scroll += positionDiff;
1554
+ state.scrollForNextCalculateItemsInView = void 0;
1555
+ const didLayout = peek$(ctx, "containersDidLayout");
1556
+ if (didLayout) {
1557
+ doit();
1266
1558
  } else {
1267
1559
  requestAnimationFrame(doit);
1268
1560
  }
1269
1561
  }
1270
1562
  }
1271
1563
 
1564
+ // src/core/ensureInitialAnchor.ts
1565
+ var INITIAL_ANCHOR_TOLERANCE = 0.5;
1566
+ var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1567
+ var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1568
+ function ensureInitialAnchor(ctx, state) {
1569
+ var _a3, _b, _c, _d, _e;
1570
+ const anchor = state.initialAnchor;
1571
+ const item = state.props.data[anchor.index];
1572
+ const containersDidLayout = peek$(ctx, "containersDidLayout");
1573
+ if (!containersDidLayout) {
1574
+ return;
1575
+ }
1576
+ const id = getId(state, anchor.index);
1577
+ if (state.positions.get(id) === void 0) {
1578
+ return;
1579
+ }
1580
+ const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1581
+ if (size === void 0) {
1582
+ return;
1583
+ }
1584
+ const availableSpace = Math.max(0, state.scrollLength - size);
1585
+ const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1586
+ const contentSize = getContentSize(ctx);
1587
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1588
+ const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1589
+ const delta = clampedDesiredOffset - state.scroll;
1590
+ if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1591
+ const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1592
+ if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1593
+ state.initialAnchor = void 0;
1594
+ } else {
1595
+ anchor.settledTicks = settledTicks;
1596
+ }
1597
+ return;
1598
+ }
1599
+ if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1600
+ state.initialAnchor = void 0;
1601
+ return;
1602
+ }
1603
+ const lastDelta = anchor.lastDelta;
1604
+ if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1605
+ state.initialAnchor = void 0;
1606
+ return;
1607
+ }
1608
+ Object.assign(anchor, {
1609
+ attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1610
+ lastDelta: delta,
1611
+ settledTicks: 0
1612
+ });
1613
+ requestAdjust(ctx, state, delta);
1614
+ }
1615
+
1272
1616
  // src/core/mvcp.ts
1273
1617
  function prepareMVCP(ctx, state, dataChanged) {
1274
1618
  const {
1275
1619
  idsInView,
1276
1620
  positions,
1277
- scrollingTo,
1278
1621
  props: { maintainVisibleContentPosition }
1279
1622
  } = state;
1623
+ const scrollingTo = peek$(ctx, "scrollingTo");
1280
1624
  let prevPosition;
1281
1625
  let targetId;
1282
1626
  const idsInViewWithPositions = [];
@@ -1317,7 +1661,7 @@ function prepareMVCP(ctx, state, dataChanged) {
1317
1661
  if (targetId !== void 0 && prevPosition !== void 0) {
1318
1662
  const newPosition = positions.get(targetId);
1319
1663
  if (newPosition !== void 0) {
1320
- const totalSize = peek$(ctx, "totalSize");
1664
+ const totalSize = getContentSize(ctx);
1321
1665
  let diff = newPosition - prevPosition;
1322
1666
  if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
1323
1667
  if (diff > 0) {
@@ -1352,7 +1696,7 @@ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1352
1696
  const prevId = state.idCache[prevIndex];
1353
1697
  const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1354
1698
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1355
- const prevRowHeight = calculateRowMaxSize(state, prevRowStart, prevIndex, useAverageSize);
1699
+ const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1356
1700
  currentRowTop = prevPosition + prevRowHeight;
1357
1701
  }
1358
1702
  return {
@@ -1375,7 +1719,7 @@ function findRowStartIndex(state, numColumns, index) {
1375
1719
  }
1376
1720
  return rowStart;
1377
1721
  }
1378
- function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1722
+ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1379
1723
  if (endIndex < startIndex) {
1380
1724
  return 0;
1381
1725
  }
@@ -1389,7 +1733,7 @@ function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1389
1733
  continue;
1390
1734
  }
1391
1735
  const id = state.idCache[i];
1392
- const size = getItemSize(state, id, i, data[i], useAverageSize);
1736
+ const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1393
1737
  if (size > maxSize) {
1394
1738
  maxSize = size;
1395
1739
  }
@@ -1397,41 +1741,6 @@ function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1397
1741
  return maxSize;
1398
1742
  }
1399
1743
 
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
1744
  // src/core/updateTotalSize.ts
1436
1745
  function updateTotalSize(ctx, state) {
1437
1746
  const {
@@ -1445,7 +1754,7 @@ function updateTotalSize(ctx, state) {
1445
1754
  if (lastId !== void 0) {
1446
1755
  const lastPosition = positions.get(lastId);
1447
1756
  if (lastPosition !== void 0) {
1448
- const lastSize = getItemSize(state, lastId, data.length - 1, data[data.length - 1]);
1757
+ const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1449
1758
  if (lastSize !== void 0) {
1450
1759
  const totalSize = lastPosition + lastSize;
1451
1760
  addTotalSize(ctx, state, null, totalSize);
@@ -1454,25 +1763,44 @@ function updateTotalSize(ctx, state) {
1454
1763
  }
1455
1764
  }
1456
1765
  }
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;
1766
+
1767
+ // src/utils/getScrollVelocity.ts
1768
+ var getScrollVelocity = (state) => {
1769
+ const { scrollHistory } = state;
1770
+ let velocity = 0;
1771
+ if (scrollHistory.length >= 1) {
1772
+ const newest = scrollHistory[scrollHistory.length - 1];
1773
+ let oldest;
1774
+ let start = 0;
1775
+ const now = Date.now();
1776
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1777
+ const entry = scrollHistory[i];
1778
+ const nextEntry = scrollHistory[i + 1];
1779
+ if (i > 0) {
1780
+ const prevEntry = scrollHistory[i - 1];
1781
+ const prevDirection = entry.scroll - prevEntry.scroll;
1782
+ const currentDirection = nextEntry.scroll - entry.scroll;
1783
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1784
+ start = i;
1785
+ break;
1786
+ }
1787
+ }
1465
1788
  }
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);
1789
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1790
+ const entry = scrollHistory[i];
1791
+ if (now - entry.time <= 1e3) {
1792
+ oldest = entry;
1793
+ break;
1794
+ }
1795
+ }
1796
+ if (oldest && oldest !== newest) {
1797
+ const scrollDiff = newest.scroll - oldest.scroll;
1798
+ const timeDiff = newest.time - oldest.time;
1799
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1473
1800
  }
1474
1801
  }
1475
- }
1802
+ return velocity;
1803
+ };
1476
1804
 
1477
1805
  // src/utils/updateSnapToOffsets.ts
1478
1806
  function updateSnapToOffsets(ctx, state) {
@@ -1490,23 +1818,30 @@ function updateSnapToOffsets(ctx, state) {
1490
1818
  }
1491
1819
 
1492
1820
  // src/core/updateItemPositions.ts
1493
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered } = { scrollBottomBuffered: -1, startIndex: 0 }) {
1494
- var _a3, _b, _c, _d;
1821
+ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false } = {
1822
+ forceFullUpdate: false,
1823
+ scrollBottomBuffered: -1,
1824
+ startIndex: 0
1825
+ }) {
1826
+ var _a3, _b, _c, _d, _e;
1495
1827
  const {
1496
1828
  columns,
1497
1829
  indexByKey,
1498
1830
  positions,
1499
1831
  idCache,
1500
1832
  sizesKnown,
1501
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
1833
+ props: { getEstimatedItemSize, snapToIndices, enableAverages, maintainVisibleContentPosition }
1502
1834
  } = state;
1503
1835
  const data = state.props.data;
1504
1836
  const dataLength = data.length;
1505
1837
  const numColumns = peek$(ctx, "numColumns");
1838
+ const scrollingTo = peek$(ctx, "scrollingTo");
1506
1839
  const hasColumns = numColumns > 1;
1507
1840
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1841
+ const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1508
1842
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1509
1843
  const useAverageSize = enableAverages && !getEstimatedItemSize;
1844
+ const preferCachedSize = maintainVisibleContentPosition && (dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0);
1510
1845
  let currentRowTop = 0;
1511
1846
  let column = 1;
1512
1847
  let maxSizeInRow = 0;
@@ -1523,8 +1858,8 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1523
1858
  } else if (startIndex < dataLength) {
1524
1859
  const prevIndex = startIndex - 1;
1525
1860
  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);
1861
+ const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1862
+ const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1528
1863
  currentRowTop = prevPosition + prevSize;
1529
1864
  }
1530
1865
  }
@@ -1532,16 +1867,16 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1532
1867
  let didBreakEarly = false;
1533
1868
  let breakAt;
1534
1869
  for (let i = startIndex; i < dataLength; i++) {
1535
- if (breakAt && i > breakAt) {
1870
+ if (shouldOptimize && breakAt !== void 0 && i > breakAt) {
1536
1871
  didBreakEarly = true;
1537
1872
  break;
1538
1873
  }
1539
- if (breakAt === void 0 && !dataChanged && currentRowTop > maxVisibleArea) {
1874
+ if (shouldOptimize && breakAt === void 0 && !scrollingTo && !dataChanged && currentRowTop > maxVisibleArea) {
1540
1875
  const itemsPerRow = hasColumns ? numColumns : 1;
1541
1876
  breakAt = i + itemsPerRow + 10;
1542
1877
  }
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);
1878
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1879
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
1545
1880
  if (IS_DEV && needsIndexByKey) {
1546
1881
  if (indexByKeyForChecking.has(id)) {
1547
1882
  console.error(
@@ -1759,16 +2094,6 @@ function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize,
1759
2094
  const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
1760
2095
  return value.isViewable;
1761
2096
  }
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
2097
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1773
2098
  const key = containerId + configId;
1774
2099
  ctx.mapViewabilityValues.set(key, viewToken);
@@ -1817,7 +2142,7 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1817
2142
  for (const containerIndex of stickyContainerPool) {
1818
2143
  const key = peek$(ctx, `containerItemKey${containerIndex}`);
1819
2144
  const isPendingRemoval = pendingRemovalSet.has(containerIndex);
1820
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
2145
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType) && !result.includes(containerIndex)) {
1821
2146
  result.push(containerIndex);
1822
2147
  if (isPendingRemoval && pendingRemovalSet.delete(containerIndex)) {
1823
2148
  pendingRemovalChanged = true;
@@ -1910,144 +2235,30 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1910
2235
  }
1911
2236
  return result.sort(comparatorDefault);
1912
2237
  }
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();
2008
- }
2009
- }
2010
- return true;
2011
- };
2238
+ function comparatorByDistance(a, b) {
2239
+ return b.distance - a.distance;
2240
+ }
2012
2241
 
2013
- // src/utils/checkAtBottom.ts
2014
- function checkAtBottom(ctx, state) {
2015
- var _a3;
2016
- if (!state) {
2017
- return;
2242
+ // src/core/scrollToIndex.ts
2243
+ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2244
+ if (index >= state.props.data.length) {
2245
+ index = state.props.data.length - 1;
2246
+ } else if (index < 0) {
2247
+ index = 0;
2018
2248
  }
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
- );
2249
+ const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2250
+ const isLast = index === state.props.data.length - 1;
2251
+ if (isLast && viewPosition === void 0) {
2252
+ viewPosition = 1;
2050
2253
  }
2254
+ state.scrollForNextCalculateItemsInView = void 0;
2255
+ scrollTo(ctx, state, {
2256
+ animated,
2257
+ index,
2258
+ offset: firstIndexOffset,
2259
+ viewOffset,
2260
+ viewPosition: viewPosition != null ? viewPosition : 0
2261
+ });
2051
2262
  }
2052
2263
 
2053
2264
  // src/utils/setDidLayout.ts
@@ -2129,7 +2340,7 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2129
2340
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2130
2341
  if (currentId) {
2131
2342
  const currentPos = state.positions.get(currentId);
2132
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
2343
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2133
2344
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2134
2345
  }
2135
2346
  }
@@ -2140,46 +2351,52 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2140
2351
  }
2141
2352
  function calculateItemsInView(ctx, state, params = {}) {
2142
2353
  unstable_batchedUpdates(() => {
2143
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
2354
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2144
2355
  const {
2145
2356
  columns,
2146
2357
  containerItemKeys,
2147
2358
  enableScrollForNextCalculateItemsInView,
2148
2359
  idCache,
2149
2360
  indexByKey,
2361
+ initialScroll,
2150
2362
  minIndexSizeChanged,
2151
2363
  positions,
2364
+ props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2152
2365
  scrollForNextCalculateItemsInView,
2153
2366
  scrollLength,
2154
2367
  sizes,
2155
2368
  startBufferedId: startBufferedIdOrig,
2156
- viewabilityConfigCallbackPairs,
2157
- props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer }
2369
+ viewabilityConfigCallbackPairs
2158
2370
  } = state;
2159
2371
  const { data } = state.props;
2160
2372
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2161
2373
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2162
2374
  const prevNumContainers = peek$(ctx, "numContainers");
2163
2375
  if (!data || scrollLength === 0 || !prevNumContainers) {
2376
+ if (state.initialAnchor) {
2377
+ ensureInitialAnchor(ctx, state);
2378
+ }
2164
2379
  return;
2165
2380
  }
2166
- const totalSize = peek$(ctx, "totalSize");
2381
+ const totalSize = getContentSize(ctx);
2167
2382
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2168
2383
  const numColumns = peek$(ctx, "numColumns");
2169
- const { dataChanged, doMVCP } = params;
2384
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
2170
2385
  const speed = getScrollVelocity(state);
2171
2386
  const scrollExtra = 0;
2172
2387
  const { queuedInitialLayout } = state;
2173
2388
  let { scroll: scrollState } = state;
2174
2389
  if (!queuedInitialLayout && initialScroll) {
2175
2390
  const updatedOffset = calculateOffsetWithOffsetPosition(
2391
+ ctx,
2176
2392
  state,
2177
2393
  calculateOffsetForIndex(ctx, state, initialScroll.index),
2178
2394
  initialScroll
2179
2395
  );
2180
2396
  scrollState = updatedOffset;
2181
2397
  }
2182
- const scrollAdjustPad = -topPad;
2398
+ const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2399
+ const scrollAdjustPad = scrollAdjustPending - topPad;
2183
2400
  let scroll = scrollState + scrollExtra + scrollAdjustPad;
2184
2401
  if (scroll + scrollLength > totalSize) {
2185
2402
  scroll = Math.max(0, totalSize - scrollLength);
@@ -2192,6 +2409,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2192
2409
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2193
2410
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2194
2411
  state.activeStickyIndex = nextActiveStickyIndex;
2412
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2195
2413
  let scrollBufferTop = scrollBuffer;
2196
2414
  let scrollBufferBottom = scrollBuffer;
2197
2415
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2207,6 +2425,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2207
2425
  if (!dataChanged && scrollForNextCalculateItemsInView) {
2208
2426
  const { top, bottom } = scrollForNextCalculateItemsInView;
2209
2427
  if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2428
+ if (state.initialAnchor) {
2429
+ ensureInitialAnchor(ctx, state);
2430
+ }
2210
2431
  return;
2211
2432
  }
2212
2433
  }
@@ -2216,8 +2437,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2216
2437
  idCache.length = 0;
2217
2438
  positions.clear();
2218
2439
  }
2219
- const startIndex = dataChanged ? 0 : (_a3 = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _a3 : 0;
2220
- updateItemPositions(ctx, state, dataChanged, { scrollBottomBuffered, startIndex });
2440
+ const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2441
+ updateItemPositions(ctx, state, dataChanged, {
2442
+ forceFullUpdate: !!forceFullItemPositions,
2443
+ scrollBottomBuffered,
2444
+ startIndex
2445
+ });
2221
2446
  if (minIndexSizeChanged !== void 0) {
2222
2447
  state.minIndexSizeChanged = void 0;
2223
2448
  }
@@ -2229,9 +2454,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2229
2454
  let endBuffered = null;
2230
2455
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2231
2456
  for (let i = loopStart; i >= 0; i--) {
2232
- const id = (_b = idCache[i]) != null ? _b : getId(state, i);
2457
+ const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2233
2458
  const top = positions.get(id);
2234
- const size = (_c = sizes.get(id)) != null ? _c : getItemSize(state, id, i, data[i]);
2459
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2235
2460
  const bottom = top + size;
2236
2461
  if (bottom > scroll - scrollBuffer) {
2237
2462
  loopStart = i;
@@ -2257,8 +2482,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2257
2482
  let firstFullyOnScreenIndex;
2258
2483
  const dataLength = data.length;
2259
2484
  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]);
2485
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2486
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2262
2487
  const top = positions.get(id);
2263
2488
  if (!foundEnd) {
2264
2489
  if (startNoBuffer === null && top + size > scroll) {
@@ -2287,7 +2512,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2287
2512
  }
2288
2513
  const idsInView = [];
2289
2514
  for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2290
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2515
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2291
2516
  idsInView.push(id);
2292
2517
  }
2293
2518
  Object.assign(state, {
@@ -2319,7 +2544,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2319
2544
  let numContainers2 = prevNumContainers;
2320
2545
  const needNewContainers = [];
2321
2546
  for (let i = startBuffered; i <= endBuffered; i++) {
2322
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2547
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2323
2548
  if (!containerItemKeys.has(id)) {
2324
2549
  needNewContainers.push(i);
2325
2550
  }
@@ -2337,6 +2562,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2337
2562
  );
2338
2563
  } else {
2339
2564
  state.activeStickyIndex = void 0;
2565
+ set$(ctx, "activeStickyIndex", void 0);
2340
2566
  }
2341
2567
  if (needNewContainers.length > 0) {
2342
2568
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
@@ -2356,7 +2582,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2356
2582
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2357
2583
  const i = needNewContainers[idx];
2358
2584
  const containerIndex = availableContainers[idx];
2359
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2585
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2360
2586
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2361
2587
  if (oldKey && oldKey !== id) {
2362
2588
  containerItemKeys.delete(oldKey);
@@ -2395,7 +2621,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2395
2621
  for (let i = 0; i < numContainers; i++) {
2396
2622
  const itemKey = peek$(ctx, `containerItemKey${i}`);
2397
2623
  if (pendingRemoval.includes(i)) {
2398
- if (itemKey) {
2624
+ if (itemKey !== void 0) {
2399
2625
  containerItemKeys.delete(itemKey);
2400
2626
  }
2401
2627
  state.containerItemTypes.delete(i);
@@ -2412,11 +2638,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2412
2638
  const itemIndex = indexByKey.get(itemKey);
2413
2639
  const item = data[itemIndex];
2414
2640
  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) {
2641
+ const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2642
+ const positionValue = positions.get(id);
2643
+ if (positionValue === void 0) {
2418
2644
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2419
2645
  } else {
2646
+ const position = (positionValue || 0) - scrollAdjustPending;
2420
2647
  const column = columns.get(id) || 1;
2421
2648
  const prevPos = peek$(ctx, `containerPosition${i}`);
2422
2649
  const prevColumn = peek$(ctx, `containerColumn${i}`);
@@ -2453,6 +2680,29 @@ function calculateItemsInView(ctx, state, params = {}) {
2453
2680
  }
2454
2681
  }
2455
2682
  });
2683
+ if (state.initialAnchor) {
2684
+ ensureInitialAnchor(ctx, state);
2685
+ }
2686
+ }
2687
+
2688
+ // src/core/checkActualChange.ts
2689
+ function checkActualChange(state, dataProp, previousData) {
2690
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
2691
+ return true;
2692
+ }
2693
+ const {
2694
+ idCache,
2695
+ props: { keyExtractor }
2696
+ } = state;
2697
+ for (let i = 0; i < dataProp.length; i++) {
2698
+ if (dataProp[i] !== previousData[i]) {
2699
+ return true;
2700
+ }
2701
+ if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
2702
+ return true;
2703
+ }
2704
+ }
2705
+ return false;
2456
2706
  }
2457
2707
 
2458
2708
  // src/core/doMaintainScrollAtEnd.ts
@@ -2485,40 +2735,6 @@ function doMaintainScrollAtEnd(ctx, state, animated) {
2485
2735
  }
2486
2736
  }
2487
2737
 
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
2738
  // src/utils/updateAveragesOnDataChange.ts
2523
2739
  function updateAveragesOnDataChange(state, oldData, newData) {
2524
2740
  var _a3;
@@ -2572,25 +2788,23 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2572
2788
  }
2573
2789
 
2574
2790
  // 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
- }
2791
+ function checkResetContainers(ctx, state, dataProp) {
2792
+ const { previousData } = state;
2793
+ if (previousData) {
2794
+ updateAveragesOnDataChange(state, previousData, dataProp);
2795
+ }
2796
+ const { maintainScrollAtEnd } = state.props;
2797
+ calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2798
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2799
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2800
+ if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2801
+ state.isEndReached = false;
2802
+ }
2803
+ if (!didMaintainScrollAtEnd) {
2804
+ checkAtTop(state);
2805
+ checkAtBottom(ctx, state);
2593
2806
  }
2807
+ delete state.previousData;
2594
2808
  }
2595
2809
 
2596
2810
  // src/core/doInitialAllocateContainers.ts
@@ -2631,7 +2845,7 @@ function doInitialAllocateContainers(ctx, state) {
2631
2845
  set$(ctx, "numContainers", numContainers);
2632
2846
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2633
2847
  if (state.lastLayout) {
2634
- if (state.props.initialScroll) {
2848
+ if (state.initialScroll) {
2635
2849
  requestAnimationFrame(() => {
2636
2850
  calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2637
2851
  });
@@ -2687,78 +2901,47 @@ function handleLayout(ctx, state, layout, setCanRender) {
2687
2901
  setCanRender(true);
2688
2902
  }
2689
2903
 
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
2904
  // src/core/ScrollAdjustHandler.ts
2748
2905
  var ScrollAdjustHandler = class {
2749
2906
  constructor(ctx) {
2750
2907
  this.appliedAdjust = 0;
2908
+ this.pendingAdjust = 0;
2751
2909
  this.mounted = false;
2752
2910
  this.context = ctx;
2911
+ {
2912
+ const commitPendingAdjust = () => {
2913
+ const state = this.context.internalState;
2914
+ const pending = this.pendingAdjust;
2915
+ if (pending !== 0) {
2916
+ this.pendingAdjust = 0;
2917
+ this.appliedAdjust += pending;
2918
+ state.scroll += pending;
2919
+ state.scrollForNextCalculateItemsInView = void 0;
2920
+ set$(this.context, "scrollAdjustPending", 0);
2921
+ set$(this.context, "scrollAdjust", this.appliedAdjust);
2922
+ calculateItemsInView(this.context, this.context.internalState);
2923
+ }
2924
+ };
2925
+ listen$(this.context, "scrollingTo", (value) => {
2926
+ if (value === void 0) {
2927
+ commitPendingAdjust();
2928
+ }
2929
+ });
2930
+ }
2753
2931
  }
2754
2932
  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();
2933
+ const scrollingTo = peek$(this.context, "scrollingTo");
2934
+ if ((scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2935
+ this.pendingAdjust += add;
2936
+ set$(this.context, "scrollAdjustPending", this.pendingAdjust);
2760
2937
  } else {
2761
- requestAnimationFrame(set);
2938
+ this.appliedAdjust += add;
2939
+ const setter = () => set$(this.context, "scrollAdjust", this.appliedAdjust);
2940
+ if (this.mounted) {
2941
+ setter();
2942
+ } else {
2943
+ requestAnimationFrame(setter);
2944
+ }
2762
2945
  }
2763
2946
  }
2764
2947
  setMounted() {
@@ -2806,8 +2989,8 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2806
2989
  let minIndexSizeChanged;
2807
2990
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2808
2991
  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;
2992
+ const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
2993
+ const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
2811
2994
  if (diff !== 0) {
2812
2995
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2813
2996
  const { startBuffered, endBuffered } = state;
@@ -2828,7 +3011,6 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2828
3011
  if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2829
3012
  shouldMaintainScrollAtEnd = true;
2830
3013
  }
2831
- addTotalSize(ctx, state, itemKey, diff);
2832
3014
  onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2833
3015
  index,
2834
3016
  itemData: state.props.data[index],
@@ -2868,7 +3050,7 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2868
3050
  }
2869
3051
  }
2870
3052
  }
2871
- function updateOneItemSize(state, itemKey, sizeObj) {
3053
+ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2872
3054
  var _a3;
2873
3055
  const {
2874
3056
  sizes,
@@ -2879,7 +3061,7 @@ function updateOneItemSize(state, itemKey, sizeObj) {
2879
3061
  } = state;
2880
3062
  if (!data) return 0;
2881
3063
  const index = indexByKey.get(itemKey);
2882
- const prevSize = getItemSize(state, itemKey, index, data[index]);
3064
+ const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
2883
3065
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
2884
3066
  const size = Math.round(rawSize) ;
2885
3067
  sizesKnown.set(itemKey, size);
@@ -2893,7 +3075,7 @@ function updateOneItemSize(state, itemKey, sizeObj) {
2893
3075
  averages.num++;
2894
3076
  }
2895
3077
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2896
- sizes.set(itemKey, size);
3078
+ setSize(ctx, state, itemKey, size);
2897
3079
  return size - prevSize;
2898
3080
  }
2899
3081
  return 0;
@@ -2938,6 +3120,91 @@ function createColumnWrapperStyle(contentContainerStyle) {
2938
3120
  };
2939
3121
  }
2940
3122
  }
3123
+
3124
+ // src/utils/createImperativeHandle.ts
3125
+ function createImperativeHandle(ctx, state) {
3126
+ const scrollIndexIntoView = (options) => {
3127
+ if (state) {
3128
+ const { index, ...rest } = options;
3129
+ const { startNoBuffer, endNoBuffer } = state;
3130
+ if (index < startNoBuffer || index > endNoBuffer) {
3131
+ const viewPosition = index < startNoBuffer ? 0 : 1;
3132
+ scrollToIndex(ctx, state, {
3133
+ ...rest,
3134
+ index,
3135
+ viewPosition
3136
+ });
3137
+ }
3138
+ }
3139
+ };
3140
+ const refScroller = state.refScroller;
3141
+ return {
3142
+ flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
3143
+ getNativeScrollRef: () => refScroller.current,
3144
+ getScrollableNode: () => refScroller.current.getScrollableNode(),
3145
+ getScrollResponder: () => refScroller.current.getScrollResponder(),
3146
+ getState: () => ({
3147
+ activeStickyIndex: state.activeStickyIndex,
3148
+ contentLength: state.totalSize,
3149
+ data: state.props.data,
3150
+ elementAtIndex: (index) => {
3151
+ var _a3;
3152
+ return (_a3 = ctx.viewRefs.get(findContainerId(ctx, getId(state, index)))) == null ? void 0 : _a3.current;
3153
+ },
3154
+ end: state.endNoBuffer,
3155
+ endBuffered: state.endBuffered,
3156
+ isAtEnd: state.isAtEnd,
3157
+ isAtStart: state.isAtStart,
3158
+ positionAtIndex: (index) => state.positions.get(getId(state, index)),
3159
+ positions: state.positions,
3160
+ scroll: state.scroll,
3161
+ scrollLength: state.scrollLength,
3162
+ sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
3163
+ sizes: state.sizesKnown,
3164
+ start: state.startNoBuffer,
3165
+ startBuffered: state.startBuffered
3166
+ }),
3167
+ scrollIndexIntoView,
3168
+ scrollItemIntoView: ({ item, ...props }) => {
3169
+ const data = state.props.data;
3170
+ const index = data.indexOf(item);
3171
+ if (index !== -1) {
3172
+ scrollIndexIntoView({ index, ...props });
3173
+ }
3174
+ },
3175
+ scrollToEnd: (options) => {
3176
+ const data = state.props.data;
3177
+ const stylePaddingBottom = state.props.stylePaddingBottom;
3178
+ const index = data.length - 1;
3179
+ if (index !== -1) {
3180
+ const paddingBottom = stylePaddingBottom || 0;
3181
+ const footerSize = peek$(ctx, "footerSize") || 0;
3182
+ scrollToIndex(ctx, state, {
3183
+ index,
3184
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3185
+ viewPosition: 1,
3186
+ ...options
3187
+ });
3188
+ }
3189
+ },
3190
+ scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3191
+ scrollToItem: ({ item, ...props }) => {
3192
+ const data = state.props.data;
3193
+ const index = data.indexOf(item);
3194
+ if (index !== -1) {
3195
+ scrollToIndex(ctx, state, { index, ...props });
3196
+ }
3197
+ },
3198
+ scrollToOffset: (params) => scrollTo(ctx, state, params),
3199
+ setScrollProcessingEnabled: (enabled) => {
3200
+ state.scrollProcessingEnabled = enabled;
3201
+ },
3202
+ setVisibleContentAnchorOffset: (value) => {
3203
+ const val = isFunction(value) ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
3204
+ set$(ctx, "scrollAdjustUserOffset", val);
3205
+ }
3206
+ };
3207
+ }
2941
3208
  function getRenderedItem(ctx, state, key) {
2942
3209
  var _a3;
2943
3210
  if (!state) {
@@ -3032,12 +3299,13 @@ var LegendList = typedMemo(
3032
3299
  })
3033
3300
  );
3034
3301
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3035
- var _a3;
3302
+ var _a3, _b;
3036
3303
  const {
3037
3304
  alignItemsAtEnd = false,
3038
3305
  columnWrapperStyle,
3039
3306
  contentContainerStyle: contentContainerStyleProp,
3040
3307
  data: dataProp = [],
3308
+ dataVersion,
3041
3309
  drawDistance = 250,
3042
3310
  enableAverages = true,
3043
3311
  estimatedItemSize: estimatedItemSizeProp,
@@ -3048,6 +3316,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3048
3316
  getItemType,
3049
3317
  horizontal,
3050
3318
  initialContainerPoolRatio = 2,
3319
+ initialScrollAtEnd = false,
3051
3320
  initialScrollIndex: initialScrollIndexProp,
3052
3321
  initialScrollOffset: initialScrollOffsetProp,
3053
3322
  itemsAreEqual,
@@ -3086,13 +3355,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3086
3355
  waitForInitialLayout = true,
3087
3356
  ...rest
3088
3357
  } = 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
3358
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3093
3359
  const style = { ...StyleSheet.flatten(styleProp) };
3094
3360
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3095
3361
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3362
+ const [renderNum, setRenderNum] = useState(0);
3363
+ 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;
3364
+ const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
3096
3365
  const ctx = useStateContext();
3097
3366
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
3098
3367
  const refScroller = useRef(null);
@@ -3111,6 +3380,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3111
3380
  containerItemKeys: /* @__PURE__ */ new Set(),
3112
3381
  containerItemTypes: /* @__PURE__ */ new Map(),
3113
3382
  dataChangeNeedsScrollUpdate: false,
3383
+ didColumnsChange: false,
3384
+ didDataChange: false,
3114
3385
  enableScrollForNextCalculateItemsInView: true,
3115
3386
  endBuffered: -1,
3116
3387
  endNoBuffer: -1,
@@ -3119,10 +3390,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3119
3390
  idCache: [],
3120
3391
  idsInView: [],
3121
3392
  indexByKey: /* @__PURE__ */ new Map(),
3122
- initialScroll,
3393
+ initialAnchor: (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
3394
+ attempts: 0,
3395
+ index: initialScrollProp.index,
3396
+ settledTicks: 0,
3397
+ viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3398
+ viewPosition: initialScrollProp.viewPosition
3399
+ } : void 0,
3400
+ initialScroll: initialScrollProp,
3123
3401
  isAtEnd: false,
3124
3402
  isAtStart: false,
3125
3403
  isEndReached: false,
3404
+ isFirst: true,
3126
3405
  isStartReached: false,
3127
3406
  lastBatchingAction: Date.now(),
3128
3407
  lastLayout: void 0,
@@ -3155,21 +3434,27 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3155
3434
  totalSize: 0,
3156
3435
  viewabilityConfigCallbackPairs: void 0
3157
3436
  };
3437
+ const internalState = ctx.internalState;
3438
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3158
3439
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3159
3440
  set$(ctx, "extraData", extraData);
3160
3441
  }
3161
3442
  refState.current = ctx.internalState;
3162
3443
  }
3163
3444
  const state = refState.current;
3164
- const isFirst = !state.props.renderItem;
3165
- const didDataChange = state.props.data !== dataProp;
3166
- if (didDataChange) {
3445
+ const isFirstLocal = state.isFirst;
3446
+ state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3447
+ const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3448
+ if (didDataChangeLocal) {
3167
3449
  state.dataChangeNeedsScrollUpdate = true;
3450
+ state.didDataChange = true;
3451
+ state.previousData = state.props.data;
3168
3452
  }
3169
3453
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3170
3454
  state.props = {
3171
3455
  alignItemsAtEnd,
3172
3456
  data: dataProp,
3457
+ dataVersion,
3173
3458
  enableAverages,
3174
3459
  estimatedItemSize,
3175
3460
  getEstimatedItemSize,
@@ -3177,7 +3462,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3177
3462
  getItemType,
3178
3463
  horizontal: !!horizontal,
3179
3464
  initialContainerPoolRatio,
3180
- initialScroll,
3181
3465
  itemsAreEqual,
3182
3466
  keyExtractor,
3183
3467
  maintainScrollAtEnd,
@@ -3209,7 +3493,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3209
3493
  { length: Math.min(numColumnsProp, dataProp.length) },
3210
3494
  (_, i) => getId(state, dataProp.length - 1 - i)
3211
3495
  );
3212
- }, [dataProp, numColumnsProp]);
3496
+ }, [dataProp, dataVersion, numColumnsProp]);
3213
3497
  const initializeStateVars = () => {
3214
3498
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3215
3499
  set$(ctx, "numColumns", numColumnsProp);
@@ -3224,7 +3508,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3224
3508
  requestAdjust(ctx, state, paddingDiff);
3225
3509
  }
3226
3510
  };
3227
- if (isFirst) {
3511
+ if (isFirstLocal) {
3228
3512
  initializeStateVars();
3229
3513
  updateItemPositions(
3230
3514
  ctx,
@@ -3234,38 +3518,53 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3234
3518
  );
3235
3519
  }
3236
3520
  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;
3521
+ var _a4, _b2;
3522
+ const { initialScroll } = refState.current;
3523
+ if (!initialScroll) {
3524
+ refState.current.initialAnchor = void 0;
3525
+ return 0;
3526
+ }
3527
+ if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3528
+ refState.current.initialAnchor = {
3529
+ attempts: 0,
3530
+ index: initialScroll.index,
3531
+ settledTicks: 0,
3532
+ viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3533
+ viewPosition: initialScroll.viewPosition
3534
+ };
3254
3535
  }
3255
- return 0;
3256
- }, [renderNum]);
3257
- if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
3536
+ if (initialScroll.contentOffset !== void 0) {
3537
+ return initialScroll.contentOffset;
3538
+ }
3539
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3540
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3541
+ let clampedOffset = resolvedOffset;
3542
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3543
+ const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3544
+ clampedOffset = Math.min(clampedOffset, maxOffset);
3545
+ }
3546
+ clampedOffset = Math.max(0, clampedOffset);
3547
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3548
+ refState.current.initialScroll = updatedInitialScroll;
3549
+ state.initialScroll = updatedInitialScroll;
3550
+ refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3551
+ return clampedOffset;
3552
+ }, [renderNum, state.initialScroll]);
3553
+ if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3258
3554
  refState.current.lastBatchingAction = Date.now();
3259
- if (!keyExtractorProp && !isFirst && didDataChange) {
3555
+ if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
3260
3556
  IS_DEV && warnDevOnce(
3261
3557
  "keyExtractor",
3262
3558
  "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
3559
  );
3264
3560
  refState.current.sizes.clear();
3265
3561
  refState.current.positions.clear();
3562
+ refState.current.totalSize = 0;
3563
+ set$(ctx, "totalSize", 0);
3266
3564
  }
3267
3565
  }
3268
3566
  const onLayoutHeader = useCallback((rect, fromLayoutEffect) => {
3567
+ const { initialScroll } = refState.current;
3269
3568
  const size = rect[horizontal ? "width" : "height"];
3270
3569
  set$(ctx, "headerSize", size);
3271
3570
  if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
@@ -3276,31 +3575,58 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3276
3575
  }
3277
3576
  }
3278
3577
  }, []);
3578
+ const doInitialScroll = useCallback(() => {
3579
+ var _a4;
3580
+ const initialScroll = state.initialScroll;
3581
+ if (initialScroll) {
3582
+ scrollTo(ctx, state, {
3583
+ animated: false,
3584
+ index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3585
+ isInitialScroll: true,
3586
+ offset: initialContentOffset,
3587
+ precomputedWithViewOffset: true
3588
+ });
3589
+ }
3590
+ }, [initialContentOffset, state.initialScroll]);
3591
+ const onLayoutChange = useCallback((layout) => {
3592
+ doInitialScroll();
3593
+ handleLayout(ctx, state, layout, setCanRender);
3594
+ }, []);
3595
+ const { onLayout } = useOnLayoutSync({
3596
+ onLayoutChange,
3597
+ onLayoutProp,
3598
+ ref: refScroller
3599
+ // the type of ScrollView doesn't include measure?
3600
+ });
3279
3601
  useLayoutEffect(() => {
3280
3602
  if (snapToIndices) {
3281
3603
  updateSnapToOffsets(ctx, state);
3282
3604
  }
3283
3605
  }, [snapToIndices]);
3284
3606
  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]);
3607
+ const {
3608
+ didColumnsChange,
3609
+ didDataChange,
3610
+ isFirst,
3611
+ props: { data }
3612
+ } = state;
3613
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3614
+ if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3615
+ checkResetContainers(ctx, state, data);
3616
+ }
3617
+ state.didColumnsChange = false;
3618
+ state.didDataChange = false;
3619
+ state.isFirst = false;
3620
+ }, [dataProp, dataVersion, numColumnsProp]);
3296
3621
  useLayoutEffect(() => {
3297
3622
  set$(ctx, "extraData", extraData);
3298
3623
  }, [extraData]);
3299
3624
  useLayoutEffect(initializeStateVars, [
3625
+ dataVersion,
3300
3626
  memoizedLastItemKeys.join(","),
3301
3627
  numColumnsProp,
3302
- stylePaddingTopState,
3303
- stylePaddingBottomState
3628
+ stylePaddingBottomState,
3629
+ stylePaddingTopState
3304
3630
  ]);
3305
3631
  useEffect(() => {
3306
3632
  const viewability = setupViewability({
@@ -3311,103 +3637,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3311
3637
  state.viewabilityConfigCallbackPairs = viewability;
3312
3638
  state.enableScrollForNextCalculateItemsInView = !viewability;
3313
3639
  }, [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
- }, []);
3640
+ useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
3405
3641
  {
3406
- useEffect(() => {
3407
- if (initialContentOffset) {
3408
- scrollTo(state, { animated: false, offset: initialContentOffset, ...initialScroll || {} });
3409
- }
3410
- }, []);
3642
+ useEffect(doInitialScroll, []);
3411
3643
  }
3412
3644
  const fns = useMemo(
3413
3645
  () => ({
@@ -3436,7 +3668,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3436
3668
  onMomentumScrollEnd: (event) => {
3437
3669
  {
3438
3670
  requestAnimationFrame(() => {
3439
- finishScrollTo(refState.current);
3671
+ finishScrollTo(ctx, refState.current);
3440
3672
  });
3441
3673
  }
3442
3674
  if (onMomentumScrollEnd) {
@@ -3456,7 +3688,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3456
3688
  }
3457
3689
  ),
3458
3690
  refScrollView: combinedRef,
3459
- scrollAdjustHandler: (_a3 = refState.current) == null ? void 0 : _a3.scrollAdjustHandler,
3691
+ scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3460
3692
  scrollEventThrottle: 16 ,
3461
3693
  snapToIndices,
3462
3694
  stickyIndices,