@legendapp/list 3.0.0-beta.43 → 3.0.0-beta.45

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/react-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,
@@ -403,25 +367,46 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
403
367
  children,
404
368
  ...rest
405
369
  }) {
406
- const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0] = useArr$([
370
+ const ctx = useStateContext();
371
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0, itemKey, _totalSize = 0] = useArr$([
407
372
  `containerPosition${id}`,
408
373
  "headerSize",
409
- "stylePaddingTop"
374
+ "stylePaddingTop",
375
+ `containerItemKey${id}`,
376
+ "totalSize"
410
377
  ]);
378
+ const pushLimit = React2__namespace.useMemo(
379
+ () => getStickyPushLimit(ctx.state, index, itemKey),
380
+ [ctx.state, index, itemKey, _totalSize]
381
+ );
411
382
  const transform = React2__namespace.useMemo(() => {
412
383
  var _a3;
413
384
  if (animatedScrollY) {
414
385
  const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
415
386
  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
- });
387
+ let stickyPosition;
388
+ if (pushLimit !== void 0) {
389
+ if (pushLimit <= position) {
390
+ stickyPosition = pushLimit;
391
+ } else {
392
+ stickyPosition = animatedScrollY.interpolate({
393
+ extrapolateLeft: "clamp",
394
+ extrapolateRight: "clamp",
395
+ inputRange: [stickyStart, stickyStart + (pushLimit - position)],
396
+ outputRange: [position, pushLimit]
397
+ });
398
+ }
399
+ } else {
400
+ stickyPosition = animatedScrollY.interpolate({
401
+ extrapolateLeft: "clamp",
402
+ extrapolateRight: "extend",
403
+ inputRange: [stickyStart, stickyStart + 5e3],
404
+ outputRange: [position, position + 5e3]
405
+ });
406
+ }
422
407
  return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
423
408
  }
424
- }, [animatedScrollY, headerSize, horizontal, position, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
409
+ }, [animatedScrollY, headerSize, horizontal, position, pushLimit, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
425
410
  const viewStyle = React2__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
426
411
  const renderStickyHeaderBackdrop = React2__namespace.useMemo(() => {
427
412
  if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
@@ -446,6 +431,52 @@ function useInit(cb) {
446
431
  React2.useState(() => cb());
447
432
  }
448
433
 
434
+ // src/utils/helpers.ts
435
+ function isFunction(obj) {
436
+ return typeof obj === "function";
437
+ }
438
+ function isArray(obj) {
439
+ return Array.isArray(obj);
440
+ }
441
+ var warned = /* @__PURE__ */ new Set();
442
+ function warnDevOnce(id, text) {
443
+ if (IS_DEV && !warned.has(id)) {
444
+ warned.add(id);
445
+ console.warn(`[legend-list] ${text}`);
446
+ }
447
+ }
448
+ function roundSize(size) {
449
+ return Math.floor(size * 8) / 8;
450
+ }
451
+ function isNullOrUndefined(value) {
452
+ return value === null || value === void 0;
453
+ }
454
+ function comparatorDefault(a, b) {
455
+ return a - b;
456
+ }
457
+ function getPadding(s, type) {
458
+ var _a3, _b, _c;
459
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
460
+ }
461
+ function extractPadding(style, contentContainerStyle, type) {
462
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
463
+ }
464
+ function findContainerId(ctx, key) {
465
+ var _a3, _b;
466
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
467
+ if (directMatch !== void 0) {
468
+ return directMatch;
469
+ }
470
+ const numContainers = peek$(ctx, "numContainers");
471
+ for (let i = 0; i < numContainers; i++) {
472
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
473
+ if (itemKey === key) {
474
+ return i;
475
+ }
476
+ }
477
+ return -1;
478
+ }
479
+
449
480
  // src/state/ContextContainer.ts
450
481
  var ContextContainer = React2.createContext(null);
451
482
  function useContextContainer() {
@@ -613,11 +644,11 @@ function useOnLayoutSync({
613
644
  const { layout } = event.nativeEvent;
614
645
  if (layout.height !== ((_a3 = lastLayoutRef.current) == null ? void 0 : _a3.height) || layout.width !== ((_b = lastLayoutRef.current) == null ? void 0 : _b.width)) {
615
646
  onLayoutChange(layout, false);
616
- onLayoutProp == null ? void 0 : onLayoutProp(event);
617
647
  lastLayoutRef.current = layout;
618
648
  }
649
+ onLayoutProp == null ? void 0 : onLayoutProp(event);
619
650
  },
620
- [onLayoutChange]
651
+ [onLayoutChange, onLayoutProp]
621
652
  );
622
653
  if (IsNewArchitecture) {
623
654
  React2.useLayoutEffect(() => {
@@ -634,8 +665,6 @@ function useOnLayoutSync({
634
665
  }
635
666
  var Platform2 = ReactNative.Platform;
636
667
  var PlatformAdjustBreaksScroll = Platform2.OS === "android";
637
- var typedForwardRef = React2__namespace.forwardRef;
638
- var typedMemo2 = React2__namespace.memo;
639
668
 
640
669
  // src/utils/isInMVCPActiveMode.native.ts
641
670
  function isInMVCPActiveMode(state) {
@@ -643,7 +672,7 @@ function isInMVCPActiveMode(state) {
643
672
  }
644
673
 
645
674
  // src/components/Container.tsx
646
- var Container = typedMemo2(function Container2({
675
+ var Container = typedMemo(function Container2({
647
676
  id,
648
677
  recycleItems,
649
678
  horizontal,
@@ -687,17 +716,20 @@ var Container = typedMemo2(function Container2({
687
716
  const { columnGap, rowGap, gap } = columnWrapperStyle;
688
717
  if (horizontal) {
689
718
  paddingStyles = {
719
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
690
720
  paddingRight: columnGap || gap || void 0,
691
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
721
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
692
722
  };
693
723
  } else {
694
724
  paddingStyles = {
695
725
  paddingBottom: rowGap || gap || void 0,
696
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
726
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
727
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
697
728
  };
698
729
  }
699
730
  }
700
731
  return horizontal ? {
732
+ boxSizing: paddingStyles ? "border-box" : void 0,
701
733
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
702
734
  height: otherAxisSize,
703
735
  left: 0,
@@ -705,6 +737,7 @@ var Container = typedMemo2(function Container2({
705
737
  top: otherAxisPos,
706
738
  ...paddingStyles || {}
707
739
  } : {
740
+ boxSizing: paddingStyles ? "border-box" : void 0,
708
741
  left: otherAxisPos,
709
742
  position: "absolute",
710
743
  right: numColumns > 1 ? null : 0,
@@ -831,7 +864,6 @@ var Containers = typedMemo(function Containers2({
831
864
  horizontal,
832
865
  recycleItems,
833
866
  ItemSeparatorComponent,
834
- waitForInitialLayout,
835
867
  stickyHeaderConfig,
836
868
  updateItemSize: updateItemSize2,
837
869
  getRenderedItem: getRenderedItem2
@@ -839,16 +871,9 @@ var Containers = typedMemo(function Containers2({
839
871
  const ctx = useStateContext();
840
872
  const columnWrapperStyle = ctx.columnWrapperStyle;
841
873
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
842
- const animSize = useValue$("totalSize", {
843
- // Use a microtask if increasing the size significantly, otherwise use a timeout
844
- // If this is the initial scroll, we don't want to delay because we want to update the size immediately
845
- delay: (value, prevValue) => {
846
- var _a3;
847
- return !((_a3 = ctx.state) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
848
- }
849
- });
850
- const animOpacity = waitForInitialLayout ? useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 }) : void 0;
851
- const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
874
+ const animSize = useValue$("totalSize");
875
+ const otherAxisSize = useValue$("otherAxisSize");
876
+ const animOpacity = useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 });
852
877
  const containers = [];
853
878
  for (let i = 0; i < numContainers; i++) {
854
879
  containers.push(
@@ -892,17 +917,20 @@ var Containers = typedMemo(function Containers2({
892
917
  });
893
918
  var ListComponentScrollView = ReactNative.Animated.ScrollView;
894
919
  function ScrollAdjust() {
920
+ var _a3;
921
+ const ctx = useStateContext();
895
922
  const bias = 1e7;
896
923
  const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
897
924
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0) + bias;
925
+ const horizontal = !!((_a3 = ctx.state) == null ? void 0 : _a3.props.horizontal);
898
926
  return /* @__PURE__ */ React2__namespace.createElement(
899
927
  ReactNative.View,
900
928
  {
901
929
  style: {
902
930
  height: 0,
903
- left: 0,
931
+ left: horizontal ? scrollOffset : 0,
904
932
  position: "absolute",
905
- top: scrollOffset,
933
+ top: horizontal ? 0 : scrollOffset,
906
934
  width: 0
907
935
  }
908
936
  }
@@ -912,14 +940,25 @@ function SnapWrapper({ ScrollComponent, ...props }) {
912
940
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
913
941
  return /* @__PURE__ */ React2__namespace.createElement(ScrollComponent, { ...props, snapToOffsets });
914
942
  }
943
+ function WebAnchoredEndSpace({ horizontal }) {
944
+ const ctx = useStateContext();
945
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
946
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
947
+ if (!shouldRenderAnchoredEndSpace) {
948
+ return null;
949
+ }
950
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
951
+ return /* @__PURE__ */ React2__namespace.createElement("div", { style }, null);
952
+ }
915
953
  var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
916
- const ref = refView != null ? refView : React2.useRef(null);
954
+ const localRef = React2.useRef(null);
955
+ const ref = refView != null ? refView : localRef;
917
956
  const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
918
957
  return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
919
958
  };
920
959
 
921
960
  // src/components/ListComponent.tsx
922
- var ListComponent = typedMemo2(function ListComponent2({
961
+ var ListComponent = typedMemo(function ListComponent2({
923
962
  canRender,
924
963
  style,
925
964
  contentContainerStyle,
@@ -928,7 +967,6 @@ var ListComponent = typedMemo2(function ListComponent2({
928
967
  recycleItems,
929
968
  ItemSeparatorComponent,
930
969
  alignItemsAtEnd: _alignItemsAtEnd,
931
- waitForInitialLayout,
932
970
  onScroll: onScroll2,
933
971
  onLayout,
934
972
  ListHeaderComponent,
@@ -950,12 +988,14 @@ var ListComponent = typedMemo2(function ListComponent2({
950
988
  }) {
951
989
  const ctx = useStateContext();
952
990
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
953
- const ScrollComponent = renderScrollComponent ? React2.useMemo(
954
- () => React2__namespace.forwardRef(
991
+ const ScrollComponent = React2.useMemo(() => {
992
+ if (!renderScrollComponent) {
993
+ return ListComponentScrollView;
994
+ }
995
+ return React2__namespace.forwardRef(
955
996
  (props, ref) => renderScrollComponent({ ...props, ref })
956
- ),
957
- [renderScrollComponent]
958
- ) : ListComponentScrollView;
997
+ );
998
+ }, [renderScrollComponent]);
959
999
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
960
1000
  React2.useLayoutEffect(() => {
961
1001
  if (!ListHeaderComponent) {
@@ -991,7 +1031,7 @@ var ListComponent = typedMemo2(function ListComponent2({
991
1031
  height: "100%"
992
1032
  } : {}
993
1033
  ],
994
- contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1034
+ contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
995
1035
  horizontal,
996
1036
  maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
997
1037
  onLayout,
@@ -1011,170 +1051,229 @@ var ListComponent = typedMemo2(function ListComponent2({
1011
1051
  ItemSeparatorComponent,
1012
1052
  recycleItems,
1013
1053
  stickyHeaderConfig,
1014
- updateItemSize: updateItemSize2,
1015
- waitForInitialLayout
1054
+ updateItemSize: updateItemSize2
1016
1055
  }
1017
1056
  ),
1018
1057
  ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1058
+ Platform2.OS === "web" && /* @__PURE__ */ React2__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
1019
1059
  IS_DEV && ENABLE_DEVMODE
1020
1060
  );
1021
1061
  });
1022
-
1023
- // src/core/calculateOffsetForIndex.ts
1024
- function calculateOffsetForIndex(ctx, index) {
1025
- const state = ctx.state;
1026
- return index !== void 0 ? state.positions[index] || 0 : 0;
1062
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1063
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1064
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1065
+ function useDevChecksImpl(props) {
1066
+ const ctx = useStateContext();
1067
+ const { childrenMode, keyExtractor, renderScrollComponent, stickyHeaderIndices, stickyIndices, useWindowScroll } = props;
1068
+ React2.useEffect(() => {
1069
+ if (stickyIndices && !stickyHeaderIndices) {
1070
+ warnDevOnce(
1071
+ "stickyIndices",
1072
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
1073
+ );
1074
+ }
1075
+ }, [stickyHeaderIndices, stickyIndices]);
1076
+ React2.useEffect(() => {
1077
+ if (useWindowScroll && renderScrollComponent) {
1078
+ warnDevOnce(
1079
+ "useWindowScrollRenderScrollComponent",
1080
+ "useWindowScroll is not supported when renderScrollComponent is provided."
1081
+ );
1082
+ }
1083
+ }, [renderScrollComponent, useWindowScroll]);
1084
+ React2.useEffect(() => {
1085
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1086
+ warnDevOnce(
1087
+ "keyExtractor",
1088
+ "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."
1089
+ );
1090
+ }
1091
+ }, [childrenMode, ctx, keyExtractor]);
1092
+ React2.useEffect(() => {
1093
+ const state = ctx.state;
1094
+ const dataLength = state.props.data.length;
1095
+ const useWindowScrollResolved = state.props.useWindowScroll;
1096
+ if (Platform2.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1097
+ return;
1098
+ }
1099
+ const warnIfUnboundedOuterSize = () => {
1100
+ const readyToRender = peek$(ctx, "readyToRender");
1101
+ const numContainers = peek$(ctx, "numContainers") || 0;
1102
+ const totalSize = peek$(ctx, "totalSize") || 0;
1103
+ const scrollLength = ctx.state.scrollLength || 0;
1104
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1105
+ return;
1106
+ }
1107
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1108
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1109
+ if (rendersAlmostEverything && viewportMatchesContent) {
1110
+ warnDevOnce(
1111
+ "webUnboundedOuterSize",
1112
+ "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."
1113
+ );
1114
+ }
1115
+ };
1116
+ warnIfUnboundedOuterSize();
1117
+ const unsubscribe = [
1118
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1119
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1120
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1121
+ ];
1122
+ return () => {
1123
+ for (const unsub of unsubscribe) {
1124
+ unsub();
1125
+ }
1126
+ };
1127
+ }, [ctx]);
1027
1128
  }
1028
-
1029
- // src/core/getTopOffsetAdjustment.ts
1030
- function getTopOffsetAdjustment(ctx) {
1031
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1129
+ function useDevChecksNoop(_props) {
1032
1130
  }
1131
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1033
1132
 
1034
- // src/utils/getId.ts
1035
- function getId(state, index) {
1036
- const { data, keyExtractor } = state.props;
1037
- if (!data) {
1038
- return "";
1133
+ // src/core/deferredPublicOnScroll.ts
1134
+ function withResolvedContentOffset(state, event, resolvedOffset) {
1135
+ return {
1136
+ ...event,
1137
+ nativeEvent: {
1138
+ ...event.nativeEvent,
1139
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1140
+ }
1141
+ };
1142
+ }
1143
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
1144
+ var _a3, _b, _c, _d;
1145
+ const state = ctx.state;
1146
+ const deferredEvent = state.deferredPublicOnScrollEvent;
1147
+ state.deferredPublicOnScrollEvent = void 0;
1148
+ if (deferredEvent) {
1149
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
1150
+ _c,
1151
+ withResolvedContentOffset(
1152
+ state,
1153
+ deferredEvent,
1154
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
1155
+ )
1156
+ );
1039
1157
  }
1040
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1041
- const id = ret;
1042
- state.idCache[index] = id;
1043
- return id;
1044
1158
  }
1045
1159
 
1046
- // src/core/addTotalSize.ts
1047
- function addTotalSize(ctx, key, add) {
1048
- const state = ctx.state;
1049
- const prevTotalSize = state.totalSize;
1050
- let totalSize = state.totalSize;
1051
- if (key === null) {
1052
- totalSize = add;
1053
- if (state.timeoutSetPaddingTop) {
1054
- clearTimeout(state.timeoutSetPaddingTop);
1055
- state.timeoutSetPaddingTop = void 0;
1056
- }
1057
- } else {
1058
- totalSize += add;
1059
- }
1060
- if (prevTotalSize !== totalSize) {
1061
- if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1062
- state.pendingTotalSize = totalSize;
1063
- } else {
1064
- state.pendingTotalSize = void 0;
1065
- state.totalSize = totalSize;
1066
- set$(ctx, "totalSize", totalSize);
1067
- }
1068
- }
1160
+ // src/core/initialScrollSession.ts
1161
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1162
+ function hasInitialScrollSessionCompletion(completion) {
1163
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
1069
1164
  }
1070
-
1071
- // src/core/setSize.ts
1072
- function setSize(ctx, itemKey, size) {
1073
- const state = ctx.state;
1074
- const { sizes } = state;
1075
- const previousSize = sizes.get(itemKey);
1076
- const diff = previousSize !== void 0 ? size - previousSize : size;
1077
- if (diff !== 0) {
1078
- addTotalSize(ctx, itemKey, diff);
1079
- }
1080
- sizes.set(itemKey, size);
1165
+ function clearInitialScrollSession(state) {
1166
+ state.initialScrollSession = void 0;
1167
+ return void 0;
1081
1168
  }
1082
-
1083
- // src/utils/getItemSize.ts
1084
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1085
- var _a3, _b;
1086
- const state = ctx.state;
1087
- const {
1088
- sizesKnown,
1089
- sizes,
1090
- averageSizes,
1091
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1092
- scrollingTo
1093
- } = state;
1094
- const sizeKnown = sizesKnown.get(key);
1095
- if (sizeKnown !== void 0) {
1096
- return sizeKnown;
1097
- }
1098
- let size;
1099
- if (preferCachedSize) {
1100
- const cachedSize = sizes.get(key);
1101
- if (cachedSize !== void 0) {
1102
- return cachedSize;
1103
- }
1104
- }
1105
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1106
- if (getFixedItemSize) {
1107
- size = getFixedItemSize(data, index, itemType);
1108
- if (size !== void 0) {
1109
- sizesKnown.set(key, size);
1110
- }
1111
- }
1112
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1113
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1114
- if (averageSizeForType !== void 0) {
1115
- size = roundSize(averageSizeForType);
1116
- }
1117
- }
1118
- if (size === void 0) {
1119
- size = sizes.get(key);
1120
- if (size !== void 0) {
1121
- return size;
1122
- }
1123
- }
1124
- if (size === void 0) {
1125
- size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1126
- }
1127
- setSize(ctx, key, size);
1128
- return size;
1169
+ function createInitialScrollSession(options) {
1170
+ const { bootstrap, completion, kind, previousDataLength } = options;
1171
+ return kind === "offset" ? {
1172
+ completion,
1173
+ kind,
1174
+ previousDataLength
1175
+ } : {
1176
+ bootstrap,
1177
+ completion,
1178
+ kind,
1179
+ previousDataLength
1180
+ };
1129
1181
  }
1130
-
1131
- // src/core/calculateOffsetWithOffsetPosition.ts
1132
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1133
- var _a3;
1134
- const state = ctx.state;
1135
- const { index, viewOffset, viewPosition } = params;
1136
- let offset = offsetParam;
1137
- if (viewOffset) {
1138
- offset -= viewOffset;
1182
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
1183
+ var _a4, _b2;
1184
+ if (!state.initialScrollSession) {
1185
+ state.initialScrollSession = createInitialScrollSession({
1186
+ completion: {},
1187
+ kind,
1188
+ previousDataLength: 0
1189
+ });
1190
+ } else if (state.initialScrollSession.kind !== kind) {
1191
+ state.initialScrollSession = createInitialScrollSession({
1192
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
1193
+ completion: state.initialScrollSession.completion,
1194
+ kind,
1195
+ previousDataLength: state.initialScrollSession.previousDataLength
1196
+ });
1139
1197
  }
1140
- if (index !== void 0) {
1141
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1142
- if (topOffsetAdjustment) {
1143
- offset += topOffsetAdjustment;
1198
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
1199
+ return state.initialScrollSession.completion;
1200
+ }
1201
+ var initialScrollCompletion = {
1202
+ didDispatchNativeScroll(state) {
1203
+ var _a3, _b;
1204
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
1205
+ },
1206
+ didRetrySilentInitialScroll(state) {
1207
+ var _a3, _b;
1208
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
1209
+ },
1210
+ markInitialScrollNativeDispatch(state) {
1211
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
1212
+ },
1213
+ markSilentInitialScrollRetry(state) {
1214
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
1215
+ },
1216
+ resetFlags(state) {
1217
+ if (!state.initialScrollSession) {
1218
+ return;
1144
1219
  }
1220
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1221
+ completion.didDispatchNativeScroll = void 0;
1222
+ completion.didRetrySilentInitialScroll = void 0;
1145
1223
  }
1146
- if (viewPosition !== void 0 && index !== void 0) {
1147
- const dataLength = state.props.data.length;
1148
- if (dataLength === 0) {
1149
- return offset;
1150
- }
1151
- const isOutOfBounds = index < 0 || index >= dataLength;
1152
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1153
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1154
- const trailingInset = getContentInsetEnd(state);
1155
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1156
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1157
- const footerSize = peek$(ctx, "footerSize") || 0;
1158
- offset += footerSize;
1224
+ };
1225
+ var initialScrollWatchdog = {
1226
+ clear(state) {
1227
+ initialScrollWatchdog.set(state, void 0);
1228
+ },
1229
+ didObserveProgress(newScroll, watchdog) {
1230
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1231
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1232
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1233
+ },
1234
+ get(state) {
1235
+ var _a3, _b;
1236
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1237
+ },
1238
+ hasNonZeroTargetOffset(targetOffset) {
1239
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1240
+ },
1241
+ isAtZeroTargetOffset(targetOffset) {
1242
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1243
+ },
1244
+ set(state, watchdog) {
1245
+ var _a3, _b;
1246
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1247
+ return;
1159
1248
  }
1249
+ const completion = ensureInitialScrollSessionCompletion(state);
1250
+ completion.watchdog = watchdog ? {
1251
+ startScroll: watchdog.startScroll,
1252
+ targetOffset: watchdog.targetOffset
1253
+ } : void 0;
1160
1254
  }
1161
- return offset;
1162
- }
1163
-
1164
- // src/core/clampScrollOffset.ts
1165
- function clampScrollOffset(ctx, offset, scrollTarget) {
1166
- const state = ctx.state;
1167
- const contentSize = getContentSize(ctx);
1168
- let clampedOffset = offset;
1169
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1170
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1171
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1172
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1173
- const maxOffset = baseMaxOffset + extraEndOffset;
1174
- clampedOffset = Math.min(offset, maxOffset);
1175
- }
1176
- clampedOffset = Math.max(0, clampedOffset);
1177
- return clampedOffset;
1255
+ };
1256
+ function setInitialScrollSession(state, options = {}) {
1257
+ var _a3, _b, _c;
1258
+ const existingSession = state.initialScrollSession;
1259
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1260
+ const completion = existingSession == null ? void 0 : existingSession.completion;
1261
+ const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1262
+ const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1263
+ if (!kind) {
1264
+ return clearInitialScrollSession(state);
1265
+ }
1266
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1267
+ return clearInitialScrollSession(state);
1268
+ }
1269
+ const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1270
+ state.initialScrollSession = createInitialScrollSession({
1271
+ bootstrap,
1272
+ completion,
1273
+ kind,
1274
+ previousDataLength
1275
+ });
1276
+ return state.initialScrollSession;
1178
1277
  }
1179
1278
 
1180
1279
  // src/utils/checkThreshold.ts
@@ -1215,11 +1314,16 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1215
1314
  return true;
1216
1315
  };
1217
1316
 
1317
+ // src/utils/hasActiveInitialScroll.ts
1318
+ function hasActiveInitialScroll(state) {
1319
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1320
+ }
1321
+
1218
1322
  // src/utils/checkAtBottom.ts
1219
1323
  function checkAtBottom(ctx) {
1220
1324
  var _a3;
1221
1325
  const state = ctx.state;
1222
- if (!state || state.initialScroll) {
1326
+ if (!state) {
1223
1327
  return;
1224
1328
  }
1225
1329
  const {
@@ -1229,42 +1333,48 @@ function checkAtBottom(ctx) {
1229
1333
  maintainingScrollAtEnd,
1230
1334
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1231
1335
  } = state;
1232
- if (state.initialScroll) {
1233
- return;
1234
- }
1235
1336
  const contentSize = getContentSize(ctx);
1236
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1237
- const insetEnd = getContentInsetEnd(state);
1337
+ if (contentSize > 0 && queuedInitialLayout) {
1338
+ const insetEnd = getContentInsetEnd(ctx);
1238
1339
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1239
1340
  const isContentLess = contentSize < scrollLength;
1240
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1241
- state.isEndReached = checkThreshold(
1242
- distanceFromEnd,
1243
- isContentLess,
1244
- onEndReachedThreshold * scrollLength,
1245
- state.isEndReached,
1246
- state.endReachedSnapshot,
1247
- {
1248
- contentSize,
1249
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1250
- scrollPosition: scroll
1251
- },
1252
- (distance) => {
1253
- var _a4, _b;
1254
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1255
- },
1256
- (snapshot) => {
1257
- state.endReachedSnapshot = snapshot;
1258
- },
1259
- true
1341
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1342
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1343
+ set$(
1344
+ ctx,
1345
+ "isWithinMaintainScrollAtEndThreshold",
1346
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1260
1347
  );
1348
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1349
+ if (!shouldSkipThresholdChecks) {
1350
+ state.isEndReached = checkThreshold(
1351
+ distanceFromEnd,
1352
+ isContentLess,
1353
+ onEndReachedThreshold * scrollLength,
1354
+ state.isEndReached,
1355
+ state.endReachedSnapshot,
1356
+ {
1357
+ contentSize,
1358
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1359
+ scrollPosition: scroll
1360
+ },
1361
+ (distance) => {
1362
+ var _a4, _b;
1363
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1364
+ },
1365
+ (snapshot) => {
1366
+ state.endReachedSnapshot = snapshot;
1367
+ },
1368
+ true
1369
+ );
1370
+ }
1261
1371
  }
1262
1372
  }
1263
1373
 
1264
1374
  // src/utils/checkAtTop.ts
1265
1375
  function checkAtTop(ctx) {
1266
1376
  const state = ctx == null ? void 0 : ctx.state;
1267
- if (!state || state.initialScroll || state.scrollingTo) {
1377
+ if (!state) {
1268
1378
  return;
1269
1379
  }
1270
1380
  const {
@@ -1287,31 +1397,33 @@ function checkAtTop(ctx) {
1287
1397
  state.startReachedSnapshot = void 0;
1288
1398
  state.startReachedSnapshotDataChangeEpoch = void 0;
1289
1399
  }
1290
- state.isAtStart = scroll <= 0;
1291
- if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1292
- return;
1400
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1401
+ set$(ctx, "isNearStart", scroll <= threshold);
1402
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1403
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1404
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1405
+ state.isStartReached = checkThreshold(
1406
+ scroll,
1407
+ false,
1408
+ threshold,
1409
+ state.isStartReached,
1410
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1411
+ {
1412
+ contentSize: totalSize,
1413
+ dataLength,
1414
+ scrollPosition: scroll
1415
+ },
1416
+ (distance) => {
1417
+ var _a3, _b;
1418
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1419
+ },
1420
+ (snapshot) => {
1421
+ state.startReachedSnapshot = snapshot;
1422
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1423
+ },
1424
+ allowReentryOnDataChange
1425
+ );
1293
1426
  }
1294
- state.isStartReached = checkThreshold(
1295
- scroll,
1296
- false,
1297
- threshold,
1298
- state.isStartReached,
1299
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1300
- {
1301
- contentSize: totalSize,
1302
- dataLength,
1303
- scrollPosition: scroll
1304
- },
1305
- (distance) => {
1306
- var _a3, _b;
1307
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1308
- },
1309
- (snapshot) => {
1310
- state.startReachedSnapshot = snapshot;
1311
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1312
- },
1313
- allowReentryOnDataChange
1314
- );
1315
1427
  }
1316
1428
 
1317
1429
  // src/utils/checkThresholds.ts
@@ -1320,6 +1432,16 @@ function checkThresholds(ctx) {
1320
1432
  checkAtTop(ctx);
1321
1433
  }
1322
1434
 
1435
+ // src/core/recalculateSettledScroll.ts
1436
+ function recalculateSettledScroll(ctx) {
1437
+ var _a3, _b;
1438
+ const state = ctx.state;
1439
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1440
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1441
+ }
1442
+ checkThresholds(ctx);
1443
+ }
1444
+
1323
1445
  // src/utils/setInitialRenderState.ts
1324
1446
  function setInitialRenderState(ctx, {
1325
1447
  didLayout,
@@ -1345,214 +1467,1361 @@ function setInitialRenderState(ctx, {
1345
1467
  }
1346
1468
  }
1347
1469
 
1348
- // src/core/finishScrollTo.ts
1349
- function finishScrollTo(ctx) {
1350
- var _a3, _b;
1470
+ // src/core/finishInitialScroll.ts
1471
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1472
+ function syncInitialScrollOffset(state, offset) {
1473
+ state.scroll = offset;
1474
+ state.scrollPending = offset;
1475
+ state.scrollPrev = offset;
1476
+ }
1477
+ function clearPreservedInitialScrollTargetTimeout(state) {
1478
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
1479
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
1480
+ state.timeoutPreservedInitialScrollClear = void 0;
1481
+ }
1482
+ }
1483
+ function clearPreservedInitialScrollTarget(state) {
1484
+ clearPreservedInitialScrollTargetTimeout(state);
1485
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1486
+ state.initialScroll = void 0;
1487
+ setInitialScrollSession(state);
1488
+ }
1489
+ function finishInitialScroll(ctx, options) {
1490
+ var _a3, _b, _c;
1351
1491
  const state = ctx.state;
1352
- if (state == null ? void 0 : state.scrollingTo) {
1353
- const resolvePendingScroll = state.pendingScrollResolve;
1354
- state.pendingScrollResolve = void 0;
1355
- const scrollingTo = state.scrollingTo;
1356
- state.scrollHistory.length = 0;
1357
- state.initialScroll = void 0;
1358
- state.initialScrollUsesOffset = false;
1359
- state.initialAnchor = void 0;
1360
- state.initialNativeScrollWatchdog = void 0;
1361
- state.scrollingTo = void 0;
1362
- if (state.pendingTotalSize !== void 0) {
1363
- addTotalSize(ctx, null, state.pendingTotalSize);
1364
- }
1365
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1366
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1492
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1493
+ syncInitialScrollOffset(state, options.resolvedOffset);
1494
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1495
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1496
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1497
+ syncInitialScrollOffset(state, observedOffset);
1498
+ }
1499
+ }
1500
+ const complete = () => {
1501
+ var _a4, _b2, _c2, _d, _e;
1502
+ const shouldReleaseDeferredPublicOnScroll = Platform2.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1503
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1504
+ initialScrollWatchdog.clear(state);
1505
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1506
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1507
+ setInitialScrollSession(state);
1508
+ clearPreservedInitialScrollTargetTimeout(state);
1509
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1510
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1511
+ var _a5;
1512
+ state.timeoutPreservedInitialScrollClear = void 0;
1513
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1514
+ return;
1515
+ }
1516
+ clearPreservedInitialScrollTarget(state);
1517
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
1518
+ }
1519
+ } else {
1520
+ clearPreservedInitialScrollTarget(state);
1367
1521
  }
1368
- if (PlatformAdjustBreaksScroll) {
1369
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1522
+ if (options == null ? void 0 : options.recalculateItems) {
1523
+ recalculateSettledScroll(ctx);
1370
1524
  }
1371
1525
  setInitialRenderState(ctx, { didInitialScroll: true });
1372
- checkThresholds(ctx);
1373
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1374
- }
1375
- }
1376
-
1377
- // src/core/checkFinishedScroll.ts
1378
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1379
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1380
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1381
- function checkFinishedScroll(ctx) {
1382
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1383
- }
1384
- function checkFinishedScrollFrame(ctx) {
1385
- var _a3;
1386
- const scrollingTo = ctx.state.scrollingTo;
1387
- if (scrollingTo) {
1388
- const { state } = ctx;
1389
- state.animFrameCheckFinishedScroll = void 0;
1390
- const scroll = state.scrollPending;
1391
- const adjust = state.scrollAdjustHandler.getAdjust();
1392
- const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
1393
- const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
1394
- const diff1 = Math.abs(scroll - clampedTargetOffset);
1395
- const diff2 = Math.abs(diff1 - adjust);
1396
- const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
1397
- const isAtTarget = diff1 < 1 || !scrollingTo.animated && diff2 < 1;
1398
- if (isNotOverscrolled && isAtTarget) {
1399
- finishScrollTo(ctx);
1526
+ if (shouldReleaseDeferredPublicOnScroll) {
1527
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1400
1528
  }
1529
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
1530
+ };
1531
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
1532
+ requestAnimationFrame(complete);
1533
+ return;
1401
1534
  }
1535
+ complete();
1402
1536
  }
1403
- function checkFinishedScrollFallback(ctx) {
1537
+
1538
+ // src/core/calculateOffsetForIndex.ts
1539
+ function calculateOffsetForIndex(ctx, index) {
1404
1540
  const state = ctx.state;
1405
- const scrollingTo = state.scrollingTo;
1406
- const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1407
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget || !state.didContainersLayout;
1408
- state.timeoutCheckFinishedScrollFallback = setTimeout(
1409
- () => {
1410
- let numChecks = 0;
1411
- const checkHasScrolled = () => {
1412
- var _a3, _b;
1413
- state.timeoutCheckFinishedScrollFallback = void 0;
1414
- const isStillScrollingTo = state.scrollingTo;
1415
- if (isStillScrollingTo) {
1416
- numChecks++;
1417
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1418
- const maxChecks = isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1419
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1420
- if (shouldFinishZeroTarget || state.hasScrolled || numChecks > maxChecks) {
1421
- finishScrollTo(ctx);
1422
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
1423
- const targetOffset = (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.targetOffset) != null ? _b : state.scrollPending;
1424
- const scroller = state.refScroller.current;
1425
- if (scroller) {
1426
- scroller.scrollTo({
1427
- animated: false,
1428
- x: state.props.horizontal ? targetOffset : 0,
1429
- y: state.props.horizontal ? 0 : targetOffset
1430
- });
1431
- }
1432
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1433
- } else {
1434
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1435
- }
1436
- }
1437
- };
1438
- checkHasScrolled();
1439
- },
1440
- slowTimeout ? 500 : 100
1441
- );
1541
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1442
1542
  }
1443
- function isNativeInitialNonZeroTarget(state) {
1444
- return !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && state.initialNativeScrollWatchdog.targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1543
+
1544
+ // src/core/getTopOffsetAdjustment.ts
1545
+ function getTopOffsetAdjustment(ctx) {
1546
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1445
1547
  }
1446
- function shouldFinishInitialZeroTargetScroll(ctx) {
1447
- var _a3;
1448
- const { state } = ctx;
1449
- return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
1548
+
1549
+ // src/utils/getId.ts
1550
+ function getId(state, index) {
1551
+ const { data, keyExtractor } = state.props;
1552
+ if (!data) {
1553
+ return "";
1554
+ }
1555
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1556
+ const id = ret;
1557
+ state.idCache[index] = id;
1558
+ return id;
1450
1559
  }
1451
1560
 
1452
- // src/core/doScrollTo.native.ts
1453
- function doScrollTo(ctx, params) {
1561
+ // src/core/addTotalSize.ts
1562
+ function addTotalSize(ctx, key, add) {
1454
1563
  const state = ctx.state;
1455
- const { animated, horizontal, offset } = params;
1456
- const isAnimated = !!animated;
1457
- const { refScroller } = state;
1458
- const scroller = refScroller.current;
1459
- if (!scroller) {
1460
- return;
1461
- }
1462
- scroller.scrollTo({
1463
- animated: isAnimated,
1564
+ const prevTotalSize = state.totalSize;
1565
+ let totalSize = state.totalSize;
1566
+ if (key === null) {
1567
+ totalSize = add;
1568
+ if (state.timeoutSetPaddingTop) {
1569
+ clearTimeout(state.timeoutSetPaddingTop);
1570
+ state.timeoutSetPaddingTop = void 0;
1571
+ }
1572
+ } else {
1573
+ totalSize += add;
1574
+ }
1575
+ if (prevTotalSize !== totalSize) {
1576
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1577
+ state.pendingTotalSize = totalSize;
1578
+ } else {
1579
+ state.pendingTotalSize = void 0;
1580
+ state.totalSize = totalSize;
1581
+ set$(ctx, "totalSize", totalSize);
1582
+ }
1583
+ }
1584
+ }
1585
+
1586
+ // src/core/setSize.ts
1587
+ function setSize(ctx, itemKey, size) {
1588
+ const state = ctx.state;
1589
+ const { sizes } = state;
1590
+ const previousSize = sizes.get(itemKey);
1591
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1592
+ if (diff !== 0) {
1593
+ addTotalSize(ctx, itemKey, diff);
1594
+ }
1595
+ sizes.set(itemKey, size);
1596
+ }
1597
+
1598
+ // src/utils/getItemSize.ts
1599
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1600
+ var _a3, _b, _c;
1601
+ const state = ctx.state;
1602
+ const {
1603
+ sizesKnown,
1604
+ sizes,
1605
+ averageSizes,
1606
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1607
+ scrollingTo
1608
+ } = state;
1609
+ const sizeKnown = sizesKnown.get(key);
1610
+ if (sizeKnown !== void 0) {
1611
+ return sizeKnown;
1612
+ }
1613
+ let size;
1614
+ const renderedSize = sizes.get(key);
1615
+ if (preferCachedSize) {
1616
+ if (renderedSize !== void 0) {
1617
+ return renderedSize;
1618
+ }
1619
+ }
1620
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1621
+ if (getFixedItemSize) {
1622
+ size = getFixedItemSize(data, index, itemType);
1623
+ if (size !== void 0) {
1624
+ sizesKnown.set(key, size);
1625
+ }
1626
+ }
1627
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1628
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1629
+ if (averageSizeForType !== void 0) {
1630
+ size = roundSize(averageSizeForType);
1631
+ }
1632
+ }
1633
+ if (size === void 0 && renderedSize !== void 0) {
1634
+ return renderedSize;
1635
+ }
1636
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
1637
+ const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
1638
+ if (averageSizeForType !== void 0) {
1639
+ size = roundSize(averageSizeForType);
1640
+ }
1641
+ }
1642
+ if (size === void 0) {
1643
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1644
+ }
1645
+ setSize(ctx, key, size);
1646
+ return size;
1647
+ }
1648
+ function getItemSizeAtIndex(ctx, index) {
1649
+ if (index === void 0 || index < 0) {
1650
+ return void 0;
1651
+ }
1652
+ const targetId = getId(ctx.state, index);
1653
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1654
+ }
1655
+
1656
+ // src/core/calculateOffsetWithOffsetPosition.ts
1657
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1658
+ var _a3;
1659
+ const state = ctx.state;
1660
+ const { index, viewOffset, viewPosition } = params;
1661
+ let offset = offsetParam;
1662
+ if (viewOffset) {
1663
+ offset -= viewOffset;
1664
+ }
1665
+ if (index !== void 0) {
1666
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1667
+ if (topOffsetAdjustment) {
1668
+ offset += topOffsetAdjustment;
1669
+ }
1670
+ }
1671
+ if (viewPosition !== void 0 && index !== void 0) {
1672
+ const dataLength = state.props.data.length;
1673
+ if (dataLength === 0) {
1674
+ return offset;
1675
+ }
1676
+ const isOutOfBounds = index < 0 || index >= dataLength;
1677
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1678
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1679
+ const trailingInset = getContentInsetEnd(ctx);
1680
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1681
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1682
+ const footerSize = peek$(ctx, "footerSize") || 0;
1683
+ offset += footerSize;
1684
+ }
1685
+ }
1686
+ return offset;
1687
+ }
1688
+
1689
+ // src/core/clampScrollOffset.ts
1690
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1691
+ const state = ctx.state;
1692
+ const contentSize = getContentSize(ctx);
1693
+ let clampedOffset = offset;
1694
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1695
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1696
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1697
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1698
+ const maxOffset = baseMaxOffset + extraEndOffset;
1699
+ clampedOffset = Math.min(offset, maxOffset);
1700
+ }
1701
+ clampedOffset = Math.max(0, clampedOffset);
1702
+ return clampedOffset;
1703
+ }
1704
+
1705
+ // src/core/finishScrollTo.ts
1706
+ function finishScrollTo(ctx) {
1707
+ var _a3, _b;
1708
+ const state = ctx.state;
1709
+ if (state == null ? void 0 : state.scrollingTo) {
1710
+ const resolvePendingScroll = state.pendingScrollResolve;
1711
+ state.pendingScrollResolve = void 0;
1712
+ const scrollingTo = state.scrollingTo;
1713
+ state.scrollHistory.length = 0;
1714
+ state.scrollingTo = void 0;
1715
+ if (state.pendingTotalSize !== void 0) {
1716
+ addTotalSize(ctx, null, state.pendingTotalSize);
1717
+ }
1718
+ if (PlatformAdjustBreaksScroll) {
1719
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1720
+ }
1721
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
1722
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1723
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1724
+ finishInitialScroll(ctx, {
1725
+ onFinished: () => {
1726
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1727
+ },
1728
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1729
+ recalculateItems: true,
1730
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1731
+ syncObservedOffset: isOffsetSession,
1732
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1733
+ });
1734
+ return;
1735
+ }
1736
+ recalculateSettledScroll(ctx);
1737
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1738
+ }
1739
+ }
1740
+
1741
+ // src/core/checkFinishedScroll.ts
1742
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1743
+ var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
1744
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1745
+ var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
1746
+ var SILENT_INITIAL_SCROLL_TARGET_EPSILON = 1;
1747
+ function checkFinishedScroll(ctx, options) {
1748
+ const scrollingTo = ctx.state.scrollingTo;
1749
+ if (options == null ? void 0 : options.onlyIfAligned) {
1750
+ if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
1751
+ return;
1752
+ }
1753
+ if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
1754
+ return;
1755
+ }
1756
+ }
1757
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1758
+ }
1759
+ function hasScrollCompletionOwnership(state, options) {
1760
+ const { clampedTargetOffset, scrollingTo } = options;
1761
+ return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
1762
+ }
1763
+ function isSilentInitialDispatch(state, scrollingTo) {
1764
+ return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
1765
+ }
1766
+ function getInitialScrollWatchdogTargetOffset(state) {
1767
+ var _a3;
1768
+ return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
1769
+ }
1770
+ function isNativeInitialNonZeroTarget(state) {
1771
+ const targetOffset = getInitialScrollWatchdogTargetOffset(state);
1772
+ return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
1773
+ }
1774
+ function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
1775
+ var _a3, _b;
1776
+ if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
1777
+ return false;
1778
+ }
1779
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
1780
+ return false;
1781
+ }
1782
+ const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
1783
+ if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
1784
+ return false;
1785
+ }
1786
+ if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
1787
+ return false;
1788
+ }
1789
+ return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
1790
+ }
1791
+ function shouldFinishInitialZeroTargetScroll(ctx) {
1792
+ var _a3;
1793
+ const { state } = ctx;
1794
+ return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
1795
+ }
1796
+ function getResolvedScrollCompletionState(ctx, scrollingTo) {
1797
+ var _a3;
1798
+ const { state } = ctx;
1799
+ const scroll = state.scrollPending;
1800
+ const adjust = state.scrollAdjustHandler.getAdjust();
1801
+ const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
1802
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
1803
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
1804
+ const diff2 = Math.abs(diff1 - adjust);
1805
+ return {
1806
+ clampedTargetOffset,
1807
+ isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || !scrollingTo.animated && diff2 < 1)
1808
+ };
1809
+ }
1810
+ function checkFinishedScrollFrame(ctx) {
1811
+ const scrollingTo = ctx.state.scrollingTo;
1812
+ if (!scrollingTo) {
1813
+ return;
1814
+ }
1815
+ const { state } = ctx;
1816
+ state.animFrameCheckFinishedScroll = void 0;
1817
+ const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
1818
+ if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
1819
+ clampedTargetOffset: completionState.clampedTargetOffset,
1820
+ scrollingTo
1821
+ })) {
1822
+ finishScrollTo(ctx);
1823
+ }
1824
+ }
1825
+ function scrollToFallbackOffset(ctx, offset) {
1826
+ var _a3;
1827
+ (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
1828
+ animated: false,
1829
+ x: ctx.state.props.horizontal ? offset : 0,
1830
+ y: ctx.state.props.horizontal ? 0 : offset
1831
+ });
1832
+ }
1833
+ function checkFinishedScrollFallback(ctx) {
1834
+ const state = ctx.state;
1835
+ const scrollingTo = state.scrollingTo;
1836
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1837
+ const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
1838
+ const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
1839
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
1840
+ const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
1841
+ state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
1842
+ let numChecks = 0;
1843
+ const scheduleFallbackCheck = (delay) => {
1844
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
1845
+ };
1846
+ const checkHasScrolled = () => {
1847
+ var _a3, _b, _c;
1848
+ state.timeoutCheckFinishedScrollFallback = void 0;
1849
+ const isStillScrollingTo = state.scrollingTo;
1850
+ if (isStillScrollingTo) {
1851
+ numChecks++;
1852
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1853
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1854
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1855
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
1856
+ state,
1857
+ isStillScrollingTo
1858
+ );
1859
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1860
+ const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1861
+ const shouldRetrySilentInitialNativeScroll = Platform2.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1862
+ if (shouldRetrySilentInitialNativeScroll) {
1863
+ const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1864
+ const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
1865
+ initialScrollCompletion.markSilentInitialScrollRetry(state);
1866
+ scrollToFallbackOffset(ctx, jiggleOffset);
1867
+ requestAnimationFrame(() => {
1868
+ scrollToFallbackOffset(ctx, targetOffset);
1869
+ });
1870
+ scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1871
+ } else if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1872
+ finishScrollTo(ctx);
1873
+ } else if (isNativeInitialPending && numChecks <= maxChecks) {
1874
+ const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
1875
+ scrollToFallbackOffset(ctx, targetOffset);
1876
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1877
+ } else {
1878
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1879
+ }
1880
+ }
1881
+ };
1882
+ checkHasScrolled();
1883
+ }, initialDelay);
1884
+ }
1885
+
1886
+ // src/core/doScrollTo.native.ts
1887
+ function doScrollTo(ctx, params) {
1888
+ const state = ctx.state;
1889
+ const { animated, horizontal, isInitialScroll, offset } = params;
1890
+ const isAnimated = !!animated;
1891
+ const { refScroller } = state;
1892
+ const scroller = refScroller.current;
1893
+ if (!scroller) {
1894
+ return;
1895
+ }
1896
+ scroller.scrollTo({
1897
+ animated: isAnimated,
1464
1898
  x: horizontal ? offset : 0,
1465
1899
  y: horizontal ? 0 : offset
1466
1900
  });
1901
+ if (isInitialScroll) {
1902
+ initialScrollCompletion.markInitialScrollNativeDispatch(state);
1903
+ }
1467
1904
  if (!isAnimated) {
1468
1905
  state.scroll = offset;
1469
1906
  checkFinishedScrollFallback(ctx);
1470
1907
  }
1471
1908
  }
1472
-
1473
- // src/core/scrollTo.ts
1474
- var WATCHDOG_OFFSET_EPSILON = 1;
1475
- function scrollTo(ctx, params) {
1909
+
1910
+ // src/core/scrollTo.ts
1911
+ function getAverageSizeSnapshot(state) {
1912
+ if (Object.keys(state.averageSizes).length === 0) {
1913
+ return void 0;
1914
+ }
1915
+ const snapshot = {};
1916
+ for (const itemType in state.averageSizes) {
1917
+ const averages = state.averageSizes[itemType];
1918
+ snapshot[itemType] = averages.avg;
1919
+ }
1920
+ return snapshot;
1921
+ }
1922
+ function syncInitialScrollNativeWatchdog(state, options) {
1923
+ var _a3;
1924
+ const { isInitialScroll, requestedOffset, targetOffset } = options;
1925
+ const existingWatchdog = initialScrollWatchdog.get(state);
1926
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!existingWatchdog) && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
1927
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!existingWatchdog && initialScrollWatchdog.isAtZeroTargetOffset(requestedOffset);
1928
+ if (shouldWatchInitialNativeScroll) {
1929
+ state.hasScrolled = false;
1930
+ initialScrollWatchdog.set(state, {
1931
+ startScroll: (_a3 = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _a3 : state.scroll,
1932
+ targetOffset
1933
+ });
1934
+ return;
1935
+ }
1936
+ if (shouldClearInitialNativeScrollWatchdog) {
1937
+ initialScrollWatchdog.clear(state);
1938
+ }
1939
+ }
1940
+ function scrollTo(ctx, params) {
1941
+ var _a3;
1942
+ const state = ctx.state;
1943
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1944
+ const {
1945
+ animated,
1946
+ isInitialScroll,
1947
+ offset: scrollTargetOffset,
1948
+ precomputedWithViewOffset,
1949
+ waitForInitialScrollCompletionFrame
1950
+ } = scrollTarget;
1951
+ const {
1952
+ props: { horizontal }
1953
+ } = state;
1954
+ if (state.animFrameCheckFinishedScroll) {
1955
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1956
+ }
1957
+ if (state.timeoutCheckFinishedScrollFallback) {
1958
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1959
+ }
1960
+ const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1961
+ const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1962
+ const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
1963
+ const offset = shouldPreserveRawInitialOffsetRequest ? requestedOffset : targetOffset;
1964
+ state.scrollHistory.length = 0;
1965
+ if (!noScrollingTo) {
1966
+ if (isInitialScroll) {
1967
+ initialScrollCompletion.resetFlags(state);
1968
+ }
1969
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
1970
+ state.scrollingTo = {
1971
+ ...scrollTarget,
1972
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
1973
+ targetOffset,
1974
+ waitForInitialScrollCompletionFrame
1975
+ };
1976
+ }
1977
+ state.scrollPending = targetOffset;
1978
+ syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
1979
+ if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1980
+ doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1981
+ } else {
1982
+ state.scroll = offset;
1983
+ }
1984
+ }
1985
+
1986
+ // src/core/scrollToIndex.ts
1987
+ function clampScrollIndex(index, dataLength) {
1988
+ if (dataLength <= 0) {
1989
+ return -1;
1990
+ }
1991
+ if (index >= dataLength) {
1992
+ return dataLength - 1;
1993
+ }
1994
+ if (index < 0) {
1995
+ return 0;
1996
+ }
1997
+ return index;
1998
+ }
1999
+ function scrollToIndex(ctx, {
2000
+ index,
2001
+ viewOffset = 0,
2002
+ animated = true,
2003
+ forceScroll,
2004
+ isInitialScroll,
2005
+ viewPosition
2006
+ }) {
2007
+ const state = ctx.state;
2008
+ const { data } = state.props;
2009
+ index = clampScrollIndex(index, data.length);
2010
+ const itemSize = getItemSizeAtIndex(ctx, index);
2011
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2012
+ const isLast = index === data.length - 1;
2013
+ if (isLast && viewPosition === void 0) {
2014
+ viewPosition = 1;
2015
+ }
2016
+ state.scrollForNextCalculateItemsInView = void 0;
2017
+ scrollTo(ctx, {
2018
+ animated,
2019
+ forceScroll,
2020
+ index,
2021
+ isInitialScroll,
2022
+ itemSize,
2023
+ offset: firstIndexOffset,
2024
+ viewOffset,
2025
+ viewPosition: viewPosition != null ? viewPosition : 0
2026
+ });
2027
+ }
2028
+
2029
+ // src/core/initialScroll.ts
2030
+ function dispatchInitialScroll(ctx, params) {
2031
+ const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2032
+ const requestedIndex = target.index;
2033
+ const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2034
+ const itemSize = getItemSizeAtIndex(ctx, index);
2035
+ scrollTo(ctx, {
2036
+ animated: false,
2037
+ forceScroll,
2038
+ index: index !== void 0 && index >= 0 ? index : void 0,
2039
+ isInitialScroll: true,
2040
+ itemSize,
2041
+ offset: resolvedOffset,
2042
+ precomputedWithViewOffset: true,
2043
+ viewOffset: target.viewOffset,
2044
+ viewPosition: target.viewPosition,
2045
+ waitForInitialScrollCompletionFrame: waitForCompletionFrame
2046
+ });
2047
+ }
2048
+ function setInitialScrollTarget(state, target, options) {
2049
+ var _a3;
2050
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2051
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2052
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2053
+ state.timeoutPreservedInitialScrollClear = void 0;
2054
+ }
2055
+ state.initialScroll = target;
2056
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2057
+ state.didFinishInitialScroll = false;
2058
+ }
2059
+ setInitialScrollSession(state, {
2060
+ kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2061
+ });
2062
+ }
2063
+ function resolveInitialScrollOffset(ctx, initialScroll) {
2064
+ var _a3, _b;
2065
+ const state = ctx.state;
2066
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2067
+ return (_b = initialScroll.contentOffset) != null ? _b : 0;
2068
+ }
2069
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2070
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2071
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2072
+ }
2073
+ function getAdvanceableInitialScrollState(state, options) {
2074
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
2075
+ const initialScroll = state.initialScroll;
2076
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
2077
+ const shouldWaitForInitialLayout = !!(options == null ? void 0 : options.requiresMeasuredLayout) && !queuedInitialLayout && !isInitialScrollInProgress;
2078
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll || scrollingTo && !isInitialScrollInProgress) {
2079
+ return void 0;
2080
+ }
2081
+ return {
2082
+ initialScroll,
2083
+ isInitialScrollInProgress,
2084
+ queuedInitialLayout,
2085
+ scrollingTo
2086
+ };
2087
+ }
2088
+ function advanceMeasuredInitialScroll(ctx, options) {
2089
+ var _a3, _b, _c;
2090
+ const state = ctx.state;
2091
+ const advanceableState = getAdvanceableInitialScrollState(state, {
2092
+ requiresMeasuredLayout: true
2093
+ });
2094
+ if (!advanceableState) {
2095
+ return false;
2096
+ }
2097
+ const { initialScroll, isInitialScrollInProgress, queuedInitialLayout } = advanceableState;
2098
+ const scrollingTo = isInitialScrollInProgress ? advanceableState.scrollingTo : void 0;
2099
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2100
+ const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2101
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2102
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2103
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2104
+ if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2105
+ return false;
2106
+ }
2107
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2108
+ return false;
2109
+ }
2110
+ if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2111
+ setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2112
+ }
2113
+ const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2114
+ dispatchInitialScroll(ctx, {
2115
+ forceScroll,
2116
+ resolvedOffset,
2117
+ target: initialScroll
2118
+ });
2119
+ return true;
2120
+ }
2121
+ function advanceOffsetInitialScroll(ctx, options) {
2122
+ var _a3, _b;
2123
+ const state = ctx.state;
2124
+ const advanceableState = getAdvanceableInitialScrollState(state);
2125
+ if (!advanceableState) {
2126
+ return false;
2127
+ }
2128
+ const { initialScroll, queuedInitialLayout } = advanceableState;
2129
+ const resolvedOffset = (_a3 = initialScroll.contentOffset) != null ? _a3 : 0;
2130
+ const isAlreadyAtDesiredInitialTarget = Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2131
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2132
+ return false;
2133
+ }
2134
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
2135
+ const forceScroll = (_b = options == null ? void 0 : options.forceScroll) != null ? _b : hasMeasuredScrollLayout || !!queuedInitialLayout;
2136
+ dispatchInitialScroll(ctx, {
2137
+ forceScroll,
2138
+ resolvedOffset,
2139
+ target: initialScroll
2140
+ });
2141
+ return true;
2142
+ }
2143
+ function advanceCurrentInitialScrollSession(ctx, options) {
2144
+ var _a3;
2145
+ return ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? advanceOffsetInitialScroll(ctx, {
2146
+ forceScroll: options == null ? void 0 : options.forceScroll
2147
+ }) : advanceMeasuredInitialScroll(ctx, {
2148
+ forceScroll: options == null ? void 0 : options.forceScroll
2149
+ });
2150
+ }
2151
+
2152
+ // src/utils/checkAllSizesKnown.ts
2153
+ function isNullOrUndefined2(value) {
2154
+ return value === null || value === void 0;
2155
+ }
2156
+ function getMountedIndicesInRange(state, start, end) {
2157
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2158
+ 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);
2159
+ }
2160
+ return [];
2161
+ }
2162
+ function getMountedBufferedIndices(state) {
2163
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2164
+ }
2165
+ function getMountedNoBufferIndices(state) {
2166
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2167
+ }
2168
+ function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
2169
+ return indices.length > 0 && indices.every((index) => {
2170
+ const key = getId(state, index);
2171
+ return state.sizesKnown.has(key);
2172
+ });
2173
+ }
2174
+
2175
+ // src/core/bootstrapInitialScroll.ts
2176
+ var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2177
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
2178
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES = 24;
2179
+ var BOOTSTRAP_REVEAL_ABORT_WARNING = "LegendList bootstrap initial scroll aborted after exceeding convergence bounds.";
2180
+ function getBootstrapInitialScrollSession(state) {
2181
+ var _a3;
2182
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
2183
+ }
2184
+ function isOffsetInitialScrollSession(state) {
2185
+ var _a3;
2186
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2187
+ }
2188
+ function doVisibleIndicesMatch(previous, next) {
2189
+ if (!previous || previous.length !== next.length) {
2190
+ return false;
2191
+ }
2192
+ for (let i = 0; i < previous.length; i++) {
2193
+ if (previous[i] !== next[i]) {
2194
+ return false;
2195
+ }
2196
+ }
2197
+ return true;
2198
+ }
2199
+ function getBootstrapRevealVisibleIndices(options) {
2200
+ const { dataLength, getSize, offset, positions, scrollLength, startIndex: requestedStartIndex } = options;
2201
+ const endOffset = offset + scrollLength;
2202
+ const visibleIndices = [];
2203
+ let index = requestedStartIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, requestedStartIndex)) : 0;
2204
+ while (index > 0) {
2205
+ const previousIndex = index - 1;
2206
+ const previousPosition = positions[previousIndex];
2207
+ if (previousPosition === void 0) {
2208
+ index = previousIndex;
2209
+ continue;
2210
+ }
2211
+ const previousSize = getSize(previousIndex);
2212
+ if (previousSize === void 0) {
2213
+ index = previousIndex;
2214
+ continue;
2215
+ }
2216
+ if (previousPosition + previousSize <= offset) {
2217
+ break;
2218
+ }
2219
+ index = previousIndex;
2220
+ }
2221
+ for (; index < dataLength; index++) {
2222
+ const position = positions[index];
2223
+ if (position === void 0) {
2224
+ continue;
2225
+ }
2226
+ const size = getSize(index);
2227
+ if (size === void 0) {
2228
+ continue;
2229
+ }
2230
+ if (position < endOffset && position + size > offset) {
2231
+ visibleIndices.push(index);
2232
+ } else if (visibleIndices.length > 0 && position >= endOffset) {
2233
+ break;
2234
+ }
2235
+ }
2236
+ return visibleIndices;
2237
+ }
2238
+ function shouldAbortBootstrapReveal(options) {
2239
+ const {
2240
+ mountFrameCount,
2241
+ maxFrames = DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES,
2242
+ maxPasses = DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES,
2243
+ passCount
2244
+ } = options;
2245
+ return mountFrameCount >= maxFrames || passCount >= maxPasses;
2246
+ }
2247
+ function abortBootstrapRevealIfNeeded(ctx, options) {
2248
+ if (!shouldAbortBootstrapReveal(options)) {
2249
+ return false;
2250
+ }
2251
+ if (IS_DEV) {
2252
+ console.warn(BOOTSTRAP_REVEAL_ABORT_WARNING);
2253
+ }
2254
+ abortBootstrapInitialScroll(ctx);
2255
+ return true;
2256
+ }
2257
+ function clearBootstrapInitialScrollSession(state) {
2258
+ var _a3;
2259
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2260
+ const frameHandle = bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle;
2261
+ if (frameHandle !== void 0 && typeof cancelAnimationFrame === "function") {
2262
+ cancelAnimationFrame(frameHandle);
2263
+ }
2264
+ if (bootstrapInitialScroll) {
2265
+ bootstrapInitialScroll.frameHandle = void 0;
2266
+ }
2267
+ setInitialScrollSession(state, {
2268
+ bootstrap: void 0,
2269
+ kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2270
+ });
2271
+ }
2272
+ function startBootstrapInitialScrollSession(state, options) {
2273
+ var _a3, _b, _c;
2274
+ const previousBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2275
+ setInitialScrollSession(state, {
2276
+ bootstrap: {
2277
+ frameHandle: previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.frameHandle,
2278
+ // Re-arming during the initial mount should spend from the same watchdog budget.
2279
+ mountFrameCount: (_a3 = previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.mountFrameCount) != null ? _a3 : 0,
2280
+ passCount: 0,
2281
+ previousResolvedOffset: void 0,
2282
+ scroll: options.scroll,
2283
+ seedContentOffset: (_c = (_b = options.seedContentOffset) != null ? _b : previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.seedContentOffset) != null ? _c : options.scroll,
2284
+ targetIndexSeed: options.targetIndexSeed,
2285
+ visibleIndices: void 0
2286
+ },
2287
+ kind: "bootstrap"
2288
+ });
2289
+ }
2290
+ function resetBootstrapInitialScrollSession(state, options) {
2291
+ var _a3, _b, _c;
2292
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2293
+ if (!bootstrapInitialScroll) {
2294
+ if ((options == null ? void 0 : options.scroll) !== void 0) {
2295
+ startBootstrapInitialScrollSession(state, {
2296
+ scroll: options.scroll,
2297
+ seedContentOffset: options.seedContentOffset,
2298
+ targetIndexSeed: options.targetIndexSeed
2299
+ });
2300
+ }
2301
+ } else {
2302
+ bootstrapInitialScroll.passCount = 0;
2303
+ bootstrapInitialScroll.previousResolvedOffset = void 0;
2304
+ bootstrapInitialScroll.scroll = (_a3 = options == null ? void 0 : options.scroll) != null ? _a3 : bootstrapInitialScroll.scroll;
2305
+ bootstrapInitialScroll.seedContentOffset = (_b = options == null ? void 0 : options.seedContentOffset) != null ? _b : bootstrapInitialScroll.seedContentOffset;
2306
+ bootstrapInitialScroll.targetIndexSeed = (_c = options == null ? void 0 : options.targetIndexSeed) != null ? _c : bootstrapInitialScroll.targetIndexSeed;
2307
+ bootstrapInitialScroll.visibleIndices = void 0;
2308
+ setInitialScrollSession(state, {
2309
+ bootstrap: bootstrapInitialScroll,
2310
+ kind: "bootstrap"
2311
+ });
2312
+ }
2313
+ }
2314
+ function queueBootstrapInitialScrollReevaluation(state) {
2315
+ requestAnimationFrame(() => {
2316
+ var _a3;
2317
+ if (getBootstrapInitialScrollSession(state)) {
2318
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
2319
+ }
2320
+ });
2321
+ }
2322
+ function ensureBootstrapInitialScrollFrameTicker(ctx) {
2323
+ const state = ctx.state;
2324
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2325
+ if (!bootstrapInitialScroll || bootstrapInitialScroll.frameHandle !== void 0) {
2326
+ return;
2327
+ }
2328
+ const tick = () => {
2329
+ const activeBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2330
+ if (!activeBootstrapInitialScroll) {
2331
+ return;
2332
+ }
2333
+ activeBootstrapInitialScroll.frameHandle = void 0;
2334
+ activeBootstrapInitialScroll.mountFrameCount += 1;
2335
+ if (abortBootstrapRevealIfNeeded(ctx, {
2336
+ mountFrameCount: activeBootstrapInitialScroll.mountFrameCount,
2337
+ passCount: activeBootstrapInitialScroll.passCount
2338
+ })) {
2339
+ return;
2340
+ }
2341
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2342
+ };
2343
+ bootstrapInitialScroll.frameHandle = requestAnimationFrame(tick);
2344
+ }
2345
+ function rearmBootstrapInitialScroll(ctx, options) {
2346
+ resetBootstrapInitialScrollSession(ctx.state, options);
2347
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2348
+ queueBootstrapInitialScrollReevaluation(ctx.state);
2349
+ }
2350
+ function createInitialScrollAtEndTarget(options) {
2351
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2352
+ return {
2353
+ contentOffset: void 0,
2354
+ index: Math.max(0, dataLength - 1),
2355
+ preserveForBottomPadding: true,
2356
+ preserveForFooterLayout,
2357
+ viewOffset: -stylePaddingBottom - footerSize,
2358
+ viewPosition: 1
2359
+ };
2360
+ }
2361
+ function shouldPreserveInitialScrollForBottomPadding(target) {
2362
+ return !!(target == null ? void 0 : target.preserveForBottomPadding);
2363
+ }
2364
+ function shouldPreserveInitialScrollForFooterLayout(target) {
2365
+ return !!(target == null ? void 0 : target.preserveForFooterLayout);
2366
+ }
2367
+ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2368
+ return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2369
+ }
2370
+ function createRetargetedBottomAlignedInitialScroll(options) {
2371
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2372
+ const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2373
+ return {
2374
+ ...target,
2375
+ contentOffset: void 0,
2376
+ index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2377
+ preserveForBottomPadding: true,
2378
+ preserveForFooterLayout,
2379
+ viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2380
+ viewPosition: 1
2381
+ };
2382
+ }
2383
+ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2384
+ return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2385
+ }
2386
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
2387
+ const { dataLength, stylePaddingBottom, target } = options;
2388
+ const state = ctx.state;
2389
+ if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2390
+ return;
2391
+ }
2392
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
2393
+ dataLength,
2394
+ footerSize: 0,
2395
+ preserveForFooterLayout: void 0,
2396
+ stylePaddingBottom
2397
+ });
2398
+ setInitialScrollTarget(state, clearedFooterTarget);
2399
+ }
2400
+ function clearFinishedViewportRetargetableInitialScroll(state) {
2401
+ clearPreservedInitialScrollTarget(state);
2402
+ }
2403
+ function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2404
+ const state = ctx.state;
2405
+ if (!state.didFinishInitialScroll) {
2406
+ return false;
2407
+ }
2408
+ const currentOffset = getObservedBootstrapInitialScrollOffset(state);
2409
+ return Math.abs(currentOffset - resolveInitialScrollOffset(ctx, target)) > epsilon;
2410
+ }
2411
+ function getObservedBootstrapInitialScrollOffset(state) {
2412
+ var _a3, _b, _c, _d;
2413
+ const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2414
+ return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2415
+ }
2416
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2417
+ var _a3, _b;
2418
+ const state = ctx.state;
2419
+ const initialScroll = state.initialScroll;
2420
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
2421
+ return;
2422
+ }
2423
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2424
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2425
+ clearPendingInitialScrollFooterLayout(ctx, {
2426
+ dataLength: state.props.data.length,
2427
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2428
+ target: initialScroll
2429
+ });
2430
+ return;
2431
+ }
2432
+ clearFinishedViewportRetargetableInitialScroll(state);
2433
+ }
2434
+ }
2435
+ function startBootstrapInitialScrollOnMount(ctx, options) {
2436
+ var _a3, _b, _c;
2437
+ const { initialScrollAtEnd, target } = options;
2438
+ const state = ctx.state;
2439
+ const offset = resolveInitialScrollOffset(ctx, target);
2440
+ const shouldFinishAtOrigin = offset === 0 && !initialScrollAtEnd && (isOffsetInitialScrollSession(state) ? Math.abs((_a3 = target.contentOffset) != null ? _a3 : 0) <= 1 : target.index === 0 && ((_b = target.viewPosition) != null ? _b : 0) === 0 && Math.abs((_c = target.viewOffset) != null ? _c : 0) <= 1);
2441
+ const shouldFinishWithPreservedTarget = state.props.data.length === 0 && target.index !== void 0;
2442
+ if (shouldFinishAtOrigin) {
2443
+ clearBootstrapInitialScrollSession(state);
2444
+ finishInitialScroll(ctx, {
2445
+ resolvedOffset: offset
2446
+ });
2447
+ } else if (shouldFinishWithPreservedTarget) {
2448
+ clearBootstrapInitialScrollSession(state);
2449
+ finishInitialScroll(ctx, {
2450
+ preserveTarget: true,
2451
+ resolvedOffset: offset
2452
+ });
2453
+ } else {
2454
+ startBootstrapInitialScrollSession(state, {
2455
+ scroll: offset,
2456
+ seedContentOffset: Platform2.OS === "web" ? 0 : offset,
2457
+ targetIndexSeed: target.index
2458
+ });
2459
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2460
+ }
2461
+ }
2462
+ function handleBootstrapInitialScrollDataChange(ctx, options) {
2463
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2464
+ const state = ctx.state;
2465
+ const initialScroll = state.initialScroll;
2466
+ if (isOffsetInitialScrollSession(state) || !initialScroll) {
2467
+ return;
2468
+ }
2469
+ const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2470
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2471
+ const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2472
+ if (shouldClearFinishedResizePreservation) {
2473
+ clearPreservedInitialScrollTarget(state);
2474
+ return;
2475
+ }
2476
+ const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2477
+ if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2478
+ return;
2479
+ }
2480
+ if (shouldRetargetBottomAligned) {
2481
+ const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2482
+ dataLength,
2483
+ footerSize: peek$(ctx, "footerSize") || 0,
2484
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2485
+ stylePaddingBottom
2486
+ }) : createRetargetedBottomAlignedInitialScroll({
2487
+ dataLength,
2488
+ footerSize: peek$(ctx, "footerSize") || 0,
2489
+ initialScrollAtEnd,
2490
+ stylePaddingBottom,
2491
+ target: initialScroll
2492
+ });
2493
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2494
+ clearPendingInitialScrollFooterLayout(ctx, {
2495
+ dataLength,
2496
+ stylePaddingBottom,
2497
+ target: initialScroll
2498
+ });
2499
+ return;
2500
+ }
2501
+ if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2502
+ setInitialScrollTarget(state, updatedInitialScroll, {
2503
+ resetDidFinish: shouldResetDidFinish
2504
+ });
2505
+ rearmBootstrapInitialScroll(ctx, {
2506
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2507
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2508
+ targetIndexSeed: updatedInitialScroll.index
2509
+ });
2510
+ return;
2511
+ }
2512
+ }
2513
+ if (!didDataChange) {
2514
+ return;
2515
+ }
2516
+ if (bootstrapInitialScroll || shouldResetDidFinish) {
2517
+ setInitialScrollTarget(state, initialScroll, {
2518
+ resetDidFinish: shouldResetDidFinish
2519
+ });
2520
+ rearmBootstrapInitialScroll(ctx, {
2521
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
2522
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2523
+ targetIndexSeed: initialScroll.index
2524
+ });
2525
+ }
2526
+ }
2527
+ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2528
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
2529
+ const state = ctx.state;
2530
+ if (!initialScrollAtEnd) {
2531
+ return;
2532
+ }
2533
+ const initialScroll = state.initialScroll;
2534
+ if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
2535
+ return;
2536
+ }
2537
+ const shouldProcessFooterLayout = !!getBootstrapInitialScrollSession(state) || shouldPreserveInitialScrollForFooterLayout(initialScroll);
2538
+ if (!shouldProcessFooterLayout) {
2539
+ return;
2540
+ }
2541
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2542
+ clearPendingInitialScrollFooterLayout(ctx, {
2543
+ dataLength,
2544
+ stylePaddingBottom,
2545
+ target: initialScroll
2546
+ });
2547
+ } else {
2548
+ const updatedInitialScroll = createInitialScrollAtEndTarget({
2549
+ dataLength,
2550
+ footerSize,
2551
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2552
+ stylePaddingBottom
2553
+ });
2554
+ const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2555
+ if (!didTargetChange) {
2556
+ clearPendingInitialScrollFooterLayout(ctx, {
2557
+ dataLength,
2558
+ stylePaddingBottom,
2559
+ target: initialScroll
2560
+ });
2561
+ } else {
2562
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
2563
+ setInitialScrollTarget(state, updatedInitialScroll, {
2564
+ resetDidFinish: didFinishInitialScroll
2565
+ });
2566
+ rearmBootstrapInitialScroll(ctx, {
2567
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2568
+ targetIndexSeed: updatedInitialScroll.index
2569
+ });
2570
+ }
2571
+ }
2572
+ }
2573
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
2574
+ const state = ctx.state;
2575
+ const initialScroll = state.initialScroll;
2576
+ if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2577
+ return;
2578
+ }
2579
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2580
+ if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2581
+ return;
2582
+ }
2583
+ const didFinishInitialScroll = state.didFinishInitialScroll;
2584
+ if (didFinishInitialScroll) {
2585
+ setInitialScrollTarget(state, initialScroll, {
2586
+ resetDidFinish: true
2587
+ });
2588
+ state.clearPreservedInitialScrollOnNextFinish = true;
2589
+ }
2590
+ rearmBootstrapInitialScroll(ctx, {
2591
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
2592
+ seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2593
+ targetIndexSeed: initialScroll.index
2594
+ });
2595
+ }
2596
+ function evaluateBootstrapInitialScroll(ctx) {
2597
+ var _a3, _b;
2598
+ const state = ctx.state;
2599
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2600
+ const initialScroll = state.initialScroll;
2601
+ if (!bootstrapInitialScroll || !initialScroll || isOffsetInitialScrollSession(state) || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
2602
+ return;
2603
+ }
2604
+ bootstrapInitialScroll.passCount += 1;
2605
+ if (abortBootstrapRevealIfNeeded(ctx, {
2606
+ mountFrameCount: bootstrapInitialScroll.mountFrameCount,
2607
+ passCount: bootstrapInitialScroll.passCount
2608
+ })) {
2609
+ return;
2610
+ }
2611
+ if (initialScroll.index !== void 0 && state.startBuffered >= 0 && state.endBuffered >= 0 && initialScroll.index >= state.startBuffered && initialScroll.index <= state.endBuffered) {
2612
+ bootstrapInitialScroll.targetIndexSeed = void 0;
2613
+ }
2614
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2615
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
2616
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
2617
+ const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
2618
+ const { data } = state.props;
2619
+ const visibleIndices = getBootstrapRevealVisibleIndices({
2620
+ dataLength: data.length,
2621
+ getSize: (index) => {
2622
+ var _a4, _b2;
2623
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
2624
+ return (_b2 = state.sizes.get(id)) != null ? _b2 : getItemSize(ctx, id, index, data[index]);
2625
+ },
2626
+ offset: resolvedOffset,
2627
+ positions: state.positions,
2628
+ scrollLength: state.scrollLength,
2629
+ startIndex: (_b = bootstrapInitialScroll.targetIndexSeed) != null ? _b : state.startBuffered >= 0 ? state.startBuffered : void 0
2630
+ });
2631
+ const areVisibleIndicesMeasured = visibleIndices.length > 0 && visibleIndices.every((index) => {
2632
+ var _a4;
2633
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
2634
+ return state.sizesKnown.has(id);
2635
+ });
2636
+ const previousResolvedOffset = bootstrapInitialScroll.previousResolvedOffset;
2637
+ const previousVisibleIndices = bootstrapInitialScroll.visibleIndices;
2638
+ bootstrapInitialScroll.previousResolvedOffset = resolvedOffset;
2639
+ bootstrapInitialScroll.visibleIndices = visibleIndices;
2640
+ if (didResolvedOffsetChange) {
2641
+ bootstrapInitialScroll.scroll = resolvedOffset;
2642
+ queueBootstrapInitialScrollReevaluation(state);
2643
+ return;
2644
+ }
2645
+ if (!areMountedBufferedIndicesMeasured || !areVisibleIndicesMeasured) {
2646
+ return;
2647
+ }
2648
+ const didRevealSettle = previousResolvedOffset !== void 0 && Math.abs(previousResolvedOffset - resolvedOffset) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON && doVisibleIndicesMatch(previousVisibleIndices, visibleIndices);
2649
+ if (!didRevealSettle) {
2650
+ queueBootstrapInitialScrollReevaluation(state);
2651
+ return;
2652
+ }
2653
+ if (Platform2.OS !== "web" && Platform2.OS !== "android" && Math.abs(bootstrapInitialScroll.seedContentOffset - resolvedOffset) <= 1 && Math.abs(getObservedBootstrapInitialScrollOffset(state) - resolvedOffset) <= 1) {
2654
+ finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset);
2655
+ } else {
2656
+ clearBootstrapInitialScrollSession(state);
2657
+ dispatchInitialScroll(ctx, {
2658
+ forceScroll: true,
2659
+ resolvedOffset,
2660
+ target: initialScroll,
2661
+ waitForCompletionFrame: Platform2.OS === "web"
2662
+ });
2663
+ }
2664
+ }
2665
+ function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
2666
+ var _a3;
2667
+ const state = ctx.state;
2668
+ clearBootstrapInitialScrollSession(state);
2669
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
2670
+ finishInitialScroll(ctx, {
2671
+ preserveTarget: shouldPreserveResizeTarget,
2672
+ recalculateItems: true,
2673
+ resolvedOffset,
2674
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
2675
+ });
2676
+ }
2677
+ function abortBootstrapInitialScroll(ctx) {
2678
+ var _a3, _b, _c, _d;
2679
+ const state = ctx.state;
2680
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2681
+ const initialScroll = state.initialScroll;
2682
+ if (bootstrapInitialScroll && initialScroll && !isOffsetInitialScrollSession(state) && state.refScroller.current) {
2683
+ clearBootstrapInitialScrollSession(state);
2684
+ dispatchInitialScroll(ctx, {
2685
+ forceScroll: true,
2686
+ resolvedOffset: bootstrapInitialScroll.scroll,
2687
+ target: initialScroll,
2688
+ waitForCompletionFrame: Platform2.OS === "web"
2689
+ });
2690
+ } else {
2691
+ finishBootstrapInitialScrollWithoutScroll(
2692
+ ctx,
2693
+ (_d = (_c = (_b = (_a3 = getBootstrapInitialScrollSession(state)) == null ? void 0 : _a3.scroll) != null ? _b : state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0
2694
+ );
2695
+ }
2696
+ }
2697
+
2698
+ // src/core/initialScrollLifecycle.ts
2699
+ function handleInitialScrollLayoutReady(ctx) {
2700
+ var _a3;
2701
+ if (!ctx.state.initialScroll) {
2702
+ return;
2703
+ }
2704
+ const runScroll = () => advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
2705
+ runScroll();
2706
+ if (((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset") {
2707
+ requestAnimationFrame(runScroll);
2708
+ }
2709
+ checkFinishedScroll(ctx, { onlyIfAligned: true });
2710
+ }
2711
+ function initializeInitialScrollOnMount(ctx, options) {
1476
2712
  var _a3, _b;
2713
+ const { dataLength, hasFooterComponent, initialContentOffset, initialScrollAtEnd, useBootstrapInitialScroll } = options;
1477
2714
  const state = ctx.state;
1478
- const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1479
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1480
- const {
1481
- props: { horizontal }
1482
- } = state;
1483
- if (state.animFrameCheckFinishedScroll) {
1484
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2715
+ const initialScroll = state.initialScroll;
2716
+ const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
2717
+ const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
2718
+ if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
2719
+ setInitialScrollTarget(state, {
2720
+ ...initialScroll,
2721
+ contentOffset: resolvedInitialContentOffset,
2722
+ preserveForFooterLayout
2723
+ });
1485
2724
  }
1486
- if (state.timeoutCheckFinishedScrollFallback) {
1487
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2725
+ if (useBootstrapInitialScroll && initialScroll && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2726
+ startBootstrapInitialScrollOnMount(ctx, {
2727
+ initialScrollAtEnd,
2728
+ target: state.initialScroll
2729
+ });
2730
+ return;
1488
2731
  }
1489
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1490
- offset = clampScrollOffset(ctx, offset, scrollTarget);
1491
- state.scrollHistory.length = 0;
1492
- if (!noScrollingTo) {
1493
- state.scrollingTo = {
1494
- ...scrollTarget,
1495
- targetOffset: offset
1496
- };
2732
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
2733
+ if (!resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
2734
+ if (initialScroll && !initialScrollAtEnd) {
2735
+ finishInitialScroll(ctx, {
2736
+ resolvedOffset: resolvedInitialContentOffset
2737
+ });
2738
+ } else {
2739
+ setInitialRenderState(ctx, { didInitialScroll: true });
2740
+ }
1497
2741
  }
1498
- state.scrollPending = offset;
1499
- const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!state.initialNativeScrollWatchdog) && offset > WATCHDOG_OFFSET_EPSILON;
1500
- const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && offset <= WATCHDOG_OFFSET_EPSILON;
1501
- if (shouldWatchInitialNativeScroll) {
1502
- state.hasScrolled = false;
1503
- state.initialNativeScrollWatchdog = {
1504
- startScroll: (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.startScroll) != null ? _b : state.scroll,
1505
- targetOffset: offset
1506
- };
1507
- } else if (shouldClearInitialNativeScrollWatchdog) {
1508
- state.initialNativeScrollWatchdog = void 0;
2742
+ }
2743
+ function handleInitialScrollDataChange(ctx, options) {
2744
+ var _a3, _b, _c;
2745
+ const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
2746
+ const state = ctx.state;
2747
+ const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
2748
+ if (state.initialScrollSession) {
2749
+ state.initialScrollSession.previousDataLength = dataLength;
1509
2750
  }
1510
- if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1511
- doScrollTo(ctx, { animated, horizontal, offset });
1512
- } else {
1513
- state.scroll = offset;
2751
+ setInitialScrollSession(state);
2752
+ if (useBootstrapInitialScroll) {
2753
+ handleBootstrapInitialScrollDataChange(ctx, {
2754
+ dataLength,
2755
+ didDataChange,
2756
+ initialScrollAtEnd,
2757
+ previousDataLength,
2758
+ stylePaddingBottom
2759
+ });
2760
+ return;
1514
2761
  }
2762
+ const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
2763
+ if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
2764
+ return;
2765
+ }
2766
+ if (shouldReplayFinishedOffsetInitialScroll) {
2767
+ state.didFinishInitialScroll = false;
2768
+ }
2769
+ advanceCurrentInitialScrollSession(ctx);
1515
2770
  }
1516
2771
 
1517
- // src/core/doMaintainScrollAtEnd.ts
1518
- function doMaintainScrollAtEnd(ctx) {
2772
+ // src/utils/requestAdjust.ts
2773
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1519
2774
  const state = ctx.state;
1520
- const {
1521
- didContainersLayout,
1522
- isAtEnd,
1523
- pendingNativeMVCPAdjust,
1524
- refScroller,
1525
- props: { maintainScrollAtEnd }
1526
- } = state;
1527
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
1528
- if (pendingNativeMVCPAdjust) {
1529
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1530
- return false;
1531
- }
1532
- state.pendingMaintainScrollAtEnd = false;
1533
- if (shouldMaintainScrollAtEnd) {
1534
- const contentSize = getContentSize(ctx);
1535
- if (contentSize < state.scrollLength) {
1536
- state.scroll = 0;
1537
- }
1538
- requestAnimationFrame(() => {
1539
- var _a3;
1540
- if (state.isAtEnd) {
1541
- state.maintainingScrollAtEnd = true;
1542
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
1543
- animated: maintainScrollAtEnd.animated
2775
+ if (Math.abs(positionDiff) > 0.1) {
2776
+ const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2777
+ const doit = () => {
2778
+ if (needsScrollWorkaround) {
2779
+ scrollTo(ctx, {
2780
+ noScrollingTo: true,
2781
+ offset: state.scroll
1544
2782
  });
1545
- setTimeout(
1546
- () => {
1547
- state.maintainingScrollAtEnd = false;
1548
- },
1549
- maintainScrollAtEnd.animated ? 500 : 0
1550
- );
2783
+ } else {
2784
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2785
+ if (state.adjustingFromInitialMount) {
2786
+ state.adjustingFromInitialMount--;
2787
+ }
1551
2788
  }
1552
- });
1553
- return true;
2789
+ };
2790
+ state.scroll += positionDiff;
2791
+ state.scrollForNextCalculateItemsInView = void 0;
2792
+ const readyToRender = peek$(ctx, "readyToRender");
2793
+ if (readyToRender) {
2794
+ doit();
2795
+ if (Platform2.OS !== "web") {
2796
+ const threshold = state.scroll - positionDiff / 2;
2797
+ if (!state.ignoreScrollFromMVCP) {
2798
+ state.ignoreScrollFromMVCP = {};
2799
+ }
2800
+ if (positionDiff > 0) {
2801
+ state.ignoreScrollFromMVCP.lt = threshold;
2802
+ } else {
2803
+ state.ignoreScrollFromMVCP.gt = threshold;
2804
+ }
2805
+ if (state.ignoreScrollFromMVCPTimeout) {
2806
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2807
+ }
2808
+ const delay = needsScrollWorkaround ? 250 : 100;
2809
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2810
+ var _a3;
2811
+ state.ignoreScrollFromMVCP = void 0;
2812
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2813
+ if (shouldForceUpdate) {
2814
+ state.ignoreScrollFromMVCPIgnored = false;
2815
+ state.scrollPending = state.scroll;
2816
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2817
+ }
2818
+ }, delay);
2819
+ }
2820
+ } else {
2821
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2822
+ requestAnimationFrame(doit);
2823
+ }
1554
2824
  }
1555
- return false;
1556
2825
  }
1557
2826
 
1558
2827
  // src/core/mvcp.ts
@@ -1679,7 +2948,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
1679
2948
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1680
2949
  return true;
1681
2950
  }
1682
- if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
2951
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
1683
2952
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1684
2953
  return true;
1685
2954
  }
@@ -1824,202 +3093,104 @@ function prepareMVCP(ctx, dataChanged) {
1824
3093
  anchorId: anchorIdForLock,
1825
3094
  anchorPosition: anchorPositionForLock,
1826
3095
  dataChanged,
1827
- now,
1828
- positionDiff
1829
- });
1830
- if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
1831
- state.pendingNativeMVCPAdjust = {
1832
- amount: positionDiff,
1833
- furthestProgressTowardAmount: 0,
1834
- manualApplied: 0,
1835
- startScroll: prevScroll
1836
- };
1837
- maybeApplyPredictedNativeMVCPAdjust(ctx);
1838
- return;
1839
- }
1840
- if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
1841
- requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
1842
- }
1843
- };
1844
- }
1845
- }
1846
-
1847
- // src/platform/flushSync.native.ts
1848
- var flushSync = (fn) => {
1849
- fn();
1850
- };
1851
-
1852
- // src/core/updateScroll.ts
1853
- function updateScroll(ctx, newScroll, forceUpdate) {
1854
- var _a3;
1855
- const state = ctx.state;
1856
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1857
- const prevScroll = state.scroll;
1858
- state.hasScrolled = true;
1859
- state.lastBatchingAction = Date.now();
1860
- const currentTime = Date.now();
1861
- const adjust = scrollAdjustHandler.getAdjust();
1862
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1863
- if (adjustChanged) {
1864
- scrollHistory.length = 0;
1865
- }
1866
- state.lastScrollAdjustForHistory = adjust;
1867
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1868
- if (!adjustChanged) {
1869
- scrollHistory.push({ scroll: newScroll, time: currentTime });
1870
- }
1871
- }
1872
- if (scrollHistory.length > 5) {
1873
- scrollHistory.shift();
1874
- }
1875
- if (ignoreScrollFromMVCP && !scrollingTo) {
1876
- const { lt, gt } = ignoreScrollFromMVCP;
1877
- if (lt && newScroll < lt || gt && newScroll > gt) {
1878
- state.ignoreScrollFromMVCPIgnored = true;
1879
- return;
1880
- }
1881
- }
1882
- state.scrollPrev = prevScroll;
1883
- state.scrollPrevTime = state.scrollTime;
1884
- state.scroll = newScroll;
1885
- state.scrollTime = currentTime;
1886
- const scrollDelta = Math.abs(newScroll - prevScroll);
1887
- const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
1888
- const scrollLength = state.scrollLength;
1889
- const lastCalculated = state.scrollLastCalculate;
1890
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1891
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1892
- if (shouldUpdate) {
1893
- state.scrollLastCalculate = state.scroll;
1894
- state.ignoreScrollFromMVCPIgnored = false;
1895
- state.lastScrollDelta = scrollDelta;
1896
- const runCalculateItems = () => {
1897
- var _a4;
1898
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
1899
- checkThresholds(ctx);
1900
- };
1901
- if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1902
- flushSync(runCalculateItems);
1903
- } else {
1904
- runCalculateItems();
1905
- }
1906
- const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
1907
- if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
1908
- state.pendingMaintainScrollAtEnd = false;
1909
- doMaintainScrollAtEnd(ctx);
1910
- }
1911
- state.dataChangeNeedsScrollUpdate = false;
1912
- state.lastScrollDelta = 0;
1913
- }
1914
- }
1915
-
1916
- // src/utils/requestAdjust.ts
1917
- function requestAdjust(ctx, positionDiff, dataChanged) {
1918
- const state = ctx.state;
1919
- if (Math.abs(positionDiff) > 0.1) {
1920
- const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1921
- const doit = () => {
1922
- if (needsScrollWorkaround) {
1923
- scrollTo(ctx, {
1924
- noScrollingTo: true,
1925
- offset: state.scroll
1926
- });
1927
- } else {
1928
- state.scrollAdjustHandler.requestAdjust(positionDiff);
1929
- if (state.adjustingFromInitialMount) {
1930
- state.adjustingFromInitialMount--;
1931
- }
1932
- }
1933
- };
1934
- state.scroll += positionDiff;
1935
- state.scrollForNextCalculateItemsInView = void 0;
1936
- const readyToRender = peek$(ctx, "readyToRender");
1937
- if (readyToRender) {
1938
- doit();
1939
- if (Platform2.OS !== "web") {
1940
- const threshold = state.scroll - positionDiff / 2;
1941
- if (!state.ignoreScrollFromMVCP) {
1942
- state.ignoreScrollFromMVCP = {};
1943
- }
1944
- if (positionDiff > 0) {
1945
- state.ignoreScrollFromMVCP.lt = threshold;
1946
- } else {
1947
- state.ignoreScrollFromMVCP.gt = threshold;
1948
- }
1949
- if (state.ignoreScrollFromMVCPTimeout) {
1950
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
1951
- }
1952
- const delay = needsScrollWorkaround ? 250 : 100;
1953
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
1954
- state.ignoreScrollFromMVCP = void 0;
1955
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1956
- if (shouldForceUpdate) {
1957
- state.ignoreScrollFromMVCPIgnored = false;
1958
- state.scrollPending = state.scroll;
1959
- updateScroll(ctx, state.scroll, true);
1960
- }
1961
- }, delay);
3096
+ now,
3097
+ positionDiff
3098
+ });
3099
+ if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
3100
+ state.pendingNativeMVCPAdjust = {
3101
+ amount: positionDiff,
3102
+ furthestProgressTowardAmount: 0,
3103
+ manualApplied: 0,
3104
+ startScroll: prevScroll
3105
+ };
3106
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
3107
+ return;
1962
3108
  }
