@legendapp/list 3.1.2 → 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,1223 +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) {
1792
- 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);
1817
- }
1818
- }
1819
- function updateAdaptiveRender(ctx, scrollVelocity) {
1820
- var _a3, _b, _c;
1702
+ // src/core/doMaintainScrollAtEnd.ts
1703
+ function doMaintainScrollAtEnd(ctx) {
1821
1704
  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);
1836
- }
1837
- }
1838
- }
1839
-
1840
- // src/utils/getEffectiveDrawDistance.ts
1841
- var INITIAL_DRAW_DISTANCE = 100;
1842
- function getEffectiveDrawDistance(ctx) {
1843
- var _a3;
1844
- const drawDistance = ctx.state.props.drawDistance;
1845
- const initialScroll = ctx.state.initialScroll;
1846
- const needsFullInitialDrawDistance = initialScroll !== void 0 && ((_a3 = initialScroll.viewPosition) != null ? _a3 : 0) > 0;
1847
- return peek$(ctx, "readyToRender") || needsFullInitialDrawDistance ? drawDistance : Math.min(drawDistance, INITIAL_DRAW_DISTANCE);
1848
- }
1849
-
1850
- // src/utils/setInitialRenderState.ts
1851
- function setInitialRenderState(ctx, {
1852
- didLayout,
1853
- didInitialScroll
1854
- }) {
1855
- const { state } = ctx;
1856
1705
  const {
1857
- loadStartTime,
1858
- props: { onLoad }
1706
+ didContainersLayout,
1707
+ pendingNativeMVCPAdjust,
1708
+ refScroller,
1709
+ props: { maintainScrollAtEnd }
1859
1710
  } = state;
1860
- if (didLayout) {
1861
- state.didContainersLayout = true;
1862
- }
1863
- if (didInitialScroll) {
1864
- state.didFinishInitialScroll = true;
1711
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1712
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
1713
+ if (pendingNativeMVCPAdjust) {
1714
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1715
+ return false;
1865
1716
  }
1866
- const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1867
- if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1868
- set$(ctx, "readyToRender", true);
1869
- setAdaptiveRender(ctx, "normal");
1870
- if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1871
- requestAnimationFrame(() => {
1872
- var _a3;
1873
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1874
- });
1717
+ state.pendingMaintainScrollAtEnd = false;
1718
+ if (shouldMaintainScrollAtEnd) {
1719
+ const contentSize = getContentSize(ctx);
1720
+ if (contentSize < state.scrollLength) {
1721
+ state.scroll = 0;
1875
1722
  }
1876
- if (onLoad) {
1877
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
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
+ });
1878
1757
  }
1758
+ return true;
1879
1759
  }
1760
+ return false;
1880
1761
  }
1881
1762
 
1882
- // src/core/finishInitialScroll.ts
1883
- var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1884
- function syncInitialScrollOffset(state, offset) {
1885
- state.scroll = offset;
1886
- state.scrollPending = offset;
1887
- state.scrollPrev = offset;
1888
- }
1889
- function clearPreservedInitialScrollTargetTimeout(state) {
1890
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
1891
- clearTimeout(state.timeoutPreservedInitialScrollClear);
1892
- state.timeoutPreservedInitialScrollClear = void 0;
1893
- }
1894
- }
1895
- function clearPreservedInitialScrollTarget(state) {
1896
- clearPreservedInitialScrollTargetTimeout(state);
1897
- state.clearPreservedInitialScrollOnNextFinish = void 0;
1898
- state.initialScroll = void 0;
1899
- setInitialScrollSession(state);
1900
- }
1901
- function finishInitialScroll(ctx, options) {
1902
- var _a3, _b, _c;
1763
+ // src/utils/requestAdjust.ts
1764
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1903
1765
  const state = ctx.state;
1904
- if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1905
- syncInitialScrollOffset(state, options.resolvedOffset);
1906
- } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1907
- const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1908
- if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1909
- syncInitialScrollOffset(state, observedOffset);
1910
- }
1911
- }
1912
- const complete = () => {
1913
- var _a4, _b2, _c2, _d, _e;
1914
- const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1915
- const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1916
- initialScrollWatchdog.clear(state);
1917
- if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1918
- state.clearPreservedInitialScrollOnNextFinish = void 0;
1919
- setInitialScrollSession(state);
1920
- clearPreservedInitialScrollTargetTimeout(state);
1921
- if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1922
- state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1923
- var _a5;
1924
- state.timeoutPreservedInitialScrollClear = void 0;
1925
- if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1926
- 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);
1927
1805
  }
1928
- clearPreservedInitialScrollTarget(state);
1929
- }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
1806
+ }, delay);
1930
1807
  }
1931
1808
  } else {
1932
- clearPreservedInitialScrollTarget(state);
1933
- }
1934
- if (options == null ? void 0 : options.recalculateItems) {
1935
- recalculateSettledScroll(ctx);
1936
- }
1937
- setInitialRenderState(ctx, { didInitialScroll: true });
1938
- if (shouldReleaseDeferredPublicOnScroll) {
1939
- releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1809
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1810
+ requestAnimationFrame(doit);
1940
1811
  }
1941
- (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
1942
- };
1943
- if (options == null ? void 0 : options.waitForCompletionFrame) {
1944
- requestAnimationFrame(complete);
1945
- return;
1946
1812
  }
1947
- complete();
1948
1813
  }
1949
1814
 
1950
- // src/core/finishScrollTo.ts
1951
- function finishScrollTo(ctx) {
1952
- var _a3, _b;
1953
- const state = ctx.state;
1954
- if (state == null ? void 0 : state.scrollingTo) {
1955
- const resolvePendingScroll = state.pendingScrollResolve;
1956
- state.pendingScrollResolve = void 0;
1957
- const scrollingTo = state.scrollingTo;
1958
- state.scrollHistory.length = 0;
1959
- state.scrollingTo = void 0;
1960
- if (state.pendingTotalSize !== void 0) {
1961
- addTotalSize(ctx, null, state.pendingTotalSize);
1962
- }
1963
- if (PlatformAdjustBreaksScroll) {
1964
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1965
- }
1966
- if (scrollingTo.isInitialScroll || state.initialScroll) {
1967
- const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1968
- const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1969
- finishInitialScroll(ctx, {
1970
- onFinished: () => {
1971
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1972
- },
1973
- preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1974
- recalculateItems: true,
1975
- schedulePreservedTargetClear: shouldPreserveResizeTarget,
1976
- syncObservedOffset: isOffsetSession,
1977
- waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1978
- });
1979
- return;
1980
- }
1981
- recalculateSettledScroll(ctx);
1982
- 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;
1983
1834
  }
1835
+ return lock;
1984
1836
  }
