@legendapp/list 3.0.0-beta.44 → 3.0.0-beta.46

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.native.js CHANGED
@@ -29,37 +29,6 @@ var ReactNative__namespace = /*#__PURE__*/_interopNamespace(ReactNative);
29
29
  ReactNative.Animated.View;
30
30
  var View = ReactNative.View;
31
31
  var Text = ReactNative.Text;
32
-
33
- // src/state/getContentInsetEnd.ts
34
- function getContentInsetEnd(state) {
35
- var _a3;
36
- const { props } = state;
37
- const horizontal = props.horizontal;
38
- const contentInset = props.contentInset;
39
- const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
40
- const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
41
- if (overrideInset) {
42
- const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
43
- return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
44
- }
45
- if (baseInset) {
46
- return (horizontal ? baseInset.right : baseInset.bottom) || 0;
47
- }
48
- return 0;
49
- }
50
-
51
- // src/state/getContentSize.ts
52
- function getContentSize(ctx) {
53
- var _a3;
54
- const { values, state } = ctx;
55
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
56
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
57
- const headerSize = values.get("headerSize") || 0;
58
- const footerSize = values.get("footerSize") || 0;
59
- const contentInsetBottom = getContentInsetEnd(state);
60
- const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
61
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
62
- }
63
32
  var createAnimatedValue = (value) => new ReactNative.Animated.Value(value);
64
33
 
65
34
  // src/state/state.tsx
@@ -83,6 +52,11 @@ function StateProvider({ children }) {
83
52
  ["headerSize", 0],
84
53
  ["numContainers", 0],
85
54
  ["activeStickyIndex", -1],
55
+ ["isAtEnd", false],
56
+ ["isAtStart", false],
57
+ ["isNearEnd", false],
58
+ ["isNearStart", false],
59
+ ["isWithinMaintainScrollAtEndThreshold", false],
86
60
  ["totalSize", 0],
87
61
  ["scrollAdjustPending", 0]
88
62
  ]),
