@legendapp/list 3.1.1 → 3.2.0

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
@@ -165,7 +165,7 @@ function StateProvider({ children }) {
165
165
  ["isNearEnd", false],
166
166
  ["isNearStart", false],
167
167
  ["isWithinMaintainScrollAtEndThreshold", false],
168
- ["adaptiveRender", "light"],
168
+ ["adaptiveRender", "normal"],
169
169
  ["totalSize", 0],
170
170
  ["scrollAdjustPending", 0]
171
171
  ]),
@@ -779,563 +779,564 @@ function Separator({ ItemSeparatorComponent, leadingItem }) {
779
779
  const isLastItem = useIsLastItem();
780
780
  return isLastItem ? null : /* @__PURE__ */ React2__namespace.createElement(ItemSeparatorComponent, { leadingItem });
781
781
  }
782
- var PixelRatio = ReactNative.PixelRatio;
783
782
 
784
- // src/utils/layoutMeasurement.ts
785
- var FLOATING_POINT_SLACK = 0.01;
786
- var NATIVE_LAYOUT_MEASUREMENT_EPSILON = 1 / PixelRatio.get() + FLOATING_POINT_SLACK;
787
- function isWithinEpsilon(delta) {
788
- return Math.abs(delta) <= NATIVE_LAYOUT_MEASUREMENT_EPSILON;
789
- }
790
- function isNativeLayoutNoise(delta) {
791
- return isWithinEpsilon(delta);
792
- }
793
- function isNativeLayoutSizeNoise(heightDelta, widthDelta) {
794
- return isWithinEpsilon(heightDelta) && isWithinEpsilon(widthDelta);
783
+ // src/core/deferredPublicOnScroll.ts
784
+ function withResolvedContentOffset(state, event, resolvedOffset) {
785
+ return {
786
+ ...event,
787
+ nativeEvent: {
788
+ ...event.nativeEvent,
789
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
790
+ }
791
+ };
795
792
  }
796
-
797
- // src/hooks/useOnLayoutSync.native.tsx
798
- function useOnLayoutSync({
799
- ref,
800
- onLayoutProp,
801
- onLayoutChange
802
- }, deps = []) {
803
- const lastLayoutRef = React2.useRef(null);
804
- const onLayout = React2.useCallback(
805
- (event) => {
806
- const { layout } = event.nativeEvent;
807
- const lastLayout = lastLayoutRef.current;
808
- const didLayoutSizeChange = lastLayout && (layout.height !== lastLayout.height || layout.width !== lastLayout.width);
809
- const isMeasuredLayoutNoise = !!didLayoutSizeChange && !!lastLayout.measuredLayout && isNativeLayoutSizeNoise(
810
- layout.height - lastLayout.measuredLayout.height,
811
- layout.width - lastLayout.measuredLayout.width
812
- );
813
- if (!lastLayout || didLayoutSizeChange && !isMeasuredLayoutNoise) {
814
- onLayoutChange(layout, false);
815
- }
816
- if (!lastLayout || didLayoutSizeChange) {
817
- lastLayoutRef.current = {
818
- ...layout,
819
- measuredLayout: isMeasuredLayoutNoise ? lastLayout == null ? void 0 : lastLayout.measuredLayout : void 0
820
- };
821
- }
822
- onLayoutProp == null ? void 0 : onLayoutProp(event);
823
- },
824
- [onLayoutChange, onLayoutProp]
825
- );
826
- if (IsNewArchitecture) {
827
- React2.useLayoutEffect(() => {
828
- if (ref.current) {
829
- ref.current.measure((x, y, width, height) => {
830
- const layout = { height, width, x, y };
831
- lastLayoutRef.current = { ...layout, measuredLayout: layout };
832
- onLayoutChange(layout, true);
833
- });
834
- }
835
- }, deps);
793
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
794
+ var _a3, _b, _c, _d;
795
+ const state = ctx.state;
796
+ const deferredEvent = state.deferredPublicOnScrollEvent;
797
+ state.deferredPublicOnScrollEvent = void 0;
798
+ if (deferredEvent) {
799
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
800
+ _c,
801
+ withResolvedContentOffset(
802
+ state,
803
+ deferredEvent,
804
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
805
+ )
806
+ );
836
807
  }
837
- return { onLayout };
838
808
  }
839
809
 
840
- // src/utils/isInMVCPActiveMode.native.ts
841
- function isInMVCPActiveMode(state) {
842
- return state.dataChangeNeedsScrollUpdate;
810
+ // src/core/initialScrollSession.ts
811
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
812
+ function hasInitialScrollSessionCompletion(completion) {
813
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
843
814
  }
844
-
845
- // src/components/Container.tsx
846
- function getContainerPositionStyle({
847
- columnWrapperStyle,
848
- contentContainerAlignItems,
849
- horizontal,
850
- hasItemSeparator,
851
- isHorizontalRTLList,
852
- numColumns,
853
- otherAxisPos,
854
- otherAxisSize
855
- }) {
856
- let paddingStyles;
857
- if (columnWrapperStyle) {
858
- const { columnGap, rowGap, gap } = columnWrapperStyle;
859
- if (horizontal) {
860
- paddingStyles = {
861
- paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
862
- paddingRight: columnGap || gap || void 0,
863
- paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
864
- };
865
- } else {
866
- paddingStyles = {
867
- paddingBottom: rowGap || gap || void 0,
868
- paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
869
- paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
870
- };
871
- }
872
- }
873
- return horizontal ? {
874
- bottom: contentContainerAlignItems === "flex-end" && numColumns === 1 ? 0 : void 0,
875
- boxSizing: paddingStyles ? "border-box" : void 0,
876
- direction: isHorizontalRTLList && Platform.OS === "web" ? "ltr" : void 0,
877
- flexDirection: hasItemSeparator ? "row" : void 0,
878
- height: otherAxisSize,
879
- left: 0,
880
- position: "absolute",
881
- top: contentContainerAlignItems === "flex-end" && numColumns === 1 ? void 0 : otherAxisPos,
882
- ...paddingStyles || {}
815
+ function clearInitialScrollSession(state) {
816
+ state.initialScrollSession = void 0;
817
+ return void 0;
818
+ }
819
+ function createInitialScrollSession(options) {
820
+ const { bootstrap, completion, kind, previousDataLength } = options;
821
+ return kind === "offset" ? {
822
+ completion,
823
+ kind,
824
+ previousDataLength
883
825
  } : {
884
- boxSizing: paddingStyles ? "border-box" : void 0,
885
- left: otherAxisPos,
886
- position: "absolute",
887
- right: numColumns > 1 ? null : 0,
888
- top: 0,
889
- width: otherAxisSize,
890
- ...paddingStyles || {}
826
+ bootstrap,
827
+ completion,
828
+ kind,
829
+ previousDataLength
891
830
  };
892
831
  }
893
- var Container = typedMemo(function Container2({
894
- id,
895
- itemKey,
896
- recycleItems,
897
- horizontal,
898
- getRenderedItem: getRenderedItem2,
899
- updateItemSize: updateItemSize2,
900
- ItemSeparatorComponent,
901
- stickyHeaderConfig
902
- }) {
903
- const ctx = useStateContext();
904
- const { columnWrapperStyle, animatedScrollY } = ctx;
905
- const isHorizontalRTLList = isHorizontalRTL(ctx.state);
906
- const positionComponentInternal = ctx.state.props.positionComponentInternal;
907
- const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
908
- const [column = 0, span = 1, data, numColumns = 1, extraData, isSticky] = useArr$([
909
- `containerColumn${id}`,
910
- `containerSpan${id}`,
911
- `containerItemData${id}`,
912
- "numColumns",
913
- "extraData",
914
- `containerSticky${id}`
915
- ]);
916
- const itemLayoutRef = React2.useRef({
917
- didLayout: false,
918
- horizontal,
919
- itemKey,
920
- pendingShrinkToken: 0,
921
- updateItemSize: updateItemSize2
922
- });
923
- itemLayoutRef.current.horizontal = horizontal;
924
- itemLayoutRef.current.itemKey = itemKey;
925
- itemLayoutRef.current.updateItemSize = updateItemSize2;
926
- const ref = React2.useRef(null);
927
- const [layoutRenderCount, forceLayoutRender] = React2.useState(0);
928
- const resolvedColumn = column > 0 ? column : 1;
929
- const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
930
- const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
931
- const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
932
- const style = React2.useMemo(
933
- () => getContainerPositionStyle({
934
- columnWrapperStyle,
935
- contentContainerAlignItems: ctx.state.props.contentContainerAlignItems,
936
- hasItemSeparator: !!ItemSeparatorComponent,
937
- horizontal,
938
- isHorizontalRTLList,
939
- numColumns,
940
- otherAxisPos,
941
- otherAxisSize
942
- }),
943
- [
944
- horizontal,
945
- isHorizontalRTLList,
946
- otherAxisPos,
947
- otherAxisSize,
948
- columnWrapperStyle,
949
- ctx.state.props.contentContainerAlignItems,
950
- numColumns,
951
- ItemSeparatorComponent
952
- ]
953
- );
954
- const renderedItemInfo = React2.useMemo(
955
- () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
956
- [itemKey, data, extraData]
957
- );
958
- const { index, renderedItem } = renderedItemInfo || {};
959
- const onLayoutChange = React2.useCallback((rectangle) => {
832
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
833
+ var _a4, _b2;
834
+ if (!state.initialScrollSession) {
835
+ state.initialScrollSession = createInitialScrollSession({
836
+ completion: {},
837
+ kind,
838
+ previousDataLength: 0
839
+ });
840
+ } else if (state.initialScrollSession.kind !== kind) {
841
+ state.initialScrollSession = createInitialScrollSession({
842
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
843
+ completion: state.initialScrollSession.completion,
844
+ kind,
845
+ previousDataLength: state.initialScrollSession.previousDataLength
846
+ });
847
+ }
848
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
849
+ return state.initialScrollSession.completion;
850
+ }
851
+ var initialScrollCompletion = {
852
+ didDispatchNativeScroll(state) {
960
853
  var _a3, _b;
961
- const {
962
- horizontal: currentHorizontal,
963
- itemKey: currentItemKey,
964
- updateItemSize: updateItemSizeFn,
965
- lastSize,
966
- pendingShrinkToken
967
- } = itemLayoutRef.current;
968
- if (isNullOrUndefined(currentItemKey)) {
969
- return;
970
- }
971
- itemLayoutRef.current.didLayout = true;
972
- let layout = rectangle;
973
- const axis = currentHorizontal ? "width" : "height";
974
- const size = roundSize(rectangle[axis]);
975
- const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
976
- const doUpdate = () => {
977
- itemLayoutRef.current.lastSize = layout;
978
- updateItemSizeFn(currentItemKey, layout);
979
- itemLayoutRef.current.didLayout = true;
980
- };
981
- const shouldDeferWebShrinkLayoutUpdate = Platform.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
982
- if (shouldDeferWebShrinkLayoutUpdate) {
983
- const token = pendingShrinkToken + 1;
984
- itemLayoutRef.current.pendingShrinkToken = token;
985
- requestAnimationFrame(() => {
986
- var _a4;
987
- if (itemLayoutRef.current.pendingShrinkToken !== token) {
988
- return;
989
- }
990
- const element = ref.current;
991
- const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
992
- if (rect) {
993
- layout = { height: rect.height, width: rect.width };
994
- }
995
- doUpdate();
996
- });
854
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
855
+ },
856
+ didRetrySilentInitialScroll(state) {
857
+ var _a3, _b;
858
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
859
+ },
860
+ markInitialScrollNativeDispatch(state) {
861
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
862
+ },
863
+ markSilentInitialScrollRetry(state) {
864
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
865
+ },
866
+ resetFlags(state) {
867
+ if (!state.initialScrollSession) {
997
868
  return;
998
869
  }
999
- if (IsNewArchitecture || size > 0) {
1000
- doUpdate();
1001
- } else {
1002
- (_b = (_a3 = ref.current) == null ? void 0 : _a3.measure) == null ? void 0 : _b.call(_a3, (_x, _y, width, height) => {
1003
- layout = { height, width };
1004
- doUpdate();
1005
- });
870
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
871
+ completion.didDispatchNativeScroll = void 0;
872
+ completion.didRetrySilentInitialScroll = void 0;
873
+ }
874
+ };
875
+ var initialScrollWatchdog = {
876
+ clear(state) {
877
+ initialScrollWatchdog.set(state, void 0);
878
+ },
879
+ didReachTarget(newScroll, watchdog) {
880
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
881
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
882
+ },
883
+ get(state) {
884
+ var _a3, _b;
885
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
886
+ },
887
+ hasNonZeroTargetOffset(targetOffset) {
888
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
889
+ },
890
+ isAtZeroTargetOffset(targetOffset) {
891
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
892
+ },
893
+ set(state, watchdog) {
894
+ var _a3, _b;
895
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
896
+ return;
1006
897
  }
1007
- }, []);
1008
- const triggerLayout = React2.useCallback(() => {
1009
- forceLayoutRender((v) => v + 1);
1010
- }, []);
1011
- const contextValue = React2.useMemo(() => {
1012
- ctx.viewRefs.set(id, ref);
1013
- return {
1014
- containerId: id,
1015
- index,
1016
- itemKey,
1017
- triggerLayout,
1018
- value: data
1019
- };
1020
- }, [id, itemKey, index, data, triggerLayout]);
1021
- React2.useLayoutEffect(() => {
1022
- ctx.containerLayoutTriggers.set(id, triggerLayout);
1023
- return () => {
1024
- if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
1025
- ctx.containerLayoutTriggers.delete(id);
1026
- }
1027
- };
1028
- }, [ctx, id, triggerLayout]);
1029
- const { onLayout } = useOnLayoutSync(
1030
- {
1031
- onLayoutChange,
1032
- ref},
1033
- [itemKey, layoutRenderCount]
1034
- );
1035
- if (!IsNewArchitecture) {
1036
- React2.useEffect(() => {
1037
- if (!isNullOrUndefined(itemKey)) {
1038
- itemLayoutRef.current.didLayout = false;
1039
- const timeout = setTimeout(() => {
1040
- if (!itemLayoutRef.current.didLayout) {
1041
- const {
1042
- itemKey: currentItemKey,
1043
- lastSize,
1044
- updateItemSize: updateItemSizeFn
1045
- } = itemLayoutRef.current;
1046
- if (lastSize && !isNullOrUndefined(currentItemKey)) {
1047
- updateItemSizeFn(currentItemKey, lastSize);
1048
- itemLayoutRef.current.didLayout = true;
1049
- }
1050
- }
1051
- }, 16);
1052
- return () => {
1053
- clearTimeout(timeout);
1054
- };
1055
- }
1056
- }, [itemKey]);
898
+ const completion = ensureInitialScrollSessionCompletion(state);
899
+ completion.watchdog = watchdog ? {
900
+ startScroll: watchdog.startScroll,
901
+ targetOffset: watchdog.targetOffset
902
+ } : void 0;
1057
903
  }
1058
- const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
1059
- return /* @__PURE__ */ React2__namespace.createElement(
1060
- PositionComponent,
1061
- {
1062
- animatedScrollY: isSticky ? animatedScrollY : void 0,
1063
- horizontal,
1064
- id,
1065
- index,
1066
- key: recycleItems ? void 0 : itemKey,
1067
- onLayout,
1068
- refView: ref,
1069
- stickyHeaderConfig,
1070
- style
1071
- },
1072
- /* @__PURE__ */ React2__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React2__namespace.createElement(Separator, { ItemSeparatorComponent, leadingItem: renderedItemInfo.item }))
1073
- );
1074
- });
1075
-
1076
- // src/components/ContainerSlot.tsx
1077
- function ContainerSlotBase({
1078
- id,
1079
- horizontal,
1080
- recycleItems,
1081
- ItemSeparatorComponent,
1082
- updateItemSize: updateItemSize2,
1083
- getRenderedItem: getRenderedItem2,
1084
- stickyHeaderConfig,
1085
- ContainerComponent = Container
1086
- }) {
1087
- const [itemKey] = useArr$([`containerItemKey${id}`]);
1088
- if (itemKey === void 0) {
1089
- return null;
904
+ };
905
+ function setInitialScrollSession(state, options = {}) {
906
+ var _a3, _b, _c, _d;
907
+ const existingSession = state.initialScrollSession;
908
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
909
+ const completion = existingSession == null ? void 0 : existingSession.completion;
910
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
911
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
912
+ if (!kind) {
913
+ return clearInitialScrollSession(state);
1090
914
  }
1091
- return /* @__PURE__ */ React2__namespace.createElement(
1092
- ContainerComponent,
1093
- {
1094
- getRenderedItem: getRenderedItem2,
1095
- horizontal,
1096
- ItemSeparatorComponent,
1097
- id,
1098
- itemKey,
1099
- recycleItems,
1100
- stickyHeaderConfig,
1101
- updateItemSize: updateItemSize2
1102
- }
1103
- );
915
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
916
+ return clearInitialScrollSession(state);
917
+ }
918
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
919
+ state.initialScrollSession = createInitialScrollSession({
920
+ bootstrap,
921
+ completion,
922
+ kind,
923
+ previousDataLength
924
+ });
925
+ return state.initialScrollSession;
1104
926
  }
1105
- var ContainerSlot = typedMemo(function ContainerSlot2(props) {
1106
- return /* @__PURE__ */ React2__namespace.createElement(ContainerSlotBase, { ...props });
1107
- });
1108
927
 
1109
- // src/components/Containers.native.tsx
1110
- var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal }) {
1111
- const ctx = useStateContext();
1112
- const columnWrapperStyle = ctx.columnWrapperStyle;
1113
- const animSize = useValue$("totalSize");
1114
- const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);
1115
- const style = horizontal ? {
1116
- height: otherAxisSize || "100%",
1117
- minHeight: otherAxisSize,
1118
- opacity: readyToRender ? 1 : 0,
1119
- width: animSize
1120
- } : { height: animSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0 };
1121
- if (columnWrapperStyle) {
1122
- const { columnGap, rowGap, gap } = columnWrapperStyle;
1123
- const gapX = columnGap || gap || 0;
1124
- const gapY = rowGap || gap || 0;
1125
- if (horizontal) {
1126
- if (gapY && numColumns > 1) {
1127
- style.marginVertical = -gapY / 2;
1128
- }
1129
- if (gapX) {
1130
- style.marginRight = -gapX;
1131
- }
1132
- } else {
1133
- if (gapX && numColumns > 1) {
1134
- style.marginHorizontal = -gapX;
1135
- }
1136
- if (gapY) {
1137
- style.marginBottom = -gapY;
1138
- }
928
+ // src/utils/checkThreshold.ts
929
+ var HYSTERESIS_MULTIPLIER = 1.3;
930
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
931
+ const absDistance = Math.abs(distance);
932
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
933
+ const updateSnapshot = () => {
934
+ setSnapshot({
935
+ atThreshold,
936
+ contentSize: context.contentSize,
937
+ dataLength: context.dataLength,
938
+ scrollPosition: context.scrollPosition
939
+ });
940
+ };
941
+ if (!wasReached) {
942
+ if (!within) {
943
+ return false;
1139
944
  }
945
+ onReached(distance);
946
+ updateSnapshot();
947
+ return true;
1140
948
  }
1141
- return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { style }, children);
1142
- });
1143
- var Containers = typedMemo(function Containers2({
1144
- horizontal,
1145
- recycleItems,
1146
- ItemSeparatorComponent,
1147
- stickyHeaderConfig,
1148
- updateItemSize: updateItemSize2,
1149
- getRenderedItem: getRenderedItem2
1150
- }) {
1151
- const [numContainersPooled] = useArr$(["numContainersPooled"]);
1152
- const containers = [];
1153
- for (let i = 0; i < numContainersPooled; i++) {
1154
- containers.push(
1155
- /* @__PURE__ */ React2__namespace.createElement(
1156
- ContainerSlot,
1157
- {
1158
- getRenderedItem: getRenderedItem2,
1159
- horizontal,
1160
- ItemSeparatorComponent,
1161
- id: i,
1162
- key: i,
1163
- recycleItems,
1164
- stickyHeaderConfig,
1165
- updateItemSize: updateItemSize2
1166
- }
1167
- )
1168
- );
949
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
950
+ if (reset) {
951
+ setSnapshot(void 0);
952
+ return false;
1169
953
  }
1170
- return /* @__PURE__ */ React2__namespace.createElement(ContainersLayer, { horizontal }, containers);
1171
- });
1172
- var ListComponentScrollView = ReactNative.Animated.ScrollView;
1173
-
1174
- // src/components/listComponentStyles.ts
1175
- function getAutoOtherAxisStyle({
1176
- horizontal,
1177
- needsOtherAxisSize,
1178
- otherAxisSize
1179
- }) {
1180
- if (!needsOtherAxisSize || !otherAxisSize || otherAxisSize <= 0) {
1181
- return void 0;
954
+ if (within) {
955
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
956
+ if (changed) {
957
+ if (allowReentryOnChange) {
958
+ onReached(distance);
959
+ }
960
+ updateSnapshot();
961
+ }
1182
962
  }
1183
- return horizontal ? { height: otherAxisSize } : { width: otherAxisSize };
963
+ return true;
964
+ };
965
+
966
+ // src/utils/hasActiveInitialScroll.ts
967
+ function hasActiveInitialScroll(state) {
968
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1184
969
  }
1185
- function ScrollAdjust() {
970
+
971
+ // src/utils/checkAtBottom.ts
972
+ function checkAtBottom(ctx) {
1186
973
  var _a3;
1187
- const ctx = useStateContext();
1188
- const bias = 1e7;
1189
- const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
1190
- const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0) + bias;
1191
- const horizontal = !!((_a3 = ctx.state) == null ? void 0 : _a3.props.horizontal);
1192
- return /* @__PURE__ */ React2__namespace.createElement(
1193
- ReactNative.View,
1194
- {
1195
- style: {
1196
- height: 0,
1197
- left: horizontal ? scrollOffset : 0,
1198
- position: "absolute",
1199
- top: horizontal ? 0 : scrollOffset,
1200
- width: 0
1201
- }
974
+ const state = ctx.state;
975
+ if (!state) {
976
+ return;
977
+ }
978
+ const {
979
+ queuedInitialLayout,
980
+ scrollLength,
981
+ scroll,
982
+ maintainingScrollAtEnd,
983
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
984
+ } = state;
985
+ const contentSize = getContentSize(ctx);
986
+ if (contentSize > 0 && queuedInitialLayout) {
987
+ const insetEnd = getContentInsetEnd(ctx);
988
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
989
+ const isContentLess = contentSize < scrollLength;
990
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
991
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
992
+ set$(
993
+ ctx,
994
+ "isWithinMaintainScrollAtEndThreshold",
995
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
996
+ );
997
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
998
+ if (!shouldSkipThresholdChecks) {
999
+ state.isEndReached = checkThreshold(
1000
+ distanceFromEnd,
1001
+ isContentLess,
1002
+ onEndReachedThreshold * scrollLength,
1003
+ state.isEndReached,
1004
+ state.endReachedSnapshot,
1005
+ {
1006
+ contentSize,
1007
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1008
+ scrollPosition: scroll
1009
+ },
1010
+ (distance) => {
1011
+ var _a4, _b;
1012
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1013
+ },
1014
+ (snapshot) => {
1015
+ state.endReachedSnapshot = snapshot;
1016
+ },
1017
+ true
1018
+ );
1202
1019
  }
1203
- );
1204
- }
1205
- var SnapWrapper = React2__namespace.forwardRef(function SnapWrapperInner({ ScrollComponent, ...props }, ref) {
1206
- const [snapToOffsets] = useArr$(["snapToOffsets"]);
1207
- return /* @__PURE__ */ React2__namespace.createElement(ScrollComponent, { ...props, ref, snapToOffsets });
1208
- });
1209
- function WebAnchoredEndSpace({ horizontal }) {
1210
- const ctx = useStateContext();
1211
- const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
1212
- const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
1213
- if (!shouldRenderAnchoredEndSpace) {
1214
- return null;
1215
1020
  }
1216
- const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
1217
- return /* @__PURE__ */ React2__namespace.createElement("div", { style }, null);
1218
1021
  }
1219
1022
 
1220
- // src/core/doMaintainScrollAtEnd.ts
1221
- function doMaintainScrollAtEnd(ctx) {
1222
- const state = ctx.state;
1023
+ // src/utils/isInMVCPActiveMode.native.ts
1024
+ function isInMVCPActiveMode(state) {
1025
+ return state.dataChangeNeedsScrollUpdate;
1026
+ }
1027
+
1028
+ // src/utils/checkAtTop.ts
1029
+ function checkAtTop(ctx) {
1030
+ const state = ctx == null ? void 0 : ctx.state;
1031
+ if (!state) {
1032
+ return;
1033
+ }
1223
1034
  const {
1224
- didContainersLayout,
1225
- pendingNativeMVCPAdjust,
1226
- refScroller,
1227
- props: { maintainScrollAtEnd }
1035
+ dataChangeEpoch,
1036
+ isStartReached,
1037
+ props: { data, onStartReachedThreshold },
1038
+ scroll,
1039
+ scrollLength,
1040
+ startReachedSnapshot,
1041
+ startReachedSnapshotDataChangeEpoch,
1042
+ totalSize
1228
1043
  } = state;
1229
- const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1230
- const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
1231
- if (pendingNativeMVCPAdjust) {
1232
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1233
- return false;
1044
+ const dataLength = data.length;
1045
+ const threshold = onStartReachedThreshold * scrollLength;
1046
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1047
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1048
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1049
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1050
+ state.isStartReached = false;
1051
+ state.startReachedSnapshot = void 0;
1052
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1234
1053
  }
1235
- state.pendingMaintainScrollAtEnd = false;
1236
- if (shouldMaintainScrollAtEnd) {
1237
- const contentSize = getContentSize(ctx);
1238
- if (contentSize < state.scrollLength) {
1239
- state.scroll = 0;
1240
- }
1241
- if (!state.maintainingScrollAtEnd) {
1242
- const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1243
- const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1244
- state.maintainingScrollAtEnd = pendingState;
1245
- requestAnimationFrame(() => {
1246
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
1247
- state.maintainingScrollAtEnd = activeState;
1248
- const scroller = refScroller.current;
1249
- if (state.props.horizontal && isHorizontalRTL(state)) {
1250
- const currentContentSize = getContentSize(ctx);
1251
- const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
1252
- const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
1253
- scroller == null ? void 0 : scroller.scrollTo({
1254
- animated: maintainScrollAtEnd.animated,
1255
- x: nativeOffset,
1256
- y: 0
1257
- });
1258
- } else {
1259
- scroller == null ? void 0 : scroller.scrollToEnd({
1260
- animated: maintainScrollAtEnd.animated
1261
- });
1262
- }
1263
- setTimeout(
1264
- () => {
1265
- if (state.maintainingScrollAtEnd === activeState) {
1266
- state.maintainingScrollAtEnd = void 0;
1267
- }
1268
- },
1269
- maintainScrollAtEnd.animated ? 500 : 0
1270
- );
1271
- } else if (state.maintainingScrollAtEnd === pendingState) {
1272
- state.maintainingScrollAtEnd = void 0;
1273
- }
1274
- });
1275
- }
1276
- return true;
1054
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1055
+ set$(ctx, "isNearStart", scroll <= threshold);
1056
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1057
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1058
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1059
+ state.isStartReached = checkThreshold(
1060
+ scroll,
1061
+ false,
1062
+ threshold,
1063
+ state.isStartReached,
1064
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1065
+ {
1066
+ contentSize: totalSize,
1067
+ dataLength,
1068
+ scrollPosition: scroll
1069
+ },
1070
+ (distance) => {
1071
+ var _a3, _b;
1072
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1073
+ },
1074
+ (snapshot) => {
1075
+ state.startReachedSnapshot = snapshot;
1076
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1077
+ },
1078
+ allowReentryOnDataChange
1079
+ );
1277
1080
  }
1278
- return false;
1279
- }
1280
-
1281
- // src/core/calculateOffsetForIndex.ts
1282
- function calculateOffsetForIndex(ctx, index) {
1283
- const state = ctx.state;
1284
- return index !== void 0 ? state.positions[index] || 0 : 0;
1285
1081
  }
1286
1082
 
1287
- // src/core/getTopOffsetAdjustment.ts
1288
- function getTopOffsetAdjustment(ctx) {
1289
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1083
+ // src/utils/checkThresholds.ts
1084
+ function checkThresholds(ctx) {
1085
+ checkAtBottom(ctx);
1086
+ checkAtTop(ctx);
1290
1087
  }
1291
1088
 
1292
- // src/utils/getId.ts
1293
- function getId(state, index) {
1294
- const { data, keyExtractor } = state.props;
1295
- if (!data) {
1296
- return "";
1089
+ // src/core/recalculateSettledScroll.ts
1090
+ function recalculateSettledScroll(ctx) {
1091
+ var _a3, _b;
1092
+ const state = ctx.state;
1093
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1094
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1297
1095
  }
1298
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1299
- const id = ret;
1300
- state.idCache[index] = id;
1301
- return id;
1096
+ checkThresholds(ctx);
1302
1097
  }
1303
1098
 
1304
- // src/core/updateContentMetricsState.ts
1305
- function getRawContentLength(ctx) {
1306
- var _a3, _b, _c;
1307
- const { state, values } = ctx;
1308
- return (values.get("headerSize") || 0) + (values.get("footerSize") || 0) + ((_c = (_b = (_a3 = state.pendingTotalSize) != null ? _a3 : state.totalSize) != null ? _b : values.get("totalSize")) != null ? _c : 0) + (state.props.stylePaddingTop || 0) + (state.props.stylePaddingBottom || 0);
1309
- }
1310
- function getAlignItemsAtEndPadding(ctx) {
1311
- const { state } = ctx;
1312
- const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;
1313
- return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetEnd(ctx)) : 0;
1314
- }
1315
- function updateContentMetricsState(ctx) {
1316
- const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1317
- const nextPadding = getAlignItemsAtEndPadding(ctx);
1318
- if (previousPadding !== nextPadding) {
1319
- set$(ctx, "alignItemsAtEndPadding", nextPadding);
1099
+ // src/core/adaptiveRender.ts
1100
+ var DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY = 3;
1101
+ var DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY = 1;
1102
+ var DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1103
+ var DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY = 6;
1104
+ var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY = 3;
1105
+ var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1106
+ function clearAdaptiveRenderExitTimeout(ctx) {
1107
+ const state = ctx.state;
1108
+ const previousTimeout = state.timeoutAdaptiveRender;
1109
+ if (previousTimeout !== void 0) {
1110
+ clearTimeout(previousTimeout);
1111
+ state.timeouts.delete(previousTimeout);
1112
+ state.timeoutAdaptiveRender = void 0;
1320
1113
  }
1321
1114
  }
1322
-
1323
- // src/core/addTotalSize.ts
1324
- function addTotalSize(ctx, key, add, notifyTotalSize = true) {
1115
+ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1325
1116
  const state = ctx.state;
1326
- const prevTotalSize = state.totalSize;
1327
- let totalSize = state.totalSize;
1328
- if (key === null) {
1329
- totalSize = add;
1330
- if (state.timeoutSetPaddingTop) {
1331
- clearTimeout(state.timeoutSetPaddingTop);
1332
- state.timeoutSetPaddingTop = void 0;
1333
- }
1117
+ clearAdaptiveRenderExitTimeout(ctx);
1118
+ if (exitDelay <= 0) {
1119
+ setAdaptiveRender(ctx, "normal");
1334
1120
  } else {
1335
- totalSize += add;
1336
- }
1337
- if (prevTotalSize !== totalSize) {
1338
- if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1121
+ const timeout = setTimeout(() => {
1122
+ state.timeouts.delete(timeout);
1123
+ state.timeoutAdaptiveRender = void 0;
1124
+ setAdaptiveRender(ctx, "normal");
1125
+ }, exitDelay);
1126
+ state.timeoutAdaptiveRender = timeout;
1127
+ state.timeouts.add(timeout);
1128
+ }
1129
+ }
1130
+ function setAdaptiveRender(ctx, mode) {
1131
+ var _a3, _b;
1132
+ const previousMode = peek$(ctx, "adaptiveRender");
1133
+ if (previousMode !== mode) {
1134
+ set$(ctx, "adaptiveRender", mode);
1135
+ (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode);
1136
+ }
1137
+ }
1138
+ function resetAdaptiveRender(ctx) {
1139
+ clearAdaptiveRenderExitTimeout(ctx);
1140
+ if (peek$(ctx, "adaptiveRender") !== "normal") {
1141
+ setAdaptiveRender(ctx, "normal");
1142
+ }
1143
+ }
1144
+ function updateAdaptiveRender(ctx, scrollVelocity) {
1145
+ var _a3, _b, _c;
1146
+ const state = ctx.state;
1147
+ const adaptiveRender = state.props.adaptiveRender;
1148
+ const currentMode = peek$(ctx, "adaptiveRender");
1149
+ if (peek$(ctx, "readyToRender")) {
1150
+ if (adaptiveRender) {
1151
+ const isWeb = Platform.OS === "web";
1152
+ const enterVelocity = (_a3 = adaptiveRender.enterVelocity) != null ? _a3 : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY : DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY;
1153
+ const exitVelocity = (_b = adaptiveRender.exitVelocity) != null ? _b : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY : DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY;
1154
+ const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY : DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY;
1155
+ const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1156
+ const nextMode = Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1157
+ const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
1158
+ if (nextMode !== previousMode) {
1159
+ if (nextMode === "light") {
1160
+ setAdaptiveRender(ctx, "light");
1161
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1162
+ } else if (currentMode === "light") {
1163
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1164
+ }
1165
+ }
1166
+ } else {
1167
+ resetAdaptiveRender(ctx);
1168
+ }
1169
+ }
1170
+ }
1171
+
1172
+ // src/utils/getEffectiveDrawDistance.ts
1173
+ var INITIAL_DRAW_DISTANCE = 100;
1174
+ function getEffectiveDrawDistance(ctx) {
1175
+ var _a3;
1176
+ const drawDistance = ctx.state.props.drawDistance;
1177
+ const initialScroll = ctx.state.initialScroll;
1178
+ const needsFullInitialDrawDistance = initialScroll !== void 0 && ((_a3 = initialScroll.viewPosition) != null ? _a3 : 0) > 0;
1179
+ return peek$(ctx, "readyToRender") || needsFullInitialDrawDistance ? drawDistance : Math.min(drawDistance, INITIAL_DRAW_DISTANCE);
1180
+ }
1181
+
1182
+ // src/utils/setInitialRenderState.ts
1183
+ function setInitialRenderState(ctx, {
1184
+ didLayout,
1185
+ didInitialScroll
1186
+ }) {
1187
+ const { state } = ctx;
1188
+ const {
1189
+ loadStartTime,
1190
+ props: { onLoad }
1191
+ } = state;
1192
+ if (didLayout) {
1193
+ state.didContainersLayout = true;
1194
+ }
1195
+ if (didInitialScroll) {
1196
+ state.didFinishInitialScroll = true;
1197
+ }
1198
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1199
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1200
+ set$(ctx, "readyToRender", true);
1201
+ setAdaptiveRender(ctx, "normal");
1202
+ if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1203
+ requestAnimationFrame(() => {
1204
+ var _a3;
1205
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1206
+ });
1207
+ }
1208
+ if (onLoad) {
1209
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1210
+ }
1211
+ }
1212
+ }
1213
+
1214
+ // src/core/finishInitialScroll.ts
1215
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1216
+ function syncInitialScrollOffset(state, offset) {
1217
+ state.scroll = offset;
1218
+ state.scrollPending = offset;
1219
+ state.scrollPrev = offset;
1220
+ }
1221
+ function clearPreservedInitialScrollTargetTimeout(state) {
1222
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
1223
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
1224
+ state.timeoutPreservedInitialScrollClear = void 0;
1225
+ }
1226
+ }
1227
+ function clearPreservedInitialScrollTarget(state) {
1228
+ clearPreservedInitialScrollTargetTimeout(state);
1229
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1230
+ state.initialScroll = void 0;
1231
+ setInitialScrollSession(state);
1232
+ }
1233
+ function finishInitialScroll(ctx, options) {
1234
+ var _a3, _b, _c;
1235
+ const state = ctx.state;
1236
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1237
+ syncInitialScrollOffset(state, options.resolvedOffset);
1238
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1239
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1240
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1241
+ syncInitialScrollOffset(state, observedOffset);
1242
+ }
1243
+ }
1244
+ const complete = () => {
1245
+ var _a4, _b2, _c2, _d, _e;
1246
+ const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1247
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1248
+ initialScrollWatchdog.clear(state);
1249
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1250
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1251
+ setInitialScrollSession(state);
1252
+ clearPreservedInitialScrollTargetTimeout(state);
1253
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1254
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1255
+ var _a5;
1256
+ state.timeoutPreservedInitialScrollClear = void 0;
1257
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1258
+ return;
1259
+ }
1260
+ clearPreservedInitialScrollTarget(state);
1261
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
1262
+ }
1263
+ } else {
1264
+ clearPreservedInitialScrollTarget(state);
1265
+ }
1266
+ if (options == null ? void 0 : options.recalculateItems) {
1267
+ recalculateSettledScroll(ctx);
1268
+ }
1269
+ setInitialRenderState(ctx, { didInitialScroll: true });
1270
+ if (shouldReleaseDeferredPublicOnScroll) {
1271
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1272
+ }
1273
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
1274
+ };
1275
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
1276
+ requestAnimationFrame(complete);
1277
+ return;
1278
+ }
1279
+ complete();
1280
+ }
1281
+
1282
+ // src/core/calculateOffsetForIndex.ts
1283
+ function calculateOffsetForIndex(ctx, index) {
1284
+ const state = ctx.state;
1285
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1286
+ }
1287
+
1288
+ // src/core/getTopOffsetAdjustment.ts
1289
+ function getTopOffsetAdjustment(ctx) {
1290
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1291
+ }
1292
+
1293
+ // src/utils/getId.ts
1294
+ function getId(state, index) {
1295
+ const { data, keyExtractor } = state.props;
1296
+ if (!data) {
1297
+ return "";
1298
+ }
1299
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1300
+ const id = ret;
1301
+ state.idCache[index] = id;
1302
+ return id;
1303
+ }
1304
+
1305
+ // src/core/updateContentMetricsState.ts
1306
+ function getRawContentLength(ctx) {
1307
+ var _a3, _b, _c;
1308
+ const { state, values } = ctx;
1309
+ return (values.get("headerSize") || 0) + (values.get("footerSize") || 0) + ((_c = (_b = (_a3 = state.pendingTotalSize) != null ? _a3 : state.totalSize) != null ? _b : values.get("totalSize")) != null ? _c : 0) + (state.props.stylePaddingTop || 0) + (state.props.stylePaddingBottom || 0);
1310
+ }
1311
+ function getAlignItemsAtEndPadding(ctx) {
1312
+ const { state } = ctx;
1313
+ const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;
1314
+ return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetEnd(ctx)) : 0;
1315
+ }
1316
+ function updateContentMetricsState(ctx) {
1317
+ const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1318
+ const nextPadding = getAlignItemsAtEndPadding(ctx);
1319
+ if (previousPadding !== nextPadding) {
1320
+ set$(ctx, "alignItemsAtEndPadding", nextPadding);
1321
+ }
1322
+ }
1323
+
1324
+ // src/core/addTotalSize.ts
1325
+ function addTotalSize(ctx, key, add, notifyTotalSize = true) {
1326
+ const state = ctx.state;
1327
+ const prevTotalSize = state.totalSize;
1328
+ let totalSize = state.totalSize;
1329
+ if (key === null) {
1330
+ totalSize = add;
1331
+ if (state.timeoutSetPaddingTop) {
1332
+ clearTimeout(state.timeoutSetPaddingTop);
1333
+ state.timeoutSetPaddingTop = void 0;
1334
+ }
1335
+ } else {
1336
+ totalSize += add;
1337
+ }
1338
+ if (prevTotalSize !== totalSize) {
1339
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1339
1340
  state.pendingTotalSize = totalSize;
1340
1341
  } else {
1341
1342
  state.pendingTotalSize = void 0;
@@ -1494,1220 +1495,539 @@ function clampScrollOffset(ctx, offset, scrollTarget) {
1494
1495
  return clampedOffset;
1495
1496
  }
1496
1497
 
1497
- // src/core/deferredPublicOnScroll.ts
1498
- function withResolvedContentOffset(state, event, resolvedOffset) {
1499
- return {
1500
- ...event,
1501
- nativeEvent: {
1502
- ...event.nativeEvent,
1503
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1504
- }
1505
- };
1506
- }
1507
- function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
1508
- var _a3, _b, _c, _d;
1498
+ // src/core/finishScrollTo.ts
1499
+ function finishScrollTo(ctx) {
1500
+ var _a3, _b;
1509
1501
  const state = ctx.state;
1510
- const deferredEvent = state.deferredPublicOnScrollEvent;
1511
- state.deferredPublicOnScrollEvent = void 0;
1512
- if (deferredEvent) {
1513
- (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
1514
- _c,
1515
- withResolvedContentOffset(
1516
- state,
1517
- deferredEvent,
1518
- (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
1519
- )
1520
- );
1502
+ if (state == null ? void 0 : state.scrollingTo) {
1503
+ const resolvePendingScroll = state.pendingScrollResolve;
1504
+ state.pendingScrollResolve = void 0;
1505
+ const scrollingTo = state.scrollingTo;
1506
+ state.scrollHistory.length = 0;
1507
+ state.scrollingTo = void 0;
1508
+ if (state.pendingTotalSize !== void 0) {
1509
+ addTotalSize(ctx, null, state.pendingTotalSize);
1510
+ }
1511
+ if (PlatformAdjustBreaksScroll) {
1512
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1513
+ }
1514
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
1515
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1516
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1517
+ finishInitialScroll(ctx, {
1518
+ onFinished: () => {
1519
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1520
+ },
1521
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1522
+ recalculateItems: true,
1523
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1524
+ syncObservedOffset: isOffsetSession,
1525
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1526
+ });
1527
+ return;
1528
+ }
1529
+ recalculateSettledScroll(ctx);
1530
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1521
1531
  }
1522
1532
  }
1523
1533
 
1524
- // src/core/initialScrollSession.ts
1525
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1526
- function hasInitialScrollSessionCompletion(completion) {
1527
- return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
1534
+ // src/core/checkFinishedScroll.ts
1535
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1536
+ var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
1537
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1538
+ var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
1539
+ var SILENT_INITIAL_SCROLL_TARGET_EPSILON = 1;
1540
+ function checkFinishedScroll(ctx, options) {
1541
+ const scrollingTo = ctx.state.scrollingTo;
1542
+ if (options == null ? void 0 : options.onlyIfAligned) {
1543
+ if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
1544
+ return;
1545
+ }
1546
+ if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
1547
+ return;
1548
+ }
1549
+ }
1550
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1528
1551
  }
1529
- function clearInitialScrollSession(state) {
1530
- state.initialScrollSession = void 0;
1531
- return void 0;
1552
+ function hasScrollCompletionOwnership(state, options) {
1553
+ const { clampedTargetOffset, scrollingTo } = options;
1554
+ return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
1532
1555
  }
1533
- function createInitialScrollSession(options) {
1534
- const { bootstrap, completion, kind, previousDataLength } = options;
1535
- return kind === "offset" ? {
1536
- completion,
1537
- kind,
1538
- previousDataLength
1539
- } : {
1540
- bootstrap,
1541
- completion,
1542
- kind,
1543
- previousDataLength
1544
- };
1556
+ function isSilentInitialDispatch(state, scrollingTo) {
1557
+ return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
1545
1558
  }
1546
- function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
1547
- var _a4, _b2;
1548
- if (!state.initialScrollSession) {
1549
- state.initialScrollSession = createInitialScrollSession({
1550
- completion: {},
1551
- kind,
1552
- previousDataLength: 0
1553
- });
1554
- } else if (state.initialScrollSession.kind !== kind) {
1555
- state.initialScrollSession = createInitialScrollSession({
1556
- bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
1557
- completion: state.initialScrollSession.completion,
1558
- kind,
1559
- previousDataLength: state.initialScrollSession.previousDataLength
1560
- });
1561
- }
1562
- (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
1563
- return state.initialScrollSession.completion;
1559
+ function getInitialScrollWatchdogTargetOffset(state) {
1560
+ var _a3;
1561
+ return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
1564
1562
  }
1565
- var initialScrollCompletion = {
1566
- didDispatchNativeScroll(state) {
1567
- var _a3, _b;
1568
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
1569
- },
1570
- didRetrySilentInitialScroll(state) {
1571
- var _a3, _b;
1572
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
1573
- },
1574
- markInitialScrollNativeDispatch(state) {
1575
- ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
1576
- },
1577
- markSilentInitialScrollRetry(state) {
1578
- ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
1579
- },
1580
- resetFlags(state) {
1581
- if (!state.initialScrollSession) {
1582
- return;
1583
- }
1584
- const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1585
- completion.didDispatchNativeScroll = void 0;
1586
- completion.didRetrySilentInitialScroll = void 0;
1563
+ function isNativeInitialNonZeroTarget(state) {
1564
+ const targetOffset = getInitialScrollWatchdogTargetOffset(state);
1565
+ return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
1566
+ }
1567
+ function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
1568
+ var _a3, _b;
1569
+ if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
1570
+ return false;
1587
1571
  }
1588
- };
1589
- var initialScrollWatchdog = {
1590
- clear(state) {
1591
- initialScrollWatchdog.set(state, void 0);
1592
- },
1593
- didReachTarget(newScroll, watchdog) {
1594
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1595
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1596
- },
1597
- get(state) {
1598
- var _a3, _b;
1599
- return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1600
- },
1601
- hasNonZeroTargetOffset(targetOffset) {
1602
- return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1603
- },
1604
- isAtZeroTargetOffset(targetOffset) {
1605
- return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1606
- },
1607
- set(state, watchdog) {
1608
- var _a3, _b;
1609
- if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1610
- return;
1611
- }
1612
- const completion = ensureInitialScrollSessionCompletion(state);
1613
- completion.watchdog = watchdog ? {
1614
- startScroll: watchdog.startScroll,
1615
- targetOffset: watchdog.targetOffset
1616
- } : void 0;
1572
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
1573
+ return false;
1617
1574
  }
1618
- };
1619
- function setInitialScrollSession(state, options = {}) {
1620
- var _a3, _b, _c, _d;
1621
- const existingSession = state.initialScrollSession;
1622
- const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1623
- const completion = existingSession == null ? void 0 : existingSession.completion;
1624
- const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
1625
- const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
1626
- if (!kind) {
1627
- return clearInitialScrollSession(state);
1575
+ const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
1576
+ if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
1577
+ return false;
1628
1578
  }
1629
- if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1630
- return clearInitialScrollSession(state);
1579
+ if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
1580
+ return false;
1631
1581
  }
1632
- const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
1633
- state.initialScrollSession = createInitialScrollSession({
1634
- bootstrap,
1635
- completion,
1636
- kind,
1637
- previousDataLength
1638
- });
1639
- return state.initialScrollSession;
1582
+ return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
1640
1583
  }
1641
-
1642
- // src/utils/checkThreshold.ts
1643
- var HYSTERESIS_MULTIPLIER = 1.3;
1644
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1645
- const absDistance = Math.abs(distance);
1646
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
1647
- const updateSnapshot = () => {
1648
- setSnapshot({
1649
- atThreshold,
1650
- contentSize: context.contentSize,
1651
- dataLength: context.dataLength,
1652
- scrollPosition: context.scrollPosition
1653
- });
1584
+ function shouldFinishInitialZeroTargetScroll(ctx) {
1585
+ var _a3;
1586
+ const { state } = ctx;
1587
+ 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;
1588
+ }
1589
+ function isEndAlignedLastItemTarget(ctx, scrollingTo) {
1590
+ return scrollingTo.index === ctx.state.props.data.length - 1 && scrollingTo.viewPosition === 1;
1591
+ }
1592
+ function getCurrentTargetOffset(ctx, scrollingTo) {
1593
+ var _a3;
1594
+ const index = scrollingTo.index;
1595
+ const shouldRecomputeEndTarget = isEndAlignedLastItemTarget(ctx, scrollingTo);
1596
+ 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);
1597
+ return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
1598
+ }
1599
+ function getResolvedScrollCompletionState(ctx, scrollingTo) {
1600
+ const { state } = ctx;
1601
+ const scroll = state.scrollPending;
1602
+ const adjust = state.scrollAdjustHandler.getAdjust();
1603
+ const clampedTargetOffset = getCurrentTargetOffset(ctx, scrollingTo);
1604
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
1605
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
1606
+ const adjustedTargetOffset = clampedTargetOffset + adjust;
1607
+ const diff2 = Math.abs(scroll - adjustedTargetOffset);
1608
+ const canUseAdjustedCompletion = !scrollingTo.animated || Platform.OS === "ios";
1609
+ return {
1610
+ clampedTargetOffset,
1611
+ isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
1654
1612
  };
1655
- if (!wasReached) {
1656
- if (!within) {
1657
- return false;
1658
- }
1659
- onReached(distance);
1660
- updateSnapshot();
1661
- return true;
1662
- }
1663
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1664
- if (reset) {
1665
- setSnapshot(void 0);
1666
- return false;
1613
+ }
1614
+ function checkFinishedScrollFrame(ctx) {
1615
+ const scrollingTo = ctx.state.scrollingTo;
1616
+ if (!scrollingTo) {
1617
+ return;
1667
1618
  }
1668
- if (within) {
1669
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1670
- if (changed) {
1671
- if (allowReentryOnChange) {
1672
- onReached(distance);
1673
- }
1674
- updateSnapshot();
1675
- }
1619
+ const { state } = ctx;
1620
+ state.animFrameCheckFinishedScroll = void 0;
1621
+ const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
1622
+ if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
1623
+ clampedTargetOffset: completionState.clampedTargetOffset,
1624
+ scrollingTo
1625
+ })) {
1626
+ finishScrollTo(ctx);
1676
1627
  }
1677
- return true;
1678
- };
1679
-
1680
- // src/utils/hasActiveInitialScroll.ts
1681
- function hasActiveInitialScroll(state) {
1682
- return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1683
1628
  }
1684
-
1685
- // src/utils/checkAtBottom.ts
1686
- function checkAtBottom(ctx) {
1629
+ function scrollToFallbackOffset(ctx, offset) {
1687
1630
  var _a3;
1631
+ (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
1632
+ animated: false,
1633
+ x: ctx.state.props.horizontal ? offset : 0,
1634
+ y: ctx.state.props.horizontal ? 0 : offset
1635
+ });
1636
+ }
1637
+ function checkFinishedScrollFallback(ctx) {
1688
1638
  const state = ctx.state;
1689
- if (!state) {
1690
- return;
1691
- }
1692
- const {
1693
- queuedInitialLayout,
1694
- scrollLength,
1695
- scroll,
1696
- maintainingScrollAtEnd,
1697
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1698
- } = state;
1699
- const contentSize = getContentSize(ctx);
1700
- if (contentSize > 0 && queuedInitialLayout) {
1701
- const insetEnd = getContentInsetEnd(ctx);
1702
- const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1703
- const isContentLess = contentSize < scrollLength;
1704
- set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1705
- set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1706
- set$(
1707
- ctx,
1708
- "isWithinMaintainScrollAtEndThreshold",
1709
- isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1710
- );
1711
- const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1712
- if (!shouldSkipThresholdChecks) {
1713
- state.isEndReached = checkThreshold(
1714
- distanceFromEnd,
1715
- isContentLess,
1716
- onEndReachedThreshold * scrollLength,
1717
- state.isEndReached,
1718
- state.endReachedSnapshot,
1719
- {
1720
- contentSize,
1721
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1722
- scrollPosition: scroll
1723
- },
1724
- (distance) => {
1725
- var _a4, _b;
1726
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1727
- },
1728
- (snapshot) => {
1729
- state.endReachedSnapshot = snapshot;
1730
- },
1731
- true
1732
- );
1733
- }
1734
- }
1639
+ const scrollingTo = state.scrollingTo;
1640
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1641
+ const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
1642
+ const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
1643
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
1644
+ const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
1645
+ state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
1646
+ let numChecks = 0;
1647
+ const scheduleFallbackCheck = (delay) => {
1648
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
1649
+ };
1650
+ const checkHasScrolled = () => {
1651
+ var _a3, _b, _c, _d;
1652
+ state.timeoutCheckFinishedScrollFallback = void 0;
1653
+ const isStillScrollingTo = state.scrollingTo;
1654
+ if (isStillScrollingTo) {
1655
+ numChecks++;
1656
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1657
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1658
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1659
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
1660
+ state,
1661
+ isStillScrollingTo
1662
+ );
1663
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1664
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1665
+ const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1666
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
1667
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1668
+ const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1669
+ if (shouldRetrySilentInitialNativeScroll) {
1670
+ const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1671
+ const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
1672
+ initialScrollCompletion.markSilentInitialScrollRetry(state);
1673
+ scrollToFallbackOffset(ctx, jiggleOffset);
1674
+ requestAnimationFrame(() => {
1675
+ scrollToFallbackOffset(ctx, targetOffset);
1676
+ });
1677
+ scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1678
+ } else if (shouldRetryUnalignedEndScroll) {
1679
+ scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
1680
+ scheduleFallbackCheck(100);
1681
+ } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1682
+ finishScrollTo(ctx);
1683
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
1684
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1685
+ scrollToFallbackOffset(ctx, targetOffset);
1686
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1687
+ } else {
1688
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1689
+ }
1690
+ }
1691
+ };
1692
+ checkHasScrolled();
1693
+ }, initialDelay);
1735
1694
  }