1985
-
1986
- // src/core/checkFinishedScroll.ts
1987
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1988
- var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
1989
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1990
- var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
1991
- var SILENT_INITIAL_SCROLL_TARGET_EPSILON = 1;
1992
- function checkFinishedScroll(ctx, options) {
1993
- const scrollingTo = ctx.state.scrollingTo;
1994
- if (options == null ? void 0 : options.onlyIfAligned) {
1995
- 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) {
1996
1843
  return;
1997
1844
  }
1998
- 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;
1999
1849
  return;
2000
1850
  }
1851
+ state.mvcpAnchorLock = {
1852
+ expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
1853
+ id: anchorId,
1854
+ position: anchorPosition,
1855
+ quietPasses
1856
+ };
2001
1857
  }
2002
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
2003
- }
2004
- function hasScrollCompletionOwnership(state, options) {
2005
- const { clampedTargetOffset, scrollingTo } = options;
2006
- return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
2007
- }
2008
- function isSilentInitialDispatch(state, scrollingTo) {
2009
- return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
2010
- }
2011
- function getInitialScrollWatchdogTargetOffset(state) {
2012
- var _a3;
2013
- return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
2014
- }
2015
- function isNativeInitialNonZeroTarget(state) {
2016
- const targetOffset = getInitialScrollWatchdogTargetOffset(state);
2017
- return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
2018
1858
  }
2019
- function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
2020
- var _a3, _b;
2021
- if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
2022
- return false;
2023
- }
2024
- if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
2025
- return false;
2026
- }
2027
- const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
2028
- if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
2029
- return false;
2030
- }
2031
- 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) {
2032
1861
  return false;
2033
1862
  }
2034
- return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
1863
+ const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
1864
+ return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
2035
1865
  }
2036
- function shouldFinishInitialZeroTargetScroll(ctx) {
2037
- var _a3;
2038
- const { state } = ctx;
2039
- 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;
2040
- }
2041
- function isEndAlignedLastItemTarget(ctx, scrollingTo) {
2042
- return scrollingTo.index === ctx.state.props.data.length - 1 && scrollingTo.viewPosition === 1;
2043
- }
2044
- function getCurrentTargetOffset(ctx, scrollingTo) {
2045
- var _a3;
2046
- const index = scrollingTo.index;
2047
- const shouldRecomputeEndTarget = isEndAlignedLastItemTarget(ctx, scrollingTo);
2048
- 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);
2049
- return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
2050
- }
2051
- function getResolvedScrollCompletionState(ctx, scrollingTo) {
2052
- const { state } = ctx;
2053
- const scroll = state.scrollPending;
2054
- const adjust = state.scrollAdjustHandler.getAdjust();
2055
- const clampedTargetOffset = getCurrentTargetOffset(ctx, scrollingTo);
2056
- const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
2057
- const diff1 = Math.abs(scroll - clampedTargetOffset);
2058
- const adjustedTargetOffset = clampedTargetOffset + adjust;
2059
- const diff2 = Math.abs(scroll - adjustedTargetOffset);
2060
- const canUseAdjustedCompletion = !scrollingTo.animated || Platform.OS === "ios";
2061
- return {
2062
- clampedTargetOffset,
2063
- isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
2064
- };
2065
- }
2066
- function checkFinishedScrollFrame(ctx) {
2067
- const scrollingTo = ctx.state.scrollingTo;
2068
- if (!scrollingTo) {
2069
- return;
1866
+ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
1867
+ if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
1868
+ return 0;
2070
1869
  }
2071
- const { state } = ctx;
2072
- state.animFrameCheckFinishedScroll = void 0;
2073
- const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
2074
- if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
2075
- clampedTargetOffset: completionState.clampedTargetOffset,
2076
- scrollingTo
2077
- })) {
2078
- 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));
1874
+ }
1875
+ if (unresolvedAmount > 0) {
1876
+ return Math.min(unresolvedAmount, Math.max(0, clampDelta));
2079
1877
  }
1878
+ return 0;
2080
1879
  }
2081
- function scrollToFallbackOffset(ctx, offset) {
2082
- var _a3;
2083
- (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
2084
- animated: false,
2085
- x: ctx.state.props.horizontal ? offset : 0,
2086
- y: ctx.state.props.horizontal ? 0 : offset
2087
- });
1880
+ function getProgressTowardAmount(targetDelta, nativeDelta) {
1881
+ return targetDelta < 0 ? -nativeDelta : nativeDelta;
2088
1882
  }
2089
- function checkFinishedScrollFallback(ctx) {
1883
+ function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
2090
1884
  const state = ctx.state;
2091
- const scrollingTo = state.scrollingTo;
2092
- const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
2093
- const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
2094
- const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
2095
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
2096
- const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
2097
- state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
2098
- let numChecks = 0;
2099
- const scheduleFallbackCheck = (delay) => {
2100
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
2101
- };
2102
- const checkHasScrolled = () => {
2103
- var _a3, _b, _c, _d;
2104
- state.timeoutCheckFinishedScrollFallback = void 0;
2105
- const isStillScrollingTo = state.scrollingTo;
2106
- if (isStillScrollingTo) {
2107
- numChecks++;
2108
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
2109
- const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
2110
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
2111
- const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
2112
- state,
2113
- isStillScrollingTo
2114
- );
2115
- const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
2116
- const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
2117
- const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
2118
- const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
2119
- const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2120
- const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2121
- if (shouldRetrySilentInitialNativeScroll) {
2122
- const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
2123
- const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
2124
- initialScrollCompletion.markSilentInitialScrollRetry(state);
2125
- scrollToFallbackOffset(ctx, jiggleOffset);
2126
- requestAnimationFrame(() => {
2127
- scrollToFallbackOffset(ctx, targetOffset);
2128
- });
2129
- scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
2130
- } else if (shouldRetryUnalignedEndScroll) {
2131
- scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
2132
- scheduleFallbackCheck(100);
2133
- } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
2134
- finishScrollTo(ctx);
2135
- } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
2136
- const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
2137
- scrollToFallbackOffset(ctx, targetOffset);
2138
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2139
- } else {
2140
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2141
- }
2142
- }
2143
- };
2144
- checkHasScrolled();
2145
- }, 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
+ }
2146
1890
  }
