@rific/scroll-view 0.6.7 → 0.7.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/README.md CHANGED
@@ -150,6 +150,7 @@ Drop-in for React Native's `FlatList`. Accepts all `FlatListProps<T>` plus:
150
150
  | `onRefresh` | `() => Promise<void> \| void` | Enables pull-to-refresh |
151
151
  | `pullSearchHeight` | `number` | Reserve space above content for `PullSearch` |
152
152
  | `ref` | `RefObject<RNFlatList>` | Forward ref to the underlying list |
153
+ | `refreshing` | `boolean` | Controlled refreshing state |
153
154
  | `renderFilters` | `ReactNode` | Rendered below `ListHeaderComponent`, above list items |
154
155
 
155
156
  Performance props are user-overridable with sensible defaults:
@@ -194,6 +195,7 @@ import { CustomList } from '@rific/scroll-view'
194
195
  | `keyboardAware` | `boolean` | Add keyboard height to bottom content inset |
195
196
  | `onRefresh` | `() => Promise<void> \| void` | Enables pull-to-refresh |
196
197
  | `pullSearchHeight` | `number` | Reserve space above content for `PullSearch` |
198
+ | `refreshing` | `boolean` | Controlled refreshing state |
197
199
  | `renderFilters` | `ReactNode` | Rendered below `ListHeaderComponent`, above list items |
198
200
  | `scrollRef` | `RefObject<{ scrollToOffset }>` | Forward ref to the underlying list |
199
201
 
package/dist/index.d.mts CHANGED
@@ -31,10 +31,11 @@ type CustomListProps<P extends object> = Omit<P, 'contentInset' | 'contentOffset
31
31
  onScrollBeginDrag?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void;
32
32
  onScrollEndDrag?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void;
33
33
  pullSearchHeight?: number;
34
+ refreshing?: boolean;
34
35
  scrollRef?: RefObject<ScrollToOffsetRef | null>;
35
36
  style?: StyleProp<ViewStyle>;
36
37
  };
37
- declare const CustomListInner: <P extends object>({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollRef, style, ...props }: CustomListProps<P>) => react.JSX.Element;
38
+ declare const CustomListInner: <P extends object>({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, scrollRef, style, ...props }: CustomListProps<P>) => react.JSX.Element;
38
39
  declare const CustomList: typeof CustomListInner;
39
40
 
40
41
  type FlatListProps<T> = Omit<FlatListProps$1<T>, 'onRefresh' | 'refreshing'> & {
@@ -52,8 +53,9 @@ type FlatListProps<T> = Omit<FlatListProps$1<T>, 'onRefresh' | 'refreshing'> & {
52
53
  onScrollEndDrag?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void;
53
54
  pullSearchHeight?: number;
54
55
  ref?: RefObject<FlatList$1 | null>;
56
+ refreshing?: boolean;
55
57
  };
56
- declare const FlatListInner: <T>({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender, keyExtractor, maxToRenderPerBatch, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, removeClippedSubviews, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize, ...props }: FlatListProps<T>) => react.JSX.Element;
58
+ declare const FlatListInner: <T>({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender, keyExtractor, maxToRenderPerBatch, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, refreshing, removeClippedSubviews, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize, ...props }: FlatListProps<T>) => react.JSX.Element;
57
59
  declare const FlatList: typeof FlatListInner;
58
60
 
59
61
  type PullSearchHandle = {
package/dist/index.d.ts CHANGED
@@ -31,10 +31,11 @@ type CustomListProps<P extends object> = Omit<P, 'contentInset' | 'contentOffset
31
31
  onScrollBeginDrag?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void;
32
32
  onScrollEndDrag?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void;
33
33
  pullSearchHeight?: number;
34
+ refreshing?: boolean;
34
35
  scrollRef?: RefObject<ScrollToOffsetRef | null>;
35
36
  style?: StyleProp<ViewStyle>;
36
37
  };
37
- declare const CustomListInner: <P extends object>({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollRef, style, ...props }: CustomListProps<P>) => react.JSX.Element;
38
+ declare const CustomListInner: <P extends object>({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, scrollRef, style, ...props }: CustomListProps<P>) => react.JSX.Element;
38
39
  declare const CustomList: typeof CustomListInner;
39
40
 
40
41
  type FlatListProps<T> = Omit<FlatListProps$1<T>, 'onRefresh' | 'refreshing'> & {
@@ -52,8 +53,9 @@ type FlatListProps<T> = Omit<FlatListProps$1<T>, 'onRefresh' | 'refreshing'> & {
52
53
  onScrollEndDrag?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void;
53
54
  pullSearchHeight?: number;
54
55
  ref?: RefObject<FlatList$1 | null>;
56
+ refreshing?: boolean;
55
57
  };
56
- declare const FlatListInner: <T>({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender, keyExtractor, maxToRenderPerBatch, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, removeClippedSubviews, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize, ...props }: FlatListProps<T>) => react.JSX.Element;
58
+ declare const FlatListInner: <T>({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender, keyExtractor, maxToRenderPerBatch, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, refreshing, removeClippedSubviews, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize, ...props }: FlatListProps<T>) => react.JSX.Element;
57
59
  declare const FlatList: typeof FlatListInner;
58
60
 
59
61
  type PullSearchHandle = {
package/dist/index.js CHANGED
@@ -474,7 +474,7 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
474
474
 
475
475
  // src/CustomList.tsx
476
476
  var import_jsx_runtime3 = require("react/jsx-runtime");
477
- var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollRef, style, ...props }) => {
477
+ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, scrollRef, style, ...props }) => {
478
478
  const { ListHeaderComponent: listHeaderComponent, contentContainerStyle: externalContentContainerStyle, ...restProps } = props;
479
479
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
480
480
  const { listGeneration, onListUnmount } = (0, import_react6.useContext)(ScrollViewContext);
@@ -535,7 +535,7 @@ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilter
535
535
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
536
536
  scrollViewInternal.current?.scrollToOffset({ offset, animated: true });
537
537
  }, [contentInset.top, pullSearchHeight]);
