@rific/scroll-view 0.8.2 → 0.9.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/dist/index.js CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  FlatList: () => FlatList,
35
35
  PullSearch: () => PullSearch,
36
36
  ScrollView: () => ScrollView,
37
+ ScrollViewChromeProvider: () => ScrollViewChromeProvider,
37
38
  ScrollViewContext: () => ScrollViewContext,
38
39
  ScrollViewFooter: () => ScrollViewFooter,
39
40
  ScrollViewHeader: () => ScrollViewHeader,
@@ -46,6 +47,9 @@ __export(index_exports, {
46
47
  scrollViewReducer: () => scrollViewReducer,
47
48
  useKeyboardInset: () => useKeyboardInset,
48
49
  useScrollView: () => useScrollView,
50
+ useScrollViewChromePin: () => useScrollViewChromePin,
51
+ useScrollViewChromeReveal: () => useScrollViewChromeReveal,
52
+ useScrollViewChromeStyle: () => useScrollViewChromeStyle,
49
53
  useScrollViewSettings: () => useScrollViewSettings
50
54
  });
51
55
  module.exports = __toCommonJS(index_exports);
@@ -90,6 +94,9 @@ var import_react_native_reanimated3 = require("react-native-reanimated");
90
94
  var import_react = require("react");
91
95
  var ScrollViewContext = (0, import_react.createContext)({
92
96
  blur: true,
97
+ chromeHosted: false,
98
+ chromeOverhang: 0,
99
+ chromeWritable: null,
93
100
  footerAboveKeyboard: false,
94
101
  footerHeight: null,
95
102
  footerHeightShared: null,
@@ -120,6 +127,7 @@ var ScrollViewContext = (0, import_react.createContext)({
120
127
  },
121
128
  snapBackFooterShared: null,
122
129
  snapBackHeaderShared: null,
130
+ stackHeightShared: null,
123
131
  tabBarHeight: 0
124
132
  });
125
133
 
@@ -311,9 +319,20 @@ function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomen
311
319
  return { activeListHeader, handleMomentumScrollEnd, handleScrollBeginDrag, handleScrollEndDrag, hiddenHeader, onRemountSyncRetry, onRemountSynced, pullSearchMinHeight: pullSearchHeight ?? 0, remountSyncTarget, remountTarget, scrollReady };
312
320
  }
313
321
 
322
+ // src/internal/chrome.ts
323
+ var CHROME_SLACK = 2;
324
+ var chromeTravel = (maxScroll, headerHeight) => {
325
+ "worklet";
326
+ return usesContentInset ? maxScroll + headerHeight : maxScroll;
327
+ };
328
+ var chromeSettleTarget = (offset, stackHeight) => {
329
+ "worklet";
330
+ return offset > stackHeight / 2 ? stackHeight : 0;
331
+ };
332
+
314
333
  // src/internal/useScrollHandler.ts
315
334
  function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100, footerFixed, headerFixed, isHorizontal, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget }) {
316
- const { footerHeightShared, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared } = (0, import_react3.useContext)(ScrollViewContext);
335
+ const { chromeHosted, chromeWritable, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared, stackHeightShared } = (0, import_react3.useContext)(ScrollViewContext);
317
336
  const snapUpAccum = (0, import_react_native_reanimated3.useSharedValue)(0);
318
337
  const fromBottomBounce = (0, import_react_native_reanimated3.useSharedValue)(false);
319
338
  return (0, import_react_native_reanimated3.useAnimatedScrollHandler)(
@@ -348,7 +367,9 @@ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100,
348
367
  fromBottomBounce.value = false;
349
368
  }
350
369
  const snapHeader = snapBackHeaderShared.value && !headerFixed;
351
- const snapFooter = snapBackFooterShared.value && !footerFixed;
370
+ const hideable = !chromeHosted || chromeTravel(maxScroll, headerHeightShared.value) >= stackHeightShared.value;
371
+ if (chromeHosted && !hideable && chromeWritable.value && footerOffset.value !== 0) footerOffset.value = 0;
372
+ const snapFooter = snapBackFooterShared.value && !footerFixed && chromeWritable.value && hideable;
352
373
  if (snapHeader || snapFooter) {
353
374
  if (yn <= -headerHeightShared.value) {
354
375
  snapUpAccum.value = 0;
@@ -358,7 +379,7 @@ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100,
358
379
  snapUpAccum.value = 0;
359
380
  if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) {
360
381
  if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta));
361
- if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta));
382
+ if (snapFooter) footerOffset.value = Math.max(0, Math.min(stackHeightShared.value, footerOffset.value + delta));
362
383
  }
363
384
  } else if (delta < 0 && !fromBottomBounce.value) {
364
385
  if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.value -= delta;
@@ -375,21 +396,29 @@ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100,
375
396
  fromBottomBounce.value = false;
376
397
  snapUpAccum.value = 0;
377
398
  },
378
- onEndDrag: ({ contentOffset: { y }, contentSize: { height: contentHeight }, layoutMeasurement: { height: layoutHeight } }) => {
399
+ onEndDrag: ({ contentOffset: { y }, contentSize: { height: contentHeight }, layoutMeasurement: { height: layoutHeight }, velocity }) => {
379
400
  "worklet";
380
401
  if (isHorizontal) return;
402
+ const revealing = snapUpAccum.value >= 10;
381
403
  if (y >= contentHeight - layoutHeight - 10) {
382
404
  fromBottomBounce.value = true;
383
405
  snapUpAccum.value = 0;
384
406
  }
407
+ if (chromeHosted && !footerFixed && chromeWritable.value && (velocity === void 0 || Math.abs(velocity.y) < 0.1)) {
408
+ footerOffset.value = (0, import_react_native_reanimated3.withTiming)(revealing ? 0 : chromeSettleTarget(footerOffset.value, stackHeightShared.value), { duration: 200 });
409
+ }
385
410
  },
386
411
  onMomentumEnd: () => {
387
412
  "worklet";
413
+ const revealing = snapUpAccum.value >= 10;
388
414
  fromBottomBounce.value = false;
389
415
  snapUpAccum.value = 0;
416
+ if (chromeHosted && !isHorizontal && !footerFixed && chromeWritable.value) {
417
+ footerOffset.value = (0, import_react_native_reanimated3.withTiming)(revealing ? 0 : chromeSettleTarget(footerOffset.value, stackHeightShared.value), { duration: 200 });
418
+ }
390
419
  }
391
420
  },
392
- [capturedGeneration, headerFixed, footerFixed, isHorizontal, chipThreshold, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget]
421
+ [capturedGeneration, chromeHosted, headerFixed, footerFixed, isHorizontal, chipThreshold, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget]
393
422
  );
394
423
  }
395
424
 
@@ -431,18 +460,20 @@ var CHIP_SLIDE = 48;
431
460
  function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, hideUntilMeasured, isHorizontal, keyboardAware, pullSearchHeight, style } = {}) {
432
461
  const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
433
462
  const keyboardHeight = useKeyboardInset();
434
- const { footerAboveKeyboard, footerHeight, footerFixed: contextFooterFixed, headerHeight, headerHeightShared, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared, tabBarHeight } = (0, import_react5.useContext)(ScrollViewContext);
463
+ const { chromeHosted, chromeOverhang, footerAboveKeyboard, footerHeight, footerFixed: contextFooterFixed, headerHeight, headerHeightShared, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared, tabBarHeight } = (0, import_react5.useContext)(ScrollViewContext);
435
464
  const headerFixed = isHorizontal ? true : headerFixedProp ?? contextHeaderFixed;
436
- const footerFixed = isHorizontal ? true : footerFixedProp ?? contextFooterFixed;
465
+ const footerFixed = isHorizontal ? true : chromeHosted ? contextFooterFixed : footerFixedProp ?? contextFooterFixed;
437
466
  (0, import_react5.useEffect)(() => {
438
467
  pullSearchHeightShared.value = usesContentInset ? pullSearchHeight ?? 0 : 0;
439
468
  }, [pullSearchHeight, pullSearchHeightShared]);
440
469
  const containerStyle = (0, import_react5.useMemo)(() => headerHeight === null ? [{ flex: 1, opacity: hideUntilMeasured ? 0 : 1 }, style] : [import_react_native5.StyleSheet.absoluteFill, style], [headerHeight, hideUntilMeasured, style]);
441
470
  const insetGeometry = (0, import_react5.useMemo)(() => {
442
- const footerReserve = footerFixed ? footerHeight || insets.bottom : insets.bottom;
443
- const bottom = (!keyboardAware || footerFixed && footerAboveKeyboard ? footerReserve : Math.max(footerReserve, keyboardHeight)) + tabBarHeight;
471
+ const baseReserve = chromeHosted ? chromeOverhang : insets.bottom;
472
+ const footerReserve = chromeHosted ? Math.max(footerHeight ?? 0, baseReserve) : footerFixed ? footerHeight || baseReserve : baseReserve;
473
+ const barReserve = chromeHosted && footerFixed && footerAboveKeyboard ? Math.max(tabBarHeight - keyboardHeight, 0) : tabBarHeight;
474
+ const bottom = (!keyboardAware || footerFixed && footerAboveKeyboard ? footerReserve : Math.max(footerReserve, keyboardHeight)) + barReserve;
444
475
  return { bottom, top: headerHeight ?? 0 };
445
- }, [footerAboveKeyboard, footerFixed, footerHeight, insets.bottom, tabBarHeight, headerHeight, keyboardAware, keyboardHeight]);
476
+ }, [chromeHosted, chromeOverhang, footerAboveKeyboard, footerFixed, footerHeight, insets.bottom, tabBarHeight, headerHeight, keyboardAware, keyboardHeight]);
446
477
  const contentInset = usesContentInset ? insetGeometry : ZERO_INSET;