1736
1695
 
1737
- // src/utils/checkAtTop.ts
1738
- function checkAtTop(ctx) {
1739
- const state = ctx == null ? void 0 : ctx.state;
1740
- if (!state) {
1696
+ // src/core/doScrollTo.native.ts
1697
+ function doScrollTo(ctx, params) {
1698
+ const state = ctx.state;
1699
+ const { animated, horizontal, isInitialScroll, offset } = params;
1700
+ const isAnimated = !!animated;
1701
+ const { refScroller } = state;
1702
+ const scroller = refScroller.current;
1703
+ if (!scroller) {
1741
1704
  return;
1742
1705
  }
1743
- const {
1744
- dataChangeEpoch,
1745
- isStartReached,
1746
- props: { data, onStartReachedThreshold },
1747
- scroll,
1748
- scrollLength,
1749
- startReachedSnapshot,
1750
- startReachedSnapshotDataChangeEpoch,
1751
- totalSize
1752
- } = state;
1753
- const dataLength = data.length;
1754
- const threshold = onStartReachedThreshold * scrollLength;
1755
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1756
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1757
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1758
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1759
- state.isStartReached = false;
1760
- state.startReachedSnapshot = void 0;
1761
- state.startReachedSnapshotDataChangeEpoch = void 0;
1762
- }
1763
- set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1764
- set$(ctx, "isNearStart", scroll <= threshold);
1765
- const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1766
- const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1767
- if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1768
- state.isStartReached = checkThreshold(
1769
- scroll,
1770
- false,
1771
- threshold,
1772
- state.isStartReached,
1773
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1774
- {
1775
- contentSize: totalSize,
1776
- dataLength,
1777
- scrollPosition: scroll
1778
- },
1779
- (distance) => {
1780
- var _a3, _b;
1781
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1782
- },
1783
- (snapshot) => {
1784
- state.startReachedSnapshot = snapshot;
1785
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1786
- },
1787
- allowReentryOnDataChange
1788
- );
1706
+ const isHorizontal = !!horizontal;
1707
+ const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
1708
+ const nativeOffset = toNativeHorizontalOffset(state, offset, contentSize);
1709
+ scroller.scrollTo({
1710
+ animated: isAnimated,
1711
+ x: isHorizontal ? nativeOffset : 0,
1712
+ y: isHorizontal ? 0 : offset
1713
+ });
1714
+ if (isInitialScroll) {
1715
+ initialScrollCompletion.markInitialScrollNativeDispatch(state);
1789
1716
  }
1790
- }
1791
-
1792
- // src/utils/checkThresholds.ts
1793
- function checkThresholds(ctx) {
1794
- checkAtBottom(ctx);
1795
- checkAtTop(ctx);
1796
- }
1797
-
1798
- // src/core/recalculateSettledScroll.ts
1799
- function recalculateSettledScroll(ctx) {
1800
- var _a3, _b;
1801
- const state = ctx.state;
1802
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1803
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1717
+ if (!isAnimated) {
1718
+ state.scroll = offset;
1719
+ checkFinishedScrollFallback(ctx);
1804
1720
  }
1805
- checkThresholds(ctx);
1806
1721
  }
1807
1722
 
1808
- // src/core/adaptiveRender.ts
1809
- var DEFAULT_ENTER_VELOCITY = 4;
1810
- var DEFAULT_EXIT_VELOCITY = 1;
1811
- var DEFAULT_EXIT_DELAY = 1e3;
1812
- function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1723
+ // src/core/doMaintainScrollAtEnd.ts
1724
+ function doMaintainScrollAtEnd(ctx) {
1813
1725
  const state = ctx.state;
1814
- const previousTimeout = state.timeoutAdaptiveRender;
1815
- if (previousTimeout !== void 0) {
1816
- clearTimeout(previousTimeout);
1817
- state.timeouts.delete(previousTimeout);
1818
- state.timeoutAdaptiveRender = void 0;
1819
- }
1820
- if (exitDelay <= 0) {
1821
- setAdaptiveRender(ctx, "normal");
1822
- } else {
1823
- const timeout = setTimeout(() => {
1824
- state.timeouts.delete(timeout);
1825
- state.timeoutAdaptiveRender = void 0;
1826
- setAdaptiveRender(ctx, "normal");
1827
- }, exitDelay);
1828
- state.timeoutAdaptiveRender = timeout;
1829
- state.timeouts.add(timeout);
1830
- }
1831
- }
1832
- function setAdaptiveRender(ctx, mode) {
1833
- var _a3, _b;
1834
- const previousMode = peek$(ctx, "adaptiveRender");
1835
- if (previousMode !== mode) {
1836
- set$(ctx, "adaptiveRender", mode);
1837
- (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode);
1726
+ const {
1727
+ didContainersLayout,
1728
+ pendingNativeMVCPAdjust,
1729
+ refScroller,
1730
+ props: { maintainScrollAtEnd }
1731
+ } = state;
1732
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1733
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
1734
+ if (pendingNativeMVCPAdjust) {
1735
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1736
+ return false;
1838
1737
  }
1839
- }
1840
- function updateAdaptiveRender(ctx, scrollVelocity) {
1841
- var _a3, _b, _c;
1842
- const state = ctx.state;
1843
- const adaptiveRender = state.props.adaptiveRender;
1844
- const enterVelocity = (_a3 = adaptiveRender == null ? void 0 : adaptiveRender.enterVelocity) != null ? _a3 : DEFAULT_ENTER_VELOCITY;
1845
- const exitVelocity = (_b = adaptiveRender == null ? void 0 : adaptiveRender.exitVelocity) != null ? _b : DEFAULT_EXIT_VELOCITY;
1846
- const exitDelay = (_c = adaptiveRender == null ? void 0 : adaptiveRender.exitDelay) != null ? _c : DEFAULT_EXIT_DELAY;
1847
- const currentMode = peek$(ctx, "adaptiveRender");
1848
- const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1849
- const nextMode = Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1850
- const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
1851
- if (nextMode !== previousMode) {
1852
- if (nextMode === "light") {
1853
- setAdaptiveRender(ctx, "light");
1854
- scheduleAdaptiveRenderExit(ctx, exitDelay);
1855
- } else if (currentMode === "light") {
1856
- scheduleAdaptiveRenderExit(ctx, exitDelay);
1738
+ state.pendingMaintainScrollAtEnd = false;
1739
+ if (shouldMaintainScrollAtEnd) {
1740
+ const contentSize = getContentSize(ctx);
1741
+ if (contentSize < state.scrollLength) {
1742
+ state.scroll = 0;
1743
+ }
1744
+ if (!state.maintainingScrollAtEnd) {
1745
+ const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1746
+ const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1747
+ state.maintainingScrollAtEnd = pendingState;
1748
+ requestAnimationFrame(() => {
1749
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
1750
+ state.maintainingScrollAtEnd = activeState;
1751
+ const scroller = refScroller.current;
1752
+ if (state.props.horizontal && isHorizontalRTL(state)) {
1753
+ const currentContentSize = getContentSize(ctx);
1754
+ const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
1755
+ const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
1756
+ scroller == null ? void 0 : scroller.scrollTo({
1757
+ animated: maintainScrollAtEnd.animated,
1758
+ x: nativeOffset,
1759
+ y: 0
1760
+ });
1761
+ } else {
1762
+ scroller == null ? void 0 : scroller.scrollToEnd({
1763
+ animated: maintainScrollAtEnd.animated
1764
+ });
1765
+ }
1766
+ setTimeout(
1767
+ () => {
1768
+ if (state.maintainingScrollAtEnd === activeState) {
1769
+ state.maintainingScrollAtEnd = void 0;
1770
+ }
1771
+ },
1772
+ maintainScrollAtEnd.animated ? 500 : 0
1773
+ );
1774
+ } else if (state.maintainingScrollAtEnd === pendingState) {
1775
+ state.maintainingScrollAtEnd = void 0;
1776
+ }
1777
+ });
1857
1778
  }
1779
+ return true;
1858
1780
  }
1781
+ return false;
1859
1782
  }
1860
1783
 
1861
- // src/utils/getEffectiveDrawDistance.ts
1862
- var INITIAL_DRAW_DISTANCE = 100;
1863
- function getEffectiveDrawDistance(ctx) {
1864
- const drawDistance = ctx.state.props.drawDistance;
1865
- return peek$(ctx, "readyToRender") ? drawDistance : Math.min(drawDistance, INITIAL_DRAW_DISTANCE);
1866
- }
1867
-
1868
- // src/utils/setInitialRenderState.ts
1869
- function setInitialRenderState(ctx, {
1870
- didLayout,
1871
- didInitialScroll
1872
- }) {
1873
- const { state } = ctx;
1874
- const {
1875
- loadStartTime,
1876
- props: { onLoad }
1877
- } = state;
1878
- if (didLayout) {
1879
- state.didContainersLayout = true;
1880
- }
1881
- if (didInitialScroll) {
1882
- state.didFinishInitialScroll = true;
1883
- }
1884
- const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1885
- if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1886
- set$(ctx, "readyToRender", true);
1887
- setAdaptiveRender(ctx, "normal");
1888
- if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1889
- requestAnimationFrame(() => {
1890
- var _a3;
1891
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1892
- });
1893
- }
1894
- if (onLoad) {
1895
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1896
- }
1897
- }
1898
- }
1899
-
1900
- // src/core/finishInitialScroll.ts
1901
- var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1902
- function syncInitialScrollOffset(state, offset) {
1903
- state.scroll = offset;
1904
- state.scrollPending = offset;
1905
- state.scrollPrev = offset;
1906
- }
1907
- function clearPreservedInitialScrollTargetTimeout(state) {
1908
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
1909
- clearTimeout(state.timeoutPreservedInitialScrollClear);
1910
- state.timeoutPreservedInitialScrollClear = void 0;
1911
- }
1912
- }
1913
- function clearPreservedInitialScrollTarget(state) {
1914
- clearPreservedInitialScrollTargetTimeout(state);
1915
- state.clearPreservedInitialScrollOnNextFinish = void 0;
1916
- state.initialScroll = void 0;
1917
- setInitialScrollSession(state);
1918
- }
1919
- function finishInitialScroll(ctx, options) {
1920
- var _a3, _b, _c;
1784
+ // src/utils/requestAdjust.ts
1785
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1921
1786
  const state = ctx.state;
1922
- if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1923
- syncInitialScrollOffset(state, options.resolvedOffset);
1924
- } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1925
- const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1926
- if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1927
- syncInitialScrollOffset(state, observedOffset);
1928
- }
1929
- }
1930
- const complete = () => {
1931
- var _a4, _b2, _c2, _d, _e;
1932
- const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1933
- const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1934
- initialScrollWatchdog.clear(state);
1935
- if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1936
- state.clearPreservedInitialScrollOnNextFinish = void 0;
1937
- setInitialScrollSession(state);
1938
- clearPreservedInitialScrollTargetTimeout(state);
1939
- if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1940
- state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1941
- var _a5;
1942
- state.timeoutPreservedInitialScrollClear = void 0;
1943
- if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1944
- return;
1787
+ if (Math.abs(positionDiff) > 0.1) {
1788
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1789
+ const doit = () => {
1790
+ if (needsScrollWorkaround) {
1791
+ doScrollTo(ctx, { horizontal: state.props.horizontal, offset: state.scroll });
1792
+ } else {
1793
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
1794
+ if (state.adjustingFromInitialMount) {
1795
+ state.adjustingFromInitialMount--;
1796
+ }
1797
+ }
1798
+ };
1799
+ state.scroll += positionDiff;
1800
+ state.scrollForNextCalculateItemsInView = void 0;
1801
+ const readyToRender = peek$(ctx, "readyToRender");
1802
+ if (readyToRender) {
1803
+ doit();
1804
+ if (Platform.OS !== "web") {
1805
+ const threshold = state.scroll - positionDiff / 2;
1806
+ if (!state.ignoreScrollFromMVCP) {
1807
+ state.ignoreScrollFromMVCP = {};
1808
+ }
1809
+ if (positionDiff > 0) {
1810
+ state.ignoreScrollFromMVCP.lt = threshold;
1811
+ } else {
1812
+ state.ignoreScrollFromMVCP.gt = threshold;
1813
+ }
1814
+ if (state.ignoreScrollFromMVCPTimeout) {
1815
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
1816
+ }
1817
+ const delay = needsScrollWorkaround ? 250 : 100;
1818
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
1819
+ var _a3;
1820
+ state.ignoreScrollFromMVCP = void 0;
1821
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1822
+ if (shouldForceUpdate) {
1823
+ state.ignoreScrollFromMVCPIgnored = false;
1824
+ state.scrollPending = state.scroll;
1825
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
1945
1826
  }
1946
- clearPreservedInitialScrollTarget(state);
1947
- }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
1827
+ }, delay);
1948
1828
  }
