@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.mjs CHANGED
@@ -8,37 +8,6 @@ import { useSyncExternalStore } from 'use-sync-external-store/shim';
8
8
  Animated.View;
9
9
  var View = View$1;
10
10
  var Text = Text$1;
11
-
12
- // src/state/getContentInsetEnd.ts
13
- function getContentInsetEnd(state) {
14
- var _a3;
15
- const { props } = state;
16
- const horizontal = props.horizontal;
17
- const contentInset = props.contentInset;
18
- const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
19
- const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
20
- if (overrideInset) {
21
- const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
22
- return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
23
- }
24
- if (baseInset) {
25
- return (horizontal ? baseInset.right : baseInset.bottom) || 0;
26
- }
27
- return 0;
28
- }
29
-
30
- // src/state/getContentSize.ts
31
- function getContentSize(ctx) {
32
- var _a3;
33
- const { values, state } = ctx;
34
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
35
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
36
- const headerSize = values.get("headerSize") || 0;
37
- const footerSize = values.get("footerSize") || 0;
38
- const contentInsetBottom = getContentInsetEnd(state);
39
- const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
40
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
41
- }
42
11
  var createAnimatedValue = (value) => new Animated.Value(value);
43
12
 
44
13
  // src/state/state.tsx
@@ -62,6 +31,11 @@ function StateProvider({ children }) {
62
31
  ["headerSize", 0],
63
32
  ["numContainers", 0],
64
33
  ["activeStickyIndex", -1],
34
+ ["isAtEnd", false],
35
+ ["isAtStart", false],
36
+ ["isNearEnd", false],
37
+ ["isNearStart", false],
38
+ ["isWithinMaintainScrollAtEndThreshold", false],
65
39
  ["totalSize", 0],
66
40
  ["scrollAdjustPending", 0]
67
41
  ]),
@@ -153,29 +127,71 @@ function notifyPosition$(ctx, key, value) {
153
127
  function useArr$(signalNames) {
154
128
  const ctx = React2.useContext(ContextState);
155
129
  const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
156
- const value = useSyncExternalStore(subscribe, get);
130
+ const value = useSyncExternalStore(subscribe, get, get);
157
131
  return value;
158
132
  }
159
133
  function useSelector$(signalName, selector) {
160
134
  const ctx = React2.useContext(ContextState);
161
135
  const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
162
- const value = useSyncExternalStore(subscribe, () => selector(get()[0]));
136
+ const getSelectedValue = React2.useCallback(() => selector(get()[0]), [get, selector]);
137
+ const value = useSyncExternalStore(subscribe, getSelectedValue, getSelectedValue);
163
138
  return value;
164
139
  }
165
140
 
141
+ // src/state/getContentInsetEnd.ts
142
+ function getContentInsetEnd(ctx) {
143
+ var _a3, _b;
144
+ const state = ctx.state;
145
+ const { props } = state;
146
+ const horizontal = props.horizontal;
147
+ const contentInset = props.contentInset;
148
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
149
+ const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
150
+ const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
151
+ const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
152
+ const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
153
+ if (overrideInset) {
154
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
155
+ return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
156
+ }
157
+ return Math.max(baseEndInset, anchoredEndInset);
158
+ }
159
+
160
+ // src/state/getContentSize.ts
161
+ function getContentSize(ctx) {
162
+ var _a3;
163
+ const { values, state } = ctx;
164
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
165
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
166
+ const headerSize = values.get("headerSize") || 0;
167
+ const footerSize = values.get("footerSize") || 0;
168
+ const contentInsetBottom = getContentInsetEnd(ctx);
169
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
170
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
171
+ }
172
+
166
173
  // src/components/DebugView.tsx
167
174
  var DebugRow = ({ children }) => {
168
175
  return /* @__PURE__ */ React2.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
169
176
  };
170
- React2.memo(function DebugView2({ state }) {
177
+ React2.memo(function DebugView2() {
171
178
  const ctx = useStateContext();
172
- const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
179
+ const [
180
+ totalSize = 0,
181
+ scrollAdjust = 0,
182
+ rawScroll = 0,
183
+ scroll = 0,
184
+ _numContainers = 0,
185
+ _numContainersPooled = 0,
186
+ isAtEnd = false
187
+ ] = useArr$([
173
188
  "totalSize",
174
189
  "scrollAdjust",
175
190
  "debugRawScroll",
176
191
  "debugComputedScroll",
177
192
  "numContainers",
178
- "numContainersPooled"
193
+ "numContainersPooled",
194
+ "isAtEnd"
179
195
  ]);
180
196
  const contentSize = getContentSize(ctx);
181
197
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
@@ -200,7 +216,7 @@ React2.memo(function DebugView2({ state }) {
200
216
  },
201
217
  /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React2.createElement(Text, null, totalSize.toFixed(2))),
202
218
  /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React2.createElement(Text, null, contentSize.toFixed(2))),
203
- /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "At end:"), /* @__PURE__ */ React2.createElement(Text, null, String(state.isAtEnd))),
219
+ /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "At end:"), /* @__PURE__ */ React2.createElement(Text, null, String(isAtEnd))),
204
220
  /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React2.createElement(Text, null, scrollAdjust.toFixed(2))),
205
221
  /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React2.createElement(Text, null, rawScroll.toFixed(2))),
206
222
  /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React2.createElement(Text, null, scroll.toFixed(2)))
@@ -213,6 +229,30 @@ function useInterval(callback, delay) {
213
229
  }, [delay]);
214
230
  }
215
231
 
232
+ // src/components/stickyPositionUtils.ts
233
+ function getStickyPushLimit(state, index, itemKey) {
234
+ if (!itemKey) {
235
+ return void 0;
236
+ }
237
+ const currentSize = state.sizes.get(itemKey);
238
+ if (!(currentSize && currentSize > 0)) {
239
+ return void 0;
240
+ }
241
+ const stickyIndexInArray = state.props.stickyIndicesArr.indexOf(index);
242
+ if (stickyIndexInArray === -1) {
243
+ return void 0;
244
+ }
245
+ const nextStickyIndex = state.props.stickyIndicesArr[stickyIndexInArray + 1];
246
+ if (nextStickyIndex === void 0) {
247
+ return void 0;
248
+ }
249
+ const nextStickyPosition = state.positions[nextStickyIndex];
250
+ if (nextStickyPosition === void 0) {
251
+ return void 0;
252
+ }
253
+ return nextStickyPosition - currentSize;
254
+ }
255
+
216
256
  // src/utils/devEnvironment.ts
217
257
  var metroDev = typeof __DEV__ !== "undefined" ? __DEV__ : void 0;
218
258
  var _a;
@@ -223,6 +263,7 @@ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 :
223
263
 
224
264
  // src/constants.ts
225
265
  var POSITION_OUT_OF_VIEW = -1e7;
266
+ var EDGE_POSITION_EPSILON = 1;
226
267
  var ENABLE_DEVMODE = IS_DEV && false;
227
268
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
228
269
 
@@ -234,93 +275,26 @@ var useAnimatedValue = (initialValue) => {
234
275
  return animAnimatedValue;
235
276
  };
236
277
 
237
- // src/utils/helpers.ts
238
- function isFunction(obj) {
239
- return typeof obj === "function";
240
- }
241
- function isArray(obj) {
242
- return Array.isArray(obj);
243
- }
244
- var warned = /* @__PURE__ */ new Set();
245
- function warnDevOnce(id, text) {
246
- if (IS_DEV && !warned.has(id)) {
247
- warned.add(id);
248
- console.warn(`[legend-list] ${text}`);
249
- }
250
- }
251
- function roundSize(size) {
252
- return Math.floor(size * 8) / 8;
253
- }
254
- function isNullOrUndefined(value) {
255
- return value === null || value === void 0;
256
- }
257
- function comparatorDefault(a, b) {
258
- return a - b;
259
- }
260
- function getPadding(s, type) {
261
- var _a3, _b, _c;
262
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
263
- }
264
- function extractPadding(style, contentContainerStyle, type) {
265
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
266
- }
267
- function findContainerId(ctx, key) {
268
- var _a3, _b;
269
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
270
- if (directMatch !== void 0) {
271
- return directMatch;
272
- }
273
- const numContainers = peek$(ctx, "numContainers");
274
- for (let i = 0; i < numContainers; i++) {
275
- const itemKey = peek$(ctx, `containerItemKey${i}`);
276
- if (itemKey === key) {
277
- return i;
278
- }
279
- }
280
- return -1;
281
- }
282
-
283
278
  // src/hooks/useValue$.ts