1963
- } else {
1964
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1965
- requestAnimationFrame(doit);
1966
- }
3109
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3110
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3111
+ }
3112
+ };
1967
3113
  }
1968
3114
  }
1969
3115
 
1970
- // src/core/ensureInitialAnchor.ts
1971
- var INITIAL_ANCHOR_TOLERANCE = 0.5;
1972
- var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1973
- var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1974
- function ensureInitialAnchor(ctx) {
1975
- var _a3, _b, _c, _d, _e, _f;
3116
+ // src/core/syncMountedContainer.ts
3117
+ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3118
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
1976
3119
  const state = ctx.state;
1977
- const { initialAnchor, didContainersLayout, scroll, scrollLength } = state;
1978
- const anchor = initialAnchor;
1979
- if (state.initialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
1980
- return;
1981
- }
1982
- const item = state.props.data[anchor.index];
1983
- if (!didContainersLayout) {
1984
- return;
1985
- }
1986
- const id = getId(state, anchor.index);
1987
- if (state.positions[anchor.index] === void 0) {
1988
- return;
1989
- }
1990
- const size = getItemSize(ctx, id, anchor.index, item, true, true);
1991
- if (size === void 0) {
1992
- return;
1993
- }
1994
- const availableSpace = Math.max(0, scrollLength - size);
1995
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1996
- const desiredOffset = calculateOffsetForIndex(ctx, anchor.index) + topOffsetAdjustment - ((_b = anchor.viewOffset) != null ? _b : 0) - ((_c = anchor.viewPosition) != null ? _c : 0) * availableSpace;
1997
- const clampedDesiredOffset = clampScrollOffset(ctx, desiredOffset, anchor);
1998
- const delta = clampedDesiredOffset - scroll;
1999
- if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
2000
- const settledTicks = ((_d = anchor.settledTicks) != null ? _d : 0) + 1;
2001
- if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
2002
- state.initialAnchor = void 0;
2003
- } else {
2004
- anchor.settledTicks = settledTicks;
3120
+ const {
3121
+ columns,
3122
+ columnSpans,
3123
+ positions,
3124
+ props: { data, itemsAreEqual, keyExtractor }
3125
+ } = state;
3126
+ const item = data[itemIndex];
3127
+ if (item === void 0) {
3128
+ return { didChangePosition: false, didRefreshData: false };
3129
+ }
3130
+ const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3131
+ let didChangePosition = false;
3132
+ let didRefreshData = false;
3133
+ if (updateLayout) {
3134
+ const positionValue = positions[itemIndex];
3135
+ if (positionValue === void 0) {
3136
+ set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3137
+ return { didChangePosition: false, didRefreshData: false };
3138
+ }
3139
+ const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3140
+ const column = columns[itemIndex] || 1;
3141
+ const span = columnSpans[itemIndex] || 1;
3142
+ const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
3143
+ const prevColumn = peek$(ctx, `containerColumn${containerIndex}`);
3144
+ const prevSpan = peek$(ctx, `containerSpan${containerIndex}`);
3145
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3146
+ set$(ctx, `containerPosition${containerIndex}`, position);
3147
+ didChangePosition = true;
3148
+ }
3149
+ if (column >= 0 && column !== prevColumn) {
3150
+ set$(ctx, `containerColumn${containerIndex}`, column);
3151
+ }
3152
+ if (span !== prevSpan) {
3153
+ set$(ctx, `containerSpan${containerIndex}`, span);
3154
+ }
3155
+ }
3156
+ const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3157
+ if (prevData !== item) {
3158
+ 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;
3159
+ const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3160
+ if (cachedComparison === 2) {
3161
+ set$(ctx, `containerItemData${containerIndex}`, item);
3162
+ didRefreshData = true;
3163
+ } else if (cachedComparison !== 1) {
3164
+ const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3165
+ const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3166
+ if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3167
+ set$(ctx, `containerItemData${containerIndex}`, item);
3168
+ didRefreshData = true;
3169
+ } else if (!itemsAreEqual) {
3170
+ set$(ctx, `containerItemData${containerIndex}`, item);
3171
+ didRefreshData = true;
3172
+ } else {
3173
+ const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3174
+ if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
3175
+ if (state.previousData) {
3176
+ state.pendingDataComparison = {
3177
+ byIndex: [],
3178
+ nextData: data,
3179
+ previousData: state.previousData
3180
+ };
3181
+ }
3182
+ }
3183
+ if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3184
+ state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3185
+ }
3186
+ if (!isEqual) {
3187
+ set$(ctx, `containerItemData${containerIndex}`, item);
3188
+ didRefreshData = true;
3189
+ }
3190
+ }
2005
3191
  }