1949
1829
  } else {
1950
- clearPreservedInitialScrollTarget(state);
1951
- }
1952
- if (options == null ? void 0 : options.recalculateItems) {
1953
- recalculateSettledScroll(ctx);
1954
- }
1955
- setInitialRenderState(ctx, { didInitialScroll: true });
1956
- if (shouldReleaseDeferredPublicOnScroll) {
1957
- releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1830
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1831
+ requestAnimationFrame(doit);
1958
1832
  }
1959
- (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
1960
- };
1961
- if (options == null ? void 0 : options.waitForCompletionFrame) {
1962
- requestAnimationFrame(complete);
1963
- return;
1964
1833
  }
1965
- complete();
1966
1834
  }
1967
1835
 
1968
- // src/core/finishScrollTo.ts
1969
- function finishScrollTo(ctx) {
1970
- var _a3, _b;
1971
- const state = ctx.state;
1972
- if (state == null ? void 0 : state.scrollingTo) {
1973
- const resolvePendingScroll = state.pendingScrollResolve;
1974
- state.pendingScrollResolve = void 0;
1975
- const scrollingTo = state.scrollingTo;
1976
- state.scrollHistory.length = 0;
1977
- state.scrollingTo = void 0;
1978
- if (state.pendingTotalSize !== void 0) {
1979
- addTotalSize(ctx, null, state.pendingTotalSize);
1980
- }
1981
- if (PlatformAdjustBreaksScroll) {
1982
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1983
- }
1984
- if (scrollingTo.isInitialScroll || state.initialScroll) {
1985
- const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1986
- const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1987
- finishInitialScroll(ctx, {
1988
- onFinished: () => {
1989
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1990
- },
1991
- preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1992
- recalculateItems: true,
1993
- schedulePreservedTargetClear: shouldPreserveResizeTarget,
1994
- syncObservedOffset: isOffsetSession,
1995
- waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1996
- });
1997
- return;
1998
- }
1999
- recalculateSettledScroll(ctx);
2000
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1836
+ // src/core/mvcp.ts
1837
+ var MVCP_POSITION_EPSILON = 0.1;
1838
+ var MVCP_ANCHOR_LOCK_TTL_MS = 300;
1839
+ var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
1840
+ var NATIVE_END_CLAMP_EPSILON = 1;
1841
+ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
1842
+ if (!enableMVCPAnchorLock) {
1843
+ state.mvcpAnchorLock = void 0;
1844
+ return void 0;
1845
+ }
1846
+ const lock = state.mvcpAnchorLock;
1847
+ if (!lock) {
1848
+ return void 0;
1849
+ }
1850
+ const isExpired = now > lock.expiresAt;
1851
+ const isMissing = state.indexByKey.get(lock.id) === void 0;
1852
+ if (isExpired || isMissing || !mvcpData) {
1853
+ state.mvcpAnchorLock = void 0;
1854
+ return void 0;
2001
1855
  }
1856
+ return lock;
2002
1857
  }
2003
-
2004
- // src/core/checkFinishedScroll.ts
2005
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
2006
- var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
2007
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
2008
- var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
2009
- var SILENT_INITIAL_SCROLL_TARGET_EPSILON = 1;
2010
- function checkFinishedScroll(ctx, options) {
2011
- const scrollingTo = ctx.state.scrollingTo;
2012
- if (options == null ? void 0 : options.onlyIfAligned) {
2013
- if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
1858
+ function updateAnchorLock(state, params) {
1859
+ if (Platform.OS === "web") {
1860
+ const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
1861
+ const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
1862
+ const mvcpData = state.props.maintainVisibleContentPosition.data;
1863
+ if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
2014
1864
  return;
2015
1865
  }
2016
- if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
1866
+ const existingLock = state.mvcpAnchorLock;
1867
+ const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
1868
+ if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
1869
+ state.mvcpAnchorLock = void 0;
2017
1870
  return;
2018
1871
  }
1872
+ state.mvcpAnchorLock = {
1873
+ expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
1874
+ id: anchorId,
1875
+ position: anchorPosition,
1876
+ quietPasses
1877
+ };
2019
1878
  }
2020
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
2021
- }
2022
- function hasScrollCompletionOwnership(state, options) {
2023
- const { clampedTargetOffset, scrollingTo } = options;
2024
- return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
2025
- }
2026
- function isSilentInitialDispatch(state, scrollingTo) {
2027
- return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
2028
- }
2029
- function getInitialScrollWatchdogTargetOffset(state) {
2030
- var _a3;
2031
- return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
2032
- }
2033
- function isNativeInitialNonZeroTarget(state) {
2034
- const targetOffset = getInitialScrollWatchdogTargetOffset(state);
2035
- return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
2036
1879
  }
2037
- function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
2038
- var _a3, _b;
2039
- if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
2040
- return false;
2041
- }
2042
- if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
2043
- return false;
2044
- }
2045
- const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
2046
- if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
2047
- return false;
2048
- }
2049
- if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
1880
+ function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
1881
+ if (!dataChanged || Platform.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
2050
1882
  return false;
2051
1883
  }