284
279
  function useValue$(key, params) {
285
- const { getValue, delay } = params || {};
280
+ const { getValue } = params || {};
286
281
  const ctx = useStateContext();
287
282
  const getNewValue = () => {
288
283
  var _a3;
289
284
  return (_a3 = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a3 : 0;
290
285
  };
291
286
  const animValue = useAnimatedValue(getNewValue());
292
- useMemo(() => {
293
- let prevValue;
294
- let didQueueTask = false;
295
- listen$(ctx, key, () => {
296
- const newValue = getNewValue();
297
- if (delay !== void 0) {
298
- const fn = () => {
299
- didQueueTask = false;
300
- const latestValue = getNewValue();
301
- if (latestValue !== void 0) {
302
- animValue.setValue(latestValue);
303
- }
304
- };
305
- const delayValue = isFunction(delay) ? delay(newValue, prevValue) : delay;
306
- prevValue = newValue;
307
- if (!didQueueTask) {
308
- didQueueTask = true;
309
- if (delayValue === void 0) {
310
- fn();
311
- } else if (delayValue === 0) {
312
- queueMicrotask(fn);
313
- } else {
314
- setTimeout(fn, delayValue);
315
- }
316
- }
317
- } else {
318
- animValue.setValue(newValue);
319
- }
320
- });
321
- }, []);
287
+ useLayoutEffect(() => {
288
+ const syncCurrentValue = () => {
289
+ animValue.setValue(getNewValue());
290
+ };
291
+ const unsubscribe = listen$(ctx, key, syncCurrentValue);
292
+ syncCurrentValue();
293
+ return unsubscribe;
294
+ }, [animValue, ctx, key]);
322
295
  return animValue;
323
296
  }
297
+ var typedForwardRef = React2.forwardRef;
324
298
  var typedMemo = React2.memo;
325
299
  var getComponent = (Component) => {
326
300
  if (React2.isValidElement(Component)) {
@@ -340,18 +314,8 @@ var PositionViewState = typedMemo(function PositionViewState2({
340
314
  refView,
341
315
  ...rest
342
316
  }) {
343
- const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
344
- return /* @__PURE__ */ React2.createElement(
345
- View$1,
346
- {
347
- ref: refView,
348
- style: [
349
- style,
350
- horizontal ? { transform: [{ translateX: position }] } : { transform: [{ translateY: position }] }
351
- ],
352
- ...rest
353
- }
354
- );
317
+ const [position = POSITION_OUT_OF_VIEW, _itemKey] = useArr$([`containerPosition${id}`, `containerItemKey${id}`]);
318
+ return /* @__PURE__ */ React2.createElement(View$1, { ref: refView, style: [style, horizontal ? { left: position } : { top: position }], ...rest });
355
319
  });
356
320
  var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
357
321
  id,
@@ -382,25 +346,46 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
382
346
  children,
383
347
  ...rest
384
348
  }) {
385
- const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0] = useArr$([
349
+ const ctx = useStateContext();
350
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0, itemKey, _totalSize = 0] = useArr$([
386
351
  `containerPosition${id}`,
387
352
  "headerSize",
388
- "stylePaddingTop"
353
+ "stylePaddingTop",
354
+ `containerItemKey${id}`,
355
+ "totalSize"
389
356
  ]);
357
+ const pushLimit = React2.useMemo(
358
+ () => getStickyPushLimit(ctx.state, index, itemKey),
359
+ [ctx.state, index, itemKey, _totalSize]
360
+ );
390
361
  const transform = React2.useMemo(() => {
391
362
  var _a3;
392
363
  if (animatedScrollY) {
393
364
  const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
394
365
  const stickyStart = position + headerSize + stylePaddingTop - stickyConfigOffset;
395
- const stickyPosition = animatedScrollY.interpolate({
396
- extrapolateLeft: "clamp",
397
- extrapolateRight: "extend",
398
- inputRange: [stickyStart, stickyStart + 5e3],
399
- outputRange: [position, position + 5e3]
400
- });
366
+ let stickyPosition;
367
+ if (pushLimit !== void 0) {
368
+ if (pushLimit <= position) {
369
+ stickyPosition = pushLimit;
370
+ } else {
371
+ stickyPosition = animatedScrollY.interpolate({
372
+ extrapolateLeft: "clamp",
373
+ extrapolateRight: "clamp",
374
+ inputRange: [stickyStart, stickyStart + (pushLimit - position)],
375
+ outputRange: [position, pushLimit]
376
+ });
377
+ }
378
+ } else {
379
+ stickyPosition = animatedScrollY.interpolate({
380
+ extrapolateLeft: "clamp",
381
+ extrapolateRight: "extend",
382
+ inputRange: [stickyStart, stickyStart + 5e3],
383
+ outputRange: [position, position + 5e3]
384
+ });
385
+ }
401
386
  return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
402
387
  }
403
- }, [animatedScrollY, headerSize, horizontal, position, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
388
+ }, [animatedScrollY, headerSize, horizontal, position, pushLimit, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
404
389
  const viewStyle = React2.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
405
390
  const renderStickyHeaderBackdrop = React2.useMemo(() => {
406
391
  if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
@@ -425,6 +410,52 @@ function useInit(cb) {
425
410
  useState(() => cb());
426
411
  }
427
412
 
413
+ // src/utils/helpers.ts
414
+ function isFunction(obj) {
415
+ return typeof obj === "function";
416
+ }
417
+ function isArray(obj) {
418
+ return Array.isArray(obj);
419
+ }
420
+ var warned = /* @__PURE__ */ new Set();
421
+ function warnDevOnce(id, text) {
422
+ if (IS_DEV && !warned.has(id)) {
423
+ warned.add(id);
424
+ console.warn(`[legend-list] ${text}`);
425
+ }
426
+ }
427
+ function roundSize(size) {
428
+ return Math.floor(size * 8) / 8;
429
+ }
430
+ function isNullOrUndefined(value) {
431
+ return value === null || value === void 0;
432
+ }
433
+ function comparatorDefault(a, b) {
434
+ return a - b;
435
+ }
436
+ function getPadding(s, type) {
437
+ var _a3, _b, _c;
438
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
439
+ }
440
+ function extractPadding(style, contentContainerStyle, type) {
441
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
442
+ }
443
+ function findContainerId(ctx, key) {
444
+ var _a3, _b;
445
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
446
+ if (directMatch !== void 0) {
447
+ return directMatch;
448
+ }
449
+ const numContainers = peek$(ctx, "numContainers");
450
+ for (let i = 0; i < numContainers; i++) {
451
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
452
+ if (itemKey === key) {
453
+ return i;
454
+ }
455
+ }
456
+ return -1;
457
+ }
458
+
428
459
  // src/state/ContextContainer.ts
429
460
  var ContextContainer = createContext(null);
430
461
  function useContextContainer() {
@@ -592,11 +623,11 @@ function useOnLayoutSync({
592
623
  const { layout } = event.nativeEvent;
593
624
  if (layout.height !== ((_a3 = lastLayoutRef.current) == null ? void 0 : _a3.height) || layout.width !== ((_b = lastLayoutRef.current) == null ? void 0 : _b.width)) {
594
625
  onLayoutChange(layout, false);
595
- onLayoutProp == null ? void 0 : onLayoutProp(event);
596
626
  lastLayoutRef.current = layout;
597
627
  }
628
+ onLayoutProp == null ? void 0 : onLayoutProp(event);
598
629
  },
599
- [onLayoutChange]
630
+ [onLayoutChange, onLayoutProp]
600
631
  );
601
632
  if (IsNewArchitecture) {
602
633
  useLayoutEffect(() => {
@@ -613,8 +644,6 @@ function useOnLayoutSync({
613
644
  }
614
645
  var Platform2 = Platform;
615
646
  var PlatformAdjustBreaksScroll = Platform2.OS === "android";
616
- var typedForwardRef = React2.forwardRef;
617
- var typedMemo2 = React2.memo;
618
647
 
619
648
  // src/utils/isInMVCPActiveMode.native.ts
620
649
  function isInMVCPActiveMode(state) {
@@ -622,7 +651,7 @@ function isInMVCPActiveMode(state) {
622
651
  }
623
652
 
624
653
  // src/components/Container.tsx
625
- var Container = typedMemo2(function Container2({
654
+ var Container = typedMemo(function Container2({
626
655
  id,
627
656
  recycleItems,
628
657
  horizontal,
@@ -666,17 +695,20 @@ var Container = typedMemo2(function Container2({
666
695
  const { columnGap, rowGap, gap } = columnWrapperStyle;
667
696
  if (horizontal) {
668
697
  paddingStyles = {
698
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
669
699
  paddingRight: columnGap || gap || void 0,
670
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
700
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
671
701
  };
672
702
  } else {
673
703
  paddingStyles = {
674
704
  paddingBottom: rowGap || gap || void 0,
675
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
705
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
706
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
676
707
  };
677
708
  }
678
709
  }
679
710
  return horizontal ? {
711
+ boxSizing: paddingStyles ? "border-box" : void 0,
680
712
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
681
713
  height: otherAxisSize,
682
714
  left: 0,
@@ -684,6 +716,7 @@ var Container = typedMemo2(function Container2({
684
716
  top: otherAxisPos,
685
717
  ...paddingStyles || {}
686
718
  } : {
719
+ boxSizing: paddingStyles ? "border-box" : void 0,
687
720
  left: otherAxisPos,
688
721
  position: "absolute",
689
722
  right: numColumns > 1 ? null : 0,
@@ -810,7 +843,6 @@ var Containers = typedMemo(function Containers2({
810
843
  horizontal,
811
844
  recycleItems,
812
845
  ItemSeparatorComponent,
813
- waitForInitialLayout,
814
846
  stickyHeaderConfig,
815
847
  updateItemSize: updateItemSize2,
816
848
  getRenderedItem: getRenderedItem2
@@ -818,16 +850,9 @@ var Containers = typedMemo(function Containers2({
818
850
  const ctx = useStateContext();
819
851
  const columnWrapperStyle = ctx.columnWrapperStyle;
820
852
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
821
- const animSize = useValue$("totalSize", {
822
- // Use a microtask if increasing the size significantly, otherwise use a timeout
823
- // If this is the initial scroll, we don't want to delay because we want to update the size immediately
824
- delay: (value, prevValue) => {
825
- var _a3;
826
- return !((_a3 = ctx.state) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
827
- }
828
- });
829
- const animOpacity = waitForInitialLayout ? useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 }) : void 0;
830
- const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
853
+ const animSize = useValue$("totalSize");
854
+ const otherAxisSize = useValue$("otherAxisSize");
855
+ const animOpacity = useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 });
831
856
  const containers = [];
832
857
  for (let i = 0; i < numContainers; i++) {
833
858
  containers.push(
@@ -871,17 +896,20 @@ var Containers = typedMemo(function Containers2({
871
896
  });
872
897
  var ListComponentScrollView = Animated.ScrollView;
873
898
  function ScrollAdjust() {
899
+ var _a3;
900
+ const ctx = useStateContext();
874
901
  const bias = 1e7;
875
902
  const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
876
903
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0) + bias;
904
+ const horizontal = !!((_a3 = ctx.state) == null ? void 0 : _a3.props.horizontal);
877
905
  return /* @__PURE__ */ React2.createElement(
878
906
  View$1,
879
907
  {
880
908
  style: {
881
909
  height: 0,
882
- left: 0,
910
+ left: horizontal ? scrollOffset : 0,
883
911
  position: "absolute",
884
- top: scrollOffset,
912
+ top: horizontal ? 0 : scrollOffset,
885
913
  width: 0
886
914
  }
887
915
  }
@@ -891,14 +919,25 @@ function SnapWrapper({ ScrollComponent, ...props }) {
891
919
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
892
920
  return /* @__PURE__ */ React2.createElement(ScrollComponent, { ...props, snapToOffsets });
893
921
  }
922
+ function WebAnchoredEndSpace({ horizontal }) {
923
+ const ctx = useStateContext();
924
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
925
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
926
+ if (!shouldRenderAnchoredEndSpace) {
927
+ return null;
928
+ }
929
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
930
+ return /* @__PURE__ */ React2.createElement("div", { style }, null);
931
+ }
894
932
  var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
895
- const ref = refView != null ? refView : useRef(null);
933
+ const localRef = useRef(null);
934
+ const ref = refView != null ? refView : localRef;
896
935
  const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
897
936
  return /* @__PURE__ */ React2.createElement(View$1, { ...rest, onLayout, ref });
898
937
  };
899
938
 
900
939
  // src/components/ListComponent.tsx
901
- var ListComponent = typedMemo2(function ListComponent2({
940
+ var ListComponent = typedMemo(function ListComponent2({
902
941
  canRender,
903
942
  style,
904
943
  contentContainerStyle,
@@ -907,7 +946,6 @@ var ListComponent = typedMemo2(function ListComponent2({
907
946
  recycleItems,
908
947
  ItemSeparatorComponent,
909
948
  alignItemsAtEnd: _alignItemsAtEnd,
910
- waitForInitialLayout,
911
949
  onScroll: onScroll2,
912
950
  onLayout,
913
951
  ListHeaderComponent,
@@ -929,12 +967,14 @@ var ListComponent = typedMemo2(function ListComponent2({
929
967
  }) {
930
968
  const ctx = useStateContext();
931
969
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
932
- const ScrollComponent = renderScrollComponent ? useMemo(
933
- () => React2.forwardRef(
970
+ const ScrollComponent = useMemo(() => {
971
+ if (!renderScrollComponent) {
972
+ return ListComponentScrollView;
973
+ }
974
+ return React2.forwardRef(
934
975
  (props, ref) => renderScrollComponent({ ...props, ref })
935
- ),
936
- [renderScrollComponent]
937
- ) : ListComponentScrollView;
976
+ );
977
+ }, [renderScrollComponent]);
938
978
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
939
979
  useLayoutEffect(() => {
940
980
  if (!ListHeaderComponent) {
@@ -970,7 +1010,7 @@ var ListComponent = typedMemo2(function ListComponent2({
970
1010
  height: "100%"
971
1011
  } : {}
972
1012
  ],
973
- contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1013
+ contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
974
1014
  horizontal,
975
1015
  maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
976
1016
  onLayout,
@@ -990,170 +1030,229 @@ var ListComponent = typedMemo2(function ListComponent2({
990
1030
  ItemSeparatorComponent,
991
1031
  recycleItems,
992
1032
  stickyHeaderConfig,
993
- updateItemSize: updateItemSize2,
994
- waitForInitialLayout
1033
+ updateItemSize: updateItemSize2
995
1034
  }
996
1035
  ),
997
1036
  ListFooterComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1037
+ Platform2.OS === "web" && /* @__PURE__ */ React2.createElement(WebAnchoredEndSpace, { horizontal }),
998
1038
  IS_DEV && ENABLE_DEVMODE
999
1039
  );
1000
1040
  });
1001
-
1002
- // src/core/calculateOffsetForIndex.ts
1003
- function calculateOffsetForIndex(ctx, index) {
1004
- const state = ctx.state;
1005
- return index !== void 0 ? state.positions[index] || 0 : 0;
1041
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1042
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1043
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1044
+ function useDevChecksImpl(props) {
1045
+ const ctx = useStateContext();
1046
+ const { childrenMode, keyExtractor, renderScrollComponent, stickyHeaderIndices, stickyIndices, useWindowScroll } = props;
1047
+ useEffect(() => {
1048
+ if (stickyIndices && !stickyHeaderIndices) {
1049
+ warnDevOnce(
1050
+ "stickyIndices",
1051
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
1052
+ );
1053
+ }
1054
+ }, [stickyHeaderIndices, stickyIndices]);
1055
+ useEffect(() => {
1056
+ if (useWindowScroll && renderScrollComponent) {
1057
+ warnDevOnce(
1058
+ "useWindowScrollRenderScrollComponent",
1059
+ "useWindowScroll is not supported when renderScrollComponent is provided."
1060
+ );
1061
+ }
1062
+ }, [renderScrollComponent, useWindowScroll]);
1063
+ useEffect(() => {
1064
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1065
+ warnDevOnce(
1066
+ "keyExtractor",
1067
+ "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."
1068
+ );
1069
+ }
1070
+ }, [childrenMode, ctx, keyExtractor]);
1071
+ useEffect(() => {
1072
+ const state = ctx.state;
1073
+ const dataLength = state.props.data.length;
1074
+ const useWindowScrollResolved = state.props.useWindowScroll;
1075
+ if (Platform2.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1076
+ return;
1077
+ }
1078
+ const warnIfUnboundedOuterSize = () => {
1079
+ const readyToRender = peek$(ctx, "readyToRender");
1080
+ const numContainers = peek$(ctx, "numContainers") || 0;
1081
+ const totalSize = peek$(ctx, "totalSize") || 0;
1082
+ const scrollLength = ctx.state.scrollLength || 0;
1083
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1084
+ return;
1085
+ }
1086
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1087
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1088
+ if (rendersAlmostEverything && viewportMatchesContent) {
1089
+ warnDevOnce(
1090
+ "webUnboundedOuterSize",
1091
+ "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."
1092
+ );
1093
+ }
1094
+ };
1095
+ warnIfUnboundedOuterSize();
1096
+ const unsubscribe = [
1097
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1098
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1099
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1100
+ ];
1101
+ return () => {
1102
+ for (const unsub of unsubscribe) {
1103
+ unsub();
1104
+ }
1105
+ };
1106
+ }, [ctx]);
1006
1107
  }
1007
-
1008
- // src/core/getTopOffsetAdjustment.ts
1009
- function getTopOffsetAdjustment(ctx) {
1010
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1108
+ function useDevChecksNoop(_props) {
1011
1109
  }
1110
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1012
1111
 
1013
- // src/utils/getId.ts
1014
- function getId(state, index) {
1015
- const { data, keyExtractor } = state.props;
1016
- if (!data) {
1017
- return "";
1112
+ // src/core/deferredPublicOnScroll.ts
1113
+ function withResolvedContentOffset(state, event, resolvedOffset) {
1114
+ return {
1115
+ ...event,
1116
+ nativeEvent: {
1117
+ ...event.nativeEvent,
1118
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1119
+ }
1120
+ };
1121
+ }
1122
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
1123
+ var _a3, _b, _c, _d;
1124
+ const state = ctx.state;
1125
+ const deferredEvent = state.deferredPublicOnScrollEvent;
1126
+ state.deferredPublicOnScrollEvent = void 0;
1127
+ if (deferredEvent) {
1128
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
1129
+ _c,
1130
+ withResolvedContentOffset(
1131
+ state,
1132
+ deferredEvent,
1133
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
1134
+ )
1135
+ );
1018
1136
  }
1019
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1020
- const id = ret;
1021
- state.idCache[index] = id;
1022
- return id;
1023
1137
  }
1024
1138
 
1025
- // src/core/addTotalSize.ts
1026
- function addTotalSize(ctx, key, add) {
1027
- const state = ctx.state;
1028
- const prevTotalSize = state.totalSize;
1029
- let totalSize = state.totalSize;
1030
- if (key === null) {
1031
- totalSize = add;
1032
- if (state.timeoutSetPaddingTop) {
1033
- clearTimeout(state.timeoutSetPaddingTop);
1034
- state.timeoutSetPaddingTop = void 0;
1035
- }
1036
- } else {
1037
- totalSize += add;
1038
- }
1039
- if (prevTotalSize !== totalSize) {
1040
- if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1041
- state.pendingTotalSize = totalSize;
1042
- } else {
1043
- state.pendingTotalSize = void 0;
1044
- state.totalSize = totalSize;
1045
- set$(ctx, "totalSize", totalSize);
1046
- }
1047
- }
1139
+ // src/core/initialScrollSession.ts
1140
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1141
+ function hasInitialScrollSessionCompletion(completion) {
1142
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
1048
1143
  }
1049
-
1050
- // src/core/setSize.ts
1051
- function setSize(ctx, itemKey, size) {
1052
- const state = ctx.state;
1053
- const { sizes } = state;
1054
- const previousSize = sizes.get(itemKey);
1055
- const diff = previousSize !== void 0 ? size - previousSize : size;
1056
- if (diff !== 0) {
1057
- addTotalSize(ctx, itemKey, diff);
1058
- }
1059
- sizes.set(itemKey, size);
1144
+ function clearInitialScrollSession(state) {
1145
+ state.initialScrollSession = void 0;
1146
+ return void 0;
1060
1147
  }
1061
-
1062
- // src/utils/getItemSize.ts
1063
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1064
- var _a3, _b;
1065
- const state = ctx.state;
1066
- const {
1067
- sizesKnown,
1068
- sizes,
1069
- averageSizes,
1070
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1071
- scrollingTo
1072
- } = state;
1073
- const sizeKnown = sizesKnown.get(key);
1074
- if (sizeKnown !== void 0) {
1075
- return sizeKnown;
1076
- }
1077
- let size;
1078
- if (preferCachedSize) {
1079
- const cachedSize = sizes.get(key);
1080
- if (cachedSize !== void 0) {
1081
- return cachedSize;
1082
- }
1083
- }
1084
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1085
- if (getFixedItemSize) {
1086
- size = getFixedItemSize(data, index, itemType);
1087
- if (size !== void 0) {
1088
- sizesKnown.set(key, size);
1089
- }
1090
- }
1091
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1092
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1093
- if (averageSizeForType !== void 0) {
1094
- size = roundSize(averageSizeForType);
1095
- }
1096
- }
1097
- if (size === void 0) {
1098
- size = sizes.get(key);
1099
- if (size !== void 0) {
1100
- return size;
1101
- }
1102
- }
1103
- if (size === void 0) {
1104
- size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1105
- }
1106
- setSize(ctx, key, size);
1107
- return size;
1148
+ function createInitialScrollSession(options) {
1149
+ const { bootstrap, completion, kind, previousDataLength } = options;
1150
+ return kind === "offset" ? {
1151
+ completion,
1152
+ kind,
1153
+ previousDataLength
1154
+ } : {
1155
+ bootstrap,
1156
+ completion,
1157
+ kind,
1158
+ previousDataLength
1159
+ };
1108
1160
  }
1109
-
1110
- // src/core/calculateOffsetWithOffsetPosition.ts
1111
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1112
- var _a3;
1113
- const state = ctx.state;
1114
- const { index, viewOffset, viewPosition } = params;
1115
- let offset = offsetParam;
1116
- if (viewOffset) {
1117
- offset -= viewOffset;
1161
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
1162
+ var _a4, _b2;
1163
+ if (!state.initialScrollSession) {
1164
+ state.initialScrollSession = createInitialScrollSession({
1165
+ completion: {},
1166
+ kind,
1167
+ previousDataLength: 0
1168
+ });
1169
+ } else if (state.initialScrollSession.kind !== kind) {
1170
+ state.initialScrollSession = createInitialScrollSession({
1171
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
1172
+ completion: state.initialScrollSession.completion,
1173
+ kind,
1174
+ previousDataLength: state.initialScrollSession.previousDataLength
1175
+ });
1118
1176
  }
1119
- if (index !== void 0) {
1120
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1121
- if (topOffsetAdjustment) {
1122
- offset += topOffsetAdjustment;
1177
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
1178
+ return state.initialScrollSession.completion;
1179
+ }
1180
+ var initialScrollCompletion = {
1181
+ didDispatchNativeScroll(state) {
1182
+ var _a3, _b;
1183
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
1184
+ },
1185
+ didRetrySilentInitialScroll(state) {
1186
+ var _a3, _b;
1187
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
1188
+ },
1189
+ markInitialScrollNativeDispatch(state) {
1190
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
1191
+ },
1192
+ markSilentInitialScrollRetry(state) {
1193
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
1194
+ },
1195
+ resetFlags(state) {
1196
+ if (!state.initialScrollSession) {
1197
+ return;
1123
1198
  }
1199
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1200
+ completion.didDispatchNativeScroll = void 0;
1201
+ completion.didRetrySilentInitialScroll = void 0;
1124
1202
  }
1125
- if (viewPosition !== void 0 && index !== void 0) {
1126
- const dataLength = state.props.data.length;
1127
- if (dataLength === 0) {
1128
- return offset;
1129
- }
1130
- const isOutOfBounds = index < 0 || index >= dataLength;
1131
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1132
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1133
- const trailingInset = getContentInsetEnd(state);
1134
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1135
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1136
- const footerSize = peek$(ctx, "footerSize") || 0;
1137
- offset += footerSize;
1203
+ };
1204
+ var initialScrollWatchdog = {
1205
+ clear(state) {
1206
+ initialScrollWatchdog.set(state, void 0);
1207
+ },
1208
+ didObserveProgress(newScroll, watchdog) {
1209
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1210
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1211
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1212
+ },
1213
+ get(state) {
1214
+ var _a3, _b;
1215
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1216
+ },
1217
+ hasNonZeroTargetOffset(targetOffset) {
1218
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1219
+ },
1220
+ isAtZeroTargetOffset(targetOffset) {
1221
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1222
+ },
1223
+ set(state, watchdog) {
1224
+ var _a3, _b;
1225
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1226
+ return;
1138
1227
  }
1228
+ const completion = ensureInitialScrollSessionCompletion(state);
1229
+ completion.watchdog = watchdog ? {
1230
+ startScroll: watchdog.startScroll,
1231
+ targetOffset: watchdog.targetOffset
1232
+ } : void 0;
1139
1233
  }
1140
- return offset;
1141
- }
1142
-
1143
- // src/core/clampScrollOffset.ts
1144
- function clampScrollOffset(ctx, offset, scrollTarget) {
1145
- const state = ctx.state;
1146
- const contentSize = getContentSize(ctx);
1147
- let clampedOffset = offset;
1148
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1149
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1150
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1151
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1152
- const maxOffset = baseMaxOffset + extraEndOffset;
1153
- clampedOffset = Math.min(offset, maxOffset);
1154
- }
1155
- clampedOffset = Math.max(0, clampedOffset);
1156
- return clampedOffset;
1234
+ };
1235
+ function setInitialScrollSession(state, options = {}) {
1236
+ var _a3, _b, _c;
1237
+ const existingSession = state.initialScrollSession;
1238
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1239
+ const completion = existingSession == null ? void 0 : existingSession.completion;
1240
+ const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1241
+ const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1242
+ if (!kind) {
1243
+ return clearInitialScrollSession(state);
1244
+ }
1245
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1246
+ return clearInitialScrollSession(state);
1247
+ }
1248
+ const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1249
+ state.initialScrollSession = createInitialScrollSession({
1250
+ bootstrap,
1251
+ completion,
1252
+ kind,
1253
+ previousDataLength
1254
+ });
1255
+ return state.initialScrollSession;
1157
1256
  }
1158
1257
 
1159
1258
  // src/utils/checkThreshold.ts
@@ -1194,11 +1293,16 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1194
1293
  return true;
1195
1294
  };
1196
1295
 
1296
+ // src/utils/hasActiveInitialScroll.ts
1297
+ function hasActiveInitialScroll(state) {
1298
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1299
+ }
1300
+
1197
1301
  // src/utils/checkAtBottom.ts
1198
1302
  function checkAtBottom(ctx) {
1199
1303
  var _a3;
1200
1304
  const state = ctx.state;
1201
- if (!state || state.initialScroll) {
1305
+ if (!state) {
1202
1306
  return;
1203
1307
  }
1204
1308
  const {
@@ -1208,42 +1312,48 @@ function checkAtBottom(ctx) {
1208
1312
  maintainingScrollAtEnd,
1209
1313
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1210
1314
  } = state;
1211
- if (state.initialScroll) {
1212
- return;
1213
- }
1214
1315
  const contentSize = getContentSize(ctx);
1215
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1216
- const insetEnd = getContentInsetEnd(state);
1316
+ if (contentSize > 0 && queuedInitialLayout) {
1317
+ const insetEnd = getContentInsetEnd(ctx);
1217
1318
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1218
1319
  const isContentLess = contentSize < scrollLength;
1219
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1220
- state.isEndReached = checkThreshold(
1221
- distanceFromEnd,
1222
- isContentLess,
1223
- onEndReachedThreshold * scrollLength,
1224
- state.isEndReached,
1225
- state.endReachedSnapshot,
1226
- {
1227
- contentSize,
1228
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1229
- scrollPosition: scroll
1230
- },
1231
- (distance) => {
1232
- var _a4, _b;
1233
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1234
- },
1235
- (snapshot) => {
1236
- state.endReachedSnapshot = snapshot;
1237
- },
1238
- true
1320
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1321
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1322
+ set$(
1323
+ ctx,
1324
+ "isWithinMaintainScrollAtEndThreshold",
1325
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1239
1326
  );
1327
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1328
+ if (!shouldSkipThresholdChecks) {
1329
+ state.isEndReached = checkThreshold(
1330
+ distanceFromEnd,
1331
+ isContentLess,
1332
+ onEndReachedThreshold * scrollLength,
1333
+ state.isEndReached,
1334
+ state.endReachedSnapshot,
1335
+ {
1336
+ contentSize,
1337
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1338
+ scrollPosition: scroll
1339
+ },
1340
+ (distance) => {
1341
+ var _a4, _b;
1342
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1343
+ },
1344
+ (snapshot) => {
1345
+ state.endReachedSnapshot = snapshot;
1346
+ },
1347
+ true
1348
+ );
1349
+ }
1240
1350
  }
1241
1351
  }
1242
1352
 
1243
1353
  // src/utils/checkAtTop.ts
1244
1354
  function checkAtTop(ctx) {
1245
1355
  const state = ctx == null ? void 0 : ctx.state;
1246
- if (!state || state.initialScroll || state.scrollingTo) {
1356
+ if (!state) {
1247
1357
  return;
1248
1358
  }
1249
1359
  const {
@@ -1266,31 +1376,33 @@ function checkAtTop(ctx) {
1266
1376
  state.startReachedSnapshot = void 0;
1267
1377
  state.startReachedSnapshotDataChangeEpoch = void 0;
1268
1378
  }
1269
- state.isAtStart = scroll <= 0;
1270
- if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1271
- return;
1379
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1380
+ set$(ctx, "isNearStart", scroll <= threshold);
1381
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1382
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1383
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1384
+ state.isStartReached = checkThreshold(
1385
+ scroll,
1386
+ false,
1387
+ threshold,
1388
+ state.isStartReached,
1389
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1390
+ {
1391
+ contentSize: totalSize,
1392
+ dataLength,
1393
+ scrollPosition: scroll
1394
+ },
1395
+ (distance) => {
1396
+ var _a3, _b;
1397
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1398
+ },
1399
+ (snapshot) => {
1400
+ state.startReachedSnapshot = snapshot;
1401
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1402
+ },
1403
+ allowReentryOnDataChange
1404
+ );
1272
1405
  }
1273
- state.isStartReached = checkThreshold(
1274
- scroll,
1275
- false,
1276
- threshold,
1277
- state.isStartReached,
1278
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1279
- {
1280
- contentSize: totalSize,
1281
- dataLength,
1282
- scrollPosition: scroll
1283
- },
1284
- (distance) => {
1285
- var _a3, _b;
1286
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1287
- },
1288
- (snapshot) => {
1289
- state.startReachedSnapshot = snapshot;
1290
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1291
- },
1292
- allowReentryOnDataChange
1293
- );
1294
1406
  }
1295
1407
 
1296
1408
  // src/utils/checkThresholds.ts
@@ -1299,6 +1411,16 @@ function checkThresholds(ctx) {
1299
1411
  checkAtTop(ctx);
1300
1412
  }
1301
1413
 
1414
+ // src/core/recalculateSettledScroll.ts
1415
+ function recalculateSettledScroll(ctx) {
1416
+ var _a3, _b;
1417
+ const state = ctx.state;
1418
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1419
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1420
+ }
1421
+ checkThresholds(ctx);
1422
+ }
1423
+
1302
1424
  // src/utils/setInitialRenderState.ts
1303
1425
  function setInitialRenderState(ctx, {
1304
1426
  didLayout,
@@ -1324,214 +1446,1361 @@ function setInitialRenderState(ctx, {
1324
1446
  }
1325
1447
  }
1326
1448
 
1327
- // src/core/finishScrollTo.ts
1328
- function finishScrollTo(ctx) {
1329
- var _a3, _b;
1449
+ // src/core/finishInitialScroll.ts
1450
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1451
+ function syncInitialScrollOffset(state, offset) {
1452
+ state.scroll = offset;
1453
+ state.scrollPending = offset;
1454
+ state.scrollPrev = offset;
1455
+ }
1456
+ function clearPreservedInitialScrollTargetTimeout(state) {
1457
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
1458
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
1459
+ state.timeoutPreservedInitialScrollClear = void 0;
1460
+ }
1461
+ }
1462
+ function clearPreservedInitialScrollTarget(state) {
1463
+ clearPreservedInitialScrollTargetTimeout(state);
1464
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1465
+ state.initialScroll = void 0;
1466
+ setInitialScrollSession(state);
1467
+ }
1468
+ function finishInitialScroll(ctx, options) {
1469
+ var _a3, _b, _c;
1330
1470
  const state = ctx.state;
1331
- if (state == null ? void 0 : state.scrollingTo) {
1332
- const resolvePendingScroll = state.pendingScrollResolve;
1333
- state.pendingScrollResolve = void 0;
1334
- const scrollingTo = state.scrollingTo;
1335
- state.scrollHistory.length = 0;
1336
- state.initialScroll = void 0;
1337
- state.initialScrollUsesOffset = false;
1338
- state.initialAnchor = void 0;
1339
- state.initialNativeScrollWatchdog = void 0;
1340
- state.scrollingTo = void 0;
1341
- if (state.pendingTotalSize !== void 0) {
1342
- addTotalSize(ctx, null, state.pendingTotalSize);
1343
- }
1344
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1345
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1471
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1472
+ syncInitialScrollOffset(state, options.resolvedOffset);
1473
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1474
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1475
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1476
+ syncInitialScrollOffset(state, observedOffset);
1477
+ }
1478
+ }
1479
+ const complete = () => {
1480
+ var _a4, _b2, _c2, _d, _e;
1481
+ const shouldReleaseDeferredPublicOnScroll = Platform2.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1482
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1483
+ initialScrollWatchdog.clear(state);
1484
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1485
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1486
+ setInitialScrollSession(state);
1487
+ clearPreservedInitialScrollTargetTimeout(state);
1488
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1489
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1490
+ var _a5;
1491
+ state.timeoutPreservedInitialScrollClear = void 0;
1492
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1493
+ return;
1494
+ }
1495
+ clearPreservedInitialScrollTarget(state);
1496
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
1497
+ }
1498
+ } else {
1499
+ clearPreservedInitialScrollTarget(state);
1346
1500
  }
1347
- if (PlatformAdjustBreaksScroll) {
1348
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1501
+ if (options == null ? void 0 : options.recalculateItems) {
1502
+ recalculateSettledScroll(ctx);
1349
1503
  }
1350
1504
  setInitialRenderState(ctx, { didInitialScroll: true });
1351
- checkThresholds(ctx);
1352
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1353
- }
1354
- }
1355
-
1356
- // src/core/checkFinishedScroll.ts
1357
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1358
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1359
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1360
- function checkFinishedScroll(ctx) {
1361
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1362
- }
1363
- function checkFinishedScrollFrame(ctx) {
1364
- var _a3;
1365
- const scrollingTo = ctx.state.scrollingTo;
1366
- if (scrollingTo) {
1367
- const { state } = ctx;
1368
- state.animFrameCheckFinishedScroll = void 0;
1369
- const scroll = state.scrollPending;
1370
- const adjust = state.scrollAdjustHandler.getAdjust();
1371
- const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
1372
- const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
1373
- const diff1 = Math.abs(scroll - clampedTargetOffset);
1374
- const diff2 = Math.abs(diff1 - adjust);
1375
- const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
1376
- const isAtTarget = diff1 < 1 || !scrollingTo.animated && diff2 < 1;
1377
- if (isNotOverscrolled && isAtTarget) {
1378
- finishScrollTo(ctx);
1505
+ if (shouldReleaseDeferredPublicOnScroll) {
1506
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1379
1507
  }
1508
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
1509
+ };
1510
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
1511
+ requestAnimationFrame(complete);
1512
+ return;
1380
1513
  }
1514
+ complete();
1381
1515
  }
1382
- function checkFinishedScrollFallback(ctx) {
1516
+
1517
+ // src/core/calculateOffsetForIndex.ts
1518
+ function calculateOffsetForIndex(ctx, index) {
1383
1519
  const state = ctx.state;
1384
- const scrollingTo = state.scrollingTo;
1385
- const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1386
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget || !state.didContainersLayout;
1387
- state.timeoutCheckFinishedScrollFallback = setTimeout(
1388
- () => {
1389
- let numChecks = 0;
1390
- const checkHasScrolled = () => {
1391
- var _a3, _b;
1392
- state.timeoutCheckFinishedScrollFallback = void 0;
1393
- const isStillScrollingTo = state.scrollingTo;
1394
- if (isStillScrollingTo) {
1395
- numChecks++;
1396
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1397
- const maxChecks = isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1398
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1399
- if (shouldFinishZeroTarget || state.hasScrolled || numChecks > maxChecks) {
1400
- finishScrollTo(ctx);
1401
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
1402
- const targetOffset = (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.targetOffset) != null ? _b : state.scrollPending;
1403
- const scroller = state.refScroller.current;
1404
- if (scroller) {
1405
- scroller.scrollTo({
1406
- animated: false,
1407
- x: state.props.horizontal ? targetOffset : 0,
1408
- y: state.props.horizontal ? 0 : targetOffset
1409
- });
1410
- }
1411
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1412
- } else {
1413
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1414
- }
1415
- }
1416
- };
1417
- checkHasScrolled();
1418
- },
1419
- slowTimeout ? 500 : 100
1420
- );
1520
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1421
1521
  }
1422
- function isNativeInitialNonZeroTarget(state) {
1423
- return !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && state.initialNativeScrollWatchdog.targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1522
+
1523
+ // src/core/getTopOffsetAdjustment.ts
1524
+ function getTopOffsetAdjustment(ctx) {
1525
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1424
1526
  }
1425
- function shouldFinishInitialZeroTargetScroll(ctx) {
1426
- var _a3;
1427
- const { state } = ctx;
1428
- 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;
1527
+
1528
+ // src/utils/getId.ts
1529
+ function getId(state, index) {
1530
+ const { data, keyExtractor } = state.props;
1531
+ if (!data) {
1532
+ return "";
1533
+ }
1534
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1535
+ const id = ret;
1536
+ state.idCache[index] = id;
1537
+ return id;
1429
1538
  }
1430
1539
 
1431
- // src/core/doScrollTo.native.ts
1432
- function doScrollTo(ctx, params) {
1540
+ // src/core/addTotalSize.ts
1541
+ function addTotalSize(ctx, key, add) {
1433
1542
  const state = ctx.state;
1434
- const { animated, horizontal, offset } = params;
1435
- const isAnimated = !!animated;
1436
- const { refScroller } = state;
1437
- const scroller = refScroller.current;
1438
- if (!scroller) {
1439
- return;
1440
- }
1441
- scroller.scrollTo({
1442
- animated: isAnimated,
1543
+ const prevTotalSize = state.totalSize;
1544
+ let totalSize = state.totalSize;
1545
+ if (key === null) {
1546
+ totalSize = add;
1547
+ if (state.timeoutSetPaddingTop) {
1548
+ clearTimeout(state.timeoutSetPaddingTop);
1549
+ state.timeoutSetPaddingTop = void 0;
1550
+ }
1551
+ } else {
1552
+ totalSize += add;
1553
+ }
1554
+ if (prevTotalSize !== totalSize) {
1555
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1556
+ state.pendingTotalSize = totalSize;
1557
+ } else {
1558
+ state.pendingTotalSize = void 0;
1559
+ state.totalSize = totalSize;
1560
+ set$(ctx, "totalSize", totalSize);
1561
+ }
1562
+ }
1563
+ }
1564
+
1565
+ // src/core/setSize.ts
1566
+ function setSize(ctx, itemKey, size) {
1567
+ const state = ctx.state;
1568
+ const { sizes } = state;
1569
+ const previousSize = sizes.get(itemKey);
1570
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1571
+ if (diff !== 0) {
1572
+ addTotalSize(ctx, itemKey, diff);
1573
+ }
1574
+ sizes.set(itemKey, size);
1575
+ }
1576
+
1577
+ // src/utils/getItemSize.ts
1578
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1579
+ var _a3, _b, _c;
1580
+ const state = ctx.state;
1581
+ const {
1582
+ sizesKnown,
1583
+ sizes,
1584
+ averageSizes,
1585
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1586
+ scrollingTo
1587
+ } = state;
1588
+ const sizeKnown = sizesKnown.get(key);
1589
+ if (sizeKnown !== void 0) {
1590
+ return sizeKnown;
1591
+ }
1592
+ let size;
1593
+ const renderedSize = sizes.get(key);
1594
+ if (preferCachedSize) {
1595
+ if (renderedSize !== void 0) {
1596
+ return renderedSize;
1597
+ }
1598
+ }
1599
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1600
+ if (getFixedItemSize) {
1601
+ size = getFixedItemSize(data, index, itemType);
1602
+ if (size !== void 0) {
1603
+ sizesKnown.set(key, size);
1604
+ }
1605
+ }
1606
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1607
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1608
+ if (averageSizeForType !== void 0) {
1609
+ size = roundSize(averageSizeForType);
1610
+ }
1611
+ }
1612
+ if (size === void 0 && renderedSize !== void 0) {
1613
+ return renderedSize;
1614
+ }
1615
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
1616
+ const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
1617
+ if (averageSizeForType !== void 0) {
1618
+ size = roundSize(averageSizeForType);
1619
+ }
1620
+ }
1621
+ if (size === void 0) {
1622
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1623
+ }
1624
+ setSize(ctx, key, size);
1625
+ return size;
1626
+ }
1627
+ function getItemSizeAtIndex(ctx, index) {
1628
+ if (index === void 0 || index < 0) {
1629
+ return void 0;
1630
+ }
1631
+ const targetId = getId(ctx.state, index);
1632
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1633
+ }
1634
+
1635
+ // src/core/calculateOffsetWithOffsetPosition.ts
1636
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1637
+ var _a3;
1638
+ const state = ctx.state;
1639
+ const { index, viewOffset, viewPosition } = params;
1640
+ let offset = offsetParam;
1641
+ if (viewOffset) {
1642
+ offset -= viewOffset;
1643
+ }
1644
+ if (index !== void 0) {
1645
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1646
+ if (topOffsetAdjustment) {
1647
+ offset += topOffsetAdjustment;
1648
+ }
1649
+ }
1650
+ if (viewPosition !== void 0 && index !== void 0) {
1651
+ const dataLength = state.props.data.length;
1652
+ if (dataLength === 0) {
1653
+ return offset;
1654
+ }
1655
+ const isOutOfBounds = index < 0 || index >= dataLength;
1656
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1657
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1658
+ const trailingInset = getContentInsetEnd(ctx);
1659
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1660
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1661
+ const footerSize = peek$(ctx, "footerSize") || 0;
1662
+ offset += footerSize;
1663
+ }
1664
+ }
1665
+ return offset;
1666
+ }
1667
+
1668
+ // src/core/clampScrollOffset.ts
1669
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1670
+ const state = ctx.state;
1671
+ const contentSize = getContentSize(ctx);
1672
+ let clampedOffset = offset;
1673
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1674
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1675
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1676
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1677
+ const maxOffset = baseMaxOffset + extraEndOffset;
1678
+ clampedOffset = Math.min(offset, maxOffset);
1679
+ }
1680
+ clampedOffset = Math.max(0, clampedOffset);
1681
+ return clampedOffset;
1682
+ }
1683
+
1684
+ // src/core/finishScrollTo.ts
1685
+ function finishScrollTo(ctx) {
1686
+ var _a3, _b;
1687
+ const state = ctx.state;
1688
+ if (state == null ? void 0 : state.scrollingTo) {
1689
+ const resolvePendingScroll = state.pendingScrollResolve;
1690
+ state.pendingScrollResolve = void 0;
1691
+ const scrollingTo = state.scrollingTo;
1692
+ state.scrollHistory.length = 0;
1693
+ state.scrollingTo = void 0;
1694
+ if (state.pendingTotalSize !== void 0) {
1695
+ addTotalSize(ctx, null, state.pendingTotalSize);
1696
+ }
1697
+ if (PlatformAdjustBreaksScroll) {
1698
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1699
+ }
1700
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
1701
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1702
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1703
+ finishInitialScroll(ctx, {
1704
+ onFinished: () => {
1705
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1706
+ },
1707
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1708
+ recalculateItems: true,
1709
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1710
+ syncObservedOffset: isOffsetSession,
1711
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1712
+ });
1713
+ return;
1714
+ }
1715
+ recalculateSettledScroll(ctx);
1716
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1717
+ }
1718
+ }
1719
+
1720
+ // src/core/checkFinishedScroll.ts
1721
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1722
+ var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
1723
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1724
+ var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
1725
+ var SILENT_INITIAL_SCROLL_TARGET_EPSILON = 1;
1726
+ function checkFinishedScroll(ctx, options) {
1727
+ const scrollingTo = ctx.state.scrollingTo;
1728
+ if (options == null ? void 0 : options.onlyIfAligned) {
1729
+ if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
1730
+ return;
1731
+ }
1732
+ if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
1733
+ return;
1734
+ }
1735
+ }
1736
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1737
+ }
1738
+ function hasScrollCompletionOwnership(state, options) {
1739
+ const { clampedTargetOffset, scrollingTo } = options;
1740
+ return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
1741
+ }
1742
+ function isSilentInitialDispatch(state, scrollingTo) {
1743
+ return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
1744
+ }
1745
+ function getInitialScrollWatchdogTargetOffset(state) {
1746
+ var _a3;
1747
+ return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
1748
+ }
1749
+ function isNativeInitialNonZeroTarget(state) {
1750
+ const targetOffset = getInitialScrollWatchdogTargetOffset(state);
1751
+ return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
1752
+ }
1753
+ function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
1754
+ var _a3, _b;
1755
+ if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
1756
+ return false;
1757
+ }
1758
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
1759
+ return false;
1760
+ }
1761
+ const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
1762
+ if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
1763
+ return false;
1764
+ }
1765
+ if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
1766
+ return false;
1767
+ }
1768
+ return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
1769
+ }
1770
+ function shouldFinishInitialZeroTargetScroll(ctx) {
1771
+ var _a3;
1772
+ const { state } = ctx;
1773
+ 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;
1774
+ }
1775
+ function getResolvedScrollCompletionState(ctx, scrollingTo) {
1776
+ var _a3;
1777
+ const { state } = ctx;
1778
+ const scroll = state.scrollPending;
1779
+ const adjust = state.scrollAdjustHandler.getAdjust();
1780
+ const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
1781
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
1782
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
1783
+ const diff2 = Math.abs(diff1 - adjust);
1784
+ return {
1785
+ clampedTargetOffset,
1786
+ isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || !scrollingTo.animated && diff2 < 1)
1787
+ };
1788
+ }
1789
+ function checkFinishedScrollFrame(ctx) {
1790
+ const scrollingTo = ctx.state.scrollingTo;
1791
+ if (!scrollingTo) {
1792
+ return;
1793
+ }
1794
+ const { state } = ctx;
1795
+ state.animFrameCheckFinishedScroll = void 0;
1796
+ const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
1797
+ if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
1798
+ clampedTargetOffset: completionState.clampedTargetOffset,
1799
+ scrollingTo
1800
+ })) {
1801
+ finishScrollTo(ctx);
1802
+ }
1803
+ }
1804
+ function scrollToFallbackOffset(ctx, offset) {
1805
+ var _a3;
1806
+ (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
1807
+ animated: false,
1808
+ x: ctx.state.props.horizontal ? offset : 0,
1809
+ y: ctx.state.props.horizontal ? 0 : offset
1810
+ });
1811
+ }
1812
+ function checkFinishedScrollFallback(ctx) {
1813
+ const state = ctx.state;
1814
+ const scrollingTo = state.scrollingTo;
1815
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1816
+ const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
1817
+ const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
1818
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
1819
+ const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
1820
+ state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
1821
+ let numChecks = 0;
1822
+ const scheduleFallbackCheck = (delay) => {
1823
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
1824
+ };
1825
+ const checkHasScrolled = () => {
1826
+ var _a3, _b, _c;
1827
+ state.timeoutCheckFinishedScrollFallback = void 0;
1828
+ const isStillScrollingTo = state.scrollingTo;
1829
+ if (isStillScrollingTo) {
1830
+ numChecks++;
1831
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1832
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1833
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1834
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
1835
+ state,
1836
+ isStillScrollingTo
1837
+ );
1838
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1839
+ const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1840
+ const shouldRetrySilentInitialNativeScroll = Platform2.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1841
+ if (shouldRetrySilentInitialNativeScroll) {
1842
+ const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1843
+ const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
1844
+ initialScrollCompletion.markSilentInitialScrollRetry(state);
1845
+ scrollToFallbackOffset(ctx, jiggleOffset);
1846
+ requestAnimationFrame(() => {
1847
+ scrollToFallbackOffset(ctx, targetOffset);
1848
+ });
1849
+ scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1850
+ } else if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1851
+ finishScrollTo(ctx);
1852
+ } else if (isNativeInitialPending && numChecks <= maxChecks) {
1853
+ const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
1854
+ scrollToFallbackOffset(ctx, targetOffset);
1855
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1856
+ } else {
1857
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1858
+ }
1859
+ }
1860
+ };
1861
+ checkHasScrolled();
1862
+ }, initialDelay);
1863
+ }
1864
+
1865
+ // src/core/doScrollTo.native.ts
1866
+ function doScrollTo(ctx, params) {
1867
+ const state = ctx.state;
1868
+ const { animated, horizontal, isInitialScroll, offset } = params;
1869
+ const isAnimated = !!animated;
1870
+ const { refScroller } = state;
1871
+ const scroller = refScroller.current;
1872
+ if (!scroller) {
1873
+ return;
1874
+ }
1875
+ scroller.scrollTo({
1876
+ animated: isAnimated,
1443
1877
  x: horizontal ? offset : 0,
1444
1878
  y: horizontal ? 0 : offset
1445
1879
  });
1880
+ if (isInitialScroll) {
1881
+ initialScrollCompletion.markInitialScrollNativeDispatch(state);
1882
+ }
1446
1883
  if (!isAnimated) {
1447
1884
  state.scroll = offset;
1448
1885
  checkFinishedScrollFallback(ctx);
1449
1886
  }
1450
1887
  }
1451
-
1452
- // src/core/scrollTo.ts
1453
- var WATCHDOG_OFFSET_EPSILON = 1;
1454
- function scrollTo(ctx, params) {
1888
+
1889
+ // src/core/scrollTo.ts
1890
+ function getAverageSizeSnapshot(state) {
1891
+ if (Object.keys(state.averageSizes).length === 0) {
1892
+ return void 0;
1893
+ }
1894
+ const snapshot = {};
1895
+ for (const itemType in state.averageSizes) {
1896
+ const averages = state.averageSizes[itemType];
1897
+ snapshot[itemType] = averages.avg;
1898
+ }
1899
+ return snapshot;
1900
+ }
1901
+ function syncInitialScrollNativeWatchdog(state, options) {
1902
+ var _a3;
1903
+ const { isInitialScroll, requestedOffset, targetOffset } = options;
1904
+ const existingWatchdog = initialScrollWatchdog.get(state);
1905
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!existingWatchdog) && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
1906
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!existingWatchdog && initialScrollWatchdog.isAtZeroTargetOffset(requestedOffset);
1907
+ if (shouldWatchInitialNativeScroll) {
1908
+ state.hasScrolled = false;
1909
+ initialScrollWatchdog.set(state, {
1910
+ startScroll: (_a3 = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _a3 : state.scroll,
1911
+ targetOffset
1912
+ });
1913
+ return;
1914
+ }
1915
+ if (shouldClearInitialNativeScrollWatchdog) {
1916
+ initialScrollWatchdog.clear(state);
1917
+ }
1918
+ }
1919
+ function scrollTo(ctx, params) {
1920
+ var _a3;
1921
+ const state = ctx.state;
1922
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1923
+ const {
1924
+ animated,
1925
+ isInitialScroll,
1926
+ offset: scrollTargetOffset,
1927
+ precomputedWithViewOffset,
1928
+ waitForInitialScrollCompletionFrame
1929
+ } = scrollTarget;
1930
+ const {
1931
+ props: { horizontal }
1932
+ } = state;
1933
+ if (state.animFrameCheckFinishedScroll) {
1934
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1935
+ }
1936
+ if (state.timeoutCheckFinishedScrollFallback) {
1937
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1938
+ }
1939
+ const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1940
+ const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1941
+ const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
1942
+ const offset = shouldPreserveRawInitialOffsetRequest ? requestedOffset : targetOffset;
1943
+ state.scrollHistory.length = 0;
1944
+ if (!noScrollingTo) {
1945
+ if (isInitialScroll) {
1946
+ initialScrollCompletion.resetFlags(state);
1947
+ }
1948
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
1949
+ state.scrollingTo = {
1950
+ ...scrollTarget,
1951
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
1952
+ targetOffset,
1953
+ waitForInitialScrollCompletionFrame
1954
+ };
1955
+ }
1956
+ state.scrollPending = targetOffset;
1957
+ syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
1958
+ if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1959
+ doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1960
+ } else {
1961
+ state.scroll = offset;
1962
+ }
1963
+ }
1964
+
1965
+ // src/core/scrollToIndex.ts
1966
+ function clampScrollIndex(index, dataLength) {
1967
+ if (dataLength <= 0) {
1968
+ return -1;
1969
+ }
1970
+ if (index >= dataLength) {
1971
+ return dataLength - 1;
1972
+ }
1973
+ if (index < 0) {
1974
+ return 0;
1975
+ }
1976
+ return index;
1977
+ }
1978
+ function scrollToIndex(ctx, {
1979
+ index,
1980
+ viewOffset = 0,
1981
+ animated = true,
1982
+ forceScroll,
1983
+ isInitialScroll,
1984
+ viewPosition
1985
+ }) {
1986
+ const state = ctx.state;
1987
+ const { data } = state.props;
1988
+ index = clampScrollIndex(index, data.length);
1989
+ const itemSize = getItemSizeAtIndex(ctx, index);
1990
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
1991
+ const isLast = index === data.length - 1;
1992
+ if (isLast && viewPosition === void 0) {
1993
+ viewPosition = 1;
1994
+ }
1995
+ state.scrollForNextCalculateItemsInView = void 0;
1996
+ scrollTo(ctx, {
1997
+ animated,
1998
+ forceScroll,
1999
+ index,
2000
+ isInitialScroll,
2001
+ itemSize,
2002
+ offset: firstIndexOffset,
2003
+ viewOffset,
2004
+ viewPosition: viewPosition != null ? viewPosition : 0
2005
+ });
2006
+ }
2007
+
2008
+ // src/core/initialScroll.ts
2009
+ function dispatchInitialScroll(ctx, params) {
2010
+ const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2011
+ const requestedIndex = target.index;
2012
+ const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2013
+ const itemSize = getItemSizeAtIndex(ctx, index);
2014
+ scrollTo(ctx, {
2015
+ animated: false,
2016
+ forceScroll,
2017
+ index: index !== void 0 && index >= 0 ? index : void 0,
2018
+ isInitialScroll: true,
2019
+ itemSize,
2020
+ offset: resolvedOffset,
2021
+ precomputedWithViewOffset: true,
2022
+ viewOffset: target.viewOffset,
2023
+ viewPosition: target.viewPosition,
2024
+ waitForInitialScrollCompletionFrame: waitForCompletionFrame
2025
+ });
2026
+ }
2027
+ function setInitialScrollTarget(state, target, options) {
2028
+ var _a3;
2029
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2030
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2031
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2032
+ state.timeoutPreservedInitialScrollClear = void 0;
2033
+ }
2034
+ state.initialScroll = target;
2035
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2036
+ state.didFinishInitialScroll = false;
2037
+ }
2038
+ setInitialScrollSession(state, {
2039
+ kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2040
+ });
2041
+ }
2042
+ function resolveInitialScrollOffset(ctx, initialScroll) {
2043
+ var _a3, _b;
2044
+ const state = ctx.state;
2045
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2046
+ return (_b = initialScroll.contentOffset) != null ? _b : 0;
2047
+ }
2048
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2049
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2050
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2051
+ }
2052
+ function getAdvanceableInitialScrollState(state, options) {
2053
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
2054
+ const initialScroll = state.initialScroll;
2055
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
2056
+ const shouldWaitForInitialLayout = !!(options == null ? void 0 : options.requiresMeasuredLayout) && !queuedInitialLayout && !isInitialScrollInProgress;
2057
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll || scrollingTo && !isInitialScrollInProgress) {
2058
+ return void 0;
2059
+ }
2060
+ return {
2061
+ initialScroll,
2062
+ isInitialScrollInProgress,
2063
+ queuedInitialLayout,
2064
+ scrollingTo
2065
+ };
2066
+ }
2067
+ function advanceMeasuredInitialScroll(ctx, options) {
2068
+ var _a3, _b, _c;
2069
+ const state = ctx.state;
2070
+ const advanceableState = getAdvanceableInitialScrollState(state, {
2071
+ requiresMeasuredLayout: true
2072
+ });
2073
+ if (!advanceableState) {
2074
+ return false;
2075
+ }
2076
+ const { initialScroll, isInitialScrollInProgress, queuedInitialLayout } = advanceableState;
2077
+ const scrollingTo = isInitialScrollInProgress ? advanceableState.scrollingTo : void 0;
2078
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2079
+ const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2080
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2081
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2082
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2083
+ if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2084
+ return false;
2085
+ }
2086
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2087
+ return false;
2088
+ }
2089
+ if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2090
+ setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2091
+ }
2092
+ const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2093
+ dispatchInitialScroll(ctx, {
2094
+ forceScroll,
2095
+ resolvedOffset,
2096
+ target: initialScroll
2097
+ });
2098
+ return true;
2099
+ }
2100
+ function advanceOffsetInitialScroll(ctx, options) {
2101
+ var _a3, _b;
2102
+ const state = ctx.state;
2103
+ const advanceableState = getAdvanceableInitialScrollState(state);
2104
+ if (!advanceableState) {
2105
+ return false;
2106
+ }
2107
+ const { initialScroll, queuedInitialLayout } = advanceableState;
2108
+ const resolvedOffset = (_a3 = initialScroll.contentOffset) != null ? _a3 : 0;
2109
+ const isAlreadyAtDesiredInitialTarget = Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2110
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2111
+ return false;
2112
+ }
2113
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
2114
+ const forceScroll = (_b = options == null ? void 0 : options.forceScroll) != null ? _b : hasMeasuredScrollLayout || !!queuedInitialLayout;
2115
+ dispatchInitialScroll(ctx, {
2116
+ forceScroll,
2117
+ resolvedOffset,
2118
+ target: initialScroll
2119
+ });
2120
+ return true;
2121
+ }
2122
+ function advanceCurrentInitialScrollSession(ctx, options) {
2123
+ var _a3;
2124
+ return ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? advanceOffsetInitialScroll(ctx, {
2125
+ forceScroll: options == null ? void 0 : options.forceScroll
2126
+ }) : advanceMeasuredInitialScroll(ctx, {
2127
+ forceScroll: options == null ? void 0 : options.forceScroll
2128
+ });
2129
+ }
2130
+
2131
+ // src/utils/checkAllSizesKnown.ts
2132
+ function isNullOrUndefined2(value) {
2133
+ return value === null || value === void 0;
2134
+ }
2135
+ function getMountedIndicesInRange(state, start, end) {
2136
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2137
+ 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);
2138
+ }
2139
+ return [];
2140
+ }
2141
+ function getMountedBufferedIndices(state) {
2142
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2143
+ }
2144
+ function getMountedNoBufferIndices(state) {
2145
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2146
+ }
2147
+ function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
2148
+ return indices.length > 0 && indices.every((index) => {
2149
+ const key = getId(state, index);
2150
+ return state.sizesKnown.has(key);
2151
+ });
2152
+ }
2153
+
2154
+ // src/core/bootstrapInitialScroll.ts
2155
+ var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2156
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
2157
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES = 24;
2158
+ var BOOTSTRAP_REVEAL_ABORT_WARNING = "LegendList bootstrap initial scroll aborted after exceeding convergence bounds.";
2159
+ function getBootstrapInitialScrollSession(state) {
2160
+ var _a3;
2161
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
2162
+ }
2163
+ function isOffsetInitialScrollSession(state) {
2164
+ var _a3;
2165
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2166
+ }
2167
+ function doVisibleIndicesMatch(previous, next) {
2168
+ if (!previous || previous.length !== next.length) {
2169
+ return false;
2170
+ }
2171
+ for (let i = 0; i < previous.length; i++) {
2172
+ if (previous[i] !== next[i]) {
2173
+ return false;
2174
+ }
2175
+ }
2176
+ return true;
2177
+ }
2178
+ function getBootstrapRevealVisibleIndices(options) {
2179
+ const { dataLength, getSize, offset, positions, scrollLength, startIndex: requestedStartIndex } = options;
2180
+ const endOffset = offset + scrollLength;
2181
+ const visibleIndices = [];
2182
+ let index = requestedStartIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, requestedStartIndex)) : 0;
2183
+ while (index > 0) {
2184
+ const previousIndex = index - 1;
2185
+ const previousPosition = positions[previousIndex];
2186
+ if (previousPosition === void 0) {
2187
+ index = previousIndex;
2188
+ continue;
2189
+ }
2190
+ const previousSize = getSize(previousIndex);
2191
+ if (previousSize === void 0) {
2192
+ index = previousIndex;
2193
+ continue;
2194
+ }
2195
+ if (previousPosition + previousSize <= offset) {
2196
+ break;
2197
+ }
2198
+ index = previousIndex;
2199
+ }
2200
+ for (; index < dataLength; index++) {
2201
+ const position = positions[index];
2202
+ if (position === void 0) {
2203
+ continue;
2204
+ }
2205
+ const size = getSize(index);
2206
+ if (size === void 0) {
2207
+ continue;
2208
+ }
2209
+ if (position < endOffset && position + size > offset) {
2210
+ visibleIndices.push(index);
2211
+ } else if (visibleIndices.length > 0 && position >= endOffset) {
2212
+ break;
2213
+ }
2214
+ }
2215
+ return visibleIndices;
2216
+ }
2217
+ function shouldAbortBootstrapReveal(options) {
2218
+ const {
2219
+ mountFrameCount,
2220
+ maxFrames = DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES,
2221
+ maxPasses = DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES,
2222
+ passCount
2223
+ } = options;
2224
+ return mountFrameCount >= maxFrames || passCount >= maxPasses;
2225
+ }
2226
+ function abortBootstrapRevealIfNeeded(ctx, options) {
2227
+ if (!shouldAbortBootstrapReveal(options)) {
2228
+ return false;
2229
+ }
2230
+ if (IS_DEV) {
2231
+ console.warn(BOOTSTRAP_REVEAL_ABORT_WARNING);
2232
+ }
2233
+ abortBootstrapInitialScroll(ctx);
2234
+ return true;
2235
+ }
2236
+ function clearBootstrapInitialScrollSession(state) {
2237
+ var _a3;
2238
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2239
+ const frameHandle = bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle;
2240
+ if (frameHandle !== void 0 && typeof cancelAnimationFrame === "function") {
2241
+ cancelAnimationFrame(frameHandle);
2242
+ }
2243
+ if (bootstrapInitialScroll) {
2244
+ bootstrapInitialScroll.frameHandle = void 0;
2245
+ }
2246
+ setInitialScrollSession(state, {
2247
+ bootstrap: void 0,
2248
+ kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2249
+ });
2250
+ }
2251
+ function startBootstrapInitialScrollSession(state, options) {
2252
+ var _a3, _b, _c;
2253
+ const previousBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2254
+ setInitialScrollSession(state, {
2255
+ bootstrap: {
2256
+ frameHandle: previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.frameHandle,
2257
+ // Re-arming during the initial mount should spend from the same watchdog budget.
2258
+ mountFrameCount: (_a3 = previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.mountFrameCount) != null ? _a3 : 0,
2259
+ passCount: 0,
2260
+ previousResolvedOffset: void 0,
2261
+ scroll: options.scroll,
2262
+ seedContentOffset: (_c = (_b = options.seedContentOffset) != null ? _b : previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.seedContentOffset) != null ? _c : options.scroll,
2263
+ targetIndexSeed: options.targetIndexSeed,
2264
+ visibleIndices: void 0
2265
+ },
2266
+ kind: "bootstrap"
2267
+ });
2268
+ }
2269
+ function resetBootstrapInitialScrollSession(state, options) {
2270
+ var _a3, _b, _c;
2271
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2272
+ if (!bootstrapInitialScroll) {
2273
+ if ((options == null ? void 0 : options.scroll) !== void 0) {
2274
+ startBootstrapInitialScrollSession(state, {
2275
+ scroll: options.scroll,
2276
+ seedContentOffset: options.seedContentOffset,
2277
+ targetIndexSeed: options.targetIndexSeed
2278
+ });
2279
+ }
2280
+ } else {
2281
+ bootstrapInitialScroll.passCount = 0;
2282
+ bootstrapInitialScroll.previousResolvedOffset = void 0;
2283
+ bootstrapInitialScroll.scroll = (_a3 = options == null ? void 0 : options.scroll) != null ? _a3 : bootstrapInitialScroll.scroll;
2284
+ bootstrapInitialScroll.seedContentOffset = (_b = options == null ? void 0 : options.seedContentOffset) != null ? _b : bootstrapInitialScroll.seedContentOffset;
2285
+ bootstrapInitialScroll.targetIndexSeed = (_c = options == null ? void 0 : options.targetIndexSeed) != null ? _c : bootstrapInitialScroll.targetIndexSeed;
2286
+ bootstrapInitialScroll.visibleIndices = void 0;
2287
+ setInitialScrollSession(state, {
2288
+ bootstrap: bootstrapInitialScroll,
2289
+ kind: "bootstrap"
2290
+ });
2291
+ }
2292
+ }
2293
+ function queueBootstrapInitialScrollReevaluation(state) {
2294
+ requestAnimationFrame(() => {
2295
+ var _a3;
2296
+ if (getBootstrapInitialScrollSession(state)) {
2297
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
2298
+ }
2299
+ });
2300
+ }
2301
+ function ensureBootstrapInitialScrollFrameTicker(ctx) {
2302
+ const state = ctx.state;
2303
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2304
+ if (!bootstrapInitialScroll || bootstrapInitialScroll.frameHandle !== void 0) {
2305
+ return;
2306
+ }
2307
+ const tick = () => {
2308
+ const activeBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2309
+ if (!activeBootstrapInitialScroll) {
2310
+ return;
2311
+ }
2312
+ activeBootstrapInitialScroll.frameHandle = void 0;
2313
+ activeBootstrapInitialScroll.mountFrameCount += 1;
2314
+ if (abortBootstrapRevealIfNeeded(ctx, {
2315
+ mountFrameCount: activeBootstrapInitialScroll.mountFrameCount,
2316
+ passCount: activeBootstrapInitialScroll.passCount
2317
+ })) {
2318
+ return;
2319
+ }
2320
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2321
+ };
2322
+ bootstrapInitialScroll.frameHandle = requestAnimationFrame(tick);
2323
+ }
2324
+ function rearmBootstrapInitialScroll(ctx, options) {
2325
+ resetBootstrapInitialScrollSession(ctx.state, options);
2326
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2327
+ queueBootstrapInitialScrollReevaluation(ctx.state);
2328
+ }
2329
+ function createInitialScrollAtEndTarget(options) {
2330
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2331
+ return {
2332
+ contentOffset: void 0,
2333
+ index: Math.max(0, dataLength - 1),
2334
+ preserveForBottomPadding: true,
2335
+ preserveForFooterLayout,
2336
+ viewOffset: -stylePaddingBottom - footerSize,
2337
+ viewPosition: 1
2338
+ };
2339
+ }
2340
+ function shouldPreserveInitialScrollForBottomPadding(target) {
2341
+ return !!(target == null ? void 0 : target.preserveForBottomPadding);
2342
+ }
2343
+ function shouldPreserveInitialScrollForFooterLayout(target) {
2344
+ return !!(target == null ? void 0 : target.preserveForFooterLayout);
2345
+ }
2346
+ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2347
+ return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2348
+ }
2349
+ function createRetargetedBottomAlignedInitialScroll(options) {
2350
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2351
+ const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2352
+ return {
2353
+ ...target,
2354
+ contentOffset: void 0,
2355
+ index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2356
+ preserveForBottomPadding: true,
2357
+ preserveForFooterLayout,
2358
+ viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2359
+ viewPosition: 1
2360
+ };
2361
+ }
2362
+ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2363
+ return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2364
+ }
2365
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
2366
+ const { dataLength, stylePaddingBottom, target } = options;
2367
+ const state = ctx.state;
2368
+ if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2369
+ return;
2370
+ }
2371
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
2372
+ dataLength,
2373
+ footerSize: 0,
2374
+ preserveForFooterLayout: void 0,
2375
+ stylePaddingBottom
2376
+ });
2377
+ setInitialScrollTarget(state, clearedFooterTarget);
2378
+ }
2379
+ function clearFinishedViewportRetargetableInitialScroll(state) {
2380
+ clearPreservedInitialScrollTarget(state);
2381
+ }
2382
+ function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2383
+ const state = ctx.state;
2384
+ if (!state.didFinishInitialScroll) {
2385
+ return false;
2386
+ }
2387
+ const currentOffset = getObservedBootstrapInitialScrollOffset(state);
2388
+ return Math.abs(currentOffset - resolveInitialScrollOffset(ctx, target)) > epsilon;
2389
+ }
2390
+ function getObservedBootstrapInitialScrollOffset(state) {
2391
+ var _a3, _b, _c, _d;
2392
+ const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2393
+ return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2394
+ }
2395
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2396
+ var _a3, _b;
2397
+ const state = ctx.state;
2398
+ const initialScroll = state.initialScroll;
2399
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
2400
+ return;
2401
+ }
2402
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2403
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2404
+ clearPendingInitialScrollFooterLayout(ctx, {
2405
+ dataLength: state.props.data.length,
2406
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2407
+ target: initialScroll
2408
+ });
2409
+ return;
2410
+ }
2411
+ clearFinishedViewportRetargetableInitialScroll(state);
2412
+ }
2413
+ }
2414
+ function startBootstrapInitialScrollOnMount(ctx, options) {
2415
+ var _a3, _b, _c;
2416
+ const { initialScrollAtEnd, target } = options;
2417
+ const state = ctx.state;
2418
+ const offset = resolveInitialScrollOffset(ctx, target);
2419
+ 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);
2420
+ const shouldFinishWithPreservedTarget = state.props.data.length === 0 && target.index !== void 0;
2421
+ if (shouldFinishAtOrigin) {
2422
+ clearBootstrapInitialScrollSession(state);
2423
+ finishInitialScroll(ctx, {
2424
+ resolvedOffset: offset
2425
+ });
2426
+ } else if (shouldFinishWithPreservedTarget) {
2427
+ clearBootstrapInitialScrollSession(state);
2428
+ finishInitialScroll(ctx, {
2429
+ preserveTarget: true,
2430
+ resolvedOffset: offset
2431
+ });
2432
+ } else {
2433
+ startBootstrapInitialScrollSession(state, {
2434
+ scroll: offset,
2435
+ seedContentOffset: Platform2.OS === "web" ? 0 : offset,
2436
+ targetIndexSeed: target.index
2437
+ });
2438
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2439
+ }
2440
+ }
2441
+ function handleBootstrapInitialScrollDataChange(ctx, options) {
2442
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2443
+ const state = ctx.state;
2444
+ const initialScroll = state.initialScroll;
2445
+ if (isOffsetInitialScrollSession(state) || !initialScroll) {
2446
+ return;
2447
+ }
2448
+ const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2449
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2450
+ const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2451
+ if (shouldClearFinishedResizePreservation) {
2452
+ clearPreservedInitialScrollTarget(state);
2453
+ return;
2454
+ }
2455
+ const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2456
+ if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2457
+ return;
2458
+ }
2459
+ if (shouldRetargetBottomAligned) {
2460
+ const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2461
+ dataLength,
2462
+ footerSize: peek$(ctx, "footerSize") || 0,
2463
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2464
+ stylePaddingBottom
2465
+ }) : createRetargetedBottomAlignedInitialScroll({
2466
+ dataLength,
2467
+ footerSize: peek$(ctx, "footerSize") || 0,
2468
+ initialScrollAtEnd,
2469
+ stylePaddingBottom,
2470
+ target: initialScroll
2471
+ });
2472
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2473
+ clearPendingInitialScrollFooterLayout(ctx, {
2474
+ dataLength,
2475
+ stylePaddingBottom,
2476
+ target: initialScroll
2477
+ });
2478
+ return;
2479
+ }
2480
+ if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2481
+ setInitialScrollTarget(state, updatedInitialScroll, {
2482
+ resetDidFinish: shouldResetDidFinish
2483
+ });
2484
+ rearmBootstrapInitialScroll(ctx, {
2485
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2486
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2487
+ targetIndexSeed: updatedInitialScroll.index
2488
+ });
2489
+ return;
2490
+ }
2491
+ }
2492
+ if (!didDataChange) {
2493
+ return;
2494
+ }
2495
+ if (bootstrapInitialScroll || shouldResetDidFinish) {
2496
+ setInitialScrollTarget(state, initialScroll, {
2497
+ resetDidFinish: shouldResetDidFinish
2498
+ });
2499
+ rearmBootstrapInitialScroll(ctx, {
2500
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
2501
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2502
+ targetIndexSeed: initialScroll.index
2503
+ });
2504
+ }
2505
+ }
2506
+ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2507
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
2508
+ const state = ctx.state;
2509
+ if (!initialScrollAtEnd) {
2510
+ return;
2511
+ }
2512
+ const initialScroll = state.initialScroll;
2513
+ if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
2514
+ return;
2515
+ }
2516
+ const shouldProcessFooterLayout = !!getBootstrapInitialScrollSession(state) || shouldPreserveInitialScrollForFooterLayout(initialScroll);
2517
+ if (!shouldProcessFooterLayout) {
2518
+ return;
2519
+ }
2520
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2521
+ clearPendingInitialScrollFooterLayout(ctx, {
2522
+ dataLength,
2523
+ stylePaddingBottom,
2524
+ target: initialScroll
2525
+ });
2526
+ } else {
2527
+ const updatedInitialScroll = createInitialScrollAtEndTarget({
2528
+ dataLength,
2529
+ footerSize,
2530
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2531
+ stylePaddingBottom
2532
+ });
2533
+ const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2534
+ if (!didTargetChange) {
2535
+ clearPendingInitialScrollFooterLayout(ctx, {
2536
+ dataLength,
2537
+ stylePaddingBottom,
2538
+ target: initialScroll
2539
+ });
2540
+ } else {
2541
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
2542
+ setInitialScrollTarget(state, updatedInitialScroll, {
2543
+ resetDidFinish: didFinishInitialScroll
2544
+ });
2545
+ rearmBootstrapInitialScroll(ctx, {
2546
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2547
+ targetIndexSeed: updatedInitialScroll.index
2548
+ });
2549
+ }
2550
+ }
2551
+ }
2552
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
2553
+ const state = ctx.state;
2554
+ const initialScroll = state.initialScroll;
2555
+ if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2556
+ return;
2557
+ }
2558
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2559
+ if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2560
+ return;
2561
+ }
2562
+ const didFinishInitialScroll = state.didFinishInitialScroll;
2563
+ if (didFinishInitialScroll) {
2564
+ setInitialScrollTarget(state, initialScroll, {
2565
+ resetDidFinish: true
2566
+ });
2567
+ state.clearPreservedInitialScrollOnNextFinish = true;
2568
+ }
2569
+ rearmBootstrapInitialScroll(ctx, {
2570
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
2571
+ seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2572
+ targetIndexSeed: initialScroll.index
2573
+ });
2574
+ }
2575
+ function evaluateBootstrapInitialScroll(ctx) {
2576
+ var _a3, _b;
2577
+ const state = ctx.state;
2578
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2579
+ const initialScroll = state.initialScroll;
2580
+ if (!bootstrapInitialScroll || !initialScroll || isOffsetInitialScrollSession(state) || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
2581
+ return;
2582
+ }
2583
+ bootstrapInitialScroll.passCount += 1;
2584
+ if (abortBootstrapRevealIfNeeded(ctx, {
2585
+ mountFrameCount: bootstrapInitialScroll.mountFrameCount,
2586
+ passCount: bootstrapInitialScroll.passCount
2587
+ })) {
2588
+ return;
2589
+ }
2590
+ if (initialScroll.index !== void 0 && state.startBuffered >= 0 && state.endBuffered >= 0 && initialScroll.index >= state.startBuffered && initialScroll.index <= state.endBuffered) {
2591
+ bootstrapInitialScroll.targetIndexSeed = void 0;
2592
+ }
2593
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2594
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
2595
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
2596
+ const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
2597
+ const { data } = state.props;
2598
+ const visibleIndices = getBootstrapRevealVisibleIndices({
2599
+ dataLength: data.length,
2600
+ getSize: (index) => {
2601
+ var _a4, _b2;
2602
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
2603
+ return (_b2 = state.sizes.get(id)) != null ? _b2 : getItemSize(ctx, id, index, data[index]);
2604
+ },
2605
+ offset: resolvedOffset,
2606
+ positions: state.positions,
2607
+ scrollLength: state.scrollLength,
2608
+ startIndex: (_b = bootstrapInitialScroll.targetIndexSeed) != null ? _b : state.startBuffered >= 0 ? state.startBuffered : void 0
2609
+ });
2610
+ const areVisibleIndicesMeasured = visibleIndices.length > 0 && visibleIndices.every((index) => {
2611
+ var _a4;
2612
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
2613
+ return state.sizesKnown.has(id);
2614
+ });
2615
+ const previousResolvedOffset = bootstrapInitialScroll.previousResolvedOffset;
2616
+ const previousVisibleIndices = bootstrapInitialScroll.visibleIndices;
2617
+ bootstrapInitialScroll.previousResolvedOffset = resolvedOffset;
2618
+ bootstrapInitialScroll.visibleIndices = visibleIndices;
2619
+ if (didResolvedOffsetChange) {
2620
+ bootstrapInitialScroll.scroll = resolvedOffset;
2621
+ queueBootstrapInitialScrollReevaluation(state);
2622
+ return;
2623
+ }
2624
+ if (!areMountedBufferedIndicesMeasured || !areVisibleIndicesMeasured) {
2625
+ return;
2626
+ }
2627
+ const didRevealSettle = previousResolvedOffset !== void 0 && Math.abs(previousResolvedOffset - resolvedOffset) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON && doVisibleIndicesMatch(previousVisibleIndices, visibleIndices);
2628
+ if (!didRevealSettle) {
2629
+ queueBootstrapInitialScrollReevaluation(state);
2630
+ return;
2631
+ }
2632
+ if (Platform2.OS !== "web" && Platform2.OS !== "android" && Math.abs(bootstrapInitialScroll.seedContentOffset - resolvedOffset) <= 1 && Math.abs(getObservedBootstrapInitialScrollOffset(state) - resolvedOffset) <= 1) {
2633
+ finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset);
2634
+ } else {
2635
+ clearBootstrapInitialScrollSession(state);
2636
+ dispatchInitialScroll(ctx, {
2637
+ forceScroll: true,
2638
+ resolvedOffset,
2639
+ target: initialScroll,
2640
+ waitForCompletionFrame: Platform2.OS === "web"
2641
+ });
2642
+ }
2643
+ }
2644
+ function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
2645
+ var _a3;
2646
+ const state = ctx.state;
2647
+ clearBootstrapInitialScrollSession(state);
2648
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
2649
+ finishInitialScroll(ctx, {
2650
+ preserveTarget: shouldPreserveResizeTarget,
2651
+ recalculateItems: true,
2652
+ resolvedOffset,
2653
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
2654
+ });
2655
+ }
2656
+ function abortBootstrapInitialScroll(ctx) {
2657
+ var _a3, _b, _c, _d;
2658
+ const state = ctx.state;
2659
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2660
+ const initialScroll = state.initialScroll;
2661
+ if (bootstrapInitialScroll && initialScroll && !isOffsetInitialScrollSession(state) && state.refScroller.current) {
2662
+ clearBootstrapInitialScrollSession(state);
2663
+ dispatchInitialScroll(ctx, {
2664
+ forceScroll: true,
2665
+ resolvedOffset: bootstrapInitialScroll.scroll,
2666
+ target: initialScroll,
2667
+ waitForCompletionFrame: Platform2.OS === "web"
2668
+ });
2669
+ } else {
2670
+ finishBootstrapInitialScrollWithoutScroll(
2671
+ ctx,
2672
+ (_d = (_c = (_b = (_a3 = getBootstrapInitialScrollSession(state)) == null ? void 0 : _a3.scroll) != null ? _b : state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0
2673
+ );
2674
+ }
2675
+ }
2676
+
2677
+ // src/core/initialScrollLifecycle.ts
2678
+ function handleInitialScrollLayoutReady(ctx) {
2679
+ var _a3;
2680
+ if (!ctx.state.initialScroll) {
2681
+ return;
2682
+ }
2683
+ const runScroll = () => advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
2684
+ runScroll();
2685
+ if (((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset") {
2686
+ requestAnimationFrame(runScroll);
2687
+ }
2688
+ checkFinishedScroll(ctx, { onlyIfAligned: true });
2689
+ }
2690
+ function initializeInitialScrollOnMount(ctx, options) {
1455
2691
  var _a3, _b;
2692
+ const { dataLength, hasFooterComponent, initialContentOffset, initialScrollAtEnd, useBootstrapInitialScroll } = options;
1456
2693
  const state = ctx.state;
1457
- const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1458
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1459
- const {
1460
- props: { horizontal }
1461
- } = state;
1462
- if (state.animFrameCheckFinishedScroll) {
1463
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2694
+ const initialScroll = state.initialScroll;
2695
+ const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
2696
+ const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
2697
+ if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
2698
+ setInitialScrollTarget(state, {
2699
+ ...initialScroll,
2700
+ contentOffset: resolvedInitialContentOffset,
2701
+ preserveForFooterLayout
2702
+ });
1464
2703
  }
1465
- if (state.timeoutCheckFinishedScrollFallback) {
1466
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2704
+ if (useBootstrapInitialScroll && initialScroll && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2705
+ startBootstrapInitialScrollOnMount(ctx, {
2706
+ initialScrollAtEnd,
2707
+ target: state.initialScroll
2708
+ });
2709
+ return;
1467
2710
  }
1468
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1469
- offset = clampScrollOffset(ctx, offset, scrollTarget);
1470
- state.scrollHistory.length = 0;
1471
- if (!noScrollingTo) {
1472
- state.scrollingTo = {
1473
- ...scrollTarget,
1474
- targetOffset: offset
1475
- };
2711
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
2712
+ if (!resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
2713
+ if (initialScroll && !initialScrollAtEnd) {
2714
+ finishInitialScroll(ctx, {
2715
+ resolvedOffset: resolvedInitialContentOffset
2716
+ });
2717
+ } else {
2718
+ setInitialRenderState(ctx, { didInitialScroll: true });
2719
+ }
1476
2720
  }
1477
- state.scrollPending = offset;
1478
- const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!state.initialNativeScrollWatchdog) && offset > WATCHDOG_OFFSET_EPSILON;
1479
- const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && offset <= WATCHDOG_OFFSET_EPSILON;
1480
- if (shouldWatchInitialNativeScroll) {
1481
- state.hasScrolled = false;
1482
- state.initialNativeScrollWatchdog = {
1483
- startScroll: (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.startScroll) != null ? _b : state.scroll,
1484
- targetOffset: offset
1485
- };
1486
- } else if (shouldClearInitialNativeScrollWatchdog) {
1487
- state.initialNativeScrollWatchdog = void 0;
2721
+ }
2722
+ function handleInitialScrollDataChange(ctx, options) {
2723
+ var _a3, _b, _c;
2724
+ const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
2725
+ const state = ctx.state;
2726
+ const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
2727
+ if (state.initialScrollSession) {
2728
+ state.initialScrollSession.previousDataLength = dataLength;
1488
2729
  }
1489
- if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1490
- doScrollTo(ctx, { animated, horizontal, offset });
1491
- } else {
1492
- state.scroll = offset;
2730
+ setInitialScrollSession(state);
2731
+ if (useBootstrapInitialScroll) {
2732
+ handleBootstrapInitialScrollDataChange(ctx, {
2733
+ dataLength,
2734
+ didDataChange,
2735
+ initialScrollAtEnd,
2736
+ previousDataLength,
2737
+ stylePaddingBottom
2738
+ });
2739
+ return;
1493
2740
  }
2741
+ const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
2742
+ if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
2743
+ return;
2744
+ }
2745
+ if (shouldReplayFinishedOffsetInitialScroll) {
2746
+ state.didFinishInitialScroll = false;
2747
+ }
2748
+ advanceCurrentInitialScrollSession(ctx);
1494
2749
  }
1495
2750
 
1496
- // src/core/doMaintainScrollAtEnd.ts
1497
- function doMaintainScrollAtEnd(ctx) {
2751
+ // src/utils/requestAdjust.ts
2752
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1498
2753
  const state = ctx.state;
1499
- const {
1500
- didContainersLayout,
1501
- isAtEnd,
1502
- pendingNativeMVCPAdjust,
1503
- refScroller,
1504
- props: { maintainScrollAtEnd }
1505
- } = state;
1506
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
1507
- if (pendingNativeMVCPAdjust) {
1508
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1509
- return false;
1510
- }
1511
- state.pendingMaintainScrollAtEnd = false;
1512
- if (shouldMaintainScrollAtEnd) {
1513
- const contentSize = getContentSize(ctx);
1514
- if (contentSize < state.scrollLength) {
1515
- state.scroll = 0;
1516
- }
1517
- requestAnimationFrame(() => {
1518
- var _a3;
1519
- if (state.isAtEnd) {
1520
- state.maintainingScrollAtEnd = true;
1521
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
1522
- animated: maintainScrollAtEnd.animated
2754
+ if (Math.abs(positionDiff) > 0.1) {
2755
+ const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2756
+ const doit = () => {
2757
+ if (needsScrollWorkaround) {
2758
+ scrollTo(ctx, {
2759
+ noScrollingTo: true,
2760
+ offset: state.scroll
1523
2761
  });
1524
- setTimeout(
1525
- () => {
1526
- state.maintainingScrollAtEnd = false;
1527
- },
1528
- maintainScrollAtEnd.animated ? 500 : 0
1529
- );
2762
+ } else {
2763
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2764
+ if (state.adjustingFromInitialMount) {
2765
+ state.adjustingFromInitialMount--;
2766
+ }
1530
2767
  }
1531
- });
1532
- return true;
2768
+ };
2769
+ state.scroll += positionDiff;
2770
+ state.scrollForNextCalculateItemsInView = void 0;
2771
+ const readyToRender = peek$(ctx, "readyToRender");
2772
+ if (readyToRender) {
2773
+ doit();
2774
+ if (Platform2.OS !== "web") {
2775
+ const threshold = state.scroll - positionDiff / 2;
2776
+ if (!state.ignoreScrollFromMVCP) {
2777
+ state.ignoreScrollFromMVCP = {};
2778
+ }
2779
+ if (positionDiff > 0) {
2780
+ state.ignoreScrollFromMVCP.lt = threshold;
2781
+ } else {
2782
+ state.ignoreScrollFromMVCP.gt = threshold;
2783
+ }
2784
+ if (state.ignoreScrollFromMVCPTimeout) {
2785
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2786
+ }
2787
+ const delay = needsScrollWorkaround ? 250 : 100;
2788
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2789
+ var _a3;
2790
+ state.ignoreScrollFromMVCP = void 0;
2791
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2792
+ if (shouldForceUpdate) {
2793
+ state.ignoreScrollFromMVCPIgnored = false;
2794
+ state.scrollPending = state.scroll;
2795
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2796
+ }
2797
+ }, delay);
2798
+ }
2799
+ } else {
2800
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2801
+ requestAnimationFrame(doit);
2802
+ }
1533
2803
  }
1534
- return false;
1535
2804
  }
1536
2805
 
1537
2806
  // src/core/mvcp.ts
@@ -1658,7 +2927,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
1658
2927
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1659
2928
  return true;
1660
2929
  }
1661
- if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
2930
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
1662
2931
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1663
2932
  return true;
1664
2933
  }
@@ -1803,202 +3072,104 @@ function prepareMVCP(ctx, dataChanged) {
1803
3072
  anchorId: anchorIdForLock,
1804
3073
  anchorPosition: anchorPositionForLock,
1805
3074
  dataChanged,
1806
- now,
1807
- positionDiff
1808
- });
1809
- if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
1810
- state.pendingNativeMVCPAdjust = {
1811
- amount: positionDiff,
1812
- furthestProgressTowardAmount: 0,
1813
- manualApplied: 0,
1814
- startScroll: prevScroll
1815
- };
1816
- maybeApplyPredictedNativeMVCPAdjust(ctx);
1817
- return;
1818
- }
1819
- if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
1820
- requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
1821
- }
1822
- };
1823
- }
1824
- }
1825
-
1826
- // src/platform/flushSync.native.ts
1827
- var flushSync = (fn) => {
1828
- fn();
1829
- };
1830
-
1831
- // src/core/updateScroll.ts
1832
- function updateScroll(ctx, newScroll, forceUpdate) {
1833
- var _a3;
1834
- const state = ctx.state;
1835
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1836
- const prevScroll = state.scroll;
1837
- state.hasScrolled = true;
1838
- state.lastBatchingAction = Date.now();
1839
- const currentTime = Date.now();
1840
- const adjust = scrollAdjustHandler.getAdjust();
1841
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1842
- if (adjustChanged) {
1843
- scrollHistory.length = 0;
1844
- }
1845
- state.lastScrollAdjustForHistory = adjust;
1846
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1847
- if (!adjustChanged) {
1848
- scrollHistory.push({ scroll: newScroll, time: currentTime });
1849
- }
1850
- }
1851
- if (scrollHistory.length > 5) {
1852
- scrollHistory.shift();
1853
- }
1854
- if (ignoreScrollFromMVCP && !scrollingTo) {
1855
- const { lt, gt } = ignoreScrollFromMVCP;
1856
- if (lt && newScroll < lt || gt && newScroll > gt) {
1857
- state.ignoreScrollFromMVCPIgnored = true;
1858
- return;
1859
- }
1860
- }
1861
- state.scrollPrev = prevScroll;
1862
- state.scrollPrevTime = state.scrollTime;
1863
- state.scroll = newScroll;
1864
- state.scrollTime = currentTime;
1865
- const scrollDelta = Math.abs(newScroll - prevScroll);
1866
- const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
1867
- const scrollLength = state.scrollLength;
1868
- const lastCalculated = state.scrollLastCalculate;
1869
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1870
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1871
- if (shouldUpdate) {
1872
- state.scrollLastCalculate = state.scroll;
1873
- state.ignoreScrollFromMVCPIgnored = false;
1874
- state.lastScrollDelta = scrollDelta;
1875
- const runCalculateItems = () => {
1876
- var _a4;
1877
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
1878
- checkThresholds(ctx);
1879
- };
1880
- if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1881
- flushSync(runCalculateItems);
1882
- } else {
1883
- runCalculateItems();
1884
- }
1885
- const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
1886
- if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
1887
- state.pendingMaintainScrollAtEnd = false;
1888
- doMaintainScrollAtEnd(ctx);
1889
- }
1890
- state.dataChangeNeedsScrollUpdate = false;
1891
- state.lastScrollDelta = 0;
1892
- }
1893
- }
1894
-
1895
- // src/utils/requestAdjust.ts
1896
- function requestAdjust(ctx, positionDiff, dataChanged) {
1897
- const state = ctx.state;
1898
- if (Math.abs(positionDiff) > 0.1) {
1899
- const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1900
- const doit = () => {
1901
- if (needsScrollWorkaround) {
1902
- scrollTo(ctx, {
1903
- noScrollingTo: true,
1904
- offset: state.scroll
1905
- });
1906
- } else {
1907
- state.scrollAdjustHandler.requestAdjust(positionDiff);
1908
- if (state.adjustingFromInitialMount) {
1909
- state.adjustingFromInitialMount--;
1910
- }
1911
- }
1912
- };
1913
- state.scroll += positionDiff;
1914
- state.scrollForNextCalculateItemsInView = void 0;
1915
- const readyToRender = peek$(ctx, "readyToRender");
1916
- if (readyToRender) {
1917
- doit();
1918
- if (Platform2.OS !== "web") {
1919
- const threshold = state.scroll - positionDiff / 2;
1920
- if (!state.ignoreScrollFromMVCP) {
1921
- state.ignoreScrollFromMVCP = {};
1922
- }
1923
- if (positionDiff > 0) {
1924
- state.ignoreScrollFromMVCP.lt = threshold;
1925
- } else {
1926
- state.ignoreScrollFromMVCP.gt = threshold;
1927
- }
1928
- if (state.ignoreScrollFromMVCPTimeout) {
1929
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
1930
- }
1931
- const delay = needsScrollWorkaround ? 250 : 100;
1932
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
1933
- state.ignoreScrollFromMVCP = void 0;
1934
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1935
- if (shouldForceUpdate) {
1936
- state.ignoreScrollFromMVCPIgnored = false;
1937
- state.scrollPending = state.scroll;
1938
- updateScroll(ctx, state.scroll, true);
1939
- }
1940
- }, delay);
3075
+ now,
3076
+ positionDiff
3077
+ });
3078
+ if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
3079
+ state.pendingNativeMVCPAdjust = {
3080
+ amount: positionDiff,
3081
+ furthestProgressTowardAmount: 0,
3082
+ manualApplied: 0,
3083
+ startScroll: prevScroll
3084
+ };
3085
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
3086
+ return;
1941
3087
  }