447
478
  const contentPadding = (0, import_react5.useMemo)(() => usesContentInset || isHorizontal ? null : { paddingBottom: insetGeometry.bottom, paddingTop: insetGeometry.top }, [insetGeometry, isHorizontal]);
448
479
  const [startedWithHeader] = (0, import_react5.useState)(headerHeight !== null);
@@ -631,7 +662,7 @@ var import_react_native_reanimated8 = require("react-native-reanimated");
631
662
  var REMOUNT_RETRY_TOLERANCE = 1;
632
663
  var REMOUNT_RETRY_MAX_ATTEMPTS = 8;
633
664
  function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 100, footerFixed, headerFixed, isHorizontal, jsListGeneration, remountTarget = null, scrollTo }) {
634
- const { footerHeightShared, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared } = (0, import_react8.useContext)(ScrollViewContext);
665
+ const { chromeHosted, chromeWritable, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared, stackHeightShared } = (0, import_react8.useContext)(ScrollViewContext);
635
666
  const snapUpAccum = (0, import_react8.useRef)(0);
636
667
  const fromBottomBounce = (0, import_react8.useRef)(false);
637
668
  const pendingRemountTarget = (0, import_react8.useRef)(remountTarget);
@@ -675,7 +706,9 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
675
706
  fromBottomBounce.current = false;
676
707
  }
677
708
  const snapHeader = snapBackHeaderShared.value && !headerFixed;
678
- const snapFooter = snapBackFooterShared.value && !footerFixed;
709
+ const hideable = !chromeHosted || chromeTravel(maxScroll, headerHeightShared.value) >= stackHeightShared.value;
710
+ if (chromeHosted && !hideable && chromeWritable.value && footerOffset.value !== 0) footerOffset.value = 0;
711
+ const snapFooter = snapBackFooterShared.value && !footerFixed && chromeWritable.value && hideable;
679
712
  if (snapHeader || snapFooter) {
680
713
  if (yn <= -headerHeightShared.value) {
681
714
  snapUpAccum.current = 0;
@@ -685,7 +718,7 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
685
718
  snapUpAccum.current = 0;
686
719
  if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) {
687
720
  if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta));
688
- if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta));
721
+ if (snapFooter) footerOffset.value = Math.max(0, Math.min(stackHeightShared.value, footerOffset.value + delta));
689
722
  }
690
723
  } else if (delta < 0 && !fromBottomBounce.current) {
691
724
  if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.current -= delta;
@@ -697,7 +730,7 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
697
730
  }
698
731
  }
699
732
  },
700
- [capturedGeneration, chipHidden, chipThreshold, footerFixed, footerHeightShared, footerOffset, headerFixed, headerHeightShared, headerOffset, isHorizontal, jsListGeneration, pullSearchHeightShared, scrollPosition, scrollTo, snapBackFooterShared, snapBackHeaderShared]
733
+ [capturedGeneration, chipHidden, chipThreshold, chromeHosted, chromeWritable, footerFixed, footerOffset, headerFixed, headerHeightShared, headerOffset, isHorizontal, jsListGeneration, pullSearchHeightShared, scrollPosition, scrollTo, snapBackFooterShared, snapBackHeaderShared, stackHeightShared]
701
734
  );
702
735
  const onScrollBeginDrag = (0, import_react8.useCallback)(() => {
703
736
  fromBottomBounce.current = false;
@@ -705,25 +738,37 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
705
738
  }, []);
706
739
  const onMomentumScrollBegin = (0, import_react8.useCallback)(() => {
707
740
  }, []);
741
+ const settle = (0, import_react8.useCallback)(
742
+ (revealing) => {
743
+ if (!chromeHosted || footerFixed || !chromeWritable.value) return;
744
+ footerOffset.value = (0, import_react_native_reanimated8.withTiming)(revealing ? 0 : chromeSettleTarget(footerOffset.value, stackHeightShared.value), { duration: 200 });
745
+ },
746
+ [chromeHosted, chromeWritable, footerFixed, footerOffset, stackHeightShared]
747
+ );
708
748
  const onScrollEndDrag = (0, import_react8.useCallback)(
709
749
  (event) => {
710
750
  if (isHorizontal) return;
711
751
  const {
712
752
  contentOffset: { y },
713
753
  contentSize: { height: contentHeight },
714
- layoutMeasurement: { height: layoutHeight }
754
+ layoutMeasurement: { height: layoutHeight },
755
+ velocity
715
756
  } = event.nativeEvent;
757
+ const revealing = snapUpAccum.current >= 10;
716
758
  if (y >= contentHeight - layoutHeight - 10) {
717
759
  fromBottomBounce.current = true;
718
760
  snapUpAccum.current = 0;
719
761
  }
762
+ if (velocity === void 0 || Math.abs(velocity.y) < 0.1) settle(revealing);
720
763
  },
721
- [isHorizontal]
764
+ [isHorizontal, settle]
722
765
  );
723
766
  const onMomentumScrollEnd = (0, import_react8.useCallback)(() => {
767
+ const revealing = snapUpAccum.current >= 10;
724
768
  fromBottomBounce.current = false;
725
769
  snapUpAccum.current = 0;
726
- }, []);
770
+ if (!isHorizontal) settle(revealing);
771
+ }, [isHorizontal, settle]);
727
772
  return { onMomentumScrollBegin, onMomentumScrollEnd, onScroll, onScrollBeginDrag, onScrollEndDrag };
728
773
  }
729
774
 
@@ -1052,13 +1097,47 @@ var ScrollViewInner = ({ chipProps, chipThreshold, children, contentContainerSty
1052
1097
  };
1053
1098
  var ScrollView = (0, import_react11.memo)(ScrollViewInner);
1054
1099
 
1100
+ // src/ScrollViewChromeProvider.tsx
1101
+ var import_react13 = require("react");
1102
+ var import_react_native_reanimated11 = require("react-native-reanimated");
1103
+
1104
+ // src/ScrollViewChromeContext.ts
1105
+ var import_react12 = require("react");
1106
+ var ScrollViewChromeHostContext = (0, import_react12.createContext)(null);
1107
+ var ScrollViewChromeContext = (0, import_react12.createContext)(null);
1108
+
1109
+ // src/ScrollViewChromeProvider.tsx
1110
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1111
+ var REVEAL_DURATION = 200;
1112
+ var alwaysActive = () => true;
1113
+ var ScrollViewChromeProvider = ({ children, footprint, overhang = 0, snapBack = true, useIsActive = alwaysActive }) => {
1114
+ const offset = (0, import_react_native_reanimated11.useSharedValue)(0);
1115
+ const [claims, setClaims] = (0, import_react13.useState)([]);
1116
+ const [pins, setPins] = (0, import_react13.useState)(0);
1117
+ const activeId = claims.length > 0 ? claims[claims.length - 1] : null;
1118
+ const pinned = pins > 0;
1119
+ const claim = (0, import_react13.useCallback)((id) => setClaims((current) => [...current.filter((c) => c !== id), id]), []);
1120
+ const release = (0, import_react13.useCallback)((id) => setClaims((current) => current.filter((c) => c !== id)), []);
1121
+ const addPin = (0, import_react13.useCallback)(() => setPins((current) => current + 1), []);
1122
+ const removePin = (0, import_react13.useCallback)(() => setPins((current) => Math.max(0, current - 1)), []);
1123
+ const reveal = (0, import_react13.useCallback)(() => {
1124
+ offset.value = (0, import_react_native_reanimated11.withTiming)(0, { duration: REVEAL_DURATION });
1125
+ }, [offset]);
1126
+ (0, import_react13.useEffect)(() => {
1127
+ reveal();
1128
+ }, [activeId, pinned, reveal]);
1129
+ const host = (0, import_react13.useMemo)(() => ({ activeId, claim, footprint, offset, overhang, pinned, release, reveal, snapBack, useIsActive }), [activeId, claim, footprint, offset, overhang, pinned, release, reveal, snapBack, useIsActive]);
1130
+ const chrome = (0, import_react13.useMemo)(() => ({ addPin, footprint, offset, overhang, removePin, reveal }), [addPin, footprint, offset, overhang, removePin, reveal]);
1131
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ScrollViewChromeContext.Provider, { value: chrome, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ScrollViewChromeHostContext.Provider, { value: host, children }) });
1132
+ };
1133
+
1055
1134
  // src/ScrollViewFooter.tsx