2052
- return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
1884
+ const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
1885
+ return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
2053
1886
  }
2054
- function shouldFinishInitialZeroTargetScroll(ctx) {
2055
- var _a3;
2056
- const { state } = ctx;
2057
- 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;
2058
- }
2059
- function isEndAlignedLastItemTarget(ctx, scrollingTo) {
2060
- return scrollingTo.index === ctx.state.props.data.length - 1 && scrollingTo.viewPosition === 1;
2061
- }
2062
- function getCurrentTargetOffset(ctx, scrollingTo) {
2063
- var _a3;
2064
- const index = scrollingTo.index;
2065
- const shouldRecomputeEndTarget = isEndAlignedLastItemTarget(ctx, scrollingTo);
2066
- 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);
2067
- return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
2068
- }
2069
- function getResolvedScrollCompletionState(ctx, scrollingTo) {
2070
- const { state } = ctx;
2071
- const scroll = state.scrollPending;
2072
- const adjust = state.scrollAdjustHandler.getAdjust();
2073
- const clampedTargetOffset = getCurrentTargetOffset(ctx, scrollingTo);
2074
- const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
2075
- const diff1 = Math.abs(scroll - clampedTargetOffset);
2076
- const adjustedTargetOffset = clampedTargetOffset + adjust;
2077
- const diff2 = Math.abs(scroll - adjustedTargetOffset);
2078
- const canUseAdjustedCompletion = !scrollingTo.animated || Platform.OS === "ios";
2079
- return {
2080
- clampedTargetOffset,
2081
- isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
2082
- };
2083
- }
2084
- function checkFinishedScrollFrame(ctx) {
2085
- const scrollingTo = ctx.state.scrollingTo;
2086
- if (!scrollingTo) {
2087
- return;
1887
+ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
1888
+ if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
1889
+ return 0;
2088
1890
  }
2089
- const { state } = ctx;
2090
- state.animFrameCheckFinishedScroll = void 0;
2091
- const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
2092
- if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
2093
- clampedTargetOffset: completionState.clampedTargetOffset,
2094
- scrollingTo
2095
- })) {
2096
- finishScrollTo(ctx);
1891
+ const maxScroll = Math.max(0, totalSize - state.scrollLength);
1892
+ const clampDelta = maxScroll - state.scroll;
1893
+ if (unresolvedAmount < 0) {
1894
+ return Math.max(unresolvedAmount, Math.min(0, clampDelta));
2097
1895
  }
1896
+ if (unresolvedAmount > 0) {
1897
+ return Math.min(unresolvedAmount, Math.max(0, clampDelta));
1898
+ }
1899
+ return 0;
2098
1900
  }
2099
- function scrollToFallbackOffset(ctx, offset) {
2100
- var _a3;
2101
- (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
2102
- animated: false,
2103
- x: ctx.state.props.horizontal ? offset : 0,
2104
- y: ctx.state.props.horizontal ? 0 : offset
2105
- });
1901
+ function getProgressTowardAmount(targetDelta, nativeDelta) {
1902
+ return targetDelta < 0 ? -nativeDelta : nativeDelta;
2106
1903
  }
2107
- function checkFinishedScrollFallback(ctx) {
1904
+ function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
2108
1905
  const state = ctx.state;
2109
- const scrollingTo = state.scrollingTo;
2110
- const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
2111
- const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
2112
- const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
2113
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
2114
- const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
2115
- state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
2116
- let numChecks = 0;
2117
- const scheduleFallbackCheck = (delay) => {
2118
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
2119
- };
2120
- const checkHasScrolled = () => {
2121
- var _a3, _b, _c, _d;
2122
- state.timeoutCheckFinishedScrollFallback = void 0;
2123
- const isStillScrollingTo = state.scrollingTo;
2124
- if (isStillScrollingTo) {
2125
- numChecks++;
2126
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
2127
- const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
2128
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
2129
- const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
2130
- state,
2131
- isStillScrollingTo
2132
- );
2133
- const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
2134
- const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
2135
- const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
2136
- const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
2137
- const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2138
- const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2139
- if (shouldRetrySilentInitialNativeScroll) {
2140
- const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
2141
- const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
2142
- initialScrollCompletion.markSilentInitialScrollRetry(state);
2143
- scrollToFallbackOffset(ctx, jiggleOffset);
2144
- requestAnimationFrame(() => {
2145
- scrollToFallbackOffset(ctx, targetOffset);
2146
- });
2147
- scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
2148
- } else if (shouldRetryUnalignedEndScroll) {
2149
- scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
2150
- scheduleFallbackCheck(100);
2151
- } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
2152
- finishScrollTo(ctx);
2153
- } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
2154
- const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
2155
- scrollToFallbackOffset(ctx, targetOffset);
2156
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2157
- } else {
2158
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2159
- }
2160
- }
2161
- };
2162
- checkHasScrolled();
2163
- }, initialDelay);
1906
+ state.pendingNativeMVCPAdjust = void 0;
1907
+ const remaining = remainingAfterManual - nativeDelta;
1908
+ if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
1909
+ requestAdjust(ctx, remaining, true);
1910
+ }
2164
1911
  }
2165
-
2166
- // src/core/doScrollTo.native.ts
2167
- function doScrollTo(ctx, params) {
1912
+ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
2168
1913
  const state = ctx.state;
2169
- const { animated, horizontal, isInitialScroll, offset } = params;
2170
- const isAnimated = !!animated;
2171
- const { refScroller } = state;
2172
- const scroller = refScroller.current;
2173
- if (!scroller) {
1914
+ const pending = state.pendingNativeMVCPAdjust;
1915
+ if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
2174
1916
  return;
2175
1917
  }
2176
- const isHorizontal = !!horizontal;
2177
- const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
2178
- const nativeOffset = toNativeHorizontalOffset(state, offset, contentSize);
2179
- scroller.scrollTo({
2180
- animated: isAnimated,
2181
- x: isHorizontal ? nativeOffset : 0,
2182
- y: isHorizontal ? 0 : offset
2183
- });
2184
- if (isInitialScroll) {
2185
- initialScrollCompletion.markInitialScrollNativeDispatch(state);
1918
+ const totalSize = getContentSize(ctx);
1919
+ const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
1920
+ if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
1921
+ return;
2186
1922
  }
2187
- if (!isAnimated) {
2188
- state.scroll = offset;
2189
- checkFinishedScrollFallback(ctx);
1923
+ const manualDesired = pending.amount - predictedNativeClamp;
1924
+ if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
1925
+ return;
2190
1926
  }
1927
+ pending.manualApplied = manualDesired;
1928
+ requestAdjust(ctx, manualDesired, true);
1929
+ pending.furthestProgressTowardAmount = 0;
2191
1930
  }
2192
-
2193
- // src/utils/requestAdjust.ts
2194
- function requestAdjust(ctx, positionDiff, dataChanged) {
1931
+ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2195
1932
  const state = ctx.state;
2196
- if (Math.abs(positionDiff) > 0.1) {
2197
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2198
- const doit = () => {
2199
- if (needsScrollWorkaround) {
2200
- doScrollTo(ctx, { horizontal: state.props.horizontal, offset: state.scroll });
2201
- } else {
2202
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2203
- if (state.adjustingFromInitialMount) {
2204
- state.adjustingFromInitialMount--;
2205
- }
2206
- }
2207
- };
2208
- state.scroll += positionDiff;
2209
- state.scrollForNextCalculateItemsInView = void 0;
2210
- const readyToRender = peek$(ctx, "readyToRender");
2211
- if (readyToRender) {
2212
- doit();
2213
- if (Platform.OS !== "web") {
2214
- const threshold = state.scroll - positionDiff / 2;
2215
- if (!state.ignoreScrollFromMVCP) {
2216
- state.ignoreScrollFromMVCP = {};
2217
- }
2218
- if (positionDiff > 0) {
2219
- state.ignoreScrollFromMVCP.lt = threshold;
2220
- } else {
2221
- state.ignoreScrollFromMVCP.gt = threshold;
2222
- }
2223
- if (state.ignoreScrollFromMVCPTimeout) {
2224
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
2225
- }
2226
- const delay = needsScrollWorkaround ? 250 : 100;
2227
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2228
- var _a3;
2229
- state.ignoreScrollFromMVCP = void 0;
2230
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2231
- if (shouldForceUpdate) {
2232
- state.ignoreScrollFromMVCPIgnored = false;
2233
- state.scrollPending = state.scroll;
2234
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2235
- }
2236
- }, delay);
2237
- }
2238
- } else {
2239
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2240
- requestAnimationFrame(doit);
2241
- }
1933
+ const pending = state.pendingNativeMVCPAdjust;
1934
+ if (!pending) {
1935
+ return false;
2242
1936
  }