1942
- } else {
1943
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1944
- requestAnimationFrame(doit);
1945
- }
3088
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3089
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3090
+ }
3091
+ };
1946
3092
  }
1947
3093
  }
1948
3094
 
1949
- // src/core/ensureInitialAnchor.ts
1950
- var INITIAL_ANCHOR_TOLERANCE = 0.5;
1951
- var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1952
- var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1953
- function ensureInitialAnchor(ctx) {
1954
- var _a3, _b, _c, _d, _e, _f;
3095
+ // src/core/syncMountedContainer.ts
3096
+ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3097
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
1955
3098
  const state = ctx.state;
1956
- const { initialAnchor, didContainersLayout, scroll, scrollLength } = state;
1957
- const anchor = initialAnchor;
1958
- if (state.initialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
1959
- return;
1960
- }
1961
- const item = state.props.data[anchor.index];
1962
- if (!didContainersLayout) {
1963
- return;
1964
- }
1965
- const id = getId(state, anchor.index);
1966
- if (state.positions[anchor.index] === void 0) {
1967
- return;
1968
- }
1969
- const size = getItemSize(ctx, id, anchor.index, item, true, true);
1970
- if (size === void 0) {
1971
- return;
1972
- }
1973
- const availableSpace = Math.max(0, scrollLength - size);
1974
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1975
- const desiredOffset = calculateOffsetForIndex(ctx, anchor.index) + topOffsetAdjustment - ((_b = anchor.viewOffset) != null ? _b : 0) - ((_c = anchor.viewPosition) != null ? _c : 0) * availableSpace;
1976
- const clampedDesiredOffset = clampScrollOffset(ctx, desiredOffset, anchor);
1977
- const delta = clampedDesiredOffset - scroll;
1978
- if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1979
- const settledTicks = ((_d = anchor.settledTicks) != null ? _d : 0) + 1;
1980
- if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1981
- state.initialAnchor = void 0;
1982
- } else {
1983
- anchor.settledTicks = settledTicks;
3099
+ const {
3100
+ columns,
3101
+ columnSpans,
3102
+ positions,
3103
+ props: { data, itemsAreEqual, keyExtractor }
3104
+ } = state;
3105
+ const item = data[itemIndex];
3106
+ if (item === void 0) {
3107
+ return { didChangePosition: false, didRefreshData: false };
3108
+ }
3109
+ const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3110
+ let didChangePosition = false;
3111
+ let didRefreshData = false;
3112
+ if (updateLayout) {
3113
+ const positionValue = positions[itemIndex];
3114
+ if (positionValue === void 0) {
3115
+ set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3116
+ return { didChangePosition: false, didRefreshData: false };
3117
+ }
3118
+ const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3119
+ const column = columns[itemIndex] || 1;
3120
+ const span = columnSpans[itemIndex] || 1;
3121
+ const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
3122
+ const prevColumn = peek$(ctx, `containerColumn${containerIndex}`);
3123
+ const prevSpan = peek$(ctx, `containerSpan${containerIndex}`);
3124
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3125
+ set$(ctx, `containerPosition${containerIndex}`, position);
3126
+ didChangePosition = true;
3127
+ }
3128
+ if (column >= 0 && column !== prevColumn) {
3129
+ set$(ctx, `containerColumn${containerIndex}`, column);
3130
+ }
3131
+ if (span !== prevSpan) {
3132
+ set$(ctx, `containerSpan${containerIndex}`, span);
3133
+ }
3134
+ }
3135
+ const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3136
+ if (prevData !== item) {
3137
+ 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;
3138
+ const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3139
+ if (cachedComparison === 2) {
3140
+ set$(ctx, `containerItemData${containerIndex}`, item);
3141
+ didRefreshData = true;
3142
+ } else if (cachedComparison !== 1) {
3143
+ const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3144
+ const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3145
+ if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3146
+ set$(ctx, `containerItemData${containerIndex}`, item);
3147
+ didRefreshData = true;
3148
+ } else if (!itemsAreEqual) {
3149
+ set$(ctx, `containerItemData${containerIndex}`, item);
3150
+ didRefreshData = true;
3151
+ } else {
3152
+ const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3153
+ if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
3154
+ if (state.previousData) {
3155
+ state.pendingDataComparison = {
3156
+ byIndex: [],
3157
+ nextData: data,
3158
+ previousData: state.previousData
3159
+ };
3160
+ }
3161
+ }
3162
+ if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3163
+ state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3164
+ }
3165
+ if (!isEqual) {
3166
+ set$(ctx, `containerItemData${containerIndex}`, item);
3167
+ didRefreshData = true;
3168
+ }
3169
+ }
1984
3170
  }