2147
-
2148
- // src/core/doScrollTo.native.ts
2149
- function doScrollTo(ctx, params) {
1891
+ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
2150
1892
  const state = ctx.state;
2151
- const { animated, horizontal, isInitialScroll, offset } = params;
2152
- const isAnimated = !!animated;
2153
- const { refScroller } = state;
2154
- const scroller = refScroller.current;
2155
- if (!scroller) {
1893
+ const pending = state.pendingNativeMVCPAdjust;
1894
+ if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
2156
1895
  return;
2157
1896
  }
2158
- const isHorizontal = !!horizontal;
2159
- const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
2160
- const nativeOffset = toNativeHorizontalOffset(state, offset, contentSize);
2161
- scroller.scrollTo({
2162
- animated: isAnimated,
2163
- x: isHorizontal ? nativeOffset : 0,
2164
- y: isHorizontal ? 0 : offset
2165
- });
2166
- if (isInitialScroll) {
2167
- 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;
2168
1901
  }
2169
- if (!isAnimated) {
2170
- state.scroll = offset;
2171
- checkFinishedScrollFallback(ctx);
1902
+ const manualDesired = pending.amount - predictedNativeClamp;
1903
+ if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
1904
+ return;
2172
1905
  }
1906
+ pending.manualApplied = manualDesired;
1907
+ requestAdjust(ctx, manualDesired, true);
1908
+ pending.furthestProgressTowardAmount = 0;
2173
1909
  }
2174
-
2175
- // src/utils/requestAdjust.ts
2176
- function requestAdjust(ctx, positionDiff, dataChanged) {
1910
+ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2177
1911
  const state = ctx.state;
2178
- if (Math.abs(positionDiff) > 0.1) {
2179
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2180
- const doit = () => {
2181
- if (needsScrollWorkaround) {
2182
- doScrollTo(ctx, { horizontal: state.props.horizontal, offset: state.scroll });
2183
- } else {
2184
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2185
- if (state.adjustingFromInitialMount) {
2186
- state.adjustingFromInitialMount--;
2187
- }
2188
- }
2189
- };
2190
- state.scroll += positionDiff;
2191
- state.scrollForNextCalculateItemsInView = void 0;
2192
- const readyToRender = peek$(ctx, "readyToRender");
2193
- if (readyToRender) {
2194
- doit();
2195
- if (Platform.OS !== "web") {
2196
- const threshold = state.scroll - positionDiff / 2;
2197
- if (!state.ignoreScrollFromMVCP) {
2198
- state.ignoreScrollFromMVCP = {};
2199
- }
2200
- if (positionDiff > 0) {
2201
- state.ignoreScrollFromMVCP.lt = threshold;
2202
- } else {
2203
- state.ignoreScrollFromMVCP.gt = threshold;
2204
- }
2205
- if (state.ignoreScrollFromMVCPTimeout) {
2206
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
2207
- }
2208
- const delay = needsScrollWorkaround ? 250 : 100;
2209
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2210
- var _a3;
2211
- state.ignoreScrollFromMVCP = void 0;
2212
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2213
- if (shouldForceUpdate) {
2214
- state.ignoreScrollFromMVCPIgnored = false;
2215
- state.scrollPending = state.scroll;
2216
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2217
- }
2218
- }, delay);
2219
- }
2220
- } else {
2221
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2222
- requestAnimationFrame(doit);
2223
- }
1912
+ const pending = state.pendingNativeMVCPAdjust;
1913
+ if (!pending) {
1914
+ return false;
2224
1915
  }
2225
- }
2226
-
2227
- // src/core/updateContentMetrics.ts
2228
- var SCROLL_ADJUST_EPSILON = 0.1;
2229
- function setContentLengthSignal(ctx, signalName, size) {
2230
- const didChange = peek$(ctx, signalName) !== size;
2231
- if (didChange) {
2232
- set$(ctx, signalName, size);
2233
- 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;
2234
1923
  }
2235
- return didChange;
2236
- }
2237
- function shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, nextHeaderSize) {
2238
- const { didContainersLayout, didFinishInitialScroll, props, scroll, scrollingTo } = ctx.state;
2239
- const sizeDiff = nextHeaderSize - previousHeaderSize;
2240
- const leadingPadding = props.horizontal ? props.stylePaddingLeft : props.stylePaddingTop;
2241
- const previousHeaderEnd = (leadingPadding || 0) + previousHeaderSize;
2242
- return Platform.OS === "web" && props.maintainVisibleContentPosition.size && didContainersLayout && didFinishInitialScroll && !scrollingTo && scroll >= previousHeaderEnd - SCROLL_ADJUST_EPSILON && Math.abs(sizeDiff) > SCROLL_ADJUST_EPSILON;
2243
- }
2244
- function setHeaderSize(ctx, size) {
2245
- const { state } = ctx;
2246
- const previousHeaderSize = peek$(ctx, "headerSize") || 0;
2247
- const didChange = previousHeaderSize !== size;
2248
- const hasMeasuredOrEstimatedHeaderBaseline = state.didMeasureHeader || previousHeaderSize > SCROLL_ADJUST_EPSILON;
2249
- if (didChange) {
2250
- set$(ctx, "headerSize", size);
2251
- updateContentMetricsState(ctx);
2252
- if (hasMeasuredOrEstimatedHeaderBaseline && shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, size)) {
2253
- requestAdjust(ctx, size - previousHeaderSize);
2254
- }
1924
+ if (isWrongDirection) {
1925
+ state.pendingNativeMVCPAdjust = void 0;
1926
+ return false;
2255
1927
  }
2256
- state.didMeasureHeader = true;
2257
- }
2258
- function setFooterSize(ctx, size) {
2259
- return setContentLengthSignal(ctx, "footerSize", size);
2260
- }
2261
- function areInsetsEqual(left, right) {
2262
- var _a3, _b, _c, _d, _e, _f, _g, _h;
2263
- 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);
2264
- }
2265
- function setContentInsetOverride(ctx, inset) {
2266
- const { state } = ctx;
2267
- const previousInset = state.contentInsetOverride;
2268
- const nextInset = inset != null ? inset : void 0;
2269
- const didChange = !areInsetsEqual(previousInset, nextInset);
2270
- state.contentInsetOverride = nextInset;
2271
- if (didChange) {
2272
- updateContentMetricsState(ctx);
1928
+ if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
1929
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1930
+ return true;
2273
1931
  }
2274
- return didChange;
2275
- }
2276
- function useLatestRef(value) {
2277
- const ref = React2.useRef(value);
2278
- ref.current = value;
2279
- return ref;
2280
- }
2281
-
2282
- // src/hooks/useStableRenderComponent.tsx
2283
- function useStableRenderComponent(renderComponent, mapProps) {
2284
- const renderComponentRef = useLatestRef(renderComponent);
2285
- const mapPropsRef = useLatestRef(mapProps);
2286
- return React2.useMemo(
2287
- () => React2.forwardRef(
2288
- (props, ref) => {
2289
- var _a3, _b;
2290
- return (_b = (_a3 = renderComponentRef.current) == null ? void 0 : _a3.call(renderComponentRef, mapPropsRef.current(props, ref))) != null ? _b : null;
2291
- }
2292
- ),
2293
- [mapPropsRef, renderComponentRef]
2294
- );
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;
2295
1952
  }