@@ -174,29 +148,71 @@ function notifyPosition$(ctx, key, value) {
174
148
  function useArr$(signalNames) {
175
149
  const ctx = React2__namespace.useContext(ContextState);
176
150
  const { subscribe, get } = React2__namespace.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
177
- const value = shim.useSyncExternalStore(subscribe, get);
151
+ const value = shim.useSyncExternalStore(subscribe, get, get);
178
152
  return value;
179
153
  }
180
154
  function useSelector$(signalName, selector) {
181
155
  const ctx = React2__namespace.useContext(ContextState);
182
156
  const { subscribe, get } = React2__namespace.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
183
- const value = shim.useSyncExternalStore(subscribe, () => selector(get()[0]));
157
+ const getSelectedValue = React2__namespace.useCallback(() => selector(get()[0]), [get, selector]);
158
+ const value = shim.useSyncExternalStore(subscribe, getSelectedValue, getSelectedValue);
184
159
  return value;
185
160
  }
186
161
 
162
+ // src/state/getContentInsetEnd.ts
163
+ function getContentInsetEnd(ctx) {
164
+ var _a3, _b;
165
+ const state = ctx.state;
166
+ const { props } = state;
167
+ const horizontal = props.horizontal;
168
+ const contentInset = props.contentInset;
169
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
170
+ const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
171
+ const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
172
+ const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
173
+ const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
174
+ if (overrideInset) {
175
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
176
+ return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
177
+ }
178
+ return Math.max(baseEndInset, anchoredEndInset);
179
+ }
180
+
181
+ // src/state/getContentSize.ts
182
+ function getContentSize(ctx) {
183
+ var _a3;
184
+ const { values, state } = ctx;
185
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
186
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
187
+ const headerSize = values.get("headerSize") || 0;
188
+ const footerSize = values.get("footerSize") || 0;
189
+ const contentInsetBottom = getContentInsetEnd(ctx);
190
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
191
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
192
+ }
193
+
187
194
  // src/components/DebugView.tsx
188
195
  var DebugRow = ({ children }) => {
189
196
  return /* @__PURE__ */ React2__namespace.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
190
197
  };
191
- React2__namespace.memo(function DebugView2({ state }) {
198
+ React2__namespace.memo(function DebugView2() {
192
199
  const ctx = useStateContext();
193
- const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
200
+ const [
201
+ totalSize = 0,
202
+ scrollAdjust = 0,
203
+ rawScroll = 0,
204
+ scroll = 0,
205
+ _numContainers = 0,
206
+ _numContainersPooled = 0,
207
+ isAtEnd = false
208
+ ] = useArr$([
194
209
  "totalSize",
195
210
  "scrollAdjust",
196
211
  "debugRawScroll",
197
212
  "debugComputedScroll",
198
213
  "numContainers",
199
- "numContainersPooled"
214
+ "numContainersPooled",
215
+ "isAtEnd"
200
216
  ]);
201
217
  const contentSize = getContentSize(ctx);
202
218
  const [, forceUpdate] = React2.useReducer((x) => x + 1, 0);
@@ -221,7 +237,7 @@ React2__namespace.memo(function DebugView2({ state }) {
221
237
  },
222
238
  /* @__PURE__ */ React2__namespace.createElement(DebugRow, null, /* @__PURE__ */ React2__namespace.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React2__namespace.createElement(Text, null, totalSize.toFixed(2))),
223
239
  /* @__PURE__ */ React2__namespace.createElement(DebugRow, null, /* @__PURE__ */ React2__namespace.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React2__namespace.createElement(Text, null, contentSize.toFixed(2))),
224
- /* @__PURE__ */ React2__namespace.createElement(DebugRow, null, /* @__PURE__ */ React2__namespace.createElement(Text, null, "At end:"), /* @__PURE__ */ React2__namespace.createElement(Text, null, String(state.isAtEnd))),
240
+ /* @__PURE__ */ React2__namespace.createElement(DebugRow, null, /* @__PURE__ */ React2__namespace.createElement(Text, null, "At end:"), /* @__PURE__ */ React2__namespace.createElement(Text, null, String(isAtEnd))),
225
241
  /* @__PURE__ */ React2__namespace.createElement(DebugRow, null, /* @__PURE__ */ React2__namespace.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React2__namespace.createElement(Text, null, scrollAdjust.toFixed(2))),
226
242
  /* @__PURE__ */ React2__namespace.createElement(DebugRow, null, /* @__PURE__ */ React2__namespace.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React2__namespace.createElement(Text, null, rawScroll.toFixed(2))),
227
243
  /* @__PURE__ */ React2__namespace.createElement(DebugRow, null, /* @__PURE__ */ React2__namespace.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React2__namespace.createElement(Text, null, scroll.toFixed(2)))
@@ -234,6 +250,30 @@ function useInterval(callback, delay) {
234
250
  }, [delay]);
235
251
  }
236
252
 
253
+ // src/components/stickyPositionUtils.ts
254
+ function getStickyPushLimit(state, index, itemKey) {
255
+ if (!itemKey) {
256
+ return void 0;
257
+ }
258
+ const currentSize = state.sizes.get(itemKey);
259
+ if (!(currentSize && currentSize > 0)) {
260
+ return void 0;
261
+ }
262
+ const stickyIndexInArray = state.props.stickyIndicesArr.indexOf(index);
263
+ if (stickyIndexInArray === -1) {
264
+ return void 0;
265
+ }
266
+ const nextStickyIndex = state.props.stickyIndicesArr[stickyIndexInArray + 1];
267
+ if (nextStickyIndex === void 0) {
268
+ return void 0;
269
+ }
270
+ const nextStickyPosition = state.positions[nextStickyIndex];
271
+ if (nextStickyPosition === void 0) {
272
+ return void 0;
273
+ }
274
+ return nextStickyPosition - currentSize;
275
+ }
276
+
237
277
  // src/utils/devEnvironment.ts
238
278
  var metroDev = typeof __DEV__ !== "undefined" ? __DEV__ : void 0;
239
279
  var _a;
@@ -244,6 +284,7 @@ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 :
244
284
 
245
285
  // src/constants.ts
246
286
  var POSITION_OUT_OF_VIEW = -1e7;
287
+ var EDGE_POSITION_EPSILON = 1;
247
288
  var ENABLE_DEVMODE = IS_DEV && false;
248
289
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
249
290
 
@@ -255,93 +296,26 @@ var useAnimatedValue = (initialValue) => {
255
296
  return animAnimatedValue;
256
297
  };
257
298
 
258
- // src/utils/helpers.ts
259
- function isFunction(obj) {
260
- return typeof obj === "function";
261
- }
262
- function isArray(obj) {
263
- return Array.isArray(obj);
264
- }
265
- var warned = /* @__PURE__ */ new Set();
266
- function warnDevOnce(id, text) {
267
- if (IS_DEV && !warned.has(id)) {
268
- warned.add(id);
269
- console.warn(`[legend-list] ${text}`);
270
- }
271
- }
272
- function roundSize(size) {
273
- return Math.floor(size * 8) / 8;
274
- }
275
- function isNullOrUndefined(value) {
276
- return value === null || value === void 0;
277
- }
278
- function comparatorDefault(a, b) {
279
- return a - b;
280
- }
281
- function getPadding(s, type) {
282
- var _a3, _b, _c;
283
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
284
- }
285
- function extractPadding(style, contentContainerStyle, type) {
286
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
287
- }
288
- function findContainerId(ctx, key) {
289
- var _a3, _b;
290
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
291
- if (directMatch !== void 0) {
292
- return directMatch;
293
- }
294
- const numContainers = peek$(ctx, "numContainers");
295
- for (let i = 0; i < numContainers; i++) {
296
- const itemKey = peek$(ctx, `containerItemKey${i}`);
297
- if (itemKey === key) {
298
- return i;
299
- }
300
- }
301
- return -1;
302
- }
303
-
304
299
  // src/hooks/useValue$.ts
305
300
  function useValue$(key, params) {
306
- const { getValue, delay } = params || {};
301
+ const { getValue } = params || {};
307
302
  const ctx = useStateContext();
308
303
  const getNewValue = () => {
309
304
  var _a3;
310
305
  return (_a3 = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a3 : 0;
311
306
  };
312
307
  const animValue = useAnimatedValue(getNewValue());
313
- React2.useMemo(() => {
314
- let prevValue;
315
- let didQueueTask = false;
316
- listen$(ctx, key, () => {
317
- const newValue = getNewValue();
318
- if (delay !== void 0) {
319
- const fn = () => {
320
- didQueueTask = false;
321
- const latestValue = getNewValue();
322
- if (latestValue !== void 0) {
323
- animValue.setValue(latestValue);
324
- }
325
- };
326
- const delayValue = isFunction(delay) ? delay(newValue, prevValue) : delay;
327
- prevValue = newValue;
328
- if (!didQueueTask) {
329
- didQueueTask = true;
330
- if (delayValue === void 0) {
331
- fn();
332
- } else if (delayValue === 0) {
333
- queueMicrotask(fn);
334
- } else {
335
- setTimeout(fn, delayValue);
336
- }
337
- }
338
- } else {
339
- animValue.setValue(newValue);
340
- }
341
- });
342
- }, []);
308
+ React2.useLayoutEffect(() => {
309
+ const syncCurrentValue = () => {
310
+ animValue.setValue(getNewValue());
311
+ };
312
+ const unsubscribe = listen$(ctx, key, syncCurrentValue);
313
+ syncCurrentValue();
314
+ return unsubscribe;
315
+ }, [animValue, ctx, key]);
343
316
  return animValue;
344
317
  }
318
+ var typedForwardRef = React2__namespace.forwardRef;
345
319
  var typedMemo = React2__namespace.memo;
346
320
  var getComponent = (Component) => {
347
321
  if (React2__namespace.isValidElement(Component)) {
@@ -361,18 +335,8 @@ var PositionViewState = typedMemo(function PositionViewState2({
361
335
  refView,
362
336
  ...rest
363
337
  }) {
364
- const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
365
- return /* @__PURE__ */ React2__namespace.createElement(
366
- ReactNative.View,
367
- {
368
- ref: refView,
369
- style: [
370
- style,
371
- horizontal ? { transform: [{ translateX: position }] } : { transform: [{ translateY: position }] }
372
- ],
373
- ...rest
374
- }
375
- );
338
+ const [position = POSITION_OUT_OF_VIEW, _itemKey] = useArr$([`containerPosition${id}`, `containerItemKey${id}`]);
339
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ref: refView, style: [style, horizontal ? { left: position } : { top: position }], ...rest });
376
340
  });
377
341
  var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
378
342
  id,
@@ -384,12 +348,7 @@ var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
384
348
  const position$ = useValue$(`containerPosition${id}`, {
385
349
  getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
386
350
  });
387
- let position;
388
- if (ReactNative.Platform.OS === "ios" || ReactNative.Platform.OS === "android") {
389
- position = horizontal ? { transform: [{ translateX: position$ }] } : { transform: [{ translateY: position$ }] };
390
- } else {
391
- position = horizontal ? { left: position$ } : { top: position$ };
392
- }
351
+ const position = horizontal ? { left: position$ } : { top: position$ };
393
352
  return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { ref: refView, style: [style, position], ...rest });
394
353
  });
395
354
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
@@ -403,26 +362,50 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
403
362
  children,
404
363
  ...rest
405
364
  }) {
406
- const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0] = useArr$([
365
+ const ctx = useStateContext();
366
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0, itemKey, _totalSize = 0] = useArr$([
407
367
  `containerPosition${id}`,
408
368
  "headerSize",
409
- "stylePaddingTop"
369
+ "stylePaddingTop",
370
+ `containerItemKey${id}`,
371
+ "totalSize"
410
372
  ]);
411
- const transform = React2__namespace.useMemo(() => {
373
+ const pushLimit = React2__namespace.useMemo(
374
+ () => getStickyPushLimit(ctx.state, index, itemKey),
375
+ [ctx.state, index, itemKey, _totalSize]
376
+ );
377
+ const stickyPosition = React2__namespace.useMemo(() => {
412
378
  var _a3;
413
379
  if (animatedScrollY) {
414
380
  const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
415
381
  const stickyStart = position + headerSize + stylePaddingTop - stickyConfigOffset;
416
- const stickyPosition = animatedScrollY.interpolate({
417
- extrapolateLeft: "clamp",
418
- extrapolateRight: "extend",
419
- inputRange: [stickyStart, stickyStart + 5e3],
420
- outputRange: [position, position + 5e3]
421
- });
422
- return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
382
+ let nextStickyPosition;
383
+ if (pushLimit !== void 0) {
384
+ if (pushLimit <= position) {
385
+ nextStickyPosition = pushLimit;
386
+ } else {
387
+ nextStickyPosition = animatedScrollY.interpolate({
388
+ extrapolateLeft: "clamp",
389
+ extrapolateRight: "clamp",
390
+ inputRange: [stickyStart, stickyStart + (pushLimit - position)],
391
+ outputRange: [position, pushLimit]
392
+ });
393
+ }
394
+ } else {
395
+ nextStickyPosition = animatedScrollY.interpolate({
396
+ extrapolateLeft: "clamp",
397
+ extrapolateRight: "extend",
398
+ inputRange: [stickyStart, stickyStart + 5e3],
399
+ outputRange: [position, position + 5e3]
400
+ });
401
+ }
402
+ return nextStickyPosition;
423
403
  }
424
- }, [animatedScrollY, headerSize, horizontal, position, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
425
- const viewStyle = React2__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
404
+ }, [animatedScrollY, headerSize, position, pushLimit, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
405
+ const viewStyle = React2__namespace.useMemo(
406
+ () => [style, { zIndex: index + 1e3 }, horizontal ? { left: stickyPosition } : { top: stickyPosition }],
407
+ [horizontal, index, stickyPosition, style]
408
+ );
426
409
  const renderStickyHeaderBackdrop = React2__namespace.useMemo(() => {
427
410
  if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
428
411
  return null;
@@ -446,6 +429,52 @@ function useInit(cb) {
446
429
  React2.useState(() => cb());
447
430
  }
448
431
 
432
+ // src/utils/helpers.ts
433
+ function isFunction(obj) {
434
+ return typeof obj === "function";
435
+ }
436
+ function isArray(obj) {
437
+ return Array.isArray(obj);
438
+ }
439
+ var warned = /* @__PURE__ */ new Set();
440
+ function warnDevOnce(id, text) {
441
+ if (IS_DEV && !warned.has(id)) {
442
+ warned.add(id);
443
+ console.warn(`[legend-list] ${text}`);
444
+ }
445
+ }
446
+ function roundSize(size) {
447
+ return Math.floor(size * 8) / 8;
448
+ }
449
+ function isNullOrUndefined(value) {
450
+ return value === null || value === void 0;
451
+ }
452
+ function comparatorDefault(a, b) {
453
+ return a - b;
454
+ }
455
+ function getPadding(s, type) {
456
+ var _a3, _b, _c;
457
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
458
+ }
459
+ function extractPadding(style, contentContainerStyle, type) {
460
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
461
+ }
462
+ function findContainerId(ctx, key) {
463
+ var _a3, _b;
464
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
465
+ if (directMatch !== void 0) {
466
+ return directMatch;
467
+ }
468
+ const numContainers = peek$(ctx, "numContainers");
469
+ for (let i = 0; i < numContainers; i++) {
470
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
471
+ if (itemKey === key) {
472
+ return i;
473
+ }
474
+ }
475
+ return -1;
476
+ }
477
+
449
478
  // src/state/ContextContainer.ts
450
479
  var ContextContainer = React2.createContext(null);
451
480
  function useContextContainer() {
@@ -613,11 +642,11 @@ function useOnLayoutSync({
613
642
  const { layout } = event.nativeEvent;
614
643
  if (layout.height !== ((_a3 = lastLayoutRef.current) == null ? void 0 : _a3.height) || layout.width !== ((_b = lastLayoutRef.current) == null ? void 0 : _b.width)) {
615
644
  onLayoutChange(layout, false);
616
- onLayoutProp == null ? void 0 : onLayoutProp(event);
617
645
  lastLayoutRef.current = layout;
618
646
  }
647
+ onLayoutProp == null ? void 0 : onLayoutProp(event);
619
648
  },
620
- [onLayoutChange]
649
+ [onLayoutChange, onLayoutProp]
621
650
  );
622
651
  if (IsNewArchitecture) {
623
652
  React2.useLayoutEffect(() => {
@@ -632,10 +661,8 @@ function useOnLayoutSync({
632
661
  }
633
662
  return { onLayout };
634
663
  }
635
- var Platform2 = ReactNative.Platform;
636
- var PlatformAdjustBreaksScroll = Platform2.OS === "android";
637
- var typedForwardRef = React2__namespace.forwardRef;
638
- var typedMemo2 = React2__namespace.memo;
664
+ var Platform = ReactNative.Platform;
665
+ var PlatformAdjustBreaksScroll = Platform.OS === "android";
639
666
 
640
667
  // src/utils/isInMVCPActiveMode.native.ts
641
668
  function isInMVCPActiveMode(state) {
@@ -643,7 +670,7 @@ function isInMVCPActiveMode(state) {
643
670
  }
644
671
 
645
672
  // src/components/Container.tsx
646
- var Container = typedMemo2(function Container2({
673
+ var Container = typedMemo(function Container2({
647
674
  id,
648
675
  recycleItems,
649
676
  horizontal,
@@ -687,17 +714,20 @@ var Container = typedMemo2(function Container2({
687
714
  const { columnGap, rowGap, gap } = columnWrapperStyle;
688
715
  if (horizontal) {
689
716
  paddingStyles = {
717
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
690
718
  paddingRight: columnGap || gap || void 0,
691
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
719
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
692
720
  };
693
721
  } else {
694
722
  paddingStyles = {
695
723
  paddingBottom: rowGap || gap || void 0,
696
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
724
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
725
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
697
726
  };
698
727
  }
699
728
  }
700
729
  return horizontal ? {
730
+ boxSizing: paddingStyles ? "border-box" : void 0,
701
731
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
702
732
  height: otherAxisSize,
703
733
  left: 0,
@@ -705,6 +735,7 @@ var Container = typedMemo2(function Container2({
705
735
  top: otherAxisPos,
706
736
  ...paddingStyles || {}
707
737
  } : {
738
+ boxSizing: paddingStyles ? "border-box" : void 0,
708
739
  left: otherAxisPos,
709
740
  position: "absolute",
710
741
  right: numColumns > 1 ? null : 0,
@@ -752,7 +783,7 @@ var Container = typedMemo2(function Container2({
752
783
  updateItemSizeFn(currentItemKey, layout);
753
784
  itemLayoutRef.current.didLayout = true;
754
785
  };
755
- const shouldDeferWebShrinkLayoutUpdate = Platform2.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
786
+ const shouldDeferWebShrinkLayoutUpdate = Platform.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
756
787
  if (shouldDeferWebShrinkLayoutUpdate) {
757
788
  const token = pendingShrinkToken + 1;
758
789
  itemLayoutRef.current.pendingShrinkToken = token;
@@ -838,16 +869,9 @@ var Containers = typedMemo(function Containers2({
838
869
  const ctx = useStateContext();
839
870
  const columnWrapperStyle = ctx.columnWrapperStyle;
840
871
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
841
- const animSize = useValue$("totalSize", {
842
- // Use a microtask if increasing the size significantly, otherwise use a timeout
843
- // If this is the initial scroll, we don't want to delay because we want to update the size immediately
844
- delay: (value, prevValue) => {
845
- var _a3;
846
- return !((_a3 = ctx.state) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
847
- }
848
- });
872
+ const animSize = useValue$("totalSize");
873
+ const otherAxisSize = useValue$("otherAxisSize");
849
874
  const animOpacity = useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 });
850
- const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
851
875
  const containers = [];
852
876
  for (let i = 0; i < numContainers; i++) {
853
877
  containers.push(
@@ -891,17 +915,20 @@ var Containers = typedMemo(function Containers2({
891
915
  });
892
916
  var ListComponentScrollView = ReactNative.Animated.ScrollView;
893
917
  function ScrollAdjust() {
918
+ var _a3;
919
+ const ctx = useStateContext();
894
920
  const bias = 1e7;
895
921
  const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
896
922
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0) + bias;
923
+ const horizontal = !!((_a3 = ctx.state) == null ? void 0 : _a3.props.horizontal);
897
924
  return /* @__PURE__ */ React2__namespace.createElement(
898
925
  ReactNative.View,
899
926
  {
900
927
  style: {
901
928
  height: 0,
902
- left: 0,
929
+ left: horizontal ? scrollOffset : 0,
903
930
  position: "absolute",
904
- top: scrollOffset,
931
+ top: horizontal ? 0 : scrollOffset,
905
932
  width: 0
906
933
  }
907
934
  }
@@ -911,14 +938,25 @@ function SnapWrapper({ ScrollComponent, ...props }) {
911
938
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
912
939
  return /* @__PURE__ */ React2__namespace.createElement(ScrollComponent, { ...props, snapToOffsets });
913
940
  }
941
+ function WebAnchoredEndSpace({ horizontal }) {
942
+ const ctx = useStateContext();
943
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
944
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
945
+ if (!shouldRenderAnchoredEndSpace) {
946
+ return null;
947
+ }
948
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
949
+ return /* @__PURE__ */ React2__namespace.createElement("div", { style }, null);
950
+ }
914
951
  var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
915
- const ref = refView != null ? refView : React2.useRef(null);
952
+ const localRef = React2.useRef(null);
953
+ const ref = refView != null ? refView : localRef;
916
954
  const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
917
955
  return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
918
956
  };
919
957
 
920
958
  // src/components/ListComponent.tsx
921
- var ListComponent = typedMemo2(function ListComponent2({
959
+ var ListComponent = typedMemo(function ListComponent2({
922
960
  canRender,
923
961
  style,
924
962
  contentContainerStyle,
@@ -948,12 +986,14 @@ var ListComponent = typedMemo2(function ListComponent2({
948
986
  }) {
949
987
  const ctx = useStateContext();
950
988
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
951
- const ScrollComponent = renderScrollComponent ? React2.useMemo(
952
- () => React2__namespace.forwardRef(
989
+ const ScrollComponent = React2.useMemo(() => {
990
+ if (!renderScrollComponent) {
991
+ return ListComponentScrollView;
992
+ }
993
+ return React2__namespace.forwardRef(
953
994
  (props, ref) => renderScrollComponent({ ...props, ref })
954
- ),
955
- [renderScrollComponent]
956
- ) : ListComponentScrollView;
995
+ );
996
+ }, [renderScrollComponent]);
957
997
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
958
998
  React2.useLayoutEffect(() => {
959
999
  if (!ListHeaderComponent) {
@@ -1013,183 +1053,90 @@ var ListComponent = typedMemo2(function ListComponent2({
1013
1053
  }
1014
1054
  ),
1015
1055
  ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1056
+ Platform.OS === "web" && /* @__PURE__ */ React2__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
1016
1057
  IS_DEV && ENABLE_DEVMODE
1017
1058
  );
1018
1059
  });
1019
-
1020
- // src/core/calculateOffsetForIndex.ts
1021
- function calculateOffsetForIndex(ctx, index) {
1022
- const state = ctx.state;
1023
- return index !== void 0 ? state.positions[index] || 0 : 0;
1060
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1061
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1062
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1063
+ function useDevChecksImpl(props) {
1064
+ const ctx = useStateContext();
1065
+ const { childrenMode, keyExtractor, renderScrollComponent, stickyHeaderIndices, stickyIndices, useWindowScroll } = props;
1066
+ React2.useEffect(() => {
1067
+ if (stickyIndices && !stickyHeaderIndices) {
1068
+ warnDevOnce(
1069
+ "stickyIndices",
1070
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
1071
+ );
1072
+ }
1073
+ }, [stickyHeaderIndices, stickyIndices]);
1074
+ React2.useEffect(() => {
1075
+ if (useWindowScroll && renderScrollComponent) {
1076
+ warnDevOnce(
1077
+ "useWindowScrollRenderScrollComponent",
1078
+ "useWindowScroll is not supported when renderScrollComponent is provided."
1079
+ );
1080
+ }
1081
+ }, [renderScrollComponent, useWindowScroll]);
1082
+ React2.useEffect(() => {
1083
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1084
+ warnDevOnce(
1085
+ "keyExtractor",
1086
+ "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."
1087
+ );
1088
+ }
1089
+ }, [childrenMode, ctx, keyExtractor]);
1090
+ React2.useEffect(() => {
1091
+ const state = ctx.state;
1092
+ const dataLength = state.props.data.length;
1093
+ const useWindowScrollResolved = state.props.useWindowScroll;
1094
+ if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1095
+ return;
1096
+ }
1097
+ const warnIfUnboundedOuterSize = () => {
1098
+ const readyToRender = peek$(ctx, "readyToRender");
1099
+ const numContainers = peek$(ctx, "numContainers") || 0;
1100
+ const totalSize = peek$(ctx, "totalSize") || 0;
1101
+ const scrollLength = ctx.state.scrollLength || 0;
1102
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1103
+ return;
1104
+ }
1105
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1106
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1107
+ if (rendersAlmostEverything && viewportMatchesContent) {
1108
+ warnDevOnce(
1109
+ "webUnboundedOuterSize",
1110
+ "LegendList appears to have an unbounded outer height on web, so virtualization is effectively disabled. Set a bounded height or flex: 1 on the list container, or use useWindowScroll."
1111
+ );
1112
+ }
1113
+ };
1114
+ warnIfUnboundedOuterSize();
1115
+ const unsubscribe = [
1116
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1117
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1118
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1119
+ ];
1120
+ return () => {
1121
+ for (const unsub of unsubscribe) {
1122
+ unsub();
1123
+ }
1124
+ };
1125
+ }, [ctx]);
1024
1126
  }
1025
-
1026
- // src/core/getTopOffsetAdjustment.ts
1027
- function getTopOffsetAdjustment(ctx) {
1028
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1127
+ function useDevChecksNoop(_props) {
1029
1128
  }
1129
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1030
1130
 
1031
- // src/utils/getId.ts
1032
- function getId(state, index) {
1033
- const { data, keyExtractor } = state.props;
1034
- if (!data) {
1035
- return "";
1036
- }
1037
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1038
- const id = ret;
1039
- state.idCache[index] = id;
1040
- return id;
1041
- }
1042
-
1043
- // src/core/addTotalSize.ts
1044
- function addTotalSize(ctx, key, add) {
1045
- const state = ctx.state;
1046
- const prevTotalSize = state.totalSize;
1047
- let totalSize = state.totalSize;
1048
- if (key === null) {
1049
- totalSize = add;
1050
- if (state.timeoutSetPaddingTop) {
1051
- clearTimeout(state.timeoutSetPaddingTop);
1052
- state.timeoutSetPaddingTop = void 0;
1053
- }
1054
- } else {
1055
- totalSize += add;
1056
- }
1057
- if (prevTotalSize !== totalSize) {
1058
- if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1059
- state.pendingTotalSize = totalSize;
1060
- } else {
1061
- state.pendingTotalSize = void 0;
1062
- state.totalSize = totalSize;
1063
- set$(ctx, "totalSize", totalSize);
1064
- }
1065
- }
1066
- }
1067
-
1068
- // src/core/setSize.ts
1069
- function setSize(ctx, itemKey, size) {
1070
- const state = ctx.state;
1071
- const { sizes } = state;
1072
- const previousSize = sizes.get(itemKey);
1073
- const diff = previousSize !== void 0 ? size - previousSize : size;
1074
- if (diff !== 0) {
1075
- addTotalSize(ctx, itemKey, diff);
1076
- }
1077
- sizes.set(itemKey, size);
1078
- }
1079
-
1080
- // src/utils/getItemSize.ts
1081
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1082
- var _a3, _b;
1083
- const state = ctx.state;
1084
- const {
1085
- sizesKnown,
1086
- sizes,
1087
- averageSizes,
1088
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1089
- scrollingTo
1090
- } = state;
1091
- const sizeKnown = sizesKnown.get(key);
1092
- if (sizeKnown !== void 0) {
1093
- return sizeKnown;
1094
- }
1095
- let size;
1096
- if (preferCachedSize) {
1097
- const cachedSize = sizes.get(key);
1098
- if (cachedSize !== void 0) {
1099
- return cachedSize;
1100
- }
1101
- }
1102
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1103
- if (getFixedItemSize) {
1104
- size = getFixedItemSize(data, index, itemType);
1105
- if (size !== void 0) {
1106
- sizesKnown.set(key, size);
1107
- }
1108
- }
1109
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1110
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1111
- if (averageSizeForType !== void 0) {
1112
- size = roundSize(averageSizeForType);
1113
- }
1114
- }
1115
- if (size === void 0) {
1116
- size = sizes.get(key);
1117
- if (size !== void 0) {
1118
- return size;
1119
- }
1120
- }
1121
- if (size === void 0) {
1122
- size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1123
- }
1124
- setSize(ctx, key, size);
1125
- return size;
1126
- }
1127
- function getItemSizeAtIndex(ctx, index) {
1128
- if (index === void 0 || index < 0) {
1129
- return void 0;
1130
- }
1131
- const targetId = getId(ctx.state, index);
1132
- return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1133
- }
1134
-
1135
- // src/core/calculateOffsetWithOffsetPosition.ts
1136
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1137
- var _a3;
1138
- const state = ctx.state;
1139
- const { index, viewOffset, viewPosition } = params;
1140
- let offset = offsetParam;
1141
- if (viewOffset) {
1142
- offset -= viewOffset;
1143
- }
1144
- if (index !== void 0) {
1145
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1146
- if (topOffsetAdjustment) {
1147
- offset += topOffsetAdjustment;
1148
- }
1149
- }
1150
- if (viewPosition !== void 0 && index !== void 0) {
1151
- const dataLength = state.props.data.length;
1152
- if (dataLength === 0) {
1153
- return offset;
1154
- }
1155
- const isOutOfBounds = index < 0 || index >= dataLength;
1156
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1157
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1158
- const trailingInset = getContentInsetEnd(state);
1159
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1160
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1161
- const footerSize = peek$(ctx, "footerSize") || 0;
1162
- offset += footerSize;
1163
- }
1164
- }
1165
- return offset;
1166
- }
1167
-
1168
- // src/core/clampScrollOffset.ts
1169
- function clampScrollOffset(ctx, offset, scrollTarget) {
1170
- const state = ctx.state;
1171
- const contentSize = getContentSize(ctx);
1172
- let clampedOffset = offset;
1173
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1174
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1175
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1176
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1177
- const maxOffset = baseMaxOffset + extraEndOffset;
1178
- clampedOffset = Math.min(offset, maxOffset);
1179
- }
1180
- clampedOffset = Math.max(0, clampedOffset);
1181
- return clampedOffset;
1182
- }
1183
-
1184
- // src/core/deferredPublicOnScroll.ts
1185
- function withResolvedContentOffset(state, event, resolvedOffset) {
1186
- return {
1187
- ...event,
1188
- nativeEvent: {
1189
- ...event.nativeEvent,
1190
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1191
- }
1192
- };
1131
+ // src/core/deferredPublicOnScroll.ts
1132
+ function withResolvedContentOffset(state, event, resolvedOffset) {
1133
+ return {
1134
+ ...event,
1135
+ nativeEvent: {
1136
+ ...event.nativeEvent,
1137
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1138
+ }
1139
+ };
1193
1140
  }
1194
1141
  function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
1195
1142
  var _a3, _b, _c, _d;
@@ -1268,63 +1215,489 @@ var initialScrollCompletion = {
1268
1215
  if (!state.initialScrollSession) {
1269
1216
  return;
1270
1217
  }
1271
- const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1272
- completion.didDispatchNativeScroll = void 0;
1273
- completion.didRetrySilentInitialScroll = void 0;
1218
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1219
+ completion.didDispatchNativeScroll = void 0;
1220
+ completion.didRetrySilentInitialScroll = void 0;
1221
+ }
1222
+ };
1223
+ var initialScrollWatchdog = {
1224
+ clear(state) {
1225
+ initialScrollWatchdog.set(state, void 0);
1226
+ },
1227
+ didObserveProgress(newScroll, watchdog) {
1228
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1229
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1230
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1231
+ },
1232
+ get(state) {
1233
+ var _a3, _b;
1234
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1235
+ },
1236
+ hasNonZeroTargetOffset(targetOffset) {
1237
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1238
+ },
1239
+ isAtZeroTargetOffset(targetOffset) {
1240
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1241
+ },
1242
+ set(state, watchdog) {
1243
+ var _a3, _b;
1244
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1245
+ return;
1246
+ }
1247
+ const completion = ensureInitialScrollSessionCompletion(state);
1248
+ completion.watchdog = watchdog ? {
1249
+ startScroll: watchdog.startScroll,
1250
+ targetOffset: watchdog.targetOffset
1251
+ } : void 0;
1252
+ }
1253
+ };
1254
+ function setInitialScrollSession(state, options = {}) {
1255
+ var _a3, _b, _c;
1256
+ const existingSession = state.initialScrollSession;
1257
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1258
+ const completion = existingSession == null ? void 0 : existingSession.completion;
1259
+ const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1260
+ const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1261
+ if (!kind) {
1262
+ return clearInitialScrollSession(state);
1263
+ }
1264
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1265
+ return clearInitialScrollSession(state);
1266
+ }
1267
+ const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1268
+ state.initialScrollSession = createInitialScrollSession({
1269
+ bootstrap,
1270
+ completion,
1271
+ kind,
1272
+ previousDataLength
1273
+ });
1274
+ return state.initialScrollSession;
1275
+ }
1276
+
1277
+ // src/utils/checkThreshold.ts
1278
+ var HYSTERESIS_MULTIPLIER = 1.3;
1279
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1280
+ const absDistance = Math.abs(distance);
1281
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
1282
+ const updateSnapshot = () => {
1283
+ setSnapshot({
1284
+ atThreshold,
1285
+ contentSize: context.contentSize,
1286
+ dataLength: context.dataLength,
1287
+ scrollPosition: context.scrollPosition
1288
+ });
1289
+ };
1290
+ if (!wasReached) {
1291
+ if (!within) {
1292
+ return false;
1293
+ }
1294
+ onReached(distance);
1295
+ updateSnapshot();
1296
+ return true;
1297
+ }
1298
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1299
+ if (reset) {
1300
+ setSnapshot(void 0);
1301
+ return false;
1302
+ }
1303
+ if (within) {
1304
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1305
+ if (changed) {
1306
+ if (allowReentryOnChange) {
1307
+ onReached(distance);
1308
+ }
1309
+ updateSnapshot();
1310
+ }
1311
+ }
1312
+ return true;
1313
+ };
1314
+
1315
+ // src/utils/hasActiveInitialScroll.ts
1316
+ function hasActiveInitialScroll(state) {
1317
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1318
+ }
1319
+
1320
+ // src/utils/checkAtBottom.ts
1321
+ function checkAtBottom(ctx) {
1322
+ var _a3;
1323
+ const state = ctx.state;
1324
+ if (!state) {
1325
+ return;
1326
+ }
1327
+ const {
1328
+ queuedInitialLayout,
1329
+ scrollLength,
1330
+ scroll,
1331
+ maintainingScrollAtEnd,
1332
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1333
+ } = state;
1334
+ const contentSize = getContentSize(ctx);
1335
+ if (contentSize > 0 && queuedInitialLayout) {
1336
+ const insetEnd = getContentInsetEnd(ctx);
1337
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1338
+ const isContentLess = contentSize < scrollLength;
1339
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1340
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1341
+ set$(
1342
+ ctx,
1343
+ "isWithinMaintainScrollAtEndThreshold",
1344
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1345
+ );
1346
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1347
+ if (!shouldSkipThresholdChecks) {
1348
+ state.isEndReached = checkThreshold(
1349
+ distanceFromEnd,
1350
+ isContentLess,
1351
+ onEndReachedThreshold * scrollLength,
1352
+ state.isEndReached,
1353
+ state.endReachedSnapshot,
1354
+ {
1355
+ contentSize,
1356
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1357
+ scrollPosition: scroll
1358
+ },
1359
+ (distance) => {
1360
+ var _a4, _b;
1361
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1362
+ },
1363
+ (snapshot) => {
1364
+ state.endReachedSnapshot = snapshot;
1365
+ },
1366
+ true
1367
+ );
1368
+ }
1369
+ }
1370
+ }
1371
+
1372
+ // src/utils/checkAtTop.ts
1373
+ function checkAtTop(ctx) {
1374
+ const state = ctx == null ? void 0 : ctx.state;
1375
+ if (!state) {
1376
+ return;
1377
+ }
1378
+ const {
1379
+ dataChangeEpoch,
1380
+ isStartReached,
1381
+ props: { data, onStartReachedThreshold },
1382
+ scroll,
1383
+ scrollLength,
1384
+ startReachedSnapshot,
1385
+ startReachedSnapshotDataChangeEpoch,
1386
+ totalSize
1387
+ } = state;
1388
+ const dataLength = data.length;
1389
+ const threshold = onStartReachedThreshold * scrollLength;
1390
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1391
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1392
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1393
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1394
+ state.isStartReached = false;
1395
+ state.startReachedSnapshot = void 0;
1396
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1397
+ }
1398
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1399
+ set$(ctx, "isNearStart", scroll <= threshold);
1400
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1401
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1402
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1403
+ state.isStartReached = checkThreshold(
1404
+ scroll,
1405
+ false,
1406
+ threshold,
1407
+ state.isStartReached,
1408
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1409
+ {
1410
+ contentSize: totalSize,
1411
+ dataLength,
1412
+ scrollPosition: scroll
1413
+ },
1414
+ (distance) => {
1415
+ var _a3, _b;
1416
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1417
+ },
1418
+ (snapshot) => {
1419
+ state.startReachedSnapshot = snapshot;
1420
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1421
+ },
1422
+ allowReentryOnDataChange
1423
+ );
1424
+ }
1425
+ }
1426
+
1427
+ // src/utils/checkThresholds.ts
1428
+ function checkThresholds(ctx) {
1429
+ checkAtBottom(ctx);
1430
+ checkAtTop(ctx);
1431
+ }
1432
+
1433
+ // src/core/recalculateSettledScroll.ts
1434
+ function recalculateSettledScroll(ctx) {
1435
+ var _a3, _b;
1436
+ const state = ctx.state;
1437
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1438
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1439
+ }
1440
+ checkThresholds(ctx);
1441
+ }
1442
+
1443
+ // src/utils/setInitialRenderState.ts
1444
+ function setInitialRenderState(ctx, {
1445
+ didLayout,
1446
+ didInitialScroll
1447
+ }) {
1448
+ const { state } = ctx;
1449
+ const {
1450
+ loadStartTime,
1451
+ props: { onLoad }
1452
+ } = state;
1453
+ if (didLayout) {
1454
+ state.didContainersLayout = true;
1455
+ }
1456
+ if (didInitialScroll) {
1457
+ state.didFinishInitialScroll = true;
1458
+ }
1459
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1460
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1461
+ set$(ctx, "readyToRender", true);
1462
+ if (onLoad) {
1463
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1464
+ }
1465
+ }
1466
+ }
1467
+
1468
+ // src/core/finishInitialScroll.ts
1469
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1470
+ function syncInitialScrollOffset(state, offset) {
1471
+ state.scroll = offset;
1472
+ state.scrollPending = offset;
1473
+ state.scrollPrev = offset;
1474
+ }
1475
+ function clearPreservedInitialScrollTargetTimeout(state) {
1476
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
1477
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
1478
+ state.timeoutPreservedInitialScrollClear = void 0;
1479
+ }
1480
+ }
1481
+ function clearPreservedInitialScrollTarget(state) {
1482
+ clearPreservedInitialScrollTargetTimeout(state);
1483
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1484
+ state.initialScroll = void 0;
1485
+ setInitialScrollSession(state);
1486
+ }
1487
+ function finishInitialScroll(ctx, options) {
1488
+ var _a3, _b, _c;
1489
+ const state = ctx.state;
1490
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1491
+ syncInitialScrollOffset(state, options.resolvedOffset);
1492
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1493
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1494
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1495
+ syncInitialScrollOffset(state, observedOffset);
1496
+ }
1497
+ }
1498
+ const complete = () => {
1499
+ var _a4, _b2, _c2, _d, _e;
1500
+ const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1501
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1502
+ initialScrollWatchdog.clear(state);
1503
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1504
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1505
+ setInitialScrollSession(state);
1506
+ clearPreservedInitialScrollTargetTimeout(state);
1507
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1508
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1509
+ var _a5;
1510
+ state.timeoutPreservedInitialScrollClear = void 0;
1511
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1512
+ return;
1513
+ }
1514
+ clearPreservedInitialScrollTarget(state);
1515
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
1516
+ }
1517
+ } else {
1518
+ clearPreservedInitialScrollTarget(state);
1519
+ }
1520
+ if (options == null ? void 0 : options.recalculateItems) {
1521
+ recalculateSettledScroll(ctx);
1522
+ }
1523
+ setInitialRenderState(ctx, { didInitialScroll: true });
1524
+ if (shouldReleaseDeferredPublicOnScroll) {
1525
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1526
+ }
1527
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
1528
+ };
1529
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
1530
+ requestAnimationFrame(complete);
1531
+ return;
1532
+ }
1533
+ complete();
1534
+ }
1535
+
1536
+ // src/core/calculateOffsetForIndex.ts
1537
+ function calculateOffsetForIndex(ctx, index) {
1538
+ const state = ctx.state;
1539
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1540
+ }
1541
+
1542
+ // src/core/getTopOffsetAdjustment.ts
1543
+ function getTopOffsetAdjustment(ctx) {
1544
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1545
+ }
1546
+
1547
+ // src/utils/getId.ts
1548
+ function getId(state, index) {
1549
+ const { data, keyExtractor } = state.props;
1550
+ if (!data) {
1551
+ return "";
1552
+ }
1553
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1554
+ const id = ret;
1555
+ state.idCache[index] = id;
1556
+ return id;
1557
+ }
1558
+
1559
+ // src/core/addTotalSize.ts
1560
+ function addTotalSize(ctx, key, add) {
1561
+ const state = ctx.state;
1562
+ const prevTotalSize = state.totalSize;
1563
+ let totalSize = state.totalSize;
1564
+ if (key === null) {
1565
+ totalSize = add;
1566
+ if (state.timeoutSetPaddingTop) {
1567
+ clearTimeout(state.timeoutSetPaddingTop);
1568
+ state.timeoutSetPaddingTop = void 0;
1569
+ }
1570
+ } else {
1571
+ totalSize += add;
1572
+ }
1573
+ if (prevTotalSize !== totalSize) {
1574
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1575
+ state.pendingTotalSize = totalSize;
1576
+ } else {
1577
+ state.pendingTotalSize = void 0;
1578
+ state.totalSize = totalSize;
1579
+ set$(ctx, "totalSize", totalSize);
1580
+ }
1581
+ }
1582
+ }
1583
+
1584
+ // src/core/setSize.ts
1585
+ function setSize(ctx, itemKey, size) {
1586
+ const state = ctx.state;
1587
+ const { sizes } = state;
1588
+ const previousSize = sizes.get(itemKey);
1589
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1590
+ if (diff !== 0) {
1591
+ addTotalSize(ctx, itemKey, diff);
1592
+ }
1593
+ sizes.set(itemKey, size);
1594
+ }
1595
+
1596
+ // src/utils/getItemSize.ts
1597
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1598
+ var _a3, _b, _c;
1599
+ const state = ctx.state;
1600
+ const {
1601
+ sizesKnown,
1602
+ sizes,
1603
+ averageSizes,
1604
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1605
+ scrollingTo
1606
+ } = state;
1607
+ const sizeKnown = sizesKnown.get(key);
1608
+ if (sizeKnown !== void 0) {
1609
+ return sizeKnown;
1610
+ }
1611
+ let size;
1612
+ const renderedSize = sizes.get(key);
1613
+ if (preferCachedSize) {
1614
+ if (renderedSize !== void 0) {
1615
+ return renderedSize;
1616
+ }
1617
+ }
1618
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1619
+ if (getFixedItemSize) {
1620
+ size = getFixedItemSize(data, index, itemType);
1621
+ if (size !== void 0) {
1622
+ sizesKnown.set(key, size);
1623
+ }
1624
+ }
1625
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1626
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1627
+ if (averageSizeForType !== void 0) {
1628
+ size = roundSize(averageSizeForType);
1629
+ }
1630
+ }
1631
+ if (size === void 0 && renderedSize !== void 0) {
1632
+ return renderedSize;
1633
+ }
1634
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
1635
+ const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
1636
+ if (averageSizeForType !== void 0) {
1637
+ size = roundSize(averageSizeForType);
1638
+ }
1274
1639
  }
1275
- };
1276
- var initialScrollWatchdog = {
1277
- clear(state) {
1278
- initialScrollWatchdog.set(state, void 0);
1279
- },
1280
- didObserveProgress(newScroll, watchdog) {
1281
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1282
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1283
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1284
- },
1285
- get(state) {
1286
- var _a3, _b;
1287
- return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1288
- },
1289
- hasNonZeroTargetOffset(targetOffset) {
1290
- return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1291
- },
1292
- isAtZeroTargetOffset(targetOffset) {
1293
- return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1294
- },
1295
- set(state, watchdog) {
1296
- var _a3, _b;
1297
- if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1298
- return;
1640
+ if (size === void 0) {
1641
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1642
+ }
1643
+ setSize(ctx, key, size);
1644
+ return size;
1645
+ }
1646
+ function getItemSizeAtIndex(ctx, index) {
1647
+ if (index === void 0 || index < 0) {
1648
+ return void 0;
1649
+ }
1650
+ const targetId = getId(ctx.state, index);
1651
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1652
+ }
1653
+
1654
+ // src/core/calculateOffsetWithOffsetPosition.ts
1655
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1656
+ var _a3;
1657
+ const state = ctx.state;
1658
+ const { index, viewOffset, viewPosition } = params;
1659
+ let offset = offsetParam;
1660
+ if (viewOffset) {
1661
+ offset -= viewOffset;
1662
+ }
1663
+ if (index !== void 0) {
1664
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1665
+ if (topOffsetAdjustment) {
1666
+ offset += topOffsetAdjustment;
1299
1667
  }
1300
- const completion = ensureInitialScrollSessionCompletion(state);
1301
- completion.watchdog = watchdog ? {
1302
- startScroll: watchdog.startScroll,
1303
- targetOffset: watchdog.targetOffset
1304
- } : void 0;
1305
1668
  }
1306
- };
1307
- function setInitialScrollSession(state, options = {}) {
1308
- var _a3, _b, _c;
1309
- const existingSession = state.initialScrollSession;
1310
- const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1311
- const completion = existingSession == null ? void 0 : existingSession.completion;
1312
- const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1313
- const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1314
- if (!kind) {
1315
- return clearInitialScrollSession(state);
1669
+ if (viewPosition !== void 0 && index !== void 0) {
1670
+ const dataLength = state.props.data.length;
1671
+ if (dataLength === 0) {
1672
+ return offset;
1673
+ }
1674
+ const isOutOfBounds = index < 0 || index >= dataLength;
1675
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1676
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1677
+ const trailingInset = getContentInsetEnd(ctx);
1678
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1679
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1680
+ const footerSize = peek$(ctx, "footerSize") || 0;
1681
+ offset += footerSize;
1682
+ }
1316
1683
  }
1317
- if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1318
- return clearInitialScrollSession(state);
1684
+ return offset;
1685
+ }
1686
+
1687
+ // src/core/clampScrollOffset.ts
1688
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1689
+ const state = ctx.state;
1690
+ const contentSize = getContentSize(ctx);
1691
+ let clampedOffset = offset;
1692
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
1693
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1694
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1695
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1696
+ const maxOffset = baseMaxOffset + extraEndOffset;
1697
+ clampedOffset = Math.min(offset, maxOffset);
1319
1698
  }
1320
- const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1321
- state.initialScrollSession = createInitialScrollSession({
1322
- bootstrap,
1323
- completion,
1324
- kind,
1325
- previousDataLength
1326
- });
1327
- return state.initialScrollSession;
1699
+ clampedOffset = Math.max(0, clampedOffset);
1700
+ return clampedOffset;
1328
1701
  }
1329
1702
 
1330
1703
  // src/core/finishScrollTo.ts
@@ -1345,15 +1718,20 @@ function finishScrollTo(ctx) {
1345
1718
  }
1346
1719
  if (scrollingTo.isInitialScroll || state.initialScroll) {
1347
1720
  const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1721
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1348
1722
  finishInitialScroll(ctx, {
1349
- onFinished: resolvePendingScroll,
1350
- preserveTarget: isOffsetSession && state.props.data.length === 0 || !!scrollingTo.isInitialScroll && !!((_b = state.initialScroll) == null ? void 0 : _b.preserveForFooterLayout),
1723
+ onFinished: () => {
1724
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1725
+ },
1726
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1351
1727
  recalculateItems: true,
1728
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1352
1729
  syncObservedOffset: isOffsetSession,
1353
1730
  waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1354
1731
  });
1355
1732
  return;
1356
1733
  }
1734
+ recalculateSettledScroll(ctx);
1357
1735
  resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1358
1736
  }
1359
1737
  }
@@ -1478,7 +1856,7 @@ function checkFinishedScrollFallback(ctx) {
1478
1856
  );
1479
1857
  const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1480
1858
  const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1481
- const shouldRetrySilentInitialNativeScroll = Platform2.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1859
+ const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1482
1860
  if (shouldRetrySilentInitialNativeScroll) {
1483
1861
  const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1484
1862
  const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
@@ -1528,6 +1906,17 @@ function doScrollTo(ctx, params) {
1528
1906
  }
1529
1907
 
1530
1908
  // src/core/scrollTo.ts
1909
+ function getAverageSizeSnapshot(state) {
1910
+ if (Object.keys(state.averageSizes).length === 0) {
1911
+ return void 0;
1912
+ }
1913
+ const snapshot = {};
1914
+ for (const itemType in state.averageSizes) {
1915
+ const averages = state.averageSizes[itemType];
1916
+ snapshot[itemType] = averages.avg;
1917
+ }
1918
+ return snapshot;
1919
+ }
1531
1920
  function syncInitialScrollNativeWatchdog(state, options) {
1532
1921
  var _a3;
1533
1922
  const { isInitialScroll, requestedOffset, targetOffset } = options;
@@ -1575,15 +1964,17 @@ function scrollTo(ctx, params) {
1575
1964
  if (isInitialScroll) {
1576
1965
  initialScrollCompletion.resetFlags(state);
1577
1966
  }
1967
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
1578
1968
  state.scrollingTo = {
1579
1969
  ...scrollTarget,
1970
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
1580
1971
  targetOffset,
1581
1972
  waitForInitialScrollCompletionFrame
1582
1973
  };
1583
1974
  }
1584
1975
  state.scrollPending = targetOffset;
1585
1976
  syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
1586
- if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1977
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1587
1978
  doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1588
1979
  } else {
1589
1980
  state.scroll = offset;
@@ -1633,180 +2024,7 @@ function scrollToIndex(ctx, {
1633
2024
  });
1634
2025
  }
1635
2026
 
1636
- // src/utils/checkThreshold.ts
1637
- var HYSTERESIS_MULTIPLIER = 1.3;
1638
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1639
- const absDistance = Math.abs(distance);
1640
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
1641
- const updateSnapshot = () => {
1642
- setSnapshot({
1643
- atThreshold,
1644
- contentSize: context.contentSize,
1645
- dataLength: context.dataLength,
1646
- scrollPosition: context.scrollPosition
1647
- });
1648
- };
1649
- if (!wasReached) {
1650
- if (!within) {
1651
- return false;
1652
- }
1653
- onReached(distance);
1654
- updateSnapshot();
1655
- return true;
1656
- }
1657
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1658
- if (reset) {
1659
- setSnapshot(void 0);
1660
- return false;
1661
- }
1662
- if (within) {
1663
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1664
- if (changed) {
1665
- if (allowReentryOnChange) {
1666
- onReached(distance);
1667
- }
1668
- updateSnapshot();
1669
- }
1670
- }
1671
- return true;
1672
- };
1673
-
1674
- // src/utils/checkAtBottom.ts
1675
- function checkAtBottom(ctx) {
1676
- var _a3;
1677
- const state = ctx.state;
1678
- if (!state || state.initialScroll) {
1679
- return;
1680
- }
1681
- const {
1682
- queuedInitialLayout,
1683
- scrollLength,
1684
- scroll,
1685
- maintainingScrollAtEnd,
1686
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1687
- } = state;
1688
- if (state.initialScroll) {
1689
- return;
1690
- }
1691
- const contentSize = getContentSize(ctx);
1692
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1693
- const insetEnd = getContentInsetEnd(state);
1694
- const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1695
- const isContentLess = contentSize < scrollLength;
1696
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1697
- state.isEndReached = checkThreshold(
1698
- distanceFromEnd,
1699
- isContentLess,
1700
- onEndReachedThreshold * scrollLength,
1701
- state.isEndReached,
1702
- state.endReachedSnapshot,
1703
- {
1704
- contentSize,
1705
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1706
- scrollPosition: scroll
1707
- },
1708
- (distance) => {
1709
- var _a4, _b;
1710
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1711
- },
1712
- (snapshot) => {
1713
- state.endReachedSnapshot = snapshot;
1714
- },
1715
- true
1716
- );
1717
- }
1718
- }
1719
-
1720
- // src/utils/checkAtTop.ts
1721
- function checkAtTop(ctx) {
1722
- const state = ctx == null ? void 0 : ctx.state;
1723
- if (!state || state.initialScroll || state.scrollingTo) {
1724
- return;
1725
- }
1726
- const {
1727
- dataChangeEpoch,
1728
- isStartReached,
1729
- props: { data, onStartReachedThreshold },
1730
- scroll,
1731
- scrollLength,
1732
- startReachedSnapshot,
1733
- startReachedSnapshotDataChangeEpoch,
1734
- totalSize
1735
- } = state;
1736
- const dataLength = data.length;
1737
- const threshold = onStartReachedThreshold * scrollLength;
1738
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1739
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1740
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1741
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1742
- state.isStartReached = false;
1743
- state.startReachedSnapshot = void 0;
1744
- state.startReachedSnapshotDataChangeEpoch = void 0;
1745
- }
1746
- state.isAtStart = scroll <= 0;
1747
- if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1748
- return;
1749
- }
1750
- state.isStartReached = checkThreshold(
1751
- scroll,
1752
- false,
1753
- threshold,
1754
- state.isStartReached,
1755
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1756
- {
1757
- contentSize: totalSize,
1758
- dataLength,
1759
- scrollPosition: scroll
1760
- },
1761
- (distance) => {
1762
- var _a3, _b;
1763
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1764
- },
1765
- (snapshot) => {
1766
- state.startReachedSnapshot = snapshot;
1767
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1768
- },
1769
- allowReentryOnDataChange
1770
- );
1771
- }
1772
-
1773
- // src/utils/checkThresholds.ts
1774
- function checkThresholds(ctx) {
1775
- checkAtBottom(ctx);
1776
- checkAtTop(ctx);
1777
- }
1778
-
1779
- // src/utils/setInitialRenderState.ts
1780
- function setInitialRenderState(ctx, {
1781
- didLayout,
1782
- didInitialScroll
1783
- }) {
1784
- const { state } = ctx;
1785
- const {
1786
- loadStartTime,
1787
- props: { onLoad }
1788
- } = state;
1789
- if (didLayout) {
1790
- state.didContainersLayout = true;
1791
- }
1792
- if (didInitialScroll) {
1793
- state.didFinishInitialScroll = true;
1794
- }
1795
- const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1796
- if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1797
- set$(ctx, "readyToRender", true);
1798
- if (onLoad) {
1799
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1800
- }
1801
- }
1802
- }
1803
-
1804
2027
  // src/core/initialScroll.ts
1805
- function syncInitialScrollOffset(state, offset) {
1806
- state.scroll = offset;
1807
- state.scrollPending = offset;
1808
- state.scrollPrev = offset;
1809
- }
1810
2028
  function dispatchInitialScroll(ctx, params) {
1811
2029
  const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
1812
2030
  const requestedIndex = target.index;
@@ -1827,6 +2045,11 @@ function dispatchInitialScroll(ctx, params) {
1827
2045
  }
1828
2046
  function setInitialScrollTarget(state, target, options) {
1829
2047
  var _a3;
2048
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2049
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2050
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2051
+ state.timeoutPreservedInitialScrollClear = void 0;
2052
+ }
1830
2053
  state.initialScroll = target;
1831
2054
  if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
1832
2055
  state.didFinishInitialScroll = false;
@@ -1835,44 +2058,6 @@ function setInitialScrollTarget(state, target, options) {
1835
2058
  kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
1836
2059
  });
1837
2060
  }
1838
- function finishInitialScroll(ctx, options) {
1839
- var _a3, _b, _c;
1840
- const state = ctx.state;
1841
- if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1842
- syncInitialScrollOffset(state, options.resolvedOffset);
1843
- } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1844
- const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1845
- if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1846
- syncInitialScrollOffset(state, observedOffset);
1847
- }
1848
- }
1849
- const complete = () => {
1850
- var _a4, _b2, _c2, _d, _e, _f, _g;
1851
- const shouldReleaseDeferredPublicOnScroll = Platform2.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1852
- const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1853
- initialScrollWatchdog.clear(state);
1854
- if (!(options == null ? void 0 : options.preserveTarget)) {
1855
- state.initialScroll = void 0;
1856
- }
1857
- setInitialScrollSession(state);
1858
- if ((options == null ? void 0 : options.recalculateItems) && ((_e = state.props) == null ? void 0 : _e.data)) {
1859
- (_f = state.triggerCalculateItemsInView) == null ? void 0 : _f.call(state, { forceFullItemPositions: true });
1860
- }
1861
- if (options == null ? void 0 : options.recalculateItems) {
1862
- checkThresholds(ctx);
1863
- }
1864
- setInitialRenderState(ctx, { didInitialScroll: true });
1865
- if (shouldReleaseDeferredPublicOnScroll) {
1866
- releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1867
- }
1868
- (_g = options == null ? void 0 : options.onFinished) == null ? void 0 : _g.call(options);
1869
- };
1870
- if (options == null ? void 0 : options.waitForCompletionFrame) {
1871
- requestAnimationFrame(complete);
1872
- return;
1873
- }
1874
- complete();
1875
- }
1876
2061
  function resolveInitialScrollOffset(ctx, initialScroll) {
1877
2062
  var _a3, _b;
1878
2063
  const state = ctx.state;
@@ -1966,13 +2151,18 @@ function advanceCurrentInitialScrollSession(ctx, options) {
1966
2151
  function isNullOrUndefined2(value) {
1967
2152
  return value === null || value === void 0;
1968
2153
  }
1969
- function getMountedBufferedIndices(state) {
1970
- const { startBuffered, endBuffered } = state;
1971
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
1972
- return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= startBuffered && index <= endBuffered).sort((a, b) => a - b);
2154
+ function getMountedIndicesInRange(state, start, end) {
2155
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2156
+ return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= start && index <= end).sort((a, b) => a - b);
1973
2157
  }
1974
2158
  return [];
1975
2159
  }
2160
+ function getMountedBufferedIndices(state) {
2161
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2162
+ }
2163
+ function getMountedNoBufferIndices(state) {
2164
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2165
+ }
1976
2166
  function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
1977
2167
  return indices.length > 0 && indices.every((index) => {
1978
2168
  const key = getId(state, index);
@@ -2191,16 +2381,22 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2191
2381
  function areEquivalentBootstrapInitialScrollTargets(current, next) {
2192
2382
  return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2193
2383
  }
2194
- function clearPendingInitialScrollFooterLayout(state, target) {
2384
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
2385
+ const { dataLength, stylePaddingBottom, target } = options;
2386
+ const state = ctx.state;
2195
2387
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2196
2388
  return;
2197
2389
  }
2198
- if (state.didFinishInitialScroll && !getBootstrapInitialScrollSession(state)) {
2199
- state.initialScroll = void 0;
2200
- setInitialScrollSession(state);
2201
- return;
2202
- }
2203
- setInitialScrollTarget(state, { ...target, preserveForFooterLayout: void 0 });
2390
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
2391
+ dataLength,
2392
+ footerSize: 0,
2393
+ preserveForFooterLayout: void 0,
2394
+ stylePaddingBottom
2395
+ });
2396
+ setInitialScrollTarget(state, clearedFooterTarget);
2397
+ }
2398
+ function clearFinishedViewportRetargetableInitialScroll(state) {
2399
+ clearPreservedInitialScrollTarget(state);
2204
2400
  }
2205
2401
  function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2206
2402
  const state = ctx.state;
@@ -2215,6 +2411,25 @@ function getObservedBootstrapInitialScrollOffset(state) {
2215
2411
  const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2216
2412
  return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2217
2413
  }
2414
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2415
+ var _a3, _b;
2416
+ const state = ctx.state;
2417
+ const initialScroll = state.initialScroll;
2418
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
2419
+ return;
2420
+ }
2421
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2422
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2423
+ clearPendingInitialScrollFooterLayout(ctx, {
2424
+ dataLength: state.props.data.length,
2425
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2426
+ target: initialScroll
2427
+ });
2428
+ return;
2429
+ }
2430
+ clearFinishedViewportRetargetableInitialScroll(state);
2431
+ }
2432
+ }
2218
2433
  function startBootstrapInitialScrollOnMount(ctx, options) {
2219
2434
  var _a3, _b, _c;
2220
2435
  const { initialScrollAtEnd, target } = options;
@@ -2236,7 +2451,7 @@ function startBootstrapInitialScrollOnMount(ctx, options) {
2236
2451
  } else {
2237
2452
  startBootstrapInitialScrollSession(state, {
2238
2453
  scroll: offset,
2239
- seedContentOffset: Platform2.OS === "web" ? 0 : offset,
2454
+ seedContentOffset: Platform.OS === "web" ? 0 : offset,
2240
2455
  targetIndexSeed: target.index
2241
2456
  });
2242
2457
  ensureBootstrapInitialScrollFrameTicker(ctx);
@@ -2251,15 +2466,16 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2251
2466
  }
2252
2467
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2253
2468
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2469
+ const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2470
+ if (shouldClearFinishedResizePreservation) {
2471
+ clearPreservedInitialScrollTarget(state);
2472
+ return;
2473
+ }
2254
2474
  const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2255
2475
  if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2256
2476
  return;
2257
2477
  }
