@legendapp/list 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react-native.js CHANGED
@@ -2002,18 +2002,29 @@ function shouldFinishInitialZeroTargetScroll(ctx) {
2002
2002
  const { state } = ctx;
2003
2003
  return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
2004
2004
  }
2005
- function getResolvedScrollCompletionState(ctx, scrollingTo) {
2005
+ function isEndAlignedLastItemTarget(ctx, scrollingTo) {
2006
+ return scrollingTo.index === ctx.state.props.data.length - 1 && scrollingTo.viewPosition === 1;
2007
+ }
2008
+ function getCurrentTargetOffset(ctx, scrollingTo) {
2006
2009
  var _a3;
2010
+ const index = scrollingTo.index;
2011
+ const shouldRecomputeEndTarget = isEndAlignedLastItemTarget(ctx, scrollingTo);
2012
+ const requestedTargetOffset = shouldRecomputeEndTarget && index !== void 0 ? calculateOffsetWithOffsetPosition(ctx, calculateOffsetForIndex(ctx, index), scrollingTo) : (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
2013
+ return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
2014
+ }
2015
+ function getResolvedScrollCompletionState(ctx, scrollingTo) {
2007
2016
  const { state } = ctx;
2008
2017
  const scroll = state.scrollPending;
2009
2018
  const adjust = state.scrollAdjustHandler.getAdjust();
2010
- const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
2019
+ const clampedTargetOffset = getCurrentTargetOffset(ctx, scrollingTo);
2011
2020
  const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
2012
2021
  const diff1 = Math.abs(scroll - clampedTargetOffset);
2013
- const diff2 = Math.abs(diff1 - adjust);
2022
+ const adjustedTargetOffset = clampedTargetOffset + adjust;
2023
+ const diff2 = Math.abs(scroll - adjustedTargetOffset);
2024
+ const canUseAdjustedCompletion = !scrollingTo.animated || Platform.OS === "ios";
2014
2025
  return {
2015
2026
  clampedTargetOffset,
2016
- isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || !scrollingTo.animated && diff2 < 1)
2027
+ isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
2017
2028
  };
2018
2029
  }
2019
2030
  function checkFinishedScrollFrame(ctx) {
@@ -2070,6 +2081,7 @@ function checkFinishedScrollFallback(ctx) {
2070
2081
  const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
2071
2082
  const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
2072
2083
  const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2084
+ const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2073
2085
  if (shouldRetrySilentInitialNativeScroll) {
2074
2086
  const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
2075
2087
  const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
@@ -2079,6 +2091,9 @@ function checkFinishedScrollFallback(ctx) {
2079
2091
  scrollToFallbackOffset(ctx, targetOffset);
2080
2092
  });
2081
2093
  scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
2094
+ } else if (shouldRetryUnalignedEndScroll) {
2095
+ scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
2096
+ scheduleFallbackCheck(100);
2082
2097
  } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
2083
2098
  finishScrollTo(ctx);
2084
2099
  } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
@@ -2121,733 +2136,1162 @@ function doScrollTo(ctx, params) {
2121
2136
  }
2122
2137
  }
2123
2138
 
2124
- // src/core/scrollTo.ts
2125
- function getAverageSizeSnapshot(state) {
2126
- if (Object.keys(state.averageSizes).length === 0) {
2127
- return void 0;
2139
+ // src/core/doMaintainScrollAtEnd.ts
2140
+ function doMaintainScrollAtEnd(ctx) {
2141
+ const state = ctx.state;
2142
+ const {
2143
+ didContainersLayout,
2144
+ pendingNativeMVCPAdjust,
2145
+ refScroller,
2146
+ props: { maintainScrollAtEnd }
2147
+ } = state;
2148
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2149
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
2150
+ if (pendingNativeMVCPAdjust) {
2151
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
2152
+ return false;
2128
2153
  }
2129
- const snapshot = {};
2130
- for (const itemType in state.averageSizes) {
2131
- const averages = state.averageSizes[itemType];
2132
- snapshot[itemType] = averages.avg;
2154
+ state.pendingMaintainScrollAtEnd = false;
2155
+ if (shouldMaintainScrollAtEnd) {
2156
+ const contentSize = getContentSize(ctx);
2157
+ if (contentSize < state.scrollLength) {
2158
+ state.scroll = 0;
2159
+ }
2160
+ if (!state.maintainingScrollAtEnd) {
2161
+ state.maintainingScrollAtEnd = true;
2162
+ requestAnimationFrame(() => {
2163
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
2164
+ const scroller = refScroller.current;
2165
+ if (state.props.horizontal && isHorizontalRTL(state)) {
2166
+ const currentContentSize = getContentSize(ctx);
2167
+ const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
2168
+ const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
2169
+ scroller == null ? void 0 : scroller.scrollTo({
2170
+ animated: maintainScrollAtEnd.animated,
2171
+ x: nativeOffset,
2172
+ y: 0
2173
+ });
2174
+ } else {
2175
+ scroller == null ? void 0 : scroller.scrollToEnd({
2176
+ animated: maintainScrollAtEnd.animated
2177
+ });
2178
+ }
2179
+ setTimeout(
2180
+ () => {
2181
+ state.maintainingScrollAtEnd = false;
2182
+ },
2183
+ maintainScrollAtEnd.animated ? 500 : 0
2184
+ );
2185
+ } else {
2186
+ state.maintainingScrollAtEnd = false;
2187
+ }
2188
+ });
2189
+ }
2190
+ return true;
2133
2191
  }
2134
- return snapshot;
2192
+ return false;
2135
2193
  }
2136
- function syncInitialScrollNativeWatchdog(state, options) {
2137
- var _a3;
2138
- const { isInitialScroll, requestedOffset, targetOffset } = options;
2139
- const existingWatchdog = initialScrollWatchdog.get(state);
2140
- const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!existingWatchdog) && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
2141
- const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!existingWatchdog && initialScrollWatchdog.isAtZeroTargetOffset(requestedOffset);
2142
- if (shouldWatchInitialNativeScroll) {
2143
- state.hasScrolled = false;
2144
- initialScrollWatchdog.set(state, {
2145
- startScroll: (_a3 = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _a3 : state.scroll,
2146
- targetOffset
2147
- });
2148
- return;
2149
- }
2150
- if (shouldClearInitialNativeScrollWatchdog) {
2151
- initialScrollWatchdog.clear(state);
2194
+
2195
+ // src/utils/requestAdjust.ts
2196
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2197
+ const state = ctx.state;
2198
+ if (Math.abs(positionDiff) > 0.1) {
2199
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2200
+ const doit = () => {
2201
+ if (needsScrollWorkaround) {
2202
+ scrollTo(ctx, {
2203
+ noScrollingTo: true,
2204
+ offset: state.scroll
2205
+ });
2206
+ } else {
2207
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2208
+ if (state.adjustingFromInitialMount) {
2209
+ state.adjustingFromInitialMount--;
2210
+ }
2211
+ }
2212
+ };
2213
+ state.scroll += positionDiff;
2214
+ state.scrollForNextCalculateItemsInView = void 0;
2215
+ const readyToRender = peek$(ctx, "readyToRender");
2216
+ if (readyToRender) {
2217
+ doit();
2218
+ if (Platform.OS !== "web") {
2219
+ const threshold = state.scroll - positionDiff / 2;
2220
+ if (!state.ignoreScrollFromMVCP) {
2221
+ state.ignoreScrollFromMVCP = {};
2222
+ }
2223
+ if (positionDiff > 0) {
2224
+ state.ignoreScrollFromMVCP.lt = threshold;
2225
+ } else {
2226
+ state.ignoreScrollFromMVCP.gt = threshold;
2227
+ }
2228
+ if (state.ignoreScrollFromMVCPTimeout) {
2229
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2230
+ }
2231
+ const delay = needsScrollWorkaround ? 250 : 100;
2232
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2233
+ var _a3;
2234
+ state.ignoreScrollFromMVCP = void 0;
2235
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2236
+ if (shouldForceUpdate) {
2237
+ state.ignoreScrollFromMVCPIgnored = false;
2238
+ state.scrollPending = state.scroll;
2239
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2240
+ }
2241
+ }, delay);
2242
+ }
2243
+ } else {
2244
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2245
+ requestAnimationFrame(doit);
2246
+ }
2152
2247
  }
2153
2248
  }
2154
- function scrollTo(ctx, params) {
2155
- var _a3;
2156
- const state = ctx.state;
2157
- const { noScrollingTo, forceScroll, ...scrollTarget } = params;
2158
- const {
2159
- animated,
2160
- isInitialScroll,
2161
- offset: scrollTargetOffset,
2162
- precomputedWithViewOffset,
2163
- waitForInitialScrollCompletionFrame
2164
- } = scrollTarget;
2165
- const {
2166
- props: { horizontal }
2167
- } = state;
2168
- if (state.animFrameCheckFinishedScroll) {
2169
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2249
+
2250
+ // src/core/mvcp.ts
2251
+ var MVCP_POSITION_EPSILON = 0.1;
2252
+ var MVCP_ANCHOR_LOCK_TTL_MS = 300;
2253
+ var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
2254
+ var NATIVE_END_CLAMP_EPSILON = 1;
2255
+ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
2256
+ if (!enableMVCPAnchorLock) {
2257
+ state.mvcpAnchorLock = void 0;
2258
+ return void 0;
2170
2259
  }
2171
- if (state.timeoutCheckFinishedScrollFallback) {
2172
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2260
+ const lock = state.mvcpAnchorLock;
2261
+ if (!lock) {
2262
+ return void 0;
2173
2263
  }
2174
- const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
2175
- const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2176
- const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
2177
- const offset = shouldPreserveRawInitialOffsetRequest ? requestedOffset : targetOffset;
2178
- state.scrollHistory.length = 0;
2179
- if (!noScrollingTo) {
2180
- if (isInitialScroll) {
2181
- initialScrollCompletion.resetFlags(state);
2264
+ const isExpired = now > lock.expiresAt;
2265
+ const isMissing = state.indexByKey.get(lock.id) === void 0;
2266
+ if (isExpired || isMissing || !mvcpData) {
2267
+ state.mvcpAnchorLock = void 0;
2268
+ return void 0;
2269
+ }
2270
+ return lock;
2271
+ }
2272
+ function updateAnchorLock(state, params) {
2273
+ if (Platform.OS === "web") {
2274
+ const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
2275
+ const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
2276
+ const mvcpData = state.props.maintainVisibleContentPosition.data;
2277
+ if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
2278
+ return;
2182
2279
  }
2183
- const averageSizeSnapshot = getAverageSizeSnapshot(state);
2184
- state.scrollingTo = {
2185
- ...scrollTarget,
2186
- ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
2187
- targetOffset,
2188
- waitForInitialScrollCompletionFrame
2280
+ const existingLock = state.mvcpAnchorLock;
2281
+ const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
2282
+ if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
2283
+ state.mvcpAnchorLock = void 0;
2284
+ return;
2285
+ }
2286
+ state.mvcpAnchorLock = {
2287
+ expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
2288
+ id: anchorId,
2289
+ position: anchorPosition,
2290
+ quietPasses
2189
2291
  };
2190
2292
  }
2191
- state.scrollPending = targetOffset;
2192
- syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2193
- if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2194
- doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
2195
- } else {
2196
- state.scroll = offset;
2293
+ }
2294
+ function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
2295
+ if (!dataChanged || Platform.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
2296
+ return false;
2197
2297
  }
2298
+ const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
2299
+ return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
2198
2300
  }
2199
-
2200
- // src/core/scrollToIndex.ts
2201
- function clampScrollIndex(index, dataLength) {
2202
- if (dataLength <= 0) {
2203
- return -1;
2301
+ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
2302
+ if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
2303
+ return 0;
2204
2304
  }
2205
- if (index >= dataLength) {
2206
- return dataLength - 1;
2305
+ const maxScroll = Math.max(0, totalSize - state.scrollLength);
2306
+ const clampDelta = maxScroll - state.scroll;
2307
+ if (unresolvedAmount < 0) {
2308
+ return Math.max(unresolvedAmount, Math.min(0, clampDelta));
2207
2309
  }
2208
- if (index < 0) {
2209
- return 0;
2310
+ if (unresolvedAmount > 0) {
2311
+ return Math.min(unresolvedAmount, Math.max(0, clampDelta));
2210
2312
  }
2211
- return index;
2313
+ return 0;
2212
2314
  }
2213
- function scrollToIndex(ctx, {
2214
- index,
2215
- viewOffset = 0,
2216
- animated = true,
2217
- forceScroll,
2218
- isInitialScroll,
2219
- viewPosition
2220
- }) {
2315
+ function getProgressTowardAmount(targetDelta, nativeDelta) {
2316
+ return targetDelta < 0 ? -nativeDelta : nativeDelta;
2317
+ }
2318
+ function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
2221
2319
  const state = ctx.state;
2222
- const { data } = state.props;
2223
- index = clampScrollIndex(index, data.length);
2224
- const itemSize = getItemSizeAtIndex(ctx, index);
2225
- const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2226
- const isLast = index === data.length - 1;
2227
- if (isLast && viewPosition === void 0) {
2228
- viewPosition = 1;
2229
- }
2230
- state.scrollForNextCalculateItemsInView = void 0;
2231
- scrollTo(ctx, {
2232
- animated,
2233
- forceScroll,
2234
- index,
2235
- isInitialScroll,
2236
- itemSize,
2237
- offset: firstIndexOffset,
2238
- viewOffset,
2239
- viewPosition: viewPosition != null ? viewPosition : 0
2240
- });
2241
- }
2242
-
2243
- // src/core/initialScroll.ts
2244
- function dispatchInitialScroll(ctx, params) {
2245
- const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2246
- const requestedIndex = target.index;
2247
- const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2248
- const itemSize = getItemSizeAtIndex(ctx, index);
2249
- scrollTo(ctx, {
2250
- animated: false,
2251
- forceScroll,
2252
- index: index !== void 0 && index >= 0 ? index : void 0,
2253
- isInitialScroll: true,
2254
- itemSize,
2255
- offset: resolvedOffset,
2256
- precomputedWithViewOffset: true,
2257
- viewOffset: target.viewOffset,
2258
- viewPosition: target.viewPosition,
2259
- waitForInitialScrollCompletionFrame: waitForCompletionFrame
2260
- });
2261
- }
2262
- function setInitialScrollTarget(state, target, options) {
2263
- var _a3;
2264
- state.clearPreservedInitialScrollOnNextFinish = void 0;
2265
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
2266
- clearTimeout(state.timeoutPreservedInitialScrollClear);
2267
- state.timeoutPreservedInitialScrollClear = void 0;
2268
- }
2269
- state.initialScroll = target;
2270
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2271
- state.didFinishInitialScroll = false;
2320
+ state.pendingNativeMVCPAdjust = void 0;
2321
+ const remaining = remainingAfterManual - nativeDelta;
2322
+ if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
2323
+ requestAdjust(ctx, remaining, true);
2272
2324
  }
2273
- setInitialScrollSession(state, {
2274
- kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2275
- });
2276
2325
  }
2277
- function resolveInitialScrollOffset(ctx, initialScroll) {
2278
- var _a3, _b;
2326
+ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
2279
2327
  const state = ctx.state;
2280
- if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2281
- return (_b = initialScroll.contentOffset) != null ? _b : 0;
2328
+ const pending = state.pendingNativeMVCPAdjust;
2329
+ if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
2330
+ return;
2282
2331
  }
2283
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2284
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2285
- return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2286
- }
2287
- function getAdvanceableInitialScrollState(state, options) {
2288
- const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
2289
- const initialScroll = state.initialScroll;
2290
- const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
2291
- const shouldWaitForInitialLayout = !!(options == null ? void 0 : options.requiresMeasuredLayout) && !queuedInitialLayout && !isInitialScrollInProgress;
2292
- if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll || scrollingTo && !isInitialScrollInProgress) {
2293
- return void 0;
2332
+ const totalSize = getContentSize(ctx);
2333
+ const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
2334
+ if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
2335
+ return;
2294
2336
  }
2295
- return {
2296
- initialScroll,
2297
- isInitialScrollInProgress,
2298
- queuedInitialLayout,
2299
- scrollingTo
2300
- };
2337
+ const manualDesired = pending.amount - predictedNativeClamp;
2338
+ if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
2339
+ return;
2340
+ }
2341
+ pending.manualApplied = manualDesired;
2342
+ requestAdjust(ctx, manualDesired, true);
2343
+ pending.furthestProgressTowardAmount = 0;
2301
2344
  }