1985
- return;
1986
- }
1987
- if (((_e = anchor.attempts) != null ? _e : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1988
- state.initialAnchor = void 0;
1989
- return;
1990
- }
1991
- const lastDelta = anchor.lastDelta;
1992
- if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1993
- state.initialAnchor = void 0;
1994
- return;
1995
3171
  }
1996
- Object.assign(anchor, {
1997
- attempts: ((_f = anchor.attempts) != null ? _f : 0) + 1,
1998
- lastDelta: delta,
1999
- settledTicks: 0
2000
- });
2001
- requestAdjust(ctx, delta);
3172
+ return { didChangePosition, didRefreshData };
2002
3173
  }
2003
3174
 
2004
3175
  // src/core/prepareColumnStartState.ts
@@ -2163,9 +3334,10 @@ function updateSnapToOffsets(ctx) {
2163
3334
  }
2164
3335
 
2165
3336
  // src/core/updateItemPositions.ts
2166
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
3337
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
2167
3338
  doMVCP: false,
2168
3339
  forceFullUpdate: false,
3340
+ optimizeForVisibleWindow: false,
2169
3341
  scrollBottomBuffered: -1,
2170
3342
  startIndex: 0
2171
3343
  }) {
@@ -2190,7 +3362,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2190
3362
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
2191
3363
  const lastScrollDelta = state.lastScrollDelta;
2192
3364
  const velocity = getScrollVelocity(state);
2193
- const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3365
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
2194
3366
  const maxVisibleArea = scrollBottomBuffered + 1e3;
2195
3367
  const useAverageSize = !getEstimatedItemSize;
2196
3368
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
@@ -2305,7 +3477,15 @@ function ensureViewabilityState(ctx, configId) {
2305
3477
  }
2306
3478
  let state = map.get(configId);
2307
3479
  if (!state) {
2308
- state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
3480
+ state = {
3481
+ end: -1,
3482
+ endBuffered: -1,
3483
+ previousEnd: -1,
3484
+ previousStart: -1,
3485
+ start: -1,
3486
+ startBuffered: -1,
3487
+ viewableItems: []
3488
+ };
2309
3489
  map.set(configId, state);
2310
3490
  }
2311
3491
  return state;
@@ -2325,7 +3505,7 @@ function setupViewability(props) {
2325
3505
  }
2326
3506
  return viewabilityConfigCallbackPairs;
2327
3507
  }