1056
1135
  var import_auto_paper3 = require("@rific/auto-paper");
1057
- var import_react12 = require("react");
1136
+ var import_react14 = require("react");
1058
1137
  var import_react_native11 = require("react-native");
1059
- var import_react_native_reanimated11 = __toESM(require("react-native-reanimated"));
1138
+ var import_react_native_reanimated12 = __toESM(require("react-native-reanimated"));
1060
1139
  var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
1061
- var import_jsx_runtime8 = (
1140
+ var import_jsx_runtime9 = (
1062
1141
  // bottom: tabBarHeight (not the styles.footer default of 0) - a consuming app's own persistent
1063
1142
  // tab bar (a fixture outside this package's header/footer concepts entirely, same as
1064
1143
  // useScrollList's content-inset reservation) sits below this at the true screen bottom, so this
@@ -1066,14 +1145,14 @@ var import_jsx_runtime8 = (
1066
1145
  require("react/jsx-runtime")
1067
1146
  );
1068
1147
  var ScrollViewFooter = ({ children, style }) => {
1069
- const { blur, footerAboveKeyboard, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared, tabBarHeight } = (0, import_react12.useContext)(ScrollViewContext);
1148
+ const { blur, chromeHosted, chromeOverhang, footerAboveKeyboard, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared, stackHeightShared, tabBarHeight } = (0, import_react14.useContext)(ScrollViewContext);
1070
1149
  const insets = (0, import_react_native_safe_area_context3.useSafeAreaInsets)();
1071
1150
  const keyboardHeight = useKeyboardInset();
1072
- const setFooterHeightRef = (0, import_react12.useRef)(setFooterHeight);
1073
- (0, import_react12.useEffect)(() => {
1151
+ const setFooterHeightRef = (0, import_react14.useRef)(setFooterHeight);
1152
+ (0, import_react14.useEffect)(() => {
1074
1153
  setFooterHeightRef.current = setFooterHeight;
1075
1154
  });
1076
- (0, import_react12.useEffect)(
1155
+ (0, import_react14.useEffect)(
1077
1156
  () => () => {
1078
1157
  setFooterHeightRef.current(null);
1079
1158
  },
@@ -1086,19 +1165,29 @@ var ScrollViewFooter = ({ children, style }) => {
1086
1165
  }) => {
1087
1166
  if (footerHeight !== height) setFooterHeight(height);
1088
1167
  };
1089
- const footerStyle = (0, import_react_native_reanimated11.useAnimatedStyle)(() => {
1168
+ const footerStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
1169
+ if (chromeHosted) return { transform: [{ translateY: Math.min(Math.max(footerOffset.value, 0), stackHeightShared.value) }] };
1090
1170
  if (footerFixed) return { transform: [{ translateY: 0 }] };
1091
1171
  if (snapBackFooterShared.value) return { transform: [{ translateY: footerOffset.value }] };
1092
1172
  const effective = scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value;
1093
1173
  if (effective <= 0) return { transform: [{ translateY: 0 }] };
1094
1174
  return { transform: [{ translateY: Math.min(effective, footerHeight ?? 0) }] };
1095
- }, [footerHeight, footerFixed, headerHeightShared, pullSearchHeightShared, scrollPosition, snapBackFooterShared, footerOffset]);
1175
+ }, [chromeHosted, footerHeight, footerFixed, headerHeightShared, pullSearchHeightShared, scrollPosition, snapBackFooterShared, footerOffset, stackHeightShared]);
1096
1176
  const containerPaddingBottom = footerFixed && footerAboveKeyboard ? keyboardHeight : 0;
1097
- const rowPaddingBottom = footerFixed && footerAboveKeyboard ? Math.max(insets.bottom - keyboardHeight, 0) : insets.bottom;
1177
+ const rowRef = (0, import_react14.useRef)(null);
1178
+ (0, import_react14.useLayoutEffect)(() => {
1179
+ if (import_react_native11.Platform.OS !== "web" || footerHeight) return;
1180
+ const height = rowRef.current?.offsetHeight;
1181
+ if (typeof height !== "number" || height <= 0) return;
1182
+ setFooterHeight(height + containerPaddingBottom);
1183
+ }, []);
1184
+ const bottomPadding = chromeHosted ? chromeOverhang : insets.bottom;
1185
+ const rowPaddingBottom = footerFixed && footerAboveKeyboard ? Math.max(bottomPadding - keyboardHeight, 0) : bottomPadding;
1186
+ const footerBottom = chromeHosted && footerFixed && footerAboveKeyboard ? Math.max(tabBarHeight - keyboardHeight, 0) : tabBarHeight;
1098
1187
  const { paddingBottom: consumerPaddingBottom, ...restStyle } = style ?? {};
1099
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_reanimated11.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [styles6.footer, { bottom: tabBarHeight, paddingBottom: containerPaddingBottom }, footerStyle], children: [
1100
- (footerHeight ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper3.BlurView, { blur, style: import_react_native11.StyleSheet.absoluteFill }),
1101
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native11.View, { style: [styles6.row, restStyle, { paddingBottom: rowPaddingBottom + (typeof consumerPaddingBottom === "number" ? consumerPaddingBottom : 0) }], children })
1188
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [styles6.footer, { bottom: footerBottom, paddingBottom: containerPaddingBottom }, footerStyle], children: [
1189
+ (footerHeight ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper3.BlurView, { blur, style: import_react_native11.StyleSheet.absoluteFill }),
1190
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native11.View, { ref: rowRef, style: [styles6.row, restStyle, { paddingBottom: rowPaddingBottom + (typeof consumerPaddingBottom === "number" ? consumerPaddingBottom : 0) }], children })
1102
1191
  ] });
1103
1192
  };
1104
1193
  var styles6 = import_react_native11.StyleSheet.create({
@@ -1108,21 +1197,21 @@ var styles6 = import_react_native11.StyleSheet.create({
1108
1197
 
1109
1198
  // src/ScrollViewHeader.tsx
1110
1199
  var import_auto_paper4 = require("@rific/auto-paper");
1111
- var import_react13 = require("react");
1200
+ var import_react15 = require("react");
1112
1201
  var import_react_native12 = require("react-native");
1113
1202
  var import_react_native_paper4 = require("react-native-paper");
1114
- var import_react_native_reanimated12 = __toESM(require("react-native-reanimated"));
1203
+ var import_react_native_reanimated13 = __toESM(require("react-native-reanimated"));
1115
1204
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
1116
- var import_jsx_runtime9 = require("react/jsx-runtime");
1117
- var ActionBg = ({ blur, style }) => blur ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [import_react_native12.StyleSheet.absoluteFill, styles7.actionBg, style], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper4.BlurView, { blur: true, style: import_react_native12.StyleSheet.absoluteFill }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [import_react_native12.StyleSheet.absoluteFill, styles7.actionBg, style], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper4.Surface, { style: import_react_native12.StyleSheet.absoluteFill, elevation: 1, children: null }) });
1205
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1206
+ var ActionBg = ({ blur, style }) => blur ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: [import_react_native12.StyleSheet.absoluteFill, styles7.actionBg, style], children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_auto_paper4.BlurView, { blur: true, style: import_react_native12.StyleSheet.absoluteFill }) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: [import_react_native12.StyleSheet.absoluteFill, styles7.actionBg, style], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper4.Surface, { style: import_react_native12.StyleSheet.absoluteFill, elevation: 1, children: null }) });
1118
1207
  var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAccessibilityLabel, backActionFixed, caption, centerContent, children, iconSize, style, title, topInset = true, trailingAction, trailingActionFixed = true }) => {
1119
- const { blur, headerHeight, headerFixed, headerHeightShared, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = (0, import_react13.useContext)(ScrollViewContext);
1120
- const { settings } = (0, import_react13.useContext)(ScrollViewSettingsContext);
1208
+ const { blur, headerHeight, headerFixed, headerHeightShared, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = (0, import_react15.useContext)(ScrollViewContext);
1209
+ const { settings } = (0, import_react15.useContext)(ScrollViewSettingsContext);
1121
1210
  const effectiveBackActionFixed = backActionFixed ?? settings.backActionFixed;
1122
1211
  const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
1123
1212
  const theme = (0, import_react_native_paper4.useTheme)();
1124
1213
  const top = topInset ? insets.top : 0;
1125
- const measuredShared = (0, import_react_native_reanimated12.useSharedValue)(false);
1214
+ const measuredShared = (0, import_react_native_reanimated13.useSharedValue)(false);
1126
1215
  const handleLayout = ({
1127
1216
  nativeEvent: {
1128
1217
  layout: { height }
@@ -1131,24 +1220,32 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAc
1131
1220
  measuredShared.value = true;
1132
1221
  if (headerHeight !== height) setHeaderHeight(height);
1133
1222
  };
1223
+ const contentRef = (0, import_react15.useRef)(null);
1224
+ (0, import_react15.useLayoutEffect)(() => {
1225
+ if (import_react_native12.Platform.OS !== "web" || headerHeight) return;
1226
+ const height = contentRef.current?.offsetHeight;
1227
+ if (typeof height !== "number" || height <= 0) return;
1228
+ measuredShared.value = true;
1229
+ setHeaderHeight(height);
1230
+ }, []);
1134
1231
  const actionMargin = 4;
1135
1232
  const contentMinHeight = actionSize + 2 * actionMargin;
1136
1233
  const unmeasuredHeight = top + contentMinHeight;
1137
- const translateStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
1234
+ const translateStyle = (0, import_react_native_reanimated13.useAnimatedStyle)(() => {
1138
1235
  if (headerFixed) return { transform: [{ translateY: 0 }] };
1139
1236
  if (snapBackHeaderShared.value) return { transform: [{ translateY: headerOffset.value }] };
1140
1237
  const effective = scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value;
1141
1238
  if (effective <= 0) return { transform: [{ translateY: 0 }] };
1142
1239
  return { transform: [{ translateY: -effective }] };
1143
1240
  }, [headerFixed, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1144
- const blurStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
1241
+ const blurStyle = (0, import_react_native_reanimated13.useAnimatedStyle)(() => {
1145
1242
  if (!measuredShared.value) return { height: unmeasuredHeight };
1146
1243
  if (!headerHeightShared.value) return { height: 0 };
1147
1244
  if (headerFixed) return { height: headerHeightShared.value };
1148
1245
  const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value);
1149
1246
  return { height: Math.max(headerHeightShared.value + translateY, top) };
1150
1247
  }, [headerFixed, top, unmeasuredHeight, measuredShared, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1151
- const progressStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
1248
+ const progressStyle = (0, import_react_native_reanimated13.useAnimatedStyle)(() => {
1152
1249
  const h = headerHeightShared.value;
1153
1250
  if (headerFixed) return { top: h };
1154
1251
  const slide = snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
@@ -1159,32 +1256,32 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAc
1159
1256
  const actionShadow = { borderRadius: actionSize / 2, elevation: 2, shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.15, shadowRadius: 2 };
1160
1257
  const leadingStyle = { alignItems: "center", height: actionSize, justifyContent: "center", left: actionMargin, position: "absolute", top: actionTop, width: actionSize, zIndex: 3, ...actionShadow };
1161
1258
  const trailingStyle = { alignItems: "center", height: actionSize, justifyContent: "center", position: "absolute", right: actionMargin, top: actionTop, width: actionSize, zIndex: 3, ...actionShadow };
1162
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1163
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_reanimated12.default.View, { pointerEvents: "none", style: [styles7.blur, blurStyle], children: headerHeight !== 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper4.BlurView, { blur, style: [styles7.blurInner, { height: headerHeight ?? unmeasuredHeight }] }) }),
1164
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [headerHeight === null ? styles7.headerInit : styles7.header, translateStyle], children: [
1165
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [{ paddingTop: top }, style], children: children ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native12.View, { style: [styles7.content, { minHeight: contentMinHeight }], children: [
1166
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: styles7.side }),
1167
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: styles7.spacer }),
1168
- (centerContent || title || caption) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [import_react_native12.StyleSheet.absoluteFill, styles7.titleContainer], pointerEvents: centerContent ? "box-none" : "none", children: centerContent ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1169
- title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.Text, { numberOfLines: 1, style: [styles7.title, { color: theme.colors.onSurface }], children: title }),
1170
- caption && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.Text, { numberOfLines: 1, style: [styles7.caption, { color: theme.colors.onSurfaceVariant }], children: caption })
1259
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1260
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_reanimated13.default.View, { pointerEvents: "none", style: [styles7.blur, blurStyle], children: headerHeight !== 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_auto_paper4.BlurView, { blur, style: [styles7.blurInner, { height: headerHeight ?? unmeasuredHeight }] }) }),
1261
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native_reanimated13.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [headerHeight === null ? styles7.headerInit : styles7.header, translateStyle], children: [
1262
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { ref: contentRef, style: [{ paddingTop: top }, style], children: children ?? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: [styles7.content, { minHeight: contentMinHeight }], children: [
1263
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: styles7.side }),
1264
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: styles7.spacer }),
1265
+ (centerContent || title || caption) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: [import_react_native12.StyleSheet.absoluteFill, styles7.titleContainer], pointerEvents: centerContent ? "box-none" : "none", children: centerContent ?? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1266
+ title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.Text, { numberOfLines: 1, style: [styles7.title, { color: theme.colors.onSurface }], children: title }),
1267
+ caption && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.Text, { numberOfLines: 1, style: [styles7.caption, { color: theme.colors.onSurfaceVariant }], children: caption })
1171
1268
  ] }) })
1172
1269
  ] }) }),
1173
- !blur && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [styles7.divider, { backgroundColor: theme.colors.outlineVariant }] })
1270
+ !blur && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: [styles7.divider, { backgroundColor: theme.colors.outlineVariant }] })
1174
1271
  ] }),
1175
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_reanimated12.default.View, { pointerEvents: "none", style: [styles7.progress, progressStyle], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper4.ProgressBar, { indeterminate: progress === null, visible: progressing, progress: progress ?? void 0 }) }),
1176
- backAction && (effectiveBackActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: leadingStyle, children: [
1177
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
1178
- typeof backAction === "function" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper4.Appbar.BackAction, { accessibilityLabel: backActionAccessibilityLabel, onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle }) : backAction
1179
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: [leadingStyle, translateStyle], children: [
1180
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
1181
- typeof backAction === "function" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper4.Appbar.BackAction, { accessibilityLabel: backActionAccessibilityLabel, onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle }) : backAction
1272
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_reanimated13.default.View, { pointerEvents: "none", style: [styles7.progress, progressStyle], children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper4.ProgressBar, { indeterminate: progress === null, visible: progressing, progress: progress ?? void 0 }) }),
1273
+ backAction && (effectiveBackActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native_reanimated13.default.View, { style: leadingStyle, children: [
1274
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ActionBg, { blur, style: actionStyle }),
1275
+ typeof backAction === "function" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper4.Appbar.BackAction, { accessibilityLabel: backActionAccessibilityLabel, onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle }) : backAction
1276
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native_reanimated13.default.View, { style: [leadingStyle, translateStyle], children: [
1277
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ActionBg, { blur, style: actionStyle }),
1278
+ typeof backAction === "function" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native_paper4.Appbar.BackAction, { accessibilityLabel: backActionAccessibilityLabel, onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle }) : backAction
1182
1279
  ] })),
1183
- trailingAction && (trailingActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native12.View, { style: trailingStyle, children: [
1184
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
1280
+ trailingAction && (trailingActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.View, { style: trailingStyle, children: [
1281
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ActionBg, { blur, style: actionStyle }),
1185
1282
  trailingAction
1186
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: [trailingStyle, translateStyle], children: [
1187
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
1283
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native_reanimated13.default.View, { style: [trailingStyle, translateStyle], children: [
1284
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ActionBg, { blur, style: actionStyle }),
1188
1285
  trailingAction
1189
1286
  ] }))
1190
1287
  ] });