2302
- function advanceMeasuredInitialScroll(ctx, options) {
2303
- var _a3, _b, _c;
2345
+ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2304
2346
  const state = ctx.state;
2305
- const advanceableState = getAdvanceableInitialScrollState(state, {
2306
- requiresMeasuredLayout: true
2307
- });
2308
- if (!advanceableState) {
2347
+ const pending = state.pendingNativeMVCPAdjust;
2348
+ if (!pending) {
2309
2349
  return false;
2310
2350
  }
2311
- const { initialScroll, isInitialScrollInProgress, queuedInitialLayout } = advanceableState;
2312
- const scrollingTo = isInitialScrollInProgress ? advanceableState.scrollingTo : void 0;
2313
- const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2314
- const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2315
- const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2316
- const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2317
- const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2318
- if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2319
- return false;
2351
+ const remainingAfterManual = pending.amount - pending.manualApplied;
2352
+ const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
2353
+ const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
2354
+ const progressTowardAmount = getProgressTowardAmount(remainingAfterManual, nativeDelta);
2355
+ if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
2356
+ state.pendingNativeMVCPAdjust = void 0;
2357
+ return true;
2320
2358
  }
2321
- if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2359
+ if (isWrongDirection) {
2360
+ state.pendingNativeMVCPAdjust = void 0;
2322
2361
  return false;
2323
2362
  }
2324
- if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2325
- setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2363
+ if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
2364
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2365
+ return true;
2326
2366
  }
2327
- const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2328
- dispatchInitialScroll(ctx, {
2329
- forceScroll,
2330
- resolvedOffset,
2331
- target: initialScroll
2332
- });
2333
- return true;
2334
- }
2335
- function advanceOffsetInitialScroll(ctx, options) {
2336
- var _a3, _b;
2337
- const state = ctx.state;
2338
- const advanceableState = getAdvanceableInitialScrollState(state);
2339
- if (!advanceableState) {
2340
- return false;
2367
+ const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
2368
+ const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
2369
+ const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
2370
+ if (isAtExpectedNativeClamp) {
2371
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2372
+ return true;
2341
2373
  }
2342
- const { initialScroll, queuedInitialLayout } = advanceableState;
2343
- const resolvedOffset = (_a3 = initialScroll.contentOffset) != null ? _a3 : 0;
2344
- const isAlreadyAtDesiredInitialTarget = Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2345
- if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2374
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
2375
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2376
+ return true;
2377
+ }
2378
+ if (progressTowardAmount > pending.furthestProgressTowardAmount + MVCP_POSITION_EPSILON) {
2379
+ pending.furthestProgressTowardAmount = progressTowardAmount;
2346
2380
  return false;
2347
2381
  }
2348
- const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
2349
- const forceScroll = (_b = options == null ? void 0 : options.forceScroll) != null ? _b : hasMeasuredScrollLayout || !!queuedInitialLayout;
2350
- dispatchInitialScroll(ctx, {
2351
- forceScroll,
2352
- resolvedOffset,
2353
- target: initialScroll
2354
- });
2355
- return true;
2356
- }
2357
- function advanceCurrentInitialScrollSession(ctx, options) {
2358
- var _a3;
2359
- return ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? advanceOffsetInitialScroll(ctx, {
2360
- forceScroll: options == null ? void 0 : options.forceScroll
2361
- }) : advanceMeasuredInitialScroll(ctx, {
2362
- forceScroll: options == null ? void 0 : options.forceScroll
2363
- });
2364
- }
2365
-
2366
- // src/utils/checkAllSizesKnown.ts
2367
- function isNullOrUndefined2(value) {
2368
- return value === null || value === void 0;
2369
- }
2370
- function getMountedIndicesInRange(state, start, end) {
2371
- if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2372
- 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);
2382
+ if (pending.furthestProgressTowardAmount > MVCP_POSITION_EPSILON && progressTowardAmount < pending.furthestProgressTowardAmount - MVCP_POSITION_EPSILON) {
2383
+ state.pendingNativeMVCPAdjust = void 0;
2384
+ return false;
2373
2385
  }
2374
- return [];
2375
- }
2376
- function getMountedBufferedIndices(state) {
2377
- return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2378
- }
2379
- function getMountedNoBufferIndices(state) {
2380
- return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2381
- }
2382
- function checkAllSizesKnown(state, indices) {
2383
- return indices.length > 0 && indices.every((index) => {
2384
- const key = getId(state, index);
2385
- return key !== void 0 && state.sizesKnown.has(key);
2386
- });
2386
+ return false;
2387
2387
  }