2328
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
3508
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
2329
3509
  const {
2330
3510
  timeouts,
2331
3511
  props: { data }
@@ -2334,6 +3514,8 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
2334
3514
  const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
2335
3515
  viewabilityState.start = start;
2336
3516
  viewabilityState.end = end;
3517
+ viewabilityState.startBuffered = startBuffered;
3518
+ viewabilityState.endBuffered = endBuffered;
2337
3519
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
2338
3520
  const timer = setTimeout(() => {
2339
3521
  timeouts.delete(timer);
@@ -2349,7 +3531,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2349
3531
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
2350
3532
  const configId = viewabilityConfig.id;
2351
3533
  const viewabilityState = ensureViewabilityState(ctx, configId);
2352
- const { viewableItems: previousViewableItems, start, end } = viewabilityState;
3534
+ const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
2353
3535
  const viewabilityTokens = /* @__PURE__ */ new Map();
2354
3536
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
2355
3537
  viewabilityTokens.set(
@@ -2418,7 +3600,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2418
3600
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
2419
3601
  }
2420
3602
  if (onViewableItemsChanged) {
2421
- onViewableItemsChanged({ changed, viewableItems });
3603
+ onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
2422
3604
  }
2423
3605
  }
2424
3606
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -2514,23 +3696,6 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
2514
3696
  var unstableBatchedUpdates = ReactNative.unstable_batchedUpdates;
2515
3697
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
2516
3698
 
2517
- // src/utils/checkAllSizesKnown.ts
2518
- function isNullOrUndefined2(value) {
2519
- return value === null || value === void 0;
2520
- }
2521
- function checkAllSizesKnown(state) {
2522
- const { startBuffered, endBuffered, sizesKnown } = state;
2523
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
2524
- let areAllKnown = true;
2525
- for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
2526
- const key = getId(state, i);
2527
- areAllKnown && (areAllKnown = sizesKnown.has(key));
2528
- }
2529
- return areAllKnown;
2530
- }
2531
- return false;
2532
- }
2533
-
2534
3699
  // src/utils/findAvailableContainers.ts
