@rific/scroll-view 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -428,7 +428,7 @@ var CHIP_SLIDE = 48;
428
428
  function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, hideUntilMeasured, isHorizontal, keyboardAware, pullSearchHeight, style } = {}) {
429
429
  const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
430
430
  const keyboardHeight = useKeyboardInset();
431
- const { footerAboveKeyboard, footerHeight, footerFixed: contextFooterFixed, headerHeight, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared, tabBarHeight } = (0, import_react5.useContext)(ScrollViewContext);
431
+ const { footerAboveKeyboard, footerHeight, footerFixed: contextFooterFixed, headerHeight, headerHeightShared, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared, tabBarHeight } = (0, import_react5.useContext)(ScrollViewContext);
432
432
  const headerFixed = isHorizontal ? true : headerFixedProp ?? contextHeaderFixed;
433
433
  const footerFixed = isHorizontal ? true : footerFixedProp ?? contextFooterFixed;
434
434
  (0, import_react5.useEffect)(() => {
@@ -452,11 +452,11 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
452
452
  return {
453
453
  opacity: chipHidden.value ? (0, import_react_native_reanimated5.withTiming)(0) : (0, import_react_native_reanimated5.withTiming)(1),
454
454
  pointerEvents,
455
- top: Math.max(headerHeight ?? 0, insets.top) + 4,
455
+ top: Math.max(headerHeightShared.value, insets.top) + 4,
456
456
  transform: [{ translateX: chipHidden.value ? (0, import_react_native_reanimated5.withTiming)(-CHIP_SLIDE) : (0, import_react_native_reanimated5.withTiming)(0) }]
457
457
  };
458
458
  }
459
- const h = headerHeight ?? 0;
459
+ const h = headerHeightShared.value;
460
460
  const slide = headerFixed ? 0 : snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
461
461
  const top = Math.max(h - slide, insets.top) + 4;
462
462
  return {
@@ -465,7 +465,7 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
465
465
  top,
466
466
  transform: [{ translateY: chipHidden.value ? (0, import_react_native_reanimated5.withTiming)(-CHIP_SLIDE) : (0, import_react_native_reanimated5.withTiming)(0) }]
467
467
  };
468
- }, [isHorizontal, headerFixed, headerHeight, insets.top, chipHidden, snapBackHeaderShared, headerOffset, scrollPosition, pullSearchHeightShared]);
468
+ }, [isHorizontal, headerFixed, headerHeightShared, insets.top, chipHidden, snapBackHeaderShared, headerOffset, scrollPosition, pullSearchHeightShared]);
469
469
  return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false };
470
470
  }
471
471
 
@@ -1105,17 +1105,19 @@ var import_react_native_safe_area_context4 = require("react-native-safe-area-con
1105
1105
  var import_jsx_runtime9 = require("react/jsx-runtime");
1106
1106
  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_paper3.Surface, { style: import_react_native12.StyleSheet.absoluteFill, elevation: 1, children: null }) });