2258
2478
  if (shouldRetargetBottomAligned) {
2259
- if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2260
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2261
- return;
2262
- }
2263
2479
  const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2264
2480
  dataLength,
2265
2481
  footerSize: peek$(ctx, "footerSize") || 0,
@@ -2272,6 +2488,14 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2272
2488
  stylePaddingBottom,
2273
2489
  target: initialScroll
2274
2490
  });
2491
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2492
+ clearPendingInitialScrollFooterLayout(ctx, {
2493
+ dataLength,
2494
+ stylePaddingBottom,
2495
+ target: initialScroll
2496
+ });
2497
+ return;
2498
+ }
2275
2499
  if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2276
2500
  setInitialScrollTarget(state, updatedInitialScroll, {
2277
2501
  resetDidFinish: shouldResetDidFinish
@@ -2313,7 +2537,11 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2313
2537
  return;
2314
2538
  }
2315
2539
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2316
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2540
+ clearPendingInitialScrollFooterLayout(ctx, {
2541
+ dataLength,
2542
+ stylePaddingBottom,
2543
+ target: initialScroll
2544
+ });
2317
2545
  } else {
2318
2546
  const updatedInitialScroll = createInitialScrollAtEndTarget({
2319
2547
  dataLength,
@@ -2323,10 +2551,15 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2323
2551
  });
2324
2552
  const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2325
2553
  if (!didTargetChange) {
2326
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2554
+ clearPendingInitialScrollFooterLayout(ctx, {
2555
+ dataLength,
2556
+ stylePaddingBottom,
2557
+ target: initialScroll
2558
+ });
2327
2559
  } else {
2560
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
2328
2561
  setInitialScrollTarget(state, updatedInitialScroll, {
2329
- resetDidFinish: !!state.didFinishInitialScroll
2562
+ resetDidFinish: didFinishInitialScroll
2330
2563
  });
2331
2564
  rearmBootstrapInitialScroll(ctx, {
2332
2565
  scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
@@ -2335,6 +2568,29 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2335
2568
  }
2336
2569
  }
2337
2570
  }
2571
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
2572
+ const state = ctx.state;
2573
+ const initialScroll = state.initialScroll;
2574
+ if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2575
+ return;
2576
+ }
2577
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2578
+ if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2579
+ return;
2580
+ }
2581
+ const didFinishInitialScroll = state.didFinishInitialScroll;
2582
+ if (didFinishInitialScroll) {
2583
+ setInitialScrollTarget(state, initialScroll, {
2584
+ resetDidFinish: true
2585
+ });
2586
+ state.clearPreservedInitialScrollOnNextFinish = true;
2587
+ }
2588
+ rearmBootstrapInitialScroll(ctx, {
2589
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
2590
+ seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2591
+ targetIndexSeed: initialScroll.index
2592
+ });
2593
+ }
2338
2594
  function evaluateBootstrapInitialScroll(ctx) {
2339
2595
  var _a3, _b;
2340
2596
  const state = ctx.state;
@@ -2392,7 +2648,7 @@ function evaluateBootstrapInitialScroll(ctx) {
2392
2648
  queueBootstrapInitialScrollReevaluation(state);
2393
2649
  return;
2394
2650
  }
2395
- if (Platform2.OS !== "web" && Platform2.OS !== "android" && Math.abs(bootstrapInitialScroll.seedContentOffset - resolvedOffset) <= 1) {
2651
+ if (Platform.OS !== "web" && Platform.OS !== "android" && Math.abs(bootstrapInitialScroll.seedContentOffset - resolvedOffset) <= 1 && Math.abs(getObservedBootstrapInitialScrollOffset(state) - resolvedOffset) <= 1) {
2396
2652
  finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset);
2397
2653
  } else {
2398
2654
  clearBootstrapInitialScrollSession(state);
@@ -2400,17 +2656,20 @@ function evaluateBootstrapInitialScroll(ctx) {
2400
2656
  forceScroll: true,
2401
2657
  resolvedOffset,
2402
2658
  target: initialScroll,
2403
- waitForCompletionFrame: Platform2.OS === "web"
2659
+ waitForCompletionFrame: Platform.OS === "web"
2404
2660
  });
2405
2661
  }
