@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 +17 -15
- package/dist/index.mjs +34 -32
- package/package.json +13 -11
- package/src/ScrollViewHeader.tsx +32 -19
- package/src/internal/useScrollList.ts +12 -8
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(
|
|
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 =
|
|
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,
|
|
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 +
|
|
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,
|
|
1132
|
+
}, [headerFixed, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
|
|
1131
1133
|
const blurStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
|
|
1132
|
-
if (
|
|
1133
|
-
if (!
|
|
1134
|
-
if (headerFixed) return { height:
|
|
1135
|
-
const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value +
|
|
1136
|
-
return { height: Math.max(
|
|
1137
|
-
}, [headerFixed,
|
|
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 =
|
|
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,
|
|
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(
|
|
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 =
|
|
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,
|
|
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 +
|
|
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,
|
|
1080
|
+
}, [headerFixed, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared]);
|
|
1079
1081
|
const blurStyle = useAnimatedStyle5(() => {
|
|
1080
|
-
if (
|
|
1081
|
-
if (!
|
|
1082
|
-
if (headerFixed) return { height:
|
|
1083
|
-
const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value +
|
|
1084
|
-
return { height: Math.max(
|
|
1085
|
-
}, [headerFixed,
|
|
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 =
|
|
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,
|
|
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
|
|
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 =
|
|
1160
|
-
const listGeneration =
|
|
1161
|
+
const scrollPosition = useSharedValue7(0);
|
|
1162
|
+
const listGeneration = useSharedValue7(0);
|
|
1161
1163
|
const jsListGeneration = useRef7(0);
|
|
1162
|
-
const headerHeightShared =
|
|
1163
|
-
const footerHeightShared =
|
|
1164
|
-
const headerOffset =
|
|
1165
|
-
const footerOffset =
|
|
1166
|
-
const pullSearchHeightShared =
|
|
1167
|
-
const snapBackHeaderShared =
|
|
1168
|
-
const snapBackFooterShared =
|
|
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
|
|
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
|
|
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 =
|
|
1244
|
-
const sectionHeights =
|
|
1245
|
+
const sectionYPositions = useSharedValue8([]);
|
|
1246
|
+
const sectionHeights = useSharedValue8([]);
|
|
1245
1247
|
const [activeIndex, setActiveIndex] = useState7(-1);
|
|
1246
|
-
const contentIndexShared =
|
|
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 =
|
|
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.
|
|
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": "./
|
|
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": "
|
|
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.
|
|
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": ">=
|
|
97
|
-
"react-native": ">=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": ">=
|
|
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"
|
package/src/ScrollViewHeader.tsx
CHANGED
|
@@ -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
|
|
65
|
-
// headerOffset, pullSearchHeightShared
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
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 +
|
|
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,
|
|
90
|
+
}, [headerFixed, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared])
|
|
78
91
|
const blurStyle = useAnimatedStyle(() => {
|
|
79
|
-
if (
|
|
80
|
-
if (!
|
|
81
|
-
if (headerFixed) return { height:
|
|
82
|
-
const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value +
|
|
83
|
-
return { height: Math.max(
|
|
84
|
-
}, [headerFixed,
|
|
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 =
|
|
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,
|
|
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
|
|
83
|
-
// be listed here too, not just read — see ScrollViewHeader's
|
|
84
|
-
// reanimated reactivity gap: a SharedValue's mutations don't
|
|
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(
|
|
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 =
|
|
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,
|
|
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
|
}
|