2243
- }
2244
-
2245
- // src/core/updateContentMetrics.ts
2246
- var SCROLL_ADJUST_EPSILON = 0.1;
2247
- function setContentLengthSignal(ctx, signalName, size) {
2248
- const didChange = peek$(ctx, signalName) !== size;
2249
- if (didChange) {
2250
- set$(ctx, signalName, size);
2251
- updateContentMetricsState(ctx);
1937
+ const remainingAfterManual = pending.amount - pending.manualApplied;
1938
+ const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
1939
+ const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
1940
+ const progressTowardAmount = getProgressTowardAmount(remainingAfterManual, nativeDelta);
1941
+ if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
1942
+ state.pendingNativeMVCPAdjust = void 0;
1943
+ return true;
2252
1944
  }
2253
- return didChange;
2254
- }
2255
- function shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, nextHeaderSize) {
2256
- const { didContainersLayout, didFinishInitialScroll, props, scroll, scrollingTo } = ctx.state;
2257
- const sizeDiff = nextHeaderSize - previousHeaderSize;
2258
- const leadingPadding = props.horizontal ? props.stylePaddingLeft : props.stylePaddingTop;
2259
- const previousHeaderEnd = (leadingPadding || 0) + previousHeaderSize;
2260
- return Platform.OS === "web" && props.maintainVisibleContentPosition.size && didContainersLayout && didFinishInitialScroll && !scrollingTo && scroll >= previousHeaderEnd - SCROLL_ADJUST_EPSILON && Math.abs(sizeDiff) > SCROLL_ADJUST_EPSILON;
2261
- }
2262
- function setHeaderSize(ctx, size) {
2263
- const { state } = ctx;
2264
- const previousHeaderSize = peek$(ctx, "headerSize") || 0;
2265
- const didChange = previousHeaderSize !== size;
2266
- const hasMeasuredOrEstimatedHeaderBaseline = state.didMeasureHeader || previousHeaderSize > SCROLL_ADJUST_EPSILON;
2267
- if (didChange) {
2268
- set$(ctx, "headerSize", size);
2269
- updateContentMetricsState(ctx);
2270
- if (hasMeasuredOrEstimatedHeaderBaseline && shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, size)) {
2271
- requestAdjust(ctx, size - previousHeaderSize);
2272
- }
1945
+ if (isWrongDirection) {
1946
+ state.pendingNativeMVCPAdjust = void 0;
1947
+ return false;
2273
1948
  }
2274
- state.didMeasureHeader = true;
2275
- }
2276
- function setFooterSize(ctx, size) {
2277
- return setContentLengthSignal(ctx, "footerSize", size);
2278
- }
2279
- function areInsetsEqual(left, right) {
2280
- var _a3, _b, _c, _d, _e, _f, _g, _h;
2281
- return ((_a3 = left == null ? void 0 : left.top) != null ? _a3 : 0) === ((_b = right == null ? void 0 : right.top) != null ? _b : 0) && ((_c = left == null ? void 0 : left.bottom) != null ? _c : 0) === ((_d = right == null ? void 0 : right.bottom) != null ? _d : 0) && ((_e = left == null ? void 0 : left.left) != null ? _e : 0) === ((_f = right == null ? void 0 : right.left) != null ? _f : 0) && ((_g = left == null ? void 0 : left.right) != null ? _g : 0) === ((_h = right == null ? void 0 : right.right) != null ? _h : 0);
2282
- }
2283
- function setContentInsetOverride(ctx, inset) {
2284
- const { state } = ctx;
2285
- const previousInset = state.contentInsetOverride;
2286
- const nextInset = inset != null ? inset : void 0;
2287
- const didChange = !areInsetsEqual(previousInset, nextInset);
2288
- state.contentInsetOverride = nextInset;
2289
- if (didChange) {
2290
- updateContentMetricsState(ctx);
1949
+ if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
1950
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1951
+ return true;
2291
1952
  }
2292
- return didChange;
2293
- }
2294
- function useLatestRef(value) {
2295
- const ref = React2__namespace.useRef(value);
2296
- ref.current = value;
2297
- return ref;
2298
- }
2299
-
2300
- // src/hooks/useStableRenderComponent.tsx
2301
- function useStableRenderComponent(renderComponent, mapProps) {
2302
- const renderComponentRef = useLatestRef(renderComponent);
2303
- const mapPropsRef = useLatestRef(mapProps);
2304
- return React2__namespace.useMemo(
2305
- () => React2__namespace.forwardRef(
2306
- (props, ref) => {
2307
- var _a3, _b;
2308
- return (_b = (_a3 = renderComponentRef.current) == null ? void 0 : _a3.call(renderComponentRef, mapPropsRef.current(props, ref))) != null ? _b : null;
2309
- }
2310
- ),
2311
- [mapPropsRef, renderComponentRef]
2312
- );
1953
+ const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
1954
+ const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
1955
+ const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
1956
+ if (isAtExpectedNativeClamp) {
1957
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1958
+ return true;
1959
+ }
1960
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
1961
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1962
+ return true;
1963
+ }
1964
+ if (progressTowardAmount > pending.furthestProgressTowardAmount + MVCP_POSITION_EPSILON) {
1965
+ pending.furthestProgressTowardAmount = progressTowardAmount;
1966
+ return false;
1967
+ }
1968
+ if (pending.furthestProgressTowardAmount > MVCP_POSITION_EPSILON && progressTowardAmount < pending.furthestProgressTowardAmount - MVCP_POSITION_EPSILON) {
1969
+ state.pendingNativeMVCPAdjust = void 0;
1970
+ return false;
1971
+ }
1972
+ return false;
2313
1973
  }
2314
- var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
2315
- const localRef = React2.useRef(null);
2316
- const ref = refView != null ? refView : localRef;
2317
- const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
2318
- return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
2319
- };
2320
-
2321
- // src/components/ListComponent.tsx
2322
- var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
2323
- const [alignItemsAtEndPadding = 0] = useArr$(["alignItemsAtEndPadding"]);
2324
- if (alignItemsAtEndPadding <= 0) {
2325
- return null;
1974
+ function prepareMVCP(ctx, dataChanged) {
1975
+ const state = ctx.state;
1976
+ const { idsInView, positions, props } = state;
1977
+ const {
1978
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
1979
+ } = props;
1980
+ const isWeb = Platform.OS === "web";
1981
+ const now = Date.now();
1982
+ const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
1983
+ const scrollingTo = state.scrollingTo;
1984
+ if (isWeb && dataChanged && state.pendingScrollToEnd && scrollingTo === void 0) {
1985
+ state.mvcpAnchorLock = void 0;
1986
+ return void 0;
2326
1987
  }
2327
- return /* @__PURE__ */ React2__namespace.createElement(
2328
- View,
2329
- {
2330
- style: horizontal ? { flexShrink: 0, width: alignItemsAtEndPadding } : { flexShrink: 0, height: alignItemsAtEndPadding }
2331
- },
2332
- null
2333
- );
2334
- });
2335
- var ListComponent = typedMemo(function ListComponent2({
2336
- canRender,
2337
- style,
2338
- contentContainerStyle,
2339
- horizontal,
2340
- initialContentOffset,
2341
- recycleItems,
2342
- ItemSeparatorComponent,
2343
- alignItemsAtEnd: _alignItemsAtEnd,
2344
- onScroll: onScroll2,
2345
- onLayout,
2346
- ListHeaderComponent,
2347
- ListHeaderComponentStyle,
2348
- ListFooterComponent,
2349
- ListFooterComponentStyle,
2350
- ListEmptyComponent,
2351
- getRenderedItem: getRenderedItem2,
2352
- updateItemSize: updateItemSize2,
2353
- refScrollView,
2354
- renderScrollComponent,
2355
- onLayoutFooter,
2356
- scrollAdjustHandler,
2357
- snapToIndices,
2358
- stickyHeaderConfig,
2359
- stickyHeaderIndices,
2360
- useWindowScroll = false,
2361
- ...rest
2362
- }) {
2363
- const ctx = useStateContext();
2364
- const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
2365
- const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
2366
- const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
2367
- const autoOtherAxisStyle = getAutoOtherAxisStyle({
2368
- horizontal,
2369
- needsOtherAxisSize: ctx.state.needsOtherAxisSize,
2370
- otherAxisSize
2371
- });
2372
- const CustomScrollComponent = useStableRenderComponent(
2373
- renderScrollComponent,
2374
- (props, ref) => ({ ...props, ref })
2375
- );
2376
- const ScrollComponent = renderScrollComponent ? CustomScrollComponent : ListComponentScrollView;
2377
- const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
2378
- const updateFooterSize = React2.useCallback(
2379
- (size, afterSizeUpdate) => {
2380
- var _a3;
2381
- const didFooterSizeChange = setFooterSize(ctx, size);
2382
- afterSizeUpdate == null ? void 0 : afterSizeUpdate();
2383
- if (didFooterSizeChange && ((_a3 = ctx.state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onFooterLayout)) {
2384
- doMaintainScrollAtEnd(ctx);
1988
+ const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
1989
+ let prevPosition;
1990
+ let targetId;
1991
+ const idsInViewWithPositions = [];
1992
+ const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1993
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
1994
+ const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
1995
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1996
+ const indexByKey = state.indexByKey;
1997
+ const prevScroll = state.scroll;
1998
+ const prevTotalSize = getContentSize(ctx);
1999
+ if (shouldMVCP) {
2000
+ if (!isWeb && state.pendingNativeMVCPAdjust && scrollTarget === void 0) {
2001
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
2002
+ return void 0;
2003
+ }
2004
+ if (anchorLock && scrollTarget === void 0) {
2005
+ targetId = anchorLock.id;
2006
+ prevPosition = anchorLock.position;
2007
+ } else if (scrollTarget !== void 0) {
2008
+ if (!IsNewArchitecture && (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll)) {
2009
+ return void 0;
2385
2010
  }
2386
- },
2387
- [ctx]
2388
- );
2389
- React2.useLayoutEffect(() => {
2390
- if (!ListHeaderComponent) {
2391
- setHeaderSize(ctx, 0);
2011
+ targetId = getId(state, scrollTarget);
2012
+ } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
2013
+ targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
2392
2014
  }
2393
- if (!ListFooterComponent) {
2394
- updateFooterSize(0);
2015
+ if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
2016
+ for (let i = 0; i < idsInView.length; i++) {
2017
+ const id = idsInView[i];
2018
+ const index = indexByKey.get(id);
2019
+ if (index !== void 0) {
2020
+ const position = positions[index];
2021
+ if (position !== void 0) {
2022
+ idsInViewWithPositions.push({ id, position });
2023
+ }
2024
+ }
2025
+ }
2395
2026
  }
2396
- }, [ListHeaderComponent, ListFooterComponent, ctx, updateFooterSize]);
2397
- const onLayoutHeader = React2.useCallback(
2398
- (rect) => {
2399
- const size = rect[horizontal ? "width" : "height"];
2400
- setHeaderSize(ctx, size);
2401
- },
2402
- [ctx, horizontal]
2403
- );
2404
- const onLayoutFooterInternal = React2.useCallback(
2405
- (rect, fromLayoutEffect) => {
2406
- const size = rect[horizontal ? "width" : "height"];
2407
- updateFooterSize(size, () => {
2408
- onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
2409
- });
2410
- },
2411
- [horizontal, onLayoutFooter, updateFooterSize]
2412
- );
2413
- return /* @__PURE__ */ React2__namespace.createElement(
2414
- SnapOrScroll,
2415
- {
2416
- ...rest,
2417
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
2418
- contentContainerStyle: [
2419
- horizontal ? {
2420
- height: "100%"
2421
- } : {},
2422
- contentContainerStyle
2423
- ],
2424
- contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
2425
- horizontal,
2426
- maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
2427
- onLayout,
2428
- onScroll: onScroll2,
2429
- ref: refScrollView,
2430
- ScrollComponent: snapToIndices ? ScrollComponent : void 0,
2431
- style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
2432
- },
2433
- /* @__PURE__ */ React2__namespace.createElement(ScrollAdjust, null),
2434
- ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
2435
- ListEmptyComponent && getComponent(ListEmptyComponent),
2436
- shouldRenderAlignItemsAtEndSpacer && /* @__PURE__ */ React2__namespace.createElement(AlignItemsAtEndSpacer, { horizontal }),
2437
- canRender && !ListEmptyComponent && /* @__PURE__ */ React2__namespace.createElement(
2438
- Containers,
2439
- {
2440
- getRenderedItem: getRenderedItem2,
2441
- horizontal,
2442
- ItemSeparatorComponent,
2443
- recycleItems,
2444
- stickyHeaderConfig,
2445
- updateItemSize: updateItemSize2
2446
- }
2447
- ),
2448
- ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
2449
- Platform.OS === "web" && /* @__PURE__ */ React2__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
2450
- IS_DEV && ENABLE_DEVMODE
2451
- );
2452
- });
2453
- var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
2454
- var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
2455
- var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
2456
- function useDevChecksImpl(props) {
2457
- const ctx = useStateContext();
2458
- const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
2459
- React2.useEffect(() => {
2460
- if (useWindowScroll && renderScrollComponent) {
2461
- warnDevOnce(
2462
- "useWindowScrollRenderScrollComponent",
2463
- "useWindowScroll is not supported when renderScrollComponent is provided."
2464
- );
2465
- }
2466
- }, [renderScrollComponent, useWindowScroll]);
2467
- React2.useEffect(() => {
2468
- if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
2469
- warnDevOnce(
2470
- "keyExtractor",
2471
- "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
2472
- );
2473
- }
2474
- }, [childrenMode, ctx, keyExtractor]);
2475
- React2.useEffect(() => {
2476
- const state = ctx.state;
2477
- const dataLength = state.props.data.length;
2478
- const useWindowScrollResolved = state.props.useWindowScroll;
2479
- if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
2480
- return;
2481
- }
2482
- const warnIfUnboundedOuterSize = () => {
2483
- const readyToRender = peek$(ctx, "readyToRender");
2484
- const numContainers = peek$(ctx, "numContainers") || 0;
2485
- const totalSize = peek$(ctx, "totalSize") || 0;
2486
- const scrollLength = ctx.state.scrollLength || 0;
2487
- if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
2488
- return;
2489
- }
2490
- const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
2491
- const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
2492
- if (rendersAlmostEverything && viewportMatchesContent) {
2493
- warnDevOnce(
2494
- "webUnboundedOuterSize",
2495
- "LegendList appears to have an unbounded outer height on web, so virtualization is effectively disabled. Set a bounded height or flex: 1 on the list container, or use useWindowScroll."
2496
- );
2497
- }
2498
- };
2499
- warnIfUnboundedOuterSize();
2500
- const unsubscribe = [
2501
- listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
2502
- listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
2503
- listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
2504
- ];
2505
- return () => {
2506
- for (const unsub of unsubscribe) {
2507
- unsub();
2508
- }
2509
- };
2510
- }, [ctx]);
2511
- }
2512
- function useDevChecksNoop(_props) {
2513
- }
2514
- var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
2515
-
2516
- // src/core/mvcp.ts
2517
- var MVCP_POSITION_EPSILON = 0.1;
2518
- var MVCP_ANCHOR_LOCK_TTL_MS = 300;
2519
- var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
2520
- var NATIVE_END_CLAMP_EPSILON = 1;
2521
- function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
2522
- if (!enableMVCPAnchorLock) {
2523
- state.mvcpAnchorLock = void 0;
2524
- return void 0;
2525
- }
2526
- const lock = state.mvcpAnchorLock;
2527
- if (!lock) {
2528
- return void 0;
2529
- }
2530
- const isExpired = now > lock.expiresAt;
2531
- const isMissing = state.indexByKey.get(lock.id) === void 0;
2532
- if (isExpired || isMissing || !mvcpData) {
2533
- state.mvcpAnchorLock = void 0;
2534
- return void 0;
2535
- }
2536
- return lock;
2537
- }
2538
- function updateAnchorLock(state, params) {
2539
- if (Platform.OS === "web") {
2540
- const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
2541
- const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
2542
- const mvcpData = state.props.maintainVisibleContentPosition.data;
2543
- if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
2544
- return;
2545
- }
2546
- const existingLock = state.mvcpAnchorLock;
2547
- const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
2548
- if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
2549
- state.mvcpAnchorLock = void 0;
2550
- return;
2551
- }
2552
- state.mvcpAnchorLock = {
2553
- expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
2554
- id: anchorId,
2555
- position: anchorPosition,
2556
- quietPasses
2557
- };
2558
- }
2559
- }
2560
- function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
2561
- if (!dataChanged || Platform.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
2562
- return false;
2563
- }
2564
- const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
2565
- return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
2566
- }
2567
- function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
2568
- if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
2569
- return 0;
2570
- }
2571
- const maxScroll = Math.max(0, totalSize - state.scrollLength);
2572
- const clampDelta = maxScroll - state.scroll;
2573
- if (unresolvedAmount < 0) {
2574
- return Math.max(unresolvedAmount, Math.min(0, clampDelta));
2575
- }
2576
- if (unresolvedAmount > 0) {
2577
- return Math.min(unresolvedAmount, Math.max(0, clampDelta));
2578
- }
2579
- return 0;
2580
- }
2581
- function getProgressTowardAmount(targetDelta, nativeDelta) {
2582
- return targetDelta < 0 ? -nativeDelta : nativeDelta;
2583
- }
2584
- function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
2585
- const state = ctx.state;
2586
- state.pendingNativeMVCPAdjust = void 0;
2587
- const remaining = remainingAfterManual - nativeDelta;
2588
- if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
2589
- requestAdjust(ctx, remaining, true);
2590
- }
2591
- }
2592
- function maybeApplyPredictedNativeMVCPAdjust(ctx) {
2593
- const state = ctx.state;
2594
- const pending = state.pendingNativeMVCPAdjust;
2595
- if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
2596
- return;
2597
- }
2598
- const totalSize = getContentSize(ctx);
2599
- const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
2600
- if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
2601
- return;
2602
- }
2603
- const manualDesired = pending.amount - predictedNativeClamp;
2604
- if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
2605
- return;
2606
- }
2607
- pending.manualApplied = manualDesired;
2608
- requestAdjust(ctx, manualDesired, true);
2609
- pending.furthestProgressTowardAmount = 0;
2610
- }
2611
- function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2612
- const state = ctx.state;
2613
- const pending = state.pendingNativeMVCPAdjust;
2614
- if (!pending) {
2615
- return false;
2616
- }
2617
- const remainingAfterManual = pending.amount - pending.manualApplied;
2618
- const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
2619
- const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
2620
- const progressTowardAmount = getProgressTowardAmount(remainingAfterManual, nativeDelta);
2621
- if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
2622
- state.pendingNativeMVCPAdjust = void 0;
2623
- return true;
2624
- }
2625
- if (isWrongDirection) {
2626
- state.pendingNativeMVCPAdjust = void 0;
2627
- return false;
2628
- }
2629
- if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
2630
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2631
- return true;
2632
- }
2633
- const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
2634
- const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
2635
- const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
2636
- if (isAtExpectedNativeClamp) {
2637
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2638
- return true;
2639
- }
2640
- if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
2641
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2642
- return true;
2643
- }
2644
- if (progressTowardAmount > pending.furthestProgressTowardAmount + MVCP_POSITION_EPSILON) {
2645
- pending.furthestProgressTowardAmount = progressTowardAmount;
2646
- return false;
2647
- }
2648
- if (pending.furthestProgressTowardAmount > MVCP_POSITION_EPSILON && progressTowardAmount < pending.furthestProgressTowardAmount - MVCP_POSITION_EPSILON) {
2649
- state.pendingNativeMVCPAdjust = void 0;
2650
- return false;
2651
- }
2652
- return false;
2653
- }
2654
- function prepareMVCP(ctx, dataChanged) {
2655
- const state = ctx.state;
2656
- const { idsInView, positions, props } = state;
2657
- const {
2658
- maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
2659
- } = props;
2660
- const isWeb = Platform.OS === "web";
2661
- const now = Date.now();
2662
- const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
2663
- const scrollingTo = state.scrollingTo;
2664
- if (isWeb && dataChanged && state.pendingScrollToEnd && scrollingTo === void 0) {
2665
- state.mvcpAnchorLock = void 0;
2666
- return void 0;
2667
- }
2668
- const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
2669
- let prevPosition;
2670
- let targetId;
2671
- const idsInViewWithPositions = [];
2672
- const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
2673
- const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
2674
- const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
2675
- const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
2676
- const indexByKey = state.indexByKey;
2677
- const prevScroll = state.scroll;
2678
- const prevTotalSize = getContentSize(ctx);
2679
- if (shouldMVCP) {
2680
- if (!isWeb && state.pendingNativeMVCPAdjust && scrollTarget === void 0) {
2681
- maybeApplyPredictedNativeMVCPAdjust(ctx);
2682
- return void 0;
2683
- }
2684
- if (anchorLock && scrollTarget === void 0) {
2685
- targetId = anchorLock.id;
2686
- prevPosition = anchorLock.position;
2687
- } else if (scrollTarget !== void 0) {
2688
- if (!IsNewArchitecture && (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll)) {
2689
- return void 0;
2690
- }
2691
- targetId = getId(state, scrollTarget);
2692
- } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
2693
- targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
2694
- }
2695
- if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
2696
- for (let i = 0; i < idsInView.length; i++) {
2697
- const id = idsInView[i];
2698
- const index = indexByKey.get(id);
2699
- if (index !== void 0) {
2700
- const position = positions[index];
2701
- if (position !== void 0) {
2702
- idsInViewWithPositions.push({ id, position });
2703
- }
2704
- }
2705
- }
2706
- }
2707
- if (targetId !== void 0 && prevPosition === void 0) {
2708
- const targetIndex = indexByKey.get(targetId);
2709
- if (targetIndex !== void 0) {
2710
- prevPosition = positions[targetIndex];
2027
+ if (targetId !== void 0 && prevPosition === void 0) {
2028
+ const targetIndex = indexByKey.get(targetId);
2029
+ if (targetIndex !== void 0) {
2030
+ prevPosition = positions[targetIndex];
2711
2031
  }
2712
2032
  }
2713
2033
  return () => {
@@ -2763,6 +2083,9 @@ function prepareMVCP(ctx, dataChanged) {
2763
2083
  if (diff > 0) {
2764
2084
  diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
2765
2085
  } else {
2086
+ const maxScroll = Math.max(0, totalSize - state.scrollLength);
2087
+ state.scroll = maxScroll;
2088
+ state.scrollPending = maxScroll;
2766
2089
  diff = 0;
2767
2090
  }
2768
2091
  }
@@ -4772,7 +4095,21 @@ function getIdsInVisibleRange(state, range) {
4772
4095
  }
4773
4096
  return idsInView;
4774
4097
  }
4775
- function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4098
+ function maybeEmitFirstVisibleItemChanged(state, index) {
4099
+ var _a3;
4100
+ const onFirstVisibleItemChanged = state.props.onFirstVisibleItemChanged;
4101
+ if (!onFirstVisibleItemChanged || index === null || index < 0 || index >= state.props.data.length) {
4102
+ return;
4103
+ }
4104
+ const key = (_a3 = state.idCache[index]) != null ? _a3 : getId(state, index);
4105
+ const previous = state.lastFirstVisibleItemCallback;
4106
+ if ((previous == null ? void 0 : previous.index) === index && previous.key === key) {
4107
+ return;
4108
+ }
4109
+ state.lastFirstVisibleItemCallback = { index, key };
4110
+ onFirstVisibleItemChanged({ index, item: state.props.data[index], key });
4111
+ }
4112
+ function findFirstVisibleIndexInCachedRange(ctx, scroll) {
4776
4113
  var _a3, _b;
4777
4114
  const state = ctx.state;
4778
4115
  const {
@@ -4784,18 +4121,42 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4784
4121
  startBuffered
4785
4122
  } = state;
4786
4123
  if (startBuffered === null || endBuffered === null || startBuffered < 0 || endBuffered < startBuffered) {
4787
- return;
4124
+ return null;
4788
4125
  }
4789
- const visibleRange = {
4790
- endNoBuffer: null,
4791
- firstFullyOnScreenIndex: void 0,
4792
- startNoBuffer: null
4793
- };
4794
4126
  for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
4795
4127
  const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
4796
4128
  const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
4797
4129
  const top = positions[i];
4798
- const didPassVisibleEnd = trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
4130
+ if (top + size > scroll) {
4131
+ return i;
4132
+ }
4133
+ }
4134
+ return null;
4135
+ }
4136
+ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4137
+ var _a3, _b;
4138
+ const state = ctx.state;
4139
+ const {
4140
+ endBuffered,
4141
+ idCache,
4142
+ positions,
4143
+ props: { data },
4144
+ sizes,
4145
+ startBuffered
4146
+ } = state;
4147
+ if (startBuffered === null || endBuffered === null || startBuffered < 0 || endBuffered < startBuffered) {
4148
+ return;
4149
+ }
4150
+ const visibleRange = {
4151
+ endNoBuffer: null,
4152
+ firstFullyOnScreenIndex: void 0,
4153
+ startNoBuffer: null
4154
+ };
4155
+ for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
4156
+ const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
4157
+ const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
4158
+ const top = positions[i];
4159
+ const didPassVisibleEnd = trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
4799
4160
  if (didPassVisibleEnd) {
4800
4161
  break;
4801
4162
  }
@@ -4806,6 +4167,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4806
4167
  idsInView: getIdsInVisibleRange(state, visibleRange),
4807
4168
  startNoBuffer: visibleRange.startNoBuffer
4808
4169
  });