2296
- var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
2297
- const localRef = useRef(null);
2298
- const ref = refView != null ? refView : localRef;
2299
- const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
2300
- return /* @__PURE__ */ React2.createElement(View$1, { ...rest, onLayout, ref });
2301
- };
2302
-
2303
- // src/components/ListComponent.tsx
2304
- var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
2305
- const [alignItemsAtEndPadding = 0] = useArr$(["alignItemsAtEndPadding"]);
2306
- if (alignItemsAtEndPadding <= 0) {
2307
- 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;
2308
1966
  }
2309
- return /* @__PURE__ */ React2.createElement(
2310
- View,
2311
- {
2312
- style: horizontal ? { flexShrink: 0, width: alignItemsAtEndPadding } : { flexShrink: 0, height: alignItemsAtEndPadding }
2313
- },
2314
- null
2315
- );
2316
- });
2317
- var ListComponent = typedMemo(function ListComponent2({
2318
- canRender,
2319
- style,
2320
- contentContainerStyle,
2321
- horizontal,
2322
- initialContentOffset,
2323
- recycleItems,
2324
- ItemSeparatorComponent,
2325
- alignItemsAtEnd: _alignItemsAtEnd,
2326
- onScroll: onScroll2,
2327
- onLayout,
2328
- ListHeaderComponent,
2329
- ListHeaderComponentStyle,
2330
- ListFooterComponent,
2331
- ListFooterComponentStyle,
2332
- ListEmptyComponent,
2333
- getRenderedItem: getRenderedItem2,
2334
- updateItemSize: updateItemSize2,
2335
- refScrollView,
2336
- renderScrollComponent,
2337
- onLayoutFooter,
2338
- scrollAdjustHandler,
2339
- snapToIndices,
2340
- stickyHeaderConfig,
2341
- stickyHeaderIndices,
2342
- useWindowScroll = false,
2343
- ...rest
2344
- }) {
2345
- const ctx = useStateContext();
2346
- const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
2347
- const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
2348
- const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
2349
- const autoOtherAxisStyle = getAutoOtherAxisStyle({
2350
- horizontal,
2351
- needsOtherAxisSize: ctx.state.needsOtherAxisSize,
2352
- otherAxisSize
2353
- });
2354
- const CustomScrollComponent = useStableRenderComponent(
2355
- renderScrollComponent,
2356
- (props, ref) => ({ ...props, ref })
2357
- );
2358
- const ScrollComponent = renderScrollComponent ? CustomScrollComponent : ListComponentScrollView;
2359
- const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
2360
- const updateFooterSize = useCallback(
2361
- (size, afterSizeUpdate) => {
2362
- var _a3;
2363
- const didFooterSizeChange = setFooterSize(ctx, size);
2364
- afterSizeUpdate == null ? void 0 : afterSizeUpdate();
2365
- if (didFooterSizeChange && ((_a3 = ctx.state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onFooterLayout)) {
2366
- 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;
2367
1989
  }
2368
- },
2369
- [ctx]
2370
- );
2371
- useLayoutEffect(() => {
2372
- if (!ListHeaderComponent) {
2373
- 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);
2374
1993
  }
2375
- if (!ListFooterComponent) {
2376
- 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
+ }
2377
2005
  }
2378
- }, [ListHeaderComponent, ListFooterComponent, ctx, updateFooterSize]);
2379
- const onLayoutHeader = useCallback(
2380
- (rect) => {
2381
- const size = rect[horizontal ? "width" : "height"];
2382
- setHeaderSize(ctx, size);
2383
- },
2384
- [ctx, horizontal]
2385
- );
2386
- const onLayoutFooterInternal = useCallback(
2387
- (rect, fromLayoutEffect) => {
2388
- const size = rect[horizontal ? "width" : "height"];
2389
- updateFooterSize(size, () => {
2390
- onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
2391
- });
2392
- },
2393
- [horizontal, onLayoutFooter, updateFooterSize]
2394
- );
2395
- return /* @__PURE__ */ React2.createElement(
2396
- SnapOrScroll,
2397
- {
2398
- ...rest,
2399
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
2400
- contentContainerStyle: [
2401
- horizontal ? {
2402
- height: "100%"
2403
- } : {},
2404
- contentContainerStyle
2405
- ],
2406
- contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
2407
- horizontal,
2408
- maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
2409
- onLayout,
2410
- onScroll: onScroll2,
2411
- ref: refScrollView,
2412
- ScrollComponent: snapToIndices ? ScrollComponent : void 0,
2413
- style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
2414
- },
2415
- /* @__PURE__ */ React2.createElement(ScrollAdjust, null),
2416
- ListHeaderComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
2417
- ListEmptyComponent && getComponent(ListEmptyComponent),
2418
- shouldRenderAlignItemsAtEndSpacer && /* @__PURE__ */ React2.createElement(AlignItemsAtEndSpacer, { horizontal }),
2419
- canRender && !ListEmptyComponent && /* @__PURE__ */ React2.createElement(
2420
- Containers,
2421
- {
2422
- getRenderedItem: getRenderedItem2,
2423
- horizontal,
2424
- ItemSeparatorComponent,
2425
- recycleItems,
2426
- stickyHeaderConfig,
2427
- updateItemSize: updateItemSize2
2428
- }
2429
- ),
2430
- ListFooterComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
2431
- Platform.OS === "web" && /* @__PURE__ */ React2.createElement(WebAnchoredEndSpace, { horizontal }),
2432
- IS_DEV && ENABLE_DEVMODE
2433
- );
2434
- });
2435
- var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
2436
- var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
2437
- var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
2438
- function useDevChecksImpl(props) {
2439
- const ctx = useStateContext();
2440
- const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
2441
- useEffect(() => {
2442
- if (useWindowScroll && renderScrollComponent) {
2443
- warnDevOnce(
2444
- "useWindowScrollRenderScrollComponent",
2445
- "useWindowScroll is not supported when renderScrollComponent is provided."
2446
- );
2447
- }
2448
- }, [renderScrollComponent, useWindowScroll]);
2449
- useEffect(() => {
2450
- if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
2451
- warnDevOnce(
2452
- "keyExtractor",
2453
- "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."
2454
- );
2455
- }
2456
- }, [childrenMode, ctx, keyExtractor]);
2457
- useEffect(() => {
2458
- const state = ctx.state;
2459
- const dataLength = state.props.data.length;
2460
- const useWindowScrollResolved = state.props.useWindowScroll;
2461
- if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
2462
- return;
2463
- }
2464
- const warnIfUnboundedOuterSize = () => {
2465
- const readyToRender = peek$(ctx, "readyToRender");
2466
- const numContainers = peek$(ctx, "numContainers") || 0;
2467
- const totalSize = peek$(ctx, "totalSize") || 0;
2468
- const scrollLength = ctx.state.scrollLength || 0;
2469
- if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
2470
- return;
2471
- }
2472
- const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
2473
- const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
2474
- if (rendersAlmostEverything && viewportMatchesContent) {
2475
- warnDevOnce(
2476
- "webUnboundedOuterSize",
2477
- "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."
2478
- );
2479
- }
2480
- };
2481
- warnIfUnboundedOuterSize();
2482
- const unsubscribe = [
2483
- listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
2484
- listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
2485
- listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
2486
- ];
2487
- return () => {
2488
- for (const unsub of unsubscribe) {
2489
- unsub();
2490
- }
2491
- };
2492
- }, [ctx]);
2493
- }
2494
- function useDevChecksNoop(_props) {
2495
- }
2496
- var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
2497
-
2498
- // src/core/mvcp.ts
2499
- var MVCP_POSITION_EPSILON = 0.1;
2500
- var MVCP_ANCHOR_LOCK_TTL_MS = 300;
2501
- var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
2502
- var NATIVE_END_CLAMP_EPSILON = 1;
2503
- function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
2504
- if (!enableMVCPAnchorLock) {
2505
- state.mvcpAnchorLock = void 0;
2506
- return void 0;
2507
- }
2508
- const lock = state.mvcpAnchorLock;
2509
- if (!lock) {
2510
- return void 0;
2511
- }
2512
- const isExpired = now > lock.expiresAt;
2513
- const isMissing = state.indexByKey.get(lock.id) === void 0;
2514
- if (isExpired || isMissing || !mvcpData) {
2515
- state.mvcpAnchorLock = void 0;
2516
- return void 0;
2517
- }
2518
- return lock;
2519
- }
2520
- function updateAnchorLock(state, params) {
2521
- if (Platform.OS === "web") {
2522
- const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
2523
- const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
2524
- const mvcpData = state.props.maintainVisibleContentPosition.data;
2525
- if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
2526
- return;
2527
- }
2528
- const existingLock = state.mvcpAnchorLock;
2529
- const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
2530
- if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
2531
- state.mvcpAnchorLock = void 0;
2532
- return;
2533
- }
2534
- state.mvcpAnchorLock = {
2535
- expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
2536
- id: anchorId,
2537
- position: anchorPosition,
2538
- quietPasses
2539
- };
2540
- }
2541
- }
2542
- function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
2543
- if (!dataChanged || Platform.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
2544
- return false;
2545
- }
2546
- const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
2547
- return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
2548
- }
2549
- function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
2550
- if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
2551
- return 0;
2552
- }
2553
- const maxScroll = Math.max(0, totalSize - state.scrollLength);
2554
- const clampDelta = maxScroll - state.scroll;
2555
- if (unresolvedAmount < 0) {
2556
- return Math.max(unresolvedAmount, Math.min(0, clampDelta));
2557
- }
2558
- if (unresolvedAmount > 0) {
2559
- return Math.min(unresolvedAmount, Math.max(0, clampDelta));
2560
- }
2561
- return 0;
2562
- }
2563
- function getProgressTowardAmount(targetDelta, nativeDelta) {
2564
- return targetDelta < 0 ? -nativeDelta : nativeDelta;
2565
- }
2566
- function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
2567
- const state = ctx.state;
2568
- state.pendingNativeMVCPAdjust = void 0;
2569
- const remaining = remainingAfterManual - nativeDelta;
2570
- if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
2571
- requestAdjust(ctx, remaining, true);
2572
- }
2573
- }
2574
- function maybeApplyPredictedNativeMVCPAdjust(ctx) {
2575
- const state = ctx.state;
2576
- const pending = state.pendingNativeMVCPAdjust;
2577
- if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
2578
- return;
2579
- }
2580
- const totalSize = getContentSize(ctx);
2581
- const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
2582
- if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
2583
- return;
2584
- }
2585
- const manualDesired = pending.amount - predictedNativeClamp;
2586
- if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
2587
- return;
2588
- }
2589
- pending.manualApplied = manualDesired;
2590
- requestAdjust(ctx, manualDesired, true);
2591
- pending.furthestProgressTowardAmount = 0;
2592
- }
2593
- function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2594
- const state = ctx.state;
2595
- const pending = state.pendingNativeMVCPAdjust;
2596
- if (!pending) {
2597
- return false;
2598
- }
2599
- const remainingAfterManual = pending.amount - pending.manualApplied;
2600
- const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
2601
- const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
2602
- const progressTowardAmount = getProgressTowardAmount(remainingAfterManual, nativeDelta);
2603
- if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
2604
- state.pendingNativeMVCPAdjust = void 0;
2605
- return true;
2606
- }
2607
- if (isWrongDirection) {
2608
- state.pendingNativeMVCPAdjust = void 0;
2609
- return false;
2610
- }
2611
- if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
2612
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2613
- return true;
2614
- }
2615
- const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
2616
- const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
2617
- const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
2618
- if (isAtExpectedNativeClamp) {
2619
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2620
- return true;
2621
- }
2622
- if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
2623
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2624
- return true;
2625
- }
2626
- if (progressTowardAmount > pending.furthestProgressTowardAmount + MVCP_POSITION_EPSILON) {
2627
- pending.furthestProgressTowardAmount = progressTowardAmount;
2628
- return false;
2629
- }
2630
- if (pending.furthestProgressTowardAmount > MVCP_POSITION_EPSILON && progressTowardAmount < pending.furthestProgressTowardAmount - MVCP_POSITION_EPSILON) {
2631
- state.pendingNativeMVCPAdjust = void 0;
2632
- return false;
2633
- }
2634
- return false;
2635
- }
2636
- function prepareMVCP(ctx, dataChanged) {
2637
- const state = ctx.state;
2638
- const { idsInView, positions, props } = state;
2639
- const {
2640
- maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
2641
- } = props;
2642
- const isWeb = Platform.OS === "web";
2643
- const now = Date.now();
2644
- const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
2645
- const scrollingTo = state.scrollingTo;
2646
- if (isWeb && dataChanged && state.pendingScrollToEnd && scrollingTo === void 0) {
2647
- state.mvcpAnchorLock = void 0;
2648
- return void 0;
2649
- }
2650
- const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
2651
- let prevPosition;
2652
- let targetId;
2653
- const idsInViewWithPositions = [];
2654
- const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
2655
- const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
2656
- const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
2657
- const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
2658
- const indexByKey = state.indexByKey;
2659
- const prevScroll = state.scroll;
2660
- const prevTotalSize = getContentSize(ctx);
2661
- if (shouldMVCP) {
2662
- if (!isWeb && state.pendingNativeMVCPAdjust && scrollTarget === void 0) {
2663
- maybeApplyPredictedNativeMVCPAdjust(ctx);
2664
- return void 0;
2665
- }
2666
- if (anchorLock && scrollTarget === void 0) {
2667
- targetId = anchorLock.id;
2668
- prevPosition = anchorLock.position;
2669
- } else if (scrollTarget !== void 0) {
2670
- if (!IsNewArchitecture && (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll)) {
2671
- return void 0;
2672
- }
2673
- targetId = getId(state, scrollTarget);
2674
- } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
2675
- targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
2676
- }
2677
- if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
2678
- for (let i = 0; i < idsInView.length; i++) {
2679
- const id = idsInView[i];
2680
- const index = indexByKey.get(id);
2681
- if (index !== void 0) {
2682
- const position = positions[index];
2683
- if (position !== void 0) {
2684
- idsInViewWithPositions.push({ id, position });
2685
- }
2686
- }
2687
- }
2688
- }
2689
- if (targetId !== void 0 && prevPosition === void 0) {
2690
- const targetIndex = indexByKey.get(targetId);
2691
- if (targetIndex !== void 0) {
2692
- 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];
2693
2010
  }