538
- const refreshControl = (0, import_react6.useMemo)(() => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RefreshControl, {}), []);
538
+ const refreshControl = (0, import_react6.useMemo)(() => onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RefreshControl, {}), [onRefresh, refreshing]);
539
539
  const nativeGesture = (0, import_react6.useMemo)(() => import_react_native_gesture_handler.Gesture.Native(), []);
540
540
  const combinedGesture = (0, import_react6.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
541
541
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
@@ -729,7 +729,7 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
729
729
 
730
730
  // src/FlatList.tsx
731
731
  var import_jsx_runtime5 = require("react/jsx-runtime");
732
- var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender = 20, keyExtractor, maxToRenderPerBatch = 50, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, removeClippedSubviews = false, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize = 100, ...props }) => {
732
+ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender = 20, keyExtractor, maxToRenderPerBatch = 50, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, refreshing, removeClippedSubviews = false, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize = 100, ...props }) => {
733
733
  const scrollView = (0, import_react9.useRef)(null);
734
734
  const isHorizontal = horizontal === true;
735
735
  const showDots = isHorizontal && pagingEnabled === true && (data?.length ?? 0) > 1;
@@ -828,7 +828,7 @@ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentCont
828
828
  },
829
829
  [onContentSizeChange]
830
830
  );
831
- const refreshControl = (0, import_react9.useMemo)(() => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RefreshControl, {}), []);
831
+ const refreshControl = (0, import_react9.useMemo)(() => onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RefreshControl, {}), [onRefresh, refreshing]);
832
832
  const nativeGesture = (0, import_react9.useMemo)(() => import_react_native_gesture_handler2.Gesture.Native(), []);
833
833
  const combinedGesture = (0, import_react9.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler2.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
834
834
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
@@ -1193,11 +1193,11 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAc
1193
1193
  };