2406
2662
  }
2407
2663
  function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
2664
+ var _a3;
2408
2665
  const state = ctx.state;
2409
2666
  clearBootstrapInitialScrollSession(state);
2667
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
2410
2668
  finishInitialScroll(ctx, {
2411
- preserveTarget: shouldPreserveInitialScrollForFooterLayout(state.initialScroll),
2669
+ preserveTarget: shouldPreserveResizeTarget,
2412
2670
  recalculateItems: true,
2413
- resolvedOffset
2671
+ resolvedOffset,
2672
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
2414
2673
  });
2415
2674
  }
2416
2675
  function abortBootstrapInitialScroll(ctx) {
@@ -2424,7 +2683,7 @@ function abortBootstrapInitialScroll(ctx) {
2424
2683
  forceScroll: true,
2425
2684
  resolvedOffset: bootstrapInitialScroll.scroll,
2426
2685
  target: initialScroll,
2427
- waitForCompletionFrame: Platform2.OS === "web"
2686
+ waitForCompletionFrame: Platform.OS === "web"
2428
2687
  });
2429
2688
  } else {
2430
2689
  finishBootstrapInitialScrollWithoutScroll(
@@ -2512,7 +2771,7 @@ function handleInitialScrollDataChange(ctx, options) {
2512
2771
  function requestAdjust(ctx, positionDiff, dataChanged) {
2513
2772
  const state = ctx.state;
2514
2773
  if (Math.abs(positionDiff) > 0.1) {
2515
- const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2774
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2516
2775
  const doit = () => {
2517
2776
  if (needsScrollWorkaround) {
2518
2777
  scrollTo(ctx, {
@@ -2531,7 +2790,7 @@ function requestAdjust(ctx, positionDiff, dataChanged) {
2531
2790
  const readyToRender = peek$(ctx, "readyToRender");
2532
2791
  if (readyToRender) {
2533
2792
  doit();
2534
- if (Platform2.OS !== "web") {
2793
+ if (Platform.OS !== "web") {
2535
2794
  const threshold = state.scroll - positionDiff / 2;
2536
2795
  if (!state.ignoreScrollFromMVCP) {
2537
2796
  state.ignoreScrollFromMVCP = {};
@@ -2586,7 +2845,7 @@ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
2586
2845
  return lock;
2587
2846
  }
2588
2847
  function updateAnchorLock(state, params) {
2589
- if (Platform2.OS === "web") {
2848
+ if (Platform.OS === "web") {
2590
2849
  const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
2591
2850
  const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
2592
2851
  const mvcpData = state.props.maintainVisibleContentPosition.data;
@@ -2608,7 +2867,7 @@ function updateAnchorLock(state, params) {
2608
2867
  }
2609
2868
  }
2610
2869
  function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
2611
- if (!dataChanged || Platform2.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
2870
+ if (!dataChanged || Platform.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
2612
2871
  return false;
2613
2872
  }
2614
2873
  const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
@@ -2687,7 +2946,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2687
2946
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2688
2947
  return true;
2689
2948
  }
2690
- if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
2949
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
2691
2950
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2692
2951
  return true;
2693
2952
  }
@@ -2707,7 +2966,7 @@ function prepareMVCP(ctx, dataChanged) {
2707
2966
  const {
2708
2967
  maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
2709
2968
  } = props;
2710
- const isWeb = Platform2.OS === "web";
2969
+ const isWeb = Platform.OS === "web";
2711
2970
  const now = Date.now();
2712
2971
  const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
2713
2972
  const scrollingTo = state.scrollingTo;
@@ -2852,6 +3111,86 @@ function prepareMVCP(ctx, dataChanged) {
2852
3111
  }
2853
3112
  }
2854
3113
 
3114
+ // src/core/syncMountedContainer.ts
3115
+ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3116
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
3117
+ const state = ctx.state;
3118
+ const {
3119
+ columns,
3120
+ columnSpans,
3121
+ positions,
3122
+ props: { data, itemsAreEqual, keyExtractor }
3123
+ } = state;
3124
+ const item = data[itemIndex];
3125
+ if (item === void 0) {
3126
+ return { didChangePosition: false, didRefreshData: false };
3127
+ }
3128
+ const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3129
+ let didChangePosition = false;
3130
+ let didRefreshData = false;
3131
+ if (updateLayout) {
3132
+ const positionValue = positions[itemIndex];
3133
+ if (positionValue === void 0) {
3134
+ set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3135
+ return { didChangePosition: false, didRefreshData: false };
3136
+ }
3137
+ const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3138
+ const column = columns[itemIndex] || 1;
3139
+ const span = columnSpans[itemIndex] || 1;
3140
+ const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
3141
+ const prevColumn = peek$(ctx, `containerColumn${containerIndex}`);
3142
+ const prevSpan = peek$(ctx, `containerSpan${containerIndex}`);
3143
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3144
+ set$(ctx, `containerPosition${containerIndex}`, position);
3145
+ didChangePosition = true;
3146
+ }
3147
+ if (column >= 0 && column !== prevColumn) {
3148
+ set$(ctx, `containerColumn${containerIndex}`, column);
3149
+ }
3150
+ if (span !== prevSpan) {
3151
+ set$(ctx, `containerSpan${containerIndex}`, span);
3152
+ }
3153
+ }
3154
+ const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3155
+ if (prevData !== item) {
3156
+ const pendingDataComparison = ((_c = state.pendingDataComparison) == null ? void 0 : _c.previousData) === state.previousData && ((_d = state.pendingDataComparison) == null ? void 0 : _d.nextData) === data ? state.pendingDataComparison : void 0;
3157
+ const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3158
+ if (cachedComparison === 2) {
3159
+ set$(ctx, `containerItemData${containerIndex}`, item);
3160
+ didRefreshData = true;
3161
+ } else if (cachedComparison !== 1) {
3162
+ const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3163
+ const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3164
+ if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3165
+ set$(ctx, `containerItemData${containerIndex}`, item);
3166
+ didRefreshData = true;
3167
+ } else if (!itemsAreEqual) {
3168
+ set$(ctx, `containerItemData${containerIndex}`, item);
3169
+ didRefreshData = true;
3170
+ } else {
3171
+ const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3172
+ if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
3173
+ if (state.previousData) {
3174
+ state.pendingDataComparison = {
3175
+ byIndex: [],
3176
+ nextData: data,
3177
+ previousData: state.previousData
3178
+ };
3179
+ }
3180
+ }
3181
+ if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3182
+ state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3183
+ }
3184
+ if (!isEqual) {
3185
+ set$(ctx, `containerItemData${containerIndex}`, item);
3186
+ didRefreshData = true;
3187
+ }
3188
+ }
3189
+ }
3190
+ }
3191
+ return { didChangePosition, didRefreshData };
3192
+ }
3193
+
2855
3194
  // src/core/prepareColumnStartState.ts
2856
3195
  function prepareColumnStartState(ctx, startIndex, useAverageSize) {
2857
3196
  var _a3;
@@ -3014,9 +3353,10 @@ function updateSnapToOffsets(ctx) {
3014
3353
  }
3015
3354
 
3016
3355
  // src/core/updateItemPositions.ts
3017
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
3356
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
3018
3357
  doMVCP: false,
3019
3358
  forceFullUpdate: false,
3359
+ optimizeForVisibleWindow: false,
3020
3360
  scrollBottomBuffered: -1,
3021
3361
  startIndex: 0
3022
3362
  }) {
@@ -3041,7 +3381,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
3041
3381
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
3042
3382
  const lastScrollDelta = state.lastScrollDelta;
3043
3383
  const velocity = getScrollVelocity(state);
3044
- const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3384
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || Platform.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3045
3385
  const maxVisibleArea = scrollBottomBuffered + 1e3;
3046
3386
  const useAverageSize = !getEstimatedItemSize;
3047
3387
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
@@ -3156,7 +3496,15 @@ function ensureViewabilityState(ctx, configId) {
3156
3496
  }
3157
3497
  let state = map.get(configId);
3158
3498
  if (!state) {
3159
- state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
3499
+ state = {
3500
+ end: -1,
3501
+ endBuffered: -1,
3502
+ previousEnd: -1,
3503
+ previousStart: -1,
3504
+ start: -1,
3505
+ startBuffered: -1,
3506
+ viewableItems: []
3507
+ };
3160
3508
  map.set(configId, state);
3161
3509
  }
3162
3510
  return state;
@@ -3176,7 +3524,7 @@ function setupViewability(props) {
3176
3524
  }
3177
3525
  return viewabilityConfigCallbackPairs;
3178
3526
  }
3179
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
3527
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
3180
3528
  const {
3181
3529
  timeouts,
3182
3530
  props: { data }
@@ -3185,6 +3533,8 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
3185
3533
  const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
3186
3534
  viewabilityState.start = start;
3187
3535
  viewabilityState.end = end;
3536
+ viewabilityState.startBuffered = startBuffered;
3537
+ viewabilityState.endBuffered = endBuffered;
3188
3538
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
3189
3539
  const timer = setTimeout(() => {
3190
3540
  timeouts.delete(timer);
@@ -3200,7 +3550,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3200
3550
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
3201
3551
  const configId = viewabilityConfig.id;
3202
3552
  const viewabilityState = ensureViewabilityState(ctx, configId);
3203
- const { viewableItems: previousViewableItems, start, end } = viewabilityState;
3553
+ const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
3204
3554
  const viewabilityTokens = /* @__PURE__ */ new Map();
3205
3555
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
3206
3556
  viewabilityTokens.set(
@@ -3269,7 +3619,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3269
3619
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
3270
3620
  }
3271
3621
  if (onViewableItemsChanged) {
3272
- onViewableItemsChanged({ changed, viewableItems });
3622
+ onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
3273
3623
  }
3274
3624
  }
3275
3625
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -3370,6 +3720,7 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3370
3720
  const numContainers = peek$(ctx, "numContainers");
3371
3721
  const state = ctx.state;
3372
3722
  const { stickyContainerPool, containerItemTypes } = state;
3723
+ const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3373
3724
  const result = [];
3374
3725
  const availableContainers = [];
3375
3726
  const pendingRemovalSet = new Set(pendingRemoval);
@@ -3426,18 +3777,20 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3426
3777
  }
3427
3778
  }
3428
3779
  }
3429
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
3430
- if (stickyContainerPool.has(u)) {
3431
- continue;
3432
- }
3433
- const key = peek$(ctx, `containerItemKey${u}`);
3434
- if (key === void 0) continue;
3435
- const index = state.indexByKey.get(key);
3436
- const isOutOfView = index < startBuffered || index > endBuffered;
3437
- if (isOutOfView) {
3438
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3439
- if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
3440
- availableContainers.push({ distance, index: u });
3780
+ if (!shouldAvoidAssignedContainerReuse) {
3781
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
3782
+ if (stickyContainerPool.has(u)) {
3783
+ continue;
3784
+ }
3785
+ const key = peek$(ctx, `containerItemKey${u}`);
3786
+ if (key === void 0) continue;
3787
+ const index = state.indexByKey.get(key);
3788
+ const isOutOfView = index < startBuffered || index > endBuffered;
3789
+ if (isOutOfView) {
3790
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3791
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
3792
+ availableContainers.push({ distance, index: u });
3793
+ }
3441
3794
  }
3442
3795
  }
3443
3796
  }
@@ -3581,7 +3934,6 @@ function calculateItemsInView(ctx, params = {}) {
3581
3934
  alwaysRenderIndicesSet,
3582
3935
  drawDistance,
3583
3936
  getItemType,
3584
- itemsAreEqual,
3585
3937
  keyExtractor,
3586
3938
  onStickyHeaderChange
3587
3939
  },
@@ -3608,11 +3960,11 @@ function calculateItemsInView(ctx, params = {}) {
3608
3960
  const numColumns = peek$(ctx, "numColumns");
3609
3961
  const speed = getScrollVelocity(state);
3610
3962
  const scrollExtra = 0;
3611
- const { queuedInitialLayout } = state;
3612
- const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && state.initialScroll ? (
3963
+ const { initialScroll, queuedInitialLayout } = state;
3964
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
3613
3965
  // Before the initial layout settles, keep viewport math anchored to the
3614
3966
  // current initial-scroll target instead of transient native adjustments.
3615
- resolveInitialScrollOffset(ctx, state.initialScroll)
3967
+ resolveInitialScrollOffset(ctx, initialScroll)
3616
3968
  ) : state.scroll;
3617
3969
  const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
3618
3970
  const scrollAdjustPad = scrollAdjustPending - topPad;
@@ -3643,7 +3995,7 @@ function calculateItemsInView(ctx, params = {}) {
3643
3995
  if (top === null && bottom === null) {
3644
3996
  state.scrollForNextCalculateItemsInView = void 0;
3645
3997
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
3646
- if (Platform2.OS !== "web" || !isInMVCPActiveMode(state)) {
3998
+ if (Platform.OS !== "web" || !isInMVCPActiveMode(state)) {
3647
3999
  return;
3648
4000
  }
3649
4001
  }
@@ -3657,9 +4009,11 @@ function calculateItemsInView(ctx, params = {}) {
3657
4009
  columnSpans.length = 0;
3658
4010
  }
3659
4011
  const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4012
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
3660
4013
  updateItemPositions(ctx, dataChanged, {
3661
4014
  doMVCP,
3662
4015
  forceFullUpdate: !!forceFullItemPositions,
4016
+ optimizeForVisibleWindow,
3663
4017
  scrollBottomBuffered,
3664
4018
  startIndex
3665
4019
  });
@@ -3907,44 +4261,25 @@ function calculateItemsInView(ctx, params = {}) {
3907
4261
  set$(ctx, `containerSpan${i}`, 1);
3908
4262
  } else {
3909
4263
  const itemIndex = indexByKey.get(itemKey);
3910
- const item = data[itemIndex];
3911
- if (item !== void 0) {
3912
- const positionValue = positions[itemIndex];
3913
- if (positionValue === void 0) {
3914
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
3915
- } else {
3916
- const position = (positionValue || 0) - scrollAdjustPending;
3917
- const column = columns[itemIndex] || 1;
3918
- const span = columnSpans[itemIndex] || 1;
3919
- const prevPos = peek$(ctx, `containerPosition${i}`);
3920
- const prevColumn = peek$(ctx, `containerColumn${i}`);
3921
- const prevSpan = peek$(ctx, `containerSpan${i}`);
3922
- const prevData = peek$(ctx, `containerItemData${i}`);
3923
- if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3924
- set$(ctx, `containerPosition${i}`, position);
3925
- didChangePositions = true;
3926
- }
3927
- if (column >= 0 && column !== prevColumn) {
3928
- set$(ctx, `containerColumn${i}`, column);
3929
- }
3930
- if (span !== prevSpan) {
3931
- set$(ctx, `containerSpan${i}`, span);
3932
- }
3933
- if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
3934
- set$(ctx, `containerItemData${i}`, item);
3935
- }
3936
- }
4264
+ if (itemIndex !== void 0) {
4265
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4266
+ scrollAdjustPending,
4267
+ updateLayout: true
4268
+ }).didChangePosition || didChangePositions;
3937
4269
  }
3938
4270
  }
3939
4271
  }
3940
- if (Platform2.OS === "web" && didChangePositions) {
4272
+ if (Platform.OS === "web" && didChangePositions) {
3941
4273
  set$(ctx, "lastPositionUpdate", Date.now());
3942
4274
  }
3943
4275
  if (suppressInitialScrollSideEffects) {
3944
4276
  evaluateBootstrapInitialScroll(ctx);
3945
4277
  return;
3946
4278
  }
3947
- if (!queuedInitialLayout && endBuffered !== null && checkAllSizesKnown(state)) {
4279
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4280
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4281
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4282
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
3948
4283
  setDidLayout(ctx);
3949
4284
  handleInitialScrollLayoutReady(ctx);
3950
4285
  }
@@ -3955,7 +4290,9 @@ function calculateItemsInView(ctx, params = {}) {
3955
4290
  viewabilityConfigCallbackPairs,
3956
4291
  scrollLength,
3957
4292
  startNoBuffer,
3958
- endNoBuffer
4293
+ endNoBuffer,
4294
+ startBuffered != null ? startBuffered : startNoBuffer,
4295
+ endBuffered != null ? endBuffered : endNoBuffer
3959
4296
  );
3960
4297
  }
3961
4298
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
@@ -3967,37 +4304,17 @@ function calculateItemsInView(ctx, params = {}) {
3967
4304
  });
3968
4305
  }
3969
4306
 
3970
- // src/core/checkActualChange.ts
3971
- function checkActualChange(state, dataProp, previousData) {
3972
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
3973
- return true;
3974
- }
3975
- const {
3976
- idCache,
3977
- props: { keyExtractor }
3978
- } = state;
3979
- for (let i = 0; i < dataProp.length; i++) {
3980
- if (dataProp[i] !== previousData[i]) {
3981
- return true;
3982
- }
3983
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
3984
- return true;
3985
- }
3986
- }
3987
- return false;
3988
- }
3989
-
3990
4307
  // src/core/doMaintainScrollAtEnd.ts
3991
4308
  function doMaintainScrollAtEnd(ctx) {
3992
4309
  const state = ctx.state;
3993
4310
  const {
3994
4311
  didContainersLayout,
3995
- isAtEnd,
3996
4312
  pendingNativeMVCPAdjust,
3997
4313
  refScroller,
3998
4314
  props: { maintainScrollAtEnd }
3999
4315
  } = state;
4000
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
4316
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4317
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4001
4318
  if (pendingNativeMVCPAdjust) {
4002
4319
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4003
4320
  return false;
@@ -4010,7 +4327,7 @@ function doMaintainScrollAtEnd(ctx) {
4010
4327
  }
4011
4328
  requestAnimationFrame(() => {
4012
4329
  var _a3;
4013
- if (state.isAtEnd) {
4330
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4014
4331
  state.maintainingScrollAtEnd = true;
4015
4332
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4016
4333
  animated: maintainScrollAtEnd.animated
@@ -4028,68 +4345,22 @@ function doMaintainScrollAtEnd(ctx) {
4028
4345
  return false;
4029
4346
  }
4030
4347
 
4031
- // src/utils/updateAveragesOnDataChange.ts
4032
- function updateAveragesOnDataChange(state, oldData, newData) {
4033
- var _a3;
4034
- const {
4035
- averageSizes,
4036
- sizesKnown,
4037
- indexByKey,
4038
- props: { itemsAreEqual, getItemType, keyExtractor }
4039
- } = state;
4040
- if (!itemsAreEqual || !oldData.length || !newData.length) {
4041
- for (const key in averageSizes) {
4042
- delete averageSizes[key];
4043
- }
4044
- return;
4045
- }
4046
- const itemTypesToPreserve = {};
4047
- const newDataLength = newData.length;
4048
- const oldDataLength = oldData.length;
4049
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
4050
- const newItem = newData[newIndex];
4051
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
4052
- const oldIndex = indexByKey.get(id);
4053
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
4054
- const knownSize = sizesKnown.get(id);
4055
- if (knownSize === void 0) continue;
4056
- const oldItem = oldData[oldIndex];
4057
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
4058
- if (areEqual) {
4059
- const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
4060
- let typeData = itemTypesToPreserve[itemType];
4061
- if (!typeData) {
4062
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
4063
- }
4064
- typeData.totalSize += knownSize;
4065
- typeData.count++;
4066
- }
4067
- }
4068
- }
4069
- for (const key in averageSizes) {
4070
- delete averageSizes[key];
4071
- }
4072
- for (const itemType in itemTypesToPreserve) {
4073
- const { totalSize, count } = itemTypesToPreserve[itemType];
4074
- if (count > 0) {
4075
- averageSizes[itemType] = {
4076
- avg: totalSize / count,
4077
- num: count
4078
- };
4079
- }
4080
- }
4081
- }
4082
-
4083
4348
  // src/core/checkResetContainers.ts
4084
- function checkResetContainers(ctx, dataProp) {
4349
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
4085
4350
  const state = ctx.state;
4086
4351
  const { previousData } = state;
4087
- if (previousData) {
4088
- updateAveragesOnDataChange(state, previousData, dataProp);
4089
- }
4090
4352
  const { maintainScrollAtEnd } = state.props;
4353
+ if (didColumnsChange) {
4354
+ state.sizes.clear();
4355
+ state.sizesKnown.clear();
4356
+ for (const key in state.averageSizes) {
4357
+ delete state.averageSizes[key];
4358
+ }
4359
+ state.minIndexSizeChanged = 0;
4360
+ state.scrollForNextCalculateItemsInView = void 0;
4361
+ }
4091
4362
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
4092
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4363
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
4093
4364
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
4094
4365
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
4095
4366
  state.isEndReached = false;
@@ -4100,6 +4371,53 @@ function checkResetContainers(ctx, dataProp) {
4100
4371
  delete state.previousData;
4101
4372
  }
4102
4373
 
4374
+ // src/core/checkStructuralDataChange.ts
4375
+ function checkStructuralDataChange(state, dataProp, previousData) {
4376
+ var _a3;
4377
+ state.pendingDataComparison = void 0;
4378
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4379
+ return true;
4380
+ }
4381
+ const {
4382
+ idCache,
4383
+ props: { itemsAreEqual, keyExtractor }
4384
+ } = state;
4385
+ let byIndex;
4386
+ for (let i = 0; i < dataProp.length; i++) {
4387
+ if (dataProp[i] === previousData[i]) {
4388
+ continue;
4389
+ }
4390
+ if (!keyExtractor) {
4391
+ if (byIndex) {
4392
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4393
+ }
4394
+ return true;
4395
+ }
4396
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4397
+ const nextKey = keyExtractor(dataProp[i], i);
4398
+ if (previousKey !== nextKey) {
4399
+ if (byIndex) {
4400
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4401
+ }
4402
+ return true;
4403
+ }
4404
+ if (!itemsAreEqual) {
4405
+ if (byIndex) {
4406
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4407
+ }
4408
+ return true;
4409
+ }
4410
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4411
+ byIndex != null ? byIndex : byIndex = [];
4412
+ byIndex[i] = isEqual ? 1 : 2;
4413
+ if (!isEqual) {
4414
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4415
+ return true;
4416
+ }
4417
+ }
4418
+ return false;
4419
+ }
4420
+
4103
4421
  // src/core/doInitialAllocateContainers.ts
4104
4422
  function doInitialAllocateContainers(ctx) {
4105
4423
  var _a3, _b, _c;
@@ -4265,7 +4583,7 @@ function updateScroll(ctx, newScroll, forceUpdate) {
4265
4583
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
4266
4584
  checkThresholds(ctx);
4267
4585
  };
4268
- if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
4586
+ if (Platform.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
4269
4587
  flushSync(runCalculateItems);
4270
4588
  } else {
4271
4589
  runCalculateItems();
@@ -4298,7 +4616,7 @@ function trackInitialScrollNativeProgress(state, newScroll) {
4298
4616
  }
4299
4617
  function shouldDeferPublicOnScroll(state) {
4300
4618
  var _a3;
4301
- return Platform2.OS === "web" && !!state.initialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" && !state.didFinishInitialScroll;
4619
+ return Platform.OS === "web" && !!state.initialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" && !state.didFinishInitialScroll;
4302
4620
  }
4303
4621
  function cloneScrollEvent(event) {
4304
4622
  return {
@@ -4344,6 +4662,7 @@ function onScroll(ctx, event) {
4344
4662
  state.scrollPending = newScroll;
4345
4663
  updateScroll(ctx, newScroll, insetChanged);
4346
4664
  trackInitialScrollNativeProgress(state, newScroll);
4665
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
4347
4666
  if (state.scrollingTo) {
4348
4667
  checkFinishedScroll(ctx);
4349
4668
  }
@@ -4407,10 +4726,47 @@ var ScrollAdjustHandler = class {
4407
4726
  }
4408
4727
  };
4409
4728
 
4729
+ // src/core/updateAnchoredEndSpace.ts
4730
+ function maybeUpdateAnchoredEndSpace(ctx) {
4731
+ var _a3;
4732
+ const state = ctx.state;
4733
+ const anchoredEndSpace = state.props.anchoredEndSpace;
4734
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
4735
+ let nextSize = 0;
4736
+ if (anchoredEndSpace) {
4737
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
4738
+ const { data } = state.props;
4739
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
4740
+ let contentBelowAnchor = 0;
4741
+ const footerSize = ctx.values.get("footerSize") || 0;
4742
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4743
+ for (let index = anchorIndex; index < data.length; index++) {
4744
+ const itemKey = getId(state, index);
4745
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
4746
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
4747
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
4748
+ contentBelowAnchor += effectiveSize;
4749
+ }
4750
+ }
4751
+ contentBelowAnchor += footerSize + stylePaddingBottom;
4752
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4753
+ }
4754
+ }
4755
+ if (previousSize === nextSize) {
4756
+ return nextSize;
4757
+ }
4758
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
4759
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4760
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
4761
+ updateScroll(ctx, state.scroll, true);
4762
+ }
4763
+ return nextSize;
4764
+ }
4765
+
4410
4766
  // src/core/updateItemSize.ts
4411
4767
  function runOrScheduleMVCPRecalculate(ctx) {
4412
4768
  const state = ctx.state;
4413
- if (Platform2.OS === "web") {
4769
+ if (Platform.OS === "web") {
4414
4770
  if (!state.mvcpAnchorLock) {
4415
4771
  if (state.queuedMVCPRecalculate !== void 0) {
4416
4772
  cancelAnimationFrame(state.queuedMVCPRecalculate);
@@ -4490,6 +4846,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4490
4846
  previous: size - diff,
4491
4847
  size
4492
4848
  });
4849
+ maybeUpdateAnchoredEndSpace(ctx);
4493
4850
  }
4494
4851
  if (minIndexSizeChanged !== void 0) {
4495
4852
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -4535,7 +4892,7 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
4535
4892
  const index = indexByKey.get(itemKey);
4536
4893
  const prevSize = getItemSize(ctx, itemKey, index, data[index]);
4537
4894
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
4538
- const size = Platform2.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
4895
+ const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
4539
4896
  const prevSizeKnown = sizesKnown.get(itemKey);
4540
4897
  sizesKnown.set(itemKey, size);
4541
4898
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
@@ -4620,26 +4977,13 @@ function createColumnWrapperStyle(contentContainerStyle) {
4620
4977
  }
4621
4978
  }
4622
4979
 
4623
- // src/utils/hasActiveMVCPAnchorLock.ts
4624
- function hasActiveMVCPAnchorLock(state) {
4625
- const lock = state.mvcpAnchorLock;
4626
- if (!lock) {
4627
- return false;
4628
- }
4629
- if (Date.now() > lock.expiresAt) {
4630
- state.mvcpAnchorLock = void 0;
4631
- return false;
4632
- }
4633
- return true;
4634
- }
4635
-
4636
4980
  // src/utils/createImperativeHandle.ts
4637
4981
  function createImperativeHandle(ctx) {
4638
4982
  const state = ctx.state;
4639
4983
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
4640
4984
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
4641
4985
  let imperativeScrollToken = 0;
4642
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
4986
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
4643
4987
  const runWhenSettled = (token, run) => {
4644
4988
  const startedAt = Date.now();
4645
4989
  let stableFrames = 0;
@@ -4661,9 +5005,10 @@ function createImperativeHandle(ctx) {
4661
5005
  };
4662
5006
  requestAnimationFrame(check);
4663
5007
  };
4664
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5008
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
4665
5009
  var _a3;
4666
5010
  const token = ++imperativeScrollToken;
5011
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
4667
5012
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
4668
5013
  state.pendingScrollResolve = resolve;
4669
5014
  const runNow = () => {
@@ -4678,11 +5023,12 @@ function createImperativeHandle(ctx) {
4678
5023
  resolve();
4679
5024
  }
4680
5025
  };
5026
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
4681
5027
  if (isSettlingAfterDataChange()) {
4682
- runWhenSettled(token, runNow);
4683
- return;
5028
+ runWhenSettled(token, execute);
5029
+ } else {
5030
+ execute();
4684
5031
  }
4685
- runNow();
4686
5032
  });
4687
5033
  const scrollIndexIntoView = (options) => {
4688
5034
  if (state) {
@@ -4739,10 +5085,13 @@ function createImperativeHandle(ctx) {
4739
5085
  },
4740
5086
  end: state.endNoBuffer,
4741
5087
  endBuffered: state.endBuffered,
4742
- isAtEnd: state.isAtEnd,
4743
- isAtStart: state.isAtStart,
5088
+ isAtEnd: peek$(ctx, "isAtEnd"),
5089
+ isAtStart: peek$(ctx, "isAtStart"),
4744
5090
  isEndReached: state.isEndReached,
5091
+ isNearEnd: peek$(ctx, "isNearEnd"),
5092
+ isNearStart: peek$(ctx, "isNearStart"),
4745
5093
  isStartReached: state.isStartReached,
5094
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
4746
5095
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
4747
5096
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
4748
5097
  positionAtIndex: (index) => state.positions[index],
@@ -4789,10 +5138,15 @@ function createImperativeHandle(ctx) {
4789
5138
  }
4790
5139
  return false;
4791
5140
  }),
4792
- scrollToIndex: (params) => runScrollWithPromise(() => {
4793
- scrollToIndex(ctx, params);
4794
- return true;
4795
- }),
5141
+ scrollToIndex: (params) => runScrollWithPromise(
5142
+ () => {
5143
+ scrollToIndex(ctx, params);
5144
+ return true;
5145
+ },
5146
+ {
5147
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5148
+ }
5149
+ ),
4796
5150
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
4797
5151
  const data = state.props.data;
4798
5152
  const index = data.indexOf(item);
@@ -4888,7 +5242,7 @@ function getRenderedItem(ctx, key) {
4888
5242
  item,
4889
5243
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
4890
5244
  };
4891
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React2__namespace.default.createElement(renderItem, itemProps);
5245
+ renderedItem = React2__namespace.default.createElement(renderItem, itemProps);
4892
5246
  }
4893
5247
  return { index, item: data[index], renderedItem };
4894
5248
  }
@@ -5003,7 +5357,7 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
5003
5357
  }
5004
5358
 
5005
5359
  // src/components/LegendList.tsx
5006
- var LegendList = typedMemo2(
5360
+ var LegendList = typedMemo(
5007
5361
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
5008
5362
  typedForwardRef(function LegendList2(props, forwardedRef) {
5009
5363
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
@@ -5022,9 +5376,18 @@ var LegendList = typedMemo2(
5022
5376
  })
5023
5377
  );
5024
5378
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
5025
- var _a3, _b, _c, _d, _e, _f, _g;
5379
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5380
+ const noopOnScroll = React2.useCallback((_event) => {
5381
+ }, []);
5382
+ if (props.recycleItems === void 0) {
5383
+ warnDevOnce(
5384
+ "recycleItems-omitted",
5385
+ "recycleItems was not provided, so it defaults to false. Set recycleItems explicitly to true for better performance with recycling-aware rows, or false to preserve remount-on-reuse behavior."
5386
+ );
5387
+ }
5026
5388
  const {
5027
5389
  alignItemsAtEnd = false,
5390
+ anchoredEndSpace,
5028
5391
  alwaysRender,
5029
5392
  columnWrapperStyle,
5030
5393
  contentContainerStyle: contentContainerStyleProp,
@@ -5090,7 +5453,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5090
5453
  const positionComponentInternal = props.positionComponentInternal;
5091
5454
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
5092
5455
  const {
5093
- childrenMode,
5094
5456
  positionComponentInternal: _positionComponentInternal,
5095
5457
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
5096
5458
  ...restProps
@@ -5154,19 +5516,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5154
5516
  dataVersion,
5155
5517
  keyExtractor
5156
5518
  ]);
5157
- if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
5158
- warnDevOnce(
5159
- "stickyIndices",
5160
- "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
5161
- );
5162
- }
5163
- if (IS_DEV && useWindowScroll && renderScrollComponent) {
5164
- warnDevOnce(
5165
- "useWindowScrollRenderScrollComponent",
5166
- "useWindowScroll is not supported when renderScrollComponent is provided."
5167
- );
5168
- }
5169
- const useWindowScrollResolved = Platform2.OS === "web" && !!useWindowScroll && !renderScrollComponent;
5519
+ const useWindowScrollResolved = Platform.OS === "web" && !!useWindowScroll && !renderScrollComponent;
5170
5520
  const refState = React2.useRef(void 0);
5171
5521
  const hasOverrideItemLayout = !!overrideItemLayout;
5172
5522
  const prevHasOverrideItemLayout = React2.useRef(hasOverrideItemLayout);
@@ -5174,7 +5524,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5174
5524
  if (!ctx.state) {
5175
5525
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
5176
5526
  ctx.state = {
5177
- activeStickyIndex: -1,
5178
5527
  averageSizes: {},
5179
5528
  columnSpans: [],
5180
5529
  columns: [],
@@ -5198,8 +5547,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5198
5547
  kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5199
5548
  previousDataLength: dataProp.length
5200
5549
  } : void 0,
5201
- isAtEnd: false,
5202
- isAtStart: false,
5203
5550
  isEndReached: null,
5204
5551
  isFirst: true,
5205
5552
  isStartReached: null,
@@ -5210,6 +5557,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5210
5557
  minIndexSizeChanged: 0,
5211
5558
  nativeContentInset: void 0,
5212
5559
  nativeMarginTop: 0,
5560
+ pendingDataComparison: void 0,
5213
5561
  pendingNativeMVCPAdjust: void 0,
5214
5562
  positions: [],
5215
5563
  props: {},
@@ -5248,22 +5596,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5248
5596
  }
5249
5597
  const state = refState.current;
5250
5598
  const isFirstLocal = state.isFirst;
5251
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
5599
+ const previousNumColumnsProp = state.props.numColumns;
5600
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
5252
5601
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5253
5602
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5254
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
5603
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
5604
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5605
+ clearPreservedInitialScrollTarget(state);
5606
+ }
5255
5607
  if (didDataChangeLocal) {
5256
5608
  state.dataChangeEpoch += 1;
5257
5609
  state.dataChangeNeedsScrollUpdate = true;
5258
5610
  state.didDataChange = true;
5259
5611
  state.previousData = state.props.data;
5260
5612
  }
5261
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
5613
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
5614
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
5615
+ const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
5262
5616
  state.props = {
5263
5617
  alignItemsAtEnd,
5264
5618
  alwaysRender,
5265
5619
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
5266
5620
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
5621
+ anchoredEndSpace: anchoredEndSpaceResolved,
5267
5622
  animatedProps: animatedPropsInternal,
5268
5623
  contentInset,
5269
5624
  data: dataProp,
@@ -5317,7 +5672,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5317
5672
  setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
5318
5673
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
5319
5674
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
5320
- if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
5675
+ if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
5321
5676
  if (state.scroll < 0) {
5322
5677
  paddingDiff += state.scroll;
5323
5678
  }
@@ -5339,7 +5694,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5339
5694
  return void 0;
5340
5695
  }
5341
5696
  const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);
5342
- return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform2.OS === "web" ? void 0 : resolvedOffset;
5697
+ return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform.OS === "web" ? void 0 : resolvedOffset;
5343
5698
  }, [usesBootstrapInitialScroll]);
5344
5699
  React2.useLayoutEffect(() => {
5345
5700
  initializeInitialScrollOnMount(ctx, {
@@ -5353,16 +5708,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5353
5708
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5354
5709
  refState.current.lastBatchingAction = Date.now();
5355
5710
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
5356
- IS_DEV && !childrenMode && warnDevOnce(
5357
- "keyExtractor",
5358
- "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."
5359
- );
5360
5711
  refState.current.sizes.clear();
5361
5712
  refState.current.positions.length = 0;
5362
5713
  refState.current.totalSize = 0;
5363
5714
  set$(ctx, "totalSize", 0);
5364
5715
  }
5365
5716
  }
5717
+ if (IS_DEV) {
5718
+ useDevChecks(props);
5719
+ }
5366
5720
  React2.useLayoutEffect(() => {
5367
5721
  handleInitialScrollDataChange(ctx, {
5368
5722
  dataLength: dataProp.length,
@@ -5372,6 +5726,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5372
5726
  useBootstrapInitialScroll: usesBootstrapInitialScroll
5373
5727
  });
5374
5728
  }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
5729
+ React2.useLayoutEffect(() => {
5730
+ maybeUpdateAnchoredEndSpace(ctx);
5731
+ }, [
5732
+ ctx,
5733
+ dataProp,
5734
+ dataVersion,
5735
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
5736
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
5737
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
5738
+ numColumnsProp
5739
+ ]);
5375
5740
  const onLayoutFooter = React2.useCallback(
5376
5741
  (layout) => {
5377
5742
  if (!usesBootstrapInitialScroll) {
@@ -5387,14 +5752,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5387
5752
  [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5388
5753
  );
5389
5754
  const onLayoutChange = React2.useCallback(
5390
- (layout) => {
5755
+ (layout, fromLayoutEffect) => {
5756
+ const previousScrollLength = state.scrollLength;
5757
+ const previousOtherAxisSize = state.otherAxisSize;
5391
5758
  handleLayout(ctx, layout, setCanRender);
5759
+ maybeUpdateAnchoredEndSpace(ctx);
5760
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
5761
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
5762
+ handleBootstrapInitialScrollLayoutChange(ctx);
5763
+ }
5392
5764
  if (usesBootstrapInitialScroll) {
5393
5765
  return;
5394
5766
  }
5395
5767
  advanceCurrentInitialScrollSession(ctx);
5396
5768
  },
5397
- [usesBootstrapInitialScroll]
5769
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5398
5770
  );
5399
5771
  const { onLayout } = useOnLayoutSync({
5400
5772
  onLayoutChange,
@@ -5407,6 +5779,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5407
5779
  updateSnapToOffsets(ctx);
5408
5780
  }
5409
5781
  }, [snapToIndices]);
5782
+ React2.useLayoutEffect(
5783
+ () => initializeStateVars(true),
5784
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5785
+ );
5410
5786
  React2.useLayoutEffect(() => {
5411
5787
  const {
5412
5788
  didColumnsChange,
@@ -5416,7 +5792,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5416
5792
  } = state;
5417
5793
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5418
5794
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5419
- checkResetContainers(ctx, data);
5795
+ checkResetContainers(ctx, data, { didColumnsChange });
5796
+ }
5797
+ if (didDataChange) {
5798
+ state.pendingDataComparison = void 0;
5420
5799
  }
5421
5800
  state.didColumnsChange = false;
5422
5801
  state.didDataChange = false;
@@ -5431,10 +5810,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5431
5810
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5432
5811
  }
5433
5812
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5434
- React2.useLayoutEffect(
5435
- () => initializeStateVars(true),
5436
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5437
- );
5438
5813
  React2.useEffect(() => {
5439
5814
  if (!onMetricsChange) {
5440
5815
  return;
@@ -5467,20 +5842,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5467
5842
  state.viewabilityConfigCallbackPairs = viewability;
5468
5843
  state.enableScrollForNextCalculateItemsInView = !viewability;
5469
5844
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
5470
- if (!IsNewArchitecture) {
5471
- useInit(() => {
5845
+ useInit(() => {
5846
+ if (!IsNewArchitecture) {
5472
5847
  doInitialAllocateContainers(ctx);
5473
- });
5474
- }
5848
+ }
5849
+ });
5475
5850
  React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5476
- if (Platform2.OS === "web") {
5477
- React2.useEffect(() => {
5478
- if (usesBootstrapInitialScroll) {
5479
- return;
5480
- }
5481
- advanceCurrentInitialScrollSession(ctx);
5482
- }, [usesBootstrapInitialScroll]);
5483
- }
5851
+ React2.useEffect(() => {
5852
+ if (Platform.OS !== "web" || usesBootstrapInitialScroll) {
5853
+ return;
5854
+ }
5855
+ advanceCurrentInitialScrollSession(ctx);
5856
+ }, [ctx, usesBootstrapInitialScroll]);
5484
5857
  const fns = React2.useMemo(
5485
5858
  () => ({
5486
5859
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5518,7 +5891,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5518
5891
  onScroll: onScrollHandler,
5519
5892
  recycleItems,
5520
5893
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
5521
- progressViewOffset: ((_f = refreshControlElement.props.progressViewOffset) != null ? _f : 0) + stylePaddingTopState
5894
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
5522
5895
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
5523
5896
  ReactNative.RefreshControl,
5524
5897
  {
@@ -5529,7 +5902,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5529
5902
  ),
5530
5903
  refScrollView: combinedRef,
5531
5904
  renderScrollComponent,
5532
- scrollAdjustHandler: (_g = refState.current) == null ? void 0 : _g.scrollAdjustHandler,
5905
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
5533
5906
  scrollEventThrottle: 0,
5534
5907
  snapToIndices,
5535
5908
  stickyHeaderIndices,
@@ -5550,7 +5923,7 @@ if (IS_DEV) {
5550
5923
 
5551
5924
  exports.LegendList = LegendList3;
5552
5925
  exports.typedForwardRef = typedForwardRef;
5553
- exports.typedMemo = typedMemo2;
5926
+ exports.typedMemo = typedMemo;
5554
5927
  exports.useIsLastItem = useIsLastItem;
5555
5928
  exports.useListScrollSize = useListScrollSize;
5556
5929
  exports.useRecyclingEffect = useRecyclingEffect;