2694
2011
  }
2695
2012
  return () => {
@@ -4757,7 +4074,21 @@ function getIdsInVisibleRange(state, range) {
4757
4074
  }
4758
4075
  return idsInView;
4759
4076
  }
4760
- 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) {
4761
4092
  var _a3, _b;
4762
4093
  const state = ctx.state;
4763
4094
  const {
@@ -4769,13 +4100,37 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4769
4100
  startBuffered
4770
4101
  } = state;
4771
4102
  if (startBuffered === null || endBuffered === null || startBuffered < 0 || endBuffered < startBuffered) {
4772
- return;
4103
+ return null;
4773
4104
  }
4774
- const visibleRange = {
4775
- endNoBuffer: null,
4776
- firstFullyOnScreenIndex: void 0,
4777
- startNoBuffer: null
4778
- };
4105
+ for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
4106
+ const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
4107
+ const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
4108
+ const top = positions[i];
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
+ };
4779
4134
  for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
4780
4135
  const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
4781
4136
  const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
@@ -4791,6 +4146,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4791
4146
  idsInView: getIdsInVisibleRange(state, visibleRange),
4792
4147
  startNoBuffer: visibleRange.startNoBuffer
4793
4148
  });
4149
+ maybeEmitFirstVisibleItemChanged(state, visibleRange.startNoBuffer);
4794
4150
  if (visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4795
4151
  updateViewableItems(
4796
4152
  state,
@@ -4916,6 +4272,8 @@ function calculateItemsInView(ctx, params = {}) {
4916
4272
  scroll,
4917
4273
  scrollBottom
4918
4274
  );
4275
+ } else if (state.props.onFirstVisibleItemChanged) {
4276
+ maybeEmitFirstVisibleItemChanged(state, findFirstVisibleIndexInCachedRange(ctx, scroll));
4919
4277
  }
4920
4278
  (_d = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _d.call(stickyState);
4921
4279
  return;
@@ -5151,87 +4509,1035 @@ function calculateItemsInView(ctx, params = {}) {
5151
4509
  state.userScrollAnchorReset = void 0;
5152
4510
  }
5153
4511
  }
5154
- if (alwaysRenderArr.length > 0) {
5155
- for (const index of alwaysRenderArr) {
5156
- if (index < 0 || index >= dataLength) continue;
5157
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
5158
- const containerIndex = containerItemKeys.get(id);
5159
- if (containerIndex !== void 0) {
5160
- state.stickyContainerPool.add(containerIndex);
5161
- }
5162
- }
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);
5163
5412
  }