2388
-
2389
- // src/utils/requestAdjust.ts
2390
- function requestAdjust(ctx, positionDiff, dataChanged) {
2388
+ function prepareMVCP(ctx, dataChanged) {
2391
2389
  const state = ctx.state;
2392
- if (Math.abs(positionDiff) > 0.1) {
2393
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2394
- const doit = () => {
2395
- if (needsScrollWorkaround) {
2396
- scrollTo(ctx, {
2397
- noScrollingTo: true,
2398
- offset: state.scroll
2399
- });
2400
- } else {
2401
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2402
- if (state.adjustingFromInitialMount) {
2403
- state.adjustingFromInitialMount--;
2390
+ const { idsInView, positions, props } = state;
2391
+ const {
2392
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
2393
+ } = props;
2394
+ const isWeb = Platform.OS === "web";
2395
+ const now = Date.now();
2396
+ const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
2397
+ const scrollingTo = state.scrollingTo;
2398
+ const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
2399
+ let prevPosition;
2400
+ let targetId;
2401
+ const idsInViewWithPositions = [];
2402
+ const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
2403
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
2404
+ const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
2405
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
2406
+ const indexByKey = state.indexByKey;
2407
+ const prevScroll = state.scroll;
2408
+ const prevTotalSize = getContentSize(ctx);
2409
+ if (shouldMVCP) {
2410
+ if (!isWeb && state.pendingNativeMVCPAdjust && scrollTarget === void 0) {
2411
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
2412
+ return void 0;
2413
+ }
2414
+ if (anchorLock && scrollTarget === void 0) {
2415
+ targetId = anchorLock.id;
2416
+ prevPosition = anchorLock.position;
2417
+ } else if (scrollTarget !== void 0) {
2418
+ if (!IsNewArchitecture && (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll)) {
2419
+ return void 0;
2420
+ }
2421
+ targetId = getId(state, scrollTarget);
2422
+ } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
2423
+ targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
2424
+ }
2425
+ if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
2426
+ for (let i = 0; i < idsInView.length; i++) {
2427
+ const id = idsInView[i];
2428
+ const index = indexByKey.get(id);
2429
+ if (index !== void 0) {
2430
+ const position = positions[index];
2431
+ if (position !== void 0) {
2432
+ idsInViewWithPositions.push({ id, position });
2433
+ }
2404
2434
  }
2405
2435
  }
2406
- };
2407
- state.scroll += positionDiff;
2408
- state.scrollForNextCalculateItemsInView = void 0;
2409
- const readyToRender = peek$(ctx, "readyToRender");
2410
- if (readyToRender) {
2411
- doit();
2412
- if (Platform.OS !== "web") {
2413
- const threshold = state.scroll - positionDiff / 2;
2414
- if (!state.ignoreScrollFromMVCP) {
2415
- state.ignoreScrollFromMVCP = {};
2436
+ }
2437
+ if (targetId !== void 0 && prevPosition === void 0) {
2438
+ const targetIndex = indexByKey.get(targetId);
2439
+ if (targetIndex !== void 0) {
2440
+ prevPosition = positions[targetIndex];
2441
+ }
2442
+ }
2443
+ return () => {
2444
+ let positionDiff = 0;
2445
+ let anchorIdForLock = anchorLock == null ? void 0 : anchorLock.id;
2446
+ let anchorPositionForLock;
2447
+ let skipTargetAnchor = false;
2448
+ const data = state.props.data;
2449
+ const shouldValidateLockedAnchor = isWeb && dataChanged && mvcpData && scrollTarget === void 0 && targetId !== void 0 && (anchorLock == null ? void 0 : anchorLock.id) === targetId && shouldRestorePosition !== void 0;
2450
+ if (shouldValidateLockedAnchor && targetId !== void 0) {
2451
+ const index = indexByKey.get(targetId);
2452
+ if (index !== void 0) {
2453
+ const item = data[index];
2454
+ skipTargetAnchor = item === void 0 || !shouldRestorePosition(item, index, data);
2455
+ if (skipTargetAnchor && (anchorLock == null ? void 0 : anchorLock.id) === targetId) {
2456
+ state.mvcpAnchorLock = void 0;
2457
+ }
2416
2458
  }
2417
- if (positionDiff > 0) {
2418
- state.ignoreScrollFromMVCP.lt = threshold;
2419
- } else {
2420
- state.ignoreScrollFromMVCP.gt = threshold;
2459
+ }
2460
+ const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (() => {
2461
+ if (targetId === void 0 || skipTargetAnchor) {
2462
+ return true;
2421
2463
  }
2422
- if (state.ignoreScrollFromMVCPTimeout) {
2423
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
2464
+ const targetIndex = indexByKey.get(targetId);
2465
+ return targetIndex === void 0 || positions[targetIndex] === void 0;
2466
+ })();
2467
+ if (shouldUseFallbackVisibleAnchor) {
2468
+ for (let i = 0; i < idsInViewWithPositions.length; i++) {
2469
+ const { id, position } = idsInViewWithPositions[i];
2470
+ const index = indexByKey.get(id);
2471
+ if (index !== void 0 && shouldRestorePosition) {
2472
+ const item = data[index];
2473
+ if (item === void 0 || !shouldRestorePosition(item, index, data)) {
2474
+ continue;
2475
+ }
2476
+ }
2477
+ const newPosition = index !== void 0 ? positions[index] : void 0;
2478
+ if (newPosition !== void 0) {
2479
+ positionDiff = newPosition - position;
2480
+ anchorIdForLock = id;
2481
+ anchorPositionForLock = newPosition;
2482
+ break;
2483
+ }
2424
2484
  }
2425
- const delay = needsScrollWorkaround ? 250 : 100;
2426
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2427
- var _a3;
2428
- state.ignoreScrollFromMVCP = void 0;
2429
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2430
- if (shouldForceUpdate) {
2431
- state.ignoreScrollFromMVCPIgnored = false;
2432
- state.scrollPending = state.scroll;
2433
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2485
+ }
2486
+ if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
2487
+ const targetIndex = indexByKey.get(targetId);
2488
+ const newPosition = targetIndex !== void 0 ? positions[targetIndex] : void 0;
2489
+ if (newPosition !== void 0) {
2490
+ const totalSize = getContentSize(ctx);
2491
+ let diff = newPosition - prevPosition;
2492
+ if (diff !== 0 && isEndAnchoredScrollTarget && state.scroll + state.scrollLength > totalSize) {
2493
+ if (diff > 0) {
2494
+ diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
2495
+ } else {
2496
+ diff = 0;
2497
+ }
2434
2498
  }
2435
- }, delay);
2499
+ positionDiff = diff;
2500
+ anchorIdForLock = targetId;
2501
+ anchorPositionForLock = newPosition;
2502
+ }
2436
2503
  }
2437
- } else {
2438
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2439
- requestAnimationFrame(doit);
2440
- }
2504
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
2505
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
2506
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
2507
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== prevSize) {
2508
+ const diff = newSize - prevSize;
2509
+ if (diff !== 0) {
2510
+ positionDiff += diff * scrollingToViewPosition;
2511
+ scrollingTo.itemSize = newSize;
2512
+ }
2513
+ }
2514
+ }
2515
+ updateAnchorLock(state, {
2516
+ anchorId: anchorIdForLock,
2517
+ anchorPosition: anchorPositionForLock,
2518
+ dataChanged,
2519
+ now,
2520
+ positionDiff
2521
+ });
2522
+ if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
2523
+ state.pendingNativeMVCPAdjust = {
2524
+ amount: positionDiff,
2525
+ furthestProgressTowardAmount: 0,
2526
+ manualApplied: 0,
2527
+ startScroll: prevScroll
2528
+ };
2529
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
2530
+ return;
2531
+ }
2532
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
2533
+ const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2534
+ if (!shouldSkipAdjustForMaintainedEnd) {
2535
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
2536
+ }
2537
+ }
2538
+ };
2441
2539
  }
2442
2540
  }
2443
2541
 