4170
+ maybeEmitFirstVisibleItemChanged(state, visibleRange.startNoBuffer);
4809
4171
  if (visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4810
4172
  updateViewableItems(
4811
4173
  state,
@@ -4931,6 +4293,8 @@ function calculateItemsInView(ctx, params = {}) {
4931
4293
  scroll,
4932
4294
  scrollBottom
4933
4295
  );
4296
+ } else if (state.props.onFirstVisibleItemChanged) {
4297
+ maybeEmitFirstVisibleItemChanged(state, findFirstVisibleIndexInCachedRange(ctx, scroll));
4934
4298
  }
4935
4299
  (_d = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _d.call(stickyState);
4936
4300
  return;
@@ -4970,7 +4334,7 @@ function calculateItemsInView(ctx, params = {}) {
4970
4334
  const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4971
4335
  checkMVCP == null ? void 0 : checkMVCP();
4972
4336
  const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4973
- if (didMVCPAdjustScroll && initialScroll) {
4337
+ if (didMVCPAdjustScroll && (initialScroll || state.scrollingTo)) {
4974
4338
  updateScroll2(state.scroll);
4975
4339
  updateScrollRange();
4976
4340
  }
@@ -5166,87 +4530,1035 @@ function calculateItemsInView(ctx, params = {}) {
5166
4530
  state.userScrollAnchorReset = void 0;
5167
4531
  }
5168
4532
  }
5169
- if (alwaysRenderArr.length > 0) {
5170
- for (const index of alwaysRenderArr) {
5171
- if (index < 0 || index >= dataLength) continue;
5172
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
5173
- const containerIndex = containerItemKeys.get(id);
5174
- if (containerIndex !== void 0) {
5175
- state.stickyContainerPool.add(containerIndex);
5176
- }
5177
- }
4533
+ if (alwaysRenderArr.length > 0) {
4534
+ for (const index of alwaysRenderArr) {
4535
+ if (index < 0 || index >= dataLength) continue;
4536
+ const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4537
+ const containerIndex = containerItemKeys.get(id);
4538
+ if (containerIndex !== void 0) {
4539
+ state.stickyContainerPool.add(containerIndex);
4540
+ }
4541
+ }
4542
+ }
4543
+ }
4544
+ if (state.stickyContainerPool.size > 0) {
4545
+ handleStickyRecycling(
4546
+ ctx,
4547
+ stickyHeaderIndicesArr,
4548
+ scroll,
4549
+ drawDistance,
4550
+ (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
4551
+ pendingRemoval,
4552
+ alwaysRenderSet
4553
+ );
4554
+ }
4555
+ const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
4556
+ let didChangePositions = false;
4557
+ for (let i = 0; i < numContainers; i++) {
4558
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
4559
+ if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
4560
+ if (itemKey !== void 0) {
4561
+ containerItemKeys.delete(itemKey);
4562
+ }
4563
+ state.containerItemTypes.delete(i);
4564
+ if (state.stickyContainerPool.has(i)) {
4565
+ set$(ctx, `containerSticky${i}`, false);
4566
+ state.stickyContainerPool.delete(i);
4567
+ }
4568
+ set$(ctx, `containerItemKey${i}`, void 0);
4569
+ set$(ctx, `containerItemData${i}`, void 0);
4570
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4571
+ set$(ctx, `containerColumn${i}`, -1);
4572
+ set$(ctx, `containerSpan${i}`, 1);
4573
+ } else {
4574
+ const itemIndex = indexByKey.get(itemKey);
4575
+ if (itemIndex !== void 0) {
4576
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4577
+ scrollAdjustPending,
4578
+ updateLayout: true
4579
+ }).didChangePosition || didChangePositions;
4580
+ }
4581
+ }
4582
+ }
4583
+ if (Platform.OS === "web" && didChangePositions) {
4584
+ set$(ctx, "lastPositionUpdate", Date.now());
4585
+ }
4586
+ if (suppressInitialScrollSideEffects) {
4587
+ evaluateBootstrapInitialScroll(ctx);
4588
+ return;
4589
+ }
4590
+ maybeEmitFirstVisibleItemChanged(state, visibleRange.startNoBuffer);
4591
+ if (!queuedInitialLayout && !state.didContainersLayout) {
4592
+ const isInitialLayoutReady = hasActiveInitialScroll(state) ? checkAllSizesKnown(state, state.startBuffered, state.endBuffered) : checkAllSizesKnown(state, state.startNoBuffer, state.endNoBuffer) || checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
4593
+ if (isInitialLayoutReady) {
4594
+ setDidLayout(ctx);
4595
+ handleInitialScrollLayoutReady(ctx);
4596
+ }
4597
+ }
4598
+ if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4599
+ if (!didMVCPAdjustScroll) {
4600
+ updateViewableItems(
4601
+ ctx.state,
4602
+ ctx,
4603
+ viewabilityConfigCallbackPairs,
4604
+ scrollLength,
4605
+ visibleRange.startNoBuffer,
4606
+ visibleRange.endNoBuffer,
4607
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4608
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4609
+ );
4610
+ }
4611
+ }
4612
+ (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
4613
+ });
4614
+ }
4615
+
4616
+ // src/core/updateAnchoredEndSpace.ts
4617
+ function maybeUpdateAnchoredEndSpace(ctx) {
4618
+ var _a3, _b;
4619
+ const state = ctx.state;
4620
+ const anchoredEndSpace = state.props.anchoredEndSpace;
4621
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
4622
+ const previousReadyAnchorIndex = state.anchoredEndSpaceReadyAnchorIndex;
4623
+ const previousReadyAnchorKey = state.anchoredEndSpaceReadyAnchorKey;
4624
+ const nextAnchorIndex = anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex;
4625
+ let nextAnchorKey;
4626
+ let isReady = true;
4627
+ let nextSize = 0;
4628
+ if (anchoredEndSpace) {
4629
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
4630
+ const { data } = state.props;
4631
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
4632
+ nextAnchorKey = getId(state, anchorIndex);
4633
+ let contentBelowAnchor = 0;
4634
+ const footerSize = ctx.values.get("footerSize") || 0;
4635
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4636
+ let hasUnknownTailSize = false;
4637
+ for (let index = anchorIndex; index < data.length; index++) {
4638
+ const size = getKnownOrFixedItemSize(ctx, index);
4639
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
4640
+ if (size === void 0) {
4641
+ hasUnknownTailSize = true;
4642
+ }
4643
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
4644
+ contentBelowAnchor += effectiveSize;
4645
+ }
4646
+ }
4647
+ contentBelowAnchor += footerSize + stylePaddingBottom;
4648
+ isReady = !hasUnknownTailSize;
4649
+ nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4650
+ } else if (anchorIndex >= 0) {
4651
+ isReady = false;
4652
+ }
4653
+ }
4654
+ const didSizeChange = previousSize !== nextSize;
4655
+ const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
4656
+ if (isReady && (didSizeChange || didReadyAnchorChange)) {
4657
+ state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
4658
+ state.anchoredEndSpaceReadyAnchorKey = nextAnchorKey;
4659
+ if (didSizeChange) {
4660
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
4661
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4662
+ }
4663
+ if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
4664
+ updateScroll(ctx, state.scroll, true);
4665
+ }
4666
+ (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
4667
+ }
4668
+ return nextSize;
4669
+ }
4670
+ var PixelRatio = ReactNative.PixelRatio;
4671
+
4672
+ // src/utils/layoutMeasurement.ts
4673
+ var FLOATING_POINT_SLACK = 0.01;
4674
+ var NATIVE_LAYOUT_MEASUREMENT_EPSILON = 1 / PixelRatio.get() + FLOATING_POINT_SLACK;
4675
+ function isWithinEpsilon(delta) {
4676
+ return Math.abs(delta) <= NATIVE_LAYOUT_MEASUREMENT_EPSILON;
4677
+ }
4678
+ function isNativeLayoutNoise(delta) {
4679
+ return isWithinEpsilon(delta);
4680
+ }
4681
+
4682
+ // src/core/updateItemSizes.ts
4683
+ function runOrScheduleMVCPRecalculate(ctx) {
4684
+ var _a3;
4685
+ const state = ctx.state;
4686
+ if (state.userScrollAnchorReset !== void 0) {
4687
+ calculateItemsInView(ctx);
4688
+ if (((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4689
+ state.userScrollAnchorReset = void 0;
4690
+ }
4691
+ } else if (Platform.OS === "web") {
4692
+ if (!state.mvcpAnchorLock) {
4693
+ if (state.queuedMVCPRecalculate !== void 0) {
4694
+ cancelAnimationFrame(state.queuedMVCPRecalculate);
4695
+ state.queuedMVCPRecalculate = void 0;
4696
+ }
4697
+ calculateItemsInView(ctx, { doMVCP: true });
4698
+ } else if (state.queuedMVCPRecalculate === void 0) {
4699
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
4700
+ state.queuedMVCPRecalculate = void 0;
4701
+ calculateItemsInView(ctx, { doMVCP: true });
4702
+ });
4703
+ }
4704
+ } else {
4705
+ calculateItemsInView(ctx, { doMVCP: true });
4706
+ }
4707
+ }
4708
+ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
4709
+ const state = ctx.state;
4710
+ if (state.needsOtherAxisSize) {
4711
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
4712
+ const currentOtherAxisSize = peek$(ctx, "otherAxisSize");
4713
+ if (!currentOtherAxisSize || otherAxisSize > currentOtherAxisSize) {
4714
+ set$(ctx, "otherAxisSize", otherAxisSize);
4715
+ }
4716
+ }
4717
+ }
4718
+ function mergeItemSizeUpdateResult(result, next) {
4719
+ result.didChange || (result.didChange = next.didChange);
4720
+ result.didMeasureUserScrollAnchorResetItem || (result.didMeasureUserScrollAnchorResetItem = next.didMeasureUserScrollAnchorResetItem);
4721
+ result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4722
+ result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4723
+ }
4724
+ function flushItemSizeUpdates(ctx, result) {
4725
+ var _a3;
4726
+ const state = ctx.state;
4727
+ if (result.needsRecalculate) {
4728
+ state.scrollForNextCalculateItemsInView = void 0;
4729
+ runOrScheduleMVCPRecalculate(ctx);
4730
+ } else if (result.didMeasureUserScrollAnchorResetItem && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4731
+ state.userScrollAnchorReset = void 0;
4732
+ }
4733
+ if (result.didChange && result.shouldMaintainScrollAtEnd) {
4734
+ doMaintainScrollAtEnd(ctx);
4735
+ }
4736
+ }
4737
+ function updateItemSizes(ctx, measurement) {
4738
+ var _a3, _b, _c, _d;
4739
+ const state = ctx.state;
4740
+ const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4741
+ const shouldBatchPendingMeasurements = IsNewArchitecture && measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4742
+ if (!shouldBatchPendingMeasurements) {
4743
+ flushItemSizeUpdates(ctx, applyItemSize(ctx, measurement.itemKey, measurement.size));
4744
+ } else {
4745
+ const result = {};
4746
+ const updateContainerItemSize = (itemKey, containerId, size) => {
4747
+ if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4748
+ mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
4749
+ }
4750
+ };
4751
+ updateContainerItemSize(measurement.itemKey, measurement.containerId, measurement.size);
4752
+ const keys = Array.from(pendingKeys);
4753
+ for (const itemKey of keys) {
4754
+ const containerId = state.containerItemKeys.get(itemKey);
4755
+ if (containerId !== void 0) {
4756
+ (_d = (_c = (_b = ctx.viewRefs.get(containerId)) == null ? void 0 : _b.current) == null ? void 0 : _c.measure) == null ? void 0 : _d.call(_c, (_x, _y, width, height) => {
4757
+ if (pendingKeys.has(itemKey)) {
4758
+ updateContainerItemSize(itemKey, containerId, { height, width });
4759
+ }
4760
+ });
4761
+ }
4762
+ }
4763
+ flushItemSizeUpdates(ctx, result);
4764
+ }
4765
+ }
4766
+ function applyItemSize(ctx, itemKey, sizeObj) {
4767
+ var _a3;
4768
+ const state = ctx.state;
4769
+ const userScrollAnchorReset = state.userScrollAnchorReset;
4770
+ const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
4771
+ const {
4772
+ didContainersLayout,
4773
+ sizesKnown,
4774
+ props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
4775
+ } = state;
4776
+ if (!data) return { didMeasureUserScrollAnchorResetItem };
4777
+ const index = state.indexByKey.get(itemKey);
4778
+ let resolvedMeasurementItem;
4779
+ if (getFixedItemSize) {
4780
+ if (index === void 0) {
4781
+ return { didMeasureUserScrollAnchorResetItem };
4782
+ }
4783
+ const itemData = state.props.data[index];
4784
+ if (itemData === void 0) {
4785
+ return { didMeasureUserScrollAnchorResetItem };
4786
+ }
4787
+ const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4788
+ const size2 = getFixedItemSize(itemData, index, type);
4789
+ resolvedMeasurementItem = {
4790
+ didResolveFixedItemSize: true,
4791
+ fixedItemSize: size2,
4792
+ itemData,
4793
+ itemType: type
4794
+ };
4795
+ if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
4796
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4797
+ return { didMeasureUserScrollAnchorResetItem };
4798
+ }
4799
+ }
4800
+ let needsRecalculate = !didContainersLayout;
4801
+ let shouldMaintainScrollAtEnd = false;
4802
+ let minIndexSizeChanged;
4803
+ const prevSizeKnown = state.sizesKnown.get(itemKey);
4804
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
4805
+ const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
4806
+ if (diff !== 0) {
4807
+ minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
4808
+ const { startBuffered, endBuffered } = state;
4809
+ needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
4810
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
4811
+ needsRecalculate = true;
4812
+ }
4813
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
4814
+ shouldMaintainScrollAtEnd = true;
4815
+ }
4816
+ onItemSizeChanged == null ? void 0 : onItemSizeChanged({
4817
+ index,
4818
+ itemData: state.props.data[index],
4819
+ itemKey,
4820
+ previous: size - diff,
4821
+ size
4822
+ });
4823
+ maybeUpdateAnchoredEndSpace(ctx);
4824
+ }
4825
+ if (minIndexSizeChanged !== void 0) {
4826
+ state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
4827
+ }
4828
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4829
+ if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
4830
+ const canMaintainScrollAtEnd = shouldMaintainScrollAtEnd && !!(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout);
4831
+ return {
4832
+ didChange: diff !== 0,
4833
+ didMeasureUserScrollAnchorResetItem,
4834
+ needsRecalculate,
4835
+ shouldMaintainScrollAtEnd: canMaintainScrollAtEnd
4836
+ };
4837
+ }
4838
+ return {
4839
+ didChange: diff !== 0,
4840
+ didMeasureUserScrollAnchorResetItem
4841
+ };
4842
+ }
4843
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4844
+ var _a3, _b, _c;
4845
+ const state = ctx.state;
4846
+ const {
4847
+ indexByKey,
4848
+ sizesKnown,
4849
+ averageSizes,
4850
+ props: { data, horizontal, getItemType, getFixedItemSize }
4851
+ } = state;
4852
+ if (!data) return 0;
4853
+ const index = indexByKey.get(itemKey);
4854
+ const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
4855
+ let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
4856
+ let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
4857
+ if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
4858
+ itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
4859
+ fixedItemSize = getFixedItemSize(itemData, index, itemType);
4860
+ }
4861
+ const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
4862
+ didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
4863
+ fixedItemSize,
4864
+ itemType
4865
+ } : void 0;
4866
+ const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
4867
+ const rawSize = horizontal ? sizeObj.width : sizeObj.height;
4868
+ const prevSizeKnown = sizesKnown.get(itemKey);
4869
+ if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
4870
+ return 0;
4871
+ }
4872
+ const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
4873
+ sizesKnown.set(itemKey, size);
4874
+ if (fixedItemSize === void 0 && size > 0) {
4875
+ itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
4876
+ let averages = averageSizes[itemType];
4877
+ if (!averages) {
4878
+ averages = averageSizes[itemType] = { avg: 0, num: 0 };
4879
+ }
4880
+ if (averages.num === 0) {
4881
+ averages.avg = size;
4882
+ averages.num++;
4883
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
4884
+ averages.avg += (size - prevSizeKnown) / averages.num;
4885
+ } else {
4886
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
4887
+ averages.num++;
4888
+ }
4889
+ }
4890
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
4891
+ setSize(ctx, itemKey, size);
4892
+ return size - prevSize;
4893
+ }
4894
+ return 0;
4895
+ }
4896
+ function useOnLayoutSync({
4897
+ ref,
4898
+ onLayoutProp,
4899
+ onLayoutChange
4900
+ }, deps = []) {
4901
+ const onLayout = React2.useCallback(
4902
+ (event) => {
4903
+ const { layout } = event.nativeEvent;
4904
+ onLayoutChange(layout, false);
4905
+ onLayoutProp == null ? void 0 : onLayoutProp(event);
4906
+ },
4907
+ [onLayoutChange, onLayoutProp]
4908
+ );
4909
+ if (IsNewArchitecture) {
4910
+ React2.useLayoutEffect(() => {
4911
+ if (ref.current) {
4912
+ ref.current.measure((x, y, width, height) => {
4913
+ onLayoutChange({ height, width, x, y }, true);
4914
+ });
4915
+ }
4916
+ }, deps);
4917
+ }
4918
+ return { onLayout };
4919
+ }
4920
+
4921
+ // src/components/Container.tsx
4922
+ function getContainerPositionStyle({
4923
+ columnWrapperStyle,
4924
+ contentContainerAlignItems,
4925
+ horizontal,
4926
+ hasItemSeparator,
4927
+ isHorizontalRTLList,
4928
+ numColumns,
4929
+ otherAxisPos,
4930
+ otherAxisSize
4931
+ }) {
4932
+ let paddingStyles;
4933
+ if (columnWrapperStyle) {
4934
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
4935
+ if (horizontal) {
4936
+ paddingStyles = {
4937
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
4938
+ paddingRight: columnGap || gap || void 0,
4939
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
4940
+ };
4941
+ } else {
4942
+ paddingStyles = {
4943
+ paddingBottom: rowGap || gap || void 0,
4944
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
4945
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
4946
+ };
4947
+ }
4948
+ }
4949
+ return horizontal ? {
4950
+ bottom: contentContainerAlignItems === "flex-end" && numColumns === 1 ? 0 : void 0,
4951
+ boxSizing: paddingStyles ? "border-box" : void 0,
4952
+ direction: isHorizontalRTLList && Platform.OS === "web" ? "ltr" : void 0,
4953
+ flexDirection: hasItemSeparator ? "row" : void 0,
4954
+ height: otherAxisSize,
4955
+ left: 0,
4956
+ position: "absolute",
4957
+ top: contentContainerAlignItems === "flex-end" && numColumns === 1 ? void 0 : otherAxisPos,
4958
+ ...paddingStyles || {}
4959
+ } : {
4960
+ boxSizing: paddingStyles ? "border-box" : void 0,
4961
+ left: otherAxisPos,
4962
+ position: "absolute",
4963
+ right: numColumns > 1 ? null : 0,
4964
+ top: 0,
4965
+ width: otherAxisSize,
4966
+ ...paddingStyles || {}
4967
+ };
4968
+ }
4969
+ var Container = typedMemo(function Container2({
4970
+ id,
4971
+ itemKey,
4972
+ recycleItems,
4973
+ horizontal,
4974
+ getRenderedItem: getRenderedItem2,
4975
+ ItemSeparatorComponent,
4976
+ stickyHeaderConfig
4977
+ }) {
4978
+ const ctx = useStateContext();
4979
+ const { columnWrapperStyle, animatedScrollY } = ctx;
4980
+ const isHorizontalRTLList = isHorizontalRTL(ctx.state);
4981
+ const positionComponentInternal = ctx.state.props.positionComponentInternal;
4982
+ const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
4983
+ const [column = 0, span = 1, data, numColumns = 1, extraData, isSticky] = useArr$([
4984
+ `containerColumn${id}`,
4985
+ `containerSpan${id}`,
4986
+ `containerItemData${id}`,
4987
+ "numColumns",
4988
+ "extraData",
4989
+ `containerSticky${id}`
4990
+ ]);
4991
+ const itemLayoutRef = React2.useRef({
4992
+ didLayout: false,
4993
+ horizontal,
4994
+ itemKey,
4995
+ pendingShrinkToken: 0
4996
+ });
4997
+ itemLayoutRef.current.horizontal = horizontal;
4998
+ itemLayoutRef.current.itemKey = itemKey;
4999
+ const ref = React2.useRef(null);
5000
+ const [layoutRenderCount, forceLayoutRender] = React2.useState(0);
5001
+ const resolvedColumn = column > 0 ? column : 1;
5002
+ const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
5003
+ const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
5004
+ const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
5005
+ const style = React2.useMemo(
5006
+ () => getContainerPositionStyle({
5007
+ columnWrapperStyle,
5008
+ contentContainerAlignItems: ctx.state.props.contentContainerAlignItems,
5009
+ hasItemSeparator: !!ItemSeparatorComponent,
5010
+ horizontal,
5011
+ isHorizontalRTLList,
5012
+ numColumns,
5013
+ otherAxisPos,
5014
+ otherAxisSize
5015
+ }),
5016
+ [
5017
+ horizontal,
5018
+ isHorizontalRTLList,
5019
+ otherAxisPos,
5020
+ otherAxisSize,
5021
+ columnWrapperStyle,
5022
+ ctx.state.props.contentContainerAlignItems,
5023
+ numColumns,
5024
+ ItemSeparatorComponent
5025
+ ]
5026
+ );
5027
+ const renderedItemInfo = React2.useMemo(
5028
+ () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
5029
+ [itemKey, data, extraData]
5030
+ );
5031
+ const { index, renderedItem } = renderedItemInfo || {};
5032
+ const onLayoutChange = React2.useCallback((rectangle, fromLayoutEffect) => {
5033
+ var _a3, _b;
5034
+ const {
5035
+ horizontal: currentHorizontal,
5036
+ itemKey: currentItemKey,
5037
+ lastSize,
5038
+ pendingShrinkToken
5039
+ } = itemLayoutRef.current;
5040
+ if (isNullOrUndefined(currentItemKey)) {
5041
+ return;
5042
+ }
5043
+ itemLayoutRef.current.didLayout = true;
5044
+ let layout = rectangle;
5045
+ const axis = currentHorizontal ? "width" : "height";
5046
+ const size = roundSize(rectangle[axis]);
5047
+ const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
5048
+ const doUpdate = () => {
5049
+ itemLayoutRef.current.lastSize = layout;
5050
+ updateItemSizes(ctx, {
5051
+ containerId: id,
5052
+ fromLayoutEffect,
5053
+ itemKey: currentItemKey,
5054
+ size: layout
5055
+ });
5056
+ itemLayoutRef.current.didLayout = true;
5057
+ };
5058
+ const shouldDeferWebShrinkLayoutUpdate = Platform.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
5059
+ if (shouldDeferWebShrinkLayoutUpdate) {
5060
+ const token = pendingShrinkToken + 1;
5061
+ itemLayoutRef.current.pendingShrinkToken = token;
5062
+ requestAnimationFrame(() => {
5063
+ var _a4;
5064
+ if (itemLayoutRef.current.pendingShrinkToken !== token) {
5065
+ return;
5066
+ }
5067
+ const element = ref.current;
5068
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5069
+ if (rect) {
5070
+ layout = { height: rect.height, width: rect.width };
5071
+ }
5072
+ doUpdate();
5073
+ });
5074
+ return;
5075
+ }
5076
+ if (IsNewArchitecture || size > 0) {
5077
+ doUpdate();
5078
+ } else {
5079
+ (_b = (_a3 = ref.current) == null ? void 0 : _a3.measure) == null ? void 0 : _b.call(_a3, (_x, _y, width, height) => {
5080
+ layout = { height, width };
5081
+ doUpdate();
5082
+ });
5083
+ }
5084
+ }, []);
5085
+ const triggerLayout = React2.useCallback(() => {
5086
+ forceLayoutRender((v) => v + 1);
5087
+ }, []);
5088
+ const contextValue = React2.useMemo(() => {
5089
+ ctx.viewRefs.set(id, ref);
5090
+ return {
5091
+ containerId: id,
5092
+ index,
5093
+ itemKey,
5094
+ triggerLayout,
5095
+ value: data
5096
+ };
5097
+ }, [id, itemKey, index, data, triggerLayout]);
5098
+ React2.useLayoutEffect(() => {
5099
+ ctx.containerLayoutTriggers.set(id, triggerLayout);
5100
+ return () => {
5101
+ if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
5102
+ ctx.containerLayoutTriggers.delete(id);
5103
+ }
5104
+ };
5105
+ }, [ctx, id, triggerLayout]);
5106
+ const { onLayout } = useOnLayoutSync(
5107
+ {
5108
+ onLayoutChange,
5109
+ ref},
5110
+ [itemKey, layoutRenderCount]
5111
+ );
5112
+ if (!IsNewArchitecture) {
5113
+ React2.useEffect(() => {
5114
+ if (!isNullOrUndefined(itemKey)) {
5115
+ itemLayoutRef.current.didLayout = false;
5116
+ const timeout = setTimeout(() => {
5117
+ if (!itemLayoutRef.current.didLayout) {
5118
+ const { itemKey: currentItemKey, lastSize } = itemLayoutRef.current;
5119
+ if (lastSize && !isNullOrUndefined(currentItemKey)) {
5120
+ updateItemSizes(ctx, {
5121
+ containerId: id,
5122
+ fromLayoutEffect: false,
5123
+ itemKey: currentItemKey,
5124
+ size: lastSize
5125
+ });
5126
+ itemLayoutRef.current.didLayout = true;
5127
+ }
5128
+ }
5129
+ }, 16);
5130
+ return () => {
5131
+ clearTimeout(timeout);
5132
+ };
5133
+ }
5134
+ }, [itemKey]);
5135
+ }
5136
+ const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
5137
+ return /* @__PURE__ */ React2__namespace.createElement(
5138
+ PositionComponent,
5139
+ {
5140
+ animatedScrollY: isSticky ? animatedScrollY : void 0,
5141
+ horizontal,
5142
+ id,
5143
+ index,
5144
+ key: recycleItems ? void 0 : itemKey,
5145
+ onLayout,
5146
+ refView: ref,
5147
+ stickyHeaderConfig,
5148
+ style
5149
+ },
5150
+ /* @__PURE__ */ React2__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React2__namespace.createElement(Separator, { ItemSeparatorComponent, leadingItem: renderedItemInfo.item }))
5151
+ );
5152
+ });
5153
+
5154
+ // src/components/ContainerSlot.tsx
5155
+ function ContainerSlotBase({
5156
+ id,
5157
+ horizontal,
5158
+ recycleItems,
5159
+ ItemSeparatorComponent,
5160
+ getRenderedItem: getRenderedItem2,
5161
+ stickyHeaderConfig,
5162
+ ContainerComponent = Container
5163
+ }) {
5164
+ const [itemKey] = useArr$([`containerItemKey${id}`]);
5165
+ if (itemKey === void 0) {
5166
+ return null;
5167
+ }
5168
+ return /* @__PURE__ */ React2__namespace.createElement(
5169
+ ContainerComponent,
5170
+ {
5171
+ getRenderedItem: getRenderedItem2,
5172
+ horizontal,
5173
+ ItemSeparatorComponent,
5174
+ id,
5175
+ itemKey,
5176
+ recycleItems,
5177
+ stickyHeaderConfig
5178
+ }
5179
+ );
5180
+ }
5181
+ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
5182
+ return /* @__PURE__ */ React2__namespace.createElement(ContainerSlotBase, { ...props });
5183
+ });
5184
+
5185
+ // src/components/Containers.native.tsx
5186
+ var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal }) {
5187
+ const ctx = useStateContext();
5188
+ const columnWrapperStyle = ctx.columnWrapperStyle;
5189
+ const animSize = useValue$("totalSize");
5190
+ const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);
5191
+ const style = horizontal ? {
5192
+ height: otherAxisSize || "100%",
5193
+ minHeight: otherAxisSize,
5194
+ opacity: readyToRender ? 1 : 0,
5195
+ width: animSize
5196
+ } : { height: animSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0 };
5197
+ if (columnWrapperStyle) {
5198
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
5199
+ const gapX = columnGap || gap || 0;
5200
+ const gapY = rowGap || gap || 0;
5201
+ if (horizontal) {
5202
+ if (gapY && numColumns > 1) {
5203
+ style.marginVertical = -gapY / 2;
5204
+ }
5205
+ if (gapX) {
5206
+ style.marginRight = -gapX;
5207
+ }
5208
+ } else {
5209
+ if (gapX && numColumns > 1) {
5210
+ style.marginHorizontal = -gapX;
5211
+ }
5212
+ if (gapY) {
5213
+ style.marginBottom = -gapY;
5214
+ }
5215
+ }
5216
+ }
5217
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { style }, children);
5218
+ });
5219
+ var Containers = typedMemo(function Containers2({
5220
+ horizontal,
5221
+ recycleItems,
5222
+ ItemSeparatorComponent,
5223
+ stickyHeaderConfig,
5224
+ getRenderedItem: getRenderedItem2
5225
+ }) {
5226
+ const [numContainersPooled] = useArr$(["numContainersPooled"]);
5227
+ const containers = [];
5228
+ for (let i = 0; i < numContainersPooled; i++) {
5229
+ containers.push(
5230
+ /* @__PURE__ */ React2__namespace.createElement(
5231
+ ContainerSlot,
5232
+ {
5233
+ getRenderedItem: getRenderedItem2,
5234
+ horizontal,
5235
+ ItemSeparatorComponent,
5236
+ id: i,
5237
+ key: i,
5238
+ recycleItems,
5239
+ stickyHeaderConfig
5240
+ }
5241
+ )
5242
+ );
5243
+ }
5244
+ return /* @__PURE__ */ React2__namespace.createElement(ContainersLayer, { horizontal }, containers);
5245
+ });
5246
+ var ListComponentScrollView = ReactNative.Animated.ScrollView;
5247
+
5248
+ // src/components/listComponentStyles.ts
5249
+ function getAutoOtherAxisStyle({
5250
+ horizontal,
5251
+ needsOtherAxisSize,
5252
+ otherAxisSize
5253
+ }) {
5254
+ if (!needsOtherAxisSize || !otherAxisSize || otherAxisSize <= 0) {
5255
+ return void 0;
5256
+ }
5257
+ return horizontal ? { height: otherAxisSize } : { width: otherAxisSize };
5258
+ }
5259
+ function ScrollAdjust() {
5260
+ var _a3;
5261
+ const ctx = useStateContext();
5262
+ const bias = 1e7;
5263
+ const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
5264
+ const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0) + bias;
5265
+ const horizontal = !!((_a3 = ctx.state) == null ? void 0 : _a3.props.horizontal);
5266
+ return /* @__PURE__ */ React2__namespace.createElement(
5267
+ ReactNative.View,
5268
+ {
5269
+ style: {
5270
+ height: 0,
5271
+ left: horizontal ? scrollOffset : 0,
5272
+ position: "absolute",
5273
+ top: horizontal ? 0 : scrollOffset,
5274
+ width: 0
5275
+ }
5276
+ }
5277
+ );
5278
+ }
5279
+ var SnapWrapper = React2__namespace.forwardRef(function SnapWrapperInner({ ScrollComponent, ...props }, ref) {
5280
+ const [snapToOffsets] = useArr$(["snapToOffsets"]);
5281
+ return /* @__PURE__ */ React2__namespace.createElement(ScrollComponent, { ...props, ref, snapToOffsets });
5282
+ });
5283
+ function WebAnchoredEndSpace({ horizontal }) {
5284
+ const ctx = useStateContext();
5285
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
5286
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
5287
+ if (!shouldRenderAnchoredEndSpace) {
5288
+ return null;
5289
+ }
5290
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
5291
+ return /* @__PURE__ */ React2__namespace.createElement("div", { style }, null);
5292
+ }
5293
+
5294
+ // src/core/updateContentMetrics.ts
5295
+ var SCROLL_ADJUST_EPSILON = 0.1;
5296
+ function setContentLengthSignal(ctx, signalName, size) {
5297
+ const didChange = peek$(ctx, signalName) !== size;
5298
+ if (didChange) {
5299
+ set$(ctx, signalName, size);
5300
+ updateContentMetricsState(ctx);
5301
+ }
5302
+ return didChange;
5303
+ }
5304
+ function shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, nextHeaderSize) {
5305
+ const { didContainersLayout, didFinishInitialScroll, props, scroll, scrollingTo } = ctx.state;
5306
+ const sizeDiff = nextHeaderSize - previousHeaderSize;
5307
+ const leadingPadding = props.horizontal ? props.stylePaddingLeft : props.stylePaddingTop;
5308
+ const previousHeaderEnd = (leadingPadding || 0) + previousHeaderSize;
5309
+ return Platform.OS === "web" && props.maintainVisibleContentPosition.size && didContainersLayout && didFinishInitialScroll && !scrollingTo && scroll >= previousHeaderEnd - SCROLL_ADJUST_EPSILON && Math.abs(sizeDiff) > SCROLL_ADJUST_EPSILON;
5310
+ }
5311
+ function setHeaderSize(ctx, size) {
5312
+ const { state } = ctx;
5313
+ const previousHeaderSize = peek$(ctx, "headerSize") || 0;
5314
+ const didChange = previousHeaderSize !== size;
5315
+ const hasMeasuredOrEstimatedHeaderBaseline = state.didMeasureHeader || previousHeaderSize > SCROLL_ADJUST_EPSILON;
5316
+ if (didChange) {
5317
+ set$(ctx, "headerSize", size);
5318
+ updateContentMetricsState(ctx);
5319
+ if (hasMeasuredOrEstimatedHeaderBaseline && shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, size)) {
5320
+ requestAdjust(ctx, size - previousHeaderSize);
5321
+ }
5322
+ }
5323
+ state.didMeasureHeader = true;
5324
+ }
5325
+ function setFooterSize(ctx, size) {
5326
+ return setContentLengthSignal(ctx, "footerSize", size);
5327
+ }
5328
+ function areInsetsEqual(left, right) {
5329
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5330
+ return ((_a3 = left == null ? void 0 : left.top) != null ? _a3 : 0) === ((_b = right == null ? void 0 : right.top) != null ? _b : 0) && ((_c = left == null ? void 0 : left.bottom) != null ? _c : 0) === ((_d = right == null ? void 0 : right.bottom) != null ? _d : 0) && ((_e = left == null ? void 0 : left.left) != null ? _e : 0) === ((_f = right == null ? void 0 : right.left) != null ? _f : 0) && ((_g = left == null ? void 0 : left.right) != null ? _g : 0) === ((_h = right == null ? void 0 : right.right) != null ? _h : 0);
5331
+ }
5332
+ function setContentInsetOverride(ctx, inset) {
5333
+ const { state } = ctx;
5334
+ const previousInset = state.contentInsetOverride;
5335
+ const nextInset = inset != null ? inset : void 0;
5336
+ const didChange = !areInsetsEqual(previousInset, nextInset);
5337
+ state.contentInsetOverride = nextInset;
5338
+ if (didChange) {
5339
+ updateContentMetricsState(ctx);
5340
+ }
5341
+ return didChange;
5342
+ }
5343
+ function useLatestRef(value) {
5344
+ const ref = React2__namespace.useRef(value);
5345
+ ref.current = value;
5346
+ return ref;
5347
+ }
5348
+
5349
+ // src/hooks/useStableRenderComponent.tsx
5350
+ function useStableRenderComponent(renderComponent, mapProps) {
5351
+ const renderComponentRef = useLatestRef(renderComponent);
5352
+ const mapPropsRef = useLatestRef(mapProps);
5353
+ return React2__namespace.useMemo(
5354
+ () => React2__namespace.forwardRef(
5355
+ (props, ref) => {
5356
+ var _a3, _b;
5357
+ return (_b = (_a3 = renderComponentRef.current) == null ? void 0 : _a3.call(renderComponentRef, mapPropsRef.current(props, ref))) != null ? _b : null;
5358
+ }
5359
+ ),
5360
+ [mapPropsRef, renderComponentRef]
5361
+ );
5362
+ }
5363
+ var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
5364
+ const localRef = React2.useRef(null);
5365
+ const ref = refView != null ? refView : localRef;
5366
+ const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
5367
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
5368
+ };
5369
+
5370
+ // src/components/ListComponent.tsx
5371
+ var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
5372
+ const [alignItemsAtEndPadding = 0] = useArr$(["alignItemsAtEndPadding"]);
5373
+ if (alignItemsAtEndPadding <= 0) {
5374
+ return null;
5375
+ }
5376
+ return /* @__PURE__ */ React2__namespace.createElement(
5377
+ View,
5378
+ {
5379
+ style: horizontal ? { flexShrink: 0, width: alignItemsAtEndPadding } : { flexShrink: 0, height: alignItemsAtEndPadding }
5380
+ },
5381
+ null
5382
+ );
5383
+ });
5384
+ var ListComponent = typedMemo(function ListComponent2({
5385
+ canRender,
5386
+ style,
5387
+ contentContainerStyle,
5388
+ horizontal,
5389
+ initialContentOffset,
5390
+ recycleItems,
5391
+ ItemSeparatorComponent,
5392
+ alignItemsAtEnd: _alignItemsAtEnd,
5393
+ onScroll: onScroll2,
5394
+ onLayout,
5395
+ ListHeaderComponent,
5396
+ ListHeaderComponentStyle,
5397
+ ListFooterComponent,
5398
+ ListFooterComponentStyle,
5399
+ ListEmptyComponent,
5400
+ getRenderedItem: getRenderedItem2,
5401
+ refScrollView,
5402
+ renderScrollComponent,
5403
+ onLayoutFooter,
5404
+ scrollAdjustHandler,
5405
+ snapToIndices,
5406
+ stickyHeaderConfig,
5407
+ stickyHeaderIndices,
5408
+ useWindowScroll = false,
5409
+ ...rest
5410
+ }) {
5411
+ const ctx = useStateContext();
5412
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
5413
+ const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
5414
+ const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
5415
+ const autoOtherAxisStyle = getAutoOtherAxisStyle({
5416
+ horizontal,
5417
+ needsOtherAxisSize: ctx.state.needsOtherAxisSize,
5418
+ otherAxisSize
5419
+ });
5420
+ const CustomScrollComponent = useStableRenderComponent(
5421
+ renderScrollComponent,
5422
+ (props, ref) => ({ ...props, ref })
5423
+ );
5424
+ const ScrollComponent = renderScrollComponent ? CustomScrollComponent : ListComponentScrollView;
5425
+ const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
5426
+ const updateFooterSize = React2.useCallback(
5427
+ (size, afterSizeUpdate) => {
5428
+ var _a3;
5429
+ const didFooterSizeChange = setFooterSize(ctx, size);
5430
+ afterSizeUpdate == null ? void 0 : afterSizeUpdate();
5431
+ if (didFooterSizeChange && ((_a3 = ctx.state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onFooterLayout)) {
5432
+ doMaintainScrollAtEnd(ctx);
5178
5433
  }
5434
+ },
5435
+ [ctx]
5436
+ );
5437
+ React2.useLayoutEffect(() => {
5438
+ if (!ListHeaderComponent) {
5439
+ setHeaderSize(ctx, 0);
5179
5440
  }
5180
- if (state.stickyContainerPool.size > 0) {
5181
- handleStickyRecycling(
5182
- ctx,
5183
- stickyHeaderIndicesArr,
5184
- scroll,
5185
- drawDistance,
5186
- (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
5187
- pendingRemoval,
5188
- alwaysRenderSet
5189
- );
5441
+ if (!ListFooterComponent) {
5442
+ updateFooterSize(0);
5190
5443
  }
5191
- const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
5192
- let didChangePositions = false;
5193
- for (let i = 0; i < numContainers; i++) {
5194
- const itemKey = peek$(ctx, `containerItemKey${i}`);
5195
- if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
5196
- if (itemKey !== void 0) {
5197
- containerItemKeys.delete(itemKey);
5198
- }
5199
- state.containerItemTypes.delete(i);
5200
- if (state.stickyContainerPool.has(i)) {
5201
- set$(ctx, `containerSticky${i}`, false);
5202
- state.stickyContainerPool.delete(i);
5203
- }
5204
- set$(ctx, `containerItemKey${i}`, void 0);
5205
- set$(ctx, `containerItemData${i}`, void 0);
5206
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
5207
- set$(ctx, `containerColumn${i}`, -1);
5208
- set$(ctx, `containerSpan${i}`, 1);
5209
- } else {
5210
- const itemIndex = indexByKey.get(itemKey);
5211
- if (itemIndex !== void 0) {
5212
- didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
5213
- scrollAdjustPending,
5214
- updateLayout: true
5215
- }).didChangePosition || didChangePositions;
5216
- }
5444
+ }, [ListHeaderComponent, ListFooterComponent, ctx, updateFooterSize]);
5445
+ const onLayoutHeader = React2.useCallback(
5446
+ (rect) => {
5447
+ const size = rect[horizontal ? "width" : "height"];
5448
+ setHeaderSize(ctx, size);
5449
+ },
5450
+ [ctx, horizontal]
5451
+ );
5452
+ const onLayoutFooterInternal = React2.useCallback(
5453
+ (rect, fromLayoutEffect) => {
5454
+ const size = rect[horizontal ? "width" : "height"];
5455
+ updateFooterSize(size, () => {
5456
+ onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
5457
+ });
5458
+ },
5459
+ [horizontal, onLayoutFooter, updateFooterSize]
5460
+ );
5461
+ return /* @__PURE__ */ React2__namespace.createElement(
5462
+ SnapOrScroll,
5463
+ {
5464
+ ...rest,
5465
+ ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
5466
+ contentContainerStyle: [
5467
+ horizontal ? {
5468
+ height: "100%"
5469
+ } : {},
5470
+ contentContainerStyle
5471
+ ],
5472
+ contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
5473
+ horizontal,
5474
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
5475
+ onLayout,
5476
+ onScroll: onScroll2,
5477
+ ref: refScrollView,
5478
+ ScrollComponent: snapToIndices ? ScrollComponent : void 0,
5479
+ style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
5480
+ },
5481
+ /* @__PURE__ */ React2__namespace.createElement(ScrollAdjust, null),
5482
+ ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
5483
+ ListEmptyComponent && getComponent(ListEmptyComponent),
5484
+ shouldRenderAlignItemsAtEndSpacer && /* @__PURE__ */ React2__namespace.createElement(AlignItemsAtEndSpacer, { horizontal }),
5485
+ canRender && !ListEmptyComponent && /* @__PURE__ */ React2__namespace.createElement(
5486
+ Containers,
5487
+ {
5488
+ getRenderedItem: getRenderedItem2,
5489
+ horizontal,
5490
+ ItemSeparatorComponent,
5491
+ recycleItems,
5492
+ stickyHeaderConfig
5217
5493
  }
5494
+ ),
5495
+ ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
5496
+ Platform.OS === "web" && /* @__PURE__ */ React2__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
5497
+ IS_DEV && ENABLE_DEVMODE
5498
+ );
5499
+ });
5500
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
5501
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
5502
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
5503
+ function useDevChecksImpl(props) {
5504
+ const ctx = useStateContext();
5505
+ const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
5506
+ React2.useEffect(() => {
5507
+ if (useWindowScroll && renderScrollComponent) {
5508
+ warnDevOnce(
5509
+ "useWindowScrollRenderScrollComponent",
5510
+ "useWindowScroll is not supported when renderScrollComponent is provided."
5511
+ );
5218
5512
  }
5219
- if (Platform.OS === "web" && didChangePositions) {
5220
- set$(ctx, "lastPositionUpdate", Date.now());
5513
+ }, [renderScrollComponent, useWindowScroll]);
5514
+ React2.useEffect(() => {
5515
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
5516
+ warnDevOnce(
5517
+ "keyExtractor",
5518
+ "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
5519
+ );
5221
5520
  }
5222
- if (suppressInitialScrollSideEffects) {
5223
- evaluateBootstrapInitialScroll(ctx);
5521
+ }, [childrenMode, ctx, keyExtractor]);
5522
+ React2.useEffect(() => {
5523
+ const state = ctx.state;
5524
+ const dataLength = state.props.data.length;
5525
+ const useWindowScrollResolved = state.props.useWindowScroll;
5526
+ if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
5224
5527
  return;
5225
5528
  }
5226
- if (!queuedInitialLayout && !state.didContainersLayout) {
5227
- const isInitialLayoutReady = hasActiveInitialScroll(state) ? checkAllSizesKnown(state, state.startBuffered, state.endBuffered) : checkAllSizesKnown(state, state.startNoBuffer, state.endNoBuffer) || checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
5228
- if (isInitialLayoutReady) {
5229
- setDidLayout(ctx);
5230
- handleInitialScrollLayoutReady(ctx);
5529
+ const warnIfUnboundedOuterSize = () => {
5530
+ const readyToRender = peek$(ctx, "readyToRender");
5531
+ const numContainers = peek$(ctx, "numContainers") || 0;
5532
+ const totalSize = peek$(ctx, "totalSize") || 0;
5533
+ const scrollLength = ctx.state.scrollLength || 0;
5534
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
5535
+ return;
5231
5536
  }
5232
- }
5233
- if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
5234
- if (!didMVCPAdjustScroll) {
5235
- updateViewableItems(
5236
- ctx.state,
5237
- ctx,
5238
- viewabilityConfigCallbackPairs,
5239
- scrollLength,
5240
- visibleRange.startNoBuffer,
5241
- visibleRange.endNoBuffer,
5242
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
5243
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
5537
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
5538
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
5539
+ if (rendersAlmostEverything && viewportMatchesContent) {
5540
+ warnDevOnce(
5541
+ "webUnboundedOuterSize",
5542
+ "LegendList appears to have an unbounded outer height on web, so virtualization is effectively disabled. Set a bounded height or flex: 1 on the list container, or use useWindowScroll."
5244
5543
  );
5245
5544
  }
5246
- }
5247
- (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
5248
- });
5545
+ };
5546
+ warnIfUnboundedOuterSize();
5547
+ const unsubscribe = [
5548
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
5549
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
5550
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
5551
+ ];
5552
+ return () => {
5553
+ for (const unsub of unsubscribe) {
5554
+ unsub();
5555
+ }
5556
+ };
5557
+ }, [ctx]);
5558
+ }
5559
+ function useDevChecksNoop(_props) {
5249
5560
  }
5561
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
5250
5562
 
5251
5563
  // src/core/checkResetContainers.ts
5252
5564
  function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
@@ -5568,61 +5880,6 @@ var ScrollAdjustHandler = class {
5568
5880
  }
5569
5881
  };
5570
5882
 
5571
- // src/core/updateAnchoredEndSpace.ts
5572
- function maybeUpdateAnchoredEndSpace(ctx) {
5573
- var _a3, _b;
5574
- const state = ctx.state;
5575
- const anchoredEndSpace = state.props.anchoredEndSpace;
5576
- const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5577
- const previousReadyAnchorIndex = state.anchoredEndSpaceReadyAnchorIndex;
5578
- const previousReadyAnchorKey = state.anchoredEndSpaceReadyAnchorKey;
5579
- const nextAnchorIndex = anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex;
5580
- let nextAnchorKey;
5581
- let isReady = true;
5582
- let nextSize = 0;
5583
- if (anchoredEndSpace) {
5584
- const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5585
- const { data } = state.props;
5586
- if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5587
- nextAnchorKey = getId(state, anchorIndex);
5588
- let contentBelowAnchor = 0;
5589
- const footerSize = ctx.values.get("footerSize") || 0;
5590
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5591
- let hasUnknownTailSize = false;
5592
- for (let index = anchorIndex; index < data.length; index++) {
5593
- const size = getKnownOrFixedItemSize(ctx, index);
5594
- const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5595
- if (size === void 0) {
5596
- hasUnknownTailSize = true;
5597
- }
5598
- if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5599
- contentBelowAnchor += effectiveSize;
5600
- }
5601
- }
5602
- contentBelowAnchor += footerSize + stylePaddingBottom;
5603
- isReady = !hasUnknownTailSize;
5604
- nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5605
- } else if (anchorIndex >= 0) {
5606
- isReady = false;
5607
- }
5608
- }
5609
- const didSizeChange = previousSize !== nextSize;
5610
- const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
5611
- if (isReady && (didSizeChange || didReadyAnchorChange)) {
5612
- state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
5613
- state.anchoredEndSpaceReadyAnchorKey = nextAnchorKey;
5614
- if (didSizeChange) {
5615
- set$(ctx, "anchoredEndSpaceSize", nextSize);
5616
- (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5617
- }
5618
- if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
5619
- updateScroll(ctx, state.scroll, true);
5620
- }
5621
- (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
5622
- }
5623
- return nextSize;
5624
- }
5625
-
5626
5883
  // src/core/updateContentInsetEndAdjustment.ts
5627
5884
  function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
5628
5885
  const state = ctx.state;
@@ -5638,173 +5895,6 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5638
5895
  }
5639
5896
  }
5640
5897
  }
5641
-
5642
- // src/core/updateItemSize.ts
5643
- function runOrScheduleMVCPRecalculate(ctx) {
5644
- var _a3;
5645
- const state = ctx.state;
5646
- if (state.userScrollAnchorReset !== void 0) {
5647
- calculateItemsInView(ctx);
5648
- if (((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
5649
- state.userScrollAnchorReset = void 0;
5650
- }
5651
- } else if (Platform.OS === "web") {
5652
- if (!state.mvcpAnchorLock) {
5653
- if (state.queuedMVCPRecalculate !== void 0) {
5654
- cancelAnimationFrame(state.queuedMVCPRecalculate);
5655
- state.queuedMVCPRecalculate = void 0;
5656
- }
5657
- calculateItemsInView(ctx, { doMVCP: true });
5658
- } else if (state.queuedMVCPRecalculate === void 0) {
5659
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5660
- state.queuedMVCPRecalculate = void 0;
5661
- calculateItemsInView(ctx, { doMVCP: true });
5662
- });
5663
- }
5664
- } else {
5665
- calculateItemsInView(ctx, { doMVCP: true });
5666
- }
5667
- }
5668
- function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5669
- const state = ctx.state;
5670
- if (state.needsOtherAxisSize) {
5671
- const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
5672
- const currentOtherAxisSize = peek$(ctx, "otherAxisSize");
5673
- if (!currentOtherAxisSize || otherAxisSize > currentOtherAxisSize) {
5674
- set$(ctx, "otherAxisSize", otherAxisSize);
5675
- }
5676
- }
5677
- }
5678
- function updateItemSize(ctx, itemKey, sizeObj) {
5679
- var _a3;
5680
- const state = ctx.state;
5681
- const userScrollAnchorReset = state.userScrollAnchorReset;
5682
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5683
- const {
5684
- didContainersLayout,
5685
- sizesKnown,
5686
- props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
5687
- } = state;
5688
- if (!data) return;
5689
- const index = state.indexByKey.get(itemKey);
5690
- let resolvedMeasurementItem;
5691
- if (getFixedItemSize) {
5692
- if (index === void 0) {
5693
- return;
5694
- }
5695
- const itemData = state.props.data[index];
5696
- if (itemData === void 0) {
5697
- return;
5698
- }
5699
- const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5700
- const size2 = getFixedItemSize(itemData, index, type);
5701
- resolvedMeasurementItem = {
5702
- didResolveFixedItemSize: true,
5703
- fixedItemSize: size2,
5704
- itemData,
5705
- itemType: type
5706
- };
5707
- if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5708
- updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5709
- return;
5710
- }
5711
- }
5712
- let needsRecalculate = !didContainersLayout;
5713
- let shouldMaintainScrollAtEnd = false;
5714
- let minIndexSizeChanged;
5715
- const prevSizeKnown = state.sizesKnown.get(itemKey);
5716
- const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5717
- const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
5718
- if (diff !== 0) {
5719
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
5720
- const { startBuffered, endBuffered } = state;
5721
- needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
5722
- if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
5723
- needsRecalculate = true;
5724
- }
5725
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
5726
- shouldMaintainScrollAtEnd = true;
5727
- }
5728
- onItemSizeChanged == null ? void 0 : onItemSizeChanged({
5729
- index,
5730
- itemData: state.props.data[index],
5731
- itemKey,
5732
- previous: size - diff,
5733
- size
5734
- });
5735
- maybeUpdateAnchoredEndSpace(ctx);
5736
- }
5737
- if (minIndexSizeChanged !== void 0) {
5738
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5739
- }
5740
- updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5741
- if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
5742
- if (needsRecalculate) {
5743
- state.scrollForNextCalculateItemsInView = void 0;
5744
- runOrScheduleMVCPRecalculate(ctx);
5745
- } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.size) === 0) {
5746
- state.userScrollAnchorReset = void 0;
5747
- }
5748
- if (shouldMaintainScrollAtEnd) {
5749
- if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
5750
- doMaintainScrollAtEnd(ctx);
5751
- }
5752
- }
5753
- }
5754
- }
5755
- function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5756
- var _a3, _b, _c;
5757
- const state = ctx.state;
5758
- const {
5759
- indexByKey,
5760
- sizesKnown,
5761
- averageSizes,
5762
- props: { data, horizontal, getItemType, getFixedItemSize }
5763
- } = state;
5764
- if (!data) return 0;
5765
- const index = indexByKey.get(itemKey);
5766
- const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
5767
- let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5768
- let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5769
- if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5770
- itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5771
- fixedItemSize = getFixedItemSize(itemData, index, itemType);
5772
- }
5773
- const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
5774
- didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
5775
- fixedItemSize,
5776
- itemType
5777
- } : void 0;
5778
- const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5779
- const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5780
- const prevSizeKnown = sizesKnown.get(itemKey);
5781
- if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
5782
- return 0;
5783
- }
5784
- const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5785
- sizesKnown.set(itemKey, size);
5786
- if (fixedItemSize === void 0 && size > 0) {
5787
- itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
5788
- let averages = averageSizes[itemType];
5789
- if (!averages) {
5790
- averages = averageSizes[itemType] = { avg: 0, num: 0 };
5791
- }
5792
- if (averages.num === 0) {
5793
- averages.avg = size;
5794
- averages.num++;
5795
- } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
5796
- averages.avg += (size - prevSizeKnown) / averages.num;
5797
- } else {
5798
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
5799
- averages.num++;
5800
- }
5801
- }
5802
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5803
- setSize(ctx, itemKey, size);
5804
- return size - prevSize;
5805
- }
5806
- return 0;
5807
- }
5808
5898
  function useWrapIfItem(fn) {
5809
5899
  return React2.useMemo(
5810
5900
  () => fn ? (arg1, arg2, arg3) => arg1 !== void 0 && arg2 !== void 0 ? fn(arg1, arg2, arg3) : void 0 : void 0,
@@ -6125,7 +6215,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6125
6215
  return true;
6126
6216
  }),
6127
6217
  setItemSize: (itemKey, size) => {
6128
- updateItemSize(ctx, itemKey, size);
6218
+ updateItemSizes(ctx, { itemKey, size });
6129
6219
  },
6130
6220
  setScrollProcessingEnabled: (enabled) => {
6131
6221
  state.scrollProcessingEnabled = enabled;
@@ -6352,7 +6442,7 @@ var LegendList = typedMemo(
6352
6442
  })
6353
6443
  );
6354
6444
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
6355
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
6445
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
6356
6446
  const noopOnScroll = React2.useCallback((_event) => {
6357
6447
  }, []);
6358
6448
  if (props.recycleItems === void 0) {
@@ -6407,6 +6497,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6407
6497
  onStartReached,
6408
6498
  onStartReachedThreshold = 0.5,
6409
6499
  onStickyHeaderChange,
6500
+ onFirstVisibleItemChanged,
6410
6501
  onViewableItemsChanged,
6411
6502
  progressViewOffset,
6412
6503
  recycleItems = false,
@@ -6508,6 +6599,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6508
6599
  if (!refState.current) {
6509
6600
  if (!ctx.state) {
6510
6601
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
6602
+ ctx.values.set("adaptiveRender", (_l = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _l : "normal");
6511
6603
  ctx.state = {
6512
6604
  averageSizes: {},
6513
6605
  columnSpans: [],
@@ -6585,6 +6677,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6585
6677
  }
6586
6678
  const state = refState.current;
6587
6679
  const isFirstLocal = state.isFirst;
6680
+ const previousAdaptiveRender = state.props.adaptiveRender;
6588
6681
  const previousNumColumnsProp = state.props.numColumns;
6589
6682
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
6590
6683
  ctx.scrollAxisGap = nextScrollAxisGap;
@@ -6592,7 +6685,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6592
6685
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6593
6686
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6594
6687
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6595
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_l = state.initialScroll) == null ? void 0 : _l.viewPosition) === 1 && state.props.data.length > 0) {
6688
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_m = state.initialScroll) == null ? void 0 : _m.viewPosition) === 1 && state.props.data.length > 0) {
6596
6689
  clearPreservedInitialScrollTarget(state);
6597
6690
  }
6598
6691
  if (didDataChangeLocal) {
@@ -6604,7 +6697,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6604
6697
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6605
6698
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6606
6699
  const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
6607
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_m = state.props.anchoredEndSpace) == null ? void 0 : _m.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6700
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_n = state.props.anchoredEndSpace) == null ? void 0 : _n.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6608
6701
  state.props = {
6609
6702
  adaptiveRender: experimental_adaptiveRender,
6610
6703
  alignItemsAtEnd,
@@ -6632,6 +6725,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6632
6725
  numColumns: numColumnsProp,
6633
6726
  onEndReached,
6634
6727
  onEndReachedThreshold,
6728
+ onFirstVisibleItemChanged,
6635
6729
  onItemSizeChanged,
6636
6730
  onLoad,
6637
6731
  onScroll: throttleScrollFn,
@@ -6654,6 +6748,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6654
6748
  useWindowScroll: useWindowScrollResolved
6655
6749
  };
6656
6750
  state.refScroller = refScroller;
6751
+ if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) {
6752
+ resetAdaptiveRender(ctx);
6753
+ }
6657
6754
  const memoizedLastItemKeys = React2.useMemo(() => {
6658
6755
  if (!dataProp.length) return [];
6659
6756
  return Array.from(
@@ -6898,8 +6995,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6898
6995
  onMomentumScrollEnd(event);
6899
6996
  }
6900
6997
  },
6901
- onScroll: (event) => onScroll(ctx, event),
6902
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
6998
+ onScroll: (event) => onScroll(ctx, event)
6903
6999
  }),
6904
7000
  []
6905
7001
  );
@@ -6926,7 +7022,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6926
7022
  onScroll: onScrollHandler,
6927
7023
  recycleItems,
6928
7024
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
6929
- progressViewOffset: ((_n = refreshControlElement.props.progressViewOffset) != null ? _n : 0) + stylePaddingTopState
7025
+ progressViewOffset: ((_o = refreshControlElement.props.progressViewOffset) != null ? _o : 0) + stylePaddingTopState
6930
7026
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
6931
7027
  ReactNative.RefreshControl,
6932
7028
  {
@@ -6937,12 +7033,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6937
7033
  ),
6938
7034
  refScrollView: combinedRef,
6939
7035
  renderScrollComponent,
6940
- scrollAdjustHandler: (_o = refState.current) == null ? void 0 : _o.scrollAdjustHandler,
7036
+ scrollAdjustHandler: (_p = refState.current) == null ? void 0 : _p.scrollAdjustHandler,
6941
7037
  scrollEventThrottle: 0,
6942
7038
  snapToIndices,
6943
7039
  stickyHeaderIndices,
6944
7040
  style,
6945
- updateItemSize: fns.updateItemSize,
6946
7041
  useWindowScroll: useWindowScrollResolved
6947
7042
  }
6948
7043
  ), IS_DEV && ENABLE_DEBUG_VIEW);