5413
+ },
5414
+ [ctx]
5415
+ );
5416
+ useLayoutEffect(() => {
5417
+ if (!ListHeaderComponent) {
5418
+ setHeaderSize(ctx, 0);
5164
5419
  }
5165
- if (state.stickyContainerPool.size > 0) {
5166
- handleStickyRecycling(
5167
- ctx,
5168
- stickyHeaderIndicesArr,
5169
- scroll,
5170
- drawDistance,
5171
- (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
5172
- pendingRemoval,
5173
- alwaysRenderSet
5174
- );
5420
+ if (!ListFooterComponent) {
5421
+ updateFooterSize(0);
5175
5422
  }
5176
- const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
5177
- let didChangePositions = false;
5178
- for (let i = 0; i < numContainers; i++) {
5179
- const itemKey = peek$(ctx, `containerItemKey${i}`);
5180
- if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
5181
- if (itemKey !== void 0) {
5182
- containerItemKeys.delete(itemKey);
5183
- }
5184
- state.containerItemTypes.delete(i);
5185
- if (state.stickyContainerPool.has(i)) {
5186
- set$(ctx, `containerSticky${i}`, false);
5187
- state.stickyContainerPool.delete(i);
5188
- }
5189
- set$(ctx, `containerItemKey${i}`, void 0);
5190
- set$(ctx, `containerItemData${i}`, void 0);
5191
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
5192
- set$(ctx, `containerColumn${i}`, -1);
5193
- set$(ctx, `containerSpan${i}`, 1);
5194
- } else {
5195
- const itemIndex = indexByKey.get(itemKey);
5196
- if (itemIndex !== void 0) {
5197
- didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
5198
- scrollAdjustPending,
5199
- updateLayout: true
5200
- }).didChangePosition || didChangePositions;
5201
- }
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
5202
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
+ );
5203
5491
  }
5204
- if (Platform.OS === "web" && didChangePositions) {
5205
- 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
+ );
5206
5499
  }
5207
- if (suppressInitialScrollSideEffects) {
5208
- 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) {
5209
5506
  return;
5210
5507
  }
5211
- if (!queuedInitialLayout && !state.didContainersLayout) {
5212
- const isInitialLayoutReady = hasActiveInitialScroll(state) ? checkAllSizesKnown(state, state.startBuffered, state.endBuffered) : checkAllSizesKnown(state, state.startNoBuffer, state.endNoBuffer) || checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
5213
- if (isInitialLayoutReady) {
5214
- setDidLayout(ctx);
5215
- 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;
5216
5515
  }
5217
- }
5218
- if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
5219
- if (!didMVCPAdjustScroll) {
5220
- updateViewableItems(
5221
- ctx.state,
5222
- ctx,
5223
- viewabilityConfigCallbackPairs,
5224
- scrollLength,
5225
- visibleRange.startNoBuffer,
5226
- visibleRange.endNoBuffer,
5227
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
5228
- 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."
5229
5522
  );
5230
5523
  }
5231
- }
5232
- (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
5233
- });
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) {
5234
5539
  }
5540
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
5235
5541
 
5236
5542
  // src/core/checkResetContainers.ts
5237
5543
  function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
@@ -5553,61 +5859,6 @@ var ScrollAdjustHandler = class {
5553
5859
  }
5554
5860
  };
5555
5861
 
5556
- // src/core/updateAnchoredEndSpace.ts
5557
- function maybeUpdateAnchoredEndSpace(ctx) {
5558
- var _a3, _b;
5559
- const state = ctx.state;
5560
- const anchoredEndSpace = state.props.anchoredEndSpace;
5561
- const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5562
- const previousReadyAnchorIndex = state.anchoredEndSpaceReadyAnchorIndex;
5563
- const previousReadyAnchorKey = state.anchoredEndSpaceReadyAnchorKey;
5564
- const nextAnchorIndex = anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex;
5565
- let nextAnchorKey;
5566
- let isReady = true;
5567
- let nextSize = 0;
5568
- if (anchoredEndSpace) {
5569
- const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5570
- const { data } = state.props;
5571
- if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5572
- nextAnchorKey = getId(state, anchorIndex);
5573
- let contentBelowAnchor = 0;
5574
- const footerSize = ctx.values.get("footerSize") || 0;
5575
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5576
- let hasUnknownTailSize = false;
5577
- for (let index = anchorIndex; index < data.length; index++) {
5578
- const size = getKnownOrFixedItemSize(ctx, index);
5579
- const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5580
- if (size === void 0) {
5581
- hasUnknownTailSize = true;
5582
- }
5583
- if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5584
- contentBelowAnchor += effectiveSize;
5585
- }
5586
- }
5587
- contentBelowAnchor += footerSize + stylePaddingBottom;
5588
- isReady = !hasUnknownTailSize;
5589
- nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5590
- } else if (anchorIndex >= 0) {
5591
- isReady = false;
5592
- }
5593
- }
5594
- const didSizeChange = previousSize !== nextSize;
5595
- const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
5596
- if (isReady && (didSizeChange || didReadyAnchorChange)) {
5597
- state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
5598
- state.anchoredEndSpaceReadyAnchorKey = nextAnchorKey;
5599
- if (didSizeChange) {
5600
- set$(ctx, "anchoredEndSpaceSize", nextSize);
5601
- (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5602
- }
5603
- if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
5604
- updateScroll(ctx, state.scroll, true);
5605
- }
5606
- (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
5607
- }
5608
- return nextSize;
5609
- }
5610
-
5611
5862
  // src/core/updateContentInsetEndAdjustment.ts
5612
5863
  function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
5613
5864
  const state = ctx.state;
@@ -5623,173 +5874,6 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5623
5874
  }
5624
5875
  }
5625
5876
  }
5626
-
5627
- // src/core/updateItemSize.ts
5628
- function runOrScheduleMVCPRecalculate(ctx) {
5629
- var _a3;
5630
- const state = ctx.state;
5631
- if (state.userScrollAnchorReset !== void 0) {
5632
- calculateItemsInView(ctx);
5633
- if (((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
5634
- state.userScrollAnchorReset = void 0;
5635
- }
5636
- } else if (Platform.OS === "web") {
5637
- if (!state.mvcpAnchorLock) {
5638
- if (state.queuedMVCPRecalculate !== void 0) {
5639
- cancelAnimationFrame(state.queuedMVCPRecalculate);
5640
- state.queuedMVCPRecalculate = void 0;
5641
- }
5642
- calculateItemsInView(ctx, { doMVCP: true });
5643
- } else if (state.queuedMVCPRecalculate === void 0) {
5644
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5645
- state.queuedMVCPRecalculate = void 0;
5646
- calculateItemsInView(ctx, { doMVCP: true });
5647
- });
5648
- }
5649
- } else {
5650
- calculateItemsInView(ctx, { doMVCP: true });
5651
- }
5652
- }
5653
- function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5654
- const state = ctx.state;
5655
- if (state.needsOtherAxisSize) {
5656
- const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
5657
- const currentOtherAxisSize = peek$(ctx, "otherAxisSize");
5658
- if (!currentOtherAxisSize || otherAxisSize > currentOtherAxisSize) {
5659
- set$(ctx, "otherAxisSize", otherAxisSize);
5660
- }
5661
- }
5662
- }
5663
- function updateItemSize(ctx, itemKey, sizeObj) {
5664
- var _a3;
5665
- const state = ctx.state;
5666
- const userScrollAnchorReset = state.userScrollAnchorReset;
5667
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5668
- const {
5669
- didContainersLayout,
5670
- sizesKnown,
5671
- props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
5672
- } = state;
5673
- if (!data) return;
5674
- const index = state.indexByKey.get(itemKey);
5675
- let resolvedMeasurementItem;
5676
- if (getFixedItemSize) {
5677
- if (index === void 0) {
5678
- return;
5679
- }
5680
- const itemData = state.props.data[index];
5681
- if (itemData === void 0) {
5682
- return;
5683
- }
5684
- const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5685
- const size2 = getFixedItemSize(itemData, index, type);
5686
- resolvedMeasurementItem = {
5687
- didResolveFixedItemSize: true,
5688
- fixedItemSize: size2,
5689
- itemData,
5690
- itemType: type
5691
- };
5692
- if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5693
- updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5694
- return;
5695
- }
5696
- }
5697
- let needsRecalculate = !didContainersLayout;
5698
- let shouldMaintainScrollAtEnd = false;
5699
- let minIndexSizeChanged;
5700
- const prevSizeKnown = state.sizesKnown.get(itemKey);
5701
- const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5702
- const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
5703
- if (diff !== 0) {
5704
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
5705
- const { startBuffered, endBuffered } = state;
5706
- needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
5707
- if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
5708
- needsRecalculate = true;
5709
- }
5710
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
5711
- shouldMaintainScrollAtEnd = true;
5712
- }
5713
- onItemSizeChanged == null ? void 0 : onItemSizeChanged({
5714
- index,
5715
- itemData: state.props.data[index],
5716
- itemKey,
5717
- previous: size - diff,
5718
- size
5719
- });
5720
- maybeUpdateAnchoredEndSpace(ctx);
5721
- }
5722
- if (minIndexSizeChanged !== void 0) {
5723
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5724
- }
5725
- updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5726
- if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
5727
- if (needsRecalculate) {
5728
- state.scrollForNextCalculateItemsInView = void 0;
5729
- runOrScheduleMVCPRecalculate(ctx);
5730
- } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.size) === 0) {
5731
- state.userScrollAnchorReset = void 0;
5732
- }
5733
- if (shouldMaintainScrollAtEnd) {
5734
- if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
5735
- doMaintainScrollAtEnd(ctx);
5736
- }
5737
- }
5738
- }
5739
- }
5740
- function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5741
- var _a3, _b, _c;
5742
- const state = ctx.state;
5743
- const {
5744
- indexByKey,
5745
- sizesKnown,
5746
- averageSizes,
5747
- props: { data, horizontal, getItemType, getFixedItemSize }
5748
- } = state;
5749
- if (!data) return 0;
5750
- const index = indexByKey.get(itemKey);
5751
- const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
5752
- let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5753
- let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5754
- if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5755
- itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5756
- fixedItemSize = getFixedItemSize(itemData, index, itemType);
5757
- }
5758
- const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
5759
- didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
5760
- fixedItemSize,
5761
- itemType
5762
- } : void 0;
5763
- const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5764
- const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5765
- const prevSizeKnown = sizesKnown.get(itemKey);
5766
- if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
5767
- return 0;
5768
- }
5769
- const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5770
- sizesKnown.set(itemKey, size);
5771
- if (fixedItemSize === void 0 && size > 0) {
5772
- itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
5773
- let averages = averageSizes[itemType];
5774
- if (!averages) {
5775
- averages = averageSizes[itemType] = { avg: 0, num: 0 };
5776
- }
5777
- if (averages.num === 0) {
5778
- averages.avg = size;
5779
- averages.num++;
5780
- } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
5781
- averages.avg += (size - prevSizeKnown) / averages.num;
5782
- } else {
5783
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
5784
- averages.num++;
5785
- }
5786
- }
5787
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5788
- setSize(ctx, itemKey, size);
5789
- return size - prevSize;
5790
- }
5791
- return 0;
5792
- }
5793
5877
  function useWrapIfItem(fn) {
5794
5878
  return useMemo(
5795
5879
  () => fn ? (arg1, arg2, arg3) => arg1 !== void 0 && arg2 !== void 0 ? fn(arg1, arg2, arg3) : void 0 : void 0,
@@ -6110,7 +6194,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6110
6194
  return true;
6111
6195
  }),