2535
3700
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2536
3701
  const numContainers = peek$(ctx, "numContainers");
@@ -2653,97 +3818,11 @@ function comparatorByDistance(a, b) {
2653
3818
  return b.distance - a.distance;
2654
3819
  }
2655
3820
 
2656
- // src/core/scrollToIndex.ts
2657
- function scrollToIndex(ctx, {
2658
- index,
2659
- viewOffset = 0,
2660
- animated = true,
2661
- forceScroll,
2662
- isInitialScroll,
2663
- viewPosition
2664
- }) {
2665
- const state = ctx.state;
2666
- const { data } = state.props;
2667
- if (index >= data.length) {
2668
- index = data.length - 1;
2669
- } else if (index < 0) {
2670
- index = 0;
2671
- }
2672
- const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2673
- const isLast = index === data.length - 1;
2674
- if (isLast && viewPosition === void 0) {
2675
- viewPosition = 1;
2676
- }
2677
- state.scrollForNextCalculateItemsInView = void 0;
2678
- const targetId = getId(state, index);
2679
- const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2680
- scrollTo(ctx, {
2681
- animated,
2682
- forceScroll,
2683
- index,
2684
- isInitialScroll,
2685
- itemSize,
2686
- offset: firstIndexOffset,
2687
- viewOffset,
2688
- viewPosition: viewPosition != null ? viewPosition : 0
2689
- });
2690
- }
2691
-
2692
- // src/utils/performInitialScroll.ts
2693
- function performInitialScroll(ctx, params) {
2694
- var _a3;
2695
- const { forceScroll, initialScrollUsesOffset, resolvedOffset, target } = params;
2696
- if (initialScrollUsesOffset || resolvedOffset !== void 0) {
2697
- scrollTo(ctx, {
2698
- animated: false,
2699
- forceScroll,
2700
- index: initialScrollUsesOffset ? void 0 : target.index,
2701
- isInitialScroll: true,
2702
- offset: (_a3 = resolvedOffset != null ? resolvedOffset : target.contentOffset) != null ? _a3 : 0,
2703
- precomputedWithViewOffset: resolvedOffset !== void 0
2704
- });
2705
- return;
2706
- }
2707
- if (target.index === void 0) {
2708
- return;
2709
- }
2710
- scrollToIndex(ctx, {
2711
- ...target,
2712
- animated: false,
2713
- forceScroll,
2714
- isInitialScroll: true
2715
- });
2716
- }
2717
-
2718
3821
  // src/utils/setDidLayout.ts
2719
3822
  function setDidLayout(ctx) {
2720
3823
  const state = ctx.state;
2721
- const { initialScroll } = state;
2722
3824
  state.queuedInitialLayout = true;
2723
3825
  checkAtBottom(ctx);
2724
- if (initialScroll) {
2725
- const runScroll = () => {
2726
- var _a3, _b;
2727
- const target = state.initialScroll;
2728
- if (!target) {
2729
- return;
2730
- }
2731
- const activeInitialTargetOffset = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? (_b = state.scrollingTo.targetOffset) != null ? _b : state.scrollingTo.offset : void 0;
2732
- const desiredInitialTargetOffset = state.initialScrollUsesOffset ? target.contentOffset : activeInitialTargetOffset;
2733
- const isAlreadyAtDesiredInitialTarget = desiredInitialTargetOffset !== void 0 && Math.abs(state.scroll - desiredInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - desiredInitialTargetOffset) <= 1;
2734
- if (!isAlreadyAtDesiredInitialTarget) {
2735
- performInitialScroll(ctx, {
2736
- forceScroll: true,
2737
- initialScrollUsesOffset: state.initialScrollUsesOffset,
2738
- // Offset-based initial scrolls do not need item lookup, so they can run even before data exists.
2739
- // Re-run on the next frame to pick up measured viewport size without waiting for index resolution.
2740
- target
2741
- });
2742
- }
2743
- };
2744
- runScroll();
2745
- requestAnimationFrame(runScroll);
2746
- }
2747
3826
  setInitialRenderState(ctx, { didLayout: true });
2748
3827
  }
2749
3828
 