2006
- return;
2007
- }
2008
- if (((_e = anchor.attempts) != null ? _e : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
2009
- state.initialAnchor = void 0;
2010
- return;
2011
- }
2012
- const lastDelta = anchor.lastDelta;
2013
- if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
2014
- state.initialAnchor = void 0;
2015
- return;
2016
3192
  }
2017
- Object.assign(anchor, {
2018
- attempts: ((_f = anchor.attempts) != null ? _f : 0) + 1,
2019
- lastDelta: delta,
2020
- settledTicks: 0
2021
- });
2022
- requestAdjust(ctx, delta);
3193
+ return { didChangePosition, didRefreshData };
2023
3194
  }
2024
3195
 
2025
3196
  // src/core/prepareColumnStartState.ts
@@ -2184,9 +3355,10 @@ function updateSnapToOffsets(ctx) {
2184
3355
  }
2185
3356
 
2186
3357
  // src/core/updateItemPositions.ts
2187
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
3358
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
2188
3359
  doMVCP: false,
2189
3360
  forceFullUpdate: false,
3361
+ optimizeForVisibleWindow: false,
2190
3362
  scrollBottomBuffered: -1,
2191
3363
  startIndex: 0
2192
3364
  }) {
@@ -2211,7 +3383,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2211
3383
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
2212
3384
  const lastScrollDelta = state.lastScrollDelta;
2213
3385
  const velocity = getScrollVelocity(state);
2214
- const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3386
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
2215
3387
  const maxVisibleArea = scrollBottomBuffered + 1e3;
2216
3388
  const useAverageSize = !getEstimatedItemSize;
2217
3389
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
@@ -2326,7 +3498,15 @@ function ensureViewabilityState(ctx, configId) {
2326
3498
  }
2327
3499
  let state = map.get(configId);
2328
3500
  if (!state) {
2329
- state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
3501
+ state = {
3502
+ end: -1,
3503
+ endBuffered: -1,
3504
+ previousEnd: -1,
3505
+ previousStart: -1,
3506
+ start: -1,
3507
+ startBuffered: -1,
3508
+ viewableItems: []
3509
+ };
2330
3510
  map.set(configId, state);
2331
3511
  }
2332
3512
  return state;
@@ -2346,7 +3526,7 @@ function setupViewability(props) {
2346
3526
  }
2347
3527
  return viewabilityConfigCallbackPairs;
2348
3528
  }
2349
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
3529
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
2350
3530
  const {
2351
3531
  timeouts,
2352
3532
  props: { data }
@@ -2355,6 +3535,8 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
2355
3535
  const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
2356
3536
  viewabilityState.start = start;
2357
3537
  viewabilityState.end = end;
3538
+ viewabilityState.startBuffered = startBuffered;
3539
+ viewabilityState.endBuffered = endBuffered;
2358
3540
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
2359
3541
  const timer = setTimeout(() => {
2360
3542
  timeouts.delete(timer);
@@ -2370,7 +3552,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2370
3552
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
2371
3553
  const configId = viewabilityConfig.id;
2372
3554
  const viewabilityState = ensureViewabilityState(ctx, configId);
2373
- const { viewableItems: previousViewableItems, start, end } = viewabilityState;
3555
+ const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
2374
3556
  const viewabilityTokens = /* @__PURE__ */ new Map();
2375
3557
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
2376
3558
  viewabilityTokens.set(
@@ -2439,7 +3621,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2439
3621
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
2440
3622
  }
2441
3623
  if (onViewableItemsChanged) {
2442
- onViewableItemsChanged({ changed, viewableItems });
3624
+ onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
2443
3625
  }
2444
3626
  }
2445
3627
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -2535,23 +3717,6 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
2535
3717
  var unstableBatchedUpdates = ReactNative__namespace.unstable_batchedUpdates;
2536
3718
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
2537
3719
 
2538
- // src/utils/checkAllSizesKnown.ts
2539
- function isNullOrUndefined2(value) {
2540
- return value === null || value === void 0;
2541
- }
2542
- function checkAllSizesKnown(state) {
2543
- const { startBuffered, endBuffered, sizesKnown } = state;
2544
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
2545
- let areAllKnown = true;
2546
- for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
2547
- const key = getId(state, i);
2548
- areAllKnown && (areAllKnown = sizesKnown.has(key));
2549
- }
2550
- return areAllKnown;
2551
- }
2552
- return false;
2553
- }
2554
-
2555
3720
  // src/utils/findAvailableContainers.ts