1194
1194
  var styles7 = import_react_native12.StyleSheet.create({
1195
1195
  actionBg: { borderRadius: 100, overflow: "hidden" },
1196
- divider: { bottom: 0, height: import_react_native12.StyleSheet.hairlineWidth, left: 0, position: "absolute", right: 0 },
1197
- caption: { fontSize: 12, fontWeight: "400", textAlign: "center" },
1198
- content: { alignItems: "center", flexDirection: "row", width: "100%" },
1199
1196
  blur: { left: 0, overflow: "hidden", position: "absolute", right: 0, top: 0, zIndex: 2 },
1200
1197
  blurInner: { left: 0, position: "absolute", right: 0, top: 0 },
1198
+ caption: { fontSize: 12, fontWeight: "400", textAlign: "center" },
1199
+ content: { alignItems: "center", flexDirection: "row", width: "100%" },
1200
+ divider: { bottom: 0, height: import_react_native12.StyleSheet.hairlineWidth, left: 0, position: "absolute", right: 0 },
1201
1201
  header: { left: 0, overflow: "hidden", position: "absolute", right: 0, top: 0, zIndex: 2 },
1202
1202
  headerInit: { left: 0, overflow: "hidden", right: 0, zIndex: 2 },
1203
1203
  progress: { left: 0, position: "absolute", right: 0, zIndex: 3 },
@@ -1258,6 +1258,17 @@ var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard =
1258
1258
  }, 3e3);
1259
1259
  return () => clearTimeout(timeout);
1260
1260
  }, [headerHeight]);
1261
+ (0, import_react15.useEffect)(() => {
1262
+ if (import_react_native13.Platform.OS !== "web" || headerHeight !== null) return;
1263
+ let raf2 = 0;
1264
+ const raf1 = requestAnimationFrame(() => {
1265
+ raf2 = requestAnimationFrame(() => setHeaderHeight(0));
1266
+ });
1267
+ return () => {
1268
+ cancelAnimationFrame(raf1);
1269
+ cancelAnimationFrame(raf2);
1270
+ };
1271
+ }, [headerHeight, setHeaderHeight]);
1261
1272
  const setFooterHeight = (0, import_react15.useCallback)(
1262
1273
  (h) => {
1263
1274
  if (h === 0 && footerHeight) return;
package/dist/index.mjs CHANGED
@@ -422,7 +422,7 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
422
422
 
423
423
  // src/CustomList.tsx
424
424
  import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
425
- var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollRef, style, ...props }) => {
425
+ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, scrollRef, style, ...props }) => {
426
426
  const { ListHeaderComponent: listHeaderComponent, contentContainerStyle: externalContentContainerStyle, ...restProps } = props;
427
427
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
428
428
  const { listGeneration, onListUnmount } = useContext4(ScrollViewContext);
@@ -483,7 +483,7 @@ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilter
483
483
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
484
484
  scrollViewInternal.current?.scrollToOffset({ offset, animated: true });
485
485
  }, [contentInset.top, pullSearchHeight]);
486
- const refreshControl = useMemo2(() => /* @__PURE__ */ jsx3(RefreshControl, {}), []);
486
+ const refreshControl = useMemo2(() => onRefresh ? /* @__PURE__ */ jsx3(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ jsx3(RefreshControl, {}), [onRefresh, refreshing]);
487
487
  const nativeGesture = useMemo2(() => Gesture.Native(), []);
488
488
  const combinedGesture = useMemo2(() => gesture !== void 0 ? Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
489
489
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
@@ -677,7 +677,7 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
677
677
 
678
678
  // src/FlatList.tsx
679
679
  import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
680
- var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender = 20, keyExtractor, maxToRenderPerBatch = 50, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, removeClippedSubviews = false, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize = 100, ...props }) => {
680
+ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender = 20, keyExtractor, maxToRenderPerBatch = 50, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, refreshing, removeClippedSubviews = false, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize = 100, ...props }) => {
681
681
  const scrollView = useRef4(null);
682
682
  const isHorizontal = horizontal === true;
683
683
  const showDots = isHorizontal && pagingEnabled === true && (data?.length ?? 0) > 1;
@@ -776,7 +776,7 @@ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentCont
776
776
  },
777
777
  [onContentSizeChange]
778
778
  );