@@ -2818,7 +3897,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
2818
3897
  function calculateItemsInView(ctx, params = {}) {
2819
3898
  const state = ctx.state;
2820
3899
  batchedUpdates(() => {
2821
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
3900
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
2822
3901
  const {
2823
3902
  columns,
2824
3903
  columnSpans,
@@ -2826,7 +3905,6 @@ function calculateItemsInView(ctx, params = {}) {
2826
3905
  enableScrollForNextCalculateItemsInView,
2827
3906
  idCache,
2828
3907
  indexByKey,
2829
- initialScroll,
2830
3908
  minIndexSizeChanged,
2831
3909
  positions,
2832
3910
  props: {
@@ -2834,7 +3912,6 @@ function calculateItemsInView(ctx, params = {}) {
2834
3912
  alwaysRenderIndicesSet,
2835
3913
  drawDistance,
2836
3914
  getItemType,
2837
- itemsAreEqual,
2838
3915
  keyExtractor,
2839
3916
  onStickyHeaderChange
2840
3917
  },
@@ -2850,11 +3927,10 @@ function calculateItemsInView(ctx, params = {}) {
2850
3927
  const alwaysRenderArr = alwaysRenderIndicesArr || [];
2851
3928
  const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2852
3929
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
3930
+ const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
3931
+ const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
2853
3932
  const prevNumContainers = peek$(ctx, "numContainers");
2854
3933
  if (!data || scrollLength === 0 || !prevNumContainers) {
2855
- if (!IsNewArchitecture && state.initialAnchor) {
2856
- ensureInitialAnchor(ctx);
2857
- }
2858
3934
  return;
2859
3935
  }
2860
3936
  let totalSize = getContentSize(ctx);
@@ -2862,17 +3938,13 @@ function calculateItemsInView(ctx, params = {}) {
2862
3938
  const numColumns = peek$(ctx, "numColumns");
2863
3939
  const speed = getScrollVelocity(state);
2864
3940
  const scrollExtra = 0;
2865
- const { queuedInitialLayout } = state;
2866
- let { scroll: scrollState } = state;
2867
- if (!queuedInitialLayout && initialScroll) {
2868
- const updatedOffset = state.initialScrollUsesOffset ? (_a3 = initialScroll.contentOffset) != null ? _a3 : 0 : calculateOffsetWithOffsetPosition(
2869
- ctx,
2870
- calculateOffsetForIndex(ctx, initialScroll.index),
2871
- initialScroll
2872
- );
2873
- scrollState = updatedOffset;
2874
- }
2875
- const scrollAdjustPending = (_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0;
3941
+ const { initialScroll, queuedInitialLayout } = state;
3942
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
3943
+ // Before the initial layout settles, keep viewport math anchored to the
3944
+ // current initial-scroll target instead of transient native adjustments.
3945
+ resolveInitialScrollOffset(ctx, initialScroll)
3946
+ ) : state.scroll;
3947
+ const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
2876
3948
  const scrollAdjustPad = scrollAdjustPending - topPad;
2877
3949
  let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2878
3950
  if (scroll + scrollLength > totalSize) {
@@ -2896,20 +3968,17 @@ function calculateItemsInView(ctx, params = {}) {
2896
3968
  const scrollTopBuffered = scroll - scrollBufferTop;
2897
3969
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2898
3970
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2899
- if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
3971
+ if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2900
3972
  const { top, bottom } = scrollForNextCalculateItemsInView;
2901
3973
  if (top === null && bottom === null) {
2902
3974
  state.scrollForNextCalculateItemsInView = void 0;
2903
3975
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2904
- if (!IsNewArchitecture && state.initialAnchor) {
2905
- ensureInitialAnchor(ctx);
2906
- }
2907
3976
  if (Platform2.OS !== "web" || !isInMVCPActiveMode(state)) {
2908
3977
  return;
2909
3978
  }
2910
3979
  }
2911
3980
  }
2912
- const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
3981
+ const checkMVCP = doMVCP && !suppressInitialScrollSideEffects ? prepareMVCP(ctx, dataChanged) : void 0;
2913
3982
  if (dataChanged) {
2914
3983
  indexByKey.clear();
2915
3984
  idCache.length = 0;
@@ -2917,10 +3986,12 @@ function calculateItemsInView(ctx, params = {}) {
2917
3986
  columns.length = 0;
2918
3987
  columnSpans.length = 0;
2919
3988
  }
2920
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
3989
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
3990
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
2921
3991
  updateItemPositions(ctx, dataChanged, {
2922
3992
  doMVCP,
2923
3993
  forceFullUpdate: !!forceFullItemPositions,
3994
+ optimizeForVisibleWindow,
2924
3995
  scrollBottomBuffered,
2925
3996
  startIndex
2926
3997
  });
@@ -2934,11 +4005,11 @@ function calculateItemsInView(ctx, params = {}) {
2934
4005
  let startBufferedId = null;
2935
4006
  let endNoBuffer = null;
2936
4007
  let endBuffered = null;
2937
- let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4008
+ let loopStart = (_e = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _e : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2938
4009
  for (let i = loopStart; i >= 0; i--) {
2939
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
4010
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2940
4011
  const top = positions[i];
2941
- const size = (_e = sizes.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i]);
4012
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
2942
4013
  const bottom = top + size;
2943
4014
  if (bottom > scroll - scrollBufferTop) {
2944
4015
  loopStart = i;
@@ -2969,8 +4040,8 @@ function calculateItemsInView(ctx, params = {}) {
2969
4040
  let firstFullyOnScreenIndex;
2970
4041
  const dataLength = data.length;
2971
4042
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2972
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2973
- const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
4043
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4044
+ const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
2974
4045
  const top = positions[i];
2975
4046
  if (!foundEnd) {
2976
4047
  if (startNoBuffer === null && top + size > scroll) {
@@ -3009,7 +4080,7 @@ function calculateItemsInView(ctx, params = {}) {
3009
4080
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
3010
4081
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
3011
4082
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
3012
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4083
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
3013
4084
  idsInView.push(id);
3014
4085
  }
3015
4086
  }
@@ -3042,7 +4113,7 @@ function calculateItemsInView(ctx, params = {}) {
3042
4113
  const needNewContainers = [];
3043
4114
  const needNewContainersSet = /* @__PURE__ */ new Set();
3044
4115
  for (let i = startBuffered; i <= endBuffered; i++) {
3045
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4116
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
3046
4117
  if (!containerItemKeys.has(id)) {
3047
4118
  needNewContainersSet.add(i);
3048
4119
  needNewContainers.push(i);
@@ -3051,7 +4122,7 @@ function calculateItemsInView(ctx, params = {}) {
3051
4122
  if (alwaysRenderArr.length > 0) {
3052
4123
  for (const index of alwaysRenderArr) {
3053
4124
  if (index < 0 || index >= dataLength) continue;
3054
- const id = (_j = idCache[index]) != null ? _j : getId(state, index);
4125
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3055
4126
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
3056
4127
  needNewContainersSet.add(index);
3057
4128
  needNewContainers.push(index);
@@ -3089,7 +4160,7 @@ function calculateItemsInView(ctx, params = {}) {
3089
4160
  for (let idx = 0; idx < needNewContainers.length; idx++) {
3090
4161
  const i = needNewContainers[idx];
3091
4162
  const containerIndex = availableContainers[idx];
3092
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4163
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
3093
4164
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
3094
4165
  if (oldKey && oldKey !== id) {
3095
4166
  containerItemKeys.delete(oldKey);
@@ -3130,7 +4201,7 @@ function calculateItemsInView(ctx, params = {}) {
3130
4201
  if (alwaysRenderArr.length > 0) {
3131
4202
  for (const index of alwaysRenderArr) {
3132
4203
  if (index < 0 || index >= dataLength) continue;
3133
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
4204
+ const id = (_n = idCache[index]) != null ? _n : getId(state, index);
3134
4205
  const containerIndex = containerItemKeys.get(id);
3135
4206
  if (containerIndex !== void 0) {
3136
4207
  state.stickyContainerPool.add(containerIndex);
@@ -3168,46 +4239,39 @@ function calculateItemsInView(ctx, params = {}) {
3168
4239
  set$(ctx, `containerSpan${i}`, 1);
3169
4240
  } else {
3170
4241
  const itemIndex = indexByKey.get(itemKey);
3171
- const item = data[itemIndex];
3172
- if (item !== void 0) {
3173
- const positionValue = positions[itemIndex];
3174
- if (positionValue === void 0) {
3175
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
3176
- } else {
3177
- const position = (positionValue || 0) - scrollAdjustPending;
3178
- const column = columns[itemIndex] || 1;
3179
- const span = columnSpans[itemIndex] || 1;
3180
- const prevPos = peek$(ctx, `containerPosition${i}`);
3181
- const prevColumn = peek$(ctx, `containerColumn${i}`);
3182
- const prevSpan = peek$(ctx, `containerSpan${i}`);
3183
- const prevData = peek$(ctx, `containerItemData${i}`);
3184
- if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3185
- set$(ctx, `containerPosition${i}`, position);
3186
- didChangePositions = true;
3187
- }
3188
- if (column >= 0 && column !== prevColumn) {
3189
- set$(ctx, `containerColumn${i}`, column);
3190
- }
3191
- if (span !== prevSpan) {
3192
- set$(ctx, `containerSpan${i}`, span);
3193
- }
3194
- if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
3195
- set$(ctx, `containerItemData${i}`, item);
3196
- }
3197
- }
4242
+ if (itemIndex !== void 0) {
4243
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4244
+ scrollAdjustPending,
4245
+ updateLayout: true
4246
+ }).didChangePosition || didChangePositions;
3198
4247
  }
3199
4248
  }
3200
4249
  }
3201
4250
  if (Platform2.OS === "web" && didChangePositions) {
3202
4251
  set$(ctx, "lastPositionUpdate", Date.now());
3203
4252
  }
3204
- if (!queuedInitialLayout && endBuffered !== null) {
3205
- if (checkAllSizesKnown(state)) {
3206
- setDidLayout(ctx);
3207
- }
4253
+ if (suppressInitialScrollSideEffects) {
4254
+ evaluateBootstrapInitialScroll(ctx);
4255
+ return;
4256
+ }
4257
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4258
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4259
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4260
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4261
+ setDidLayout(ctx);
4262
+ handleInitialScrollLayoutReady(ctx);
3208
4263
  }
3209
- if (viewabilityConfigCallbackPairs) {
3210
- updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
4264
+ if (viewabilityConfigCallbackPairs && startNoBuffer !== null && endNoBuffer !== null) {
4265
+ updateViewableItems(
4266
+ ctx.state,
4267
+ ctx,
4268
+ viewabilityConfigCallbackPairs,
4269
+ scrollLength,
4270
+ startNoBuffer,
4271
+ endNoBuffer,
4272
+ startBuffered != null ? startBuffered : startNoBuffer,
4273
+ endBuffered != null ? endBuffered : endNoBuffer
4274
+ );
3211
4275
  }
3212
4276
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
3213
4277
  const item = data[nextActiveStickyIndex];
@@ -3216,93 +4280,65 @@ function calculateItemsInView(ctx, params = {}) {
3216
4280
  }
3217
4281
  }
3218
4282
  });
3219
- if (!IsNewArchitecture && state.initialAnchor) {
3220
- ensureInitialAnchor(ctx);
3221
- }
3222
4283
  }
3223
4284
 
3224
- // src/core/checkActualChange.ts
3225
- function checkActualChange(state, dataProp, previousData) {
3226
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
3227
- return true;
3228
- }
4285
+ // src/core/doMaintainScrollAtEnd.ts
4286
+ function doMaintainScrollAtEnd(ctx) {
4287
+ const state = ctx.state;
3229
4288
  const {
3230
- idCache,
3231
- props: { keyExtractor }
4289
+ didContainersLayout,
4290
+ pendingNativeMVCPAdjust,
4291
+ refScroller,
4292
+ props: { maintainScrollAtEnd }
3232
4293
  } = state;
3233
- for (let i = 0; i < dataProp.length; i++) {
3234
- if (dataProp[i] !== previousData[i]) {
3235
- return true;
3236
- }
3237
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
3238
- return true;
3239
- }
4294
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4295
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4296
+ if (pendingNativeMVCPAdjust) {
4297
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4298
+ return false;
3240
4299
  }
3241
- return false;
3242
- }
3243
-
3244
- // src/utils/updateAveragesOnDataChange.ts
3245
- function updateAveragesOnDataChange(state, oldData, newData) {
3246
- var _a3;
3247
- const {
3248
- averageSizes,
3249
- sizesKnown,
3250
- indexByKey,
3251
- props: { itemsAreEqual, getItemType, keyExtractor }
3252
- } = state;
3253
- if (!itemsAreEqual || !oldData.length || !newData.length) {
3254
- for (const key in averageSizes) {
3255
- delete averageSizes[key];
4300
+ state.pendingMaintainScrollAtEnd = false;
4301
+ if (shouldMaintainScrollAtEnd) {
4302
+ const contentSize = getContentSize(ctx);
4303
+ if (contentSize < state.scrollLength) {
4304
+ state.scroll = 0;
3256
4305
  }
3257
- return;
3258
- }
3259
- const itemTypesToPreserve = {};
3260
- const newDataLength = newData.length;
3261
- const oldDataLength = oldData.length;
3262
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
3263
- const newItem = newData[newIndex];
3264
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
3265
- const oldIndex = indexByKey.get(id);
3266
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
3267
- const knownSize = sizesKnown.get(id);
3268
- if (knownSize === void 0) continue;
3269
- const oldItem = oldData[oldIndex];
3270
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
3271
- if (areEqual) {
3272
- const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
3273
- let typeData = itemTypesToPreserve[itemType];
3274
- if (!typeData) {
3275
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
3276
- }
3277
- typeData.totalSize += knownSize;
3278
- typeData.count++;
4306
+ requestAnimationFrame(() => {
4307
+ var _a3;
4308
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4309
+ state.maintainingScrollAtEnd = true;
4310
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4311
+ animated: maintainScrollAtEnd.animated
4312
+ });
4313
+ setTimeout(
4314
+ () => {
4315
+ state.maintainingScrollAtEnd = false;
4316
+ },
4317
+ maintainScrollAtEnd.animated ? 500 : 0
4318
+ );
3279
4319
  }
3280
- }
3281
- }
3282
- for (const key in averageSizes) {
3283
- delete averageSizes[key];
3284
- }
3285
- for (const itemType in itemTypesToPreserve) {
3286
- const { totalSize, count } = itemTypesToPreserve[itemType];
3287
- if (count > 0) {
3288
- averageSizes[itemType] = {
3289
- avg: totalSize / count,
3290
- num: count
3291
- };
3292
- }
4320
+ });
4321
+ return true;
3293
4322
  }
4323
+ return false;
3294
4324
  }
3295
4325
 
3296
4326
  // src/core/checkResetContainers.ts
3297
- function checkResetContainers(ctx, dataProp) {
4327
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
3298
4328
  const state = ctx.state;
3299
4329
  const { previousData } = state;
3300
- if (previousData) {
3301
- updateAveragesOnDataChange(state, previousData, dataProp);
3302
- }
3303
4330
  const { maintainScrollAtEnd } = state.props;
4331
+ if (didColumnsChange) {
4332
+ state.sizes.clear();
4333
+ state.sizesKnown.clear();
4334
+ for (const key in state.averageSizes) {
4335
+ delete state.averageSizes[key];
4336
+ }
4337
+ state.minIndexSizeChanged = 0;
4338
+ state.scrollForNextCalculateItemsInView = void 0;
4339
+ }
3304
4340
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
3305
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4341
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
3306
4342
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
3307
4343
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
3308
4344
  state.isEndReached = false;
@@ -3313,6 +4349,53 @@ function checkResetContainers(ctx, dataProp) {
3313
4349
  delete state.previousData;
3314
4350
  }
3315
4351
 
4352
+ // src/core/checkStructuralDataChange.ts
4353
+ function checkStructuralDataChange(state, dataProp, previousData) {
4354
+ var _a3;
4355
+ state.pendingDataComparison = void 0;
4356
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4357
+ return true;
4358
+ }
4359
+ const {
4360
+ idCache,
4361
+ props: { itemsAreEqual, keyExtractor }
4362
+ } = state;
4363
+ let byIndex;
4364
+ for (let i = 0; i < dataProp.length; i++) {
4365
+ if (dataProp[i] === previousData[i]) {
4366
+ continue;
4367
+ }
4368
+ if (!keyExtractor) {
4369
+ if (byIndex) {
4370
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4371
+ }
4372
+ return true;
4373
+ }
4374
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4375
+ const nextKey = keyExtractor(dataProp[i], i);
4376
+ if (previousKey !== nextKey) {
4377
+ if (byIndex) {
4378
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4379
+ }
4380
+ return true;
4381
+ }
4382
+ if (!itemsAreEqual) {
4383
+ if (byIndex) {
4384
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4385
+ }
4386
+ return true;
4387
+ }
4388
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4389
+ byIndex != null ? byIndex : byIndex = [];
4390
+ byIndex[i] = isEqual ? 1 : 2;
4391
+ if (!isEqual) {
4392
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4393
+ return true;
4394
+ }
4395
+ }
4396
+ return false;
4397
+ }
4398
+
3316
4399
  // src/core/doInitialAllocateContainers.ts
3317
4400
  function doInitialAllocateContainers(ctx) {
3318
4401
  var _a3, _b, _c;
@@ -3401,43 +4484,130 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3401
4484
  if (scrollLength > 0) {
3402
4485
  doInitialAllocateContainers(ctx);
3403
4486
  }
3404
- if (needsCalculate) {
3405
- calculateItemsInView(ctx, { doMVCP: true });
4487
+ if (needsCalculate) {
4488
+ calculateItemsInView(ctx, { doMVCP: true });
4489
+ }
4490
+ if (didChange || otherAxisSize !== prevOtherAxisSize) {
4491
+ set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
4492
+ }
4493
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
4494
+ doMaintainScrollAtEnd(ctx);
4495
+ }
4496
+ checkThresholds(ctx);
4497
+ if (state) {
4498
+ state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
4499
+ }
4500
+ if (IS_DEV && measuredLength === 0) {
4501
+ warnDevOnce(
4502
+ "height0",
4503
+ `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.`
4504
+ );
4505
+ }
4506
+ }
4507
+ setCanRender(true);
4508
+ }
4509
+
4510
+ // src/platform/flushSync.native.ts
4511
+ var flushSync = (fn) => {
4512
+ fn();
4513
+ };
4514
+
4515
+ // src/core/updateScroll.ts
4516
+ function updateScroll(ctx, newScroll, forceUpdate) {
4517
+ var _a3;
4518
+ const state = ctx.state;
4519
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
4520
+ const prevScroll = state.scroll;
4521
+ state.hasScrolled = true;
4522
+ state.lastBatchingAction = Date.now();
4523
+ const currentTime = Date.now();
4524
+ const adjust = scrollAdjustHandler.getAdjust();
4525
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
4526
+ if (adjustChanged) {
4527
+ scrollHistory.length = 0;
4528
+ }
4529
+ state.lastScrollAdjustForHistory = adjust;
4530
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
4531
+ if (!adjustChanged) {
4532
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
4533
+ }
4534
+ }
4535
+ if (scrollHistory.length > 5) {
4536
+ scrollHistory.shift();
4537
+ }
4538
+ if (ignoreScrollFromMVCP && !scrollingTo) {
4539
+ const { lt, gt } = ignoreScrollFromMVCP;
4540
+ if (lt && newScroll < lt || gt && newScroll > gt) {
4541
+ state.ignoreScrollFromMVCPIgnored = true;
4542
+ return;
3406
4543
  }
3407
- if (didChange || otherAxisSize !== prevOtherAxisSize) {
3408
- set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
4544
+ }
4545
+ state.scrollPrev = prevScroll;
4546
+ state.scrollPrevTime = state.scrollTime;
4547
+ state.scroll = newScroll;
4548
+ state.scrollTime = currentTime;
4549
+ const scrollDelta = Math.abs(newScroll - prevScroll);
4550
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
4551
+ const scrollLength = state.scrollLength;
4552
+ const lastCalculated = state.scrollLastCalculate;
4553
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
4554
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
4555
+ if (shouldUpdate) {
4556
+ state.scrollLastCalculate = state.scroll;
4557
+ state.ignoreScrollFromMVCPIgnored = false;
4558
+ state.lastScrollDelta = scrollDelta;
4559
+ const runCalculateItems = () => {
4560
+ var _a4;
4561
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
4562
+ checkThresholds(ctx);
4563
+ };
4564
+ if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
4565
+ flushSync(runCalculateItems);
4566
+ } else {
4567
+ runCalculateItems();
3409
4568
  }
3410
- if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
4569
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
4570
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
4571
+ state.pendingMaintainScrollAtEnd = false;
3411
4572
  doMaintainScrollAtEnd(ctx);
3412
4573
  }
3413
- checkThresholds(ctx);
3414
- if (state) {
3415
- state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
3416
- }
3417
- if (IS_DEV && measuredLength === 0) {
3418
- warnDevOnce(
3419
- "height0",
3420
- `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.`
3421
- );
3422
- }
4574
+ state.dataChangeNeedsScrollUpdate = false;
4575
+ state.lastScrollDelta = 0;
3423
4576
  }
3424
- setCanRender(true);
3425
4577
  }
3426
4578
 
3427
4579
  // src/core/onScroll.ts
3428
- var INITIAL_SCROLL_PROGRESS_EPSILON = 1;
3429
- function didObserveInitialScrollProgress(newScroll, watchdog) {
3430
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
3431
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
3432
- return nextDistance <= INITIAL_SCROLL_PROGRESS_EPSILON || nextDistance + INITIAL_SCROLL_PROGRESS_EPSILON < previousDistance;
4580
+ function trackInitialScrollNativeProgress(state, newScroll) {
4581
+ const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
4582
+ const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
4583
+ if (didInitialScrollProgress) {
4584
+ initialScrollWatchdog.clear(state);
4585
+ return;
4586
+ }
4587
+ if (initialNativeScrollWatchdog) {
4588
+ state.hasScrolled = false;
4589
+ initialScrollWatchdog.set(state, {
4590
+ startScroll: initialNativeScrollWatchdog.startScroll,
4591
+ targetOffset: initialNativeScrollWatchdog.targetOffset
4592
+ });
4593
+ }
4594
+ }
4595
+ function shouldDeferPublicOnScroll(state) {
4596
+ var _a3;
4597
+ return Platform2.OS === "web" && !!state.initialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" && !state.didFinishInitialScroll;
4598
+ }
4599
+ function cloneScrollEvent(event) {
4600
+ return {
4601
+ ...event,
4602
+ nativeEvent: {
4603
+ ...event.nativeEvent
4604
+ }
4605
+ };
3433
4606
  }
3434
4607
  function onScroll(ctx, event) {
3435
4608
  var _a3, _b, _c, _d;
3436
4609
  const state = ctx.state;
3437
- const {
3438
- scrollProcessingEnabled,
3439
- props: { onScroll: onScrollProp }
3440
- } = state;
4610
+ const { scrollProcessingEnabled } = state;
3441
4611
  if (scrollProcessingEnabled === false) {
3442
4612
  return;
3443
4613
  }
@@ -3468,20 +4638,19 @@ function onScroll(ctx, event) {
3468
4638
  }
3469
4639
  }
3470
4640
  state.scrollPending = newScroll;
3471
- const initialNativeScrollWatchdog = state.initialNativeScrollWatchdog;
3472
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && didObserveInitialScrollProgress(newScroll, initialNativeScrollWatchdog);
3473
- if (didInitialScrollProgress) {
3474
- state.initialNativeScrollWatchdog = void 0;
3475
- }
3476
4641
  updateScroll(ctx, newScroll, insetChanged);
3477
- if (initialNativeScrollWatchdog && !didInitialScrollProgress) {
3478
- state.hasScrolled = false;
3479
- state.initialNativeScrollWatchdog = initialNativeScrollWatchdog;
3480
- }
4642
+ trackInitialScrollNativeProgress(state, newScroll);
4643
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
3481
4644
  if (state.scrollingTo) {
3482
4645
  checkFinishedScroll(ctx);
3483
4646
  }
3484
- onScrollProp == null ? void 0 : onScrollProp(event);
4647
+ if (state.props.onScroll) {
4648
+ if (shouldDeferPublicOnScroll(state)) {
4649
+ state.deferredPublicOnScrollEvent = cloneScrollEvent(event);
4650
+ } else {
4651
+ state.props.onScroll(event);
4652
+ }
4653
+ }
3485
4654
  }
3486
4655
 
3487
4656
  // src/core/ScrollAdjustHandler.ts
@@ -3535,6 +4704,43 @@ var ScrollAdjustHandler = class {
3535
4704
  }
3536
4705
  };
3537
4706
 
4707
+ // src/core/updateAnchoredEndSpace.ts
4708
+ function maybeUpdateAnchoredEndSpace(ctx) {
4709
+ var _a3;
4710
+ const state = ctx.state;
4711
+ const anchoredEndSpace = state.props.anchoredEndSpace;
4712
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
4713
+ let nextSize = 0;
4714
+ if (anchoredEndSpace) {
4715
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
4716
+ const { data } = state.props;
4717
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
4718
+ let contentBelowAnchor = 0;
4719
+ const footerSize = ctx.values.get("footerSize") || 0;
4720
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4721
+ for (let index = anchorIndex; index < data.length; index++) {
4722
+ const itemKey = getId(state, index);
4723
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
4724
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
4725
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
4726
+ contentBelowAnchor += effectiveSize;
4727
+ }
4728
+ }
4729
+ contentBelowAnchor += footerSize + stylePaddingBottom;
4730
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4731
+ }
4732
+ }
4733
+ if (previousSize === nextSize) {
4734
+ return nextSize;
4735
+ }
4736
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
4737
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4738
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
4739
+ updateScroll(ctx, state.scroll, true);
4740
+ }
4741
+ return nextSize;
4742
+ }
4743
+
3538
4744
  // src/core/updateItemSize.ts
3539
4745
  function runOrScheduleMVCPRecalculate(ctx) {
3540
4746
  const state = ctx.state;
@@ -3618,6 +4824,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
3618
4824
  previous: size - diff,
3619
4825
  size
3620
4826
  });
4827
+ maybeUpdateAnchoredEndSpace(ctx);
3621
4828
  }
3622
4829
  if (minIndexSizeChanged !== void 0) {
3623
4830
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -3748,26 +4955,13 @@ function createColumnWrapperStyle(contentContainerStyle) {
3748
4955
  }
3749
4956
  }
3750
4957
 
3751
- // src/utils/hasActiveMVCPAnchorLock.ts
3752
- function hasActiveMVCPAnchorLock(state) {
3753
- const lock = state.mvcpAnchorLock;
3754
- if (!lock) {
3755
- return false;
3756
- }
3757
- if (Date.now() > lock.expiresAt) {
3758
- state.mvcpAnchorLock = void 0;
3759
- return false;
3760
- }
3761
- return true;
3762
- }
3763
-
3764
4958
  // src/utils/createImperativeHandle.ts
3765
4959
  function createImperativeHandle(ctx) {
3766
4960
  const state = ctx.state;
3767
4961
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
3768
4962
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
3769
4963
  let imperativeScrollToken = 0;
3770
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
4964
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
3771
4965
  const runWhenSettled = (token, run) => {
3772
4966
  const startedAt = Date.now();
3773
4967
  let stableFrames = 0;
@@ -3789,9 +4983,10 @@ function createImperativeHandle(ctx) {
3789
4983
  };
3790
4984
  requestAnimationFrame(check);
3791
4985
  };
3792
- const runScrollWithPromise = (run) => new Promise((resolve) => {
4986
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
3793
4987
  var _a3;
3794
4988
  const token = ++imperativeScrollToken;
4989
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
3795
4990
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
3796
4991
  state.pendingScrollResolve = resolve;
3797
4992
  const runNow = () => {
@@ -3806,11 +5001,12 @@ function createImperativeHandle(ctx) {
3806
5001
  resolve();
3807
5002
  }
3808
5003
  };
5004
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
3809
5005
  if (isSettlingAfterDataChange()) {
3810
- runWhenSettled(token, runNow);
3811
- return;
5006
+ runWhenSettled(token, execute);
5007
+ } else {
5008
+ execute();
3812
5009
  }
3813
- runNow();
3814
5010
  });
3815
5011
  const scrollIndexIntoView = (options) => {
3816
5012
  if (state) {
@@ -3867,10 +5063,13 @@ function createImperativeHandle(ctx) {
3867
5063
  },
3868
5064
  end: state.endNoBuffer,
3869
5065
  endBuffered: state.endBuffered,
3870
- isAtEnd: state.isAtEnd,
3871
- isAtStart: state.isAtStart,
5066
+ isAtEnd: peek$(ctx, "isAtEnd"),
5067
+ isAtStart: peek$(ctx, "isAtStart"),
3872
5068
  isEndReached: state.isEndReached,
5069
+ isNearEnd: peek$(ctx, "isNearEnd"),
5070
+ isNearStart: peek$(ctx, "isNearStart"),
3873
5071
  isStartReached: state.isStartReached,
5072
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
3874
5073
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
3875
5074
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3876
5075
  positionAtIndex: (index) => state.positions[index],
@@ -3917,10 +5116,15 @@ function createImperativeHandle(ctx) {
3917
5116
  }
3918
5117
  return false;
3919
5118
  }),
3920
- scrollToIndex: (params) => runScrollWithPromise(() => {
3921
- scrollToIndex(ctx, params);
3922
- return true;
3923
- }),
5119
+ scrollToIndex: (params) => runScrollWithPromise(
5120
+ () => {
5121
+ scrollToIndex(ctx, params);
5122
+ return true;
5123
+ },
5124
+ {
5125
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5126
+ }
5127
+ ),
3924
5128
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
3925
5129
  const data = state.props.data;
3926
5130
  const index = data.indexOf(item);
@@ -4016,7 +5220,7 @@ function getRenderedItem(ctx, key) {
4016
5220
  item,
4017
5221
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
4018
5222
  };
4019
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React2__default.createElement(renderItem, itemProps);
5223
+ renderedItem = React2__default.createElement(renderItem, itemProps);
4020
5224
  }
4021
5225
  return { index, item: data[index], renderedItem };
4022
5226
  }
@@ -4131,7 +5335,7 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
4131
5335
  }
4132
5336
 
4133
5337
  // src/components/LegendList.tsx
4134
- var LegendList = typedMemo2(
5338
+ var LegendList = typedMemo(
4135
5339
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
4136
5340
  typedForwardRef(function LegendList2(props, forwardedRef) {
4137
5341
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
@@ -4151,8 +5355,17 @@ var LegendList = typedMemo2(
4151
5355
  );
4152
5356
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
4153
5357
  var _a3, _b, _c, _d, _e, _f, _g, _h;
5358
+ const noopOnScroll = useCallback((_event) => {
5359
+ }, []);
5360
+ if (props.recycleItems === void 0) {
5361
+ warnDevOnce(
5362
+ "recycleItems-omitted",
5363
+ "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."
5364
+ );
5365
+ }
4154
5366
  const {
4155
5367
  alignItemsAtEnd = false,
5368
+ anchoredEndSpace,
4156
5369
  alwaysRender,
4157
5370
  columnWrapperStyle,
4158
5371
  contentContainerStyle: contentContainerStyleProp,
@@ -4174,6 +5387,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4174
5387
  itemsAreEqual,
4175
5388
  keyExtractor: keyExtractorProp,
4176
5389
  ListEmptyComponent,
5390
+ ListFooterComponent,
5391
+ ListFooterComponentStyle,
4177
5392
  ListHeaderComponent,
4178
5393
  maintainScrollAtEnd = false,
4179
5394
  maintainScrollAtEndThreshold = 0.1,
@@ -4210,14 +5425,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4210
5425
  useWindowScroll = false,
4211
5426
  viewabilityConfig,
4212
5427
  viewabilityConfigCallbackPairs,
4213
- waitForInitialLayout = true,
4214
5428
  ...rest
4215
5429
  } = props;
4216
5430
  const animatedPropsInternal = props.animatedPropsInternal;
4217
5431
  const positionComponentInternal = props.positionComponentInternal;
4218
5432
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
4219
5433
  const {
4220
- childrenMode,
4221
5434
  positionComponentInternal: _positionComponentInternal,
4222
5435
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
4223
5436
  ...restProps
@@ -4243,8 +5456,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4243
5456
  const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
4244
5457
  const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
4245
5458
  const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
4246
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
5459
+ const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
5460
+ const initialScrollProp = initialScrollAtEnd ? {
5461
+ index: Math.max(0, dataProp.length - 1),
5462
+ preserveForBottomPadding: true,
5463
+ viewOffset: -stylePaddingBottomState,
5464
+ viewPosition: 1
5465
+ } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
4247
5466
  index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
5467
+ preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
4248
5468
  viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
4249
5469
  viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
4250
5470
  } : {
@@ -4274,18 +5494,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4274
5494
  dataVersion,
4275
5495
  keyExtractor
4276
5496
  ]);
4277
- if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
4278
- warnDevOnce(
4279
- "stickyIndices",
4280
- "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
4281
- );
4282
- }
4283
- if (IS_DEV && useWindowScroll && renderScrollComponent) {
4284
- warnDevOnce(
4285
- "useWindowScrollRenderScrollComponent",
4286
- "useWindowScroll is not supported when renderScrollComponent is provided."
4287
- );
4288
- }
4289
5497
  const useWindowScrollResolved = Platform2.OS === "web" && !!useWindowScroll && !renderScrollComponent;
4290
5498
  const refState = useRef(void 0);
4291
5499
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -4294,7 +5502,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4294
5502
  if (!ctx.state) {
4295
5503
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
4296
5504
  ctx.state = {
4297
- activeStickyIndex: -1,
4298
5505
  averageSizes: {},
4299
5506
  columnSpans: [],
4300
5507
  columns: [],
@@ -4313,24 +5520,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4313
5520
  idCache: [],
4314
5521
  idsInView: [],
4315
5522
  indexByKey: /* @__PURE__ */ new Map(),
4316
- initialAnchor: !initialScrollUsesOffsetOnly && (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4317
- attempts: 0,
4318
- index: initialScrollProp.index,
4319
- settledTicks: 0,
4320
- viewOffset: (_f = initialScrollProp.viewOffset) != null ? _f : 0,
4321
- viewPosition: initialScrollProp.viewPosition
4322
- } : void 0,
4323
- initialNativeScrollWatchdog: void 0,
4324
5523
  initialScroll: initialScrollProp,
4325
- initialScrollLastDidFinish: false,
4326
- initialScrollLastTarget: initialScrollProp,
4327
- initialScrollLastTargetUsesOffset: initialScrollUsesOffsetOnly,
4328
- initialScrollPreviousDataLength: dataProp.length,
4329
- initialScrollRetryLastLength: void 0,
4330
- initialScrollRetryWindowUntil: 0,
4331
- initialScrollUsesOffset: initialScrollUsesOffsetOnly,
4332
- isAtEnd: false,
4333
- isAtStart: false,
5524
+ initialScrollSession: initialScrollProp ? {
5525
+ kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5526
+ previousDataLength: dataProp.length
5527
+ } : void 0,
4334
5528
  isEndReached: null,
4335
5529
  isFirst: true,
4336
5530
  isStartReached: null,
@@ -4341,6 +5535,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4341
5535
  minIndexSizeChanged: 0,
4342
5536
  nativeContentInset: void 0,
4343
5537
  nativeMarginTop: 0,
5538
+ pendingDataComparison: void 0,
4344
5539
  pendingNativeMVCPAdjust: void 0,
4345
5540
  positions: [],
4346
5541
  props: {},
@@ -4371,6 +5566,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4371
5566
  };
4372
5567
  const internalState = ctx.state;
4373
5568
  internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
5569
+ internalState.reprocessCurrentScroll = () => updateScroll(ctx, internalState.scroll, true);
4374
5570
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
4375
5571
  set$(ctx, "extraData", extraData);
4376
5572
  }
@@ -4378,22 +5574,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4378
5574
  }
4379
5575
  const state = refState.current;
4380
5576
  const isFirstLocal = state.isFirst;
4381
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
5577
+ const previousNumColumnsProp = state.props.numColumns;
5578
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
4382
5579
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
4383
5580
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
4384
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
5581
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
5582
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5583
+ clearPreservedInitialScrollTarget(state);
5584
+ }
4385
5585
  if (didDataChangeLocal) {
4386
5586
  state.dataChangeEpoch += 1;
4387
5587
  state.dataChangeNeedsScrollUpdate = true;
4388
5588
  state.didDataChange = true;
4389
5589
  state.previousData = state.props.data;
4390
5590
  }
4391
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
5591
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
5592
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
5593
+ const anchoredEndSpaceResolved = Platform2.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
4392
5594
  state.props = {
4393
5595
  alignItemsAtEnd,
4394
5596
  alwaysRender,
4395
5597
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
4396
5598
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
5599
+ anchoredEndSpace: anchoredEndSpaceResolved,
4397
5600
  animatedProps: animatedPropsInternal,
4398
5601
  contentInset,
4399
5602
  data: dataProp,
@@ -4462,311 +5665,87 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4462
5665
  true
4463
5666
  );
4464
5667
  }
4465
- const resolveInitialScrollOffset = useCallback((initialScroll) => {
4466
- var _a4;
4467
- if (state.initialScrollUsesOffset) {
4468
- return clampScrollOffset(ctx, (_a4 = initialScroll.contentOffset) != null ? _a4 : 0);
4469
- }
4470
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4471
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4472
- return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4473
- }, []);
4474
- const finishInitialScrollWithoutScroll = useCallback(() => {
4475
- refState.current.initialAnchor = void 0;
4476
- refState.current.initialScroll = void 0;
4477
- state.initialAnchor = void 0;
4478
- state.initialScroll = void 0;
4479
- state.initialScrollUsesOffset = false;
4480
- state.initialScrollLastTarget = void 0;
4481
- state.initialScrollLastTargetUsesOffset = false;
4482
- setInitialRenderState(ctx, { didInitialScroll: true });
4483
- }, []);
4484
- const setActiveInitialScrollTarget = useCallback(
4485
- (target, options) => {
4486
- var _a4;
4487
- const usesOffset = !!(options == null ? void 0 : options.usesOffset);
4488
- state.initialScrollUsesOffset = usesOffset;
4489
- state.initialScrollLastTarget = target;
4490
- state.initialScrollLastTargetUsesOffset = usesOffset;
4491
- refState.current.initialScroll = target;
4492
- state.initialScroll = target;
4493
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
4494
- state.didFinishInitialScroll = false;
4495
- }
4496
- if (!(options == null ? void 0 : options.syncAnchor)) {
4497
- return;
4498
- }
4499
- if (!IsNewArchitecture && !usesOffset && target.index !== void 0 && target.viewPosition !== void 0) {
4500
- state.initialAnchor = {
4501
- attempts: 0,
4502
- index: target.index,
4503
- settledTicks: 0,
4504
- viewOffset: (_a4 = target.viewOffset) != null ? _a4 : 0,
4505
- viewPosition: target.viewPosition
4506
- };
4507
- }
4508
- },
4509
- []
4510
- );
4511
- const shouldFinishInitialScrollAtOrigin = useCallback(
4512
- (initialScroll, offset) => {
4513
- var _a4, _b2, _c2;
4514
- if (offset !== 0 || initialScrollAtEnd) {
4515
- return false;
4516
- }
4517
- if (state.initialScrollUsesOffset) {
4518
- return Math.abs((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) <= 1;
4519
- }
4520
- return initialScroll.index === 0 && ((_b2 = initialScroll.viewPosition) != null ? _b2 : 0) === 0 && Math.abs((_c2 = initialScroll.viewOffset) != null ? _c2 : 0) <= 1;
4521
- },
4522
- [initialScrollAtEnd]
4523
- );
4524
- const shouldFinishEmptyInitialScrollAtEnd = useCallback(
4525
- (initialScroll, offset) => {
4526
- return dataProp.length === 0 && initialScrollAtEnd && offset === 0 && initialScroll.viewPosition === 1;
4527
- },
4528
- [dataProp.length, initialScrollAtEnd]
4529
- );
4530
- const shouldRearmFinishedEmptyInitialScrollAtEnd = useCallback(
4531
- (initialScroll) => {
4532
- var _a4;
4533
- return !!(state.didFinishInitialScroll && dataProp.length > 0 && initialScroll && !state.initialScrollUsesOffset && initialScroll.index === 0 && initialScroll.viewPosition === 1 && ((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) === 0);
4534
- },
4535
- [dataProp.length]
4536
- );
4537
5668
  const initialContentOffset = useMemo(() => {
4538
- var _a4;
4539
- let value;
4540
- const { initialScroll, initialAnchor } = refState.current;
4541
- if (initialScroll) {
4542
- if (!state.initialScrollUsesOffset && !IsNewArchitecture && initialScroll.index !== void 0 && (!initialAnchor || (initialAnchor == null ? void 0 : initialAnchor.index) !== initialScroll.index)) {
4543
- refState.current.initialAnchor = {
4544
- attempts: 0,
4545
- index: initialScroll.index,
4546
- settledTicks: 0,
4547
- viewOffset: (_a4 = initialScroll.viewOffset) != null ? _a4 : 0,
4548
- viewPosition: initialScroll.viewPosition
4549
- };
4550
- }
4551
- if (initialScroll.contentOffset !== void 0) {
4552
- value = initialScroll.contentOffset;
4553
- } else {
4554
- const clampedOffset = resolveInitialScrollOffset(initialScroll);
4555
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4556
- setActiveInitialScrollTarget(updatedInitialScroll, {
4557
- usesOffset: state.initialScrollUsesOffset
4558
- });
4559
- value = clampedOffset;
4560
- }
4561
- } else {
4562
- refState.current.initialAnchor = void 0;
4563
- value = 0;
4564
- }
4565
- const hasPendingDataDependentInitialScroll = !!initialScroll && dataProp.length === 0 && !shouldFinishInitialScrollAtOrigin(initialScroll, value) && !shouldFinishEmptyInitialScrollAtEnd(initialScroll, value);
4566
- if (!value && !hasPendingDataDependentInitialScroll) {
4567
- if (initialScroll && shouldFinishInitialScrollAtOrigin(initialScroll, value)) {
4568
- finishInitialScrollWithoutScroll();
4569
- } else {
4570
- setInitialRenderState(ctx, { didInitialScroll: true });
4571
- }
5669
+ var _a4, _b2;
5670
+ const initialScroll = state.initialScroll;
5671
+ if (!initialScroll) {
5672
+ return void 0;
4572
5673
  }
4573
- return value;
5674
+ const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);
5675
+ return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform2.OS === "web" ? void 0 : resolvedOffset;
5676
+ }, [usesBootstrapInitialScroll]);
5677
+ useLayoutEffect(() => {
5678
+ initializeInitialScrollOnMount(ctx, {
5679
+ dataLength: dataProp.length,
5680
+ hasFooterComponent: !!ListFooterComponent,
5681
+ initialContentOffset,
5682
+ initialScrollAtEnd,
5683
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
5684
+ });
4574
5685
  }, []);
4575
5686
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
4576
5687
  refState.current.lastBatchingAction = Date.now();
4577
5688
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
4578
- IS_DEV && !childrenMode && warnDevOnce(
4579
- "keyExtractor",
4580
- "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."
4581
- );
4582
5689
  refState.current.sizes.clear();
4583
5690
  refState.current.positions.length = 0;
4584
5691
  refState.current.totalSize = 0;
4585
5692
  set$(ctx, "totalSize", 0);
4586
5693
  }
4587
5694
  }
4588
- const doInitialScroll = useCallback((options) => {
4589
- var _a4, _b2;
4590
- const allowPostFinishRetry = !!(options == null ? void 0 : options.allowPostFinishRetry);
4591
- const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4592
- const initialScroll = (_a4 = state.initialScroll) != null ? _a4 : allowPostFinishRetry ? state.initialScrollLastTarget : void 0;
4593
- const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
4594
- const needsContainerLayoutForInitialScroll = !state.initialScrollUsesOffset;
4595
- const shouldWaitForInitialLayout = waitForInitialLayout && needsContainerLayoutForInitialScroll && !queuedInitialLayout && !allowPostFinishRetry && !isInitialScrollInProgress;
4596
- if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll && !allowPostFinishRetry || scrollingTo && !isInitialScrollInProgress) {
4597
- return;
4598
- }
4599
- if (allowPostFinishRetry && state.initialScrollLastTargetUsesOffset) {
4600
- return;
4601
- }
4602
- const didMoveAwayFromInitialTarget = allowPostFinishRetry && initialScroll.contentOffset !== void 0 && Math.abs(state.scroll - initialScroll.contentOffset) > 1;
4603
- if (didMoveAwayFromInitialTarget) {
4604
- state.initialScrollRetryWindowUntil = 0;
4605
- return;
4606
- }
4607
- const offset = resolveInitialScrollOffset(initialScroll);
4608
- const activeInitialTargetOffset = isInitialScrollInProgress ? (_b2 = scrollingTo.targetOffset) != null ? _b2 : scrollingTo.offset : void 0;
4609
- const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - offset) > 1;
4610
- const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - offset) > 1;
4611
- if (!didOffsetChange && (allowPostFinishRetry || isInitialScrollInProgress && !didActiveInitialTargetChange)) {
4612
- return;
4613
- }
4614
- if (didOffsetChange) {
4615
- const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
4616
- if (!state.initialScrollUsesOffset) {
4617
- state.initialScrollLastTarget = updatedInitialScroll;
4618
- state.initialScrollLastTargetUsesOffset = false;
4619
- if (state.initialScroll) {
4620
- refState.current.initialScroll = updatedInitialScroll;
4621
- state.initialScroll = updatedInitialScroll;
4622
- }
4623
- }
4624
- }
4625
- const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
4626
- const shouldForceNativeInitialScroll = state.initialScrollUsesOffset && hasMeasuredScrollLayout || allowPostFinishRetry || !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
4627
- performInitialScroll(ctx, {
4628
- forceScroll: shouldForceNativeInitialScroll,
4629
- initialScrollUsesOffset: state.initialScrollUsesOffset,
4630
- resolvedOffset: offset,
4631
- target: initialScroll
4632
- });
4633
- }, []);
4634
- useLayoutEffect(() => {
4635
- var _a4;
4636
- const previousDataLength = state.initialScrollPreviousDataLength;
4637
- state.initialScrollPreviousDataLength = dataProp.length;
4638
- if (previousDataLength !== 0 || dataProp.length === 0 || !state.initialScroll || !state.queuedInitialLayout) {
4639
- return;
4640
- }
4641
- if (initialScrollAtEnd) {
4642
- const lastIndex = Math.max(0, dataProp.length - 1);
4643
- const initialScroll = state.initialScroll;
4644
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
4645
- if (state.didFinishInitialScroll && !shouldRearm) {
4646
- return;
4647
- }
4648
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
4649
- return;
4650
- }
4651
- const updatedInitialScroll = {
4652
- contentOffset: void 0,
4653
- index: lastIndex,
4654
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
4655
- viewPosition: 1
4656
- };
4657
- setActiveInitialScrollTarget(updatedInitialScroll, {
4658
- resetDidFinish: shouldRearm,
4659
- syncAnchor: true
4660
- });
4661
- doInitialScroll();
4662
- return;
4663
- }
4664
- if (state.didFinishInitialScroll) {
4665
- return;
4666
- }
4667
- doInitialScroll();
4668
- }, [
4669
- dataProp.length,
4670
- doInitialScroll,
4671
- initialScrollAtEnd,
4672
- shouldRearmFinishedEmptyInitialScrollAtEnd,
4673
- stylePaddingBottomState
4674
- ]);
5695
+ if (IS_DEV) {
5696
+ useDevChecks(props);
5697
+ }
4675
5698
  useLayoutEffect(() => {
4676
- var _a4;
4677
- if (!initialScrollAtEnd) {
4678
- return;
4679
- }
4680
- const lastIndex = Math.max(0, dataProp.length - 1);
4681
- const initialScroll = state.initialScroll;
4682
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
4683
- if (state.didFinishInitialScroll && !shouldRearm) {
4684
- return;
4685
- }
4686
- if (shouldRearm) {
4687
- state.didFinishInitialScroll = false;
4688
- }
4689
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
4690
- return;
4691
- }
4692
- const updatedInitialScroll = {
4693
- contentOffset: void 0,
4694
- index: lastIndex,
4695
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
4696
- viewPosition: 1
4697
- };
4698
- setActiveInitialScrollTarget(updatedInitialScroll, {
4699
- resetDidFinish: shouldRearm,
4700
- syncAnchor: true
5699
+ handleInitialScrollDataChange(ctx, {
5700
+ dataLength: dataProp.length,
5701
+ didDataChange: didDataChangeLocal,
5702
+ initialScrollAtEnd,
5703
+ stylePaddingBottom: stylePaddingBottomState,
5704
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
4701
5705
  });
4702
- doInitialScroll();
5706
+ }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
5707
+ useLayoutEffect(() => {
5708
+ maybeUpdateAnchoredEndSpace(ctx);
4703
5709
  }, [
4704
- dataProp.length,
4705
- doInitialScroll,
4706
- initialScrollAtEnd,
4707
- shouldRearmFinishedEmptyInitialScrollAtEnd,
4708
- stylePaddingBottomState
5710
+ ctx,
5711
+ dataProp,
5712
+ dataVersion,
5713
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
5714
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
5715
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
5716
+ numColumnsProp
4709
5717
  ]);
4710
5718
  const onLayoutFooter = useCallback(
4711
5719
  (layout) => {
4712
- var _a4;
4713
- if (!initialScrollAtEnd) {
5720
+ if (!usesBootstrapInitialScroll) {
4714
5721
  return;
4715
5722
  }
4716
- const { initialScroll } = state;
4717
- if (!initialScroll) {
4718
- return;
5723
+ handleBootstrapInitialScrollFooterLayout(ctx, {
5724
+ dataLength: dataProp.length,
5725
+ footerSize: layout[horizontal ? "width" : "height"],
5726
+ initialScrollAtEnd,
5727
+ stylePaddingBottom: stylePaddingBottomState
5728
+ });
5729
+ },
5730
+ [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5731
+ );
5732
+ const onLayoutChange = useCallback(
5733
+ (layout, fromLayoutEffect) => {
5734
+ const previousScrollLength = state.scrollLength;
5735
+ const previousOtherAxisSize = state.otherAxisSize;
5736
+ handleLayout(ctx, layout, setCanRender);
5737
+ maybeUpdateAnchoredEndSpace(ctx);
5738
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
5739
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
5740
+ handleBootstrapInitialScrollLayoutChange(ctx);
4719
5741
  }
4720
- const lastIndex = Math.max(0, dataProp.length - 1);
4721
- if (initialScroll.index !== lastIndex || initialScroll.viewPosition !== 1) {
5742
+ if (usesBootstrapInitialScroll) {
4722
5743
  return;
4723
5744
  }
4724
- const footerSize = layout[horizontal ? "width" : "height"];
4725
- const viewOffset = -stylePaddingBottomState - footerSize;
4726
- if (initialScroll.viewOffset !== viewOffset) {
4727
- const previousTargetOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(initialScroll);
4728
- const didMoveAwayFromFinishedInitialTarget = state.didFinishInitialScroll && Math.abs(state.scroll - previousTargetOffset) > 1;
4729
- if (didMoveAwayFromFinishedInitialTarget) {
4730
- return;
4731
- }
4732
- const updatedInitialScroll = { ...initialScroll, viewOffset };
4733
- setActiveInitialScrollTarget(updatedInitialScroll, {
4734
- resetDidFinish: true
4735
- });
4736
- doInitialScroll();
4737
- }
5745
+ advanceCurrentInitialScrollSession(ctx);
4738
5746
  },
4739
- [
4740
- dataProp.length,
4741
- doInitialScroll,
4742
- horizontal,
4743
- initialScrollAtEnd,
4744
- resolveInitialScrollOffset,
4745
- stylePaddingBottomState
4746
- ]
5747
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
4747
5748
  );
4748
- const onLayoutChange = useCallback((layout) => {
4749
- var _a4;
4750
- handleLayout(ctx, layout, setCanRender);
4751
- const SCROLL_LENGTH_RETRY_WINDOW_MS = 600;
4752
- const now = Date.now();
4753
- const didFinishInitialScroll = !!state.didFinishInitialScroll;
4754
- if (didFinishInitialScroll && !state.initialScrollLastDidFinish) {
4755
- state.initialScrollRetryWindowUntil = now + SCROLL_LENGTH_RETRY_WINDOW_MS;
4756
- }
4757
- state.initialScrollLastDidFinish = didFinishInitialScroll;
4758
- const previousScrollLength = state.initialScrollRetryLastLength;
4759
- const currentScrollLength = state.scrollLength;
4760
- const didScrollLengthChange = previousScrollLength === void 0 || Math.abs(currentScrollLength - previousScrollLength) > 1;
4761
- if (didScrollLengthChange) {
4762
- state.initialScrollRetryLastLength = currentScrollLength;
4763
- }
4764
- if (didFinishInitialScroll && didScrollLengthChange && now <= state.initialScrollRetryWindowUntil && !state.initialScrollLastTargetUsesOffset && ((_a4 = state.initialScrollLastTarget) == null ? void 0 : _a4.index) !== void 0) {
4765
- doInitialScroll({ allowPostFinishRetry: true });
4766
- return;
4767
- }
4768
- doInitialScroll();
4769
- }, []);
4770
5749
  const { onLayout } = useOnLayoutSync({
4771
5750
  onLayoutChange,
4772
5751
  onLayoutProp,
@@ -4778,6 +5757,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4778
5757
  updateSnapToOffsets(ctx);
4779
5758
  }
4780
5759
  }, [snapToIndices]);
5760
+ useLayoutEffect(
5761
+ () => initializeStateVars(true),
5762
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5763
+ );
4781
5764
  useLayoutEffect(() => {
4782
5765
  const {
4783
5766
  didColumnsChange,
@@ -4787,7 +5770,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4787
5770
  } = state;
4788
5771
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
4789
5772
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
4790
- checkResetContainers(ctx, data);
5773
+ checkResetContainers(ctx, data, { didColumnsChange });
5774
+ }
5775
+ if (didDataChange) {
5776
+ state.pendingDataComparison = void 0;
4791
5777
  }
4792
5778
  state.didColumnsChange = false;
4793
5779
  state.didDataChange = false;
@@ -4802,10 +5788,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4802
5788
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
4803
5789
  }
4804
5790
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
4805
- useLayoutEffect(
4806
- () => initializeStateVars(true),
4807
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
4808
- );
4809
5791
  useEffect(() => {
4810
5792
  if (!onMetricsChange) {
4811
5793
  return;
@@ -4838,15 +5820,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4838
5820
  state.viewabilityConfigCallbackPairs = viewability;
4839
5821
  state.enableScrollForNextCalculateItemsInView = !viewability;
4840
5822
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
4841
- if (!IsNewArchitecture) {
4842
- useInit(() => {
5823
+ useInit(() => {
5824
+ if (!IsNewArchitecture) {
4843
5825
  doInitialAllocateContainers(ctx);
4844
- });
4845
- }
5826
+ }
5827
+ });
4846
5828
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
4847
- if (Platform2.OS === "web") {
4848
- useEffect(doInitialScroll, []);
4849
- }
5829
+ useEffect(() => {
5830
+ if (Platform2.OS !== "web" || usesBootstrapInitialScroll) {
5831
+ return;
5832
+ }
5833
+ advanceCurrentInitialScrollSession(ctx);
5834
+ }, [ctx, usesBootstrapInitialScroll]);
4850
5835
  const fns = useMemo(
4851
5836
  () => ({
4852
5837
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -4875,6 +5860,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4875
5860
  horizontal,
4876
5861
  initialContentOffset,
4877
5862
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
5863
+ ListFooterComponent,
5864
+ ListFooterComponentStyle,
4878
5865
  ListHeaderComponent,
4879
5866
  onLayout,
4880
5867
  onLayoutFooter,
@@ -4899,8 +5886,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4899
5886
  stickyHeaderIndices,
4900
5887
  style,
4901
5888
  updateItemSize: fns.updateItemSize,
4902
- useWindowScroll: useWindowScrollResolved,
4903
- waitForInitialLayout
5889
+ useWindowScroll: useWindowScrollResolved
4904
5890
  }
4905
5891
  ), IS_DEV && ENABLE_DEBUG_VIEW);
4906
5892
  });
@@ -4909,9 +5895,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4909
5895
  var LegendListRuntime = LegendList;
4910
5896
  var internal = {
4911
5897
  getComponent,
5898
+ getStickyPushLimit,
4912
5899
  IsNewArchitecture,
4913
5900
  POSITION_OUT_OF_VIEW,
4914
5901
  peek$,
5902
+ typedForwardRef,
5903
+ typedMemo,
4915
5904
  useArr$,
4916
5905
  useCombinedRef,
4917
5906
  useStateContext
@@ -4921,4 +5910,4 @@ var internal = {
4921
5910
  var LegendList3 = LegendListRuntime;
4922
5911
  var internal2 = internal;
4923
5912
 
4924
- export { LegendList3 as LegendList, internal2 as internal, typedForwardRef, typedMemo2 as typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
5913
+ export { LegendList3 as LegendList, internal2 as internal, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };