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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.native.mjs CHANGED
@@ -1,44 +1,13 @@
1
1
  import * as React2 from 'react';
2
2
  import React2__default, { useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useLayoutEffect, useImperativeHandle, useContext } from 'react';
3
3
  import * as ReactNative from 'react-native';
4
- import { Animated, View as View$1, Text as Text$1, Platform, StyleSheet as StyleSheet$1, RefreshControl, Dimensions } from 'react-native';
4
+ import { Animated, View as View$1, Text as Text$1, Platform as Platform$1, StyleSheet as StyleSheet$1, RefreshControl, Dimensions } from 'react-native';
5
5
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
6
6
 
7
7
  // src/components/LegendList.tsx
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,
@@ -363,12 +327,7 @@ var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
363
327
  const position$ = useValue$(`containerPosition${id}`, {
364
328
  getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
365
329
  });
366
- let position;
367
- if (Platform.OS === "ios" || Platform.OS === "android") {
368
- position = horizontal ? { transform: [{ translateX: position$ }] } : { transform: [{ translateY: position$ }] };
369
- } else {
370
- position = horizontal ? { left: position$ } : { top: position$ };
371
- }
330
+ const position = horizontal ? { left: position$ } : { top: position$ };
372
331
  return /* @__PURE__ */ React2.createElement(Animated.View, { ref: refView, style: [style, position], ...rest });
373
332
  });
374
333
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
@@ -382,26 +341,50 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
382
341
  children,
383
342
  ...rest
384
343
  }) {
385
- const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0] = useArr$([
344
+ const ctx = useStateContext();
345
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0, itemKey, _totalSize = 0] = useArr$([
386
346
  `containerPosition${id}`,
387
347
  "headerSize",
388
- "stylePaddingTop"
348
+ "stylePaddingTop",
349
+ `containerItemKey${id}`,
350
+ "totalSize"
389
351
  ]);
390
- const transform = React2.useMemo(() => {
352
+ const pushLimit = React2.useMemo(
353
+ () => getStickyPushLimit(ctx.state, index, itemKey),
354
+ [ctx.state, index, itemKey, _totalSize]
355
+ );
356
+ const stickyPosition = React2.useMemo(() => {
391
357
  var _a3;
392
358
  if (animatedScrollY) {
393
359
  const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
394
360
  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
- });
401
- return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
361
+ let nextStickyPosition;
362
+ if (pushLimit !== void 0) {
363
+ if (pushLimit <= position) {
364
+ nextStickyPosition = pushLimit;
365
+ } else {
366
+ nextStickyPosition = animatedScrollY.interpolate({
367
+ extrapolateLeft: "clamp",
368
+ extrapolateRight: "clamp",
369
+ inputRange: [stickyStart, stickyStart + (pushLimit - position)],
370
+ outputRange: [position, pushLimit]
371
+ });
372
+ }
373
+ } else {
374
+ nextStickyPosition = animatedScrollY.interpolate({
375
+ extrapolateLeft: "clamp",
376
+ extrapolateRight: "extend",
377
+ inputRange: [stickyStart, stickyStart + 5e3],
378
+ outputRange: [position, position + 5e3]
379
+ });
380
+ }
381
+ return nextStickyPosition;
402
382
  }
403
- }, [animatedScrollY, headerSize, horizontal, position, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
404
- const viewStyle = React2.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
383
+ }, [animatedScrollY, headerSize, position, pushLimit, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
384
+ const viewStyle = React2.useMemo(
385
+ () => [style, { zIndex: index + 1e3 }, horizontal ? { left: stickyPosition } : { top: stickyPosition }],
386
+ [horizontal, index, stickyPosition, style]
387
+ );
405
388
  const renderStickyHeaderBackdrop = React2.useMemo(() => {
406
389
  if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
407
390
  return null;
@@ -425,6 +408,52 @@ function useInit(cb) {
425
408
  useState(() => cb());
426
409
  }
427
410
 
411
+ // src/utils/helpers.ts
412
+ function isFunction(obj) {
413
+ return typeof obj === "function";
414
+ }
415
+ function isArray(obj) {
416
+ return Array.isArray(obj);
417
+ }
418
+ var warned = /* @__PURE__ */ new Set();
419
+ function warnDevOnce(id, text) {
420
+ if (IS_DEV && !warned.has(id)) {
421
+ warned.add(id);
422
+ console.warn(`[legend-list] ${text}`);
423
+ }
424
+ }
425
+ function roundSize(size) {
426
+ return Math.floor(size * 8) / 8;
427
+ }
428
+ function isNullOrUndefined(value) {
429
+ return value === null || value === void 0;
430
+ }
431
+ function comparatorDefault(a, b) {
432
+ return a - b;
433
+ }
434
+ function getPadding(s, type) {
435
+ var _a3, _b, _c;
436
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
437
+ }
438
+ function extractPadding(style, contentContainerStyle, type) {
439
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
440
+ }
441
+ function findContainerId(ctx, key) {
442
+ var _a3, _b;
443
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
444
+ if (directMatch !== void 0) {
445
+ return directMatch;
446
+ }
447
+ const numContainers = peek$(ctx, "numContainers");
448
+ for (let i = 0; i < numContainers; i++) {
449
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
450
+ if (itemKey === key) {
451
+ return i;
452
+ }
453
+ }
454
+ return -1;
455
+ }
456
+
428
457
  // src/state/ContextContainer.ts
429
458
  var ContextContainer = createContext(null);
430
459
  function useContextContainer() {
@@ -592,11 +621,11 @@ function useOnLayoutSync({
592
621
  const { layout } = event.nativeEvent;
593
622
  if (layout.height !== ((_a3 = lastLayoutRef.current) == null ? void 0 : _a3.height) || layout.width !== ((_b = lastLayoutRef.current) == null ? void 0 : _b.width)) {
594
623
  onLayoutChange(layout, false);
595
- onLayoutProp == null ? void 0 : onLayoutProp(event);
596
624
  lastLayoutRef.current = layout;
597
625
  }
626
+ onLayoutProp == null ? void 0 : onLayoutProp(event);
598
627
  },
599
- [onLayoutChange]
628
+ [onLayoutChange, onLayoutProp]
600
629
  );
601
630
  if (IsNewArchitecture) {
602
631
  useLayoutEffect(() => {
@@ -611,10 +640,8 @@ function useOnLayoutSync({
611
640
  }
612
641
  return { onLayout };
613
642
  }
614
- var Platform2 = Platform;
615
- var PlatformAdjustBreaksScroll = Platform2.OS === "android";
616
- var typedForwardRef = React2.forwardRef;
617
- var typedMemo2 = React2.memo;
643
+ var Platform = Platform$1;
644
+ var PlatformAdjustBreaksScroll = Platform.OS === "android";
618
645
 
619
646
  // src/utils/isInMVCPActiveMode.native.ts
620
647
  function isInMVCPActiveMode(state) {
@@ -622,7 +649,7 @@ function isInMVCPActiveMode(state) {
622
649
  }
623
650
 
624
651
  // src/components/Container.tsx
625
- var Container = typedMemo2(function Container2({
652
+ var Container = typedMemo(function Container2({
626
653
  id,
627
654
  recycleItems,
628
655
  horizontal,
@@ -666,17 +693,20 @@ var Container = typedMemo2(function Container2({
666
693
  const { columnGap, rowGap, gap } = columnWrapperStyle;
667
694
  if (horizontal) {
668
695
  paddingStyles = {
696
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
669
697
  paddingRight: columnGap || gap || void 0,
670
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
698
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
671
699
  };
672
700
  } else {
673
701
  paddingStyles = {
674
702
  paddingBottom: rowGap || gap || void 0,
675
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
703
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
704
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
676
705
  };
677
706
  }
678
707
  }
679
708
  return horizontal ? {
709
+ boxSizing: paddingStyles ? "border-box" : void 0,
680
710
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
681
711
  height: otherAxisSize,
682
712
  left: 0,
@@ -684,6 +714,7 @@ var Container = typedMemo2(function Container2({
684
714
  top: otherAxisPos,
685
715
  ...paddingStyles || {}
686
716
  } : {
717
+ boxSizing: paddingStyles ? "border-box" : void 0,
687
718
  left: otherAxisPos,
688
719
  position: "absolute",
689
720
  right: numColumns > 1 ? null : 0,
@@ -731,7 +762,7 @@ var Container = typedMemo2(function Container2({
731
762
  updateItemSizeFn(currentItemKey, layout);
732
763
  itemLayoutRef.current.didLayout = true;
733
764
  };
734
- const shouldDeferWebShrinkLayoutUpdate = Platform2.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
765
+ const shouldDeferWebShrinkLayoutUpdate = Platform.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
735
766
  if (shouldDeferWebShrinkLayoutUpdate) {
736
767
  const token = pendingShrinkToken + 1;
737
768
  itemLayoutRef.current.pendingShrinkToken = token;
@@ -817,16 +848,9 @@ var Containers = typedMemo(function Containers2({
817
848
  const ctx = useStateContext();
818
849
  const columnWrapperStyle = ctx.columnWrapperStyle;
819
850
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
820
- const animSize = useValue$("totalSize", {
821
- // Use a microtask if increasing the size significantly, otherwise use a timeout
822
- // If this is the initial scroll, we don't want to delay because we want to update the size immediately
823
- delay: (value, prevValue) => {
824
- var _a3;
825
- return !((_a3 = ctx.state) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
826
- }
827
- });
851
+ const animSize = useValue$("totalSize");
852
+ const otherAxisSize = useValue$("otherAxisSize");
828
853
  const animOpacity = useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 });
829
- const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
830
854
  const containers = [];
831
855
  for (let i = 0; i < numContainers; i++) {
832
856
  containers.push(
@@ -870,17 +894,20 @@ var Containers = typedMemo(function Containers2({
870
894
  });
871
895
  var ListComponentScrollView = Animated.ScrollView;
872
896
  function ScrollAdjust() {
897
+ var _a3;
898
+ const ctx = useStateContext();
873
899
  const bias = 1e7;
874
900
  const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
875
901
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0) + bias;
902
+ const horizontal = !!((_a3 = ctx.state) == null ? void 0 : _a3.props.horizontal);
876
903
  return /* @__PURE__ */ React2.createElement(
877
904
  View$1,
878
905
  {
879
906
  style: {
880
907
  height: 0,
881
- left: 0,
908
+ left: horizontal ? scrollOffset : 0,
882
909
  position: "absolute",
883
- top: scrollOffset,
910
+ top: horizontal ? 0 : scrollOffset,
884
911
  width: 0
885
912
  }
886
913
  }
@@ -890,14 +917,25 @@ function SnapWrapper({ ScrollComponent, ...props }) {
890
917
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
891
918
  return /* @__PURE__ */ React2.createElement(ScrollComponent, { ...props, snapToOffsets });
892
919
  }
920
+ function WebAnchoredEndSpace({ horizontal }) {
921
+ const ctx = useStateContext();
922
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
923
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
924
+ if (!shouldRenderAnchoredEndSpace) {
925
+ return null;
926
+ }
927
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
928
+ return /* @__PURE__ */ React2.createElement("div", { style }, null);
929
+ }
893
930
  var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
894
- const ref = refView != null ? refView : useRef(null);
931
+ const localRef = useRef(null);
932
+ const ref = refView != null ? refView : localRef;
895
933
  const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
896
934
  return /* @__PURE__ */ React2.createElement(View$1, { ...rest, onLayout, ref });
897
935
  };
898
936
 
899
937
  // src/components/ListComponent.tsx
900
- var ListComponent = typedMemo2(function ListComponent2({
938
+ var ListComponent = typedMemo(function ListComponent2({
901
939
  canRender,
902
940
  style,
903
941
  contentContainerStyle,
@@ -927,12 +965,14 @@ var ListComponent = typedMemo2(function ListComponent2({
927
965
  }) {
928
966
  const ctx = useStateContext();
929
967
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
930
- const ScrollComponent = renderScrollComponent ? useMemo(
931
- () => React2.forwardRef(
968
+ const ScrollComponent = useMemo(() => {
969
+ if (!renderScrollComponent) {
970
+ return ListComponentScrollView;
971
+ }
972
+ return React2.forwardRef(
932
973
  (props, ref) => renderScrollComponent({ ...props, ref })
933
- ),
934
- [renderScrollComponent]
935
- ) : ListComponentScrollView;
974
+ );
975
+ }, [renderScrollComponent]);
936
976
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
937
977
  useLayoutEffect(() => {
938
978
  if (!ListHeaderComponent) {
@@ -992,183 +1032,90 @@ var ListComponent = typedMemo2(function ListComponent2({
992
1032
  }
993
1033
  ),
994
1034
  ListFooterComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1035
+ Platform.OS === "web" && /* @__PURE__ */ React2.createElement(WebAnchoredEndSpace, { horizontal }),
995
1036
  IS_DEV && ENABLE_DEVMODE
996
1037
  );
997
1038
  });
998
-
999
- // src/core/calculateOffsetForIndex.ts
1000
- function calculateOffsetForIndex(ctx, index) {
1001
- const state = ctx.state;
1002
- return index !== void 0 ? state.positions[index] || 0 : 0;
1039
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1040
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1041
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1042
+ function useDevChecksImpl(props) {
1043
+ const ctx = useStateContext();
1044
+ const { childrenMode, keyExtractor, renderScrollComponent, stickyHeaderIndices, stickyIndices, useWindowScroll } = props;
1045
+ useEffect(() => {
1046
+ if (stickyIndices && !stickyHeaderIndices) {
1047
+ warnDevOnce(
1048
+ "stickyIndices",
1049
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
1050
+ );
1051
+ }
1052
+ }, [stickyHeaderIndices, stickyIndices]);
1053
+ useEffect(() => {
1054
+ if (useWindowScroll && renderScrollComponent) {
1055
+ warnDevOnce(
1056
+ "useWindowScrollRenderScrollComponent",
1057
+ "useWindowScroll is not supported when renderScrollComponent is provided."
1058
+ );
1059
+ }
1060
+ }, [renderScrollComponent, useWindowScroll]);
1061
+ useEffect(() => {
1062
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1063
+ warnDevOnce(
1064
+ "keyExtractor",
1065
+ "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."
1066
+ );
1067
+ }
1068
+ }, [childrenMode, ctx, keyExtractor]);
1069
+ useEffect(() => {
1070
+ const state = ctx.state;
1071
+ const dataLength = state.props.data.length;
1072
+ const useWindowScrollResolved = state.props.useWindowScroll;
1073
+ if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1074
+ return;
1075
+ }
1076
+ const warnIfUnboundedOuterSize = () => {
1077
+ const readyToRender = peek$(ctx, "readyToRender");
1078
+ const numContainers = peek$(ctx, "numContainers") || 0;
1079
+ const totalSize = peek$(ctx, "totalSize") || 0;
1080
+ const scrollLength = ctx.state.scrollLength || 0;
1081
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1082
+ return;
1083
+ }
1084
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1085
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1086
+ if (rendersAlmostEverything && viewportMatchesContent) {
1087
+ warnDevOnce(
1088
+ "webUnboundedOuterSize",
1089
+ "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."
1090
+ );
1091
+ }
1092
+ };
1093
+ warnIfUnboundedOuterSize();
1094
+ const unsubscribe = [
1095
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1096
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1097
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1098
+ ];
1099
+ return () => {
1100
+ for (const unsub of unsubscribe) {
1101
+ unsub();
1102
+ }
1103
+ };
1104
+ }, [ctx]);
1003
1105
  }
1004
-
1005
- // src/core/getTopOffsetAdjustment.ts
1006
- function getTopOffsetAdjustment(ctx) {
1007
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1106
+ function useDevChecksNoop(_props) {
1008
1107
  }
1108
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1009
1109
 
1010
- // src/utils/getId.ts
1011
- function getId(state, index) {
1012
- const { data, keyExtractor } = state.props;
1013
- if (!data) {
1014
- return "";
1015
- }
1016
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1017
- const id = ret;
1018
- state.idCache[index] = id;
1019
- return id;
1020
- }
1021
-
1022
- // src/core/addTotalSize.ts
1023
- function addTotalSize(ctx, key, add) {
1024
- const state = ctx.state;
1025
- const prevTotalSize = state.totalSize;
1026
- let totalSize = state.totalSize;
1027
- if (key === null) {
1028
- totalSize = add;
1029
- if (state.timeoutSetPaddingTop) {
1030
- clearTimeout(state.timeoutSetPaddingTop);
1031
- state.timeoutSetPaddingTop = void 0;
1032
- }
1033
- } else {
1034
- totalSize += add;
1035
- }
1036
- if (prevTotalSize !== totalSize) {
1037
- if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1038
- state.pendingTotalSize = totalSize;
1039
- } else {
1040
- state.pendingTotalSize = void 0;
1041
- state.totalSize = totalSize;
1042
- set$(ctx, "totalSize", totalSize);
1043
- }
1044
- }
1045
- }
1046
-
1047
- // src/core/setSize.ts
1048
- function setSize(ctx, itemKey, size) {
1049
- const state = ctx.state;
1050
- const { sizes } = state;
1051
- const previousSize = sizes.get(itemKey);
1052
- const diff = previousSize !== void 0 ? size - previousSize : size;
1053
- if (diff !== 0) {
1054
- addTotalSize(ctx, itemKey, diff);
1055
- }
1056
- sizes.set(itemKey, size);
1057
- }
1058
-
1059
- // src/utils/getItemSize.ts
1060
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1061
- var _a3, _b;
1062
- const state = ctx.state;
1063
- const {
1064
- sizesKnown,
1065
- sizes,
1066
- averageSizes,
1067
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1068
- scrollingTo
1069
- } = state;
1070
- const sizeKnown = sizesKnown.get(key);
1071
- if (sizeKnown !== void 0) {
1072
- return sizeKnown;
1073
- }
1074
- let size;
1075
- if (preferCachedSize) {
1076
- const cachedSize = sizes.get(key);
1077
- if (cachedSize !== void 0) {
1078
- return cachedSize;
1079
- }
1080
- }
1081
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1082
- if (getFixedItemSize) {
1083
- size = getFixedItemSize(data, index, itemType);
1084
- if (size !== void 0) {
1085
- sizesKnown.set(key, size);
1086
- }
1087
- }
1088
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1089
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1090
- if (averageSizeForType !== void 0) {
1091
- size = roundSize(averageSizeForType);
1092
- }
1093
- }
1094
- if (size === void 0) {
1095
- size = sizes.get(key);
1096
- if (size !== void 0) {
1097
- return size;
1098
- }
1099
- }
1100
- if (size === void 0) {
1101
- size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1102
- }
1103
- setSize(ctx, key, size);
1104
- return size;
1105
- }
1106
- function getItemSizeAtIndex(ctx, index) {
1107
- if (index === void 0 || index < 0) {
1108
- return void 0;
1109
- }
1110
- const targetId = getId(ctx.state, index);
1111
- return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1112
- }
1113
-
1114
- // src/core/calculateOffsetWithOffsetPosition.ts
1115
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1116
- var _a3;
1117
- const state = ctx.state;
1118
- const { index, viewOffset, viewPosition } = params;
1119
- let offset = offsetParam;
1120
- if (viewOffset) {
1121
- offset -= viewOffset;
1122
- }
1123
- if (index !== void 0) {
1124
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1125
- if (topOffsetAdjustment) {
1126
- offset += topOffsetAdjustment;
1127
- }
1128
- }
1129
- if (viewPosition !== void 0 && index !== void 0) {
1130
- const dataLength = state.props.data.length;
1131
- if (dataLength === 0) {
1132
- return offset;
1133
- }
1134
- const isOutOfBounds = index < 0 || index >= dataLength;
1135
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1136
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1137
- const trailingInset = getContentInsetEnd(state);
1138
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1139
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1140
- const footerSize = peek$(ctx, "footerSize") || 0;
1141
- offset += footerSize;
1142
- }
1143
- }
1144
- return offset;
1145
- }
1146
-
1147
- // src/core/clampScrollOffset.ts
1148
- function clampScrollOffset(ctx, offset, scrollTarget) {
1149
- const state = ctx.state;
1150
- const contentSize = getContentSize(ctx);
1151
- let clampedOffset = offset;
1152
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1153
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1154
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1155
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1156
- const maxOffset = baseMaxOffset + extraEndOffset;
1157
- clampedOffset = Math.min(offset, maxOffset);
1158
- }
1159
- clampedOffset = Math.max(0, clampedOffset);
1160
- return clampedOffset;
1161
- }
1162
-
1163
- // src/core/deferredPublicOnScroll.ts
1164
- function withResolvedContentOffset(state, event, resolvedOffset) {
1165
- return {
1166
- ...event,
1167
- nativeEvent: {
1168
- ...event.nativeEvent,
1169
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1170
- }
1171
- };
1110
+ // src/core/deferredPublicOnScroll.ts
1111
+ function withResolvedContentOffset(state, event, resolvedOffset) {
1112
+ return {
1113
+ ...event,
1114
+ nativeEvent: {
1115
+ ...event.nativeEvent,
1116
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1117
+ }
1118
+ };
1172
1119
  }
1173
1120
  function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
1174
1121
  var _a3, _b, _c, _d;
@@ -1247,63 +1194,489 @@ var initialScrollCompletion = {
1247
1194
  if (!state.initialScrollSession) {
1248
1195
  return;
1249
1196
  }
1250
- const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1251
- completion.didDispatchNativeScroll = void 0;
1252
- completion.didRetrySilentInitialScroll = void 0;
1197
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1198
+ completion.didDispatchNativeScroll = void 0;
1199
+ completion.didRetrySilentInitialScroll = void 0;
1200
+ }
1201
+ };
1202
+ var initialScrollWatchdog = {
1203
+ clear(state) {
1204
+ initialScrollWatchdog.set(state, void 0);
1205
+ },
1206
+ didObserveProgress(newScroll, watchdog) {
1207
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1208
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1209
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1210
+ },
1211
+ get(state) {
1212
+ var _a3, _b;
1213
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1214
+ },
1215
+ hasNonZeroTargetOffset(targetOffset) {
1216
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1217
+ },
1218
+ isAtZeroTargetOffset(targetOffset) {
1219
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1220
+ },
1221
+ set(state, watchdog) {
1222
+ var _a3, _b;
1223
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1224
+ return;
1225
+ }
1226
+ const completion = ensureInitialScrollSessionCompletion(state);
1227
+ completion.watchdog = watchdog ? {
1228
+ startScroll: watchdog.startScroll,
1229
+ targetOffset: watchdog.targetOffset
1230
+ } : void 0;
1231
+ }
1232
+ };
1233
+ function setInitialScrollSession(state, options = {}) {
1234
+ var _a3, _b, _c;
1235
+ const existingSession = state.initialScrollSession;
1236
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1237
+ const completion = existingSession == null ? void 0 : existingSession.completion;
1238
+ const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1239
+ const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1240
+ if (!kind) {
1241
+ return clearInitialScrollSession(state);
1242
+ }
1243
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1244
+ return clearInitialScrollSession(state);
1245
+ }
1246
+ const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1247
+ state.initialScrollSession = createInitialScrollSession({
1248
+ bootstrap,
1249
+ completion,
1250
+ kind,
1251
+ previousDataLength
1252
+ });
1253
+ return state.initialScrollSession;
1254
+ }
1255
+
1256
+ // src/utils/checkThreshold.ts
1257
+ var HYSTERESIS_MULTIPLIER = 1.3;
1258
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1259
+ const absDistance = Math.abs(distance);
1260
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
1261
+ const updateSnapshot = () => {
1262
+ setSnapshot({
1263
+ atThreshold,
1264
+ contentSize: context.contentSize,
1265
+ dataLength: context.dataLength,
1266
+ scrollPosition: context.scrollPosition
1267
+ });
1268
+ };
1269
+ if (!wasReached) {
1270
+ if (!within) {
1271
+ return false;
1272
+ }
1273
+ onReached(distance);
1274
+ updateSnapshot();
1275
+ return true;
1276
+ }
1277
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1278
+ if (reset) {
1279
+ setSnapshot(void 0);
1280
+ return false;
1281
+ }
1282
+ if (within) {
1283
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1284
+ if (changed) {
1285
+ if (allowReentryOnChange) {
1286
+ onReached(distance);
1287
+ }
1288
+ updateSnapshot();
1289
+ }
1290
+ }
1291
+ return true;
1292
+ };
1293
+
1294
+ // src/utils/hasActiveInitialScroll.ts
1295
+ function hasActiveInitialScroll(state) {
1296
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1297
+ }
1298
+
1299
+ // src/utils/checkAtBottom.ts
1300
+ function checkAtBottom(ctx) {
1301
+ var _a3;
1302
+ const state = ctx.state;
1303
+ if (!state) {
1304
+ return;
1305
+ }
1306
+ const {
1307
+ queuedInitialLayout,
1308
+ scrollLength,
1309
+ scroll,
1310
+ maintainingScrollAtEnd,
1311
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1312
+ } = state;
1313
+ const contentSize = getContentSize(ctx);
1314
+ if (contentSize > 0 && queuedInitialLayout) {
1315
+ const insetEnd = getContentInsetEnd(ctx);
1316
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1317
+ const isContentLess = contentSize < scrollLength;
1318
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1319
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1320
+ set$(
1321
+ ctx,
1322
+ "isWithinMaintainScrollAtEndThreshold",
1323
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1324
+ );
1325
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1326
+ if (!shouldSkipThresholdChecks) {
1327
+ state.isEndReached = checkThreshold(
1328
+ distanceFromEnd,
1329
+ isContentLess,
1330
+ onEndReachedThreshold * scrollLength,
1331
+ state.isEndReached,
1332
+ state.endReachedSnapshot,
1333
+ {
1334
+ contentSize,
1335
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1336
+ scrollPosition: scroll
1337
+ },
1338
+ (distance) => {
1339
+ var _a4, _b;
1340
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1341
+ },
1342
+ (snapshot) => {
1343
+ state.endReachedSnapshot = snapshot;
1344
+ },
1345
+ true
1346
+ );
1347
+ }
1348
+ }
1349
+ }
1350
+
1351
+ // src/utils/checkAtTop.ts
1352
+ function checkAtTop(ctx) {
1353
+ const state = ctx == null ? void 0 : ctx.state;
1354
+ if (!state) {
1355
+ return;
1356
+ }
1357
+ const {
1358
+ dataChangeEpoch,
1359
+ isStartReached,
1360
+ props: { data, onStartReachedThreshold },
1361
+ scroll,
1362
+ scrollLength,
1363
+ startReachedSnapshot,
1364
+ startReachedSnapshotDataChangeEpoch,
1365
+ totalSize
1366
+ } = state;
1367
+ const dataLength = data.length;
1368
+ const threshold = onStartReachedThreshold * scrollLength;
1369
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1370
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1371
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1372
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1373
+ state.isStartReached = false;
1374
+ state.startReachedSnapshot = void 0;
1375
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1376
+ }
1377
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1378
+ set$(ctx, "isNearStart", scroll <= threshold);
1379
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1380
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1381
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1382
+ state.isStartReached = checkThreshold(
1383
+ scroll,
1384
+ false,
1385
+ threshold,
1386
+ state.isStartReached,
1387
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1388
+ {
1389
+ contentSize: totalSize,
1390
+ dataLength,
1391
+ scrollPosition: scroll
1392
+ },
1393
+ (distance) => {
1394
+ var _a3, _b;
1395
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1396
+ },
1397
+ (snapshot) => {
1398
+ state.startReachedSnapshot = snapshot;
1399
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1400
+ },
1401
+ allowReentryOnDataChange
1402
+ );
1403
+ }
1404
+ }
1405
+
1406
+ // src/utils/checkThresholds.ts
1407
+ function checkThresholds(ctx) {
1408
+ checkAtBottom(ctx);
1409
+ checkAtTop(ctx);
1410
+ }
1411
+
1412
+ // src/core/recalculateSettledScroll.ts
1413
+ function recalculateSettledScroll(ctx) {
1414
+ var _a3, _b;
1415
+ const state = ctx.state;
1416
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1417
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1418
+ }
1419
+ checkThresholds(ctx);
1420
+ }
1421
+
1422
+ // src/utils/setInitialRenderState.ts
1423
+ function setInitialRenderState(ctx, {
1424
+ didLayout,
1425
+ didInitialScroll
1426
+ }) {
1427
+ const { state } = ctx;
1428
+ const {
1429
+ loadStartTime,
1430
+ props: { onLoad }
1431
+ } = state;
1432
+ if (didLayout) {
1433
+ state.didContainersLayout = true;
1434
+ }
1435
+ if (didInitialScroll) {
1436
+ state.didFinishInitialScroll = true;
1437
+ }
1438
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1439
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1440
+ set$(ctx, "readyToRender", true);
1441
+ if (onLoad) {
1442
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1443
+ }
1444
+ }
1445
+ }
1446
+
1447
+ // src/core/finishInitialScroll.ts
1448
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1449
+ function syncInitialScrollOffset(state, offset) {
1450
+ state.scroll = offset;
1451
+ state.scrollPending = offset;
1452
+ state.scrollPrev = offset;
1453
+ }
1454
+ function clearPreservedInitialScrollTargetTimeout(state) {
1455
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
1456
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
1457
+ state.timeoutPreservedInitialScrollClear = void 0;
1458
+ }
1459
+ }
1460
+ function clearPreservedInitialScrollTarget(state) {
1461
+ clearPreservedInitialScrollTargetTimeout(state);
1462
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1463
+ state.initialScroll = void 0;
1464
+ setInitialScrollSession(state);
1465
+ }
1466
+ function finishInitialScroll(ctx, options) {
1467
+ var _a3, _b, _c;
1468
+ const state = ctx.state;
1469
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1470
+ syncInitialScrollOffset(state, options.resolvedOffset);
1471
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1472
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1473
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1474
+ syncInitialScrollOffset(state, observedOffset);
1475
+ }
1476
+ }
1477
+ const complete = () => {
1478
+ var _a4, _b2, _c2, _d, _e;
1479
+ const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1480
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1481
+ initialScrollWatchdog.clear(state);
1482
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1483
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1484
+ setInitialScrollSession(state);
1485
+ clearPreservedInitialScrollTargetTimeout(state);
1486
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1487
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1488
+ var _a5;
1489
+ state.timeoutPreservedInitialScrollClear = void 0;
1490
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1491
+ return;
1492
+ }
1493
+ clearPreservedInitialScrollTarget(state);
1494
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
1495
+ }
1496
+ } else {
1497
+ clearPreservedInitialScrollTarget(state);
1498
+ }
1499
+ if (options == null ? void 0 : options.recalculateItems) {
1500
+ recalculateSettledScroll(ctx);
1501
+ }
1502
+ setInitialRenderState(ctx, { didInitialScroll: true });
1503
+ if (shouldReleaseDeferredPublicOnScroll) {
1504
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1505
+ }
1506
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
1507
+ };
1508
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
1509
+ requestAnimationFrame(complete);
1510
+ return;
1511
+ }
1512
+ complete();
1513
+ }
1514
+
1515
+ // src/core/calculateOffsetForIndex.ts
1516
+ function calculateOffsetForIndex(ctx, index) {
1517
+ const state = ctx.state;
1518
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1519
+ }
1520
+
1521
+ // src/core/getTopOffsetAdjustment.ts
1522
+ function getTopOffsetAdjustment(ctx) {
1523
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1524
+ }
1525
+
1526
+ // src/utils/getId.ts
1527
+ function getId(state, index) {
1528
+ const { data, keyExtractor } = state.props;
1529
+ if (!data) {
1530
+ return "";
1531
+ }
1532
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1533
+ const id = ret;
1534
+ state.idCache[index] = id;
1535
+ return id;
1536
+ }
1537
+
1538
+ // src/core/addTotalSize.ts
1539
+ function addTotalSize(ctx, key, add) {
1540
+ const state = ctx.state;
1541
+ const prevTotalSize = state.totalSize;
1542
+ let totalSize = state.totalSize;
1543
+ if (key === null) {
1544
+ totalSize = add;
1545
+ if (state.timeoutSetPaddingTop) {
1546
+ clearTimeout(state.timeoutSetPaddingTop);
1547
+ state.timeoutSetPaddingTop = void 0;
1548
+ }
1549
+ } else {
1550
+ totalSize += add;
1551
+ }
1552
+ if (prevTotalSize !== totalSize) {
1553
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1554
+ state.pendingTotalSize = totalSize;
1555
+ } else {
1556
+ state.pendingTotalSize = void 0;
1557
+ state.totalSize = totalSize;
1558
+ set$(ctx, "totalSize", totalSize);
1559
+ }
1560
+ }
1561
+ }
1562
+
1563
+ // src/core/setSize.ts
1564
+ function setSize(ctx, itemKey, size) {
1565
+ const state = ctx.state;
1566
+ const { sizes } = state;
1567
+ const previousSize = sizes.get(itemKey);
1568
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1569
+ if (diff !== 0) {
1570
+ addTotalSize(ctx, itemKey, diff);
1571
+ }
1572
+ sizes.set(itemKey, size);
1573
+ }
1574
+
1575
+ // src/utils/getItemSize.ts
1576
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1577
+ var _a3, _b, _c;
1578
+ const state = ctx.state;
1579
+ const {
1580
+ sizesKnown,
1581
+ sizes,
1582
+ averageSizes,
1583
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1584
+ scrollingTo
1585
+ } = state;
1586
+ const sizeKnown = sizesKnown.get(key);
1587
+ if (sizeKnown !== void 0) {
1588
+ return sizeKnown;
1589
+ }
1590
+ let size;
1591
+ const renderedSize = sizes.get(key);
1592
+ if (preferCachedSize) {
1593
+ if (renderedSize !== void 0) {
1594
+ return renderedSize;
1595
+ }
1596
+ }
1597
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1598
+ if (getFixedItemSize) {
1599
+ size = getFixedItemSize(data, index, itemType);
1600
+ if (size !== void 0) {
1601
+ sizesKnown.set(key, size);
1602
+ }
1603
+ }
1604
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1605
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1606
+ if (averageSizeForType !== void 0) {
1607
+ size = roundSize(averageSizeForType);
1608
+ }
1609
+ }
1610
+ if (size === void 0 && renderedSize !== void 0) {
1611
+ return renderedSize;
1612
+ }
1613
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
1614
+ const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
1615
+ if (averageSizeForType !== void 0) {
1616
+ size = roundSize(averageSizeForType);
1617
+ }
1253
1618
  }
1254
- };
1255
- var initialScrollWatchdog = {
1256
- clear(state) {
1257
- initialScrollWatchdog.set(state, void 0);
1258
- },
1259
- didObserveProgress(newScroll, watchdog) {
1260
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1261
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1262
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1263
- },
1264
- get(state) {
1265
- var _a3, _b;
1266
- return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1267
- },
1268
- hasNonZeroTargetOffset(targetOffset) {
1269
- return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1270
- },
1271
- isAtZeroTargetOffset(targetOffset) {
1272
- return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1273
- },
1274
- set(state, watchdog) {
1275
- var _a3, _b;
1276
- if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1277
- return;
1619
+ if (size === void 0) {
1620
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1621
+ }
1622
+ setSize(ctx, key, size);
1623
+ return size;
1624
+ }
1625
+ function getItemSizeAtIndex(ctx, index) {
1626
+ if (index === void 0 || index < 0) {
1627
+ return void 0;
1628
+ }
1629
+ const targetId = getId(ctx.state, index);
1630
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1631
+ }
1632
+
1633
+ // src/core/calculateOffsetWithOffsetPosition.ts
1634
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1635
+ var _a3;
1636
+ const state = ctx.state;
1637
+ const { index, viewOffset, viewPosition } = params;
1638
+ let offset = offsetParam;
1639
+ if (viewOffset) {
1640
+ offset -= viewOffset;
1641
+ }
1642
+ if (index !== void 0) {
1643
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1644
+ if (topOffsetAdjustment) {
1645
+ offset += topOffsetAdjustment;
1278
1646
  }
1279
- const completion = ensureInitialScrollSessionCompletion(state);
1280
- completion.watchdog = watchdog ? {
1281
- startScroll: watchdog.startScroll,
1282
- targetOffset: watchdog.targetOffset
1283
- } : void 0;
1284
1647
  }
1285
- };
1286
- function setInitialScrollSession(state, options = {}) {
1287
- var _a3, _b, _c;
1288
- const existingSession = state.initialScrollSession;
1289
- const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1290
- const completion = existingSession == null ? void 0 : existingSession.completion;
1291
- const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1292
- const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1293
- if (!kind) {
1294
- return clearInitialScrollSession(state);
1648
+ if (viewPosition !== void 0 && index !== void 0) {
1649
+ const dataLength = state.props.data.length;
1650
+ if (dataLength === 0) {
1651
+ return offset;
1652
+ }
1653
+ const isOutOfBounds = index < 0 || index >= dataLength;
1654
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1655
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1656
+ const trailingInset = getContentInsetEnd(ctx);
1657
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1658
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1659
+ const footerSize = peek$(ctx, "footerSize") || 0;
1660
+ offset += footerSize;
1661
+ }
1295
1662
  }
1296
- if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1297
- return clearInitialScrollSession(state);
1663
+ return offset;
1664
+ }
1665
+
1666
+ // src/core/clampScrollOffset.ts
1667
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1668
+ const state = ctx.state;
1669
+ const contentSize = getContentSize(ctx);
1670
+ let clampedOffset = offset;
1671
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
1672
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1673
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1674
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1675
+ const maxOffset = baseMaxOffset + extraEndOffset;
1676
+ clampedOffset = Math.min(offset, maxOffset);
1298
1677
  }
1299
- const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1300
- state.initialScrollSession = createInitialScrollSession({
1301
- bootstrap,
1302
- completion,
1303
- kind,
1304
- previousDataLength
1305
- });
1306
- return state.initialScrollSession;
1678
+ clampedOffset = Math.max(0, clampedOffset);
1679
+ return clampedOffset;
1307
1680
  }
1308
1681
 
1309
1682
  // src/core/finishScrollTo.ts
@@ -1324,15 +1697,20 @@ function finishScrollTo(ctx) {
1324
1697
  }
1325
1698
  if (scrollingTo.isInitialScroll || state.initialScroll) {
1326
1699
  const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1700
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1327
1701
  finishInitialScroll(ctx, {
1328
- onFinished: resolvePendingScroll,
1329
- preserveTarget: isOffsetSession && state.props.data.length === 0 || !!scrollingTo.isInitialScroll && !!((_b = state.initialScroll) == null ? void 0 : _b.preserveForFooterLayout),
1702
+ onFinished: () => {
1703
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1704
+ },
1705
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1330
1706
  recalculateItems: true,
1707
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1331
1708
  syncObservedOffset: isOffsetSession,
1332
1709
  waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1333
1710
  });
1334
1711
  return;
1335
1712
  }
1713
+ recalculateSettledScroll(ctx);
1336
1714
  resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1337
1715
  }
1338
1716
  }
@@ -1457,7 +1835,7 @@ function checkFinishedScrollFallback(ctx) {
1457
1835
  );
1458
1836
  const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1459
1837
  const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1460
- const shouldRetrySilentInitialNativeScroll = Platform2.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1838
+ const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1461
1839
  if (shouldRetrySilentInitialNativeScroll) {
1462
1840
  const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1463
1841
  const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
@@ -1507,6 +1885,17 @@ function doScrollTo(ctx, params) {
1507
1885
  }
1508
1886
 
1509
1887
  // src/core/scrollTo.ts
1888
+ function getAverageSizeSnapshot(state) {
1889
+ if (Object.keys(state.averageSizes).length === 0) {
1890
+ return void 0;
1891
+ }
1892
+ const snapshot = {};
1893
+ for (const itemType in state.averageSizes) {
1894
+ const averages = state.averageSizes[itemType];
1895
+ snapshot[itemType] = averages.avg;
1896
+ }
1897
+ return snapshot;
1898
+ }
1510
1899
  function syncInitialScrollNativeWatchdog(state, options) {
1511
1900
  var _a3;
1512
1901
  const { isInitialScroll, requestedOffset, targetOffset } = options;
@@ -1554,15 +1943,17 @@ function scrollTo(ctx, params) {
1554
1943
  if (isInitialScroll) {
1555
1944
  initialScrollCompletion.resetFlags(state);
1556
1945
  }
1946
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
1557
1947
  state.scrollingTo = {
1558
1948
  ...scrollTarget,
1949
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
1559
1950
  targetOffset,
1560
1951
  waitForInitialScrollCompletionFrame
1561
1952
  };
1562
1953
  }
1563
1954
  state.scrollPending = targetOffset;
1564
1955
  syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
1565
- if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1956
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1566
1957
  doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1567
1958
  } else {
1568
1959
  state.scroll = offset;
@@ -1612,180 +2003,7 @@ function scrollToIndex(ctx, {
1612
2003
  });
1613
2004
  }
1614
2005
 
1615
- // src/utils/checkThreshold.ts
1616
- var HYSTERESIS_MULTIPLIER = 1.3;
1617
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1618
- const absDistance = Math.abs(distance);
1619
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
1620
- const updateSnapshot = () => {
1621
- setSnapshot({
1622
- atThreshold,
1623
- contentSize: context.contentSize,
1624
- dataLength: context.dataLength,
1625
- scrollPosition: context.scrollPosition
1626
- });
1627
- };
1628
- if (!wasReached) {
1629
- if (!within) {
1630
- return false;
1631
- }
1632
- onReached(distance);
1633
- updateSnapshot();
1634
- return true;
1635
- }
1636
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1637
- if (reset) {
1638
- setSnapshot(void 0);
1639
- return false;
1640
- }
1641
- if (within) {
1642
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1643
- if (changed) {
1644
- if (allowReentryOnChange) {
1645
- onReached(distance);
1646
- }
1647
- updateSnapshot();
1648
- }
1649
- }
1650
- return true;
1651
- };
1652
-
1653
- // src/utils/checkAtBottom.ts
1654
- function checkAtBottom(ctx) {
1655
- var _a3;
1656
- const state = ctx.state;
1657
- if (!state || state.initialScroll) {
1658
- return;
1659
- }
1660
- const {
1661
- queuedInitialLayout,
1662
- scrollLength,
1663
- scroll,
1664
- maintainingScrollAtEnd,
1665
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1666
- } = state;
1667
- if (state.initialScroll) {
1668
- return;
1669
- }
1670
- const contentSize = getContentSize(ctx);
1671
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1672
- const insetEnd = getContentInsetEnd(state);
1673
- const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1674
- const isContentLess = contentSize < scrollLength;
1675
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1676
- state.isEndReached = checkThreshold(
1677
- distanceFromEnd,
1678
- isContentLess,
1679
- onEndReachedThreshold * scrollLength,
1680
- state.isEndReached,
1681
- state.endReachedSnapshot,
1682
- {
1683
- contentSize,
1684
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1685
- scrollPosition: scroll
1686
- },
1687
- (distance) => {
1688
- var _a4, _b;
1689
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1690
- },
1691
- (snapshot) => {
1692
- state.endReachedSnapshot = snapshot;
1693
- },
1694
- true
1695
- );
1696
- }
1697
- }
1698
-
1699
- // src/utils/checkAtTop.ts
1700
- function checkAtTop(ctx) {
1701
- const state = ctx == null ? void 0 : ctx.state;
1702
- if (!state || state.initialScroll || state.scrollingTo) {
1703
- return;
1704
- }
1705
- const {
1706
- dataChangeEpoch,
1707
- isStartReached,
1708
- props: { data, onStartReachedThreshold },
1709
- scroll,
1710
- scrollLength,
1711
- startReachedSnapshot,
1712
- startReachedSnapshotDataChangeEpoch,
1713
- totalSize
1714
- } = state;
1715
- const dataLength = data.length;
1716
- const threshold = onStartReachedThreshold * scrollLength;
1717
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1718
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1719
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1720
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1721
- state.isStartReached = false;
1722
- state.startReachedSnapshot = void 0;
1723
- state.startReachedSnapshotDataChangeEpoch = void 0;
1724
- }
1725
- state.isAtStart = scroll <= 0;
1726
- if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1727
- return;
1728
- }
1729
- state.isStartReached = checkThreshold(
1730
- scroll,
1731
- false,
1732
- threshold,
1733
- state.isStartReached,
1734
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1735
- {
1736
- contentSize: totalSize,
1737
- dataLength,
1738
- scrollPosition: scroll
1739
- },
1740
- (distance) => {
1741
- var _a3, _b;
1742
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1743
- },
1744
- (snapshot) => {
1745
- state.startReachedSnapshot = snapshot;
1746
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1747
- },
1748
- allowReentryOnDataChange
1749
- );
1750
- }
1751
-
1752
- // src/utils/checkThresholds.ts
1753
- function checkThresholds(ctx) {
1754
- checkAtBottom(ctx);
1755
- checkAtTop(ctx);
1756
- }
1757
-
1758
- // src/utils/setInitialRenderState.ts
1759
- function setInitialRenderState(ctx, {
1760
- didLayout,
1761
- didInitialScroll
1762
- }) {
1763
- const { state } = ctx;
1764
- const {
1765
- loadStartTime,
1766
- props: { onLoad }
1767
- } = state;
1768
- if (didLayout) {
1769
- state.didContainersLayout = true;
1770
- }
1771
- if (didInitialScroll) {
1772
- state.didFinishInitialScroll = true;
1773
- }
1774
- const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1775
- if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1776
- set$(ctx, "readyToRender", true);
1777
- if (onLoad) {
1778
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1779
- }
1780
- }
1781
- }
1782
-
1783
2006
  // src/core/initialScroll.ts
1784
- function syncInitialScrollOffset(state, offset) {
1785
- state.scroll = offset;
1786
- state.scrollPending = offset;
1787
- state.scrollPrev = offset;
1788
- }
1789
2007
  function dispatchInitialScroll(ctx, params) {
1790
2008
  const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
1791
2009
  const requestedIndex = target.index;
@@ -1806,6 +2024,11 @@ function dispatchInitialScroll(ctx, params) {
1806
2024
  }
1807
2025
  function setInitialScrollTarget(state, target, options) {
1808
2026
  var _a3;
2027
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2028
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2029
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2030
+ state.timeoutPreservedInitialScrollClear = void 0;
2031
+ }
1809
2032
  state.initialScroll = target;
1810
2033
  if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
1811
2034
  state.didFinishInitialScroll = false;
@@ -1814,44 +2037,6 @@ function setInitialScrollTarget(state, target, options) {
1814
2037
  kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
1815
2038
  });
1816
2039
  }
1817
- function finishInitialScroll(ctx, options) {
1818
- var _a3, _b, _c;
1819
- const state = ctx.state;
1820
- if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1821
- syncInitialScrollOffset(state, options.resolvedOffset);
1822
- } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1823
- const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1824
- if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1825
- syncInitialScrollOffset(state, observedOffset);
1826
- }
1827
- }
1828
- const complete = () => {
1829
- var _a4, _b2, _c2, _d, _e, _f, _g;
1830
- const shouldReleaseDeferredPublicOnScroll = Platform2.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1831
- const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1832
- initialScrollWatchdog.clear(state);
1833
- if (!(options == null ? void 0 : options.preserveTarget)) {
1834
- state.initialScroll = void 0;
1835
- }
1836
- setInitialScrollSession(state);
1837
- if ((options == null ? void 0 : options.recalculateItems) && ((_e = state.props) == null ? void 0 : _e.data)) {
1838
- (_f = state.triggerCalculateItemsInView) == null ? void 0 : _f.call(state, { forceFullItemPositions: true });
1839
- }
1840
- if (options == null ? void 0 : options.recalculateItems) {
1841
- checkThresholds(ctx);
1842
- }
1843
- setInitialRenderState(ctx, { didInitialScroll: true });
1844
- if (shouldReleaseDeferredPublicOnScroll) {
1845
- releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1846
- }
1847
- (_g = options == null ? void 0 : options.onFinished) == null ? void 0 : _g.call(options);
1848
- };
1849
- if (options == null ? void 0 : options.waitForCompletionFrame) {
1850
- requestAnimationFrame(complete);
1851
- return;
1852
- }
1853
- complete();
1854
- }
1855
2040
  function resolveInitialScrollOffset(ctx, initialScroll) {
1856
2041
  var _a3, _b;
1857
2042
  const state = ctx.state;
@@ -1945,13 +2130,18 @@ function advanceCurrentInitialScrollSession(ctx, options) {
1945
2130
  function isNullOrUndefined2(value) {
1946
2131
  return value === null || value === void 0;
1947
2132
  }
1948
- function getMountedBufferedIndices(state) {
1949
- const { startBuffered, endBuffered } = state;
1950
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
1951
- return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= startBuffered && index <= endBuffered).sort((a, b) => a - b);
2133
+ function getMountedIndicesInRange(state, start, end) {
2134
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2135
+ 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);
1952
2136
  }
1953
2137
  return [];
1954
2138
  }
2139
+ function getMountedBufferedIndices(state) {
2140
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2141
+ }
2142
+ function getMountedNoBufferIndices(state) {
2143
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2144
+ }
1955
2145
  function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
1956
2146
  return indices.length > 0 && indices.every((index) => {
1957
2147
  const key = getId(state, index);
@@ -2170,16 +2360,22 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2170
2360
  function areEquivalentBootstrapInitialScrollTargets(current, next) {
2171
2361
  return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2172
2362
  }
2173
- function clearPendingInitialScrollFooterLayout(state, target) {
2363
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
2364
+ const { dataLength, stylePaddingBottom, target } = options;
2365
+ const state = ctx.state;
2174
2366
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2175
2367
  return;
2176
2368
  }
2177
- if (state.didFinishInitialScroll && !getBootstrapInitialScrollSession(state)) {
2178
- state.initialScroll = void 0;
2179
- setInitialScrollSession(state);
2180
- return;
2181
- }
2182
- setInitialScrollTarget(state, { ...target, preserveForFooterLayout: void 0 });
2369
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
2370
+ dataLength,
2371
+ footerSize: 0,
2372
+ preserveForFooterLayout: void 0,
2373
+ stylePaddingBottom
2374
+ });
2375
+ setInitialScrollTarget(state, clearedFooterTarget);
2376
+ }
2377
+ function clearFinishedViewportRetargetableInitialScroll(state) {
2378
+ clearPreservedInitialScrollTarget(state);
2183
2379
  }
2184
2380
  function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2185
2381
  const state = ctx.state;
@@ -2194,6 +2390,25 @@ function getObservedBootstrapInitialScrollOffset(state) {
2194
2390
  const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2195
2391
  return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2196
2392
  }
2393
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2394
+ var _a3, _b;
2395
+ const state = ctx.state;
2396
+ const initialScroll = state.initialScroll;
2397
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
2398
+ return;
2399
+ }
2400
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2401
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2402
+ clearPendingInitialScrollFooterLayout(ctx, {
2403
+ dataLength: state.props.data.length,
2404
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2405
+ target: initialScroll
2406
+ });
2407
+ return;
2408
+ }
2409
+ clearFinishedViewportRetargetableInitialScroll(state);
2410
+ }
2411
+ }
2197
2412
  function startBootstrapInitialScrollOnMount(ctx, options) {
2198
2413
  var _a3, _b, _c;
2199
2414
  const { initialScrollAtEnd, target } = options;
@@ -2215,7 +2430,7 @@ function startBootstrapInitialScrollOnMount(ctx, options) {
2215
2430
  } else {
2216
2431
  startBootstrapInitialScrollSession(state, {
2217
2432
  scroll: offset,
2218
- seedContentOffset: Platform2.OS === "web" ? 0 : offset,
2433
+ seedContentOffset: Platform.OS === "web" ? 0 : offset,
2219
2434
  targetIndexSeed: target.index
2220
2435
  });
2221
2436
  ensureBootstrapInitialScrollFrameTicker(ctx);
@@ -2230,15 +2445,16 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2230
2445
  }
2231
2446
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2232
2447
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2448
+ const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2449
+ if (shouldClearFinishedResizePreservation) {
2450
+ clearPreservedInitialScrollTarget(state);
2451
+ return;
2452
+ }
2233
2453
  const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2234
2454
  if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2235
2455
  return;
2236
2456
  }
2237
2457
  if (shouldRetargetBottomAligned) {
2238
- if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2239
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2240
- return;
2241
- }
2242
2458
  const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2243
2459
  dataLength,
2244
2460
  footerSize: peek$(ctx, "footerSize") || 0,
@@ -2251,6 +2467,14 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2251
2467
  stylePaddingBottom,
2252
2468
  target: initialScroll
2253
2469
  });
2470
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2471
+ clearPendingInitialScrollFooterLayout(ctx, {
2472
+ dataLength,
2473
+ stylePaddingBottom,
2474
+ target: initialScroll
2475
+ });
2476
+ return;
2477
+ }
2254
2478
  if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2255
2479
  setInitialScrollTarget(state, updatedInitialScroll, {
2256
2480
  resetDidFinish: shouldResetDidFinish
@@ -2292,7 +2516,11 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2292
2516
  return;
2293
2517
  }
2294
2518
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2295
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2519
+ clearPendingInitialScrollFooterLayout(ctx, {
2520
+ dataLength,
2521
+ stylePaddingBottom,
2522
+ target: initialScroll
2523
+ });
2296
2524
  } else {
2297
2525
  const updatedInitialScroll = createInitialScrollAtEndTarget({
2298
2526
  dataLength,
@@ -2302,10 +2530,15 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2302
2530
  });
2303
2531
  const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2304
2532
  if (!didTargetChange) {
2305
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2533
+ clearPendingInitialScrollFooterLayout(ctx, {
2534
+ dataLength,
2535
+ stylePaddingBottom,
2536
+ target: initialScroll
2537
+ });
2306
2538
  } else {
2539
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
2307
2540
  setInitialScrollTarget(state, updatedInitialScroll, {
2308
- resetDidFinish: !!state.didFinishInitialScroll
2541
+ resetDidFinish: didFinishInitialScroll
2309
2542
  });
2310
2543
  rearmBootstrapInitialScroll(ctx, {
2311
2544
  scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
@@ -2314,6 +2547,29 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2314
2547
  }
2315
2548
  }
2316
2549
  }
2550
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
2551
+ const state = ctx.state;
2552
+ const initialScroll = state.initialScroll;
2553
+ if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2554
+ return;
2555
+ }
2556
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2557
+ if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2558
+ return;
2559
+ }
2560
+ const didFinishInitialScroll = state.didFinishInitialScroll;
2561
+ if (didFinishInitialScroll) {
2562
+ setInitialScrollTarget(state, initialScroll, {
2563
+ resetDidFinish: true
2564
+ });
2565
+ state.clearPreservedInitialScrollOnNextFinish = true;
2566
+ }
2567
+ rearmBootstrapInitialScroll(ctx, {
2568
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
2569
+ seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2570
+ targetIndexSeed: initialScroll.index
2571
+ });
2572
+ }
2317
2573
  function evaluateBootstrapInitialScroll(ctx) {
2318
2574
  var _a3, _b;
2319
2575
  const state = ctx.state;
@@ -2371,7 +2627,7 @@ function evaluateBootstrapInitialScroll(ctx) {
2371
2627
  queueBootstrapInitialScrollReevaluation(state);
2372
2628
  return;
2373
2629
  }
2374
- if (Platform2.OS !== "web" && Platform2.OS !== "android" && Math.abs(bootstrapInitialScroll.seedContentOffset - resolvedOffset) <= 1) {
2630
+ if (Platform.OS !== "web" && Platform.OS !== "android" && Math.abs(bootstrapInitialScroll.seedContentOffset - resolvedOffset) <= 1 && Math.abs(getObservedBootstrapInitialScrollOffset(state) - resolvedOffset) <= 1) {
2375
2631
  finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset);
2376
2632
  } else {
2377
2633
  clearBootstrapInitialScrollSession(state);
@@ -2379,17 +2635,20 @@ function evaluateBootstrapInitialScroll(ctx) {
2379
2635
  forceScroll: true,
2380
2636
  resolvedOffset,
2381
2637
  target: initialScroll,
2382
- waitForCompletionFrame: Platform2.OS === "web"
2638
+ waitForCompletionFrame: Platform.OS === "web"
2383
2639
  });
2384
2640
  }