2444
- // src/core/bootstrapInitialScroll.ts
2445
- var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2446
- var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
2447
- var DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES = 24;
2448
- var BOOTSTRAP_REVEAL_ABORT_WARNING = "LegendList bootstrap initial scroll aborted after exceeding convergence bounds.";
2449
- function getBootstrapInitialScrollSession(state) {
2450
- var _a3;
2451
- return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
2452
- }
2453
- function isOffsetInitialScrollSession(state) {
2542
+ // src/platform/flushSync.native.ts
2543
+ var flushSync = (fn) => {
2544
+ fn();
2545
+ };
2546
+
2547
+ // src/core/updateScroll.ts
2548
+ function updateScroll(ctx, newScroll, forceUpdate, options) {
2454
2549
  var _a3;
2455
- return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2456
- }
2457
- function doVisibleIndicesMatch(previous, next) {
2458
- if (!previous || previous.length !== next.length) {
2459
- return false;
2550
+ const state = ctx.state;
2551
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
2552
+ const prevScroll = state.scroll;
2553
+ if ((options == null ? void 0 : options.markHasScrolled) !== false) {
2554
+ state.hasScrolled = true;
2460
2555
  }
2461
- for (let i = 0; i < previous.length; i++) {
2462
- if (previous[i] !== next[i]) {
2463
- return false;
2464
- }
2556
+ state.lastBatchingAction = Date.now();
2557
+ const currentTime = Date.now();
2558
+ const adjust = scrollAdjustHandler.getAdjust();
2559
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
2560
+ if (adjustChanged) {
2561
+ scrollHistory.length = 0;
2465
2562
  }
2466
- return true;
2467
- }
2468
- function getBootstrapRevealVisibleIndices(options) {
2469
- const { dataLength, getSize, offset, positions, scrollLength, startIndex: requestedStartIndex } = options;
2470
- const endOffset = offset + scrollLength;
2471
- const visibleIndices = [];
2472
- let index = requestedStartIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, requestedStartIndex)) : 0;
2473
- while (index > 0) {
2474
- const previousIndex = index - 1;
2475
- const previousPosition = positions[previousIndex];
2476
- if (previousPosition === void 0) {
2477
- index = previousIndex;
2478
- continue;
2479
- }
2480
- const previousSize = getSize(previousIndex);
2481
- if (previousSize === void 0) {
2482
- index = previousIndex;
2483
- continue;
2484
- }
2485
- if (previousPosition + previousSize <= offset) {
2486
- break;
2563
+ state.lastScrollAdjustForHistory = adjust;
2564
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
2565
+ if (!adjustChanged) {
2566
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
2487
2567
  }
2488
- index = previousIndex;
2489
2568
  }
2490
- for (; index < dataLength; index++) {
2491
- const position = positions[index];
2492
- if (position === void 0) {
2493
- continue;
2569
+ if (scrollHistory.length > 5) {
2570
+ scrollHistory.shift();
2571
+ }
2572
+ if (ignoreScrollFromMVCP && !scrollingTo) {
2573
+ const { lt, gt } = ignoreScrollFromMVCP;
2574
+ if (lt && newScroll < lt || gt && newScroll > gt) {
2575
+ state.ignoreScrollFromMVCPIgnored = true;
2576
+ return;
2494
2577
  }
2495
- const size = getSize(index);
2496
- if (size === void 0) {
2497
- continue;
2578
+ }
2579
+ state.scrollPrev = prevScroll;
2580
+ state.scrollPrevTime = state.scrollTime;
2581
+ state.scroll = newScroll;
2582
+ state.scrollTime = currentTime;
2583
+ const scrollDelta = Math.abs(newScroll - prevScroll);
2584
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2585
+ const scrollLength = state.scrollLength;
2586
+ const lastCalculated = state.scrollLastCalculate;
2587
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2588
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2589
+ if (shouldUpdate) {
2590
+ state.scrollLastCalculate = state.scroll;
2591
+ state.ignoreScrollFromMVCPIgnored = false;
2592
+ state.lastScrollDelta = scrollDelta;
2593
+ const runCalculateItems = () => {
2594
+ var _a4;
2595
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
2596
+ checkThresholds(ctx);
2597
+ };
2598
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
2599
+ state.mvcpAnchorLock = void 0;
2600
+ state.pendingNativeMVCPAdjust = void 0;
2601
+ state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
2602
+ if (state.queuedMVCPRecalculate !== void 0) {
2603
+ cancelAnimationFrame(state.queuedMVCPRecalculate);
2604
+ state.queuedMVCPRecalculate = void 0;
2605
+ }
2606
+ flushSync(runCalculateItems);
2607
+ } else {
2608
+ runCalculateItems();
2498
2609
  }
2499
- if (position < endOffset && position + size > offset) {
2500
- visibleIndices.push(index);
2501
- } else if (visibleIndices.length > 0 && position >= endOffset) {
2502
- break;
2610
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
2611
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
2612
+ state.pendingMaintainScrollAtEnd = false;
2613
+ doMaintainScrollAtEnd(ctx);
2503
2614
  }
2615
+ state.dataChangeNeedsScrollUpdate = false;
2616
+ state.lastScrollDelta = 0;
2504
2617
  }
2505
- return visibleIndices;
2506
- }
2507
- function shouldAbortBootstrapReveal(options) {
2508
- const {
2509
- mountFrameCount,
2510
- maxFrames = DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES,
2511
- maxPasses = DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES,
2512
- passCount
2513
- } = options;
2514
- return mountFrameCount >= maxFrames || passCount >= maxPasses;
2515
2618
  }
2516
- function abortBootstrapRevealIfNeeded(ctx, options) {
2517
- if (!shouldAbortBootstrapReveal(options)) {
2518
- return false;
2519
- }
2520
- if (IS_DEV) {
2521
- console.warn(BOOTSTRAP_REVEAL_ABORT_WARNING);
2619
+
2620
+ // src/core/scrollTo.ts
2621
+ function getAverageSizeSnapshot(state) {
2622
+ if (Object.keys(state.averageSizes).length === 0) {
2623
+ return void 0;
2522
2624
  }
2523
- abortBootstrapInitialScroll(ctx);
2524
- return true;
2625
+ const snapshot = {};
2626
+ for (const itemType in state.averageSizes) {
2627
+ const averages = state.averageSizes[itemType];
2628
+ snapshot[itemType] = averages.avg;
2629
+ }
2630
+ return snapshot;
2525
2631
  }
2526
- function clearBootstrapInitialScrollSession(state) {
2632
+ function syncInitialScrollNativeWatchdog(state, options) {
2527
2633
  var _a3;
2528
- const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2529
- const frameHandle = bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle;
2530
- if (frameHandle !== void 0 && typeof cancelAnimationFrame === "function") {
2531
- cancelAnimationFrame(frameHandle);
2634
+ const { isInitialScroll, requestedOffset, targetOffset } = options;
2635
+ const existingWatchdog = initialScrollWatchdog.get(state);
2636
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!existingWatchdog) && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
2637
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!existingWatchdog && initialScrollWatchdog.isAtZeroTargetOffset(requestedOffset);
2638
+ if (shouldWatchInitialNativeScroll) {
2639
+ state.hasScrolled = false;
2640
+ initialScrollWatchdog.set(state, {
2641
+ startScroll: (_a3 = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _a3 : state.scroll,
2642
+ targetOffset
2643
+ });
2644
+ return;
2532
2645
  }
2533
- if (bootstrapInitialScroll) {
2534
- bootstrapInitialScroll.frameHandle = void 0;
2646
+ if (shouldClearInitialNativeScrollWatchdog) {
2647
+ initialScrollWatchdog.clear(state);
2535
2648
  }
2536
- setInitialScrollSession(state, {
2537
- bootstrap: null,
2538
- kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2539
- });
2540
- }
2541
- function startBootstrapInitialScrollSession(state, options) {
2542
- var _a3, _b, _c;
2543
- const previousBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2544
- setInitialScrollSession(state, {
2545
- bootstrap: {
2546
- frameHandle: previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.frameHandle,
2547
- // Re-arming during the initial mount should spend from the same watchdog budget.
2548
- mountFrameCount: (_a3 = previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.mountFrameCount) != null ? _a3 : 0,
2549
- passCount: 0,
2550
- previousResolvedOffset: void 0,
2551
- scroll: options.scroll,
2552
- seedContentOffset: (_c = (_b = options.seedContentOffset) != null ? _b : previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.seedContentOffset) != null ? _c : options.scroll,
2553
- targetIndexSeed: options.targetIndexSeed,
2554
- visibleIndices: void 0
2555
- },
2556
- kind: "bootstrap"
2557
- });
2558
2649
  }
2559
- function resetBootstrapInitialScrollSession(state, options) {
2560
- var _a3, _b, _c;
2561
- const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2562
- if (!bootstrapInitialScroll) {
2563
- if ((options == null ? void 0 : options.scroll) !== void 0) {
2564
- startBootstrapInitialScrollSession(state, {
2565
- scroll: options.scroll,
2566
- seedContentOffset: options.seedContentOffset,
2567
- targetIndexSeed: options.targetIndexSeed
2568
- });
2650
+ function scrollTo(ctx, params) {
2651
+ var _a3;
2652
+ const state = ctx.state;
2653
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
2654
+ const {
2655
+ animated,
2656
+ isInitialScroll,
2657
+ offset: scrollTargetOffset,
2658
+ precomputedWithViewOffset,
2659
+ waitForInitialScrollCompletionFrame
2660
+ } = scrollTarget;
2661
+ const {
2662
+ props: { horizontal }
2663
+ } = state;
2664
+ if (state.animFrameCheckFinishedScroll) {
2665
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2666
+ }
2667
+ if (state.timeoutCheckFinishedScrollFallback) {
2668
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2669
+ }
2670
+ const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
2671
+ const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2672
+ const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
2673
+ const offset = shouldPreserveRawInitialOffsetRequest ? requestedOffset : targetOffset;
2674
+ state.scrollHistory.length = 0;
2675
+ if (!noScrollingTo) {
2676
+ if (isInitialScroll) {
2677
+ initialScrollCompletion.resetFlags(state);
2569
2678
  }
2679
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
2680
+ state.scrollingTo = {
2681
+ ...scrollTarget,
2682
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
2683
+ targetOffset,
2684
+ waitForInitialScrollCompletionFrame
2685
+ };
2686
+ }
2687
+ state.scrollPending = targetOffset;
2688
+ syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2689
+ if (!animated && !isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2690
+ updateScroll(ctx, targetOffset, false, { markHasScrolled: false });
2691
+ }
2692
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2693
+ doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
2570
2694
  } else {
2571
- bootstrapInitialScroll.passCount = 0;
2572
- bootstrapInitialScroll.previousResolvedOffset = void 0;
2573
- bootstrapInitialScroll.scroll = (_a3 = options == null ? void 0 : options.scroll) != null ? _a3 : bootstrapInitialScroll.scroll;
2574
- bootstrapInitialScroll.seedContentOffset = (_b = options == null ? void 0 : options.seedContentOffset) != null ? _b : bootstrapInitialScroll.seedContentOffset;
2575
- bootstrapInitialScroll.targetIndexSeed = (_c = options == null ? void 0 : options.targetIndexSeed) != null ? _c : bootstrapInitialScroll.targetIndexSeed;
2576
- bootstrapInitialScroll.visibleIndices = void 0;
2577
- setInitialScrollSession(state, {
2578
- bootstrap: bootstrapInitialScroll,
2579
- kind: "bootstrap"
2580
- });
2695
+ state.scroll = offset;
2581
2696
  }
2582
2697
  }
2583
- function queueBootstrapInitialScrollReevaluation(state) {
2584
- requestAnimationFrame(() => {
2585
- var _a3;
2586
- if (getBootstrapInitialScrollSession(state)) {
2587
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
2588
- }
2589
- });
2698
+
2699
+ // src/core/scrollToIndex.ts
2700
+ function clampScrollIndex(index, dataLength) {
2701
+ if (dataLength <= 0) {
2702
+ return -1;
2703
+ }
2704
+ if (index >= dataLength) {
2705
+ return dataLength - 1;
2706
+ }
2707
+ if (index < 0) {
2708
+ return 0;
2709
+ }
2710
+ return index;
2590
2711
  }
2591
- function ensureBootstrapInitialScrollFrameTicker(ctx) {
2712
+ function scrollToIndex(ctx, {
2713
+ index,
2714
+ viewOffset = 0,
2715
+ animated = true,
2716
+ forceScroll,
2717
+ isInitialScroll,
2718
+ viewPosition
2719
+ }) {
2592
2720
  const state = ctx.state;
2593
- const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2594
- if (!bootstrapInitialScroll || bootstrapInitialScroll.frameHandle !== void 0) {
2595
- return;
2721
+ const { data } = state.props;
2722
+ index = clampScrollIndex(index, data.length);
2723
+ const itemSize = getItemSizeAtIndex(ctx, index);
2724
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2725
+ const isLast = index === data.length - 1;
2726
+ if (isLast && viewPosition === void 0) {
2727
+ viewPosition = 1;
2596
2728
  }
2597
- const tick = () => {
2598
- const activeBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2599
- if (!activeBootstrapInitialScroll) {
2600
- return;
2601
- }
2602
- activeBootstrapInitialScroll.frameHandle = void 0;
2603
- activeBootstrapInitialScroll.mountFrameCount += 1;
2604
- if (abortBootstrapRevealIfNeeded(ctx, {
2605
- mountFrameCount: activeBootstrapInitialScroll.mountFrameCount,
2606
- passCount: activeBootstrapInitialScroll.passCount
2607
- })) {
2608
- return;
2609
- }
2610
- ensureBootstrapInitialScrollFrameTicker(ctx);
2611
- };
2612
- bootstrapInitialScroll.frameHandle = requestAnimationFrame(tick);
2729
+ state.scrollForNextCalculateItemsInView = void 0;
2730
+ scrollTo(ctx, {
2731
+ animated,
2732
+ forceScroll,
2733
+ index,
2734
+ isInitialScroll,
2735
+ itemSize,
2736
+ offset: firstIndexOffset,
2737
+ viewOffset,
2738
+ viewPosition: viewPosition != null ? viewPosition : 0
2739
+ });
2613
2740
  }
2614
- function rearmBootstrapInitialScroll(ctx, options) {
2615
- resetBootstrapInitialScrollSession(ctx.state, options);
2616
- ensureBootstrapInitialScrollFrameTicker(ctx);
2617
- queueBootstrapInitialScrollReevaluation(ctx.state);
2741
+
2742
+ // src/core/initialScroll.ts
2743
+ function dispatchInitialScroll(ctx, params) {
2744
+ const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2745
+ const requestedIndex = target.index;
2746
+ const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2747
+ const itemSize = getItemSizeAtIndex(ctx, index);
2748
+ scrollTo(ctx, {
2749
+ animated: false,
2750
+ forceScroll,
2751
+ index: index !== void 0 && index >= 0 ? index : void 0,
2752
+ isInitialScroll: true,
2753
+ itemSize,
2754
+ offset: resolvedOffset,
2755
+ precomputedWithViewOffset: true,
2756
+ viewOffset: target.viewOffset,
2757
+ viewPosition: target.viewPosition,
2758
+ waitForInitialScrollCompletionFrame: waitForCompletionFrame
2759
+ });
2618
2760
  }
2619
- function createInitialScrollAtEndTarget(options) {
2620
- const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2761
+ function setInitialScrollTarget(state, target, options) {
2762
+ var _a3;
2763
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2764
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2765
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2766
+ state.timeoutPreservedInitialScrollClear = void 0;
2767
+ }
2768
+ state.initialScroll = target;
2769
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2770
+ state.didFinishInitialScroll = false;
2771
+ }
2772
+ setInitialScrollSession(state, {
2773
+ kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2774
+ });
2775
+ }
2776
+ function resolveInitialScrollOffset(ctx, initialScroll) {
2777
+ var _a3, _b;
2778
+ const state = ctx.state;
2779
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2780
+ return (_b = initialScroll.contentOffset) != null ? _b : 0;
2781
+ }
2782
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2783
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2784
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2785
+ }
2786
+ function getAdvanceableInitialScrollState(state, options) {
2787
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
2788
+ const initialScroll = state.initialScroll;
2789
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
2790
+ const shouldWaitForInitialLayout = !!(options == null ? void 0 : options.requiresMeasuredLayout) && !queuedInitialLayout && !isInitialScrollInProgress;
2791
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll || scrollingTo && !isInitialScrollInProgress) {
2792
+ return void 0;
2793
+ }
2621
2794
  return {
2622
- contentOffset: void 0,
2623
- index: Math.max(0, dataLength - 1),
2624
- preserveForBottomPadding: true,
2625
- preserveForFooterLayout,
2626
- viewOffset: -stylePaddingBottom - footerSize,
2627
- viewPosition: 1
2795
+ initialScroll,
2796
+ isInitialScrollInProgress,
2797
+ queuedInitialLayout,
2798
+ scrollingTo
2628
2799
  };
2629
2800
  }
2630
- function shouldPreserveInitialScrollForBottomPadding(target) {
2631
- return !!(target == null ? void 0 : target.preserveForBottomPadding);
2632
- }
2633
- function shouldPreserveInitialScrollForFooterLayout(target) {
2634
- return !!(target == null ? void 0 : target.preserveForFooterLayout);
2635
- }
2636
- function isRetargetableBottomAlignedInitialScrollTarget(target) {
2637
- return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2638
- }
2639
- function createRetargetedBottomAlignedInitialScroll(options) {
2640
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2641
- const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2642
- return {
2643
- ...target,
2644
- contentOffset: void 0,
2645
- index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2646
- preserveForBottomPadding: true,
2647
- preserveForFooterLayout,
2648
- viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2649
- viewPosition: 1
2650
- };
2651
- }
2652
- function areEquivalentBootstrapInitialScrollTargets(current, next) {
2653
- return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2654
- }
2655
- function clearPendingInitialScrollFooterLayout(ctx, options) {
2656
- const { dataLength, stylePaddingBottom, target } = options;
2801
+ function advanceMeasuredInitialScroll(ctx, options) {
2802
+ var _a3, _b, _c;
2657
2803
  const state = ctx.state;
2658
- if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2659
- return;
2804
+ const advanceableState = getAdvanceableInitialScrollState(state, {
2805
+ requiresMeasuredLayout: true
2806
+ });
2807
+ if (!advanceableState) {
2808
+ return false;
2660
2809
  }
2661
- const clearedFooterTarget = createInitialScrollAtEndTarget({
2662
- dataLength,
2663
- footerSize: 0,
2664
- preserveForFooterLayout: void 0,
2665
- stylePaddingBottom
2810
+ const { initialScroll, isInitialScrollInProgress, queuedInitialLayout } = advanceableState;
2811
+ const scrollingTo = isInitialScrollInProgress ? advanceableState.scrollingTo : void 0;
2812
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2813
+ const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2814
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2815
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2816
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2817
+ if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2818
+ return false;
2819
+ }
2820
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2821
+ return false;
2822
+ }
2823
+ if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2824
+ setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2825
+ }
2826
+ const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2827
+ dispatchInitialScroll(ctx, {
2828
+ forceScroll,
2829
+ resolvedOffset,
2830
+ target: initialScroll
2666
2831
  });
2667
- setInitialScrollTarget(state, clearedFooterTarget);
2668
- }
2669
- function clearFinishedViewportRetargetableInitialScroll(state) {
2670
- clearPreservedInitialScrollTarget(state);
2832
+ return true;
2671
2833
  }
2672
- function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2834
+ function advanceOffsetInitialScroll(ctx, options) {
2835
+ var _a3, _b;
2673
2836
  const state = ctx.state;
2674
- if (!state.didFinishInitialScroll) {
2837
+ const advanceableState = getAdvanceableInitialScrollState(state);
2838
+ if (!advanceableState) {
2675
2839
  return false;
2676
2840
  }
2677
- const currentOffset = getObservedBootstrapInitialScrollOffset(state);
2678
- return Math.abs(currentOffset - resolveInitialScrollOffset(ctx, target)) > epsilon;
2679
- }
2680
- function getObservedBootstrapInitialScrollOffset(state) {
2681
- var _a3, _b, _c, _d;
2682
- const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2683
- return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2841
+ const { initialScroll, queuedInitialLayout } = advanceableState;
2842
+ const resolvedOffset = (_a3 = initialScroll.contentOffset) != null ? _a3 : 0;
2843
+ const isAlreadyAtDesiredInitialTarget = Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2844
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2845
+ return false;
2846
+ }
2847
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
2848
+ const forceScroll = (_b = options == null ? void 0 : options.forceScroll) != null ? _b : hasMeasuredScrollLayout || !!queuedInitialLayout;
2849
+ dispatchInitialScroll(ctx, {
2850
+ forceScroll,
2851
+ resolvedOffset,
2852
+ target: initialScroll
2853
+ });
2854
+ return true;
2684
2855
  }
2685
- function getPreservedEndAnchorOffsetDiff(ctx) {
2856
+ function advanceCurrentInitialScrollSession(ctx, options) {
2686
2857
  var _a3;
2687
- const state = ctx.state;
2688
- const initialScroll = state.initialScroll;
2689
- if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
2690
- return;
2858
+ return ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? advanceOffsetInitialScroll(ctx, {
2859
+ forceScroll: options == null ? void 0 : options.forceScroll
2860
+ }) : advanceMeasuredInitialScroll(ctx, {
2861
+ forceScroll: options == null ? void 0 : options.forceScroll
2862
+ });
2863
+ }
2864
+
2865
+ // src/utils/checkAllSizesKnown.ts
2866
+ function isNullOrUndefined2(value) {
2867
+ return value === null || value === void 0;
2868
+ }
2869
+ function getMountedIndicesInRange(state, start, end) {
2870
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2871
+ 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);
2691
2872
  }
2692
- const currentOffset = typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
2693
- return resolveInitialScrollOffset(ctx, initialScroll) - currentOffset;
2873
+ return [];
2694
2874
  }
2695
- function schedulePreservedEndAnchorCorrection(ctx) {
2696
- if (getPreservedEndAnchorOffsetDiff(ctx) === void 0) {
2875
+ function getMountedBufferedIndices(state) {
2876
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2877
+ }
2878
+ function getMountedNoBufferIndices(state) {
2879
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2880
+ }
2881
+ function checkAllSizesKnown(state, indices) {
2882
+ return indices.length > 0 && indices.every((index) => {
2883
+ const key = getId(state, index);
2884
+ return key !== void 0 && state.sizesKnown.has(key);
2885
+ });
2886
+ }
2887
+
2888
+ // src/core/bootstrapInitialScroll.ts
2889
+ var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2890
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
2891
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES = 24;
2892
+ var BOOTSTRAP_REVEAL_ABORT_WARNING = "LegendList bootstrap initial scroll aborted after exceeding convergence bounds.";
2893
+ function getBootstrapInitialScrollSession(state) {
2894
+ var _a3;
2895
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
2896
+ }
2897
+ function isOffsetInitialScrollSession(state) {
2898
+ var _a3;
2899
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2900
+ }
2901
+ function doVisibleIndicesMatch(previous, next) {
2902
+ if (!previous || previous.length !== next.length) {
2697
2903
  return false;
2698
2904
  }
2699
- const correction = {};
2700
- schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
2905
+ for (let i = 0; i < previous.length; i++) {
2906
+ if (previous[i] !== next[i]) {
2907
+ return false;
2908
+ }
2909
+ }
2701
2910
  return true;
2702
2911
  }
2703
- function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
2704
- const state = ctx.state;
2705
- state.preservedEndAnchorCorrection = correction;
2706
- requestAnimationFrame(() => {
2707
- var _a3;
2708
- const activeCorrection = state.preservedEndAnchorCorrection;
2709
- if (activeCorrection !== correction) {
2710
- return;
2912
+ function getBootstrapRevealVisibleIndices(options) {
2913
+ const { dataLength, getSize, offset, positions, scrollLength, startIndex: requestedStartIndex } = options;
2914
+ const endOffset = offset + scrollLength;
2915
+ const visibleIndices = [];
2916
+ let index = requestedStartIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, requestedStartIndex)) : 0;
2917
+ while (index > 0) {
2918
+ const previousIndex = index - 1;
2919
+ const previousPosition = positions[previousIndex];
2920
+ if (previousPosition === void 0) {
2921
+ index = previousIndex;
2922
+ continue;
2711
2923
  }
2712
- const offsetDiff = getPreservedEndAnchorOffsetDiff(ctx);
2713
- if (offsetDiff === void 0 || Math.abs(offsetDiff) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2714
- state.preservedEndAnchorCorrection = void 0;
2715
- return;
2924
+ const previousSize = getSize(previousIndex);
2925
+ if (previousSize === void 0) {
2926
+ index = previousIndex;
2927
+ continue;
2716
2928
  }
2717
- const hasObservedNativeScrollAfterRequest = !activeCorrection.lastRequestTime || ((_a3 = state.lastNativeScrollTime) != null ? _a3 : 0) > activeCorrection.lastRequestTime;
2718
- if (hasObservedNativeScrollAfterRequest) {
2719
- activeCorrection.lastRequestTime = Date.now();
2720
- requestAdjust(ctx, offsetDiff);
2929
+ if (previousPosition + previousSize <= offset) {
2930
+ break;
2721
2931
  }
2722
- schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
2723
- });
2724
- }
2725
- function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2726
- var _a3, _b;
2727
- const state = ctx.state;
2728
- const initialScroll = state.initialScroll;
2729
- if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1 || state.preservedEndAnchorCorrection) {
2730
- return;
2932
+ index = previousIndex;
2731
2933
  }
2732
- if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2733
- const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
2734
- if (!shouldKeepEndTargetAlive) {
2735
- if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2736
- clearPendingInitialScrollFooterLayout(ctx, {
2737
- dataLength: state.props.data.length,
2738
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2739
- target: initialScroll
2740
- });
2741
- } else {
2742
- clearFinishedViewportRetargetableInitialScroll(state);
2743
- }
2934
+ for (; index < dataLength; index++) {
2935
+ const position = positions[index];
2936
+ if (position === void 0) {
2937
+ continue;
2938
+ }
2939
+ const size = getSize(index);
2940
+ if (size === void 0) {
2941
+ continue;
2942
+ }
2943
+ if (position < endOffset && position + size > offset) {
2944
+ visibleIndices.push(index);
2945
+ } else if (visibleIndices.length > 0 && position >= endOffset) {
2946
+ break;
2744
2947
  }
2745
2948
  }
2949
+ return visibleIndices;
2746
2950
  }
2747
- function startBootstrapInitialScrollOnMount(ctx, options) {
2748
- var _a3, _b, _c;
2749
- const { initialScrollAtEnd, target } = options;
2750
- const state = ctx.state;
2751
- const offset = resolveInitialScrollOffset(ctx, target);
2752
- const shouldFinishAtOrigin = offset === 0 && !initialScrollAtEnd && (isOffsetInitialScrollSession(state) ? Math.abs((_a3 = target.contentOffset) != null ? _a3 : 0) <= 1 : target.index === 0 && ((_b = target.viewPosition) != null ? _b : 0) === 0 && Math.abs((_c = target.viewOffset) != null ? _c : 0) <= 1);
2753
- const shouldFinishWithPreservedTarget = state.props.data.length === 0 && target.index !== void 0;
2754
- if (shouldFinishAtOrigin) {
2755
- clearBootstrapInitialScrollSession(state);
2756
- finishInitialScroll(ctx, {
2757
- resolvedOffset: offset
2758
- });
2759
- } else if (shouldFinishWithPreservedTarget) {
2760
- clearBootstrapInitialScrollSession(state);
2761
- finishInitialScroll(ctx, {
2762
- preserveTarget: true,
2763
- resolvedOffset: offset
2764
- });
2765
- } else {
2766
- startBootstrapInitialScrollSession(state, {
2767
- scroll: offset,
2768
- seedContentOffset: Platform.OS === "web" ? 0 : offset,
2769
- targetIndexSeed: target.index
2770
- });
2771
- ensureBootstrapInitialScrollFrameTicker(ctx);
2772
- }
2951
+ function shouldAbortBootstrapReveal(options) {
2952
+ const {
2953
+ mountFrameCount,
2954
+ maxFrames = DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES,
2955
+ maxPasses = DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES,
2956
+ passCount
2957
+ } = options;
2958
+ return mountFrameCount >= maxFrames || passCount >= maxPasses;
2773
2959
  }
2774
- function handleBootstrapInitialScrollDataChange(ctx, options) {
2775
- const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2776
- const state = ctx.state;
2777
- const initialScroll = state.initialScroll;
2778
- if (isOffsetInitialScrollSession(state) || !initialScroll) {
2779
- return;
2960
+ function abortBootstrapRevealIfNeeded(ctx, options) {
2961
+ if (!shouldAbortBootstrapReveal(options)) {
2962
+ return false;
2780
2963
  }
2781
- const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2964
+ if (IS_DEV) {
2965
+ console.warn(BOOTSTRAP_REVEAL_ABORT_WARNING);
2966
+ }
2967
+ abortBootstrapInitialScroll(ctx);
2968
+ return true;
2969
+ }
2970
+ function clearBootstrapInitialScrollSession(state) {
2971
+ var _a3;
2782
2972
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2783
- const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2784
- if (shouldClearFinishedResizePreservation) {
2785
- clearPreservedInitialScrollTarget(state);
2786
- return;
2973
+ const frameHandle = bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle;
2974
+ if (frameHandle !== void 0 && typeof cancelAnimationFrame === "function") {
2975
+ cancelAnimationFrame(frameHandle);
2787
2976
  }
2788
- const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2789
- if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2790
- return;
2977
+ if (bootstrapInitialScroll) {
2978
+ bootstrapInitialScroll.frameHandle = void 0;
2791
2979
  }
2792
- if (shouldRetargetBottomAligned) {
2793
- const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2794
- dataLength,
2795
- footerSize: peek$(ctx, "footerSize") || 0,
2796
- preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2797
- stylePaddingBottom
2798
- }) : createRetargetedBottomAlignedInitialScroll({
2799
- dataLength,
2800
- footerSize: peek$(ctx, "footerSize") || 0,
2801
- initialScrollAtEnd,
2802
- stylePaddingBottom,
2803
- target: initialScroll
2804
- });
2805
- if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2806
- clearPendingInitialScrollFooterLayout(ctx, {
2807
- dataLength,
2808
- stylePaddingBottom,
2809
- target: initialScroll
2980
+ setInitialScrollSession(state, {
2981
+ bootstrap: null,
2982
+ kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2983
+ });
2984
+ }
2985
+ function startBootstrapInitialScrollSession(state, options) {
2986
+ var _a3, _b, _c;
2987
+ const previousBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2988
+ setInitialScrollSession(state, {
2989
+ bootstrap: {
2990
+ frameHandle: previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.frameHandle,
2991
+ // Re-arming during the initial mount should spend from the same watchdog budget.
2992
+ mountFrameCount: (_a3 = previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.mountFrameCount) != null ? _a3 : 0,
2993
+ passCount: 0,
2994
+ previousResolvedOffset: void 0,
2995
+ scroll: options.scroll,
2996
+ seedContentOffset: (_c = (_b = options.seedContentOffset) != null ? _b : previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.seedContentOffset) != null ? _c : options.scroll,
2997
+ targetIndexSeed: options.targetIndexSeed,
2998
+ visibleIndices: void 0
2999
+ },
3000
+ kind: "bootstrap"
3001
+ });
3002
+ }
3003
+ function resetBootstrapInitialScrollSession(state, options) {
3004
+ var _a3, _b, _c;
3005
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3006
+ if (!bootstrapInitialScroll) {
3007
+ if ((options == null ? void 0 : options.scroll) !== void 0) {
3008
+ startBootstrapInitialScrollSession(state, {
3009
+ scroll: options.scroll,
3010
+ seedContentOffset: options.seedContentOffset,
3011
+ targetIndexSeed: options.targetIndexSeed
2810
3012
  });
3013
+ }
3014
+ } else {
3015
+ bootstrapInitialScroll.passCount = 0;
3016
+ bootstrapInitialScroll.previousResolvedOffset = void 0;
3017
+ bootstrapInitialScroll.scroll = (_a3 = options == null ? void 0 : options.scroll) != null ? _a3 : bootstrapInitialScroll.scroll;
3018
+ bootstrapInitialScroll.seedContentOffset = (_b = options == null ? void 0 : options.seedContentOffset) != null ? _b : bootstrapInitialScroll.seedContentOffset;
3019
+ bootstrapInitialScroll.targetIndexSeed = (_c = options == null ? void 0 : options.targetIndexSeed) != null ? _c : bootstrapInitialScroll.targetIndexSeed;
3020
+ bootstrapInitialScroll.visibleIndices = void 0;
3021
+ setInitialScrollSession(state, {
3022
+ bootstrap: bootstrapInitialScroll,
3023
+ kind: "bootstrap"
3024
+ });
3025
+ }
3026
+ }
3027
+ function queueBootstrapInitialScrollReevaluation(state) {
3028
+ requestAnimationFrame(() => {
3029
+ var _a3;
3030
+ if (getBootstrapInitialScrollSession(state)) {
3031
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
3032
+ }
3033
+ });
3034
+ }
3035
+ function ensureBootstrapInitialScrollFrameTicker(ctx) {
3036
+ const state = ctx.state;
3037
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3038
+ if (!bootstrapInitialScroll || bootstrapInitialScroll.frameHandle !== void 0) {
3039
+ return;
3040
+ }
3041
+ const tick = () => {
3042
+ const activeBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3043
+ if (!activeBootstrapInitialScroll) {
2811
3044
  return;
2812
3045
  }
2813
- if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2814
- setInitialScrollTarget(state, updatedInitialScroll, {
2815
- resetDidFinish: shouldResetDidFinish
2816
- });
2817
- rearmBootstrapInitialScroll(ctx, {
2818
- scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2819
- seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2820
- targetIndexSeed: updatedInitialScroll.index
2821
- });
3046
+ activeBootstrapInitialScroll.frameHandle = void 0;
3047
+ activeBootstrapInitialScroll.mountFrameCount += 1;
3048
+ if (abortBootstrapRevealIfNeeded(ctx, {
3049
+ mountFrameCount: activeBootstrapInitialScroll.mountFrameCount,
3050
+ passCount: activeBootstrapInitialScroll.passCount
3051
+ })) {
2822
3052
  return;
2823
3053
  }
2824
- }
2825
- if (!didDataChange) {
3054
+ ensureBootstrapInitialScrollFrameTicker(ctx);
3055
+ };
3056
+ bootstrapInitialScroll.frameHandle = requestAnimationFrame(tick);
3057
+ }
3058
+ function rearmBootstrapInitialScroll(ctx, options) {
3059
+ resetBootstrapInitialScrollSession(ctx.state, options);
3060
+ ensureBootstrapInitialScrollFrameTicker(ctx);
3061
+ queueBootstrapInitialScrollReevaluation(ctx.state);
3062
+ }
3063
+ function createInitialScrollAtEndTarget(options) {
3064
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
3065
+ return {
3066
+ contentOffset: void 0,
3067
+ index: Math.max(0, dataLength - 1),
3068
+ preserveForBottomPadding: true,
3069
+ preserveForFooterLayout,
3070
+ viewOffset: -stylePaddingBottom - footerSize,
3071
+ viewPosition: 1
3072
+ };
3073
+ }
3074
+ function shouldPreserveInitialScrollForBottomPadding(target) {
3075
+ return !!(target == null ? void 0 : target.preserveForBottomPadding);
3076
+ }
3077
+ function shouldPreserveInitialScrollForFooterLayout(target) {
3078
+ return !!(target == null ? void 0 : target.preserveForFooterLayout);
3079
+ }
3080
+ function isRetargetableBottomAlignedInitialScrollTarget(target) {
3081
+ return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
3082
+ }
3083
+ function createRetargetedBottomAlignedInitialScroll(options) {
3084
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
3085
+ const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
3086
+ return {
3087
+ ...target,
3088
+ contentOffset: void 0,
3089
+ index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
3090
+ preserveForBottomPadding: true,
3091
+ preserveForFooterLayout,
3092
+ viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
3093
+ viewPosition: 1
3094
+ };
3095
+ }
3096
+ function areEquivalentBootstrapInitialScrollTargets(current, next) {
3097
+ return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
3098
+ }
3099
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
3100
+ const { dataLength, stylePaddingBottom, target } = options;
3101
+ const state = ctx.state;
3102
+ if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2826
3103
  return;
2827
3104
  }
2828
- if (bootstrapInitialScroll || shouldResetDidFinish) {
2829
- setInitialScrollTarget(state, initialScroll, {
2830
- resetDidFinish: shouldResetDidFinish
2831
- });
2832
- rearmBootstrapInitialScroll(ctx, {
2833
- scroll: resolveInitialScrollOffset(ctx, initialScroll),
2834
- seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2835
- targetIndexSeed: initialScroll.index
2836
- });
2837
- }
3105
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
3106
+ dataLength,
3107
+ footerSize: 0,
3108
+ preserveForFooterLayout: void 0,
3109
+ stylePaddingBottom
3110
+ });
3111
+ setInitialScrollTarget(state, clearedFooterTarget);
2838
3112
  }
2839
- function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2840
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
3113
+ function clearFinishedViewportRetargetableInitialScroll(state) {
3114
+ clearPreservedInitialScrollTarget(state);
3115
+ }
3116
+ function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2841
3117
  const state = ctx.state;
2842
- if (!initialScrollAtEnd) {
2843
- return;
3118
+ if (!state.didFinishInitialScroll) {
3119
+ return false;
2844
3120
  }
3121
+ const currentOffset = getObservedBootstrapInitialScrollOffset(state);
3122
+ return Math.abs(currentOffset - resolveInitialScrollOffset(ctx, target)) > epsilon;
3123
+ }
3124
+ function getObservedBootstrapInitialScrollOffset(state) {
3125
+ var _a3, _b, _c, _d;
3126
+ const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
3127
+ return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
3128
+ }
3129
+ function getPreservedEndAnchorOffsetDiff(ctx) {
3130
+ var _a3;
3131
+ const state = ctx.state;
2845
3132
  const initialScroll = state.initialScroll;
2846
- if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
3133
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
2847
3134
  return;
2848
3135
  }
2849
- const shouldProcessFooterLayout = !!getBootstrapInitialScrollSession(state) || shouldPreserveInitialScrollForFooterLayout(initialScroll);
2850
- if (!shouldProcessFooterLayout) {
3136
+ const currentOffset = typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
3137
+ return resolveInitialScrollOffset(ctx, initialScroll) - currentOffset;
3138
+ }
3139
+ function schedulePreservedEndAnchorCorrection(ctx) {
3140
+ if (getPreservedEndAnchorOffsetDiff(ctx) === void 0) {
3141
+ return false;
3142
+ }
3143
+ const correction = {};
3144
+ schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
3145
+ return true;
3146
+ }
3147
+ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
3148
+ const state = ctx.state;
3149
+ state.preservedEndAnchorCorrection = correction;
3150
+ requestAnimationFrame(() => {
3151
+ var _a3;
3152
+ const activeCorrection = state.preservedEndAnchorCorrection;
3153
+ if (activeCorrection !== correction) {
3154
+ return;
3155
+ }
3156
+ const offsetDiff = getPreservedEndAnchorOffsetDiff(ctx);
3157
+ if (offsetDiff === void 0 || Math.abs(offsetDiff) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3158
+ state.preservedEndAnchorCorrection = void 0;
3159
+ return;
3160
+ }
3161
+ const hasObservedNativeScrollAfterRequest = !activeCorrection.lastRequestTime || ((_a3 = state.lastNativeScrollTime) != null ? _a3 : 0) > activeCorrection.lastRequestTime;
3162
+ if (hasObservedNativeScrollAfterRequest) {
3163
+ activeCorrection.lastRequestTime = Date.now();
3164
+ requestAdjust(ctx, offsetDiff);
3165
+ }
3166
+ schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
3167
+ });
3168
+ }
3169
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
3170
+ var _a3, _b;
3171
+ const state = ctx.state;
3172
+ const initialScroll = state.initialScroll;
3173
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1 || state.preservedEndAnchorCorrection) {
3174
+ return;
3175
+ }
3176
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3177
+ const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
3178
+ if (!shouldKeepEndTargetAlive) {
3179
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
3180
+ clearPendingInitialScrollFooterLayout(ctx, {
3181
+ dataLength: state.props.data.length,
3182
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
3183
+ target: initialScroll
3184
+ });
3185
+ } else {
3186
+ clearFinishedViewportRetargetableInitialScroll(state);
3187
+ }
3188
+ }
3189
+ }
3190
+ }
3191
+ function startBootstrapInitialScrollOnMount(ctx, options) {
3192
+ var _a3, _b, _c;
3193
+ const { initialScrollAtEnd, target } = options;
3194
+ const state = ctx.state;
3195
+ const offset = resolveInitialScrollOffset(ctx, target);
3196
+ const shouldFinishAtOrigin = offset === 0 && !initialScrollAtEnd && (isOffsetInitialScrollSession(state) ? Math.abs((_a3 = target.contentOffset) != null ? _a3 : 0) <= 1 : target.index === 0 && ((_b = target.viewPosition) != null ? _b : 0) === 0 && Math.abs((_c = target.viewOffset) != null ? _c : 0) <= 1);
3197
+ const shouldFinishWithPreservedTarget = state.props.data.length === 0 && target.index !== void 0;
3198
+ if (shouldFinishAtOrigin) {
3199
+ clearBootstrapInitialScrollSession(state);
3200
+ finishInitialScroll(ctx, {
3201
+ resolvedOffset: offset
3202
+ });
3203
+ } else if (shouldFinishWithPreservedTarget) {
3204
+ clearBootstrapInitialScrollSession(state);
3205
+ finishInitialScroll(ctx, {
3206
+ preserveTarget: true,
3207
+ resolvedOffset: offset
3208
+ });
3209
+ } else {
3210
+ startBootstrapInitialScrollSession(state, {
3211
+ scroll: offset,
3212
+ seedContentOffset: Platform.OS === "web" ? 0 : offset,
3213
+ targetIndexSeed: target.index
3214
+ });
3215
+ ensureBootstrapInitialScrollFrameTicker(ctx);
3216
+ }
3217
+ }
3218
+ function handleBootstrapInitialScrollDataChange(ctx, options) {
3219
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
3220
+ const state = ctx.state;
3221
+ const initialScroll = state.initialScroll;
3222
+ if (isOffsetInitialScrollSession(state) || !initialScroll) {
3223
+ return;
3224
+ }
3225
+ const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
3226
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3227
+ const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
3228
+ if (shouldClearFinishedResizePreservation) {
3229
+ clearPreservedInitialScrollTarget(state);
3230
+ return;
3231
+ }
3232
+ const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
3233
+ if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
3234
+ return;
3235
+ }
3236
+ if (shouldRetargetBottomAligned) {
3237
+ const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
3238
+ dataLength,
3239
+ footerSize: peek$(ctx, "footerSize") || 0,
3240
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3241
+ stylePaddingBottom
3242
+ }) : createRetargetedBottomAlignedInitialScroll({
3243
+ dataLength,
3244
+ footerSize: peek$(ctx, "footerSize") || 0,
3245
+ initialScrollAtEnd,
3246
+ stylePaddingBottom,
3247
+ target: initialScroll
3248
+ });
3249
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3250
+ clearPendingInitialScrollFooterLayout(ctx, {
3251
+ dataLength,
3252
+ stylePaddingBottom,
3253
+ target: initialScroll
3254
+ });
3255
+ return;
3256
+ }
3257
+ if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
3258
+ setInitialScrollTarget(state, updatedInitialScroll, {
3259
+ resetDidFinish: shouldResetDidFinish
3260
+ });
3261
+ rearmBootstrapInitialScroll(ctx, {
3262
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
3263
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3264
+ targetIndexSeed: updatedInitialScroll.index
3265
+ });
3266
+ return;
3267
+ }
3268
+ }
3269
+ if (!didDataChange) {
3270
+ return;
3271
+ }
3272
+ if (bootstrapInitialScroll || shouldResetDidFinish) {
3273
+ setInitialScrollTarget(state, initialScroll, {
3274
+ resetDidFinish: shouldResetDidFinish
3275
+ });
3276
+ rearmBootstrapInitialScroll(ctx, {
3277
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
3278
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3279
+ targetIndexSeed: initialScroll.index
3280
+ });
3281
+ }
3282
+ }
3283
+ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3284
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
3285
+ const state = ctx.state;
3286
+ if (!initialScrollAtEnd) {
3287
+ return;
3288
+ }
3289
+ const initialScroll = state.initialScroll;
3290
+ if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
3291
+ return;
3292
+ }
3293
+ const shouldProcessFooterLayout = !!getBootstrapInitialScrollSession(state) || shouldPreserveInitialScrollForFooterLayout(initialScroll);
3294
+ if (!shouldProcessFooterLayout) {
2851
3295
  return;
2852
3296
  }
2853
3297
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
@@ -3006,405 +3450,113 @@ function abortBootstrapInitialScroll(ctx) {
3006
3450
  const state = ctx.state;
3007
3451
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3008
3452
  const initialScroll = state.initialScroll;
3009
- if (bootstrapInitialScroll && initialScroll && !isOffsetInitialScrollSession(state) && state.refScroller.current) {
3010
- clearBootstrapInitialScrollSession(state);
3011
- dispatchInitialScroll(ctx, {
3012
- forceScroll: true,
3013
- resolvedOffset: bootstrapInitialScroll.scroll,
3014
- target: initialScroll,
3015
- waitForCompletionFrame: Platform.OS === "web"
3016
- });
3017
- } else {
3018
- finishBootstrapInitialScrollWithoutScroll(
3019
- ctx,
3020
- (_d = (_c = (_b = (_a3 = getBootstrapInitialScrollSession(state)) == null ? void 0 : _a3.scroll) != null ? _b : state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0
3021
- );
3022
- }
3023
- }
3024
-
3025
- // src/core/initialScrollLifecycle.ts
3026
- function retargetActiveInitialScrollAtEnd(ctx) {
3027
- var _a3;
3028
- const state = ctx.state;
3029
- const initialScroll = state.initialScroll;
3030
- if (state.didFinishInitialScroll) {
3031
- return schedulePreservedEndAnchorCorrection(ctx);
3032
- }
3033
- if (!initialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
3034
- return false;
3035
- }
3036
- return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3037
- }
3038
- function handleInitialScrollLayoutReady(ctx) {
3039
- var _a3;
3040
- if (!ctx.state.initialScroll) {
3041
- return;
3042
- }
3043
- const runScroll = () => advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3044
- runScroll();
3045
- if (((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset") {
3046
- requestAnimationFrame(runScroll);
3047
- }
3048
- checkFinishedScroll(ctx, { onlyIfAligned: true });
3049
- }
3050
- function initializeInitialScrollOnMount(ctx, options) {
3051
- var _a3, _b;
3052
- const {
3053
- alwaysDispatchInitialScroll,
3054
- dataLength,
3055
- hasFooterComponent,
3056
- initialContentOffset,
3057
- initialScrollAtEnd,
3058
- useBootstrapInitialScroll
3059
- } = options;
3060
- const state = ctx.state;
3061
- const initialScroll = state.initialScroll;
3062
- const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
3063
- const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
3064
- if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
3065
- setInitialScrollTarget(state, {
3066
- ...initialScroll,
3067
- contentOffset: resolvedInitialContentOffset,
3068
- preserveForFooterLayout
3069
- });
3070
- }
3071
- if (useBootstrapInitialScroll && initialScroll && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
3072
- startBootstrapInitialScrollOnMount(ctx, {
3073
- initialScrollAtEnd,
3074
- target: state.initialScroll
3075
- });
3076
- return;
3077
- }
3078
- const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
3079
- if (!alwaysDispatchInitialScroll && !resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
3080
- if (initialScroll && !initialScrollAtEnd) {
3081
- finishInitialScroll(ctx, {
3082
- resolvedOffset: resolvedInitialContentOffset
3083
- });
3084
- } else {
3085
- setInitialRenderState(ctx, { didInitialScroll: true });
3086
- }
3087
- }
3088
- }
3089
- function handleInitialScrollDataChange(ctx, options) {
3090
- var _a3, _b, _c;
3091
- const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
3092
- const state = ctx.state;
3093
- const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3094
- if (state.initialScrollSession) {
3095
- state.initialScrollSession.previousDataLength = dataLength;
3096
- }
3097
- setInitialScrollSession(state);
3098
- if (useBootstrapInitialScroll) {
3099
- handleBootstrapInitialScrollDataChange(ctx, {
3100
- dataLength,
3101
- didDataChange,
3102
- initialScrollAtEnd,
3103
- previousDataLength,
3104
- stylePaddingBottom
3105
- });
3106
- return;
3107
- }
3108
- const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3109
- if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3110
- return;
3111
- }
3112
- if (shouldReplayFinishedOffsetInitialScroll) {
3113
- state.didFinishInitialScroll = false;
3114
- }
3115
- advanceCurrentInitialScrollSession(ctx);
3116
- }
3117
-
3118
- // src/core/mvcp.ts
3119
- var MVCP_POSITION_EPSILON = 0.1;
3120
- var MVCP_ANCHOR_LOCK_TTL_MS = 300;
3121
- var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
3122
- var NATIVE_END_CLAMP_EPSILON = 1;
3123
- function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
3124
- if (!enableMVCPAnchorLock) {
3125
- state.mvcpAnchorLock = void 0;
3126
- return void 0;
3127
- }
3128
- const lock = state.mvcpAnchorLock;
3129
- if (!lock) {
3130
- return void 0;
3131
- }
3132
- const isExpired = now > lock.expiresAt;
3133
- const isMissing = state.indexByKey.get(lock.id) === void 0;
3134
- if (isExpired || isMissing || !mvcpData) {
3135
- state.mvcpAnchorLock = void 0;
3136
- return void 0;
3137
- }
3138
- return lock;
3139
- }
3140
- function updateAnchorLock(state, params) {
3141
- if (Platform.OS === "web") {
3142
- const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
3143
- const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
3144
- const mvcpData = state.props.maintainVisibleContentPosition.data;
3145
- if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
3146
- return;
3147
- }
3148
- const existingLock = state.mvcpAnchorLock;
3149
- const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
3150
- if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
3151
- state.mvcpAnchorLock = void 0;
3152
- return;
3153
- }
3154
- state.mvcpAnchorLock = {
3155
- expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
3156
- id: anchorId,
3157
- position: anchorPosition,
3158
- quietPasses
3159
- };
3160
- }
3161
- }
3162
- function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
3163
- if (!dataChanged || Platform.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
3164
- return false;
3165
- }
3166
- const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
3167
- return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
3168
- }
3169
- function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
3170
- if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
3171
- return 0;
3172
- }
3173
- const maxScroll = Math.max(0, totalSize - state.scrollLength);
3174
- const clampDelta = maxScroll - state.scroll;
3175
- if (unresolvedAmount < 0) {
3176
- return Math.max(unresolvedAmount, Math.min(0, clampDelta));
3177
- }
3178
- if (unresolvedAmount > 0) {
3179
- return Math.min(unresolvedAmount, Math.max(0, clampDelta));
3180
- }
3181
- return 0;
3182
- }
3183
- function getProgressTowardAmount(targetDelta, nativeDelta) {
3184
- return targetDelta < 0 ? -nativeDelta : nativeDelta;
3185
- }
3186
- function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
3187
- const state = ctx.state;
3188
- state.pendingNativeMVCPAdjust = void 0;
3189
- const remaining = remainingAfterManual - nativeDelta;
3190
- if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
3191
- requestAdjust(ctx, remaining, true);
3192
- }
3193
- }
3194
- function maybeApplyPredictedNativeMVCPAdjust(ctx) {
3195
- const state = ctx.state;
3196
- const pending = state.pendingNativeMVCPAdjust;
3197
- if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
3198
- return;
3199
- }
3200
- const totalSize = getContentSize(ctx);
3201
- const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
3202
- if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
3203
- return;
3204
- }
3205
- const manualDesired = pending.amount - predictedNativeClamp;
3206
- if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
3207
- return;
3208
- }
3209
- pending.manualApplied = manualDesired;
3210
- requestAdjust(ctx, manualDesired, true);
3211
- pending.furthestProgressTowardAmount = 0;
3212
- }
3213
- function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
3214
- const state = ctx.state;
3215
- const pending = state.pendingNativeMVCPAdjust;
3216
- if (!pending) {
3217
- return false;
3218
- }
3219
- const remainingAfterManual = pending.amount - pending.manualApplied;
3220
- const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
3221
- const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
3222
- const progressTowardAmount = getProgressTowardAmount(remainingAfterManual, nativeDelta);
3223
- if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
3224
- state.pendingNativeMVCPAdjust = void 0;
3225
- return true;
3226
- }
3227
- if (isWrongDirection) {
3228
- state.pendingNativeMVCPAdjust = void 0;
3229
- return false;
3230
- }
3231
- if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
3232
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3233
- return true;
3234
- }
3235
- const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
3236
- const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
3237
- const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
3238
- if (isAtExpectedNativeClamp) {
3239
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3240
- return true;
3241
- }
3242
- if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
3243
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3244
- return true;
3245
- }
3246
- if (progressTowardAmount > pending.furthestProgressTowardAmount + MVCP_POSITION_EPSILON) {
3247
- pending.furthestProgressTowardAmount = progressTowardAmount;
3248
- return false;
3249
- }
3250
- if (pending.furthestProgressTowardAmount > MVCP_POSITION_EPSILON && progressTowardAmount < pending.furthestProgressTowardAmount - MVCP_POSITION_EPSILON) {
3251
- state.pendingNativeMVCPAdjust = void 0;
3252
- return false;
3253
- }
3254
- return false;
3255
- }
3256
- function prepareMVCP(ctx, dataChanged) {
3257
- const state = ctx.state;
3258
- const { idsInView, positions, props } = state;
3259
- const {
3260
- maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
3261
- } = props;
3262
- const isWeb = Platform.OS === "web";
3263
- const now = Date.now();
3264
- const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
3265
- const scrollingTo = state.scrollingTo;
3266
- const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
3267
- let prevPosition;
3268
- let targetId;
3269
- const idsInViewWithPositions = [];
3270
- const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
3271
- const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
3272
- const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
3273
- const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
3274
- const indexByKey = state.indexByKey;
3275
- const prevScroll = state.scroll;
3276
- const prevTotalSize = getContentSize(ctx);
3277
- if (shouldMVCP) {
3278
- if (!isWeb && state.pendingNativeMVCPAdjust && scrollTarget === void 0) {
3279
- maybeApplyPredictedNativeMVCPAdjust(ctx);
3280
- return void 0;
3281
- }
3282
- if (anchorLock && scrollTarget === void 0) {
3283
- targetId = anchorLock.id;
3284
- prevPosition = anchorLock.position;
3285
- } else if (scrollTarget !== void 0) {
3286
- if (!IsNewArchitecture && (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll)) {
3287
- return void 0;
3288
- }
3289
- targetId = getId(state, scrollTarget);
3290
- } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
3291
- targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
3292
- }
3293
- if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
3294
- for (let i = 0; i < idsInView.length; i++) {
3295
- const id = idsInView[i];
3296
- const index = indexByKey.get(id);
3297
- if (index !== void 0) {
3298
- const position = positions[index];
3299
- if (position !== void 0) {
3300
- idsInViewWithPositions.push({ id, position });
3301
- }
3302
- }
3303
- }
3304
- }
3305
- if (targetId !== void 0 && prevPosition === void 0) {
3306
- const targetIndex = indexByKey.get(targetId);
3307
- if (targetIndex !== void 0) {
3308
- prevPosition = positions[targetIndex];
3309
- }
3310
- }
3311
- return () => {
3312
- let positionDiff = 0;
3313
- let anchorIdForLock = anchorLock == null ? void 0 : anchorLock.id;
3314
- let anchorPositionForLock;
3315
- let skipTargetAnchor = false;
3316
- const data = state.props.data;
3317
- const shouldValidateLockedAnchor = isWeb && dataChanged && mvcpData && scrollTarget === void 0 && targetId !== void 0 && (anchorLock == null ? void 0 : anchorLock.id) === targetId && shouldRestorePosition !== void 0;
3318
- if (shouldValidateLockedAnchor && targetId !== void 0) {
3319
- const index = indexByKey.get(targetId);
3320
- if (index !== void 0) {
3321
- const item = data[index];
3322
- skipTargetAnchor = item === void 0 || !shouldRestorePosition(item, index, data);
3323
- if (skipTargetAnchor && (anchorLock == null ? void 0 : anchorLock.id) === targetId) {
3324
- state.mvcpAnchorLock = void 0;
3325
- }
3326
- }
3327
- }
3328
- const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (() => {
3329
- if (targetId === void 0 || skipTargetAnchor) {
3330
- return true;
3331
- }
3332
- const targetIndex = indexByKey.get(targetId);
3333
- return targetIndex === void 0 || positions[targetIndex] === void 0;
3334
- })();
3335
- if (shouldUseFallbackVisibleAnchor) {
3336
- for (let i = 0; i < idsInViewWithPositions.length; i++) {
3337
- const { id, position } = idsInViewWithPositions[i];
3338
- const index = indexByKey.get(id);
3339
- if (index !== void 0 && shouldRestorePosition) {
3340
- const item = data[index];
3341
- if (item === void 0 || !shouldRestorePosition(item, index, data)) {
3342
- continue;
3343
- }
3344
- }
3345
- const newPosition = index !== void 0 ? positions[index] : void 0;
3346
- if (newPosition !== void 0) {
3347
- positionDiff = newPosition - position;
3348
- anchorIdForLock = id;
3349
- anchorPositionForLock = newPosition;
3350
- break;
3351
- }
3352
- }
3353
- }
3354
- if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
3355
- const targetIndex = indexByKey.get(targetId);
3356
- const newPosition = targetIndex !== void 0 ? positions[targetIndex] : void 0;
3357
- if (newPosition !== void 0) {
3358
- const totalSize = getContentSize(ctx);
3359
- let diff = newPosition - prevPosition;
3360
- if (diff !== 0 && isEndAnchoredScrollTarget && state.scroll + state.scrollLength > totalSize) {
3361
- if (diff > 0) {
3362
- diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
3363
- } else {
3364
- diff = 0;
3365
- }
3366
- }
3367
- positionDiff = diff;
3368
- anchorIdForLock = targetId;
3369
- anchorPositionForLock = newPosition;
3370
- }
3371
- }
3372
- if (scrollingToViewPosition && scrollingToViewPosition > 0) {
3373
- const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
3374
- const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
3375
- if (newSize !== void 0 && prevSize !== void 0 && newSize !== prevSize) {
3376
- const diff = newSize - prevSize;
3377
- if (diff !== 0) {
3378
- positionDiff += diff * scrollingToViewPosition;
3379
- scrollingTo.itemSize = newSize;
3380
- }
3381
- }
3382
- }
3383
- updateAnchorLock(state, {
3384
- anchorId: anchorIdForLock,
3385
- anchorPosition: anchorPositionForLock,
3386
- dataChanged,
3387
- now,
3388
- positionDiff
3453
+ if (bootstrapInitialScroll && initialScroll && !isOffsetInitialScrollSession(state) && state.refScroller.current) {
3454
+ clearBootstrapInitialScrollSession(state);
3455
+ dispatchInitialScroll(ctx, {
3456
+ forceScroll: true,
3457
+ resolvedOffset: bootstrapInitialScroll.scroll,
3458
+ target: initialScroll,
3459
+ waitForCompletionFrame: Platform.OS === "web"
3460
+ });
3461
+ } else {
3462
+ finishBootstrapInitialScrollWithoutScroll(
3463
+ ctx,
3464
+ (_d = (_c = (_b = (_a3 = getBootstrapInitialScrollSession(state)) == null ? void 0 : _a3.scroll) != null ? _b : state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0
3465
+ );
3466
+ }
3467
+ }
3468
+
3469
+ // src/core/initialScrollLifecycle.ts
3470
+ function retargetActiveInitialScrollAtEnd(ctx) {
3471
+ var _a3;
3472
+ const state = ctx.state;
3473
+ const initialScroll = state.initialScroll;
3474
+ if (state.didFinishInitialScroll) {
3475
+ return schedulePreservedEndAnchorCorrection(ctx);
3476
+ }
3477
+ if (!initialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
3478
+ return false;
3479
+ }
3480
+ return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3481
+ }
3482
+ function handleInitialScrollLayoutReady(ctx) {
3483
+ var _a3;
3484
+ if (!ctx.state.initialScroll) {
3485
+ return;
3486
+ }
3487
+ const runScroll = () => advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3488
+ runScroll();
3489
+ if (((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset") {
3490
+ requestAnimationFrame(runScroll);
3491
+ }
3492
+ checkFinishedScroll(ctx, { onlyIfAligned: true });
3493
+ }
3494
+ function initializeInitialScrollOnMount(ctx, options) {
3495
+ var _a3, _b;
3496
+ const {
3497
+ alwaysDispatchInitialScroll,
3498
+ dataLength,
3499
+ hasFooterComponent,
3500
+ initialContentOffset,
3501
+ initialScrollAtEnd,
3502
+ useBootstrapInitialScroll
3503
+ } = options;
3504
+ const state = ctx.state;
3505
+ const initialScroll = state.initialScroll;
3506
+ const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
3507
+ const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
3508
+ if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
3509
+ setInitialScrollTarget(state, {
3510
+ ...initialScroll,
3511
+ contentOffset: resolvedInitialContentOffset,
3512
+ preserveForFooterLayout
3513
+ });
3514
+ }
3515
+ if (useBootstrapInitialScroll && initialScroll && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
3516
+ startBootstrapInitialScrollOnMount(ctx, {
3517
+ initialScrollAtEnd,
3518
+ target: state.initialScroll
3519
+ });
3520
+ return;
3521
+ }
3522
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
3523
+ if (!alwaysDispatchInitialScroll && !resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
3524
+ if (initialScroll && !initialScrollAtEnd) {
3525
+ finishInitialScroll(ctx, {
3526
+ resolvedOffset: resolvedInitialContentOffset
3389
3527
  });
3390
- if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
3391
- state.pendingNativeMVCPAdjust = {
3392
- amount: positionDiff,
3393
- furthestProgressTowardAmount: 0,
3394
- manualApplied: 0,
3395
- startScroll: prevScroll
3396
- };
3397
- maybeApplyPredictedNativeMVCPAdjust(ctx);
3398
- return;
3399
- }
3400
- if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3401
- const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
3402
- if (!shouldSkipAdjustForMaintainedEnd) {
3403
- requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3404
- }
3405
- }
3406
- };
3528
+ } else {
3529
+ setInitialRenderState(ctx, { didInitialScroll: true });
3530
+ }
3531
+ }
3532
+ }
3533
+ function handleInitialScrollDataChange(ctx, options) {
3534
+ var _a3, _b, _c;
3535
+ const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
3536
+ const state = ctx.state;
3537
+ const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3538
+ if (state.initialScrollSession) {
3539
+ state.initialScrollSession.previousDataLength = dataLength;
3540
+ }
3541
+ setInitialScrollSession(state);
3542
+ if (useBootstrapInitialScroll) {
3543
+ handleBootstrapInitialScrollDataChange(ctx, {
3544
+ dataLength,
3545
+ didDataChange,
3546
+ initialScrollAtEnd,
3547
+ previousDataLength,
3548
+ stylePaddingBottom
3549
+ });
3550
+ return;
3551
+ }
3552
+ const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3553
+ if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3554
+ return;
3555
+ }
3556
+ if (shouldReplayFinishedOffsetInitialScroll) {
3557
+ state.didFinishInitialScroll = false;
3407
3558
  }
3559
+ advanceCurrentInitialScrollSession(ctx);
3408
3560
  }
3409
3561
 
3410
3562
  // src/core/resetLayoutCachesForDataChange.ts
@@ -4690,62 +4842,6 @@ function calculateItemsInView(ctx, params = {}) {
4690
4842
  });
4691
4843
  }
4692
4844
 
4693
- // src/core/doMaintainScrollAtEnd.ts
4694
- function doMaintainScrollAtEnd(ctx) {
4695
- const state = ctx.state;
4696
- const {
4697
- didContainersLayout,
4698
- pendingNativeMVCPAdjust,
4699
- refScroller,
4700
- props: { maintainScrollAtEnd }
4701
- } = state;
4702
- const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4703
- const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4704
- if (pendingNativeMVCPAdjust) {
4705
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4706
- return false;
4707
- }
4708
- state.pendingMaintainScrollAtEnd = false;
4709
- if (shouldMaintainScrollAtEnd) {
4710
- const contentSize = getContentSize(ctx);
4711
- if (contentSize < state.scrollLength) {
4712
- state.scroll = 0;
4713
- }
4714
- if (!state.maintainingScrollAtEnd) {
4715
- state.maintainingScrollAtEnd = true;
4716
- requestAnimationFrame(() => {
4717
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4718
- const scroller = refScroller.current;
4719
- if (state.props.horizontal && isHorizontalRTL(state)) {
4720
- const currentContentSize = getContentSize(ctx);
4721
- const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
4722
- const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
4723
- scroller == null ? void 0 : scroller.scrollTo({
4724
- animated: maintainScrollAtEnd.animated,
4725
- x: nativeOffset,
4726
- y: 0
4727
- });
4728
- } else {
4729
- scroller == null ? void 0 : scroller.scrollToEnd({
4730
- animated: maintainScrollAtEnd.animated
4731
- });
4732
- }
4733
- setTimeout(
4734
- () => {
4735
- state.maintainingScrollAtEnd = false;
4736
- },
4737
- maintainScrollAtEnd.animated ? 500 : 0
4738
- );
4739
- } else {
4740
- state.maintainingScrollAtEnd = false;
4741
- }
4742
- });
4743
- }
4744
- return true;
4745
- }
4746
- return false;
4747
- }
4748
-
4749
4845
  // src/core/checkResetContainers.ts
4750
4846
  function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
4751
4847
  const state = ctx.state;
@@ -4926,84 +5022,6 @@ function handleLayout(ctx, layoutParam, setCanRender) {
4926
5022
  setCanRender(true);
4927
5023
  }
4928
5024
 
4929
- // src/platform/flushSync.native.ts
4930
- var flushSync = (fn) => {
4931
- fn();
4932
- };
4933
-
4934
- // src/core/updateScroll.ts
4935
- function updateScroll(ctx, newScroll, forceUpdate, options) {
4936
- var _a3;
4937
- const state = ctx.state;
4938
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
4939
- const prevScroll = state.scroll;
4940
- if ((options == null ? void 0 : options.markHasScrolled) !== false) {
4941
- state.hasScrolled = true;
4942
- }
4943
- state.lastBatchingAction = Date.now();
4944
- const currentTime = Date.now();
4945
- const adjust = scrollAdjustHandler.getAdjust();
4946
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
4947
- if (adjustChanged) {
4948
- scrollHistory.length = 0;
4949
- }
4950
- state.lastScrollAdjustForHistory = adjust;
4951
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
4952
- if (!adjustChanged) {
4953
- scrollHistory.push({ scroll: newScroll, time: currentTime });
4954
- }
4955
- }
4956
- if (scrollHistory.length > 5) {
4957
- scrollHistory.shift();
4958
- }
4959
- if (ignoreScrollFromMVCP && !scrollingTo) {
4960
- const { lt, gt } = ignoreScrollFromMVCP;
4961
- if (lt && newScroll < lt || gt && newScroll > gt) {
4962
- state.ignoreScrollFromMVCPIgnored = true;
4963
- return;
4964
- }
4965
- }
4966
- state.scrollPrev = prevScroll;
4967
- state.scrollPrevTime = state.scrollTime;
4968
- state.scroll = newScroll;
4969
- state.scrollTime = currentTime;
4970
- const scrollDelta = Math.abs(newScroll - prevScroll);
4971
- const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
4972
- const scrollLength = state.scrollLength;
4973
- const lastCalculated = state.scrollLastCalculate;
4974
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
4975
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
4976
- if (shouldUpdate) {
4977
- state.scrollLastCalculate = state.scroll;
4978
- state.ignoreScrollFromMVCPIgnored = false;
4979
- state.lastScrollDelta = scrollDelta;
4980
- const runCalculateItems = () => {
4981
- var _a4;
4982
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
4983
- checkThresholds(ctx);
4984
- };
4985
- if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
4986
- state.mvcpAnchorLock = void 0;
4987
- state.pendingNativeMVCPAdjust = void 0;
4988
- state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
4989
- if (state.queuedMVCPRecalculate !== void 0) {
4990
- cancelAnimationFrame(state.queuedMVCPRecalculate);
4991
- state.queuedMVCPRecalculate = void 0;
4992
- }
4993
- flushSync(runCalculateItems);
4994
- } else {
4995
- runCalculateItems();
4996
- }
4997
- const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
4998
- if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
4999
- state.pendingMaintainScrollAtEnd = false;
5000
- doMaintainScrollAtEnd(ctx);
5001
- }
5002
- state.dataChangeNeedsScrollUpdate = false;
5003
- state.lastScrollDelta = 0;
5004
- }
5005
- }
5006
-
5007
5025
  // src/core/onScroll.ts
5008
5026
  function trackInitialScrollNativeProgress(state, newScroll) {
5009
5027
  const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);