2556
3721
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2557
3722
  const numContainers = peek$(ctx, "numContainers");
@@ -2674,97 +3839,11 @@ function comparatorByDistance(a, b) {
2674
3839
  return b.distance - a.distance;
2675
3840
  }
2676
3841
 
2677
- // src/core/scrollToIndex.ts
2678
- function scrollToIndex(ctx, {
2679
- index,
2680
- viewOffset = 0,
2681
- animated = true,
2682
- forceScroll,
2683
- isInitialScroll,
2684
- viewPosition
2685
- }) {
2686
- const state = ctx.state;
2687
- const { data } = state.props;
2688
- if (index >= data.length) {
2689
- index = data.length - 1;
2690
- } else if (index < 0) {
2691
- index = 0;
2692
- }
2693
- const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2694
- const isLast = index === data.length - 1;
2695
- if (isLast && viewPosition === void 0) {
2696
- viewPosition = 1;
2697
- }
2698
- state.scrollForNextCalculateItemsInView = void 0;
2699
- const targetId = getId(state, index);
2700
- const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2701
- scrollTo(ctx, {
2702
- animated,
2703
- forceScroll,
2704
- index,
2705
- isInitialScroll,
2706
- itemSize,
2707
- offset: firstIndexOffset,
2708
- viewOffset,
2709
- viewPosition: viewPosition != null ? viewPosition : 0
2710
- });
2711
- }
2712
-
2713
- // src/utils/performInitialScroll.ts
2714
- function performInitialScroll(ctx, params) {
2715
- var _a3;
2716
- const { forceScroll, initialScrollUsesOffset, resolvedOffset, target } = params;
2717
- if (initialScrollUsesOffset || resolvedOffset !== void 0) {
2718
- scrollTo(ctx, {
2719
- animated: false,
2720
- forceScroll,
2721
- index: initialScrollUsesOffset ? void 0 : target.index,
2722
- isInitialScroll: true,
2723
- offset: (_a3 = resolvedOffset != null ? resolvedOffset : target.contentOffset) != null ? _a3 : 0,
2724
- precomputedWithViewOffset: resolvedOffset !== void 0
2725
- });
2726
- return;
2727
- }
2728
- if (target.index === void 0) {
2729
- return;
2730
- }
2731
- scrollToIndex(ctx, {
2732
- ...target,
2733
- animated: false,
2734
- forceScroll,
2735
- isInitialScroll: true
2736
- });
2737
- }
2738
-
2739
3842
  // src/utils/setDidLayout.ts
2740
3843
  function setDidLayout(ctx) {
2741
3844
  const state = ctx.state;
2742
- const { initialScroll } = state;
2743
3845
  state.queuedInitialLayout = true;
2744
3846
  checkAtBottom(ctx);
2745
- if (initialScroll) {
2746
- const runScroll = () => {
2747
- var _a3, _b;
2748
- const target = state.initialScroll;
2749
- if (!target) {
2750
- return;
2751
- }
2752
- const activeInitialTargetOffset = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? (_b = state.scrollingTo.targetOffset) != null ? _b : state.scrollingTo.offset : void 0;
2753
- const desiredInitialTargetOffset = state.initialScrollUsesOffset ? target.contentOffset : activeInitialTargetOffset;
2754
- const isAlreadyAtDesiredInitialTarget = desiredInitialTargetOffset !== void 0 && Math.abs(state.scroll - desiredInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - desiredInitialTargetOffset) <= 1;
2755
- if (!isAlreadyAtDesiredInitialTarget) {
2756
- performInitialScroll(ctx, {
2757
- forceScroll: true,
2758
- initialScrollUsesOffset: state.initialScrollUsesOffset,
2759
- // Offset-based initial scrolls do not need item lookup, so they can run even before data exists.
2760
- // Re-run on the next frame to pick up measured viewport size without waiting for index resolution.
2761
- target
2762
- });
2763
- }
2764
- };
2765
- runScroll();
2766
- requestAnimationFrame(runScroll);
2767
- }
2768
3847
  setInitialRenderState(ctx, { didLayout: true });
2769
3848
  }
2770
3849
 
@@ -2839,7 +3918,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
2839
3918
  function calculateItemsInView(ctx, params = {}) {
2840
3919
  const state = ctx.state;
2841
3920
  batchedUpdates(() => {
2842
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
3921
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
2843
3922
  const {
2844
3923
  columns,
2845
3924
  columnSpans,
@@ -2847,7 +3926,6 @@ function calculateItemsInView(ctx, params = {}) {
2847
3926
  enableScrollForNextCalculateItemsInView,
2848
3927
  idCache,
2849
3928
  indexByKey,
2850
- initialScroll,
2851
3929
  minIndexSizeChanged,
2852
3930
  positions,
2853
3931
  props: {
@@ -2855,7 +3933,6 @@ function calculateItemsInView(ctx, params = {}) {
2855
3933
  alwaysRenderIndicesSet,
2856
3934
  drawDistance,
2857
3935
  getItemType,
2858
- itemsAreEqual,
2859
3936
  keyExtractor,
2860
3937
  onStickyHeaderChange
2861
3938
  },
@@ -2871,11 +3948,10 @@ function calculateItemsInView(ctx, params = {}) {
2871
3948
  const alwaysRenderArr = alwaysRenderIndicesArr || [];
2872
3949
  const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2873
3950
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
3951
+ const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
3952
+ const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
2874
3953
  const prevNumContainers = peek$(ctx, "numContainers");
2875
3954
  if (!data || scrollLength === 0 || !prevNumContainers) {
2876
- if (!IsNewArchitecture && state.initialAnchor) {
2877
- ensureInitialAnchor(ctx);
2878
- }
2879
3955
  return;
2880
3956
  }
2881
3957
  let totalSize = getContentSize(ctx);
@@ -2883,17 +3959,13 @@ function calculateItemsInView(ctx, params = {}) {
2883
3959
  const numColumns = peek$(ctx, "numColumns");
2884
3960
  const speed = getScrollVelocity(state);
2885
3961
  const scrollExtra = 0;
2886
- const { queuedInitialLayout } = state;
2887
- let { scroll: scrollState } = state;
2888
- if (!queuedInitialLayout && initialScroll) {
2889
- const updatedOffset = state.initialScrollUsesOffset ? (_a3 = initialScroll.contentOffset) != null ? _a3 : 0 : calculateOffsetWithOffsetPosition(
2890
- ctx,
2891
- calculateOffsetForIndex(ctx, initialScroll.index),
2892
- initialScroll
2893
- );
2894
- scrollState = updatedOffset;
2895
- }
2896
- const scrollAdjustPending = (_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0;
3962
+ const { initialScroll, queuedInitialLayout } = state;
3963
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
3964
+ // Before the initial layout settles, keep viewport math anchored to the
3965
+ // current initial-scroll target instead of transient native adjustments.
3966
+ resolveInitialScrollOffset(ctx, initialScroll)
3967
+ ) : state.scroll;
3968
+ const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
2897
3969
  const scrollAdjustPad = scrollAdjustPending - topPad;
2898
3970
  let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2899
3971
  if (scroll + scrollLength > totalSize) {
@@ -2917,20 +3989,17 @@ function calculateItemsInView(ctx, params = {}) {
2917
3989
  const scrollTopBuffered = scroll - scrollBufferTop;
2918
3990
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2919
3991
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2920
- if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
3992
+ if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2921
3993
  const { top, bottom } = scrollForNextCalculateItemsInView;
2922
3994
  if (top === null && bottom === null) {
2923
3995
  state.scrollForNextCalculateItemsInView = void 0;
2924
3996
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2925
- if (!IsNewArchitecture && state.initialAnchor) {
2926
- ensureInitialAnchor(ctx);
2927
- }
2928
3997
  if (Platform2.OS !== "web" || !isInMVCPActiveMode(state)) {
2929
3998
  return;
2930
3999
  }
2931
4000
  }
2932
4001
  }
2933
- const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
4002
+ const checkMVCP = doMVCP && !suppressInitialScrollSideEffects ? prepareMVCP(ctx, dataChanged) : void 0;
2934
4003
  if (dataChanged) {
2935
4004
  indexByKey.clear();
2936
4005
  idCache.length = 0;
@@ -2938,10 +4007,12 @@ function calculateItemsInView(ctx, params = {}) {
2938
4007
  columns.length = 0;
2939
4008
  columnSpans.length = 0;
2940
4009
  }
2941
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4010
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4011
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
2942
4012
  updateItemPositions(ctx, dataChanged, {
2943
4013
  doMVCP,
2944
4014
  forceFullUpdate: !!forceFullItemPositions,
4015
+ optimizeForVisibleWindow,
2945
4016
  scrollBottomBuffered,
2946
4017
  startIndex
2947
4018
  });
@@ -2955,11 +4026,11 @@ function calculateItemsInView(ctx, params = {}) {
2955
4026
  let startBufferedId = null;
2956
4027
  let endNoBuffer = null;
2957
4028
  let endBuffered = null;
2958
- let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4029
+ let loopStart = (_e = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _e : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2959
4030
  for (let i = loopStart; i >= 0; i--) {
2960
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
4031
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2961
4032
  const top = positions[i];
2962
- const size = (_e = sizes.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i]);
4033
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
2963
4034
  const bottom = top + size;
2964
4035
  if (bottom > scroll - scrollBufferTop) {
2965
4036
  loopStart = i;
@@ -2990,8 +4061,8 @@ function calculateItemsInView(ctx, params = {}) {
2990
4061
  let firstFullyOnScreenIndex;
2991
4062
  const dataLength = data.length;
2992
4063
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2993
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2994
- const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
4064
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4065
+ const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
2995
4066
  const top = positions[i];
2996
4067
  if (!foundEnd) {
2997
4068
  if (startNoBuffer === null && top + size > scroll) {
@@ -3030,7 +4101,7 @@ function calculateItemsInView(ctx, params = {}) {
3030
4101
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
3031
4102
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
3032
4103
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
3033
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4104
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
3034
4105
  idsInView.push(id);
3035
4106
  }
3036
4107
  }
@@ -3063,7 +4134,7 @@ function calculateItemsInView(ctx, params = {}) {
3063
4134
  const needNewContainers = [];
3064
4135
  const needNewContainersSet = /* @__PURE__ */ new Set();
3065
4136
  for (let i = startBuffered; i <= endBuffered; i++) {
3066
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4137
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
3067
4138
  if (!containerItemKeys.has(id)) {
3068
4139
  needNewContainersSet.add(i);
3069
4140
  needNewContainers.push(i);
@@ -3072,7 +4143,7 @@ function calculateItemsInView(ctx, params = {}) {
3072
4143
  if (alwaysRenderArr.length > 0) {
3073
4144
  for (const index of alwaysRenderArr) {
3074
4145
  if (index < 0 || index >= dataLength) continue;
3075
- const id = (_j = idCache[index]) != null ? _j : getId(state, index);
4146
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3076
4147
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
3077
4148
  needNewContainersSet.add(index);
3078
4149
  needNewContainers.push(index);
@@ -3110,7 +4181,7 @@ function calculateItemsInView(ctx, params = {}) {
3110
4181
  for (let idx = 0; idx < needNewContainers.length; idx++) {
3111
4182
  const i = needNewContainers[idx];
3112
4183
  const containerIndex = availableContainers[idx];
3113
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4184
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
3114
4185
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
3115
4186
  if (oldKey && oldKey !== id) {
3116
4187
  containerItemKeys.delete(oldKey);
@@ -3151,7 +4222,7 @@ function calculateItemsInView(ctx, params = {}) {
3151
4222
  if (alwaysRenderArr.length > 0) {
3152
4223
  for (const index of alwaysRenderArr) {
3153
4224
  if (index < 0 || index >= dataLength) continue;
3154
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
4225
+ const id = (_n = idCache[index]) != null ? _n : getId(state, index);
3155
4226
  const containerIndex = containerItemKeys.get(id);
3156
4227
  if (containerIndex !== void 0) {
3157
4228
  state.stickyContainerPool.add(containerIndex);
@@ -3189,46 +4260,39 @@ function calculateItemsInView(ctx, params = {}) {
3189
4260
  set$(ctx, `containerSpan${i}`, 1);
3190
4261
  } else {
3191
4262
  const itemIndex = indexByKey.get(itemKey);
3192
- const item = data[itemIndex];
3193
- if (item !== void 0) {
3194
- const positionValue = positions[itemIndex];
3195
- if (positionValue === void 0) {
3196
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
3197
- } else {
3198
- const position = (positionValue || 0) - scrollAdjustPending;
3199
- const column = columns[itemIndex] || 1;
3200
- const span = columnSpans[itemIndex] || 1;
3201
- const prevPos = peek$(ctx, `containerPosition${i}`);
3202
- const prevColumn = peek$(ctx, `containerColumn${i}`);
3203
- const prevSpan = peek$(ctx, `containerSpan${i}`);
3204
- const prevData = peek$(ctx, `containerItemData${i}`);
3205
- if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3206
- set$(ctx, `containerPosition${i}`, position);
3207
- didChangePositions = true;
3208
- }
3209
- if (column >= 0 && column !== prevColumn) {
3210
- set$(ctx, `containerColumn${i}`, column);
3211
- }
3212
- if (span !== prevSpan) {
3213
- set$(ctx, `containerSpan${i}`, span);
3214
- }
3215
- if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
3216
- set$(ctx, `containerItemData${i}`, item);
3217
- }
3218
- }
4263
+ if (itemIndex !== void 0) {
4264
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4265
+ scrollAdjustPending,
4266
+ updateLayout: true
4267
+ }).didChangePosition || didChangePositions;
3219
4268
  }
3220
4269
  }
3221
4270
  }
3222
4271
  if (Platform2.OS === "web" && didChangePositions) {
3223
4272
  set$(ctx, "lastPositionUpdate", Date.now());
3224
4273
  }
3225
- if (!queuedInitialLayout && endBuffered !== null) {
3226
- if (checkAllSizesKnown(state)) {
3227
- setDidLayout(ctx);
3228
- }
4274
+ if (suppressInitialScrollSideEffects) {
4275
+ evaluateBootstrapInitialScroll(ctx);
4276
+ return;
4277
+ }
4278
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4279
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4280
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4281
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4282
+ setDidLayout(ctx);
4283
+ handleInitialScrollLayoutReady(ctx);
3229
4284
  }
3230
- if (viewabilityConfigCallbackPairs) {
3231
- updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
4285
+ if (viewabilityConfigCallbackPairs && startNoBuffer !== null && endNoBuffer !== null) {
4286
+ updateViewableItems(
4287
+ ctx.state,
4288
+ ctx,
4289
+ viewabilityConfigCallbackPairs,
4290
+ scrollLength,
4291
+ startNoBuffer,
4292
+ endNoBuffer,
4293
+ startBuffered != null ? startBuffered : startNoBuffer,
4294
+ endBuffered != null ? endBuffered : endNoBuffer
4295
+ );
3232
4296
  }
3233
4297
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
3234
4298
  const item = data[nextActiveStickyIndex];
@@ -3237,93 +4301,65 @@ function calculateItemsInView(ctx, params = {}) {
3237
4301
  }
3238
4302
  }
3239
4303
  });
3240
- if (!IsNewArchitecture && state.initialAnchor) {
3241
- ensureInitialAnchor(ctx);
3242
- }
3243
4304
  }
3244
4305
 
3245
- // src/core/checkActualChange.ts
3246
- function checkActualChange(state, dataProp, previousData) {
3247
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
3248
- return true;
3249
- }
4306
+ // src/core/doMaintainScrollAtEnd.ts
4307
+ function doMaintainScrollAtEnd(ctx) {
4308
+ const state = ctx.state;
3250
4309
  const {
3251
- idCache,
3252
- props: { keyExtractor }
4310
+ didContainersLayout,
4311
+ pendingNativeMVCPAdjust,
4312
+ refScroller,
4313
+ props: { maintainScrollAtEnd }
3253
4314
  } = state;
3254
- for (let i = 0; i < dataProp.length; i++) {
3255
- if (dataProp[i] !== previousData[i]) {
3256
- return true;
3257
- }
3258
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
3259
- return true;
3260
- }
4315
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4316
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4317
+ if (pendingNativeMVCPAdjust) {
4318
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4319
+ return false;
3261
4320
  }
3262
- return false;
3263
- }
3264
-
3265
- // src/utils/updateAveragesOnDataChange.ts
3266
- function updateAveragesOnDataChange(state, oldData, newData) {
3267
- var _a3;
3268
- const {
3269
- averageSizes,
3270
- sizesKnown,
3271
- indexByKey,
3272
- props: { itemsAreEqual, getItemType, keyExtractor }
3273
- } = state;
3274
- if (!itemsAreEqual || !oldData.length || !newData.length) {
3275
- for (const key in averageSizes) {
3276
- delete averageSizes[key];
4321
+ state.pendingMaintainScrollAtEnd = false;
4322
+ if (shouldMaintainScrollAtEnd) {
4323
+ const contentSize = getContentSize(ctx);
4324
+ if (contentSize < state.scrollLength) {
4325
+ state.scroll = 0;
3277
4326
  }
3278
- return;
3279
- }
3280
- const itemTypesToPreserve = {};
3281
- const newDataLength = newData.length;
3282
- const oldDataLength = oldData.length;
3283
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
3284
- const newItem = newData[newIndex];
3285
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
3286
- const oldIndex = indexByKey.get(id);
3287
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
3288
- const knownSize = sizesKnown.get(id);
3289
- if (knownSize === void 0) continue;
3290
- const oldItem = oldData[oldIndex];
3291
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
3292
- if (areEqual) {
3293
- const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
3294
- let typeData = itemTypesToPreserve[itemType];
3295
- if (!typeData) {
3296
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
3297
- }
3298
- typeData.totalSize += knownSize;
3299
- typeData.count++;
4327
+ requestAnimationFrame(() => {
4328
+ var _a3;
4329
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4330
+ state.maintainingScrollAtEnd = true;
4331
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4332
+ animated: maintainScrollAtEnd.animated
4333
+ });
4334
+ setTimeout(
4335
+ () => {
4336
+ state.maintainingScrollAtEnd = false;
4337
+ },
4338
+ maintainScrollAtEnd.animated ? 500 : 0
4339
+ );
3300
4340
  }
3301
- }
3302
- }
3303
- for (const key in averageSizes) {
3304
- delete averageSizes[key];
3305
- }
3306
- for (const itemType in itemTypesToPreserve) {
3307
- const { totalSize, count } = itemTypesToPreserve[itemType];
3308
- if (count > 0) {
3309
- averageSizes[itemType] = {
3310
- avg: totalSize / count,
3311
- num: count
3312
- };
3313
- }
4341
+ });
4342
+ return true;
3314
4343
  }
4344
+ return false;
3315
4345
  }
3316
4346
 
3317
4347
  // src/core/checkResetContainers.ts
3318
- function checkResetContainers(ctx, dataProp) {
4348
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
3319
4349
  const state = ctx.state;
3320
4350
  const { previousData } = state;
3321
- if (previousData) {
3322
- updateAveragesOnDataChange(state, previousData, dataProp);
3323
- }
3324
4351
  const { maintainScrollAtEnd } = state.props;
4352
+ if (didColumnsChange) {
4353
+ state.sizes.clear();
4354
+ state.sizesKnown.clear();
4355
+ for (const key in state.averageSizes) {
4356
+ delete state.averageSizes[key];
4357
+ }
4358
+ state.minIndexSizeChanged = 0;
4359
+ state.scrollForNextCalculateItemsInView = void 0;
4360
+ }
3325
4361
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
3326
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4362
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
3327
4363
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
3328
4364
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
3329
4365
  state.isEndReached = false;
@@ -3334,6 +4370,53 @@ function checkResetContainers(ctx, dataProp) {
3334
4370
  delete state.previousData;
3335
4371
  }
3336
4372
 
4373
+ // src/core/checkStructuralDataChange.ts
4374
+ function checkStructuralDataChange(state, dataProp, previousData) {
4375
+ var _a3;
4376
+ state.pendingDataComparison = void 0;
4377
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4378
+ return true;
4379
+ }
4380
+ const {
4381
+ idCache,
4382
+ props: { itemsAreEqual, keyExtractor }
4383
+ } = state;
4384
+ let byIndex;
4385
+ for (let i = 0; i < dataProp.length; i++) {
4386
+ if (dataProp[i] === previousData[i]) {
4387
+ continue;
4388
+ }
4389
+ if (!keyExtractor) {
4390
+ if (byIndex) {
4391
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4392
+ }
4393
+ return true;
4394
+ }
4395
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4396
+ const nextKey = keyExtractor(dataProp[i], i);
4397
+ if (previousKey !== nextKey) {
4398
+ if (byIndex) {
4399
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4400
+ }
4401
+ return true;
4402
+ }
4403
+ if (!itemsAreEqual) {
4404
+ if (byIndex) {
4405
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4406
+ }
4407
+ return true;
4408
+ }
4409
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4410
+ byIndex != null ? byIndex : byIndex = [];
4411
+ byIndex[i] = isEqual ? 1 : 2;
4412
+ if (!isEqual) {
4413
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4414
+ return true;
4415
+ }
4416
+ }
4417
+ return false;
4418
+ }
4419
+
3337
4420
  // src/core/doInitialAllocateContainers.ts
3338
4421
  function doInitialAllocateContainers(ctx) {
3339
4422
  var _a3, _b, _c;
@@ -3422,43 +4505,130 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3422
4505
  if (scrollLength > 0) {
3423
4506
  doInitialAllocateContainers(ctx);
3424
4507
  }
3425
- if (needsCalculate) {
3426
- calculateItemsInView(ctx, { doMVCP: true });
4508
+ if (needsCalculate) {
4509
+ calculateItemsInView(ctx, { doMVCP: true });
4510
+ }
4511
+ if (didChange || otherAxisSize !== prevOtherAxisSize) {
4512
+ set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
4513
+ }
4514
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
4515
+ doMaintainScrollAtEnd(ctx);
4516
+ }
4517
+ checkThresholds(ctx);
4518
+ if (state) {
4519
+ state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
4520
+ }
4521
+ if (IS_DEV && measuredLength === 0) {
4522
+ warnDevOnce(
4523
+ "height0",
4524
+ `List ${state.props.horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
4525
+ );
4526
+ }
4527
+ }
4528
+ setCanRender(true);
4529
+ }
4530
+
4531
+ // src/platform/flushSync.native.ts
4532
+ var flushSync = (fn) => {
4533
+ fn();
4534
+ };
4535
+
4536
+ // src/core/updateScroll.ts
4537
+ function updateScroll(ctx, newScroll, forceUpdate) {
4538
+ var _a3;
4539
+ const state = ctx.state;
4540
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
4541
+ const prevScroll = state.scroll;
4542
+ state.hasScrolled = true;
4543
+ state.lastBatchingAction = Date.now();
4544
+ const currentTime = Date.now();
4545
+ const adjust = scrollAdjustHandler.getAdjust();
4546
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
4547
+ if (adjustChanged) {
4548
+ scrollHistory.length = 0;
4549
+ }
4550
+ state.lastScrollAdjustForHistory = adjust;
4551
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
4552
+ if (!adjustChanged) {
4553
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
4554
+ }
4555
+ }
4556
+ if (scrollHistory.length > 5) {
4557
+ scrollHistory.shift();
4558
+ }
4559
+ if (ignoreScrollFromMVCP && !scrollingTo) {
4560
+ const { lt, gt } = ignoreScrollFromMVCP;
4561
+ if (lt && newScroll < lt || gt && newScroll > gt) {
4562
+ state.ignoreScrollFromMVCPIgnored = true;
4563
+ return;
3427
4564
  }
3428
- if (didChange || otherAxisSize !== prevOtherAxisSize) {
3429
- set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
4565
+ }
4566
+ state.scrollPrev = prevScroll;
4567
+ state.scrollPrevTime = state.scrollTime;
4568
+ state.scroll = newScroll;
4569
+ state.scrollTime = currentTime;
4570
+ const scrollDelta = Math.abs(newScroll - prevScroll);
4571
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
4572
+ const scrollLength = state.scrollLength;
4573
+ const lastCalculated = state.scrollLastCalculate;
4574
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
4575
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
4576
+ if (shouldUpdate) {
4577
+ state.scrollLastCalculate = state.scroll;
4578
+ state.ignoreScrollFromMVCPIgnored = false;
4579
+ state.lastScrollDelta = scrollDelta;
4580
+ const runCalculateItems = () => {
4581
+ var _a4;
4582
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
4583
+ checkThresholds(ctx);
4584
+ };
4585
+ if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
4586
+ flushSync(runCalculateItems);
4587
+ } else {
4588
+ runCalculateItems();
3430
4589
  }
3431
- if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
4590
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
4591
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
4592
+ state.pendingMaintainScrollAtEnd = false;
3432
4593
  doMaintainScrollAtEnd(ctx);
3433
4594
  }
3434
- checkThresholds(ctx);
3435
- if (state) {
3436
- state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
3437
- }
3438
- if (IS_DEV && measuredLength === 0) {
3439
- warnDevOnce(
3440
- "height0",
3441
- `List ${state.props.horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
3442
- );
3443
- }
4595
+ state.dataChangeNeedsScrollUpdate = false;
4596
+ state.lastScrollDelta = 0;
3444
4597
  }
3445
- setCanRender(true);
3446
4598
  }
3447
4599
 
3448
4600
  // src/core/onScroll.ts
3449
- var INITIAL_SCROLL_PROGRESS_EPSILON = 1;
3450
- function didObserveInitialScrollProgress(newScroll, watchdog) {
3451
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
3452
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
3453
- return nextDistance <= INITIAL_SCROLL_PROGRESS_EPSILON || nextDistance + INITIAL_SCROLL_PROGRESS_EPSILON < previousDistance;
4601
+ function trackInitialScrollNativeProgress(state, newScroll) {
4602
+ const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
4603
+ const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
4604
+ if (didInitialScrollProgress) {
4605
+ initialScrollWatchdog.clear(state);
4606
+ return;
4607
+ }
4608
+ if (initialNativeScrollWatchdog) {
4609
+ state.hasScrolled = false;
4610
+ initialScrollWatchdog.set(state, {
4611
+ startScroll: initialNativeScrollWatchdog.startScroll,
4612
+ targetOffset: initialNativeScrollWatchdog.targetOffset
4613
+ });
4614
+ }
4615
+ }
4616
+ function shouldDeferPublicOnScroll(state) {
4617
+ var _a3;
4618
+ return Platform2.OS === "web" && !!state.initialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" && !state.didFinishInitialScroll;
4619
+ }
4620
+ function cloneScrollEvent(event) {
4621
+ return {
4622
+ ...event,
4623
+ nativeEvent: {
4624
+ ...event.nativeEvent
4625
+ }
4626
+ };
3454
4627
  }
3455
4628
  function onScroll(ctx, event) {
3456
4629
  var _a3, _b, _c, _d;
3457
4630
  const state = ctx.state;
3458
- const {
3459
- scrollProcessingEnabled,
3460
- props: { onScroll: onScrollProp }
3461
- } = state;
4631
+ const { scrollProcessingEnabled } = state;
3462
4632
  if (scrollProcessingEnabled === false) {
3463
4633
  return;
3464
4634
  }
@@ -3489,20 +4659,19 @@ function onScroll(ctx, event) {
3489
4659
  }
3490
4660
  }
3491
4661
  state.scrollPending = newScroll;
3492
- const initialNativeScrollWatchdog = state.initialNativeScrollWatchdog;
3493
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && didObserveInitialScrollProgress(newScroll, initialNativeScrollWatchdog);
3494
- if (didInitialScrollProgress) {
3495
- state.initialNativeScrollWatchdog = void 0;
3496
- }
3497
4662
  updateScroll(ctx, newScroll, insetChanged);
3498
- if (initialNativeScrollWatchdog && !didInitialScrollProgress) {
3499
- state.hasScrolled = false;
3500
- state.initialNativeScrollWatchdog = initialNativeScrollWatchdog;
3501
- }
4663
+ trackInitialScrollNativeProgress(state, newScroll);
4664
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
3502
4665
  if (state.scrollingTo) {
3503
4666
  checkFinishedScroll(ctx);
3504
4667
  }
3505
- onScrollProp == null ? void 0 : onScrollProp(event);
4668
+ if (state.props.onScroll) {
4669
+ if (shouldDeferPublicOnScroll(state)) {
4670
+ state.deferredPublicOnScrollEvent = cloneScrollEvent(event);
4671
+ } else {
4672
+ state.props.onScroll(event);
4673
+ }
4674
+ }
3506
4675
  }
3507
4676
 
3508
4677
  // src/core/ScrollAdjustHandler.ts
@@ -3556,6 +4725,43 @@ var ScrollAdjustHandler = class {
3556
4725
  }
3557
4726
  };
3558
4727
 
4728
+ // src/core/updateAnchoredEndSpace.ts
4729
+ function maybeUpdateAnchoredEndSpace(ctx) {
4730
+ var _a3;
4731
+ const state = ctx.state;
4732
+ const anchoredEndSpace = state.props.anchoredEndSpace;
4733
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
4734
+ let nextSize = 0;
4735
+ if (anchoredEndSpace) {
4736
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
4737
+ const { data } = state.props;
4738
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
4739
+ let contentBelowAnchor = 0;
4740
+ const footerSize = ctx.values.get("footerSize") || 0;
4741
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4742
+ for (let index = anchorIndex; index < data.length; index++) {
4743
+ const itemKey = getId(state, index);
4744
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
4745
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
4746
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
4747
+ contentBelowAnchor += effectiveSize;
4748
+ }
4749
+ }
4750
+ contentBelowAnchor += footerSize + stylePaddingBottom;
4751
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4752
+ }
4753
+ }
4754
+ if (previousSize === nextSize) {
4755
+ return nextSize;
4756
+ }
4757
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
4758
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4759
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
4760
+ updateScroll(ctx, state.scroll, true);
4761
+ }
4762
+ return nextSize;
4763
+ }
4764
+
3559
4765
  // src/core/updateItemSize.ts
3560
4766
  function runOrScheduleMVCPRecalculate(ctx) {
3561
4767
  const state = ctx.state;
@@ -3639,6 +4845,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
3639
4845
  previous: size - diff,
3640
4846
  size
3641
4847
  });
4848
+ maybeUpdateAnchoredEndSpace(ctx);
3642
4849
  }
3643
4850
  if (minIndexSizeChanged !== void 0) {
3644
4851
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -3769,26 +4976,13 @@ function createColumnWrapperStyle(contentContainerStyle) {
3769
4976
  }
3770
4977
  }
3771
4978
 
3772
- // src/utils/hasActiveMVCPAnchorLock.ts
3773
- function hasActiveMVCPAnchorLock(state) {
3774
- const lock = state.mvcpAnchorLock;
3775
- if (!lock) {
3776
- return false;
3777
- }
3778
- if (Date.now() > lock.expiresAt) {
3779
- state.mvcpAnchorLock = void 0;
3780
- return false;
3781
- }
3782
- return true;
3783
- }
3784
-
3785
4979
  // src/utils/createImperativeHandle.ts
3786
4980
  function createImperativeHandle(ctx) {
3787
4981
  const state = ctx.state;
3788
4982
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
3789
4983
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
3790
4984
  let imperativeScrollToken = 0;
3791
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
4985
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
3792
4986
  const runWhenSettled = (token, run) => {
3793
4987
  const startedAt = Date.now();
3794
4988
  let stableFrames = 0;
@@ -3810,9 +5004,10 @@ function createImperativeHandle(ctx) {
3810
5004
  };
3811
5005
  requestAnimationFrame(check);
3812
5006
  };
3813
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5007
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
3814
5008
  var _a3;
3815
5009
  const token = ++imperativeScrollToken;
5010
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
3816
5011
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
3817
5012
  state.pendingScrollResolve = resolve;
3818
5013
  const runNow = () => {
@@ -3827,11 +5022,12 @@ function createImperativeHandle(ctx) {
3827
5022
  resolve();
3828
5023
  }
3829
5024
  };
5025
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
3830
5026
  if (isSettlingAfterDataChange()) {
3831
- runWhenSettled(token, runNow);
3832
- return;
5027
+ runWhenSettled(token, execute);
5028
+ } else {
5029
+ execute();
3833
5030
  }
3834
- runNow();
3835
5031
  });
3836
5032
  const scrollIndexIntoView = (options) => {
3837
5033
  if (state) {
@@ -3888,10 +5084,13 @@ function createImperativeHandle(ctx) {
3888
5084
  },
3889
5085
  end: state.endNoBuffer,
3890
5086
  endBuffered: state.endBuffered,
3891
- isAtEnd: state.isAtEnd,
3892
- isAtStart: state.isAtStart,
5087
+ isAtEnd: peek$(ctx, "isAtEnd"),
5088
+ isAtStart: peek$(ctx, "isAtStart"),
3893
5089
  isEndReached: state.isEndReached,
5090
+ isNearEnd: peek$(ctx, "isNearEnd"),
5091
+ isNearStart: peek$(ctx, "isNearStart"),
3894
5092
  isStartReached: state.isStartReached,
5093
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
3895
5094
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
3896
5095
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3897
5096
  positionAtIndex: (index) => state.positions[index],
@@ -3938,10 +5137,15 @@ function createImperativeHandle(ctx) {
3938
5137
  }
3939
5138
  return false;
3940
5139
  }),
3941
- scrollToIndex: (params) => runScrollWithPromise(() => {
3942
- scrollToIndex(ctx, params);
3943
- return true;
3944
- }),
5140
+ scrollToIndex: (params) => runScrollWithPromise(
5141
+ () => {
5142
+ scrollToIndex(ctx, params);
5143
+ return true;
5144
+ },
5145
+ {
5146
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5147
+ }
5148
+ ),
3945
5149
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
3946
5150
  const data = state.props.data;
3947
5151
  const index = data.indexOf(item);
@@ -4037,7 +5241,7 @@ function getRenderedItem(ctx, key) {
4037
5241
  item,
4038
5242
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
4039
5243
  };
4040
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React2__namespace.default.createElement(renderItem, itemProps);
5244
+ renderedItem = React2__namespace.default.createElement(renderItem, itemProps);
4041
5245
  }
4042
5246
  return { index, item: data[index], renderedItem };
4043
5247
  }
@@ -4152,7 +5356,7 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
4152
5356
  }
4153
5357
 
4154
5358
  // src/components/LegendList.tsx
4155
- var LegendList = typedMemo2(
5359
+ var LegendList = typedMemo(
4156
5360
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
4157
5361
  typedForwardRef(function LegendList2(props, forwardedRef) {
4158
5362
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
@@ -4172,8 +5376,17 @@ var LegendList = typedMemo2(
4172
5376
  );
4173
5377
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
4174
5378
  var _a3, _b, _c, _d, _e, _f, _g, _h;
5379
+ const noopOnScroll = React2.useCallback((_event) => {
5380
+ }, []);
5381
+ if (props.recycleItems === void 0) {
5382
+ warnDevOnce(
5383
+ "recycleItems-omitted",
5384
+ "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."
5385
+ );
5386
+ }
4175
5387
  const {
4176
5388
  alignItemsAtEnd = false,
5389
+ anchoredEndSpace,
4177
5390
  alwaysRender,
4178
5391
  columnWrapperStyle,
4179
5392
  contentContainerStyle: contentContainerStyleProp,
@@ -4195,6 +5408,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4195
5408
  itemsAreEqual,
4196
5409
  keyExtractor: keyExtractorProp,
4197
5410
  ListEmptyComponent,
5411
+ ListFooterComponent,
5412
+ ListFooterComponentStyle,
4198
5413
  ListHeaderComponent,
4199
5414
  maintainScrollAtEnd = false,
4200
5415
  maintainScrollAtEndThreshold = 0.1,
@@ -4231,14 +5446,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4231
5446
  useWindowScroll = false,
4232
5447
  viewabilityConfig,
4233
5448
  viewabilityConfigCallbackPairs,
4234
- waitForInitialLayout = true,
4235
5449
  ...rest
4236
5450
  } = props;
4237
5451
  const animatedPropsInternal = props.animatedPropsInternal;
4238
5452
  const positionComponentInternal = props.positionComponentInternal;
4239
5453
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
4240
5454
  const {
4241
- childrenMode,
4242
5455
  positionComponentInternal: _positionComponentInternal,
4243
5456
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
4244
5457
  ...restProps
@@ -4264,8 +5477,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4264
5477
  const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
4265
5478
  const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
4266
5479
  const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
4267
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
5480
+ const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
5481
+ const initialScrollProp = initialScrollAtEnd ? {
5482
+ index: Math.max(0, dataProp.length - 1),
5483
+ preserveForBottomPadding: true,
5484
+ viewOffset: -stylePaddingBottomState,
5485
+ viewPosition: 1
5486
+ } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
4268
5487
  index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
5488
+ preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
4269
5489
  viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
4270
5490
  viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
4271
5491
  } : {
@@ -4295,18 +5515,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4295
5515
  dataVersion,
4296
5516
  keyExtractor
4297
5517
  ]);
4298
- if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
4299
- warnDevOnce(
4300
- "stickyIndices",
4301
- "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
4302
- );
4303
- }
4304
- if (IS_DEV && useWindowScroll && renderScrollComponent) {
4305
- warnDevOnce(
4306
- "useWindowScrollRenderScrollComponent",
4307
- "useWindowScroll is not supported when renderScrollComponent is provided."
4308
- );
4309
- }
4310
5518
  const useWindowScrollResolved = Platform2.OS === "web" && !!useWindowScroll && !renderScrollComponent;
4311
5519
  const refState = React2.useRef(void 0);
4312
5520
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -4315,7 +5523,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4315
5523
  if (!ctx.state) {
4316
5524
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
4317
5525
  ctx.state = {
4318
- activeStickyIndex: -1,
4319
5526
  averageSizes: {},
4320
5527
  columnSpans: [],
4321
5528
  columns: [],
@@ -4334,24 +5541,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4334
5541
  idCache: [],
4335
5542
  idsInView: [],
4336
5543
  indexByKey: /* @__PURE__ */ new Map(),
4337
- initialAnchor: !initialScrollUsesOffsetOnly && (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4338
- attempts: 0,
4339
- index: initialScrollProp.index,
4340
- settledTicks: 0,
4341
- viewOffset: (_f = initialScrollProp.viewOffset) != null ? _f : 0,
4342
- viewPosition: initialScrollProp.viewPosition
4343
- } : void 0,
4344
- initialNativeScrollWatchdog: void 0,
4345
5544
  initialScroll: initialScrollProp,
4346
- initialScrollLastDidFinish: false,
4347
- initialScrollLastTarget: initialScrollProp,
4348
- initialScrollLastTargetUsesOffset: initialScrollUsesOffsetOnly,
4349
- initialScrollPreviousDataLength: dataProp.length,
4350
- initialScrollRetryLastLength: void 0,
4351
- initialScrollRetryWindowUntil: 0,
4352
- initialScrollUsesOffset: initialScrollUsesOffsetOnly,
4353
- isAtEnd: false,
4354
- isAtStart: false,
5545
+ initialScrollSession: initialScrollProp ? {
5546
+ kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5547
+ previousDataLength: dataProp.length
5548
+ } : void 0,
4355
5549
  isEndReached: null,
4356
5550
  isFirst: true,
4357
5551
  isStartReached: null,
@@ -4362,6 +5556,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4362
5556
  minIndexSizeChanged: 0,
4363
5557
  nativeContentInset: void 0,
4364
5558
  nativeMarginTop: 0,
5559
+ pendingDataComparison: void 0,
4365
5560
  pendingNativeMVCPAdjust: void 0,
4366
5561
  positions: [],
4367
5562
  props: {},
@@ -4392,6 +5587,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4392
5587
  };
4393
5588
  const internalState = ctx.state;
4394
5589
  internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
5590
+ internalState.reprocessCurrentScroll = () => updateScroll(ctx, internalState.scroll, true);
4395
5591
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
4396
5592
  set$(ctx, "extraData", extraData);
4397
5593
  }
@@ -4399,22 +5595,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4399
5595
  }
4400
5596
  const state = refState.current;
4401
5597
  const isFirstLocal = state.isFirst;
4402
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
5598
+ const previousNumColumnsProp = state.props.numColumns;
5599
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
4403
5600
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
4404
5601
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
4405
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
5602
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
5603
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5604
+ clearPreservedInitialScrollTarget(state);
5605
+ }
4406
5606
  if (didDataChangeLocal) {
4407
5607
  state.dataChangeEpoch += 1;
4408
5608
  state.dataChangeNeedsScrollUpdate = true;
4409
5609
  state.didDataChange = true;
4410
5610
  state.previousData = state.props.data;
4411
5611
  }
4412
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
5612
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
5613
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
5614
+ const anchoredEndSpaceResolved = Platform2.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
4413
5615
  state.props = {
4414
5616
  alignItemsAtEnd,
4415
5617
  alwaysRender,
4416
5618
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
4417
5619
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
5620
+ anchoredEndSpace: anchoredEndSpaceResolved,
4418
5621
  animatedProps: animatedPropsInternal,
4419
5622
  contentInset,
4420
5623
  data: dataProp,
@@ -4483,311 +5686,87 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4483
5686
  true
4484
5687
  );
4485
5688
  }
4486
- const resolveInitialScrollOffset = React2.useCallback((initialScroll) => {
4487
- var _a4;
4488
- if (state.initialScrollUsesOffset) {
4489
- return clampScrollOffset(ctx, (_a4 = initialScroll.contentOffset) != null ? _a4 : 0);
4490
- }
4491
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4492
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4493
- return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4494
- }, []);
4495
- const finishInitialScrollWithoutScroll = React2.useCallback(() => {
4496
- refState.current.initialAnchor = void 0;
4497
- refState.current.initialScroll = void 0;
4498
- state.initialAnchor = void 0;
4499
- state.initialScroll = void 0;
4500
- state.initialScrollUsesOffset = false;
4501
- state.initialScrollLastTarget = void 0;
4502
- state.initialScrollLastTargetUsesOffset = false;
4503
- setInitialRenderState(ctx, { didInitialScroll: true });
4504
- }, []);
4505
- const setActiveInitialScrollTarget = React2.useCallback(
4506
- (target, options) => {
4507
- var _a4;
4508
- const usesOffset = !!(options == null ? void 0 : options.usesOffset);
4509
- state.initialScrollUsesOffset = usesOffset;
4510
- state.initialScrollLastTarget = target;
4511
- state.initialScrollLastTargetUsesOffset = usesOffset;
4512
- refState.current.initialScroll = target;
4513
- state.initialScroll = target;
4514
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
4515
- state.didFinishInitialScroll = false;
4516
- }
4517
- if (!(options == null ? void 0 : options.syncAnchor)) {
4518
- return;
4519
- }
4520
- if (!IsNewArchitecture && !usesOffset && target.index !== void 0 && target.viewPosition !== void 0) {
4521
- state.initialAnchor = {
4522
- attempts: 0,
4523
- index: target.index,
4524
- settledTicks: 0,
4525
- viewOffset: (_a4 = target.viewOffset) != null ? _a4 : 0,
4526
- viewPosition: target.viewPosition
4527
- };
4528
- }
4529
- },
4530
- []
4531
- );
4532
- const shouldFinishInitialScrollAtOrigin = React2.useCallback(
4533
- (initialScroll, offset) => {
4534
- var _a4, _b2, _c2;
4535
- if (offset !== 0 || initialScrollAtEnd) {
4536
- return false;
4537
- }
4538
- if (state.initialScrollUsesOffset) {
4539
- return Math.abs((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) <= 1;
4540
- }
4541
- return initialScroll.index === 0 && ((_b2 = initialScroll.viewPosition) != null ? _b2 : 0) === 0 && Math.abs((_c2 = initialScroll.viewOffset) != null ? _c2 : 0) <= 1;
4542
- },
4543
- [initialScrollAtEnd]
4544
- );
4545
- const shouldFinishEmptyInitialScrollAtEnd = React2.useCallback(
4546
- (initialScroll, offset) => {
4547
- return dataProp.length === 0 && initialScrollAtEnd && offset === 0 && initialScroll.viewPosition === 1;
4548
- },
4549
- [dataProp.length, initialScrollAtEnd]
4550
- );
4551
- const shouldRearmFinishedEmptyInitialScrollAtEnd = React2.useCallback(
4552
- (initialScroll) => {
4553
- var _a4;
4554
- return !!(state.didFinishInitialScroll && dataProp.length > 0 && initialScroll && !state.initialScrollUsesOffset && initialScroll.index === 0 && initialScroll.viewPosition === 1 && ((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) === 0);
4555
- },
4556
- [dataProp.length]
4557
- );
4558
5689
  const initialContentOffset = React2.useMemo(() => {
4559
- var _a4;
4560
- let value;
4561
- const { initialScroll, initialAnchor } = refState.current;
4562
- if (initialScroll) {
4563
- if (!state.initialScrollUsesOffset && !IsNewArchitecture && initialScroll.index !== void 0 && (!initialAnchor || (initialAnchor == null ? void 0 : initialAnchor.index) !== initialScroll.index)) {
4564
- refState.current.initialAnchor = {
4565
- attempts: 0,
4566
- index: initialScroll.index,
4567
- settledTicks: 0,
4568
- viewOffset: (_a4 = initialScroll.viewOffset) != null ? _a4 : 0,
4569
- viewPosition: initialScroll.viewPosition
4570
- };
4571
- }
4572
- if (initialScroll.contentOffset !== void 0) {
4573
- value = initialScroll.contentOffset;
4574
- } else {
4575
- const clampedOffset = resolveInitialScrollOffset(initialScroll);
4576
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4577
- setActiveInitialScrollTarget(updatedInitialScroll, {
4578
- usesOffset: state.initialScrollUsesOffset
4579
- });
4580
- value = clampedOffset;
4581
- }
4582
- } else {
4583
- refState.current.initialAnchor = void 0;
4584
- value = 0;
4585
- }
4586
- const hasPendingDataDependentInitialScroll = !!initialScroll && dataProp.length === 0 && !shouldFinishInitialScrollAtOrigin(initialScroll, value) && !shouldFinishEmptyInitialScrollAtEnd(initialScroll, value);
4587
- if (!value && !hasPendingDataDependentInitialScroll) {
4588
- if (initialScroll && shouldFinishInitialScrollAtOrigin(initialScroll, value)) {
4589
- finishInitialScrollWithoutScroll();
4590
- } else {
4591
- setInitialRenderState(ctx, { didInitialScroll: true });
4592
- }
5690
+ var _a4, _b2;
5691
+ const initialScroll = state.initialScroll;
5692
+ if (!initialScroll) {
5693
+ return void 0;
4593
5694
  }
4594
- return value;
5695
+ const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);
5696
+ return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform2.OS === "web" ? void 0 : resolvedOffset;
5697
+ }, [usesBootstrapInitialScroll]);
5698
+ React2.useLayoutEffect(() => {
5699
+ initializeInitialScrollOnMount(ctx, {
5700
+ dataLength: dataProp.length,
5701
+ hasFooterComponent: !!ListFooterComponent,
5702
+ initialContentOffset,
5703
+ initialScrollAtEnd,
5704
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
5705
+ });
4595
5706
  }, []);
4596
5707
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
4597
5708
  refState.current.lastBatchingAction = Date.now();
4598
5709
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
4599
- IS_DEV && !childrenMode && warnDevOnce(
4600
- "keyExtractor",
4601
- "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."
4602
- );
4603
5710
  refState.current.sizes.clear();
4604
5711
  refState.current.positions.length = 0;
4605
5712
  refState.current.totalSize = 0;
4606
5713
  set$(ctx, "totalSize", 0);
4607
5714
  }
4608
5715
  }
4609
- const doInitialScroll = React2.useCallback((options) => {
4610
- var _a4, _b2;
4611
- const allowPostFinishRetry = !!(options == null ? void 0 : options.allowPostFinishRetry);
4612
- const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4613
- const initialScroll = (_a4 = state.initialScroll) != null ? _a4 : allowPostFinishRetry ? state.initialScrollLastTarget : void 0;
4614
- const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
4615
- const needsContainerLayoutForInitialScroll = !state.initialScrollUsesOffset;
4616
- const shouldWaitForInitialLayout = waitForInitialLayout && needsContainerLayoutForInitialScroll && !queuedInitialLayout && !allowPostFinishRetry && !isInitialScrollInProgress;
4617
- if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll && !allowPostFinishRetry || scrollingTo && !isInitialScrollInProgress) {
4618
- return;
4619
- }
4620
- if (allowPostFinishRetry && state.initialScrollLastTargetUsesOffset) {
4621
- return;
4622
- }
4623
- const didMoveAwayFromInitialTarget = allowPostFinishRetry && initialScroll.contentOffset !== void 0 && Math.abs(state.scroll - initialScroll.contentOffset) > 1;
4624
- if (didMoveAwayFromInitialTarget) {
4625
- state.initialScrollRetryWindowUntil = 0;
4626
- return;
4627
- }
4628
- const offset = resolveInitialScrollOffset(initialScroll);
4629
- const activeInitialTargetOffset = isInitialScrollInProgress ? (_b2 = scrollingTo.targetOffset) != null ? _b2 : scrollingTo.offset : void 0;
4630
- const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - offset) > 1;
4631
- const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - offset) > 1;
4632
- if (!didOffsetChange && (allowPostFinishRetry || isInitialScrollInProgress && !didActiveInitialTargetChange)) {
4633
- return;
4634
- }
4635
- if (didOffsetChange) {
4636
- const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
4637
- if (!state.initialScrollUsesOffset) {
4638
- state.initialScrollLastTarget = updatedInitialScroll;
4639
- state.initialScrollLastTargetUsesOffset = false;
4640
- if (state.initialScroll) {
4641
- refState.current.initialScroll = updatedInitialScroll;
4642
- state.initialScroll = updatedInitialScroll;
4643
- }
4644
- }
4645
- }
4646
- const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
4647
- const shouldForceNativeInitialScroll = state.initialScrollUsesOffset && hasMeasuredScrollLayout || allowPostFinishRetry || !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
4648
- performInitialScroll(ctx, {
4649
- forceScroll: shouldForceNativeInitialScroll,
4650
- initialScrollUsesOffset: state.initialScrollUsesOffset,
4651
- resolvedOffset: offset,
4652
- target: initialScroll
4653
- });
4654
- }, []);
4655
- React2.useLayoutEffect(() => {
4656
- var _a4;
4657
- const previousDataLength = state.initialScrollPreviousDataLength;
4658
- state.initialScrollPreviousDataLength = dataProp.length;
4659
- if (previousDataLength !== 0 || dataProp.length === 0 || !state.initialScroll || !state.queuedInitialLayout) {
4660
- return;
4661
- }
4662
- if (initialScrollAtEnd) {
4663
- const lastIndex = Math.max(0, dataProp.length - 1);
4664
- const initialScroll = state.initialScroll;
4665
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
4666
- if (state.didFinishInitialScroll && !shouldRearm) {
4667
- return;
4668
- }
4669
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
4670
- return;
4671
- }
4672
- const updatedInitialScroll = {
4673
- contentOffset: void 0,
4674
- index: lastIndex,
4675
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
4676
- viewPosition: 1
4677
- };
4678
- setActiveInitialScrollTarget(updatedInitialScroll, {
4679
- resetDidFinish: shouldRearm,
4680
- syncAnchor: true
4681
- });
4682
- doInitialScroll();
4683
- return;
4684
- }
4685
- if (state.didFinishInitialScroll) {
4686
- return;
4687
- }
4688
- doInitialScroll();
4689
- }, [
4690
- dataProp.length,
4691
- doInitialScroll,
4692
- initialScrollAtEnd,
4693
- shouldRearmFinishedEmptyInitialScrollAtEnd,
4694
- stylePaddingBottomState
4695
- ]);
5716
+ if (IS_DEV) {
5717
+ useDevChecks(props);
5718
+ }
4696
5719
  React2.useLayoutEffect(() => {
4697
- var _a4;
4698
- if (!initialScrollAtEnd) {
4699
- return;
4700
- }
4701
- const lastIndex = Math.max(0, dataProp.length - 1);
4702
- const initialScroll = state.initialScroll;
4703
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
4704
- if (state.didFinishInitialScroll && !shouldRearm) {
4705
- return;
4706
- }
4707
- if (shouldRearm) {
4708
- state.didFinishInitialScroll = false;
4709
- }
4710
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
4711
- return;
4712
- }
4713
- const updatedInitialScroll = {
4714
- contentOffset: void 0,
4715
- index: lastIndex,
4716
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
4717
- viewPosition: 1
4718
- };
4719
- setActiveInitialScrollTarget(updatedInitialScroll, {
4720
- resetDidFinish: shouldRearm,
4721
- syncAnchor: true
5720
+ handleInitialScrollDataChange(ctx, {
5721
+ dataLength: dataProp.length,
5722
+ didDataChange: didDataChangeLocal,
5723
+ initialScrollAtEnd,
5724
+ stylePaddingBottom: stylePaddingBottomState,
5725
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
4722
5726
  });
4723
- doInitialScroll();
5727
+ }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
5728
+ React2.useLayoutEffect(() => {
5729
+ maybeUpdateAnchoredEndSpace(ctx);
4724
5730
  }, [
4725
- dataProp.length,
4726
- doInitialScroll,
4727
- initialScrollAtEnd,
4728
- shouldRearmFinishedEmptyInitialScrollAtEnd,
4729
- stylePaddingBottomState
5731
+ ctx,
5732
+ dataProp,
5733
+ dataVersion,
5734
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
5735
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
5736
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
5737
+ numColumnsProp
4730
5738
  ]);
4731
5739
  const onLayoutFooter = React2.useCallback(
4732
5740
  (layout) => {
4733
- var _a4;
4734
- if (!initialScrollAtEnd) {
5741
+ if (!usesBootstrapInitialScroll) {
4735
5742
  return;
4736
5743
  }
4737
- const { initialScroll } = state;
4738
- if (!initialScroll) {
4739
- return;
5744
+ handleBootstrapInitialScrollFooterLayout(ctx, {
5745
+ dataLength: dataProp.length,
5746
+ footerSize: layout[horizontal ? "width" : "height"],
5747
+ initialScrollAtEnd,
5748
+ stylePaddingBottom: stylePaddingBottomState
5749
+ });
5750
+ },
5751
+ [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5752
+ );
5753
+ const onLayoutChange = React2.useCallback(
5754
+ (layout, fromLayoutEffect) => {
5755
+ const previousScrollLength = state.scrollLength;
5756
+ const previousOtherAxisSize = state.otherAxisSize;
5757
+ handleLayout(ctx, layout, setCanRender);
5758
+ maybeUpdateAnchoredEndSpace(ctx);
5759
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
5760
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
5761
+ handleBootstrapInitialScrollLayoutChange(ctx);
4740
5762
  }
4741
- const lastIndex = Math.max(0, dataProp.length - 1);
4742
- if (initialScroll.index !== lastIndex || initialScroll.viewPosition !== 1) {
5763
+ if (usesBootstrapInitialScroll) {
4743
5764
  return;
4744
5765
  }
4745
- const footerSize = layout[horizontal ? "width" : "height"];
4746
- const viewOffset = -stylePaddingBottomState - footerSize;
4747
- if (initialScroll.viewOffset !== viewOffset) {
4748
- const previousTargetOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(initialScroll);
4749
- const didMoveAwayFromFinishedInitialTarget = state.didFinishInitialScroll && Math.abs(state.scroll - previousTargetOffset) > 1;
4750
- if (didMoveAwayFromFinishedInitialTarget) {
4751
- return;
4752
- }
4753
- const updatedInitialScroll = { ...initialScroll, viewOffset };
4754
- setActiveInitialScrollTarget(updatedInitialScroll, {
4755
- resetDidFinish: true
4756
- });
4757
- doInitialScroll();
4758
- }
5766
+ advanceCurrentInitialScrollSession(ctx);
4759
5767
  },
4760
- [
4761
- dataProp.length,
4762
- doInitialScroll,
4763
- horizontal,
4764
- initialScrollAtEnd,
4765
- resolveInitialScrollOffset,
4766
- stylePaddingBottomState
4767
- ]
5768
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
4768
5769
  );
4769
- const onLayoutChange = React2.useCallback((layout) => {
4770
- var _a4;
4771
- handleLayout(ctx, layout, setCanRender);
4772
- const SCROLL_LENGTH_RETRY_WINDOW_MS = 600;
4773
- const now = Date.now();
4774
- const didFinishInitialScroll = !!state.didFinishInitialScroll;
4775
- if (didFinishInitialScroll && !state.initialScrollLastDidFinish) {
4776
- state.initialScrollRetryWindowUntil = now + SCROLL_LENGTH_RETRY_WINDOW_MS;
4777
- }
4778
- state.initialScrollLastDidFinish = didFinishInitialScroll;
4779
- const previousScrollLength = state.initialScrollRetryLastLength;
4780
- const currentScrollLength = state.scrollLength;
4781
- const didScrollLengthChange = previousScrollLength === void 0 || Math.abs(currentScrollLength - previousScrollLength) > 1;
4782
- if (didScrollLengthChange) {
4783
- state.initialScrollRetryLastLength = currentScrollLength;
4784
- }
4785
- if (didFinishInitialScroll && didScrollLengthChange && now <= state.initialScrollRetryWindowUntil && !state.initialScrollLastTargetUsesOffset && ((_a4 = state.initialScrollLastTarget) == null ? void 0 : _a4.index) !== void 0) {
4786
- doInitialScroll({ allowPostFinishRetry: true });
4787
- return;
4788
- }
4789
- doInitialScroll();
4790
- }, []);
4791
5770
  const { onLayout } = useOnLayoutSync({
4792
5771
  onLayoutChange,
4793
5772
  onLayoutProp,
@@ -4799,6 +5778,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4799
5778
  updateSnapToOffsets(ctx);
4800
5779
  }
4801
5780
  }, [snapToIndices]);
5781
+ React2.useLayoutEffect(
5782
+ () => initializeStateVars(true),
5783
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5784
+ );
4802
5785
  React2.useLayoutEffect(() => {
4803
5786
  const {
4804
5787
  didColumnsChange,
@@ -4808,7 +5791,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4808
5791
  } = state;
4809
5792
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
4810
5793
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
4811
- checkResetContainers(ctx, data);
5794
+ checkResetContainers(ctx, data, { didColumnsChange });
5795
+ }
5796
+ if (didDataChange) {
5797
+ state.pendingDataComparison = void 0;
4812
5798
  }
4813
5799
  state.didColumnsChange = false;
4814
5800
  state.didDataChange = false;
@@ -4823,10 +5809,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4823
5809
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
4824
5810
  }
4825
5811
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
4826
- React2.useLayoutEffect(
4827
- () => initializeStateVars(true),
4828
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
4829
- );
4830
5812
  React2.useEffect(() => {
4831
5813
  if (!onMetricsChange) {
4832
5814
  return;
@@ -4859,15 +5841,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4859
5841
  state.viewabilityConfigCallbackPairs = viewability;
4860
5842
  state.enableScrollForNextCalculateItemsInView = !viewability;
4861
5843
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
4862
- if (!IsNewArchitecture) {
4863
- useInit(() => {
5844
+ useInit(() => {
5845
+ if (!IsNewArchitecture) {
4864
5846
  doInitialAllocateContainers(ctx);
4865
- });
4866
- }
5847
+ }
5848
+ });
4867
5849
  React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
4868
- if (Platform2.OS === "web") {
4869
- React2.useEffect(doInitialScroll, []);
4870
- }
5850
+ React2.useEffect(() => {
5851
+ if (Platform2.OS !== "web" || usesBootstrapInitialScroll) {
5852
+ return;
5853
+ }
5854
+ advanceCurrentInitialScrollSession(ctx);
5855
+ }, [ctx, usesBootstrapInitialScroll]);
4871
5856
  const fns = React2.useMemo(
4872
5857
  () => ({
4873
5858
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -4896,6 +5881,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4896
5881
  horizontal,
4897
5882
  initialContentOffset,
4898
5883
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
5884
+ ListFooterComponent,
5885
+ ListFooterComponentStyle,
4899
5886
  ListHeaderComponent,
4900
5887
  onLayout,
4901
5888
  onLayoutFooter,
@@ -4920,8 +5907,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4920
5907
  stickyHeaderIndices,
4921
5908
  style,
4922
5909
  updateItemSize: fns.updateItemSize,
4923
- useWindowScroll: useWindowScrollResolved,
4924
- waitForInitialLayout
5910
+ useWindowScroll: useWindowScrollResolved
4925
5911
  }
4926
5912
  ), IS_DEV && ENABLE_DEBUG_VIEW);
4927
5913
  });
@@ -4930,9 +5916,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4930
5916
  var LegendListRuntime = LegendList;
4931
5917
  var internal = {
4932
5918
  getComponent,
5919
+ getStickyPushLimit,
4933
5920
  IsNewArchitecture,
4934
5921
  POSITION_OUT_OF_VIEW,
4935
5922
  peek$,
5923
+ typedForwardRef,
5924
+ typedMemo,
4936
5925
  useArr$,
4937
5926
  useCombinedRef,
4938
5927
  useStateContext
@@ -4944,8 +5933,6 @@ var internal2 = internal;
4944
5933
 
4945
5934
  exports.LegendList = LegendList3;
4946
5935
  exports.internal = internal2;
4947
- exports.typedForwardRef = typedForwardRef;
4948
- exports.typedMemo = typedMemo2;
4949
5936
  exports.useIsLastItem = useIsLastItem;
4950
5937
  exports.useListScrollSize = useListScrollSize;
4951
5938
  exports.useRecyclingEffect = useRecyclingEffect;