1107
1107
  var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAccessibilityLabel, backActionFixed, caption, centerContent, children, iconSize, style, title, topInset = true, trailingAction, trailingActionFixed = true }) => {
1108
- const { blur, headerHeight, headerFixed, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = (0, import_react14.useContext)(ScrollViewContext);
1108
+ const { blur, headerHeight, headerFixed, headerHeightShared, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = (0, import_react14.useContext)(ScrollViewContext);
1109
1109
  const { settings } = (0, import_react14.useContext)(ScrollViewSettingsContext);
1110
1110
  const effectiveBackActionFixed = backActionFixed ?? settings.backActionFixed;
1111
1111
  const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
1112
1112
  const theme = (0, import_react_native_paper3.useTheme)();
1113
1113
  const top = topInset ? insets.top : 0;
1114
+ const measuredShared = (0, import_react_native_reanimated12.useSharedValue)(false);
1114
1115
  const handleLayout = ({
1115
1116
  nativeEvent: {
1116
1117
  layout: { height }
1117
1118
  }
1118
1119
  }) => {
1120
+ measuredShared.value = true;
1119
1121
  if (headerHeight !== height) setHeaderHeight(height);
1120
1122
  };
1121
1123
  const actionMargin = 4;
@@ -1124,23 +1126,23 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAc
1124
1126
  const translateStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
1125
1127
  if (headerFixed) return { transform: [{ translateY: 0 }] };
1126
1128
  if (snapBackHeaderShared.value) return { transform: [{ translateY: headerOffset.value }] };
1127
- const effective = scrollPosition.value + (headerHeight ?? 0) - pullSearchHeightShared.value;
1129
+ const effective = scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value;
1128
1130
  if (effective <= 0) return { transform: [{ translateY: 0 }] };
1129
1131
  return { transform: [{ translateY: -effective }] };
1130
- }, [headerFixed, headerHeight, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1132
+ }, [headerFixed, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1131
1133
  const blurStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
1132
- if (headerHeight === null) return { height: unmeasuredHeight };
1133
- if (!headerHeight) return { height: 0 };
1134
- if (headerFixed) return { height: headerHeight };
1135
- const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + headerHeight - pullSearchHeightShared.value);
1136
- return { height: Math.max(headerHeight + translateY, top) };
1137
- }, [headerFixed, headerHeight, top, unmeasuredHeight, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1134
+ if (!measuredShared.value) return { height: unmeasuredHeight };
1135
+ if (!headerHeightShared.value) return { height: 0 };
1136
+ if (headerFixed) return { height: headerHeightShared.value };
1137
+ const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value);
1138
+ return { height: Math.max(headerHeightShared.value + translateY, top) };
1139
+ }, [headerFixed, top, unmeasuredHeight, measuredShared, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1138
1140
  const progressStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
1139
- const h = headerHeight ?? 0;
1141
+ const h = headerHeightShared.value;
1140
1142
  if (headerFixed) return { top: h };
1141
1143
  const slide = snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
1142
1144
  return { top: Math.max(h - slide, top) };
1143
- }, [headerFixed, headerHeight, top, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1145
+ }, [headerFixed, headerHeightShared, top, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1144
1146
  const actionTop = top + actionMargin;
1145
1147
  const buttonStyle = { borderRadius: actionSize, height: actionSize, margin: 0, width: actionSize };
1146
1148
  const actionShadow = { borderRadius: actionSize / 2, elevation: 2, shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.15, shadowRadius: 2 };
package/dist/index.mjs CHANGED
@@ -376,7 +376,7 @@ var CHIP_SLIDE = 48;
376
376
  function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, hideUntilMeasured, isHorizontal, keyboardAware, pullSearchHeight, style } = {}) {
377
377
  const insets = useSafeAreaInsets();
378
378
  const keyboardHeight = useKeyboardInset();
379
- const { footerAboveKeyboard, footerHeight, footerFixed: contextFooterFixed, headerHeight, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared, tabBarHeight } = useContext3(ScrollViewContext);
379
+ const { footerAboveKeyboard, footerHeight, footerFixed: contextFooterFixed, headerHeight, headerHeightShared, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared, tabBarHeight } = useContext3(ScrollViewContext);
380
380
  const headerFixed = isHorizontal ? true : headerFixedProp ?? contextHeaderFixed;
381
381
  const footerFixed = isHorizontal ? true : footerFixedProp ?? contextFooterFixed;
382
382
  useEffect2(() => {
@@ -400,11 +400,11 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
400
400
  return {
401
401
  opacity: chipHidden.value ? withTiming2(0) : withTiming2(1),
402
402
  pointerEvents,
403
- top: Math.max(headerHeight ?? 0, insets.top) + 4,
403
+ top: Math.max(headerHeightShared.value, insets.top) + 4,
404
404
  transform: [{ translateX: chipHidden.value ? withTiming2(-CHIP_SLIDE) : withTiming2(0) }]
405
405
  };
406
406
  }
407
- const h = headerHeight ?? 0;
407
+ const h = headerHeightShared.value;
408
408
  const slide = headerFixed ? 0 : snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
409
409
  const top = Math.max(h - slide, insets.top) + 4;
410
410
  return {
@@ -413,7 +413,7 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
413
413
  top,
414
414
  transform: [{ translateY: chipHidden.value ? withTiming2(-CHIP_SLIDE) : withTiming2(0) }]
415
415
  };
416
- }, [isHorizontal, headerFixed, headerHeight, insets.top, chipHidden, snapBackHeaderShared, headerOffset, scrollPosition, pullSearchHeightShared]);
416
+ }, [isHorizontal, headerFixed, headerHeightShared, insets.top, chipHidden, snapBackHeaderShared, headerOffset, scrollPosition, pullSearchHeightShared]);
417
417
  return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false };
