@rific/scroll-view 0.6.1 → 0.6.3
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.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +35 -25
- package/dist/index.mjs +33 -23
- package/package.json +3 -3
- package/src/ScrollViewContext.ts +3 -3
- package/src/ScrollViewFooter.tsx +10 -6
- package/src/ScrollViewHeader.tsx +10 -3
- package/src/ScrollViewProvider.tsx +12 -5
- package/src/internal/ScrollViewChip.tsx +21 -3
package/dist/index.d.mts
CHANGED
|
@@ -6,7 +6,7 @@ import { ChipProps as ChipProps$1 } from '@rific/auto-paper';
|
|
|
6
6
|
import * as react_native_reanimated from 'react-native-reanimated';
|
|
7
7
|
import { SharedValue } from 'react-native-reanimated';
|
|
8
8
|
|
|
9
|
-
type ChipProps = Omit<ChipProps$1, 'compact' | 'icon' | 'onPress'> & {
|
|
9
|
+
type ChipProps = Omit<ChipProps$1, 'compact' | 'icon' | 'onPress' | 'children'> & {
|
|
10
10
|
label?: ReactNode;
|
|
11
11
|
};
|
|
12
12
|
|
|
@@ -122,7 +122,7 @@ declare const ScrollView: typeof ScrollViewInner;
|
|
|
122
122
|
type ScrollViewContextType = {
|
|
123
123
|
blur: boolean;
|
|
124
124
|
footerAboveKeyboard: boolean;
|
|
125
|
-
footerHeight: number;
|
|
125
|
+
footerHeight: number | null;
|
|
126
126
|
footerHeightShared: SharedValue<number>;
|
|
127
127
|
footerFixed: boolean;
|
|
128
128
|
footerOffset: SharedValue<number>;
|
|
@@ -141,7 +141,7 @@ type ScrollViewContextType = {
|
|
|
141
141
|
progressing: boolean;
|
|
142
142
|
scrollHeight: number;
|
|
143
143
|
scrollPosition: SharedValue<number>;
|
|
144
|
-
setFooterHeight: (h: number) => void;
|
|
144
|
+
setFooterHeight: (h: number | null) => void;
|
|
145
145
|
setHeaderHeight: (h: number | null) => void;
|
|
146
146
|
setProgress: (p: number | null) => void;
|
|
147
147
|
setProgressing: (b: boolean) => void;
|
|
@@ -158,7 +158,7 @@ declare const ScrollViewFooter: ({ children, style }: ScrollViewFooterProps) =>
|
|
|
158
158
|
type ScrollViewHeaderProps = {
|
|
159
159
|
actionSize?: number;
|
|
160
160
|
actionStyle?: ViewStyle;
|
|
161
|
-
backAction?: () => void;
|
|
161
|
+
backAction?: (() => void) | ReactNode;
|
|
162
162
|
backActionAccessibilityLabel?: string;
|
|
163
163
|
backActionFixed?: boolean;
|
|
164
164
|
caption?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ChipProps as ChipProps$1 } from '@rific/auto-paper';
|
|
|
6
6
|
import * as react_native_reanimated from 'react-native-reanimated';
|
|
7
7
|
import { SharedValue } from 'react-native-reanimated';
|
|
8
8
|
|
|
9
|
-
type ChipProps = Omit<ChipProps$1, 'compact' | 'icon' | 'onPress'> & {
|
|
9
|
+
type ChipProps = Omit<ChipProps$1, 'compact' | 'icon' | 'onPress' | 'children'> & {
|
|
10
10
|
label?: ReactNode;
|
|
11
11
|
};
|
|
12
12
|
|
|
@@ -122,7 +122,7 @@ declare const ScrollView: typeof ScrollViewInner;
|
|
|
122
122
|
type ScrollViewContextType = {
|
|
123
123
|
blur: boolean;
|
|
124
124
|
footerAboveKeyboard: boolean;
|
|
125
|
-
footerHeight: number;
|
|
125
|
+
footerHeight: number | null;
|
|
126
126
|
footerHeightShared: SharedValue<number>;
|
|
127
127
|
footerFixed: boolean;
|
|
128
128
|
footerOffset: SharedValue<number>;
|
|
@@ -141,7 +141,7 @@ type ScrollViewContextType = {
|
|
|
141
141
|
progressing: boolean;
|
|
142
142
|
scrollHeight: number;
|
|
143
143
|
scrollPosition: SharedValue<number>;
|
|
144
|
-
setFooterHeight: (h: number) => void;
|
|
144
|
+
setFooterHeight: (h: number | null) => void;
|
|
145
145
|
setHeaderHeight: (h: number | null) => void;
|
|
146
146
|
setProgress: (p: number | null) => void;
|
|
147
147
|
setProgressing: (b: boolean) => void;
|
|
@@ -158,7 +158,7 @@ declare const ScrollViewFooter: ({ children, style }: ScrollViewFooterProps) =>
|
|
|
158
158
|
type ScrollViewHeaderProps = {
|
|
159
159
|
actionSize?: number;
|
|
160
160
|
actionStyle?: ViewStyle;
|
|
161
|
-
backAction?: () => void;
|
|
161
|
+
backAction?: (() => void) | ReactNode;
|
|
162
162
|
backActionAccessibilityLabel?: string;
|
|
163
163
|
backActionFixed?: boolean;
|
|
164
164
|
caption?: string;
|
package/dist/index.js
CHANGED
|
@@ -64,15 +64,18 @@ var RefreshControl = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(impo
|
|
|
64
64
|
// src/internal/ScrollViewChip.tsx
|
|
65
65
|
var import_auto_paper = require("@rific/auto-paper");
|
|
66
66
|
var import_react_native2 = require("react-native");
|
|
67
|
+
var import_react_native_paper = require("react-native-paper");
|
|
67
68
|
var import_react_native_reanimated = __toESM(require("react-native-reanimated"));
|
|
68
69
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
69
70
|
var ScrollViewChip = ({ chipProps, isHorizontal, onChipPress, onPress, style }) => {
|
|
70
|
-
const {
|
|
71
|
+
const { colors } = (0, import_react_native_paper.useTheme)();
|
|
72
|
+
const { label, selectedColor, style: chipStyle, ...restChipProps } = chipProps ?? {};
|
|
71
73
|
const handlePress = onChipPress ? () => {
|
|
72
74
|
onPress();
|
|
73
75
|
onChipPress();
|
|
74
76
|
} : onPress;
|
|
75
|
-
|
|
77
|
+
const iconColor = selectedColor ?? (restChipProps.mode === "outlined" ? colors.onSurfaceVariant : colors.onSecondaryContainer);
|
|
78
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_reanimated.default.View, { style: [styles.chip, style], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_auto_paper.Chip, { compact: true, selectedColor, ...restChipProps, avatar: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper.Icon, { source: isHorizontal ? "chevron-left" : "chevron-up", size: 18, color: iconColor }), onPress: handlePress, style: [styles.chipInner, chipStyle], children: label ?? (isHorizontal ? "Start" : "Top") }) });
|
|
76
79
|
};
|
|
77
80
|
var styles = import_react_native2.StyleSheet.create({
|
|
78
81
|
chip: { alignItems: "center", left: 0, position: "absolute", right: 0, zIndex: 3 },
|
|
@@ -88,7 +91,7 @@ var import_react = require("react");
|
|
|
88
91
|
var ScrollViewContext = (0, import_react.createContext)({
|
|
89
92
|
blur: true,
|
|
90
93
|
footerAboveKeyboard: false,
|
|
91
|
-
footerHeight:
|
|
94
|
+
footerHeight: null,
|
|
92
95
|
footerHeightShared: null,
|
|
93
96
|
footerFixed: false,
|
|
94
97
|
footerOffset: null,
|
|
@@ -556,7 +559,7 @@ var import_react_native_gesture_handler2 = require("react-native-gesture-handler
|
|
|
556
559
|
var import_auto_paper2 = require("@rific/auto-paper");
|
|
557
560
|
var import_react7 = require("react");
|
|
558
561
|
var import_react_native7 = require("react-native");
|
|
559
|
-
var
|
|
562
|
+
var import_react_native_paper2 = require("react-native-paper");
|
|
560
563
|
var import_react_native_reanimated7 = __toESM(require("react-native-reanimated"));
|
|
561
564
|
var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
|
|
562
565
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -586,7 +589,7 @@ var HorizontalDots = ({ total }) => {
|
|
|
586
589
|
const { blur, footerHeightShared, scrollPosition } = (0, import_react7.useContext)(ScrollViewContext);
|
|
587
590
|
const { width } = (0, import_react_native7.useWindowDimensions)();
|
|
588
591
|
const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
|
|
589
|
-
const theme = (0,
|
|
592
|
+
const theme = (0, import_react_native_paper2.useTheme)();
|
|
590
593
|
const insetsBottom = insets.bottom;
|
|
591
594
|
const activeColor = theme.colors.primary;
|
|
592
595
|
const inactiveColor = theme.colors.outlineVariant;
|
|
@@ -880,12 +883,12 @@ var FlatList = (0, import_react9.memo)(FlatListInner);
|
|
|
880
883
|
// src/PullSearch.tsx
|
|
881
884
|
var import_react10 = require("react");
|
|
882
885
|
var import_react_native9 = require("react-native");
|
|
883
|
-
var
|
|
886
|
+
var import_react_native_paper3 = require("react-native-paper");
|
|
884
887
|
var import_react_native_reanimated9 = __toESM(require("react-native-reanimated"));
|
|
885
888
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
886
889
|
var PullSearch = (0, import_react10.forwardRef)(function PullSearch2({ debounce, onChangeText, onHeightChange, placeholder, value: propValue }, ref) {
|
|
887
890
|
const { headerHeight, scrollPosition } = (0, import_react10.useContext)(ScrollViewContext);
|
|
888
|
-
const theme = (0,
|
|
891
|
+
const theme = (0, import_react_native_paper3.useTheme)();
|
|
889
892
|
const inputRef = (0, import_react10.useRef)(null);
|
|
890
893
|
const [barHeight, setBarHeight] = (0, import_react10.useState)(0);
|
|
891
894
|
const [value, setValue] = (0, import_react10.useState)(propValue ?? "");
|
|
@@ -943,7 +946,7 @@ var PullSearch = (0, import_react10.forwardRef)(function PullSearch2({ debounce,
|
|
|
943
946
|
const hideY = -h + barHeight;
|
|
944
947
|
return { opacity: (0, import_react_native_reanimated9.interpolate)(scrollPosition.value, [showY, hideY], [1, 0], import_react_native_reanimated9.Extrapolation.CLAMP) };
|
|
945
948
|
}, [barHeight, headerHeight, scrollPosition]);
|
|
946
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_reanimated9.default.View, { onLayout: handleLayout, style: [styles5.container, animatedStyle], children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_reanimated9.default.View, { onLayout: handleLayout, style: [styles5.container, animatedStyle], children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper3.Searchbar, { autoCorrect: false, onChangeText: (text) => setValue((text || "").trimStart()), onClearIconPress: handleClear, placeholder, placeholderTextColor: theme.colors.onSurfaceVariant, ref: inputRef, spellCheck: false, style: styles5.input, value }) });
|
|
947
950
|
});
|
|
948
951
|
var styles5 = import_react_native9.StyleSheet.create({
|
|
949
952
|
container: { paddingVertical: 8 },
|
|
@@ -1058,14 +1061,20 @@ var import_react13 = require("react");
|
|
|
1058
1061
|
var import_react_native11 = require("react-native");
|
|
1059
1062
|
var import_react_native_reanimated11 = __toESM(require("react-native-reanimated"));
|
|
1060
1063
|
var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
|
|
1061
|
-
var import_jsx_runtime8 =
|
|
1064
|
+
var import_jsx_runtime8 = (
|
|
1065
|
+
// bottom: tabBarHeight (not the styles.footer default of 0) - a consuming app's own persistent
|
|
1066
|
+
// tab bar (a fixture outside this package's header/footer concepts entirely, same as
|
|
1067
|
+
// useScrollList's content-inset reservation) sits below this at the true screen bottom, so this
|
|
1068
|
+
// bar has to clear it rather than rendering flush against 0 and overlapping/hiding behind it.
|
|
1069
|
+
require("react/jsx-runtime")
|
|
1070
|
+
);
|
|
1062
1071
|
var ScrollViewFooter = ({ children, style }) => {
|
|
1063
|
-
const { blur, footerAboveKeyboard, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared } = (0, import_react13.useContext)(ScrollViewContext);
|
|
1072
|
+
const { blur, footerAboveKeyboard, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared, tabBarHeight } = (0, import_react13.useContext)(ScrollViewContext);
|
|
1064
1073
|
const insets = (0, import_react_native_safe_area_context3.useSafeAreaInsets)();
|
|
1065
1074
|
const keyboardHeight = useKeyboardInset();
|
|
1066
1075
|
(0, import_react13.useEffect)(
|
|
1067
1076
|
() => () => {
|
|
1068
|
-
setFooterHeight(
|
|
1077
|
+
setFooterHeight(null);
|
|
1069
1078
|
},
|
|
1070
1079
|
[setFooterHeight]
|
|
1071
1080
|
);
|
|
@@ -1081,17 +1090,17 @@ var ScrollViewFooter = ({ children, style }) => {
|
|
|
1081
1090
|
if (snapBackFooterShared.value) return { transform: [{ translateY: footerOffset.value }] };
|
|
1082
1091
|
const effective = scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value;
|
|
1083
1092
|
if (effective <= 0) return { transform: [{ translateY: 0 }] };
|
|
1084
|
-
return { transform: [{ translateY: Math.min(effective, footerHeight) }] };
|
|
1093
|
+
return { transform: [{ translateY: Math.min(effective, footerHeight ?? 0) }] };
|
|
1085
1094
|
}, [footerHeight, footerFixed, headerHeightShared, pullSearchHeightShared, scrollPosition, snapBackFooterShared, footerOffset]);
|
|
1086
1095
|
const containerPaddingBottom = footerFixed && footerAboveKeyboard ? keyboardHeight : 0;
|
|
1087
1096
|
const rowPaddingBottom = footerFixed && footerAboveKeyboard ? Math.max(insets.bottom - keyboardHeight, 0) : insets.bottom;
|
|
1088
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_reanimated11.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [styles6.footer, { paddingBottom: containerPaddingBottom }, footerStyle], children: [
|
|
1089
|
-
footerHeight > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper3.BlurView, { blur, style: import_react_native11.StyleSheet.absoluteFill }),
|
|
1097
|
+
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: [
|
|
1098
|
+
(footerHeight ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper3.BlurView, { blur, style: import_react_native11.StyleSheet.absoluteFill }),
|
|
1090
1099
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native11.View, { style: [styles6.row, { paddingBottom: rowPaddingBottom }, style], children })
|
|
1091
1100
|
] });
|
|
1092
1101
|
};
|
|
1093
1102
|
var styles6 = import_react_native11.StyleSheet.create({
|
|
1094
|
-
footer: {
|
|
1103
|
+
footer: { left: 0, position: "absolute", right: 0, zIndex: 2 },
|
|
1095
1104
|
row: { alignItems: "center", flexDirection: "row", zIndex: 1 }
|
|
1096
1105
|
});
|
|
1097
1106
|
|
|
@@ -1099,17 +1108,17 @@ var styles6 = import_react_native11.StyleSheet.create({
|
|
|
1099
1108
|
var import_auto_paper4 = require("@rific/auto-paper");
|
|
1100
1109
|
var import_react14 = require("react");
|
|
1101
1110
|
var import_react_native12 = require("react-native");
|
|
1102
|
-
var
|
|
1111
|
+
var import_react_native_paper4 = require("react-native-paper");
|
|
1103
1112
|
var import_react_native_reanimated12 = __toESM(require("react-native-reanimated"));
|
|
1104
1113
|
var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
|
|
1105
1114
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
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)(
|
|
1115
|
+
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 }) });
|
|
1107
1116
|
var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAccessibilityLabel, backActionFixed, caption, centerContent, children, iconSize, style, title, topInset = true, trailingAction, trailingActionFixed = true }) => {
|
|
1108
1117
|
const { blur, headerHeight, headerFixed, headerHeightShared, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = (0, import_react14.useContext)(ScrollViewContext);
|
|
1109
1118
|
const { settings } = (0, import_react14.useContext)(ScrollViewSettingsContext);
|
|
1110
1119
|
const effectiveBackActionFixed = backActionFixed ?? settings.backActionFixed;
|
|
1111
1120
|
const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
|
|
1112
|
-
const theme = (0,
|
|
1121
|
+
const theme = (0, import_react_native_paper4.useTheme)();
|
|
1113
1122
|
const top = topInset ? insets.top : 0;
|
|
1114
1123
|
const measuredShared = (0, import_react_native_reanimated12.useSharedValue)(false);
|
|
1115
1124
|
const handleLayout = ({
|
|
@@ -1161,13 +1170,13 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAc
|
|
|
1161
1170
|
] }) }),
|
|
1162
1171
|
!blur && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [styles7.divider, { backgroundColor: theme.colors.outlineVariant }] })
|
|
1163
1172
|
] }),
|
|
1164
|
-
/* @__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)(
|
|
1173
|
+
/* @__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 }) }),
|
|
1165
1174
|
backAction && (effectiveBackActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: leadingStyle, children: [
|
|
1166
1175
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
|
|
1167
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1176
|
+
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
|
|
1168
1177
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: [leadingStyle, translateStyle], children: [
|
|
1169
1178
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
|
|
1170
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1179
|
+
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
|
|
1171
1180
|
] })),
|
|
1172
1181
|
trailingAction && (trailingActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native12.View, { style: trailingStyle, children: [
|
|
1173
1182
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
|
|
@@ -1207,7 +1216,7 @@ var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard =
|
|
|
1207
1216
|
const effectiveSnapBackHeader = snapBackHeader ?? settings.snapBackHeader;
|
|
1208
1217
|
const effectiveSnapBackFooter = snapBackFooter ?? settings.snapBackFooter;
|
|
1209
1218
|
const [headerHeight, setHeaderHeightState] = (0, import_react15.useState)(null);
|
|
1210
|
-
const [footerHeight, setFooterHeightState] = (0, import_react15.useState)(
|
|
1219
|
+
const [footerHeight, setFooterHeightState] = (0, import_react15.useState)(null);
|
|
1211
1220
|
const [progress, setProgress] = (0, import_react15.useState)(null);
|
|
1212
1221
|
const [progressing, setProgressing] = (0, import_react15.useState)(false);
|
|
1213
1222
|
const scrollPosition = (0, import_react_native_reanimated13.useSharedValue)(0);
|
|
@@ -1247,12 +1256,13 @@ var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard =
|
|
|
1247
1256
|
}, [headerHeight]);
|
|
1248
1257
|
const setFooterHeight = (0, import_react15.useCallback)(
|
|
1249
1258
|
(h) => {
|
|
1259
|
+
if (h === 0 && footerHeight) return;
|
|
1250
1260
|
setFooterHeightState(h);
|
|
1251
|
-
footerHeightShared.value = h;
|
|
1261
|
+
footerHeightShared.value = h ?? 0;
|
|
1252
1262
|
},
|
|
1253
|
-
[footerHeightShared]
|
|
1263
|
+
[footerHeight, footerHeightShared]
|
|
1254
1264
|
);
|
|
1255
|
-
const scrollHeight = (0, import_react15.useMemo)(() => import_react_native13.Dimensions.get("window").height - (headerHeight ?? 0) - footerHeight, [headerHeight, footerHeight]);
|
|
1265
|
+
const scrollHeight = (0, import_react15.useMemo)(() => import_react_native13.Dimensions.get("window").height - (headerHeight ?? 0) - (footerHeight ?? 0), [headerHeight, footerHeight]);
|
|
1256
1266
|
const effectiveHeaderFixed = fixed || (headerFixed ?? settings.headerFixed);
|
|
1257
1267
|
const effectiveFooterFixed = fixed || (footerFixed ?? settings.footerFixed);
|
|
1258
1268
|
const value = (0, import_react15.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]);
|
package/dist/index.mjs
CHANGED
|
@@ -12,15 +12,18 @@ var RefreshControl = (props) => /* @__PURE__ */ jsx(RNRefreshControl, { refreshi
|
|
|
12
12
|
// src/internal/ScrollViewChip.tsx
|
|
13
13
|
import { Chip } from "@rific/auto-paper";
|
|
14
14
|
import { StyleSheet } from "react-native";
|
|
15
|
+
import { Icon, useTheme } from "react-native-paper";
|
|
15
16
|
import Animated from "react-native-reanimated";
|
|
16
17
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
17
18
|
var ScrollViewChip = ({ chipProps, isHorizontal, onChipPress, onPress, style }) => {
|
|
18
|
-
const {
|
|
19
|
+
const { colors } = useTheme();
|
|
20
|
+
const { label, selectedColor, style: chipStyle, ...restChipProps } = chipProps ?? {};
|
|
19
21
|
const handlePress = onChipPress ? () => {
|
|
20
22
|
onPress();
|
|
21
23
|
onChipPress();
|
|
22
24
|
} : onPress;
|
|
23
|
-
|
|
25
|
+
const iconColor = selectedColor ?? (restChipProps.mode === "outlined" ? colors.onSurfaceVariant : colors.onSecondaryContainer);
|
|
26
|
+
return /* @__PURE__ */ jsx2(Animated.View, { style: [styles.chip, style], children: /* @__PURE__ */ jsx2(Chip, { compact: true, selectedColor, ...restChipProps, avatar: /* @__PURE__ */ jsx2(Icon, { source: isHorizontal ? "chevron-left" : "chevron-up", size: 18, color: iconColor }), onPress: handlePress, style: [styles.chipInner, chipStyle], children: label ?? (isHorizontal ? "Start" : "Top") }) });
|
|
24
27
|
};
|
|
25
28
|
var styles = StyleSheet.create({
|
|
26
29
|
chip: { alignItems: "center", left: 0, position: "absolute", right: 0, zIndex: 3 },
|
|
@@ -36,7 +39,7 @@ import { createContext } from "react";
|
|
|
36
39
|
var ScrollViewContext = createContext({
|
|
37
40
|
blur: true,
|
|
38
41
|
footerAboveKeyboard: false,
|
|
39
|
-
footerHeight:
|
|
42
|
+
footerHeight: null,
|
|
40
43
|
footerHeightShared: null,
|
|
41
44
|
footerFixed: false,
|
|
42
45
|
footerOffset: null,
|
|
@@ -504,7 +507,7 @@ import { Gesture as Gesture2, GestureDetector as GestureDetector2 } from "react-
|
|
|
504
507
|
import { BlurView } from "@rific/auto-paper";
|
|
505
508
|
import { useContext as useContext5 } from "react";
|
|
506
509
|
import { StyleSheet as StyleSheet3, useWindowDimensions, View as View2 } from "react-native";
|
|
507
|
-
import { useTheme } from "react-native-paper";
|
|
510
|
+
import { useTheme as useTheme2 } from "react-native-paper";
|
|
508
511
|
import Animated2, { interpolateColor, useAnimatedStyle as useAnimatedStyle2 } from "react-native-reanimated";
|
|
509
512
|
import { useSafeAreaInsets as useSafeAreaInsets2 } from "react-native-safe-area-context";
|
|
510
513
|
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -534,7 +537,7 @@ var HorizontalDots = ({ total }) => {
|
|
|
534
537
|
const { blur, footerHeightShared, scrollPosition } = useContext5(ScrollViewContext);
|
|
535
538
|
const { width } = useWindowDimensions();
|
|
536
539
|
const insets = useSafeAreaInsets2();
|
|
537
|
-
const theme =
|
|
540
|
+
const theme = useTheme2();
|
|
538
541
|
const insetsBottom = insets.bottom;
|
|
539
542
|
const activeColor = theme.colors.primary;
|
|
540
543
|
const inactiveColor = theme.colors.outlineVariant;
|
|
@@ -828,12 +831,12 @@ var FlatList = memo2(FlatListInner);
|
|
|
828
831
|
// src/PullSearch.tsx
|
|
829
832
|
import { forwardRef, useCallback as useCallback5, useContext as useContext8, useEffect as useEffect3, useImperativeHandle, useRef as useRef5, useState as useState4 } from "react";
|
|
830
833
|
import { StyleSheet as StyleSheet4 } from "react-native";
|
|
831
|
-
import { Searchbar, useTheme as
|
|
834
|
+
import { Searchbar, useTheme as useTheme3 } from "react-native-paper";
|
|
832
835
|
import Animated3, { Extrapolation, interpolate, runOnJS as runOnJS3, useAnimatedReaction, useAnimatedStyle as useAnimatedStyle3 } from "react-native-reanimated";
|
|
833
836
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
834
837
|
var PullSearch = forwardRef(function PullSearch2({ debounce, onChangeText, onHeightChange, placeholder, value: propValue }, ref) {
|
|
835
838
|
const { headerHeight, scrollPosition } = useContext8(ScrollViewContext);
|
|
836
|
-
const theme =
|
|
839
|
+
const theme = useTheme3();
|
|
837
840
|
const inputRef = useRef5(null);
|
|
838
841
|
const [barHeight, setBarHeight] = useState4(0);
|
|
839
842
|
const [value, setValue] = useState4(propValue ?? "");
|
|
@@ -1008,12 +1011,12 @@ import Animated5, { useAnimatedStyle as useAnimatedStyle4 } from "react-native-r
|
|
|
1008
1011
|
import { useSafeAreaInsets as useSafeAreaInsets3 } from "react-native-safe-area-context";
|
|
1009
1012
|
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1010
1013
|
var ScrollViewFooter = ({ children, style }) => {
|
|
1011
|
-
const { blur, footerAboveKeyboard, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared } = useContext10(ScrollViewContext);
|
|
1014
|
+
const { blur, footerAboveKeyboard, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared, tabBarHeight } = useContext10(ScrollViewContext);
|
|
1012
1015
|
const insets = useSafeAreaInsets3();
|
|
1013
1016
|
const keyboardHeight = useKeyboardInset();
|
|
1014
1017
|
useEffect4(
|
|
1015
1018
|
() => () => {
|
|
1016
|
-
setFooterHeight(
|
|
1019
|
+
setFooterHeight(null);
|
|
1017
1020
|
},
|
|
1018
1021
|
[setFooterHeight]
|
|
1019
1022
|
);
|
|
@@ -1029,17 +1032,23 @@ var ScrollViewFooter = ({ children, style }) => {
|
|
|
1029
1032
|
if (snapBackFooterShared.value) return { transform: [{ translateY: footerOffset.value }] };
|
|
1030
1033
|
const effective = scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value;
|
|
1031
1034
|
if (effective <= 0) return { transform: [{ translateY: 0 }] };
|
|
1032
|
-
return { transform: [{ translateY: Math.min(effective, footerHeight) }] };
|
|
1035
|
+
return { transform: [{ translateY: Math.min(effective, footerHeight ?? 0) }] };
|
|
1033
1036
|
}, [footerHeight, footerFixed, headerHeightShared, pullSearchHeightShared, scrollPosition, snapBackFooterShared, footerOffset]);
|
|
1034
1037
|
const containerPaddingBottom = footerFixed && footerAboveKeyboard ? keyboardHeight : 0;
|
|
1035
1038
|
const rowPaddingBottom = footerFixed && footerAboveKeyboard ? Math.max(insets.bottom - keyboardHeight, 0) : insets.bottom;
|
|
1036
|
-
return
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1039
|
+
return (
|
|
1040
|
+
// bottom: tabBarHeight (not the styles.footer default of 0) - a consuming app's own persistent
|
|
1041
|
+
// tab bar (a fixture outside this package's header/footer concepts entirely, same as
|
|
1042
|
+
// useScrollList's content-inset reservation) sits below this at the true screen bottom, so this
|
|
1043
|
+
// bar has to clear it rather than rendering flush against 0 and overlapping/hiding behind it.
|
|
1044
|
+
/* @__PURE__ */ jsxs5(Animated5.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [styles6.footer, { bottom: tabBarHeight, paddingBottom: containerPaddingBottom }, footerStyle], children: [
|
|
1045
|
+
(footerHeight ?? 0) > 0 && /* @__PURE__ */ jsx8(BlurView2, { blur, style: StyleSheet5.absoluteFill }),
|
|
1046
|
+
/* @__PURE__ */ jsx8(View5, { style: [styles6.row, { paddingBottom: rowPaddingBottom }, style], children })
|
|
1047
|
+
] })
|
|
1048
|
+
);
|
|
1040
1049
|
};
|
|
1041
1050
|
var styles6 = StyleSheet5.create({
|
|
1042
|
-
footer: {
|
|
1051
|
+
footer: { left: 0, position: "absolute", right: 0, zIndex: 2 },
|
|
1043
1052
|
row: { alignItems: "center", flexDirection: "row", zIndex: 1 }
|
|
1044
1053
|
});
|
|
1045
1054
|
|
|
@@ -1047,7 +1056,7 @@ var styles6 = StyleSheet5.create({
|
|
|
1047
1056
|
import { BlurView as BlurView3 } from "@rific/auto-paper";
|
|
1048
1057
|
import { useContext as useContext11 } from "react";
|
|
1049
1058
|
import { StyleSheet as StyleSheet6, Text, View as View6 } from "react-native";
|
|
1050
|
-
import { Appbar, ProgressBar, Surface, useTheme as
|
|
1059
|
+
import { Appbar, ProgressBar, Surface, useTheme as useTheme4 } from "react-native-paper";
|
|
1051
1060
|
import Animated6, { useAnimatedStyle as useAnimatedStyle5, useSharedValue as useSharedValue6 } from "react-native-reanimated";
|
|
1052
1061
|
import { useSafeAreaInsets as useSafeAreaInsets4 } from "react-native-safe-area-context";
|
|
1053
1062
|
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
@@ -1057,7 +1066,7 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAc
|
|
|
1057
1066
|
const { settings } = useContext11(ScrollViewSettingsContext);
|
|
1058
1067
|
const effectiveBackActionFixed = backActionFixed ?? settings.backActionFixed;
|
|
1059
1068
|
const insets = useSafeAreaInsets4();
|
|
1060
|
-
const theme =
|
|
1069
|
+
const theme = useTheme4();
|
|
1061
1070
|
const top = topInset ? insets.top : 0;
|
|
1062
1071
|
const measuredShared = useSharedValue6(false);
|
|
1063
1072
|
const handleLayout = ({
|
|
@@ -1112,10 +1121,10 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAc
|
|
|
1112
1121
|
/* @__PURE__ */ jsx9(Animated6.View, { pointerEvents: "none", style: [styles7.progress, progressStyle], children: /* @__PURE__ */ jsx9(ProgressBar, { indeterminate: progress === null, visible: progressing, progress: progress ?? void 0 }) }),
|
|
1113
1122
|
backAction && (effectiveBackActionFixed ? /* @__PURE__ */ jsxs6(Animated6.View, { style: leadingStyle, children: [
|
|
1114
1123
|
/* @__PURE__ */ jsx9(ActionBg, { blur, style: actionStyle }),
|
|
1115
|
-
/* @__PURE__ */ jsx9(Appbar.BackAction, { accessibilityLabel: backActionAccessibilityLabel, onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle })
|
|
1124
|
+
typeof backAction === "function" ? /* @__PURE__ */ jsx9(Appbar.BackAction, { accessibilityLabel: backActionAccessibilityLabel, onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle }) : backAction
|
|
1116
1125
|
] }) : /* @__PURE__ */ jsxs6(Animated6.View, { style: [leadingStyle, translateStyle], children: [
|
|
1117
1126
|
/* @__PURE__ */ jsx9(ActionBg, { blur, style: actionStyle }),
|
|
1118
|
-
/* @__PURE__ */ jsx9(Appbar.BackAction, { accessibilityLabel: backActionAccessibilityLabel, onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle })
|
|
1127
|
+
typeof backAction === "function" ? /* @__PURE__ */ jsx9(Appbar.BackAction, { accessibilityLabel: backActionAccessibilityLabel, onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle }) : backAction
|
|
1119
1128
|
] })),
|
|
1120
1129
|
trailingAction && (trailingActionFixed ? /* @__PURE__ */ jsxs6(View6, { style: trailingStyle, children: [
|
|
1121
1130
|
/* @__PURE__ */ jsx9(ActionBg, { blur, style: actionStyle }),
|
|
@@ -1155,7 +1164,7 @@ var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard =
|
|
|
1155
1164
|
const effectiveSnapBackHeader = snapBackHeader ?? settings.snapBackHeader;
|
|
1156
1165
|
const effectiveSnapBackFooter = snapBackFooter ?? settings.snapBackFooter;
|
|
1157
1166
|
const [headerHeight, setHeaderHeightState] = useState5(null);
|
|
1158
|
-
const [footerHeight, setFooterHeightState] = useState5(
|
|
1167
|
+
const [footerHeight, setFooterHeightState] = useState5(null);
|
|
1159
1168
|
const [progress, setProgress] = useState5(null);
|
|
1160
1169
|
const [progressing, setProgressing] = useState5(false);
|
|
1161
1170
|
const scrollPosition = useSharedValue7(0);
|
|
@@ -1195,12 +1204,13 @@ var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard =
|
|
|
1195
1204
|
}, [headerHeight]);
|
|
1196
1205
|
const setFooterHeight = useCallback7(
|
|
1197
1206
|
(h) => {
|
|
1207
|
+
if (h === 0 && footerHeight) return;
|
|
1198
1208
|
setFooterHeightState(h);
|
|
1199
|
-
footerHeightShared.value = h;
|
|
1209
|
+
footerHeightShared.value = h ?? 0;
|
|
1200
1210
|
},
|
|
1201
|
-
[footerHeightShared]
|
|
1211
|
+
[footerHeight, footerHeightShared]
|
|
1202
1212
|
);
|
|
1203
|
-
const scrollHeight = useMemo5(() => Dimensions3.get("window").height - (headerHeight ?? 0) - footerHeight, [headerHeight, footerHeight]);
|
|
1213
|
+
const scrollHeight = useMemo5(() => Dimensions3.get("window").height - (headerHeight ?? 0) - (footerHeight ?? 0), [headerHeight, footerHeight]);
|
|
1204
1214
|
const effectiveHeaderFixed = fixed || (headerFixed ?? settings.headerFixed);
|
|
1205
1215
|
const effectiveFooterFixed = fixed || (footerFixed ?? settings.footerFixed);
|
|
1206
1216
|
const value = useMemo5(() => ({ 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]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rific/scroll-view",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
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",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@reduxjs/toolkit": "^2.12.0",
|
|
63
|
-
"@rific/auto-paper": "^0.
|
|
63
|
+
"@rific/auto-paper": "^0.9.2",
|
|
64
64
|
"@testing-library/dom": "^10.4.1",
|
|
65
65
|
"@testing-library/react": "^16.3.2",
|
|
66
66
|
"@types/jest": "^30.0.0",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"typescript-eslint": "^8.59.3"
|
|
94
94
|
},
|
|
95
95
|
"peerDependencies": {
|
|
96
|
-
"@rific/auto-paper": "^0.
|
|
96
|
+
"@rific/auto-paper": "^0.9.2",
|
|
97
97
|
"react": ">=19.0.0",
|
|
98
98
|
"react-native": ">=0.83.0",
|
|
99
99
|
"react-native-gesture-handler": ">=2.0.0 <3.0.0",
|
package/src/ScrollViewContext.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { SharedValue } from 'react-native-reanimated'
|
|
|
4
4
|
export type ScrollViewContextType = {
|
|
5
5
|
blur: boolean
|
|
6
6
|
footerAboveKeyboard: boolean
|
|
7
|
-
footerHeight: number
|
|
7
|
+
footerHeight: number | null
|
|
8
8
|
footerHeightShared: SharedValue<number>
|
|
9
9
|
footerFixed: boolean
|
|
10
10
|
footerOffset: SharedValue<number>
|
|
@@ -28,7 +28,7 @@ export type ScrollViewContextType = {
|
|
|
28
28
|
progressing: boolean
|
|
29
29
|
scrollHeight: number
|
|
30
30
|
scrollPosition: SharedValue<number>
|
|
31
|
-
setFooterHeight: (h: number) => void
|
|
31
|
+
setFooterHeight: (h: number | null) => void
|
|
32
32
|
setHeaderHeight: (h: number | null) => void
|
|
33
33
|
setProgress: (p: number | null) => void
|
|
34
34
|
setProgressing: (b: boolean) => void
|
|
@@ -38,7 +38,7 @@ export type ScrollViewContextType = {
|
|
|
38
38
|
export const ScrollViewContext = createContext<ScrollViewContextType>({
|
|
39
39
|
blur: true,
|
|
40
40
|
footerAboveKeyboard: false,
|
|
41
|
-
footerHeight:
|
|
41
|
+
footerHeight: null,
|
|
42
42
|
footerHeightShared: null as unknown as SharedValue<number>,
|
|
43
43
|
footerFixed: false,
|
|
44
44
|
footerOffset: null as unknown as SharedValue<number>,
|
package/src/ScrollViewFooter.tsx
CHANGED
|
@@ -13,12 +13,12 @@ export type ScrollViewFooterProps = {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const ScrollViewFooter = ({ children, style }: ScrollViewFooterProps) => {
|
|
16
|
-
const { blur, footerAboveKeyboard, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared } = useContext(ScrollViewContext)
|
|
16
|
+
const { blur, footerAboveKeyboard, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared, tabBarHeight } = useContext(ScrollViewContext)
|
|
17
17
|
const insets = useSafeAreaInsets()
|
|
18
18
|
const keyboardHeight = useKeyboardInset()
|
|
19
19
|
useEffect(
|
|
20
20
|
() => () => {
|
|
21
|
-
setFooterHeight(
|
|
21
|
+
setFooterHeight(null)
|
|
22
22
|
},
|
|
23
23
|
[setFooterHeight]
|
|
24
24
|
)
|
|
@@ -38,7 +38,7 @@ export const ScrollViewFooter = ({ children, style }: ScrollViewFooterProps) =>
|
|
|
38
38
|
if (snapBackFooterShared.value) return { transform: [{ translateY: footerOffset.value }] }
|
|
39
39
|
const effective = scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value
|
|
40
40
|
if (effective <= 0) return { transform: [{ translateY: 0 }] }
|
|
41
|
-
return { transform: [{ translateY: Math.min(effective, footerHeight) }] }
|
|
41
|
+
return { transform: [{ translateY: Math.min(effective, footerHeight ?? 0) }] }
|
|
42
42
|
}, [footerHeight, footerFixed, headerHeightShared, pullSearchHeightShared, scrollPosition, snapBackFooterShared, footerOffset])
|
|
43
43
|
// Floating above the keyboard is done by GROWING this container (via paddingBottom) rather than
|
|
44
44
|
// translating it. Translating the whole bar left its bottom edge — and the BlurView filling it —
|
|
@@ -66,14 +66,18 @@ export const ScrollViewFooter = ({ children, style }: ScrollViewFooterProps) =>
|
|
|
66
66
|
// of dropping past it and correcting back up.
|
|
67
67
|
const rowPaddingBottom = footerFixed && footerAboveKeyboard ? Math.max(insets.bottom - keyboardHeight, 0) : insets.bottom
|
|
68
68
|
return (
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
// bottom: tabBarHeight (not the styles.footer default of 0) - a consuming app's own persistent
|
|
70
|
+
// tab bar (a fixture outside this package's header/footer concepts entirely, same as
|
|
71
|
+
// useScrollList's content-inset reservation) sits below this at the true screen bottom, so this
|
|
72
|
+
// bar has to clear it rather than rendering flush against 0 and overlapping/hiding behind it.
|
|
73
|
+
<Animated.View onLayout={handleLayout} pointerEvents='box-none' style={[styles.footer, { bottom: tabBarHeight, paddingBottom: containerPaddingBottom }, footerStyle]}>
|
|
74
|
+
{(footerHeight ?? 0) > 0 && <BlurView blur={blur} style={StyleSheet.absoluteFill} />}
|
|
71
75
|
<View style={[styles.row, { paddingBottom: rowPaddingBottom }, style]}>{children}</View>
|
|
72
76
|
</Animated.View>
|
|
73
77
|
)
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
const styles = StyleSheet.create({
|
|
77
|
-
footer: {
|
|
81
|
+
footer: { left: 0, position: 'absolute', right: 0, zIndex: 2 },
|
|
78
82
|
row: { alignItems: 'center', flexDirection: 'row', zIndex: 1 }
|
|
79
83
|
})
|
package/src/ScrollViewHeader.tsx
CHANGED
|
@@ -11,7 +11,14 @@ import { ScrollViewSettingsContext } from './ScrollViewSettingsContext'
|
|
|
11
11
|
export type ScrollViewHeaderProps = {
|
|
12
12
|
actionSize?: number
|
|
13
13
|
actionStyle?: ViewStyle
|
|
14
|
-
|
|
14
|
+
// A plain callback renders the default Appbar.BackAction (themed onSurface, sized/positioned by
|
|
15
|
+
// actionSize/iconSize below) — the common case, unchanged from before. Passing a ReactNode
|
|
16
|
+
// instead skips that default entirely and renders the given element in its place, same escape
|
|
17
|
+
// hatch trailingAction already gave the other corner: a caller that wants its own color, icon,
|
|
18
|
+
// or component altogether (a colored IconButton, say) isn't stuck fighting Appbar.BackAction's
|
|
19
|
+
// own fixed color/no-customization API to get it. backActionAccessibilityLabel/iconSize below
|
|
20
|
+
// only apply to the default-rendered case — a custom element carries its own.
|
|
21
|
+
backAction?: (() => void) | ReactNode
|
|
15
22
|
backActionAccessibilityLabel?: string
|
|
16
23
|
backActionFixed?: boolean
|
|
17
24
|
caption?: string
|
|
@@ -147,12 +154,12 @@ export const ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, bac
|
|
|
147
154
|
(effectiveBackActionFixed ? (
|
|
148
155
|
<Animated.View style={leadingStyle}>
|
|
149
156
|
<ActionBg blur={blur} style={actionStyle} />
|
|
150
|
-
<Appbar.BackAction accessibilityLabel={backActionAccessibilityLabel} onPress={backAction} size={iconSize ?? actionSize / 2} style={buttonStyle} />
|
|
157
|
+
{typeof backAction === 'function' ? <Appbar.BackAction accessibilityLabel={backActionAccessibilityLabel} onPress={backAction} size={iconSize ?? actionSize / 2} style={buttonStyle} /> : backAction}
|
|
151
158
|
</Animated.View>
|
|
152
159
|
) : (
|
|
153
160
|
<Animated.View style={[leadingStyle, translateStyle]}>
|
|
154
161
|
<ActionBg blur={blur} style={actionStyle} />
|
|
155
|
-
<Appbar.BackAction accessibilityLabel={backActionAccessibilityLabel} onPress={backAction} size={iconSize ?? actionSize / 2} style={buttonStyle} />
|
|
162
|
+
{typeof backAction === 'function' ? <Appbar.BackAction accessibilityLabel={backActionAccessibilityLabel} onPress={backAction} size={iconSize ?? actionSize / 2} style={buttonStyle} /> : backAction}
|
|
156
163
|
</Animated.View>
|
|
157
164
|
))}
|
|
158
165
|
{trailingAction &&
|
|
@@ -26,7 +26,7 @@ export const ScrollViewProvider = ({ blur, children, fixed = false, footerAboveK
|
|
|
26
26
|
const effectiveSnapBackHeader = snapBackHeader ?? settings.snapBackHeader
|
|
27
27
|
const effectiveSnapBackFooter = snapBackFooter ?? settings.snapBackFooter
|
|
28
28
|
const [headerHeight, setHeaderHeightState] = useState<number | null>(null)
|
|
29
|
-
const [footerHeight, setFooterHeightState] = useState(
|
|
29
|
+
const [footerHeight, setFooterHeightState] = useState<number | null>(null)
|
|
30
30
|
const [progress, setProgress] = useState<number | null>(null)
|
|
31
31
|
const [progressing, setProgressing] = useState(false)
|
|
32
32
|
const scrollPosition = useSharedValue(0)
|
|
@@ -71,13 +71,20 @@ export const ScrollViewProvider = ({ blur, children, fixed = false, footerAboveK
|
|
|
71
71
|
return () => clearTimeout(timeout)
|
|
72
72
|
}, [headerHeight])
|
|
73
73
|
const setFooterHeight = useCallback(
|
|
74
|
-
(h: number) => {
|
|
74
|
+
(h: number | null) => {
|
|
75
|
+
// Same race setHeaderHeight guards against, on the footer side: react-native-screens' web
|
|
76
|
+
// shim (and a footer's own unmount cleanup below, which now passes null instead of 0 for
|
|
77
|
+
// exactly this reason) can report/force a spurious 0 while a real footer is effectively
|
|
78
|
+
// still on screen (e.g. a wizard cycling through several <ScrollViewFooter> instances).
|
|
79
|
+
// Never let that clobber a known height — only an explicit null (genuinely no footer
|
|
80
|
+
// mounted) may reset it.
|
|
81
|
+
if (h === 0 && footerHeight) return
|
|
75
82
|
setFooterHeightState(h)
|
|
76
|
-
footerHeightShared.value = h
|
|
83
|
+
footerHeightShared.value = h ?? 0
|
|
77
84
|
},
|
|
78
|
-
[footerHeightShared]
|
|
85
|
+
[footerHeight, footerHeightShared]
|
|
79
86
|
)
|
|
80
|
-
const scrollHeight = useMemo(() => Dimensions.get('window').height - (headerHeight ?? 0) - footerHeight, [headerHeight, footerHeight])
|
|
87
|
+
const scrollHeight = useMemo(() => Dimensions.get('window').height - (headerHeight ?? 0) - (footerHeight ?? 0), [headerHeight, footerHeight])
|
|
81
88
|
const effectiveHeaderFixed = fixed || (headerFixed ?? settings.headerFixed)
|
|
82
89
|
const effectiveFooterFixed = fixed || (footerFixed ?? settings.footerFixed)
|
|
83
90
|
const value = 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])
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { Chip, type ChipProps as AutoPaperChipProps } from '@rific/auto-paper'
|
|
2
2
|
import { type ReactNode } from 'react'
|
|
3
3
|
import { StyleSheet, type ViewStyle } from 'react-native'
|
|
4
|
+
import { Icon, useTheme } from 'react-native-paper'
|
|
4
5
|
import Animated, { type AnimatedStyle } from 'react-native-reanimated'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
// 'children' omitted alongside the rest — this chip's own text always comes from `label` (or the
|
|
8
|
+
// isHorizontal-driven 'Start'/'Top' default below), passed as the real JSX children the underlying
|
|
9
|
+
// Chip renders with, which is what actually wins: React always takes the children an element is
|
|
10
|
+
// given between its tags over one merely passed as a "children" prop via spread. A `children` in
|
|
11
|
+
// chipProps would therefore type-check but silently do nothing at runtime — omitting it here means
|
|
12
|
+
// TypeScript catches that mismatch instead of a caller finding out the hard way.
|
|
13
|
+
export type ChipProps = Omit<AutoPaperChipProps, 'compact' | 'icon' | 'onPress' | 'children'> & {
|
|
7
14
|
label?: ReactNode
|
|
8
15
|
}
|
|
9
16
|
|
|
@@ -16,16 +23,27 @@ type Props = {
|
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
export const ScrollViewChip = ({ chipProps, isHorizontal, onChipPress, onPress, style }: Props) => {
|
|
19
|
-
const {
|
|
26
|
+
const { colors } = useTheme()
|
|
27
|
+
const { label, selectedColor, style: chipStyle, ...restChipProps } = (chipProps ?? {}) as Partial<ChipProps>
|
|
20
28
|
const handlePress = onChipPress
|
|
21
29
|
? () => {
|
|
22
30
|
onPress()
|
|
23
31
|
onChipPress()
|
|
24
32
|
}
|
|
25
33
|
: onPress
|
|
34
|
+
// Rendered via `avatar`, not Chip's own `icon` prop: react-native-paper's MD3 Chip hardcodes
|
|
35
|
+
// `icon`'s color to theme.colors.primary and ignores `selectedColor` entirely (its own doc
|
|
36
|
+
// comment: "With theme version 3 selectedColor doesn't apply to the icon. If you want [a]
|
|
37
|
+
// custom color for the icon, render your own Icon component") — a chip whose own background
|
|
38
|
+
// IS theme.colors.primary (this library's callers all set that; see Hangman's own vivid-chip
|
|
39
|
+
// convention) renders that icon invisible against itself. `avatar` has no such override; it
|
|
40
|
+
// clones whatever element/color it's given. Falls back to the same default Paper itself would
|
|
41
|
+
// resolve for unselected/non-outlined text (onSecondaryContainer) or outlined text
|
|
42
|
+
// (onSurfaceVariant), so the icon still matches the label color when no selectedColor is set.
|
|
43
|
+
const iconColor = selectedColor ?? (restChipProps.mode === 'outlined' ? colors.onSurfaceVariant : colors.onSecondaryContainer)
|
|
26
44
|
return (
|
|
27
45
|
<Animated.View style={[styles.chip, style]}>
|
|
28
|
-
<Chip compact {...restChipProps}
|
|
46
|
+
<Chip compact selectedColor={selectedColor} {...restChipProps} avatar={<Icon source={isHorizontal ? 'chevron-left' : 'chevron-up'} size={18} color={iconColor} />} onPress={handlePress} style={[styles.chipInner, chipStyle]}>
|
|
29
47
|
{label ?? (isHorizontal ? 'Start' : 'Top')}
|
|
30
48
|
</Chip>
|
|
31
49
|
</Animated.View>
|