6112
6196
  setItemSize: (itemKey, size) => {
6113
- updateItemSize(ctx, itemKey, size);
6197
+ updateItemSizes(ctx, { itemKey, size });
6114
6198
  },
6115
6199
  setScrollProcessingEnabled: (enabled) => {
6116
6200
  state.scrollProcessingEnabled = enabled;
@@ -6337,7 +6421,7 @@ var LegendList = typedMemo(
6337
6421
  })
6338
6422
  );
6339
6423
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
6340
- 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;
6341
6425
  const noopOnScroll = useCallback((_event) => {
6342
6426
  }, []);
6343
6427
  if (props.recycleItems === void 0) {
@@ -6392,6 +6476,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6392
6476
  onStartReached,
6393
6477
  onStartReachedThreshold = 0.5,
6394
6478
  onStickyHeaderChange,
6479
+ onFirstVisibleItemChanged,
6395
6480
  onViewableItemsChanged,
6396
6481
  progressViewOffset,
6397
6482
  recycleItems = false,
@@ -6493,6 +6578,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6493
6578
  if (!refState.current) {
6494
6579
  if (!ctx.state) {
6495
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");
6496
6582
  ctx.state = {
6497
6583
  averageSizes: {},
6498
6584
  columnSpans: [],
@@ -6570,6 +6656,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6570
6656
  }
6571
6657
  const state = refState.current;
6572
6658
  const isFirstLocal = state.isFirst;
6659
+ const previousAdaptiveRender = state.props.adaptiveRender;
6573
6660
  const previousNumColumnsProp = state.props.numColumns;
6574
6661
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
6575
6662
  ctx.scrollAxisGap = nextScrollAxisGap;
@@ -6577,7 +6664,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6577
6664
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6578
6665
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6579
6666
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6580
- 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) {
6581
6668
  clearPreservedInitialScrollTarget(state);
6582
6669
  }
6583
6670
  if (didDataChangeLocal) {
@@ -6589,7 +6676,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6589
6676
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6590
6677
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6591
6678
  const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
6592
- 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);
6593
6680
  state.props = {
6594
6681
  adaptiveRender: experimental_adaptiveRender,
6595
6682
  alignItemsAtEnd,
@@ -6617,6 +6704,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6617
6704
  numColumns: numColumnsProp,
6618
6705
  onEndReached,
6619
6706
  onEndReachedThreshold,
6707
+ onFirstVisibleItemChanged,
6620
6708
  onItemSizeChanged,
6621
6709
  onLoad,
6622
6710
  onScroll: throttleScrollFn,
@@ -6639,6 +6727,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6639
6727
  useWindowScroll: useWindowScrollResolved
6640
6728
  };
6641
6729
  state.refScroller = refScroller;
6730
+ if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) {
6731
+ resetAdaptiveRender(ctx);
6732
+ }
6642
6733
  const memoizedLastItemKeys = useMemo(() => {
6643
6734
  if (!dataProp.length) return [];
6644
6735
  return Array.from(
@@ -6883,8 +6974,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6883
6974
  onMomentumScrollEnd(event);
6884
6975
  }
6885
6976
  },
6886
- onScroll: (event) => onScroll(ctx, event),
6887
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
6977
+ onScroll: (event) => onScroll(ctx, event)
6888
6978
  }),
6889
6979
  []
6890
6980
  );
@@ -6911,7 +7001,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6911
7001
  onScroll: onScrollHandler,
6912
7002
  recycleItems,
6913
7003
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2.cloneElement(refreshControlElement, {
6914
- progressViewOffset: ((_n = refreshControlElement.props.progressViewOffset) != null ? _n : 0) + stylePaddingTopState
7004
+ progressViewOffset: ((_o = refreshControlElement.props.progressViewOffset) != null ? _o : 0) + stylePaddingTopState
6915
7005
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2.createElement(
6916
7006
  RefreshControl,
6917
7007
  {
@@ -6922,12 +7012,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6922
7012
  ),
6923
7013
  refScrollView: combinedRef,
6924
7014
  renderScrollComponent,
6925
- scrollAdjustHandler: (_o = refState.current) == null ? void 0 : _o.scrollAdjustHandler,
7015
+ scrollAdjustHandler: (_p = refState.current) == null ? void 0 : _p.scrollAdjustHandler,
6926
7016
  scrollEventThrottle: 0,
6927
7017
  snapToIndices,
6928
7018
  stickyHeaderIndices,
6929
7019
  style,
6930
- updateItemSize: fns.updateItemSize,
6931
7020
  useWindowScroll: useWindowScrollResolved
6932
7021
  }
6933
7022
  ), IS_DEV && ENABLE_DEBUG_VIEW);