2385
2641
  }
2386
2642
  function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
2643
+ var _a3;
2387
2644
  const state = ctx.state;
2388
2645
  clearBootstrapInitialScrollSession(state);
2646
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
2389
2647
  finishInitialScroll(ctx, {
2390
- preserveTarget: shouldPreserveInitialScrollForFooterLayout(state.initialScroll),
2648
+ preserveTarget: shouldPreserveResizeTarget,
2391
2649
  recalculateItems: true,
2392
- resolvedOffset
2650
+ resolvedOffset,
2651
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
2393
2652
  });
2394
2653
  }
2395
2654
  function abortBootstrapInitialScroll(ctx) {
@@ -2403,7 +2662,7 @@ function abortBootstrapInitialScroll(ctx) {
2403
2662
  forceScroll: true,
2404
2663
  resolvedOffset: bootstrapInitialScroll.scroll,
2405
2664
  target: initialScroll,
2406
- waitForCompletionFrame: Platform2.OS === "web"
2665
+ waitForCompletionFrame: Platform.OS === "web"
2407
2666
  });
2408
2667
  } else {
2409
2668
  finishBootstrapInitialScrollWithoutScroll(
@@ -2491,7 +2750,7 @@ function handleInitialScrollDataChange(ctx, options) {
2491
2750
  function requestAdjust(ctx, positionDiff, dataChanged) {
2492
2751
  const state = ctx.state;
2493
2752
  if (Math.abs(positionDiff) > 0.1) {
2494
- const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2753
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2495
2754
  const doit = () => {
2496
2755
  if (needsScrollWorkaround) {
2497
2756
  scrollTo(ctx, {
@@ -2510,7 +2769,7 @@ function requestAdjust(ctx, positionDiff, dataChanged) {
2510
2769
  const readyToRender = peek$(ctx, "readyToRender");
2511
2770
  if (readyToRender) {
2512
2771
  doit();
2513
- if (Platform2.OS !== "web") {
2772
+ if (Platform.OS !== "web") {
2514
2773
  const threshold = state.scroll - positionDiff / 2;
2515
2774
  if (!state.ignoreScrollFromMVCP) {
2516
2775
  state.ignoreScrollFromMVCP = {};
@@ -2565,7 +2824,7 @@ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
2565
2824
  return lock;
2566
2825
  }
2567
2826
  function updateAnchorLock(state, params) {
2568
- if (Platform2.OS === "web") {
2827
+ if (Platform.OS === "web") {
2569
2828
  const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
2570
2829
  const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
2571
2830
  const mvcpData = state.props.maintainVisibleContentPosition.data;
@@ -2587,7 +2846,7 @@ function updateAnchorLock(state, params) {
2587
2846
  }
2588
2847
  }
2589
2848
  function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
2590
- if (!dataChanged || Platform2.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
2849
+ if (!dataChanged || Platform.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
2591
2850
  return false;
2592
2851
  }
2593
2852
  const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
@@ -2666,7 +2925,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2666
2925
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2667
2926
  return true;
2668
2927
  }
2669
- if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
2928
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
2670
2929
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2671
2930
  return true;
2672
2931
  }
@@ -2686,7 +2945,7 @@ function prepareMVCP(ctx, dataChanged) {
2686
2945
  const {
2687
2946
  maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
2688
2947
  } = props;
2689
- const isWeb = Platform2.OS === "web";
2948
+ const isWeb = Platform.OS === "web";
2690
2949
  const now = Date.now();
2691
2950
  const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
2692
2951
  const scrollingTo = state.scrollingTo;
@@ -2831,6 +3090,86 @@ function prepareMVCP(ctx, dataChanged) {
2831
3090
  }
2832
3091
  }
2833
3092
 
3093
+ // src/core/syncMountedContainer.ts
3094
+ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3095
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
3096
+ const state = ctx.state;
3097
+ const {
3098
+ columns,
3099
+ columnSpans,
3100
+ positions,
3101
+ props: { data, itemsAreEqual, keyExtractor }
3102
+ } = state;
3103
+ const item = data[itemIndex];
3104
+ if (item === void 0) {
3105
+ return { didChangePosition: false, didRefreshData: false };
3106
+ }
3107
+ const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3108
+ let didChangePosition = false;
3109
+ let didRefreshData = false;
3110
+ if (updateLayout) {
3111
+ const positionValue = positions[itemIndex];
3112
+ if (positionValue === void 0) {
3113
+ set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3114
+ return { didChangePosition: false, didRefreshData: false };
3115
+ }
3116
+ const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3117
+ const column = columns[itemIndex] || 1;
3118
+ const span = columnSpans[itemIndex] || 1;
3119
+ const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
3120
+ const prevColumn = peek$(ctx, `containerColumn${containerIndex}`);
3121
+ const prevSpan = peek$(ctx, `containerSpan${containerIndex}`);
3122
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3123
+ set$(ctx, `containerPosition${containerIndex}`, position);
3124
+ didChangePosition = true;
3125
+ }
3126
+ if (column >= 0 && column !== prevColumn) {
3127
+ set$(ctx, `containerColumn${containerIndex}`, column);
3128
+ }
3129
+ if (span !== prevSpan) {
3130
+ set$(ctx, `containerSpan${containerIndex}`, span);
3131
+ }
3132
+ }
3133
+ const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3134
+ if (prevData !== item) {
3135
+ 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;
3136
+ const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3137
+ if (cachedComparison === 2) {
3138
+ set$(ctx, `containerItemData${containerIndex}`, item);
3139
+ didRefreshData = true;
3140
+ } else if (cachedComparison !== 1) {
3141
+ const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3142
+ const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3143
+ if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3144
+ set$(ctx, `containerItemData${containerIndex}`, item);
3145
+ didRefreshData = true;
3146
+ } else if (!itemsAreEqual) {
3147
+ set$(ctx, `containerItemData${containerIndex}`, item);
3148
+ didRefreshData = true;
3149
+ } else {
3150
+ const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3151
+ if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
3152
+ if (state.previousData) {
3153
+ state.pendingDataComparison = {
3154
+ byIndex: [],
3155
+ nextData: data,
3156
+ previousData: state.previousData
3157
+ };
3158
+ }
3159
+ }
3160
+ if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3161
+ state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3162
+ }
3163
+ if (!isEqual) {
3164
+ set$(ctx, `containerItemData${containerIndex}`, item);
3165
+ didRefreshData = true;
3166
+ }
3167
+ }
3168
+ }
3169
+ }
3170
+ return { didChangePosition, didRefreshData };
3171
+ }
3172
+
2834
3173
  // src/core/prepareColumnStartState.ts
2835
3174
  function prepareColumnStartState(ctx, startIndex, useAverageSize) {
2836
3175
  var _a3;
@@ -2993,9 +3332,10 @@ function updateSnapToOffsets(ctx) {
2993
3332
  }
2994
3333
 
2995
3334
  // src/core/updateItemPositions.ts
2996
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
3335
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
2997
3336
  doMVCP: false,
2998
3337
  forceFullUpdate: false,
3338
+ optimizeForVisibleWindow: false,
2999
3339
  scrollBottomBuffered: -1,
3000
3340
  startIndex: 0
3001
3341
  }) {
@@ -3020,7 +3360,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
3020
3360
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
3021
3361
  const lastScrollDelta = state.lastScrollDelta;
3022
3362
  const velocity = getScrollVelocity(state);
3023
- const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3363
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || Platform.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3024
3364
  const maxVisibleArea = scrollBottomBuffered + 1e3;
3025
3365
  const useAverageSize = !getEstimatedItemSize;
3026
3366
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
@@ -3135,7 +3475,15 @@ function ensureViewabilityState(ctx, configId) {
3135
3475
  }
3136
3476
  let state = map.get(configId);
3137
3477
  if (!state) {
3138
- state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
3478
+ state = {
3479
+ end: -1,
3480
+ endBuffered: -1,
3481
+ previousEnd: -1,
3482
+ previousStart: -1,
3483
+ start: -1,
3484
+ startBuffered: -1,
3485
+ viewableItems: []
3486
+ };
3139
3487
  map.set(configId, state);
3140
3488
  }
3141
3489
  return state;
@@ -3155,7 +3503,7 @@ function setupViewability(props) {
3155
3503
  }
3156
3504
  return viewabilityConfigCallbackPairs;
3157
3505
  }
3158
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
3506
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
3159
3507
  const {
3160
3508
  timeouts,
3161
3509
  props: { data }
@@ -3164,6 +3512,8 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
3164
3512
  const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
3165
3513
  viewabilityState.start = start;
3166
3514
  viewabilityState.end = end;
3515
+ viewabilityState.startBuffered = startBuffered;
3516
+ viewabilityState.endBuffered = endBuffered;
3167
3517
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
3168
3518
  const timer = setTimeout(() => {
3169
3519
  timeouts.delete(timer);
@@ -3179,7 +3529,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3179
3529
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
3180
3530
  const configId = viewabilityConfig.id;
3181
3531
  const viewabilityState = ensureViewabilityState(ctx, configId);
3182
- const { viewableItems: previousViewableItems, start, end } = viewabilityState;
3532
+ const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
3183
3533
  const viewabilityTokens = /* @__PURE__ */ new Map();
3184
3534
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
3185
3535
  viewabilityTokens.set(
@@ -3248,7 +3598,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3248
3598
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
3249
3599
  }
3250
3600
  if (onViewableItemsChanged) {
3251
- onViewableItemsChanged({ changed, viewableItems });
3601
+ onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
3252
3602
  }
3253
3603
  }
3254
3604
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -3349,6 +3699,7 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3349
3699
  const numContainers = peek$(ctx, "numContainers");
3350
3700
  const state = ctx.state;
3351
3701
  const { stickyContainerPool, containerItemTypes } = state;
3702
+ const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3352
3703
  const result = [];
3353
3704
  const availableContainers = [];
3354
3705
  const pendingRemovalSet = new Set(pendingRemoval);
@@ -3405,18 +3756,20 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3405
3756
  }
3406
3757
  }
3407
3758
  }
3408
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
3409
- if (stickyContainerPool.has(u)) {
3410
- continue;
3411
- }
3412
- const key = peek$(ctx, `containerItemKey${u}`);
3413
- if (key === void 0) continue;
3414
- const index = state.indexByKey.get(key);
3415
- const isOutOfView = index < startBuffered || index > endBuffered;
3416
- if (isOutOfView) {
3417
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3418
- if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
3419
- availableContainers.push({ distance, index: u });
3759
+ if (!shouldAvoidAssignedContainerReuse) {
3760
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
3761
+ if (stickyContainerPool.has(u)) {
3762
+ continue;
3763
+ }
3764
+ const key = peek$(ctx, `containerItemKey${u}`);
3765
+ if (key === void 0) continue;
3766
+ const index = state.indexByKey.get(key);
3767
+ const isOutOfView = index < startBuffered || index > endBuffered;
3768
+ if (isOutOfView) {
3769
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3770
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
3771
+ availableContainers.push({ distance, index: u });
3772
+ }
3420
3773
  }
3421
3774
  }
3422
3775
  }
@@ -3560,7 +3913,6 @@ function calculateItemsInView(ctx, params = {}) {
3560
3913
  alwaysRenderIndicesSet,
3561
3914
  drawDistance,
3562
3915
  getItemType,
3563
- itemsAreEqual,
3564
3916
  keyExtractor,
3565
3917
  onStickyHeaderChange
3566
3918
  },
@@ -3587,11 +3939,11 @@ function calculateItemsInView(ctx, params = {}) {
3587
3939
  const numColumns = peek$(ctx, "numColumns");
3588
3940
  const speed = getScrollVelocity(state);
3589
3941
  const scrollExtra = 0;
3590
- const { queuedInitialLayout } = state;
3591
- const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && state.initialScroll ? (
3942
+ const { initialScroll, queuedInitialLayout } = state;
3943
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
3592
3944
  // Before the initial layout settles, keep viewport math anchored to the
3593
3945
  // current initial-scroll target instead of transient native adjustments.
3594
- resolveInitialScrollOffset(ctx, state.initialScroll)
3946
+ resolveInitialScrollOffset(ctx, initialScroll)
3595
3947
  ) : state.scroll;
3596
3948
  const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
3597
3949
  const scrollAdjustPad = scrollAdjustPending - topPad;
@@ -3622,7 +3974,7 @@ function calculateItemsInView(ctx, params = {}) {
3622
3974
  if (top === null && bottom === null) {
3623
3975
  state.scrollForNextCalculateItemsInView = void 0;
3624
3976
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
3625
- if (Platform2.OS !== "web" || !isInMVCPActiveMode(state)) {
3977
+ if (Platform.OS !== "web" || !isInMVCPActiveMode(state)) {
3626
3978
  return;
3627
3979
  }
3628
3980
  }
@@ -3636,9 +3988,11 @@ function calculateItemsInView(ctx, params = {}) {
3636
3988
  columnSpans.length = 0;
3637
3989
  }
3638
3990
  const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
3991
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
3639
3992
  updateItemPositions(ctx, dataChanged, {
3640
3993
  doMVCP,
3641
3994
  forceFullUpdate: !!forceFullItemPositions,
3995
+ optimizeForVisibleWindow,
3642
3996
  scrollBottomBuffered,
3643
3997
  startIndex
3644
3998
  });
@@ -3886,44 +4240,25 @@ function calculateItemsInView(ctx, params = {}) {
3886
4240
  set$(ctx, `containerSpan${i}`, 1);
3887
4241
  } else {
3888
4242
  const itemIndex = indexByKey.get(itemKey);
3889
- const item = data[itemIndex];
3890
- if (item !== void 0) {
3891
- const positionValue = positions[itemIndex];
3892
- if (positionValue === void 0) {
3893
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
3894
- } else {
3895
- const position = (positionValue || 0) - scrollAdjustPending;
3896
- const column = columns[itemIndex] || 1;
3897
- const span = columnSpans[itemIndex] || 1;
3898
- const prevPos = peek$(ctx, `containerPosition${i}`);
3899
- const prevColumn = peek$(ctx, `containerColumn${i}`);
3900
- const prevSpan = peek$(ctx, `containerSpan${i}`);
3901
- const prevData = peek$(ctx, `containerItemData${i}`);
3902
- if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3903
- set$(ctx, `containerPosition${i}`, position);
3904
- didChangePositions = true;
3905
- }
3906
- if (column >= 0 && column !== prevColumn) {
3907
- set$(ctx, `containerColumn${i}`, column);
3908
- }
3909
- if (span !== prevSpan) {
3910
- set$(ctx, `containerSpan${i}`, span);
3911
- }
3912
- if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
3913
- set$(ctx, `containerItemData${i}`, item);
3914
- }
3915
- }
4243
+ if (itemIndex !== void 0) {
4244
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4245
+ scrollAdjustPending,
4246
+ updateLayout: true
4247
+ }).didChangePosition || didChangePositions;
3916
4248
  }
3917
4249
  }
3918
4250
  }
3919
- if (Platform2.OS === "web" && didChangePositions) {
4251
+ if (Platform.OS === "web" && didChangePositions) {
3920
4252
  set$(ctx, "lastPositionUpdate", Date.now());
3921
4253
  }
3922
4254
  if (suppressInitialScrollSideEffects) {
3923
4255
  evaluateBootstrapInitialScroll(ctx);
3924
4256
  return;
3925
4257
  }
3926
- if (!queuedInitialLayout && endBuffered !== null && checkAllSizesKnown(state)) {
4258
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4259
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4260
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4261
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
3927
4262
  setDidLayout(ctx);
3928
4263
  handleInitialScrollLayoutReady(ctx);
3929
4264
  }
@@ -3934,7 +4269,9 @@ function calculateItemsInView(ctx, params = {}) {
3934
4269
  viewabilityConfigCallbackPairs,
3935
4270
  scrollLength,
3936
4271
  startNoBuffer,
3937
- endNoBuffer
4272
+ endNoBuffer,
4273
+ startBuffered != null ? startBuffered : startNoBuffer,
4274
+ endBuffered != null ? endBuffered : endNoBuffer
3938
4275
  );
3939
4276
  }
3940
4277
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
@@ -3946,37 +4283,17 @@ function calculateItemsInView(ctx, params = {}) {
3946
4283
  });
3947
4284
  }
3948
4285
 
3949
- // src/core/checkActualChange.ts
3950
- function checkActualChange(state, dataProp, previousData) {
3951
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
3952
- return true;
3953
- }
3954
- const {
3955
- idCache,
3956
- props: { keyExtractor }
3957
- } = state;
3958
- for (let i = 0; i < dataProp.length; i++) {
3959
- if (dataProp[i] !== previousData[i]) {
3960
- return true;
3961
- }
3962
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
3963
- return true;
3964
- }
3965
- }
3966
- return false;
3967
- }
3968
-
3969
4286
  // src/core/doMaintainScrollAtEnd.ts
3970
4287
  function doMaintainScrollAtEnd(ctx) {
3971
4288
  const state = ctx.state;
3972
4289
  const {
3973
4290
  didContainersLayout,
3974
- isAtEnd,
3975
4291
  pendingNativeMVCPAdjust,
3976
4292
  refScroller,
3977
4293
  props: { maintainScrollAtEnd }
3978
4294
  } = state;
3979
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
4295
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4296
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
3980
4297
  if (pendingNativeMVCPAdjust) {
3981
4298
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
3982
4299
  return false;
@@ -3989,7 +4306,7 @@ function doMaintainScrollAtEnd(ctx) {
3989
4306
  }
3990
4307
  requestAnimationFrame(() => {
3991
4308
  var _a3;
3992
- if (state.isAtEnd) {
4309
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
3993
4310
  state.maintainingScrollAtEnd = true;
3994
4311
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
3995
4312
  animated: maintainScrollAtEnd.animated
@@ -4007,68 +4324,22 @@ function doMaintainScrollAtEnd(ctx) {
4007
4324
  return false;
4008
4325
  }
4009
4326
 
4010
- // src/utils/updateAveragesOnDataChange.ts
4011
- function updateAveragesOnDataChange(state, oldData, newData) {
4012
- var _a3;
4013
- const {
4014
- averageSizes,
4015
- sizesKnown,
4016
- indexByKey,
4017
- props: { itemsAreEqual, getItemType, keyExtractor }
4018
- } = state;
4019
- if (!itemsAreEqual || !oldData.length || !newData.length) {
4020
- for (const key in averageSizes) {
4021
- delete averageSizes[key];
4022
- }
4023
- return;
4024
- }
4025
- const itemTypesToPreserve = {};
4026
- const newDataLength = newData.length;
4027
- const oldDataLength = oldData.length;
4028
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
4029
- const newItem = newData[newIndex];
4030
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
4031
- const oldIndex = indexByKey.get(id);
4032
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
4033
- const knownSize = sizesKnown.get(id);
4034
- if (knownSize === void 0) continue;
4035
- const oldItem = oldData[oldIndex];
4036
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
4037
- if (areEqual) {
4038
- const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
4039
- let typeData = itemTypesToPreserve[itemType];
4040
- if (!typeData) {
4041
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
4042
- }
4043
- typeData.totalSize += knownSize;
4044
- typeData.count++;
4045
- }
4046
- }
4047
- }
4048
- for (const key in averageSizes) {
4049
- delete averageSizes[key];
4050
- }
4051
- for (const itemType in itemTypesToPreserve) {
4052
- const { totalSize, count } = itemTypesToPreserve[itemType];
4053
- if (count > 0) {
4054
- averageSizes[itemType] = {
4055
- avg: totalSize / count,
4056
- num: count
4057
- };
4058
- }
4059
- }
4060
- }
4061
-
4062
4327
  // src/core/checkResetContainers.ts
4063
- function checkResetContainers(ctx, dataProp) {
4328
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
4064
4329
  const state = ctx.state;
4065
4330
  const { previousData } = state;
4066
- if (previousData) {
4067
- updateAveragesOnDataChange(state, previousData, dataProp);
4068
- }
4069
4331
  const { maintainScrollAtEnd } = state.props;
4332
+ if (didColumnsChange) {
4333
+ state.sizes.clear();
4334
+ state.sizesKnown.clear();
4335
+ for (const key in state.averageSizes) {
4336
+ delete state.averageSizes[key];
4337
+ }
4338
+ state.minIndexSizeChanged = 0;
4339
+ state.scrollForNextCalculateItemsInView = void 0;
4340
+ }
4070
4341
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
4071
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4342
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
4072
4343
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
4073
4344
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
4074
4345
  state.isEndReached = false;
@@ -4079,6 +4350,53 @@ function checkResetContainers(ctx, dataProp) {
4079
4350
  delete state.previousData;
4080
4351
  }
4081
4352
 
4353
+ // src/core/checkStructuralDataChange.ts
4354
+ function checkStructuralDataChange(state, dataProp, previousData) {
4355
+ var _a3;
4356
+ state.pendingDataComparison = void 0;
4357
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4358
+ return true;
4359
+ }
4360
+ const {
4361
+ idCache,
4362
+ props: { itemsAreEqual, keyExtractor }
4363
+ } = state;
4364
+ let byIndex;
4365
+ for (let i = 0; i < dataProp.length; i++) {
4366
+ if (dataProp[i] === previousData[i]) {
4367
+ continue;
4368
+ }
4369
+ if (!keyExtractor) {
4370
+ if (byIndex) {
4371
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4372
+ }
4373
+ return true;
4374
+ }
4375
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4376
+ const nextKey = keyExtractor(dataProp[i], i);
4377
+ if (previousKey !== nextKey) {
4378
+ if (byIndex) {
4379
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4380
+ }
4381
+ return true;
4382
+ }
4383
+ if (!itemsAreEqual) {
4384
+ if (byIndex) {
4385
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4386
+ }
4387
+ return true;
4388
+ }
4389
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4390
+ byIndex != null ? byIndex : byIndex = [];
4391
+ byIndex[i] = isEqual ? 1 : 2;
4392
+ if (!isEqual) {
4393
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4394
+ return true;
4395
+ }
4396
+ }
4397
+ return false;
4398
+ }
4399
+
4082
4400
  // src/core/doInitialAllocateContainers.ts
4083
4401
  function doInitialAllocateContainers(ctx) {
4084
4402
  var _a3, _b, _c;
@@ -4244,7 +4562,7 @@ function updateScroll(ctx, newScroll, forceUpdate) {
4244
4562
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
4245
4563
  checkThresholds(ctx);
4246
4564
  };
4247
- if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
4565
+ if (Platform.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
4248
4566
  flushSync(runCalculateItems);
4249
4567
  } else {
4250
4568
  runCalculateItems();
@@ -4277,7 +4595,7 @@ function trackInitialScrollNativeProgress(state, newScroll) {
4277
4595
  }
4278
4596
  function shouldDeferPublicOnScroll(state) {
4279
4597
  var _a3;
4280
- return Platform2.OS === "web" && !!state.initialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" && !state.didFinishInitialScroll;
4598
+ return Platform.OS === "web" && !!state.initialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" && !state.didFinishInitialScroll;
4281
4599
  }
4282
4600
  function cloneScrollEvent(event) {
4283
4601
  return {
@@ -4323,6 +4641,7 @@ function onScroll(ctx, event) {
4323
4641
  state.scrollPending = newScroll;
4324
4642
  updateScroll(ctx, newScroll, insetChanged);
4325
4643
  trackInitialScrollNativeProgress(state, newScroll);
4644
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
4326
4645
  if (state.scrollingTo) {
4327
4646
  checkFinishedScroll(ctx);
4328
4647
  }
@@ -4386,10 +4705,47 @@ var ScrollAdjustHandler = class {
4386
4705
  }
4387
4706
  };
4388
4707
 
4708
+ // src/core/updateAnchoredEndSpace.ts
4709
+ function maybeUpdateAnchoredEndSpace(ctx) {
4710
+ var _a3;
4711
+ const state = ctx.state;
4712
+ const anchoredEndSpace = state.props.anchoredEndSpace;
4713
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
4714
+ let nextSize = 0;
4715
+ if (anchoredEndSpace) {
4716
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
4717
+ const { data } = state.props;
4718
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
4719
+ let contentBelowAnchor = 0;
4720
+ const footerSize = ctx.values.get("footerSize") || 0;
4721
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4722
+ for (let index = anchorIndex; index < data.length; index++) {
4723
+ const itemKey = getId(state, index);
4724
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
4725
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
4726
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
4727
+ contentBelowAnchor += effectiveSize;
4728
+ }
4729
+ }
4730
+ contentBelowAnchor += footerSize + stylePaddingBottom;
4731
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4732
+ }
4733
+ }
4734
+ if (previousSize === nextSize) {
4735
+ return nextSize;
4736
+ }
4737
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
4738
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4739
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
4740
+ updateScroll(ctx, state.scroll, true);
4741
+ }
4742
+ return nextSize;
4743
+ }
4744
+
4389
4745
  // src/core/updateItemSize.ts
4390
4746
  function runOrScheduleMVCPRecalculate(ctx) {
4391
4747
  const state = ctx.state;
4392
- if (Platform2.OS === "web") {
4748
+ if (Platform.OS === "web") {
4393
4749
  if (!state.mvcpAnchorLock) {
4394
4750
  if (state.queuedMVCPRecalculate !== void 0) {
4395
4751
  cancelAnimationFrame(state.queuedMVCPRecalculate);
@@ -4469,6 +4825,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4469
4825
  previous: size - diff,
4470
4826
  size
4471
4827
  });
4828
+ maybeUpdateAnchoredEndSpace(ctx);
4472
4829
  }
4473
4830
  if (minIndexSizeChanged !== void 0) {
4474
4831
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -4514,7 +4871,7 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
4514
4871
  const index = indexByKey.get(itemKey);
4515
4872
  const prevSize = getItemSize(ctx, itemKey, index, data[index]);
4516
4873
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
4517
- const size = Platform2.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
4874
+ const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
4518
4875
  const prevSizeKnown = sizesKnown.get(itemKey);
4519
4876
  sizesKnown.set(itemKey, size);
4520
4877
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
@@ -4599,26 +4956,13 @@ function createColumnWrapperStyle(contentContainerStyle) {
4599
4956
  }
4600
4957
  }
4601
4958
 
4602
- // src/utils/hasActiveMVCPAnchorLock.ts
4603
- function hasActiveMVCPAnchorLock(state) {
4604
- const lock = state.mvcpAnchorLock;
4605
- if (!lock) {
4606
- return false;
4607
- }
4608
- if (Date.now() > lock.expiresAt) {
4609
- state.mvcpAnchorLock = void 0;
4610
- return false;
4611
- }
4612
- return true;
4613
- }
4614
-
4615
4959
  // src/utils/createImperativeHandle.ts
4616
4960
  function createImperativeHandle(ctx) {
4617
4961
  const state = ctx.state;
4618
4962
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
4619
4963
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
4620
4964
  let imperativeScrollToken = 0;
4621
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
4965
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
4622
4966
  const runWhenSettled = (token, run) => {
4623
4967
  const startedAt = Date.now();
4624
4968
  let stableFrames = 0;
@@ -4640,9 +4984,10 @@ function createImperativeHandle(ctx) {
4640
4984
  };
4641
4985
  requestAnimationFrame(check);
4642
4986
  };
4643
- const runScrollWithPromise = (run) => new Promise((resolve) => {
4987
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
4644
4988
  var _a3;
4645
4989
  const token = ++imperativeScrollToken;
4990
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
4646
4991
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
4647
4992
  state.pendingScrollResolve = resolve;
4648
4993
  const runNow = () => {
@@ -4657,11 +5002,12 @@ function createImperativeHandle(ctx) {
4657
5002
  resolve();
4658
5003
  }
4659
5004
  };
5005
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
4660
5006
  if (isSettlingAfterDataChange()) {
4661
- runWhenSettled(token, runNow);
4662
- return;
5007
+ runWhenSettled(token, execute);
5008
+ } else {
5009
+ execute();
4663
5010
  }
4664
- runNow();
4665
5011
  });
4666
5012
  const scrollIndexIntoView = (options) => {
4667
5013
  if (state) {
@@ -4718,10 +5064,13 @@ function createImperativeHandle(ctx) {
4718
5064
  },
4719
5065
  end: state.endNoBuffer,
4720
5066
  endBuffered: state.endBuffered,
4721
- isAtEnd: state.isAtEnd,
4722
- isAtStart: state.isAtStart,
5067
+ isAtEnd: peek$(ctx, "isAtEnd"),
5068
+ isAtStart: peek$(ctx, "isAtStart"),
4723
5069
  isEndReached: state.isEndReached,
5070
+ isNearEnd: peek$(ctx, "isNearEnd"),
5071
+ isNearStart: peek$(ctx, "isNearStart"),
4724
5072
  isStartReached: state.isStartReached,
5073
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
4725
5074
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
4726
5075
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
4727
5076
  positionAtIndex: (index) => state.positions[index],
@@ -4768,10 +5117,15 @@ function createImperativeHandle(ctx) {
4768
5117
  }
4769
5118
  return false;
4770
5119
  }),
4771
- scrollToIndex: (params) => runScrollWithPromise(() => {
4772
- scrollToIndex(ctx, params);
4773
- return true;
4774
- }),
5120
+ scrollToIndex: (params) => runScrollWithPromise(
5121
+ () => {
5122
+ scrollToIndex(ctx, params);
5123
+ return true;
5124
+ },
5125
+ {
5126
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5127
+ }
5128
+ ),
4775
5129
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
4776
5130
  const data = state.props.data;
4777
5131
  const index = data.indexOf(item);
@@ -4867,7 +5221,7 @@ function getRenderedItem(ctx, key) {
4867
5221
  item,
4868
5222
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
4869
5223
  };
4870
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React2__default.createElement(renderItem, itemProps);
5224
+ renderedItem = React2__default.createElement(renderItem, itemProps);
4871
5225
  }
4872
5226
  return { index, item: data[index], renderedItem };
4873
5227
  }
@@ -4982,7 +5336,7 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
4982
5336
  }
4983
5337
 
4984
5338
  // src/components/LegendList.tsx
4985
- var LegendList = typedMemo2(
5339
+ var LegendList = typedMemo(
4986
5340
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
4987
5341
  typedForwardRef(function LegendList2(props, forwardedRef) {
4988
5342
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
@@ -5001,9 +5355,18 @@ var LegendList = typedMemo2(
5001
5355
  })
5002
5356
  );
5003
5357
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
5004
- var _a3, _b, _c, _d, _e, _f, _g;
5358
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5359
+ const noopOnScroll = useCallback((_event) => {
5360
+ }, []);
5361
+ if (props.recycleItems === void 0) {
5362
+ warnDevOnce(
5363
+ "recycleItems-omitted",
5364
+ "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."
5365
+ );
5366
+ }
5005
5367
  const {
5006
5368
  alignItemsAtEnd = false,
5369
+ anchoredEndSpace,
5007
5370
  alwaysRender,
5008
5371
  columnWrapperStyle,
5009
5372
  contentContainerStyle: contentContainerStyleProp,
@@ -5069,7 +5432,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5069
5432
  const positionComponentInternal = props.positionComponentInternal;
5070
5433
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
5071
5434
  const {
5072
- childrenMode,
5073
5435
  positionComponentInternal: _positionComponentInternal,
5074
5436
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
5075
5437
  ...restProps
@@ -5133,19 +5495,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5133
5495
  dataVersion,
5134
5496
  keyExtractor
5135
5497
  ]);
5136
- if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
5137
- warnDevOnce(
5138
- "stickyIndices",
5139
- "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
5140
- );
5141
- }
5142
- if (IS_DEV && useWindowScroll && renderScrollComponent) {
5143
- warnDevOnce(
5144
- "useWindowScrollRenderScrollComponent",
5145
- "useWindowScroll is not supported when renderScrollComponent is provided."
5146
- );
5147
- }
5148
- const useWindowScrollResolved = Platform2.OS === "web" && !!useWindowScroll && !renderScrollComponent;
5498
+ const useWindowScrollResolved = Platform.OS === "web" && !!useWindowScroll && !renderScrollComponent;
5149
5499
  const refState = useRef(void 0);
5150
5500
  const hasOverrideItemLayout = !!overrideItemLayout;
5151
5501
  const prevHasOverrideItemLayout = useRef(hasOverrideItemLayout);
@@ -5153,7 +5503,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5153
5503
  if (!ctx.state) {
5154
5504
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
5155
5505
  ctx.state = {
5156
- activeStickyIndex: -1,
5157
5506
  averageSizes: {},
5158
5507
  columnSpans: [],
5159
5508
  columns: [],
@@ -5177,8 +5526,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5177
5526
  kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5178
5527
  previousDataLength: dataProp.length
5179
5528
  } : void 0,
5180
- isAtEnd: false,
5181
- isAtStart: false,
5182
5529
  isEndReached: null,
5183
5530
  isFirst: true,
5184
5531
  isStartReached: null,
@@ -5189,6 +5536,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5189
5536
  minIndexSizeChanged: 0,
5190
5537
  nativeContentInset: void 0,
5191
5538
  nativeMarginTop: 0,
5539
+ pendingDataComparison: void 0,
5192
5540
  pendingNativeMVCPAdjust: void 0,
5193
5541
  positions: [],
5194
5542
  props: {},
@@ -5227,22 +5575,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5227
5575
  }
5228
5576
  const state = refState.current;
5229
5577
  const isFirstLocal = state.isFirst;
5230
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
5578
+ const previousNumColumnsProp = state.props.numColumns;
5579
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
5231
5580
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5232
5581
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5233
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
5582
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
5583
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5584
+ clearPreservedInitialScrollTarget(state);
5585
+ }
5234
5586
  if (didDataChangeLocal) {
5235
5587
  state.dataChangeEpoch += 1;
5236
5588
  state.dataChangeNeedsScrollUpdate = true;
5237
5589
  state.didDataChange = true;
5238
5590
  state.previousData = state.props.data;
5239
5591
  }
5240
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
5592
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
5593
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
5594
+ const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
5241
5595
  state.props = {
5242
5596
  alignItemsAtEnd,
5243
5597
  alwaysRender,
5244
5598
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
5245
5599
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
5600
+ anchoredEndSpace: anchoredEndSpaceResolved,
5246
5601
  animatedProps: animatedPropsInternal,
5247
5602
  contentInset,
5248
5603
  data: dataProp,
@@ -5296,7 +5651,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5296
5651
  setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
5297
5652
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
5298
5653
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
5299
- if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
5654
+ if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
5300
5655
  if (state.scroll < 0) {
5301
5656
  paddingDiff += state.scroll;
5302
5657
  }
@@ -5318,7 +5673,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5318
5673
  return void 0;
5319
5674
  }
5320
5675
  const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);
5321
- return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform2.OS === "web" ? void 0 : resolvedOffset;
5676
+ return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform.OS === "web" ? void 0 : resolvedOffset;
5322
5677
  }, [usesBootstrapInitialScroll]);
5323
5678
  useLayoutEffect(() => {
5324
5679
  initializeInitialScrollOnMount(ctx, {
@@ -5332,16 +5687,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5332
5687
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5333
5688
  refState.current.lastBatchingAction = Date.now();
5334
5689
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
5335
- IS_DEV && !childrenMode && warnDevOnce(
5336
- "keyExtractor",
5337
- "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."
5338
- );
5339
5690
  refState.current.sizes.clear();
5340
5691
  refState.current.positions.length = 0;
5341
5692
  refState.current.totalSize = 0;
5342
5693
  set$(ctx, "totalSize", 0);
5343
5694
  }
5344
5695
  }
5696
+ if (IS_DEV) {
5697
+ useDevChecks(props);
5698
+ }
5345
5699
  useLayoutEffect(() => {
5346
5700
  handleInitialScrollDataChange(ctx, {
5347
5701
  dataLength: dataProp.length,
@@ -5351,6 +5705,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5351
5705
  useBootstrapInitialScroll: usesBootstrapInitialScroll
5352
5706
  });
5353
5707
  }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
5708
+ useLayoutEffect(() => {
5709
+ maybeUpdateAnchoredEndSpace(ctx);
5710
+ }, [
5711
+ ctx,
5712
+ dataProp,
5713
+ dataVersion,
5714
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
5715
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
5716
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
5717
+ numColumnsProp
5718
+ ]);
5354
5719
  const onLayoutFooter = useCallback(
5355
5720
  (layout) => {
5356
5721
  if (!usesBootstrapInitialScroll) {
@@ -5366,14 +5731,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5366
5731
  [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5367
5732
  );
5368
5733
  const onLayoutChange = useCallback(
5369
- (layout) => {
5734
+ (layout, fromLayoutEffect) => {
5735
+ const previousScrollLength = state.scrollLength;
5736
+ const previousOtherAxisSize = state.otherAxisSize;
5370
5737
  handleLayout(ctx, layout, setCanRender);
5738
+ maybeUpdateAnchoredEndSpace(ctx);
5739
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
5740
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
5741
+ handleBootstrapInitialScrollLayoutChange(ctx);
5742
+ }
5371
5743
  if (usesBootstrapInitialScroll) {
5372
5744
  return;
5373
5745
  }
5374
5746
  advanceCurrentInitialScrollSession(ctx);
5375
5747
  },
5376
- [usesBootstrapInitialScroll]
5748
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5377
5749
  );
5378
5750
  const { onLayout } = useOnLayoutSync({
5379
5751
  onLayoutChange,
@@ -5386,6 +5758,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5386
5758
  updateSnapToOffsets(ctx);
5387
5759
  }
5388
5760
  }, [snapToIndices]);
5761
+ useLayoutEffect(
5762
+ () => initializeStateVars(true),
5763
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5764
+ );
5389
5765
  useLayoutEffect(() => {
5390
5766
  const {
5391
5767
  didColumnsChange,
@@ -5395,7 +5771,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5395
5771
  } = state;
5396
5772
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5397
5773
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5398
- checkResetContainers(ctx, data);
5774
+ checkResetContainers(ctx, data, { didColumnsChange });
5775
+ }
5776
+ if (didDataChange) {
5777
+ state.pendingDataComparison = void 0;
5399
5778
  }
5400
5779
  state.didColumnsChange = false;
5401
5780
  state.didDataChange = false;
@@ -5410,10 +5789,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5410
5789
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5411
5790
  }
5412
5791
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5413
- useLayoutEffect(
5414
- () => initializeStateVars(true),
5415
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5416
- );
5417
5792
  useEffect(() => {
5418
5793
  if (!onMetricsChange) {
5419
5794
  return;
@@ -5446,20 +5821,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5446
5821
  state.viewabilityConfigCallbackPairs = viewability;
5447
5822
  state.enableScrollForNextCalculateItemsInView = !viewability;
5448
5823
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
5449
- if (!IsNewArchitecture) {
5450
- useInit(() => {
5824
+ useInit(() => {
5825
+ if (!IsNewArchitecture) {
5451
5826
  doInitialAllocateContainers(ctx);
5452
- });
5453
- }
5827
+ }
5828
+ });
5454
5829
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5455
- if (Platform2.OS === "web") {
5456
- useEffect(() => {
5457
- if (usesBootstrapInitialScroll) {
5458
- return;
5459
- }
5460
- advanceCurrentInitialScrollSession(ctx);
5461
- }, [usesBootstrapInitialScroll]);
5462
- }
5830
+ useEffect(() => {
5831
+ if (Platform.OS !== "web" || usesBootstrapInitialScroll) {
5832
+ return;
5833
+ }
5834
+ advanceCurrentInitialScrollSession(ctx);
5835
+ }, [ctx, usesBootstrapInitialScroll]);
5463
5836
  const fns = useMemo(
5464
5837
  () => ({
5465
5838
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5497,7 +5870,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5497
5870
  onScroll: onScrollHandler,
5498
5871
  recycleItems,
5499
5872
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2.cloneElement(refreshControlElement, {
5500
- progressViewOffset: ((_f = refreshControlElement.props.progressViewOffset) != null ? _f : 0) + stylePaddingTopState
5873
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
5501
5874
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2.createElement(
5502
5875
  RefreshControl,
5503
5876
  {
@@ -5508,7 +5881,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5508
5881
  ),
5509
5882
  refScrollView: combinedRef,
5510
5883
  renderScrollComponent,
5511
- scrollAdjustHandler: (_g = refState.current) == null ? void 0 : _g.scrollAdjustHandler,
5884
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
5512
5885
  scrollEventThrottle: 0,
5513
5886
  snapToIndices,
5514
5887
  stickyHeaderIndices,
@@ -5527,4 +5900,4 @@ if (IS_DEV) {
5527
5900
  );
5528
5901
  }
5529
5902
 
5530
- export { LegendList3 as LegendList, typedForwardRef, typedMemo2 as typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
5903
+ export { LegendList3 as LegendList, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };