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