@@ -1207,56 +1304,100 @@ var styles7 = import_react_native12.StyleSheet.create({
1207
1304
 
1208
1305
  // src/ScrollViewProvider.tsx
1209
1306
  var import_auto_paper5 = require("@rific/auto-paper");
1210
- var import_react14 = require("react");
1307
+ var import_react17 = require("react");
1211
1308
  var import_react_native13 = require("react-native");
1212
- var import_react_native_reanimated13 = require("react-native-reanimated");
1213
- var import_jsx_runtime10 = require("react/jsx-runtime");
1214
- var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard = false, footerFixed, headerFixed, snapBack, snapBackFooter, snapBackHeader, tabBarHeight = 0 }) => {
1215
- const { settings } = (0, import_react14.useContext)(ScrollViewSettingsContext);
1309
+ var import_react_native_reanimated14 = require("react-native-reanimated");
1310
+
1311
+ // src/internal/ChromeRegistrar.tsx
1312
+ var import_react16 = require("react");
1313
+ var ChromeRegistrar = ({ footerFixed, host, writable }) => {
1314
+ const id = (0, import_react16.useId)();
1315
+ const active = host.useIsActive();
1316
+ const { activeId, claim, pinned, release, reveal } = host;
1317
+ (0, import_react16.useEffect)(() => {
1318
+ if (!active) return void 0;
1319
+ claim(id);
1320
+ return () => release(id);
1321
+ }, [active, claim, id, release]);
1322
+ const owner = activeId === id;
1323
+ (0, import_react16.useEffect)(() => {
1324
+ writable.value = owner && !pinned;
1325
+ }, [owner, pinned, writable]);
1326
+ (0, import_react16.useEffect)(() => {
1327
+ if (owner && footerFixed) reveal();
1328
+ }, [footerFixed, owner, reveal]);
1329
+ return null;
1330
+ };
1331
+
1332
+ // src/ScrollViewProvider.tsx
1333
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1334
+ var ScrollViewProvider = ({ blur, chrome, children, fixed = false, footerAboveKeyboard = false, footerFixed, headerFixed, snapBack, snapBackFooter, snapBackHeader, tabBarHeight = 0 }) => {
1335
+ const { settings } = (0, import_react17.useContext)(ScrollViewSettingsContext);
1336
+ const enclosingHost = (0, import_react17.useContext)(ScrollViewChromeHostContext);
1337
+ const chromeHost = chrome === false ? null : enclosingHost;
1338
+ const chromeHosted = chromeHost !== null;
1339
+ const chromeFootprint = chromeHost?.footprint ?? 0;
1340
+ const chromeOverhang = chromeHost?.overhang ?? 0;
1341
+ const chromeSnapBack = chromeHost?.snapBack ?? false;
1216
1342
  const effectiveBlur = (0, import_auto_paper5.useBlur)(blur);
1217
1343
  const effectiveSnapBack = snapBack ?? settings.snapBack;
1218
1344
  const effectiveSnapBackHeader = snapBackHeader ?? settings.snapBackHeader;
1219
1345
  const effectiveSnapBackFooter = snapBackFooter ?? settings.snapBackFooter;
1220
- const [headerHeight, setHeaderHeightState] = (0, import_react14.useState)(null);
1221
- const [footerHeight, setFooterHeightState] = (0, import_react14.useState)(null);
1222
- const [progress, setProgress] = (0, import_react14.useState)(null);
1223
- const [progressing, setProgressing] = (0, import_react14.useState)(false);
1224
- const scrollPosition = (0, import_react_native_reanimated13.useSharedValue)(0);
1225
- const listGeneration = (0, import_react_native_reanimated13.useSharedValue)(0);
1226
- const jsListGeneration = (0, import_react14.useRef)(0);
1227
- const headerHeightShared = (0, import_react_native_reanimated13.useSharedValue)(0);
1228
- const footerHeightShared = (0, import_react_native_reanimated13.useSharedValue)(0);
1229
- const headerOffset = (0, import_react_native_reanimated13.useSharedValue)(0);
1230
- const footerOffset = (0, import_react_native_reanimated13.useSharedValue)(0);
1231
- const pullSearchHeightShared = (0, import_react_native_reanimated13.useSharedValue)(0);
1232
- const snapBackHeaderShared = (0, import_react_native_reanimated13.useSharedValue)(false);
1233
- const snapBackFooterShared = (0, import_react_native_reanimated13.useSharedValue)(false);
1234
- (0, import_react14.useEffect)(() => {
1346
+ const [headerHeight, setHeaderHeightState] = (0, import_react17.useState)(null);
1347
+ const [footerHeight, setFooterHeightState] = (0, import_react17.useState)(null);
1348
+ const [progress, setProgress] = (0, import_react17.useState)(null);
1349
+ const [progressing, setProgressing] = (0, import_react17.useState)(false);
1350
+ const scrollPosition = (0, import_react_native_reanimated14.useSharedValue)(0);
1351
+ const listGeneration = (0, import_react_native_reanimated14.useSharedValue)(0);
1352
+ const jsListGeneration = (0, import_react17.useRef)(0);
1353
+ const headerHeightShared = (0, import_react_native_reanimated14.useSharedValue)(0);
1354
+ const footerHeightShared = (0, import_react_native_reanimated14.useSharedValue)(0);
1355
+ const headerOffset = (0, import_react_native_reanimated14.useSharedValue)(0);
1356
+ const ownFooterOffset = (0, import_react_native_reanimated14.useSharedValue)(0);
1357
+ const ownStackHeight = (0, import_react_native_reanimated14.useSharedValue)(0);
1358
+ const footerOffset = chromeHost ? chromeHost.offset : ownFooterOffset;
1359
+ const chromeWritable = (0, import_react_native_reanimated14.useSharedValue)(!chromeHosted);
1360
+ (0, import_react17.useEffect)(() => {
1361
+ if (!chromeHosted) chromeWritable.value = true;
1362
+ }, [chromeHosted, chromeWritable]);
1363
+ const revealChrome = chromeHost?.reveal;
1364
+ const stackHeightShared = chromeHosted ? ownStackHeight : footerHeightShared;
1365
+ const pullSearchHeightShared = (0, import_react_native_reanimated14.useSharedValue)(0);
1366
+ const snapBackHeaderShared = (0, import_react_native_reanimated14.useSharedValue)(false);
1367
+ const snapBackFooterShared = (0, import_react_native_reanimated14.useSharedValue)(false);
1368
+ (0, import_react17.useEffect)(() => {
1235
1369
  snapBackHeaderShared.value = effectiveSnapBackHeader ?? effectiveSnapBack;
1236
- snapBackFooterShared.value = effectiveSnapBackFooter ?? effectiveSnapBack;
1237
- }, [effectiveSnapBack, effectiveSnapBackFooter, effectiveSnapBackHeader, snapBackFooterShared, snapBackHeaderShared]);
1238
- const setHeaderHeight = (0, import_react14.useCallback)(
1239
- (h) => {
1240
- if (h === 0 && headerHeight) return;
1241
- setHeaderHeightState(h);
1242
- headerHeightShared.value = h ?? 0;
1243
- },
1244
- [headerHeight, headerHeightShared]
1245
- );
1246
- const onListUnmount = (0, import_react14.useCallback)(() => {
1370
+ snapBackFooterShared.value = chromeHosted ? chromeSnapBack : effectiveSnapBackFooter ?? effectiveSnapBack;
1371
+ }, [chromeHosted, chromeSnapBack, effectiveSnapBack, effectiveSnapBackFooter, effectiveSnapBackHeader, snapBackFooterShared, snapBackHeaderShared]);
1372
+ (0, import_react17.useEffect)(() => {
1373
+ if (!chromeHosted) return;
1374
+ ownStackHeight.value = chromeFootprint + Math.max(footerHeight ?? 0, chromeOverhang) + CHROME_SLACK;
1375
+ }, [chromeFootprint, chromeHosted, chromeOverhang, footerHeight, ownStackHeight]);
1376
+ const headerHeightRef = (0, import_react17.useRef)(null);
1377
+ const footerHeightRef = (0, import_react17.useRef)(null);
1378
+ const setHeaderHeight = (0, import_react17.useCallback)((h) => {
1379
+ if (h === 0 && headerHeightRef.current) return;
1380
+ headerHeightRef.current = h;
1381
+ setHeaderHeightState(h);
1382
+ }, []);
1383
+ (0, import_react17.useLayoutEffect)(() => {
1384
+ headerHeightShared.value = headerHeight ?? 0;
1385
+ }, [headerHeight, headerHeightShared]);
1386
+ const onListUnmount = (0, import_react17.useCallback)(() => {
1247
1387
  listGeneration.value += 1;
1248
- }, [listGeneration]);
1249
- const onJsListUnmount = (0, import_react14.useCallback)(() => {
1388
+ if (revealChrome && chromeWritable.value) revealChrome();
1389
+ }, [chromeWritable, listGeneration, revealChrome]);
1390
+ const onJsListUnmount = (0, import_react17.useCallback)(() => {
1250
1391
  jsListGeneration.current += 1;
1251
1392
  }, []);
1252
- (0, import_react14.useEffect)(() => {
1393
+ (0, import_react17.useEffect)(() => {
1253
1394
  if (!__DEV__ || headerHeight !== null) return;
1254
1395
  const timeout = setTimeout(() => {
1255
1396
  console.warn("[scroll-view] No ScrollViewHeader ever measured inside this ScrollViewProvider \u2014 scroll content stays hidden (opacity: 0) until one does. Render a <ScrollViewHeader> (it can be empty / zero height) inside this provider.");
1256
1397
  }, 3e3);
1257
1398
  return () => clearTimeout(timeout);
1258
1399
  }, [headerHeight]);
1259
- (0, import_react14.useEffect)(() => {
1400
+ (0, import_react17.useEffect)(() => {
1260
1401
  if (import_react_native13.Platform.OS !== "web" || headerHeight !== null) return;
1261
1402
  let raf2 = 0;
1262
1403
  const raf1 = requestAnimationFrame(() => {
@@ -1267,47 +1408,55 @@ var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard =
1267
1408
  cancelAnimationFrame(raf2);
1268
1409
  };
1269
1410
  }, [headerHeight, setHeaderHeight]);
1270
- const setFooterHeight = (0, import_react14.useCallback)(
1411
+ const setFooterHeight = (0, import_react17.useCallback)(
1271
1412
  (h) => {
1272
- if (h === 0 && footerHeight) return;
1413
+ if (h === 0 && footerHeightRef.current) return;
1414
+ footerHeightRef.current = h;
1273
1415
  setFooterHeightState(h);
1274
1416
  footerHeightShared.value = h ?? 0;
1275
1417
  },
1276
- [footerHeight, footerHeightShared]
1418
+ [footerHeightShared]
1277
1419
  );
1278
- const scrollHeight = (0, import_react14.useMemo)(() => import_react_native13.Dimensions.get("window").height - (headerHeight ?? 0) - (footerHeight ?? 0), [headerHeight, footerHeight]);
1420
+ const scrollHeight = (0, import_react17.useMemo)(() => import_react_native13.Dimensions.get("window").height - (headerHeight ?? 0) - (footerHeight ?? 0), [headerHeight, footerHeight]);
1279
1421
  const effectiveHeaderFixed = fixed || (headerFixed ?? settings.headerFixed);
1280
1422
  const effectiveFooterFixed = fixed || (footerFixed ?? settings.footerFixed);
1281
- const value = (0, import_react14.useMemo)(() => ({ blur: effectiveBlur, footerAboveKeyboard, footerHeight, footerHeightShared, footerFixed: effectiveFooterFixed, footerOffset, headerHeight, headerHeightShared, headerFixed: effectiveHeaderFixed, headerOffset, jsListGeneration, listGeneration, onJsListUnmount, onListUnmount, progress, pullSearchHeightShared, progressing, scrollHeight, scrollPosition, setFooterHeight, setHeaderHeight, setProgress, setProgressing, snapBackFooterShared, snapBackHeaderShared, tabBarHeight }), [effectiveBlur, footerAboveKeyboard, effectiveFooterFixed, effectiveHeaderFixed, footerHeight, footerHeightShared, footerOffset, headerHeight, headerHeightShared, headerOffset, jsListGeneration, listGeneration, onJsListUnmount, onListUnmount, progress, pullSearchHeightShared, progressing, scrollHeight, scrollPosition, setFooterHeight, setHeaderHeight, snapBackFooterShared, snapBackHeaderShared, tabBarHeight]);
1282
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScrollViewContext.Provider, { value, children });
1423
+ const effectiveTabBarHeight = chromeHosted ? chromeFootprint : tabBarHeight;
1424
+ const value = (0, import_react17.useMemo)(
1425
+ () => ({ blur: effectiveBlur, chromeHosted, chromeOverhang, chromeWritable, footerAboveKeyboard, footerHeight, footerHeightShared, footerFixed: effectiveFooterFixed, footerOffset, headerHeight, headerHeightShared, headerFixed: effectiveHeaderFixed, headerOffset, jsListGeneration, listGeneration, onJsListUnmount, onListUnmount, progress, pullSearchHeightShared, progressing, scrollHeight, scrollPosition, setFooterHeight, setHeaderHeight, setProgress, setProgressing, snapBackFooterShared, snapBackHeaderShared, stackHeightShared, tabBarHeight: effectiveTabBarHeight }),
1426
+ [effectiveBlur, chromeHosted, chromeOverhang, chromeWritable, footerAboveKeyboard, effectiveFooterFixed, effectiveHeaderFixed, footerHeight, footerHeightShared, footerOffset, headerHeight, headerHeightShared, headerOffset, jsListGeneration, listGeneration, onJsListUnmount, onListUnmount, progress, pullSearchHeightShared, progressing, scrollHeight, scrollPosition, setFooterHeight, setHeaderHeight, snapBackFooterShared, snapBackHeaderShared, stackHeightShared, effectiveTabBarHeight]
1427
+ );
1428
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScrollViewContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ScrollViewChromeHostContext.Provider, { value: null, children: [
1429
+ children,
1430
+ chromeHost ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChromeRegistrar, { footerFixed: effectiveFooterFixed, host: chromeHost, writable: chromeWritable }) : null
1431
+ ] }) });
1283
1432
  };
1284
1433
 
1285
1434
  // src/ScrollViewSettingsProvider.tsx
1286
1435
  var ScrollViewSettingsProvider = scrollViewSettingsProvider;
1287
1436
 
1288
1437
  // src/SectionList.tsx
1289
- var import_react16 = require("react");
1438
+ var import_react19 = require("react");
1290
1439
  var import_react_native14 = require("react-native");
1291
1440
  var import_react_native_gesture_handler4 = require("react-native-gesture-handler");
1292
- var import_react_native_reanimated15 = __toESM(require("react-native-reanimated"));
1441
+ var import_react_native_reanimated16 = __toESM(require("react-native-reanimated"));
1293
1442
 
1294
1443
  // src/internal/useStickyHeaders.ts
1295
- var import_react15 = require("react");
1296
- var import_react_native_reanimated14 = require("react-native-reanimated");
1444
+ var import_react18 = require("react");
1445
+ var import_react_native_reanimated15 = require("react-native-reanimated");
1297
1446
  var import_react_native_safe_area_context5 = require("react-native-safe-area-context");
1298
1447
  function useStickyHeaders(headerFixed, enabled) {
1299
- const { headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = (0, import_react15.useContext)(ScrollViewContext);
1448
+ const { headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = (0, import_react18.useContext)(ScrollViewContext);
1300
1449
  const insetsTop = (0, import_react_native_safe_area_context5.useSafeAreaInsets)().top;
1301
- const sectionYPositions = (0, import_react_native_reanimated14.useSharedValue)([]);
1302
- const sectionHeights = (0, import_react_native_reanimated14.useSharedValue)([]);
1303
- const [activeIndex, setActiveIndex] = (0, import_react15.useState)(-1);
1304
- const contentIndexShared = (0, import_react_native_reanimated14.useSharedValue)(-1);
1305
- (0, import_react15.useLayoutEffect)(() => {
1450
+ const sectionYPositions = (0, import_react_native_reanimated15.useSharedValue)([]);
1451
+ const sectionHeights = (0, import_react_native_reanimated15.useSharedValue)([]);
1452
+ const [activeIndex, setActiveIndex] = (0, import_react18.useState)(-1);
1453
+ const contentIndexShared = (0, import_react_native_reanimated15.useSharedValue)(-1);
1454
+ (0, import_react18.useLayoutEffect)(() => {
1306
1455
  contentIndexShared.value = activeIndex;
1307
1456
  }, [activeIndex, contentIndexShared]);
1308
- const positionsCache = (0, import_react15.useRef)([]);
1309
- const heightsCache = (0, import_react15.useRef)([]);
1310
- (0, import_react_native_reanimated14.useAnimatedReaction)(
1457
+ const positionsCache = (0, import_react18.useRef)([]);
1458
+ const heightsCache = (0, import_react18.useRef)([]);
1459
+ (0, import_react_native_reanimated15.useAnimatedReaction)(
1311
1460
  () => {
1312
1461
  if (!enabled) return -1;
1313
1462
  const positions = sectionYPositions.value;
@@ -1332,7 +1481,7 @@ function useStickyHeaders(headerFixed, enabled) {
1332
1481
  return active;
1333
1482
  },
1334
1483
  (curr, prev) => {
1335
- if (curr !== prev) (0, import_react_native_reanimated14.runOnJS)(setActiveIndex)(curr);
1484
+ if (curr !== prev) (0, import_react_native_reanimated15.runOnJS)(setActiveIndex)(curr);
1336
1485
  },
1337
1486
  // Same web-only reanimated gap as useAnimatedStyle elsewhere in this file: this "prepare"
1338
1487
  // function's own SharedValue reads (sectionYPositions, headerHeightShared, scrollPosition,
@@ -1341,7 +1490,7 @@ function useStickyHeaders(headerFixed, enabled) {
1341
1490
  // then never again on web, permanently freezing activeIndex/the active sticky section.
1342
1491
  [enabled, headerFixed, insetsTop, sectionYPositions, headerHeightShared, scrollPosition, snapBackHeaderShared, headerOffset, pullSearchHeightShared]
1343
1492
  );
1344
- const clipStyle = (0, import_react_native_reanimated14.useAnimatedStyle)(() => {
1493
+ const clipStyle = (0, import_react_native_reanimated15.useAnimatedStyle)(() => {
1345
1494
  const hHeight = headerHeightShared.value;
1346
1495
  const scrollPos = scrollPosition.value;
1347
1496
  let stickyTop;
@@ -1355,7 +1504,7 @@ function useStickyHeaders(headerFixed, enabled) {
1355
1504
  }
1356
1505
  return { top: stickyTop };
1357
1506
  }, [enabled, headerFixed, insetsTop, headerHeightShared, scrollPosition, snapBackHeaderShared, headerOffset, pullSearchHeightShared]);
1358
- const overlayStyle = (0, import_react_native_reanimated14.useAnimatedStyle)(() => {
1507
+ const overlayStyle = (0, import_react_native_reanimated15.useAnimatedStyle)(() => {
1359
1508
  const hidden = { opacity: 0, top: 0, transform: [{ translateY: 0 }] };
1360
1509
  if (!enabled) return hidden;
1361
1510
  const index = contentIndexShared.value;
@@ -1383,7 +1532,7 @@ function useStickyHeaders(headerFixed, enabled) {
1383
1532
  }
1384
1533
  return { opacity: 1, top: 0, transform: [{ translateY }] };
1385
1534
  }, [enabled, headerFixed, insetsTop, contentIndexShared, sectionYPositions, headerHeightShared, scrollPosition, snapBackHeaderShared, headerOffset, pullSearchHeightShared, sectionHeights]);
1386
- const measureHeader = (0, import_react15.useCallback)(
1535
+ const measureHeader = (0, import_react18.useCallback)(
1387
1536
  (index, pageY, height) => {
1388
1537
  const contentY = pageY + scrollPosition.value;
1389
1538
  positionsCache.current[index] = contentY;
@@ -1393,7 +1542,7 @@ function useStickyHeaders(headerFixed, enabled) {
1393
1542
  },
1394
1543
  [scrollPosition, sectionHeights, sectionYPositions]
1395
1544
  );
1396
- const resetPositions = (0, import_react15.useCallback)(
1545
+ const resetPositions = (0, import_react18.useCallback)(
1397
1546
  (count) => {
1398
1547
  positionsCache.current = Array.from({ length: count }, () => -1);
1399
1548
  sectionYPositions.value = positionsCache.current.slice();
@@ -1406,36 +1555,36 @@ function useStickyHeaders(headerFixed, enabled) {
1406
1555
  }
1407
1556
 
1408
1557
  // src/SectionList.tsx
1409
- var import_jsx_runtime11 = require("react/jsx-runtime");
1410
- var AnimatedSectionList = import_react_native_reanimated15.default.createAnimatedComponent(import_react_native14.SectionList);
1558
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1559
+ var AnimatedSectionList = import_react_native_reanimated16.default.createAnimatedComponent(import_react_native14.SectionList);
1411
1560
  var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: externalContentContainerStyle, initialNumToRender = 20, maxToRenderPerBatch = 50, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onContentSizeChange, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, removeClippedSubviews = false, sections, showsVerticalScrollIndicator, style, ref: externalRef, windowSize = 100, ...props }) => {
1412
- const scrollView = (0, import_react16.useRef)(null);
1561
+ const scrollView = (0, import_react19.useRef)(null);
1413
1562
  const { stickySectionHeadersEnabled = true, renderSectionHeader, ...passThroughProps } = props;
1414
1563
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, hideUntilMeasured: true, keyboardAware, pullSearchHeight, style });
1415
- const { listGeneration, onListUnmount } = (0, import_react16.useContext)(ScrollViewContext);
1416
- const capturedGeneration = (0, import_react_native_reanimated15.useSharedValue)(-1);
1417
- (0, import_react16.useLayoutEffect)(() => {
1564
+ const { listGeneration, onListUnmount } = (0, import_react19.useContext)(ScrollViewContext);
1565
+ const capturedGeneration = (0, import_react_native_reanimated16.useSharedValue)(-1);
1566
+ (0, import_react19.useLayoutEffect)(() => {
1418
1567
  const generation = listGeneration.value;
1419
1568
  if (import_react_native14.Platform.OS === "web") {
1420
1569
  capturedGeneration.value = generation;
1421
1570
  return;
1422
1571
  }
1423
- (0, import_react_native_reanimated15.runOnUI)((gen) => {
1572
+ (0, import_react_native_reanimated16.runOnUI)((gen) => {
1424
1573
  "worklet";
1425
1574
  capturedGeneration.value = gen;
1426
1575
  })(generation);
1427
1576
  }, [capturedGeneration, listGeneration]);
1428
1577
  const usesCustomSticky = stickySectionHeadersEnabled && renderSectionHeader != null;
1429
1578
  const { activeIndex, clipStyle, measureHeader, overlayStyle, resetPositions } = useStickyHeaders(headerFixed, usesCustomSticky);
1430
- (0, import_react16.useLayoutEffect)(() => {
1579
+ (0, import_react19.useLayoutEffect)(() => {
1431
1580
  if (usesCustomSticky) resetPositions(sections.length);
1432
1581
  }, [sections, usesCustomSticky, resetPositions, headerHeight]);
1433
- const sectionViewRefs = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
1434
- const wrappedRenderSectionHeader = (0, import_react16.useMemo)(() => {
1582
+ const sectionViewRefs = (0, import_react19.useRef)(/* @__PURE__ */ new Map());
1583
+ const wrappedRenderSectionHeader = (0, import_react19.useMemo)(() => {
1435
1584
  if (!usesCustomSticky) return renderSectionHeader ?? void 0;
1436
1585
  return (info) => {
1437
1586
  const idx = sections.findIndex((s) => s === info.section);
1438
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1587
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1439
1588
  import_react_native14.View,
1440
1589
  {
1441
1590
  ref: (el) => {
@@ -1452,8 +1601,8 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
1452
1601
  );
1453
1602
  };
1454
1603
  }, [usesCustomSticky, renderSectionHeader, sections, measureHeader]);
1455
- (0, import_react16.useLayoutEffect)(() => () => onListUnmount(), [onListUnmount]);
1456
- const scrollTo = (0, import_react16.useCallback)((offset, animated) => {
1604
+ (0, import_react19.useLayoutEffect)(() => () => onListUnmount(), [onListUnmount]);
1605
+ const scrollTo = (0, import_react19.useCallback)((offset, animated) => {
1457
1606
  scrollView.current?.getScrollResponder()?.scrollTo({ y: offset, animated });
1458
1607
  }, []);
1459
1608
  const {
@@ -1471,16 +1620,16 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
1471
1620
  pullSearchHeight,
1472
1621
  scrollTo
1473
1622
  });
1474
- const activeListHeader = (0, import_react16.useMemo)(() => {
1623
+ const activeListHeader = (0, import_react19.useMemo)(() => {
1475
1624
  if (!renderFilters) return pullSearchHeader;
1476
1625
  const Inner = pullSearchHeader;
1477
- return () => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1478
- Inner ? typeof Inner === "function" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Inner, {}) : Inner : null,
1626
+ return () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1627
+ Inner ? typeof Inner === "function" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Inner, {}) : Inner : null,
1479
1628
  renderFilters
1480
1629
  ] });
1481
1630
  }, [pullSearchHeader, renderFilters]);
1482
- const pullSearchSettled = (0, import_react16.useRef)(false);
1483
- (0, import_react16.useEffect)(() => {
1631
+ const pullSearchSettled = (0, import_react19.useRef)(false);
1632
+ (0, import_react19.useEffect)(() => {
1484
1633
  if (!usesCustomSticky || activeListHeader == null || pullSearchSettled.current) return;
1485
1634
  pullSearchSettled.current = true;
1486
1635
  const handle = requestAnimationFrame(() => {
@@ -1492,24 +1641,24 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
1492
1641
  });
1493
1642
  return () => cancelAnimationFrame(handle);
1494
1643
  }, [activeListHeader, usesCustomSticky, measureHeader]);
1495
- const contentContainerStyle = (0, import_react16.useMemo)(() => [{ minHeight: import_react_native14.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight]);
1644
+ const contentContainerStyle = (0, import_react19.useMemo)(() => [{ minHeight: import_react_native14.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight]);
1496
1645
  const handleScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration });
1497
- const handleScrollToTop = (0, import_react16.useCallback)(() => {
1646
+ const handleScrollToTop = (0, import_react19.useCallback)(() => {
1498
1647
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
1499
1648
  scrollView.current?.getScrollResponder()?.scrollTo({ y: offset, animated: true });
1500
1649
  }, [contentInset.top, pullSearchHeight]);
1501
- const handleContentSizeChange = (0, import_react16.useCallback)(
1650
+ const handleContentSizeChange = (0, import_react19.useCallback)(
1502
1651
  (w, h) => {
1503
1652
  if (onContentSizeChange) onContentSizeChange(w, h);
1504
1653
  },
1505
1654
  [onContentSizeChange]
1506
1655
  );
1507
- const refreshControl = (0, import_react16.useMemo)(() => onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RefreshControl, {}), [onRefresh, refreshing]);
1508
- const nativeGesture = (0, import_react16.useMemo)(() => import_react_native_gesture_handler4.Gesture.Native(), []);
1509
- const combinedGesture = (0, import_react16.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler4.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
1656
+ const refreshControl = (0, import_react19.useMemo)(() => onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RefreshControl, {}), [onRefresh, refreshing]);
1657
+ const nativeGesture = (0, import_react19.useMemo)(() => import_react_native_gesture_handler4.Gesture.Native(), []);
1658
+ const combinedGesture = (0, import_react19.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler4.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
1510
1659
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
1511
- const content = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native14.View, { style: containerStyle, children: [
1512
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1660
+ const content = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native14.View, { style: containerStyle, children: [
1661
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1513
1662
  AnimatedSectionList,
1514
1663
  {
1515
1664
  ...passThroughProps,
@@ -1539,26 +1688,52 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
1539
1688
  },
1540
1689
  headerHeight !== null && headerHeight > 0 ? 1 : 0
1541
1690
  ),
1542
- hiddenHeader && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native14.View, { pointerEvents: "none", style: styles8.measureContainer, children: hiddenHeader }),
1543
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScrollViewChip, { chipProps, onChipPress, onPress: handleScrollToTop, style: chipStyle }),
1544
- usesCustomSticky && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_reanimated15.default.View, { pointerEvents: "none", style: [styles8.stickyClip, clipStyle], children: activeIndex >= 0 && activeIndex < sections.length && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_reanimated15.default.View, { style: [styles8.stickyOverlay, overlayStyle], children: renderSectionHeader({ section: sections[activeIndex] }) }) })
1691
+ hiddenHeader && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native14.View, { pointerEvents: "none", style: styles8.measureContainer, children: hiddenHeader }),
1692
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ScrollViewChip, { chipProps, onChipPress, onPress: handleScrollToTop, style: chipStyle }),
1693
+ usesCustomSticky && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_reanimated16.default.View, { pointerEvents: "none", style: [styles8.stickyClip, clipStyle], children: activeIndex >= 0 && activeIndex < sections.length && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_reanimated16.default.View, { style: [styles8.stickyOverlay, overlayStyle], children: renderSectionHeader({ section: sections[activeIndex] }) }) })
1545
1694
  ] });
1546
- return detectorGesture ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_gesture_handler4.GestureDetector, { gesture: detectorGesture, children: content }) : content;
1695
+ return detectorGesture ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_gesture_handler4.GestureDetector, { gesture: detectorGesture, children: content }) : content;
1547
1696
  };
1548
1697
  var styles8 = {
1549
1698
  measureContainer: { left: 0, opacity: 0, position: "absolute", right: 0, top: -9999 },
1550
1699
  stickyClip: { bottom: 0, left: 0, overflow: "hidden", position: "absolute", right: 0, zIndex: 2 },
1551
1700
  stickyOverlay: { left: 0, position: "absolute", right: 0 }
1552
1701
  };
1553
- var SectionList = (0, import_react16.memo)(SectionListInner);
1702
+ var SectionList = (0, import_react19.memo)(SectionListInner);
1554
1703
 
1555
1704
  // src/useScrollView.ts
1556
- var import_react17 = require("react");
1705
+ var import_react20 = require("react");
1557
1706
  var useScrollView = () => {
1558
- const { progress, progressing, scrollHeight, scrollPosition, setProgress, setProgressing } = (0, import_react17.useContext)(ScrollViewContext);
1707
+ const { progress, progressing, scrollHeight, scrollPosition, setProgress, setProgressing } = (0, import_react20.useContext)(ScrollViewContext);
1559
1708
  return { progress, progressing, scrollHeight, scrollPosition, setProgress, setProgressing };
1560
1709
  };
1561
1710
 
1711
+ // src/useScrollViewChrome.ts
1712
+ var import_react21 = require("react");
1713
+ var import_react_native_reanimated17 = require("react-native-reanimated");
1714
+ var noop = () => {
1715
+ };
1716
+ var useScrollViewChromeStyle = () => {
1717
+ const chrome = (0, import_react21.useContext)(ScrollViewChromeContext);
1718
+ const fallback = (0, import_react_native_reanimated17.useSharedValue)(0);
1719
+ const offset = chrome?.offset ?? fallback;
1720
+ return (0, import_react_native_reanimated17.useAnimatedStyle)(() => ({ transform: [{ translateY: offset.value }] }), [offset]);
1721
+ };
1722
+ var useScrollViewChromePin = (active) => {
1723
+ const chrome = (0, import_react21.useContext)(ScrollViewChromeContext);
1724
+ const addPin = chrome?.addPin;
1725
+ const removePin = chrome?.removePin;
1726
+ (0, import_react21.useEffect)(() => {
1727
+ if (!active || !addPin || !removePin) return void 0;
1728
+ addPin();
1729
+ return removePin;
1730
+ }, [active, addPin, removePin]);
1731
+ };
1732
+ var useScrollViewChromeReveal = () => {
1733
+ const chrome = (0, import_react21.useContext)(ScrollViewChromeContext);
1734
+ return chrome?.reveal ?? noop;
1735
+ };
1736
+
1562
1737
  // src/useScrollViewSettings.ts
1563
1738
  var useScrollViewSettings = scrollViewUseSettings;
1564
1739
  // Annotate the CommonJS export names for ESM import in node:
@@ -1567,6 +1742,7 @@ var useScrollViewSettings = scrollViewUseSettings;
1567
1742
  FlatList,
1568
1743
  PullSearch,
1569
1744
  ScrollView,
1745
+ ScrollViewChromeProvider,
1570
1746
  ScrollViewContext,
1571
1747
  ScrollViewFooter,
1572
1748
  ScrollViewHeader,
@@ -1579,5 +1755,8 @@ var useScrollViewSettings = scrollViewUseSettings;
1579
1755
  scrollViewReducer,
1580
1756
  useKeyboardInset,
1581
1757
  useScrollView,
1758
+ useScrollViewChromePin,
1759
+ useScrollViewChromeReveal,
1760
+ useScrollViewChromeStyle,
1582
1761
  useScrollViewSettings
1583
1762
  });