779
- const refreshControl = useMemo3(() => /* @__PURE__ */ jsx5(RefreshControl, {}), []);
779
+ const refreshControl = useMemo3(() => onRefresh ? /* @__PURE__ */ jsx5(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ jsx5(RefreshControl, {}), [onRefresh, refreshing]);
780
780
  const nativeGesture = useMemo3(() => Gesture2.Native(), []);
781
781
  const combinedGesture = useMemo3(() => gesture !== void 0 ? Gesture2.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
782
782
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
@@ -1141,11 +1141,11 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionAc
1141
1141
  };
1142
1142
  var styles7 = StyleSheet6.create({
1143
1143
  actionBg: { borderRadius: 100, overflow: "hidden" },
1144
- divider: { bottom: 0, height: StyleSheet6.hairlineWidth, left: 0, position: "absolute", right: 0 },
1145
- caption: { fontSize: 12, fontWeight: "400", textAlign: "center" },
1146
- content: { alignItems: "center", flexDirection: "row", width: "100%" },
1147
1144
  blur: { left: 0, overflow: "hidden", position: "absolute", right: 0, top: 0, zIndex: 2 },
1148
1145
  blurInner: { left: 0, position: "absolute", right: 0, top: 0 },
1146
+ caption: { fontSize: 12, fontWeight: "400", textAlign: "center" },
1147
+ content: { alignItems: "center", flexDirection: "row", width: "100%" },
1148
+ divider: { bottom: 0, height: StyleSheet6.hairlineWidth, left: 0, position: "absolute", right: 0 },
1149
1149
  header: { left: 0, overflow: "hidden", position: "absolute", right: 0, top: 0, zIndex: 2 },
1150
1150
  headerInit: { left: 0, overflow: "hidden", right: 0, zIndex: 2 },
1151
1151
  progress: { left: 0, position: "absolute", right: 0, zIndex: 3 },
@@ -1158,7 +1158,7 @@ var styles7 = StyleSheet6.create({
1158
1158
  // src/ScrollViewProvider.tsx
1159
1159
  import { useBlur } from "@rific/auto-paper";
1160
1160
  import { useCallback as useCallback7, useContext as useContext12, useEffect as useEffect5, useMemo as useMemo5, useRef as useRef8, useState as useState5 } from "react";
1161
- import { Dimensions as Dimensions3 } from "react-native";
1161
+ import { Dimensions as Dimensions3, Platform as Platform5 } from "react-native";
1162
1162
  import { useSharedValue as useSharedValue7 } from "react-native-reanimated";
1163
1163
  import { jsx as jsx10 } from "react/jsx-runtime";
1164
1164
  var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard = false, footerFixed, headerFixed, snapBack, snapBackFooter, snapBackHeader, tabBarHeight = 0 }) => {
@@ -1206,6 +1206,17 @@ var ScrollViewProvider = ({ blur, children, fixed = false, footerAboveKeyboard =
1206
1206
  }, 3e3);
1207
1207
  return () => clearTimeout(timeout);
1208
1208
  }, [headerHeight]);
1209
+ useEffect5(() => {
1210
+ if (Platform5.OS !== "web" || headerHeight !== null) return;
1211
+ let raf2 = 0;
1212
+ const raf1 = requestAnimationFrame(() => {
1213
+ raf2 = requestAnimationFrame(() => setHeaderHeight(0));
1214
+ });
1215
+ return () => {
1216
+ cancelAnimationFrame(raf1);
1217
+ cancelAnimationFrame(raf2);
1218
+ };
1219
+ }, [headerHeight, setHeaderHeight]);
1209
1220
  const setFooterHeight = useCallback7(
1210
1221
  (h) => {
1211
1222
  if (h === 0 && footerHeight) return;
@@ -1245,7 +1256,7 @@ var ScrollViewSettingsProvider = ({ children, onChange, initialValue }) => {
1245
1256
 
1246
1257
  // src/SectionList.tsx
1247
1258
  import { memo as memo4, useCallback as useCallback10, useContext as useContext14, useEffect as useEffect6, useLayoutEffect as useLayoutEffect6, useMemo as useMemo7, useRef as useRef10 } from "react";
1248
- import { Dimensions as Dimensions4, Platform as Platform5, SectionList as RNSectionList, View as View7 } from "react-native";
1259
+ import { Dimensions as Dimensions4, Platform as Platform6, SectionList as RNSectionList, View as View7 } from "react-native";
1249
1260
  import { Gesture as Gesture4, GestureDetector as GestureDetector4 } from "react-native-gesture-handler";
1250
1261
  import Animated7, { runOnUI as runOnUI3, useSharedValue as useSharedValue9 } from "react-native-reanimated";
1251
1262
 
@@ -1374,7 +1385,7 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
1374
1385
  const capturedGeneration = useSharedValue9(-1);
1375
1386
  useLayoutEffect6(() => {
1376
1387
  const generation = listGeneration.value;
1377
- if (Platform5.OS === "web") {
1388
+ if (Platform6.OS === "web") {
1378
1389
  capturedGeneration.value = generation;
1379
1390
  return;
1380
1391
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/scroll-view",
3
- "version": "0.6.7",
3
+ "version": "0.7.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",
@@ -61,9 +61,9 @@
61
61
  },
62
62
  "prettier": "@infinitetoken/eslint-config/prettier",
63
63
  "devDependencies": {
64
- "@infinitetoken/eslint-config": "^0.1.8",
65
- "@infinitetoken/jest-config": "^0.2.0",
66
- "@infinitetoken/tsconfig": "^0.3.0",
64
+ "@infinitetoken/eslint-config": "0.2.0",
65
+ "@infinitetoken/jest-config": "^0.3.0",
66
+ "@infinitetoken/tsconfig": "^0.4.1",
67
67
  "@reduxjs/toolkit": "^2.12.0",
68
68
  "@rific/auto-paper": "^0.10.3",
69
69
  "@testing-library/dom": "^10.4.1",
@@ -27,11 +27,12 @@ export type CustomListProps<P extends object> = Omit<P, 'contentInset' | 'conten
27
27
  onScrollBeginDrag?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void
28
28
  onScrollEndDrag?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void
29
29
  pullSearchHeight?: number
30
+ refreshing?: boolean
30
31
  scrollRef?: RefObject<ScrollToOffsetRef | null>
31
32
  style?: StyleProp<ViewStyle>
32
33
  }
33
34
 
34
- const CustomListInner = <P extends object>({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollRef, style, ...props }: CustomListProps<P>) => {
35
+ const CustomListInner = <P extends object>({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, scrollRef, style, ...props }: CustomListProps<P>) => {
35
36
  // Intercept ListHeaderComponent so useScrollInit can apply the same 2-phase measurement as FlatList.
36
37
  const { ListHeaderComponent: listHeaderComponent, contentContainerStyle: externalContentContainerStyle, ...restProps } = props as Record<string, unknown>
37
38
 
@@ -123,7 +124,7 @@ const CustomListInner = <P extends object>({ chipProps, chipThreshold, component
123
124
  scrollViewInternal.current?.scrollToOffset({ offset, animated: true })
124
125
  }, [contentInset.top, pullSearchHeight])
125
126
 
126
- const refreshControl = useMemo(() => <RefreshControl />, [])
127
+ const refreshControl = useMemo(() => (onRefresh ? <RefreshControl onRefresh={onRefresh} refreshing={refreshing ?? false} /> : <RefreshControl />), [onRefresh, refreshing])
127
128
  const nativeGesture = useMemo(() => Gesture.Native(), [])
128
129
  const combinedGesture = useMemo(() => (gesture !== undefined ? Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture), [gesture, nativeGesture])
129
130
  const detectorGesture = gesture !== undefined ? combinedGesture : undefined
package/src/FlatList.tsx CHANGED
@@ -25,9 +25,10 @@ export type FlatListProps<T> = Omit<RNFlatListProps<T>, 'onRefresh' | 'refreshin
25
25
  onScrollEndDrag?: (e: NativeSyntheticEvent<NativeScrollEvent>) => void
26
26
  pullSearchHeight?: number
27
27
  ref?: RefObject<RNFlatList | null>
28
+ refreshing?: boolean
28
29
  }
29
30
 
30
- const FlatListInner = <T,>({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender = 20, keyExtractor, maxToRenderPerBatch = 50, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, removeClippedSubviews = false, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize = 100, ...props }: FlatListProps<T>) => {
31
+ const FlatListInner = <T,>({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender = 20, keyExtractor, maxToRenderPerBatch = 50, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, refreshing, removeClippedSubviews = false, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize = 100, ...props }: FlatListProps<T>) => {
31
32
  // Plain (non-Animated) FlatList: react-native-reanimated's Animated.FlatList does not reliably
32
33
  // honor the initial `contentOffset` prop on a key-forced remount — the header/list can briefly
33
34
  // render at the wrong position. A plain FlatList honors it correctly, so scroll position is driven
@@ -164,7 +165,7 @@ const FlatListInner = <T,>({ chipProps, chipThreshold, columnWrapperStyle, conte
164
165
  [onContentSizeChange]
165
166
  )
166
167
 
167
- const refreshControl = useMemo(() => <RefreshControl />, [])
168
+ const refreshControl = useMemo(() => (onRefresh ? <RefreshControl onRefresh={onRefresh} refreshing={refreshing ?? false} /> : <RefreshControl />), [onRefresh, refreshing])
168
169
  const nativeGesture = useMemo(() => Gesture.Native(), [])
169
170
  const combinedGesture = useMemo(() => (gesture !== undefined ? Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture), [gesture, nativeGesture])
170
171
  const detectorGesture = gesture !== undefined ? combinedGesture : undefined
@@ -180,11 +180,11 @@ export const ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, bac
180
180
 
181
181
  const styles = StyleSheet.create({
182
182
  actionBg: { borderRadius: 100, overflow: 'hidden' },
183
- divider: { bottom: 0, height: StyleSheet.hairlineWidth, left: 0, position: 'absolute', right: 0 },
184
- caption: { fontSize: 12, fontWeight: '400', textAlign: 'center' },
185
- content: { alignItems: 'center', flexDirection: 'row', width: '100%' },
186
183
  blur: { left: 0, overflow: 'hidden', position: 'absolute', right: 0, top: 0, zIndex: 2 },
187
184
  blurInner: { left: 0, position: 'absolute', right: 0, top: 0 },
185
+ caption: { fontSize: 12, fontWeight: '400', textAlign: 'center' },
186
+ content: { alignItems: 'center', flexDirection: 'row', width: '100%' },
187
+ divider: { bottom: 0, height: StyleSheet.hairlineWidth, left: 0, position: 'absolute', right: 0 },
188
188
  header: { left: 0, overflow: 'hidden', position: 'absolute', right: 0, top: 0, zIndex: 2 },
189
189
  headerInit: { left: 0, overflow: 'hidden', right: 0, zIndex: 2 },
190
190
  progress: { left: 0, position: 'absolute', right: 0, zIndex: 3 },
@@ -1,6 +1,6 @@
1
1
  import { useBlur } from '@rific/auto-paper'
2
2
  import { type ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
3
- import { Dimensions } from 'react-native'
3
+ import { Dimensions, Platform } from 'react-native'
4
4
  import { useSharedValue } from 'react-native-reanimated'
5
5
 
6
6
  import { ScrollViewContext } from './ScrollViewContext'
@@ -70,6 +70,25 @@ export const ScrollViewProvider = ({ blur, children, fixed = false, footerAboveK
70
70
  }, 3000)
71
71
  return () => clearTimeout(timeout)
72
72
  }, [headerHeight])
73
+ // Web-only recovery for a real race: a header's ResizeObserver-backed onLayout can simply never
74
+ // fire on the very first mount through Expo Router (observed in practice, not just theorized —
75
+ // content stuck at opacity: 0 indefinitely, with no error, only escaping via an unrelated resize
76
+ // forcing a fresh layout pass). Native RN doesn't have this race — onLayout is reliable there, and
77
+ // a persistently-null headerHeight really does mean "no header was ever rendered," which the
78
+ // warning above should keep flagging. On web, give a real first layout two frames to arrive before
79
+ // assuming there isn't one; setHeaderHeight(0) is exactly what a genuinely header-less screen would
80
+ // report anyway, so this is indistinguishable from that case once it fires.
81
+ useEffect(() => {
82
+ if (Platform.OS !== 'web' || headerHeight !== null) return
83
+ let raf2 = 0
84
+ const raf1 = requestAnimationFrame(() => {
85
+ raf2 = requestAnimationFrame(() => setHeaderHeight(0))
86
+ })
87
+ return () => {
88
+ cancelAnimationFrame(raf1)
89
+ cancelAnimationFrame(raf2)
90
+ }
91
+ }, [headerHeight, setHeaderHeight])
73
92
  const setFooterHeight = useCallback(
74
93
  (h: number | null) => {
75
94
  // Same race setHeaderHeight guards against, on the footer side: react-native-screens' web