418
418
  }
419
419
 
@@ -1048,22 +1048,24 @@ import { BlurView as BlurView3 } from "@rific/auto-paper";
1048
1048
  import { useContext as useContext11 } from "react";
1049
1049
  import { StyleSheet as StyleSheet6, Text, View as View6 } from "react-native";
1050
1050
  import { Appbar, ProgressBar, Surface, useTheme as useTheme3 } from "react-native-paper";
1051
- import Animated6, { useAnimatedStyle as useAnimatedStyle5 } from "react-native-reanimated";
1051
+ import Animated6, { useAnimatedStyle as useAnimatedStyle5, useSharedValue as useSharedValue6 } from "react-native-reanimated";
1052
1052
  import { useSafeAreaInsets as useSafeAreaInsets4 } from "react-native-safe-area-context";
1053
1053
  import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1054
1054
  var ActionBg = ({ blur, style }) => blur ? /* @__PURE__ */ jsx9(View6, { style: [StyleSheet6.absoluteFill, styles7.actionBg, style], children: /* @__PURE__ */ jsx9(BlurView3, { blur: true, style: StyleSheet6.absoluteFill }) }) : /* @__PURE__ */ jsx9(View6, { style: [StyleSheet6.absoluteFill, styles7.actionBg, style], pointerEvents: "none", children: /* @__PURE__ */ jsx9(Surface, { style: StyleSheet6.absoluteFill, elevation: 1, children: null }) });
1055
1055
  var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAccessibilityLabel, backActionFixed, caption, centerContent, children, iconSize, style, title, topInset = true, trailingAction, trailingActionFixed = true }) => {
1056
- const { blur, headerHeight, headerFixed, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = useContext11(ScrollViewContext);
1056
+ const { blur, headerHeight, headerFixed, headerHeightShared, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = useContext11(ScrollViewContext);
1057
1057
  const { settings } = useContext11(ScrollViewSettingsContext);
1058
1058
  const effectiveBackActionFixed = backActionFixed ?? settings.backActionFixed;
1059
1059
  const insets = useSafeAreaInsets4();
1060
1060
  const theme = useTheme3();
1061
1061
  const top = topInset ? insets.top : 0;
1062
+ const measuredShared = useSharedValue6(false);
1062
1063
  const handleLayout = ({
1063
1064
  nativeEvent: {
1064
1065
  layout: { height }
1065
1066
  }
1066
1067
  }) => {
1068
+ measuredShared.value = true;
1067
1069
  if (headerHeight !== height) setHeaderHeight(height);
1068
1070
  };
1069
1071
  const actionMargin = 4;
@@ -1072,23 +1074,23 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAc
1072
1074
  const translateStyle = useAnimatedStyle5(() => {
1073
1075
  if (headerFixed) return { transform: [{ translateY: 0 }] };
1074
1076
  if (snapBackHeaderShared.value) return { transform: [{ translateY: headerOffset.value }] };
1075
- const effective = scrollPosition.value + (headerHeight ?? 0) - pullSearchHeightShared.value;
1077
+ const effective = scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value;
1076
1078
  if (effective <= 0) return { transform: [{ translateY: 0 }] };
1077
1079
  return { transform: [{ translateY: -effective }] };
1078
- }, [headerFixed, headerHeight, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1080
+ }, [headerFixed, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1079
1081
  const blurStyle = useAnimatedStyle5(() => {
1080
- if (headerHeight === null) return { height: unmeasuredHeight };
1081
- if (!headerHeight) return { height: 0 };
1082
- if (headerFixed) return { height: headerHeight };
1083
- const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + headerHeight - pullSearchHeightShared.value);
1084
- return { height: Math.max(headerHeight + translateY, top) };
1085
- }, [headerFixed, headerHeight, top, unmeasuredHeight, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1082
+ if (!measuredShared.value) return { height: unmeasuredHeight };
1083
+ if (!headerHeightShared.value) return { height: 0 };
1084
+ if (headerFixed) return { height: headerHeightShared.value };
1085
+ const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value);
1086
+ return { height: Math.max(headerHeightShared.value + translateY, top) };
1087
+ }, [headerFixed, top, unmeasuredHeight, measuredShared, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1086
1088
  const progressStyle = useAnimatedStyle5(() => {
1087
- const h = headerHeight ?? 0;
1089
+ const h = headerHeightShared.value;
1088
1090
  if (headerFixed) return { top: h };
1089
1091
  const slide = snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
1090
1092
  return { top: Math.max(h - slide, top) };
1091
- }, [headerFixed, headerHeight, top, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1093
+ }, [headerFixed, headerHeightShared, top, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
1092
1094
  const actionTop = top + actionMargin;
1093
1095
  const buttonStyle = { borderRadius: actionSize, height: actionSize, margin: 0, width: actionSize };
1094
1096
  const actionShadow = { borderRadius: actionSize / 2, elevation: 2, shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.15, shadowRadius: 2 };
@@ -1144,7 +1146,7 @@ var styles7 = StyleSheet6.create({
1144
1146
  import { useBlur } from "@rific/auto-paper";
1145
1147
  import { useCallback as useCallback7, useContext as useContext12, useEffect as useEffect5, useMemo as useMemo5, useRef as useRef7, useState as useState5 } from "react";
1146
1148
  import { Dimensions as Dimensions3 } from "react-native";
1147
- import { useSharedValue as useSharedValue6 } from "react-native-reanimated";
1149
+ import { useSharedValue as useSharedValue7 } from "react-native-reanimated";
1148
1150
  import { jsx as jsx10 } from "react/jsx-runtime";
1149
1151
  var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard = false, footerFixed, headerFixed, snapBack, snapBackFooter, snapBackHeader, tabBarHeight = 0 }) => {
1150
1152
  const { settings } = useContext12(ScrollViewSettingsContext);
@@ -1156,16 +1158,16 @@ var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard =
1156
1158
  const [footerHeight, setFooterHeightState] = useState5(0);
1157
1159
  const [progress, setProgress] = useState5(null);
1158
1160
  const [progressing, setProgressing] = useState5(false);
1159
- const scrollPosition = useSharedValue6(0);
1160
- const listGeneration = useSharedValue6(0);
1161
+ const scrollPosition = useSharedValue7(0);
1162
+ const listGeneration = useSharedValue7(0);
1161
1163
  const jsListGeneration = useRef7(0);
1162
- const headerHeightShared = useSharedValue6(0);
1163
- const footerHeightShared = useSharedValue6(0);
1164
- const headerOffset = useSharedValue6(0);
1165
- const footerOffset = useSharedValue6(0);
1166
- const pullSearchHeightShared = useSharedValue6(0);
1167
- const snapBackHeaderShared = useSharedValue6(false);
1168
- const snapBackFooterShared = useSharedValue6(false);
1164
+ const headerHeightShared = useSharedValue7(0);
1165
+ const footerHeightShared = useSharedValue7(0);
1166
+ const headerOffset = useSharedValue7(0);
1167
+ const footerOffset = useSharedValue7(0);
1168
+ const pullSearchHeightShared = useSharedValue7(0);
1169
+ const snapBackHeaderShared = useSharedValue7(false);
1170
+ const snapBackFooterShared = useSharedValue7(false);
1169
1171
  useEffect5(() => {
1170
1172
  snapBackHeaderShared.value = effectiveSnapBackHeader ?? effectiveSnapBack;
1171
1173
  snapBackFooterShared.value = effectiveSnapBackFooter ?? effectiveSnapBack;
@@ -1231,19 +1233,19 @@ var ScrollViewSettingsProvider = ({ children, onChange, initialValue }) => {
1231
1233
  import { memo as memo4, useCallback as useCallback10, useContext as useContext14, useEffect as useEffect6, useLayoutEffect as useLayoutEffect6, useMemo as useMemo7, useRef as useRef9 } from "react";
1232
1234
  import { Dimensions as Dimensions4, Platform as Platform5, SectionList as RNSectionList, View as View7 } from "react-native";
1233
1235
  import { Gesture as Gesture4, GestureDetector as GestureDetector4 } from "react-native-gesture-handler";
1234
- import Animated7, { runOnUI as runOnUI3, useSharedValue as useSharedValue8 } from "react-native-reanimated";
1236
+ import Animated7, { runOnUI as runOnUI3, useSharedValue as useSharedValue9 } from "react-native-reanimated";
1235
1237
 
1236
1238
  // src/internal/useStickyHeaders.ts
1237
1239
  import { useCallback as useCallback9, useContext as useContext13, useLayoutEffect as useLayoutEffect5, useRef as useRef8, useState as useState7 } from "react";
1238
- import { runOnJS as runOnJS4, useAnimatedReaction as useAnimatedReaction2, useAnimatedStyle as useAnimatedStyle6, useSharedValue as useSharedValue7 } from "react-native-reanimated";
1240
+ import { runOnJS as runOnJS4, useAnimatedReaction as useAnimatedReaction2, useAnimatedStyle as useAnimatedStyle6, useSharedValue as useSharedValue8 } from "react-native-reanimated";
1239
1241
  import { useSafeAreaInsets as useSafeAreaInsets5 } from "react-native-safe-area-context";
1240
1242
  function useStickyHeaders(headerFixed, enabled) {
1241
1243
  const { headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = useContext13(ScrollViewContext);
1242
1244
  const insetsTop = useSafeAreaInsets5().top;
1243
- const sectionYPositions = useSharedValue7([]);
1244
- const sectionHeights = useSharedValue7([]);
1245
+ const sectionYPositions = useSharedValue8([]);
1246
+ const sectionHeights = useSharedValue8([]);
1245
1247
  const [activeIndex, setActiveIndex] = useState7(-1);
1246
- const contentIndexShared = useSharedValue7(-1);
1248
+ const contentIndexShared = useSharedValue8(-1);
1247
1249
  useLayoutEffect5(() => {
1248
1250
  contentIndexShared.value = activeIndex;
1249
1251
  }, [activeIndex, contentIndexShared]);
@@ -1355,7 +1357,7 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
1355
1357
  const { stickySectionHeadersEnabled = true, renderSectionHeader, ...passThroughProps } = props;
1356
1358
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, hideUntilMeasured: true, keyboardAware, pullSearchHeight, style });
1357
1359
  const { listGeneration, onListUnmount } = useContext14(ScrollViewContext);
1358
- const capturedGeneration = useSharedValue8(-1);
1360
+ const capturedGeneration = useSharedValue9(-1);
1359
1361
  useLayoutEffect6(() => {
1360
1362
  const generation = listGeneration.value;
1361
1363
  if (Platform5.OS === "web") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/scroll-view",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Blur styled scroll system for React Native. Floating headers/footers, keyboard awareness, and scroll-away chrome",
5
5
  "keywords": [
6
6
  "react-native",
@@ -27,7 +27,7 @@
27
27
  "type": "commonjs",
28
28
  "exports": {
29
29
  ".": {
30
- "react-native": "./dist/index.mjs",
30
+ "react-native": "./src/index.ts",
31
31
  "types": "./dist/index.d.ts",
32
32
  "import": "./dist/index.mjs",
33
33
  "require": "./dist/index.js"
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "main": "dist/index.js",
37
37
  "module": "dist/index.mjs",
38
- "react-native": "dist/index.mjs",
38
+ "react-native": "src/index.ts",
39
39
  "types": "dist/index.d.ts",
40
40
  "files": [
41
41
  "dist",
@@ -79,12 +79,13 @@
79
79
  "prettier": "^3.8.3",
80
80
  "react": "^19.0.0",
81
81
  "react-dom": "^19.0.0",
82
- "react-native": "^0.76.0",
83
- "react-native-gesture-handler": "*",
82
+ "react-native": "^0.83.0",
83
+ "react-native-gesture-handler": "^2.32.0",
84
84
  "react-native-keyboard-controller": "*",
85
85
  "react-native-paper": "^5.12.0",
86
- "react-native-reanimated": "*",
86
+ "react-native-reanimated": "^4.5.1",
87
87
  "react-native-safe-area-context": "*",
88
+ "react-native-worklets": "^0.10.3",
88
89
  "ts-jest": "^29.4.9",
89
90
  "ts-node": "^10.9.2",
90
91
  "tsup": "^8.0.0",
@@ -93,13 +94,14 @@
93
94
  },
94
95
  "peerDependencies": {
95
96
  "@rific/auto-paper": "^0.8.0",
96
- "react": ">=18.0.0",
97
- "react-native": ">=0.76.0",
98
- "react-native-gesture-handler": ">=2.0.0",
97
+ "react": ">=19.0.0",
98
+ "react-native": ">=0.83.0",
99
+ "react-native-gesture-handler": ">=2.0.0 <3.0.0",
99
100
  "react-native-keyboard-controller": ">=1.0.0",
100
101
  "react-native-paper": ">=5.0.0",
101
- "react-native-reanimated": ">=3.0.0",
102
- "react-native-safe-area-context": ">=5.0.0"
102
+ "react-native-reanimated": ">=4.0.0",
103
+ "react-native-safe-area-context": ">=5.0.0",
104
+ "react-native-worklets": "0.10.x"
103
105
  },
104
106
  "publishConfig": {
105
107
  "access": "public"
@@ -2,7 +2,7 @@ import { BlurView } from '@rific/auto-paper'
2
2
  import { type ReactNode, useContext } from 'react'
3
3
  import { type LayoutChangeEvent, StyleSheet, Text, View, type ViewStyle } from 'react-native'
4
4
  import { Appbar, ProgressBar, Surface, useTheme } from 'react-native-paper'
5
- import Animated, { useAnimatedStyle } from 'react-native-reanimated'
5
+ import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated'
6
6
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
7
7
 
8
8
  import { ScrollViewContext } from './ScrollViewContext'
@@ -41,17 +41,24 @@ const ActionBg = ({ blur, style }: ActionBgProps) =>
41
41
  )
42
42
 
43
43
  export const ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAccessibilityLabel, backActionFixed, caption, centerContent, children, iconSize, style, title, topInset = true, trailingAction, trailingActionFixed = true }: ScrollViewHeaderProps) => {
44
- const { blur, headerHeight, headerFixed, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = useContext(ScrollViewContext)
44
+ const { blur, headerHeight, headerFixed, headerHeightShared, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = useContext(ScrollViewContext)
45
45
  const { settings } = useContext(ScrollViewSettingsContext)
46
46
  const effectiveBackActionFixed = backActionFixed ?? settings.backActionFixed
47
47
  const insets = useSafeAreaInsets()
48
48
  const theme = useTheme()
49
49
  const top = topInset ? insets.top : 0
50
+ // Tracks "has this header's own onLayout fired yet" as a SharedValue rather than deriving it from
51
+ // headerHeight (a plain, React-state-backed number) so the worklets below never have to list a
52
+ // value that flips from null to a real measured number in their dependency arrays — see the
53
+ // comment on translateStyle/blurStyle/progressStyle for why that recreation was a real crash, not
54
+ // just a perf concern.
55
+ const measuredShared = useSharedValue(false)
50
56
  const handleLayout = ({
51
57
  nativeEvent: {
52
58
  layout: { height }
53
59
  }
54
60
  }: LayoutChangeEvent) => {
61
+ measuredShared.value = true
55
62
  if (headerHeight !== height) setHeaderHeight(height)
56
63
  }
57
64
  const actionMargin = 4
@@ -61,33 +68,39 @@ export const ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, bac
61
68
  // pattern) — used as the blur backdrop's own height for that same pre-measurement window, so it
62
69
  // doesn't render as a flat, unblurred box for the ~60-80ms an onLayout round trip takes on web.
63
70
  const unmeasuredHeight = top + contentMinHeight
64
- // Every one of these worklets reads several SharedValues (scrollPosition, snapBackHeaderShared,
65
- // headerOffset, pullSearchHeightShared) whose *mutations* — not just headerFixed/headerHeight
66
- // changing — need to re-trigger the style recompute. Reanimated's automatic worklet dependency
67
- // capture covers this on native without needing them listed, but not on web, where a SharedValue
68
- // has to appear in the array itself (object identity, not .value) for its mutations to be
69
- // treated as reactive — omitting them here silently froze the header/blur/progress-bar's scroll
70
- // tracking on web at whatever scrollPosition happened to be when headerHeight last changed.
71
+ // Every one of these worklets reads SharedValues only (scrollPosition, snapBackHeaderShared,
72
+ // headerOffset, pullSearchHeightShared, headerHeightShared, measuredShared) — deliberately NOT the
73
+ // plain `headerHeight` context value, even though it holds the same number. `headerHeight` is a
74
+ // React-state-backed `number | null` that changes identity on every measurement (starting at null,
75
+ // then flipping to a real pixel value within a frame of mount); putting it in a useAnimatedStyle
76
+ // dependency array forces Reanimated to tear down and recreate the worklet the moment that happens
77
+ // — which crashed outright ("[Worklets] Tried to synchronously call a Remote Function") on
78
+ // react-native-worklets 0.10.1. SharedValues never change identity, only their `.value`, so they're
79
+ // safe to depend on and don't retrigger this. They still have to be LISTED here despite that: on
80
+ // web, without the Reanimated Babel plugin's automatic worklet dependency capture, a SharedValue's
81
+ // mutations aren't picked up as reactive unless its reference appears in this array — omitting one
82
+ // silently freezes that value's contribution to the header/blur/progress-bar's scroll tracking at
83
+ // whatever it was when the array last changed.
71
84
  const translateStyle = useAnimatedStyle(() => {
72
85
  if (headerFixed) return { transform: [{ translateY: 0 }] }
73
86
  if (snapBackHeaderShared.value) return { transform: [{ translateY: headerOffset.value }] }
74
- const effective = scrollPosition.value + (headerHeight ?? 0) - pullSearchHeightShared.value
87
+ const effective = scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value
75
88
  if (effective <= 0) return { transform: [{ translateY: 0 }] }
76
89
  return { transform: [{ translateY: -effective }] }
77
- }, [headerFixed, headerHeight, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared])
90
+ }, [headerFixed, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared])
78
91
  const blurStyle = useAnimatedStyle(() => {
79
- if (headerHeight === null) return { height: unmeasuredHeight }
80
- if (!headerHeight) return { height: 0 }
81
- if (headerFixed) return { height: headerHeight }
82
- const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + headerHeight - pullSearchHeightShared.value)
83
- return { height: Math.max(headerHeight + translateY, top) }
84
- }, [headerFixed, headerHeight, top, unmeasuredHeight, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared])
92
+ if (!measuredShared.value) return { height: unmeasuredHeight }
93
+ if (!headerHeightShared.value) return { height: 0 }
94
+ if (headerFixed) return { height: headerHeightShared.value }
95
+ const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value)
96
+ return { height: Math.max(headerHeightShared.value + translateY, top) }
97
+ }, [headerFixed, top, unmeasuredHeight, measuredShared, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared])
85
98
  const progressStyle = useAnimatedStyle(() => {
86
- const h = headerHeight ?? 0
99
+ const h = headerHeightShared.value
87
100
  if (headerFixed) return { top: h }
88
101
  const slide = snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value)
89
102
  return { top: Math.max(h - slide, top) }
90
- }, [headerFixed, headerHeight, top, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared])
103
+ }, [headerFixed, headerHeightShared, top, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared])
91
104
  const actionTop = top + actionMargin
92
105
  const buttonStyle = { borderRadius: actionSize, height: actionSize, margin: 0, width: actionSize }
93
106
  const actionShadow = { borderRadius: actionSize / 2, elevation: 2, shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.15, shadowRadius: 2 }
@@ -25,7 +25,7 @@ export type UseScrollListOptions = {
25
25
  export function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, hideUntilMeasured, isHorizontal, keyboardAware, pullSearchHeight, style }: UseScrollListOptions = {}) {
26
26
  const insets = useSafeAreaInsets()
27
27
  const keyboardHeight = useKeyboardInset()
28
- const { footerAboveKeyboard, footerHeight, footerFixed: contextFooterFixed, headerHeight, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared, tabBarHeight } = useContext(ScrollViewContext)
28
+ const { footerAboveKeyboard, footerHeight, footerFixed: contextFooterFixed, headerHeight, headerHeightShared, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared, tabBarHeight } = useContext(ScrollViewContext)
29
29
 
30
30
  const headerFixed = isHorizontal ? true : (headerFixedProp ?? contextHeaderFixed)
31
31
  const footerFixed = isHorizontal ? true : (footerFixedProp ?? contextFooterFixed)
@@ -79,21 +79,25 @@ export function useScrollList({ footerFixed: footerFixedProp, headerFixed: heade
79
79
  const contentOffset = usesContentInset ? insetContentOffset : ZERO_OFFSET
80
80
 
81
81
  const chipHidden = useSharedValue(1)
82
- // chipHidden/snapBackHeaderShared/headerOffset/scrollPosition/pullSearchHeightShared all need to
83
- // be listed here too, not just read — see ScrollViewHeader's translateStyle for why (web-only
84
- // reanimated reactivity gap: a SharedValue's mutations don't retrigger useAnimatedStyle unless
85
- // the value itself is in this array).
82
+ // chipHidden/snapBackHeaderShared/headerOffset/scrollPosition/pullSearchHeightShared/
83
+ // headerHeightShared all need to be listed here too, not just read — see ScrollViewHeader's
84
+ // translateStyle for why (web-only reanimated reactivity gap: a SharedValue's mutations don't
85
+ // retrigger useAnimatedStyle unless the value itself is in this array). headerHeightShared is
86
+ // used here rather than the plain `headerHeight` context value specifically because the latter
87
+ // changes identity (null -> a real number) shortly after mount, which forces Reanimated to
88
+ // recreate this worklet — a crash on react-native-worklets 0.10.1, not just a perf concern. See
89
+ // ScrollViewHeader's translateStyle/blurStyle/progressStyle for the full explanation.
86
90
  const chipStyle = useAnimatedStyle(() => {
87
91
  const pointerEvents = chipHidden.value ? ('none' as const) : ('box-none' as const)
88
92
  if (isHorizontal) {
89
93
  return {
90
94
  opacity: chipHidden.value ? withTiming(0) : withTiming(1),
91
95
  pointerEvents,
92
- top: Math.max(headerHeight ?? 0, insets.top) + 4,
96
+ top: Math.max(headerHeightShared.value, insets.top) + 4,
93
97
  transform: [{ translateX: chipHidden.value ? withTiming(-CHIP_SLIDE) : withTiming(0) }]
94
98
  }
95
99
  }
96
- const h = headerHeight ?? 0
100
+ const h = headerHeightShared.value
97
101
  const slide = headerFixed ? 0 : snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value)
98
102
  const top = Math.max(h - slide, insets.top) + 4
99
103
  return {
@@ -102,7 +106,7 @@ export function useScrollList({ footerFixed: footerFixedProp, headerFixed: heade
102
106
  top,
103
107
  transform: [{ translateY: chipHidden.value ? withTiming(-CHIP_SLIDE) : withTiming(0) }]
104
108
  }
105
- }, [isHorizontal, headerFixed, headerHeight, insets.top, chipHidden, snapBackHeaderShared, headerOffset, scrollPosition, pullSearchHeightShared])
109
+ }, [isHorizontal, headerFixed, headerHeightShared, insets.top, chipHidden, snapBackHeaderShared, headerOffset, scrollPosition, pullSearchHeightShared])
106
110
 
107
111
  return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false }
108
112
  }