@rific/scroll-view 0.4.1 → 0.4.2
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 +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +90 -72
- package/dist/index.mjs +46 -28
- package/package.json +2 -2
- package/src/CustomList.tsx +2 -2
- package/src/FlatList.tsx +2 -2
- package/src/ScrollView.tsx +3 -2
- package/src/SectionList.tsx +2 -2
- package/src/internal/insetMode.ts +8 -0
- package/src/internal/useScrollHandler.ts +10 -6
- package/src/internal/useScrollHandlerJS.ts +10 -6
- package/src/internal/useScrollList.ts +14 -4
- package/src/useKeyboardInset.ts +8 -1
- package/src/useScrollInit.ts +9 -3
package/dist/index.d.mts
CHANGED
|
@@ -116,7 +116,7 @@ type ScrollViewProps = ScrollViewProps$1 & {
|
|
|
116
116
|
ref?: RefObject<ScrollView$1 | null>;
|
|
117
117
|
refreshing?: boolean;
|
|
118
118
|
};
|
|
119
|
-
declare const ScrollViewInner: ({ chipProps, chipThreshold, children, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }: ScrollViewProps) => react.JSX.Element;
|
|
119
|
+
declare const ScrollViewInner: ({ chipProps, chipThreshold, children, contentContainerStyle: externalContentContainerStyle, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }: ScrollViewProps) => react.JSX.Element;
|
|
120
120
|
declare const ScrollView: typeof ScrollViewInner;
|
|
121
121
|
|
|
122
122
|
type ScrollViewContextType = {
|
package/dist/index.d.ts
CHANGED
|
@@ -116,7 +116,7 @@ type ScrollViewProps = ScrollViewProps$1 & {
|
|
|
116
116
|
ref?: RefObject<ScrollView$1 | null>;
|
|
117
117
|
refreshing?: boolean;
|
|
118
118
|
};
|
|
119
|
-
declare const ScrollViewInner: ({ chipProps, chipThreshold, children, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }: ScrollViewProps) => react.JSX.Element;
|
|
119
|
+
declare const ScrollViewInner: ({ chipProps, chipThreshold, children, contentContainerStyle: externalContentContainerStyle, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }: ScrollViewProps) => react.JSX.Element;
|
|
120
120
|
declare const ScrollView: typeof ScrollViewInner;
|
|
121
121
|
|
|
122
122
|
type ScrollViewContextType = {
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
52
52
|
|
|
53
53
|
// src/CustomList.tsx
|
|
54
54
|
var import_react6 = require("react");
|
|
55
|
-
var
|
|
55
|
+
var import_react_native6 = require("react-native");
|
|
56
56
|
var import_react_native_gesture_handler = require("react-native-gesture-handler");
|
|
57
57
|
var import_react_native_reanimated6 = require("react-native-reanimated");
|
|
58
58
|
|
|
@@ -122,6 +122,12 @@ var ScrollViewContext = (0, import_react.createContext)({
|
|
|
122
122
|
// src/useScrollInit.ts
|
|
123
123
|
var import_react2 = require("react");
|
|
124
124
|
var import_react_native_reanimated2 = require("react-native-reanimated");
|
|
125
|
+
|
|
126
|
+
// src/internal/insetMode.ts
|
|
127
|
+
var import_react_native3 = require("react-native");
|
|
128
|
+
var usesContentInset = import_react_native3.Platform.OS === "ios";
|
|
129
|
+
|
|
130
|
+
// src/useScrollInit.ts
|
|
125
131
|
var REMOUNT_SYNC_MAX_ATTEMPTS = 8;
|
|
126
132
|
var REMOUNT_SYNC_TOLERANCE = 1;
|
|
127
133
|
var REMOUNT_SYNC_FALLBACK_MS = 500;
|
|
@@ -130,8 +136,9 @@ var SNAP_VERIFY_TOLERANCE = 1;
|
|
|
130
136
|
var SNAP_VERIFY_MAX_ATTEMPTS = 5;
|
|
131
137
|
var SNAP_VERIFY_INITIAL_DELAY_MS = 400;
|
|
132
138
|
var SNAP_VERIFY_RETRY_DELAY_MS = 150;
|
|
133
|
-
function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollTo }) {
|
|
139
|
+
function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight: pullSearchHeightProp, scrollTo }) {
|
|
134
140
|
const { headerHeight, scrollPosition, setProgressing } = (0, import_react2.useContext)(ScrollViewContext);
|
|
141
|
+
const pullSearchHeight = usesContentInset ? pullSearchHeightProp : void 0;
|
|
135
142
|
const hasPullSearch = pullSearchHeight !== void 0;
|
|
136
143
|
const [phase, setPhase] = (0, import_react2.useState)(hasPullSearch && !headerHeight ? "measuring" : "ready");
|
|
137
144
|
const [refreshing, setRefreshing] = (0, import_react2.useState)(false);
|
|
@@ -173,7 +180,7 @@ function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomen
|
|
|
173
180
|
},
|
|
174
181
|
[]
|
|
175
182
|
);
|
|
176
|
-
const [remountTarget] = (0, import_react2.useState)(() => headerHeight !== null ? -headerHeight + (pullSearchHeight ?? 0) : null);
|
|
183
|
+
const [remountTarget] = (0, import_react2.useState)(() => usesContentInset && headerHeight !== null ? -headerHeight + (pullSearchHeight ?? 0) : null);
|
|
177
184
|
const remountSyncTarget = (0, import_react_native_reanimated2.useSharedValue)(remountTarget);
|
|
178
185
|
const [scrollReady, setScrollReady] = (0, import_react2.useState)(remountTarget === null);
|
|
179
186
|
const remountAttempts = (0, import_react2.useRef)(0);
|
|
@@ -218,7 +225,7 @@ function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomen
|
|
|
218
225
|
headerInitialized.current = true;
|
|
219
226
|
const target = remountTarget ?? -headerHeight;
|
|
220
227
|
scrollPosition.value = target;
|
|
221
|
-
scrollTo(target, false);
|
|
228
|
+
scrollTo(usesContentInset ? target : 0, false);
|
|
222
229
|
if (remountTarget === null) return;
|
|
223
230
|
let framesLeft = REMOUNT_PRIME_FRAMES;
|
|
224
231
|
let handle = null;
|
|
@@ -325,9 +332,10 @@ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100,
|
|
|
325
332
|
chipHidden.value = x < chipThreshold ? 1 : 0;
|
|
326
333
|
return;
|
|
327
334
|
}
|
|
328
|
-
const
|
|
329
|
-
scrollPosition.value
|
|
330
|
-
|
|
335
|
+
const yn = usesContentInset ? y : y - headerHeightShared.value;
|
|
336
|
+
const delta = yn - scrollPosition.value;
|
|
337
|
+
scrollPosition.value = yn;
|
|
338
|
+
chipHidden.value = yn < chipThreshold ? 1 : 0;
|
|
331
339
|
const maxScroll = contentHeight - layoutHeight;
|
|
332
340
|
if (maxScroll > 0 && y >= maxScroll) {
|
|
333
341
|
fromBottomBounce.value = true;
|
|
@@ -338,18 +346,18 @@ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100,
|
|
|
338
346
|
const snapHeader = snapBackHeaderShared.value && !headerFixed;
|
|
339
347
|
const snapFooter = snapBackFooterShared.value && !footerFixed;
|
|
340
348
|
if (snapHeader || snapFooter) {
|
|
341
|
-
if (
|
|
349
|
+
if (yn <= -headerHeightShared.value) {
|
|
342
350
|
snapUpAccum.value = 0;
|
|
343
351
|
if (snapHeader) headerOffset.value = 0;
|
|
344
352
|
if (snapFooter) footerOffset.value = 0;
|
|
345
353
|
} else if (delta > 0) {
|
|
346
354
|
snapUpAccum.value = 0;
|
|
347
|
-
if (
|
|
355
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) {
|
|
348
356
|
if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta));
|
|
349
357
|
if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta));
|
|
350
358
|
}
|
|
351
359
|
} else if (delta < 0 && !fromBottomBounce.value) {
|
|
352
|
-
if (
|
|
360
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.value -= delta;
|
|
353
361
|
if (snapUpAccum.value >= 10) {
|
|
354
362
|
snapUpAccum.value = 999;
|
|
355
363
|
if (snapHeader) headerOffset.value = (0, import_react_native_reanimated3.withTiming)(0, { duration: 200 });
|
|
@@ -383,15 +391,16 @@ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100,
|
|
|
383
391
|
|
|
384
392
|
// src/internal/useScrollList.ts
|
|
385
393
|
var import_react5 = require("react");
|
|
386
|
-
var
|
|
394
|
+
var import_react_native5 = require("react-native");
|
|
387
395
|
var import_react_native_reanimated5 = require("react-native-reanimated");
|
|
388
396
|
var import_react_native_safe_area_context = require("react-native-safe-area-context");
|
|
389
397
|
|
|
390
398
|
// src/useKeyboardInset.ts
|
|
391
399
|
var import_react4 = require("react");
|
|
400
|
+
var import_react_native4 = require("react-native");
|
|
392
401
|
var import_react_native_keyboard_controller = require("react-native-keyboard-controller");
|
|
393
402
|
var import_react_native_reanimated4 = require("react-native-reanimated");
|
|
394
|
-
var
|
|
403
|
+
var useKeyboardInsetNative = () => {
|
|
395
404
|
const [keyboardHeight, setKeyboardHeight] = (0, import_react4.useState)(0);
|
|
396
405
|
(0, import_react_native_keyboard_controller.useKeyboardHandler)(
|
|
397
406
|
{
|
|
@@ -408,8 +417,12 @@ var useKeyboardInset = () => {
|
|
|
408
417
|
);
|
|
409
418
|
return keyboardHeight;
|
|
410
419
|
};
|
|
420
|
+
var useKeyboardInsetWeb = () => 0;
|
|
421
|
+
var useKeyboardInset = import_react_native4.Platform.OS === "web" ? useKeyboardInsetWeb : useKeyboardInsetNative;
|
|
411
422
|
|
|
412
423
|
// src/internal/useScrollList.ts
|
|
424
|
+
var ZERO_INSET = { bottom: 0, top: 0 };
|
|
425
|
+
var ZERO_OFFSET = { x: 0, y: 0 };
|
|
413
426
|
var CHIP_SLIDE = 48;
|
|
414
427
|
function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style } = {}) {
|
|
415
428
|
const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
|
|
@@ -418,12 +431,15 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
|
|
|
418
431
|
const headerFixed = isHorizontal ? true : headerFixedProp ?? contextHeaderFixed;
|
|
419
432
|
const footerFixed = isHorizontal ? true : footerFixedProp ?? contextFooterFixed;
|
|
420
433
|
(0, import_react5.useEffect)(() => {
|
|
421
|
-
pullSearchHeightShared.value = pullSearchHeight ?? 0;
|
|
434
|
+
pullSearchHeightShared.value = usesContentInset ? pullSearchHeight ?? 0 : 0;
|
|
422
435
|
}, [pullSearchHeight, pullSearchHeightShared]);
|
|
423
|
-
const containerStyle = (0, import_react5.useMemo)(() => headerHeight === null ? [{ flex: 1, opacity: 0 }, style] : [
|
|
424
|
-
const
|
|
436
|
+
const containerStyle = (0, import_react5.useMemo)(() => headerHeight === null ? [{ flex: 1, opacity: 0 }, style] : [import_react_native5.StyleSheet.absoluteFill, style], [headerHeight, style]);
|
|
437
|
+
const insetGeometry = (0, import_react5.useMemo)(() => ({ bottom: (footerFixed ? footerHeight || insets.bottom : insets.bottom) + (keyboardAware ? keyboardHeight : 0), top: headerHeight ?? 0 }), [footerFixed, footerHeight, insets.bottom, headerHeight, keyboardAware, keyboardHeight]);
|
|
438
|
+
const contentInset = usesContentInset ? insetGeometry : ZERO_INSET;
|
|
439
|
+
const contentPadding = (0, import_react5.useMemo)(() => usesContentInset || isHorizontal ? null : { paddingBottom: insetGeometry.bottom, paddingTop: insetGeometry.top }, [insetGeometry, isHorizontal]);
|
|
425
440
|
const [startedWithHeader] = (0, import_react5.useState)(headerHeight !== null);
|
|
426
|
-
const
|
|
441
|
+
const insetContentOffset = (0, import_react5.useMemo)(() => ({ x: 0, y: headerHeight !== null ? -headerHeight + (startedWithHeader && !isHorizontal && pullSearchHeight ? pullSearchHeight : 0) : 0 }), [headerHeight, isHorizontal, pullSearchHeight, startedWithHeader]);
|
|
442
|
+
const contentOffset = usesContentInset ? insetContentOffset : ZERO_OFFSET;
|
|
427
443
|
const chipHidden = (0, import_react_native_reanimated5.useSharedValue)(1);
|
|
428
444
|
const chipStyle = (0, import_react_native_reanimated5.useAnimatedStyle)(() => {
|
|
429
445
|
const pointerEvents = chipHidden.value ? "none" : "box-none";
|
|
@@ -445,14 +461,14 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
|
|
|
445
461
|
transform: [{ translateY: chipHidden.value ? (0, import_react_native_reanimated5.withTiming)(-CHIP_SLIDE) : (0, import_react_native_reanimated5.withTiming)(0) }]
|
|
446
462
|
};
|
|
447
463
|
}, [isHorizontal, headerFixed, headerHeight, insets.top]);
|
|
448
|
-
return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false };
|
|
464
|
+
return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false };
|
|
449
465
|
}
|
|
450
466
|
|
|
451
467
|
// src/CustomList.tsx
|
|
452
468
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
453
469
|
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 }) => {
|
|
454
470
|
const { ListHeaderComponent: listHeaderComponent, contentContainerStyle: externalContentContainerStyle, ...restProps } = props;
|
|
455
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
|
|
471
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
|
|
456
472
|
const { listGeneration, onListUnmount } = (0, import_react6.useContext)(ScrollViewContext);
|
|
457
473
|
const capturedGeneration = (0, import_react_native_reanimated6.useSharedValue)(-1);
|
|
458
474
|
(0, import_react6.useLayoutEffect)(() => {
|
|
@@ -501,7 +517,7 @@ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilter
|
|
|
501
517
|
renderFilters
|
|
502
518
|
] });
|
|
503
519
|
}, [pullSearchHeader, renderFilters]);
|
|
504
|
-
const contentContainerStyle = (0, import_react6.useMemo)(() => [{ minHeight:
|
|
520
|
+
const contentContainerStyle = (0, import_react6.useMemo)(() => [{ minHeight: import_react_native6.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight]);
|
|
505
521
|
const onScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget });
|
|
506
522
|
const handleScrollToTop = (0, import_react6.useCallback)(() => {
|
|
507
523
|
const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
|
|
@@ -512,9 +528,9 @@ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilter
|
|
|
512
528
|
const combinedGesture = (0, import_react6.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
|
|
513
529
|
const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
|
|
514
530
|
const TypedList = List;
|
|
515
|
-
const content = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
531
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native6.View, { style: containerStyle, children: [
|
|
516
532
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TypedList, { ...restProps, contentContainerStyle, contentInset, contentOffset, ListHeaderComponent: activeListHeader, onMomentumScrollEnd: handleMomentumScrollEnd, onScroll, onScrollBeginDrag: handleScrollBeginDrag, onScrollEndDrag: handleScrollEndDrag, ref: handleRef, refreshControl, scrollEventThrottle: 16 }),
|
|
517
|
-
hiddenHeader && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
533
|
+
hiddenHeader && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native6.View, { pointerEvents: "none", style: styles2.measureContainer, children: hiddenHeader }),
|
|
518
534
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ScrollViewChip, { chipProps, onChipPress, onPress: handleScrollToTop, style: chipStyle })
|
|
519
535
|
] });
|
|
520
536
|
return detectorGesture ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_gesture_handler.GestureDetector, { gesture: detectorGesture, children: content }) : content;
|
|
@@ -524,13 +540,13 @@ var CustomList = (0, import_react6.memo)(CustomListInner);
|
|
|
524
540
|
|
|
525
541
|
// src/FlatList.tsx
|
|
526
542
|
var import_react9 = require("react");
|
|
527
|
-
var
|
|
543
|
+
var import_react_native8 = require("react-native");
|
|
528
544
|
var import_react_native_gesture_handler2 = require("react-native-gesture-handler");
|
|
529
545
|
|
|
530
546
|
// src/internal/HorizontalDots.tsx
|
|
531
547
|
var import_auto_paper2 = require("@rific/auto-paper");
|
|
532
548
|
var import_react7 = require("react");
|
|
533
|
-
var
|
|
549
|
+
var import_react_native7 = require("react-native");
|
|
534
550
|
var import_react_native_paper = require("react-native-paper");
|
|
535
551
|
var import_react_native_reanimated7 = __toESM(require("react-native-reanimated"));
|
|
536
552
|
var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
|
|
@@ -555,11 +571,11 @@ var Dot = ({ activeColor, index, inactiveColor, pageWidth, scrollPosition }) =>
|
|
|
555
571
|
const backgroundColor = (0, import_react_native_reanimated7.interpolateColor)(t, [0, 1], [activeColor, inactiveColor]);
|
|
556
572
|
return { backgroundColor, borderRadius: size / 2, height: size, width: size };
|
|
557
573
|
});
|
|
558
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
574
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native7.View, { style: styles3.dotContainer, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated7.default.View, { style: circleStyle }) });
|
|
559
575
|
};
|
|
560
576
|
var HorizontalDots = ({ total }) => {
|
|
561
577
|
const { blur, footerHeightShared, scrollPosition } = (0, import_react7.useContext)(ScrollViewContext);
|
|
562
|
-
const { width } = (0,
|
|
578
|
+
const { width } = (0, import_react_native7.useWindowDimensions)();
|
|
563
579
|
const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
|
|
564
580
|
const theme = (0, import_react_native_paper.useTheme)();
|
|
565
581
|
const insetsBottom = insets.bottom;
|
|
@@ -574,12 +590,12 @@ var HorizontalDots = ({ total }) => {
|
|
|
574
590
|
const offset = Math.max(0, Math.min((page - (CLIP_COUNT - 1) / 2) * DOT_STEP, maxOffset));
|
|
575
591
|
return { transform: [{ translateX: -offset }] };
|
|
576
592
|
});
|
|
577
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated7.default.View, { pointerEvents: "none", style: [styles3.pillContainer, pillContainerStyle], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
578
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_auto_paper2.BlurView, { blur, style:
|
|
579
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
593
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated7.default.View, { pointerEvents: "none", style: [styles3.pillContainer, pillContainerStyle], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native7.View, { style: styles3.pill, children: [
|
|
594
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_auto_paper2.BlurView, { blur, style: import_react_native7.StyleSheet.absoluteFill }),
|
|
595
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native7.View, { style: styles3.dotsClip, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated7.default.View, { style: [styles3.dotsRow, rowStyle], children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dot, { activeColor, index: i, inactiveColor, pageWidth: width, scrollPosition }, i)) }) })
|
|
580
596
|
] }) });
|
|
581
597
|
};
|
|
582
|
-
var styles3 =
|
|
598
|
+
var styles3 = import_react_native7.StyleSheet.create({
|
|
583
599
|
dotContainer: { alignItems: "center", height: DOT_FULL, justifyContent: "center", width: DOT_FULL },
|
|
584
600
|
dotsClip: { overflow: "hidden", width: CLIP_WIDTH },
|
|
585
601
|
dotsRow: { flexDirection: "row", gap: DOT_GAP },
|
|
@@ -632,9 +648,10 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
|
|
|
632
648
|
chipHidden.value = x < chipThreshold ? 1 : 0;
|
|
633
649
|
return;
|
|
634
650
|
}
|
|
635
|
-
const
|
|
636
|
-
scrollPosition.value
|
|
637
|
-
|
|
651
|
+
const yn = usesContentInset ? y : y - headerHeightShared.value;
|
|
652
|
+
const delta = yn - scrollPosition.value;
|
|
653
|
+
scrollPosition.value = yn;
|
|
654
|
+
chipHidden.value = yn < chipThreshold ? 1 : 0;
|
|
638
655
|
const maxScroll = contentHeight - layoutHeight;
|
|
639
656
|
if (maxScroll > 0 && y >= maxScroll) {
|
|
640
657
|
fromBottomBounce.current = true;
|
|
@@ -645,18 +662,18 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
|
|
|
645
662
|
const snapHeader = snapBackHeaderShared.value && !headerFixed;
|
|
646
663
|
const snapFooter = snapBackFooterShared.value && !footerFixed;
|
|
647
664
|
if (snapHeader || snapFooter) {
|
|
648
|
-
if (
|
|
665
|
+
if (yn <= -headerHeightShared.value) {
|
|
649
666
|
snapUpAccum.current = 0;
|
|
650
667
|
if (snapHeader) headerOffset.value = 0;
|
|
651
668
|
if (snapFooter) footerOffset.value = 0;
|
|
652
669
|
} else if (delta > 0) {
|
|
653
670
|
snapUpAccum.current = 0;
|
|
654
|
-
if (
|
|
671
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) {
|
|
655
672
|
if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta));
|
|
656
673
|
if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta));
|
|
657
674
|
}
|
|
658
675
|
} else if (delta < 0 && !fromBottomBounce.current) {
|
|
659
|
-
if (
|
|
676
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.current -= delta;
|
|
660
677
|
if (snapUpAccum.current >= 10) {
|
|
661
678
|
snapUpAccum.current = 999;
|
|
662
679
|
if (snapHeader) headerOffset.value = (0, import_react_native_reanimated8.withTiming)(0, { duration: 200 });
|
|
@@ -720,11 +737,11 @@ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentCont
|
|
|
720
737
|
(info) => {
|
|
721
738
|
if (cols <= 1) return renderItem ? renderItem(info) : null;
|
|
722
739
|
const row = info.item;
|
|
723
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
740
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native8.View, { style: [styles4.row, columnWrapperStyle], children: row.map((item, kk) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react9.Fragment, { children: renderItem ? renderItem({ index: info.index * cols + kk, item, separators: info.separators }) : null }, kk)) });
|
|
724
741
|
},
|
|
725
742
|
[cols, columnWrapperStyle, renderItem]
|
|
726
743
|
);
|
|
727
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
|
|
744
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
|
|
728
745
|
const { jsListGeneration, onJsListUnmount } = (0, import_react9.useContext)(ScrollViewContext);
|
|
729
746
|
const capturedGeneration = (0, import_react9.useRef)(jsListGeneration.current);
|
|
730
747
|
(0, import_react9.useLayoutEffect)(() => {
|
|
@@ -759,7 +776,7 @@ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentCont
|
|
|
759
776
|
renderFilters
|
|
760
777
|
] });
|
|
761
778
|
}, [pullSearchHeader, renderFilters]);
|
|
762
|
-
const contentContainerStyle = (0, import_react9.useMemo)(() => isHorizontal ? externalContentContainerStyle : [{ minHeight:
|
|
779
|
+
const contentContainerStyle = (0, import_react9.useMemo)(() => isHorizontal ? externalContentContainerStyle : [{ minHeight: import_react_native8.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [isHorizontal, contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight]);
|
|
763
780
|
const scrollHandler = useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, isHorizontal, jsListGeneration, remountTarget, scrollTo });
|
|
764
781
|
const onScrollBeginDrag = (0, import_react9.useCallback)(
|
|
765
782
|
(e) => {
|
|
@@ -800,9 +817,9 @@ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentCont
|
|
|
800
817
|
const nativeGesture = (0, import_react9.useMemo)(() => import_react_native_gesture_handler2.Gesture.Native(), []);
|
|
801
818
|
const combinedGesture = (0, import_react9.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler2.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
|
|
802
819
|
const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
|
|
803
|
-
const content = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
820
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native8.View, { style: containerStyle, children: [
|
|
804
821
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
805
|
-
|
|
822
|
+
import_react_native8.FlatList,
|
|
806
823
|
{
|
|
807
824
|
...props,
|
|
808
825
|
contentContainerStyle,
|
|
@@ -835,7 +852,7 @@ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentCont
|
|
|
835
852
|
windowSize
|
|
836
853
|
}
|
|
837
854
|
),
|
|
838
|
-
hiddenHeader && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
855
|
+
hiddenHeader && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native8.View, { pointerEvents: "none", style: styles4.measureContainer, children: hiddenHeader }),
|
|
839
856
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ScrollViewChip, { chipProps, isHorizontal, onChipPress, onPress: handleScrollToTop, style: chipStyle }),
|
|
840
857
|
showDots && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HorizontalDots, { total: data?.length ?? 0 })
|
|
841
858
|
] });
|
|
@@ -850,7 +867,7 @@ var FlatList = (0, import_react9.memo)(FlatListInner);
|
|
|
850
867
|
|
|
851
868
|
// src/PullSearch.tsx
|
|
852
869
|
var import_react10 = require("react");
|
|
853
|
-
var
|
|
870
|
+
var import_react_native9 = require("react-native");
|
|
854
871
|
var import_react_native_paper2 = require("react-native-paper");
|
|
855
872
|
var import_react_native_reanimated9 = __toESM(require("react-native-reanimated"));
|
|
856
873
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
@@ -913,7 +930,7 @@ var PullSearch = (0, import_react10.forwardRef)(function PullSearch2({ debounce,
|
|
|
913
930
|
}, [barHeight, headerHeight]);
|
|
914
931
|
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_paper2.Searchbar, { autoCorrect: false, onChangeText: (text) => setValue((text || "").trimStart()), onClearIconPress: handleClear, placeholder, placeholderTextColor: theme.colors.onSurfaceVariant, ref: inputRef, spellCheck: false, style: styles5.input, value }) });
|
|
915
932
|
});
|
|
916
|
-
var styles5 =
|
|
933
|
+
var styles5 = import_react_native9.StyleSheet.create({
|
|
917
934
|
container: { paddingVertical: 8 },
|
|
918
935
|
input: { margin: 0 }
|
|
919
936
|
});
|
|
@@ -948,14 +965,14 @@ var scrollViewReducer = (state = defaultScrollViewSettings, action) => {
|
|
|
948
965
|
|
|
949
966
|
// src/ScrollView.tsx
|
|
950
967
|
var import_react12 = require("react");
|
|
951
|
-
var
|
|
968
|
+
var import_react_native10 = require("react-native");
|
|
952
969
|
var import_react_native_gesture_handler3 = require("react-native-gesture-handler");
|
|
953
970
|
var import_react_native_reanimated10 = __toESM(require("react-native-reanimated"));
|
|
954
971
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
955
|
-
var ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }) => {
|
|
972
|
+
var ScrollViewInner = ({ chipProps, chipThreshold, children, contentContainerStyle: externalContentContainerStyle, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }) => {
|
|
956
973
|
const scrollView = (0, import_react12.useRef)(null);
|
|
957
974
|
const isHorizontal = horizontal === true;
|
|
958
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
|
|
975
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
|
|
959
976
|
const { listGeneration, onListUnmount } = (0, import_react12.useContext)(ScrollViewContext);
|
|
960
977
|
const capturedGeneration = (0, import_react_native_reanimated10.useSharedValue)(-1);
|
|
961
978
|
(0, import_react12.useLayoutEffect)(() => {
|
|
@@ -989,11 +1006,12 @@ var ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footer
|
|
|
989
1006
|
const nativeGesture = (0, import_react12.useMemo)(() => import_react_native_gesture_handler3.Gesture.Native(), []);
|
|
990
1007
|
const combinedGesture = (0, import_react12.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler3.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
|
|
991
1008
|
const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
|
|
992
|
-
const content = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1009
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native10.View, { style: containerStyle, children: [
|
|
993
1010
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
994
1011
|
import_react_native_reanimated10.default.ScrollView,
|
|
995
1012
|
{
|
|
996
1013
|
...props,
|
|
1014
|
+
contentContainerStyle: contentPadding ? [externalContentContainerStyle, contentPadding] : externalContentContainerStyle,
|
|
997
1015
|
contentInset,
|
|
998
1016
|
contentOffset,
|
|
999
1017
|
onMomentumScrollEnd: handleMomentumScrollEnd,
|
|
@@ -1018,7 +1036,7 @@ var ScrollView = (0, import_react12.memo)(ScrollViewInner);
|
|
|
1018
1036
|
// src/ScrollViewFooter.tsx
|
|
1019
1037
|
var import_auto_paper3 = require("@rific/auto-paper");
|
|
1020
1038
|
var import_react13 = require("react");
|
|
1021
|
-
var
|
|
1039
|
+
var import_react_native11 = require("react-native");
|
|
1022
1040
|
var import_react_native_reanimated11 = __toESM(require("react-native-reanimated"));
|
|
1023
1041
|
var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
|
|
1024
1042
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
@@ -1046,11 +1064,11 @@ var ScrollViewFooter = ({ children, style }) => {
|
|
|
1046
1064
|
return { transform: [{ translateY: Math.min(effective, footerHeight) }] };
|
|
1047
1065
|
}, [footerHeight, footerFixed]);
|
|
1048
1066
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_reanimated11.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [styles6.footer, footerStyle], children: [
|
|
1049
|
-
footerHeight > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper3.BlurView, { blur, style:
|
|
1050
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1067
|
+
footerHeight > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper3.BlurView, { blur, style: import_react_native11.StyleSheet.absoluteFill }),
|
|
1068
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native11.View, { style: [styles6.row, { paddingBottom: insets.bottom }, style], children })
|
|
1051
1069
|
] });
|
|
1052
1070
|
};
|
|
1053
|
-
var styles6 =
|
|
1071
|
+
var styles6 = import_react_native11.StyleSheet.create({
|
|
1054
1072
|
footer: { bottom: 0, left: 0, position: "absolute", right: 0, zIndex: 2 },
|
|
1055
1073
|
row: { alignItems: "center", flexDirection: "row", zIndex: 1 }
|
|
1056
1074
|
});
|
|
@@ -1058,12 +1076,12 @@ var styles6 = import_react_native9.StyleSheet.create({
|
|
|
1058
1076
|
// src/ScrollViewHeader.tsx
|
|
1059
1077
|
var import_auto_paper4 = require("@rific/auto-paper");
|
|
1060
1078
|
var import_react14 = require("react");
|
|
1061
|
-
var
|
|
1079
|
+
var import_react_native12 = require("react-native");
|
|
1062
1080
|
var import_react_native_paper3 = require("react-native-paper");
|
|
1063
1081
|
var import_react_native_reanimated12 = __toESM(require("react-native-reanimated"));
|
|
1064
1082
|
var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
|
|
1065
1083
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1066
|
-
var ActionBg = ({ blur, style }) => blur ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1084
|
+
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 }) });
|
|
1067
1085
|
var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFixed, caption, centerContent, children, iconSize, style, title, topInset = true, trailingAction, trailingActionFixed = true }) => {
|
|
1068
1086
|
const { blur, headerHeight, headerFixed, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = (0, import_react14.useContext)(ScrollViewContext);
|
|
1069
1087
|
const { settings } = (0, import_react14.useContext)(ScrollViewSettingsContext);
|
|
@@ -1108,15 +1126,15 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFi
|
|
|
1108
1126
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
1109
1127
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_reanimated12.default.View, { pointerEvents: "none", style: [styles7.blur, blurStyle], children: headerHeight !== null && headerHeight > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper4.BlurView, { blur, style: [styles7.blurInner, { height: headerHeight }] }) }),
|
|
1110
1128
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [headerHeight === null ? styles7.headerInit : styles7.header, translateStyle], children: [
|
|
1111
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1112
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1113
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1114
|
-
(centerContent || title || caption) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1115
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1116
|
-
caption && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1129
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [{ paddingTop: top }, style], children: children ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native12.View, { style: [styles7.content, { minHeight: contentMinHeight }], children: [
|
|
1130
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: styles7.side }),
|
|
1131
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: styles7.spacer }),
|
|
1132
|
+
(centerContent || title || caption) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [import_react_native12.StyleSheet.absoluteFill, styles7.titleContainer], pointerEvents: centerContent ? "box-none" : "none", children: centerContent ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
1133
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.Text, { numberOfLines: 1, style: [styles7.title, { color: theme.colors.onSurface }], children: title }),
|
|
1134
|
+
caption && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.Text, { numberOfLines: 1, style: [styles7.caption, { color: theme.colors.onSurfaceVariant }], children: caption })
|
|
1117
1135
|
] }) })
|
|
1118
1136
|
] }) }),
|
|
1119
|
-
!blur && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1137
|
+
!blur && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [styles7.divider, { backgroundColor: theme.colors.outlineVariant }] })
|
|
1120
1138
|
] }),
|
|
1121
1139
|
/* @__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_paper3.ProgressBar, { indeterminate: progress === null, visible: progressing, progress: progress ?? void 0 }) }),
|
|
1122
1140
|
backAction && (effectiveBackActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: leadingStyle, children: [
|
|
@@ -1126,7 +1144,7 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFi
|
|
|
1126
1144
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
|
|
1127
1145
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper3.Appbar.BackAction, { onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle })
|
|
1128
1146
|
] })),
|
|
1129
|
-
trailingAction && (trailingActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1147
|
+
trailingAction && (trailingActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native12.View, { style: trailingStyle, children: [
|
|
1130
1148
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
|
|
1131
1149
|
trailingAction
|
|
1132
1150
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: [trailingStyle, translateStyle], children: [
|
|
@@ -1135,9 +1153,9 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFi
|
|
|
1135
1153
|
] }))
|
|
1136
1154
|
] });
|
|
1137
1155
|
};
|
|
1138
|
-
var styles7 =
|
|
1156
|
+
var styles7 = import_react_native12.StyleSheet.create({
|
|
1139
1157
|
actionBg: { borderRadius: 100, overflow: "hidden" },
|
|
1140
|
-
divider: { bottom: 0, height:
|
|
1158
|
+
divider: { bottom: 0, height: import_react_native12.StyleSheet.hairlineWidth, left: 0, position: "absolute", right: 0 },
|
|
1141
1159
|
caption: { fontSize: 12, fontWeight: "400", textAlign: "center" },
|
|
1142
1160
|
content: { alignItems: "center", flexDirection: "row", width: "100%" },
|
|
1143
1161
|
blur: { left: 0, overflow: "hidden", position: "absolute", right: 0, top: 0, zIndex: 2 },
|
|
@@ -1154,7 +1172,7 @@ var styles7 = import_react_native10.StyleSheet.create({
|
|
|
1154
1172
|
// src/ScrollViewProvider.tsx
|
|
1155
1173
|
var import_auto_paper5 = require("@rific/auto-paper");
|
|
1156
1174
|
var import_react15 = require("react");
|
|
1157
|
-
var
|
|
1175
|
+
var import_react_native13 = require("react-native");
|
|
1158
1176
|
var import_react_native_reanimated13 = require("react-native-reanimated");
|
|
1159
1177
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1160
1178
|
var ScrollViewProvider = ({ blur, children, fixed = false, footerFixed, headerFixed, snapBack, snapBackFooter, snapBackHeader, tabBarHeight = 60 }) => {
|
|
@@ -1208,7 +1226,7 @@ var ScrollViewProvider = ({ blur, children, fixed = false, footerFixed, headerFi
|
|
|
1208
1226
|
},
|
|
1209
1227
|
[footerHeightShared]
|
|
1210
1228
|
);
|
|
1211
|
-
const scrollHeight = (0, import_react15.useMemo)(() =>
|
|
1229
|
+
const scrollHeight = (0, import_react15.useMemo)(() => import_react_native13.Dimensions.get("window").height - (headerHeight ?? 0) - footerHeight, [headerHeight, footerHeight]);
|
|
1212
1230
|
const effectiveHeaderFixed = fixed || (headerFixed ?? settings.headerFixed);
|
|
1213
1231
|
const effectiveFooterFixed = fixed || (footerFixed ?? settings.footerFixed);
|
|
1214
1232
|
const value = (0, import_react15.useMemo)(() => ({ blur: effectiveBlur, 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, effectiveFooterFixed, effectiveHeaderFixed, footerHeight, footerHeightShared, footerOffset, headerHeight, headerHeightShared, headerOffset, jsListGeneration, listGeneration, onJsListUnmount, onListUnmount, progress, pullSearchHeightShared, progressing, scrollHeight, scrollPosition, setFooterHeight, setHeaderHeight, snapBackFooterShared, snapBackHeaderShared, tabBarHeight]);
|
|
@@ -1239,7 +1257,7 @@ var ScrollViewSettingsProvider = ({ children, onChange, initialValue }) => {
|
|
|
1239
1257
|
|
|
1240
1258
|
// src/SectionList.tsx
|
|
1241
1259
|
var import_react18 = require("react");
|
|
1242
|
-
var
|
|
1260
|
+
var import_react_native14 = require("react-native");
|
|
1243
1261
|
var import_react_native_gesture_handler4 = require("react-native-gesture-handler");
|
|
1244
1262
|
var import_react_native_reanimated15 = __toESM(require("react-native-reanimated"));
|
|
1245
1263
|
|
|
@@ -1354,11 +1372,11 @@ function useStickyHeaders(headerFixed, enabled) {
|
|
|
1354
1372
|
|
|
1355
1373
|
// src/SectionList.tsx
|
|
1356
1374
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1357
|
-
var AnimatedSectionList = import_react_native_reanimated15.default.createAnimatedComponent(
|
|
1375
|
+
var AnimatedSectionList = import_react_native_reanimated15.default.createAnimatedComponent(import_react_native14.SectionList);
|
|
1358
1376
|
var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: externalContentContainerStyle, initialNumToRender = 20, maxToRenderPerBatch = 50, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onContentSizeChange, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, removeClippedSubviews = false, sections, showsVerticalScrollIndicator, style, ref: externalRef, windowSize = 100, ...props }) => {
|
|
1359
1377
|
const scrollView = (0, import_react18.useRef)(null);
|
|
1360
1378
|
const { stickySectionHeadersEnabled = true, renderSectionHeader, ...passThroughProps } = props;
|
|
1361
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
|
|
1379
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
|
|
1362
1380
|
const { listGeneration, onListUnmount } = (0, import_react18.useContext)(ScrollViewContext);
|
|
1363
1381
|
const capturedGeneration = (0, import_react_native_reanimated15.useSharedValue)(-1);
|
|
1364
1382
|
(0, import_react18.useLayoutEffect)(() => {
|
|
@@ -1379,7 +1397,7 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
|
|
|
1379
1397
|
return (info) => {
|
|
1380
1398
|
const idx = sections.findIndex((s) => s === info.section);
|
|
1381
1399
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1382
|
-
|
|
1400
|
+
import_react_native14.View,
|
|
1383
1401
|
{
|
|
1384
1402
|
ref: (el) => {
|
|
1385
1403
|
sectionViewRefs.current.set(idx, el);
|
|
@@ -1435,7 +1453,7 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
|
|
|
1435
1453
|
});
|
|
1436
1454
|
return () => cancelAnimationFrame(handle);
|
|
1437
1455
|
}, [activeListHeader, usesCustomSticky, measureHeader]);
|
|
1438
|
-
const contentContainerStyle = (0, import_react18.useMemo)(() => [{ minHeight:
|
|
1456
|
+
const contentContainerStyle = (0, import_react18.useMemo)(() => [{ minHeight: import_react_native14.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight]);
|
|
1439
1457
|
const handleScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration });
|
|
1440
1458
|
const handleScrollToTop = (0, import_react18.useCallback)(() => {
|
|
1441
1459
|
const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
|
|
@@ -1451,7 +1469,7 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
|
|
|
1451
1469
|
const nativeGesture = (0, import_react18.useMemo)(() => import_react_native_gesture_handler4.Gesture.Native(), []);
|
|
1452
1470
|
const combinedGesture = (0, import_react18.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler4.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
|
|
1453
1471
|
const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
|
|
1454
|
-
const content = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
1472
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native14.View, { style: containerStyle, children: [
|
|
1455
1473
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1456
1474
|
AnimatedSectionList,
|
|
1457
1475
|
{
|
|
@@ -1482,7 +1500,7 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
|
|
|
1482
1500
|
},
|
|
1483
1501
|
headerHeight !== null && headerHeight > 0 ? 1 : 0
|
|
1484
1502
|
),
|
|
1485
|
-
hiddenHeader && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1503
|
+
hiddenHeader && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native14.View, { pointerEvents: "none", style: styles8.measureContainer, children: hiddenHeader }),
|
|
1486
1504
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ScrollViewChip, { chipProps, onChipPress, onPress: handleScrollToTop, style: chipStyle }),
|
|
1487
1505
|
usesCustomSticky && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_reanimated15.default.View, { pointerEvents: "none", style: [styles8.stickyClip, clipStyle], children: activeIndex >= 0 && activeIndex < sections.length && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_reanimated15.default.View, { style: [styles8.stickyOverlay, overlayStyle], children: renderSectionHeader({ section: sections[activeIndex] }) }) })
|
|
1488
1506
|
] });
|
package/dist/index.mjs
CHANGED
|
@@ -70,6 +70,12 @@ var ScrollViewContext = createContext({
|
|
|
70
70
|
// src/useScrollInit.ts
|
|
71
71
|
import { createElement, useCallback, useContext, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
72
72
|
import { useSharedValue } from "react-native-reanimated";
|
|
73
|
+
|
|
74
|
+
// src/internal/insetMode.ts
|
|
75
|
+
import { Platform } from "react-native";
|
|
76
|
+
var usesContentInset = Platform.OS === "ios";
|
|
77
|
+
|
|
78
|
+
// src/useScrollInit.ts
|
|
73
79
|
var REMOUNT_SYNC_MAX_ATTEMPTS = 8;
|
|
74
80
|
var REMOUNT_SYNC_TOLERANCE = 1;
|
|
75
81
|
var REMOUNT_SYNC_FALLBACK_MS = 500;
|
|
@@ -78,8 +84,9 @@ var SNAP_VERIFY_TOLERANCE = 1;
|
|
|
78
84
|
var SNAP_VERIFY_MAX_ATTEMPTS = 5;
|
|
79
85
|
var SNAP_VERIFY_INITIAL_DELAY_MS = 400;
|
|
80
86
|
var SNAP_VERIFY_RETRY_DELAY_MS = 150;
|
|
81
|
-
function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollTo }) {
|
|
87
|
+
function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight: pullSearchHeightProp, scrollTo }) {
|
|
82
88
|
const { headerHeight, scrollPosition, setProgressing } = useContext(ScrollViewContext);
|
|
89
|
+
const pullSearchHeight = usesContentInset ? pullSearchHeightProp : void 0;
|
|
83
90
|
const hasPullSearch = pullSearchHeight !== void 0;
|
|
84
91
|
const [phase, setPhase] = useState(hasPullSearch && !headerHeight ? "measuring" : "ready");
|
|
85
92
|
const [refreshing, setRefreshing] = useState(false);
|
|
@@ -121,7 +128,7 @@ function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomen
|
|
|
121
128
|
},
|
|
122
129
|
[]
|
|
123
130
|
);
|
|
124
|
-
const [remountTarget] = useState(() => headerHeight !== null ? -headerHeight + (pullSearchHeight ?? 0) : null);
|
|
131
|
+
const [remountTarget] = useState(() => usesContentInset && headerHeight !== null ? -headerHeight + (pullSearchHeight ?? 0) : null);
|
|
125
132
|
const remountSyncTarget = useSharedValue(remountTarget);
|
|
126
133
|
const [scrollReady, setScrollReady] = useState(remountTarget === null);
|
|
127
134
|
const remountAttempts = useRef(0);
|
|
@@ -166,7 +173,7 @@ function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomen
|
|
|
166
173
|
headerInitialized.current = true;
|
|
167
174
|
const target = remountTarget ?? -headerHeight;
|
|
168
175
|
scrollPosition.value = target;
|
|
169
|
-
scrollTo(target, false);
|
|
176
|
+
scrollTo(usesContentInset ? target : 0, false);
|
|
170
177
|
if (remountTarget === null) return;
|
|
171
178
|
let framesLeft = REMOUNT_PRIME_FRAMES;
|
|
172
179
|
let handle = null;
|
|
@@ -273,9 +280,10 @@ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100,
|
|
|
273
280
|
chipHidden.value = x < chipThreshold ? 1 : 0;
|
|
274
281
|
return;
|
|
275
282
|
}
|
|
276
|
-
const
|
|
277
|
-
scrollPosition.value
|
|
278
|
-
|
|
283
|
+
const yn = usesContentInset ? y : y - headerHeightShared.value;
|
|
284
|
+
const delta = yn - scrollPosition.value;
|
|
285
|
+
scrollPosition.value = yn;
|
|
286
|
+
chipHidden.value = yn < chipThreshold ? 1 : 0;
|
|
279
287
|
const maxScroll = contentHeight - layoutHeight;
|
|
280
288
|
if (maxScroll > 0 && y >= maxScroll) {
|
|
281
289
|
fromBottomBounce.value = true;
|
|
@@ -286,18 +294,18 @@ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100,
|
|
|
286
294
|
const snapHeader = snapBackHeaderShared.value && !headerFixed;
|
|
287
295
|
const snapFooter = snapBackFooterShared.value && !footerFixed;
|
|
288
296
|
if (snapHeader || snapFooter) {
|
|
289
|
-
if (
|
|
297
|
+
if (yn <= -headerHeightShared.value) {
|
|
290
298
|
snapUpAccum.value = 0;
|
|
291
299
|
if (snapHeader) headerOffset.value = 0;
|
|
292
300
|
if (snapFooter) footerOffset.value = 0;
|
|
293
301
|
} else if (delta > 0) {
|
|
294
302
|
snapUpAccum.value = 0;
|
|
295
|
-
if (
|
|
303
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) {
|
|
296
304
|
if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta));
|
|
297
305
|
if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta));
|
|
298
306
|
}
|
|
299
307
|
} else if (delta < 0 && !fromBottomBounce.value) {
|
|
300
|
-
if (
|
|
308
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.value -= delta;
|
|
301
309
|
if (snapUpAccum.value >= 10) {
|
|
302
310
|
snapUpAccum.value = 999;
|
|
303
311
|
if (snapHeader) headerOffset.value = withTiming(0, { duration: 200 });
|
|
@@ -337,9 +345,10 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
|
337
345
|
|
|
338
346
|
// src/useKeyboardInset.ts
|
|
339
347
|
import { useState as useState2 } from "react";
|
|
348
|
+
import { Platform as Platform2 } from "react-native";
|
|
340
349
|
import { useKeyboardHandler } from "react-native-keyboard-controller";
|
|
341
350
|
import { runOnJS as runOnJS2 } from "react-native-reanimated";
|
|
342
|
-
var
|
|
351
|
+
var useKeyboardInsetNative = () => {
|
|
343
352
|
const [keyboardHeight, setKeyboardHeight] = useState2(0);
|
|
344
353
|
useKeyboardHandler(
|
|
345
354
|
{
|
|
@@ -356,8 +365,12 @@ var useKeyboardInset = () => {
|
|
|
356
365
|
);
|
|
357
366
|
return keyboardHeight;
|
|
358
367
|
};
|
|
368
|
+
var useKeyboardInsetWeb = () => 0;
|
|
369
|
+
var useKeyboardInset = Platform2.OS === "web" ? useKeyboardInsetWeb : useKeyboardInsetNative;
|
|
359
370
|
|
|
360
371
|
// src/internal/useScrollList.ts
|
|
372
|
+
var ZERO_INSET = { bottom: 0, top: 0 };
|
|
373
|
+
var ZERO_OFFSET = { x: 0, y: 0 };
|
|
361
374
|
var CHIP_SLIDE = 48;
|
|
362
375
|
function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style } = {}) {
|
|
363
376
|
const insets = useSafeAreaInsets();
|
|
@@ -366,12 +379,15 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
|
|
|
366
379
|
const headerFixed = isHorizontal ? true : headerFixedProp ?? contextHeaderFixed;
|
|
367
380
|
const footerFixed = isHorizontal ? true : footerFixedProp ?? contextFooterFixed;
|
|
368
381
|
useEffect2(() => {
|
|
369
|
-
pullSearchHeightShared.value = pullSearchHeight ?? 0;
|
|
382
|
+
pullSearchHeightShared.value = usesContentInset ? pullSearchHeight ?? 0 : 0;
|
|
370
383
|
}, [pullSearchHeight, pullSearchHeightShared]);
|
|
371
384
|
const containerStyle = useMemo(() => headerHeight === null ? [{ flex: 1, opacity: 0 }, style] : [StyleSheet2.absoluteFill, style], [headerHeight, style]);
|
|
372
|
-
const
|
|
385
|
+
const insetGeometry = useMemo(() => ({ bottom: (footerFixed ? footerHeight || insets.bottom : insets.bottom) + (keyboardAware ? keyboardHeight : 0), top: headerHeight ?? 0 }), [footerFixed, footerHeight, insets.bottom, headerHeight, keyboardAware, keyboardHeight]);
|
|
386
|
+
const contentInset = usesContentInset ? insetGeometry : ZERO_INSET;
|
|
387
|
+
const contentPadding = useMemo(() => usesContentInset || isHorizontal ? null : { paddingBottom: insetGeometry.bottom, paddingTop: insetGeometry.top }, [insetGeometry, isHorizontal]);
|
|
373
388
|
const [startedWithHeader] = useState3(headerHeight !== null);
|
|
374
|
-
const
|
|
389
|
+
const insetContentOffset = useMemo(() => ({ x: 0, y: headerHeight !== null ? -headerHeight + (startedWithHeader && !isHorizontal && pullSearchHeight ? pullSearchHeight : 0) : 0 }), [headerHeight, isHorizontal, pullSearchHeight, startedWithHeader]);
|
|
390
|
+
const contentOffset = usesContentInset ? insetContentOffset : ZERO_OFFSET;
|
|
375
391
|
const chipHidden = useSharedValue3(1);
|
|
376
392
|
const chipStyle = useAnimatedStyle(() => {
|
|
377
393
|
const pointerEvents = chipHidden.value ? "none" : "box-none";
|
|
@@ -393,14 +409,14 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
|
|
|
393
409
|
transform: [{ translateY: chipHidden.value ? withTiming2(-CHIP_SLIDE) : withTiming2(0) }]
|
|
394
410
|
};
|
|
395
411
|
}, [isHorizontal, headerFixed, headerHeight, insets.top]);
|
|
396
|
-
return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false };
|
|
412
|
+
return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false };
|
|
397
413
|
}
|
|
398
414
|
|
|
399
415
|
// src/CustomList.tsx
|
|
400
416
|
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
401
417
|
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 }) => {
|
|
402
418
|
const { ListHeaderComponent: listHeaderComponent, contentContainerStyle: externalContentContainerStyle, ...restProps } = props;
|
|
403
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
|
|
419
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
|
|
404
420
|
const { listGeneration, onListUnmount } = useContext4(ScrollViewContext);
|
|
405
421
|
const capturedGeneration = useSharedValue4(-1);
|
|
406
422
|
useLayoutEffect2(() => {
|
|
@@ -449,7 +465,7 @@ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilter
|
|
|
449
465
|
renderFilters
|
|
450
466
|
] });
|
|
451
467
|
}, [pullSearchHeader, renderFilters]);
|
|
452
|
-
const contentContainerStyle = useMemo2(() => [{ minHeight: Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
|
|
468
|
+
const contentContainerStyle = useMemo2(() => [{ minHeight: Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight]);
|
|
453
469
|
const onScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget });
|
|
454
470
|
const handleScrollToTop = useCallback2(() => {
|
|
455
471
|
const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
|
|
@@ -580,9 +596,10 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
|
|
|
580
596
|
chipHidden.value = x < chipThreshold ? 1 : 0;
|
|
581
597
|
return;
|
|
582
598
|
}
|
|
583
|
-
const
|
|
584
|
-
scrollPosition.value
|
|
585
|
-
|
|
599
|
+
const yn = usesContentInset ? y : y - headerHeightShared.value;
|
|
600
|
+
const delta = yn - scrollPosition.value;
|
|
601
|
+
scrollPosition.value = yn;
|
|
602
|
+
chipHidden.value = yn < chipThreshold ? 1 : 0;
|
|
586
603
|
const maxScroll = contentHeight - layoutHeight;
|
|
587
604
|
if (maxScroll > 0 && y >= maxScroll) {
|
|
588
605
|
fromBottomBounce.current = true;
|
|
@@ -593,18 +610,18 @@ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 10
|
|
|
593
610
|
const snapHeader = snapBackHeaderShared.value && !headerFixed;
|
|
594
611
|
const snapFooter = snapBackFooterShared.value && !footerFixed;
|
|
595
612
|
if (snapHeader || snapFooter) {
|
|
596
|
-
if (
|
|
613
|
+
if (yn <= -headerHeightShared.value) {
|
|
597
614
|
snapUpAccum.current = 0;
|
|
598
615
|
if (snapHeader) headerOffset.value = 0;
|
|
599
616
|
if (snapFooter) footerOffset.value = 0;
|
|
600
617
|
} else if (delta > 0) {
|
|
601
618
|
snapUpAccum.current = 0;
|
|
602
|
-
if (
|
|
619
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) {
|
|
603
620
|
if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta));
|
|
604
621
|
if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta));
|
|
605
622
|
}
|
|
606
623
|
} else if (delta < 0 && !fromBottomBounce.current) {
|
|
607
|
-
if (
|
|
624
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.current -= delta;
|
|
608
625
|
if (snapUpAccum.current >= 10) {
|
|
609
626
|
snapUpAccum.current = 999;
|
|
610
627
|
if (snapHeader) headerOffset.value = withTiming3(0, { duration: 200 });
|
|
@@ -672,7 +689,7 @@ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentCont
|
|
|
672
689
|
},
|
|
673
690
|
[cols, columnWrapperStyle, renderItem]
|
|
674
691
|
);
|
|
675
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
|
|
692
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
|
|
676
693
|
const { jsListGeneration, onJsListUnmount } = useContext7(ScrollViewContext);
|
|
677
694
|
const capturedGeneration = useRef4(jsListGeneration.current);
|
|
678
695
|
useLayoutEffect3(() => {
|
|
@@ -707,7 +724,7 @@ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentCont
|
|
|
707
724
|
renderFilters
|
|
708
725
|
] });
|
|
709
726
|
}, [pullSearchHeader, renderFilters]);
|
|
710
|
-
const contentContainerStyle = useMemo3(() => isHorizontal ? externalContentContainerStyle : [{ minHeight: Dimensions2.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [isHorizontal, contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
|
|
727
|
+
const contentContainerStyle = useMemo3(() => isHorizontal ? externalContentContainerStyle : [{ minHeight: Dimensions2.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [isHorizontal, contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight]);
|
|
711
728
|
const scrollHandler = useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, isHorizontal, jsListGeneration, remountTarget, scrollTo });
|
|
712
729
|
const onScrollBeginDrag = useCallback4(
|
|
713
730
|
(e) => {
|
|
@@ -900,10 +917,10 @@ import { View as View4 } from "react-native";
|
|
|
900
917
|
import { Gesture as Gesture3, GestureDetector as GestureDetector3 } from "react-native-gesture-handler";
|
|
901
918
|
import Animated4, { runOnUI as runOnUI2, useSharedValue as useSharedValue5 } from "react-native-reanimated";
|
|
902
919
|
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
903
|
-
var ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }) => {
|
|
920
|
+
var ScrollViewInner = ({ chipProps, chipThreshold, children, contentContainerStyle: externalContentContainerStyle, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }) => {
|
|
904
921
|
const scrollView = useRef6(null);
|
|
905
922
|
const isHorizontal = horizontal === true;
|
|
906
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
|
|
923
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
|
|
907
924
|
const { listGeneration, onListUnmount } = useContext9(ScrollViewContext);
|
|
908
925
|
const capturedGeneration = useSharedValue5(-1);
|
|
909
926
|
useLayoutEffect4(() => {
|
|
@@ -942,6 +959,7 @@ var ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footer
|
|
|
942
959
|
Animated4.ScrollView,
|
|
943
960
|
{
|
|
944
961
|
...props,
|
|
962
|
+
contentContainerStyle: contentPadding ? [externalContentContainerStyle, contentPadding] : externalContentContainerStyle,
|
|
945
963
|
contentInset,
|
|
946
964
|
contentOffset,
|
|
947
965
|
onMomentumScrollEnd: handleMomentumScrollEnd,
|
|
@@ -1306,7 +1324,7 @@ var AnimatedSectionList = Animated7.createAnimatedComponent(RNSectionList);
|
|
|
1306
1324
|
var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: externalContentContainerStyle, initialNumToRender = 20, maxToRenderPerBatch = 50, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onContentSizeChange, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, removeClippedSubviews = false, sections, showsVerticalScrollIndicator, style, ref: externalRef, windowSize = 100, ...props }) => {
|
|
1307
1325
|
const scrollView = useRef9(null);
|
|
1308
1326
|
const { stickySectionHeadersEnabled = true, renderSectionHeader, ...passThroughProps } = props;
|
|
1309
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
|
|
1327
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
|
|
1310
1328
|
const { listGeneration, onListUnmount } = useContext14(ScrollViewContext);
|
|
1311
1329
|
const capturedGeneration = useSharedValue8(-1);
|
|
1312
1330
|
useLayoutEffect6(() => {
|
|
@@ -1383,7 +1401,7 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
|
|
|
1383
1401
|
});
|
|
1384
1402
|
return () => cancelAnimationFrame(handle);
|
|
1385
1403
|
}, [activeListHeader, usesCustomSticky, measureHeader]);
|
|
1386
|
-
const contentContainerStyle = useMemo7(() => [{ minHeight: Dimensions4.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
|
|
1404
|
+
const contentContainerStyle = useMemo7(() => [{ minHeight: Dimensions4.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight]);
|
|
1387
1405
|
const handleScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration });
|
|
1388
1406
|
const handleScrollToTop = useCallback10(() => {
|
|
1389
1407
|
const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rific/scroll-view",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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"
|
package/src/CustomList.tsx
CHANGED
|
@@ -35,7 +35,7 @@ const CustomListInner = <P extends object>({ chipProps, chipThreshold, component
|
|
|
35
35
|
// Intercept ListHeaderComponent so useScrollInit can apply the same 2-phase measurement as FlatList.
|
|
36
36
|
const { ListHeaderComponent: listHeaderComponent, contentContainerStyle: externalContentContainerStyle, ...restProps } = props as Record<string, unknown>
|
|
37
37
|
|
|
38
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style })
|
|
38
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style })
|
|
39
39
|
const { listGeneration, onListUnmount } = useContext(ScrollViewContext)
|
|
40
40
|
// -1 never matches a real generation (starts at 0, only increments) — the runOnUI call below
|
|
41
41
|
// corrects it before any scroll event could observe the placeholder.
|
|
@@ -106,7 +106,7 @@ const CustomListInner = <P extends object>({ chipProps, chipThreshold, component
|
|
|
106
106
|
)
|
|
107
107
|
}, [pullSearchHeader, renderFilters])
|
|
108
108
|
|
|
109
|
-
const contentContainerStyle = useMemo(() => [{ minHeight: Dimensions.get('window').height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight])
|
|
109
|
+
const contentContainerStyle = useMemo(() => [{ minHeight: Dimensions.get('window').height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight])
|
|
110
110
|
|
|
111
111
|
const onScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget })
|
|
112
112
|
|
package/src/FlatList.tsx
CHANGED
|
@@ -78,7 +78,7 @@ const FlatListInner = <T,>({ chipProps, chipThreshold, columnWrapperStyle, conte
|
|
|
78
78
|
[cols, columnWrapperStyle, renderItem]
|
|
79
79
|
)
|
|
80
80
|
|
|
81
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style })
|
|
81
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style })
|
|
82
82
|
const { jsListGeneration, onJsListUnmount } = useContext(ScrollViewContext)
|
|
83
83
|
// Plain ref, not a SharedValue: see the comment on ScrollViewContextType.jsListGeneration for why.
|
|
84
84
|
const capturedGeneration = useRef(jsListGeneration.current)
|
|
@@ -120,7 +120,7 @@ const FlatListInner = <T,>({ chipProps, chipThreshold, columnWrapperStyle, conte
|
|
|
120
120
|
)
|
|
121
121
|
}, [pullSearchHeader, renderFilters])
|
|
122
122
|
|
|
123
|
-
const contentContainerStyle = useMemo(() => (isHorizontal ? externalContentContainerStyle : [{ minHeight: Dimensions.get('window').height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle]), [isHorizontal, contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight])
|
|
123
|
+
const contentContainerStyle = useMemo(() => (isHorizontal ? externalContentContainerStyle : [{ minHeight: Dimensions.get('window').height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding]), [isHorizontal, contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight])
|
|
124
124
|
|
|
125
125
|
const scrollHandler = useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, isHorizontal, jsListGeneration, remountTarget, scrollTo })
|
|
126
126
|
|
package/src/ScrollView.tsx
CHANGED
|
@@ -26,11 +26,11 @@ export type ScrollViewProps = RNScrollViewProps & {
|
|
|
26
26
|
refreshing?: boolean
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }: ScrollViewProps) => {
|
|
29
|
+
const ScrollViewInner = ({ chipProps, chipThreshold, children, contentContainerStyle: externalContentContainerStyle, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }: ScrollViewProps) => {
|
|
30
30
|
const scrollView = useRef<RNScrollView>(null)
|
|
31
31
|
const isHorizontal = horizontal === true
|
|
32
32
|
|
|
33
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style })
|
|
33
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style })
|
|
34
34
|
const { listGeneration, onListUnmount } = useContext(ScrollViewContext)
|
|
35
35
|
// -1 never matches a real generation (starts at 0, only increments) — the runOnUI call below
|
|
36
36
|
// corrects it before any scroll event could observe the placeholder.
|
|
@@ -88,6 +88,7 @@ const ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: foot
|
|
|
88
88
|
<View style={containerStyle}>
|
|
89
89
|
<Animated.ScrollView
|
|
90
90
|
{...props}
|
|
91
|
+
contentContainerStyle={contentPadding ? [externalContentContainerStyle, contentPadding] : externalContentContainerStyle}
|
|
91
92
|
contentInset={contentInset}
|
|
92
93
|
contentOffset={contentOffset}
|
|
93
94
|
onMomentumScrollEnd={handleMomentumScrollEnd}
|
package/src/SectionList.tsx
CHANGED
|
@@ -34,7 +34,7 @@ const SectionListInner = <ItemT, SectionT extends { data: ReadonlyArray<ItemT> }
|
|
|
34
34
|
|
|
35
35
|
const { stickySectionHeadersEnabled = true, renderSectionHeader, ...passThroughProps } = props as RNSectionListProps<ItemT, SectionT>
|
|
36
36
|
|
|
37
|
-
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style })
|
|
37
|
+
const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style })
|
|
38
38
|
const { listGeneration, onListUnmount } = useContext(ScrollViewContext)
|
|
39
39
|
// -1 never matches a real generation (starts at 0, only increments) — the runOnUI call below
|
|
40
40
|
// corrects it before any scroll event could observe the placeholder.
|
|
@@ -144,7 +144,7 @@ const SectionListInner = <ItemT, SectionT extends { data: ReadonlyArray<ItemT> }
|
|
|
144
144
|
return () => cancelAnimationFrame(handle)
|
|
145
145
|
}, [activeListHeader, usesCustomSticky, measureHeader])
|
|
146
146
|
|
|
147
|
-
const contentContainerStyle = useMemo(() => [{ minHeight: Dimensions.get('window').height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight])
|
|
147
|
+
const contentContainerStyle = useMemo(() => [{ minHeight: Dimensions.get('window').height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle, contentPadding], [contentInset.bottom, contentInset.top, contentPadding, externalContentContainerStyle, pullSearchMinHeight])
|
|
148
148
|
|
|
149
149
|
const handleScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration })
|
|
150
150
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Platform } from 'react-native'
|
|
2
|
+
|
|
3
|
+
// iOS positions content under the translucent bars with contentInset/contentOffset. Android and
|
|
4
|
+
// react-native-web silently ignore both, so on those platforms the same geometry is emulated with
|
|
5
|
+
// content-container padding, and the scroll handlers normalize offsets back into inset space
|
|
6
|
+
// (rest position = -headerHeight) so every downstream calculation keeps one coordinate system.
|
|
7
|
+
// Pull-search relies on iOS overscroll physics and stays disabled outside inset mode.
|
|
8
|
+
export const usesContentInset = Platform.OS === 'ios'
|
|
@@ -4,6 +4,7 @@ import { runOnJS, useAnimatedScrollHandler, useSharedValue, withTiming } from 'r
|
|
|
4
4
|
|
|
5
5
|
import { ScrollViewContext } from '../ScrollViewContext'
|
|
6
6
|
import { REMOUNT_SYNC_TOLERANCE } from '../useScrollInit'
|
|
7
|
+
import { usesContentInset } from './insetMode'
|
|
7
8
|
|
|
8
9
|
type UseScrollHandlerOptions = {
|
|
9
10
|
capturedGeneration: SharedValue<number>
|
|
@@ -43,9 +44,12 @@ export function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold
|
|
|
43
44
|
chipHidden.value = x < chipThreshold ? 1 : 0
|
|
44
45
|
return
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// Normalize into inset space (rest = -headerHeight) on platforms that emulate the bars
|
|
48
|
+
// with content padding; bounce detection below stays in the raw space contentSize lives in.
|
|
49
|
+
const yn = usesContentInset ? y : y - headerHeightShared.value
|
|
50
|
+
const delta = yn - scrollPosition.value
|
|
51
|
+
scrollPosition.value = yn
|
|
52
|
+
chipHidden.value = yn < chipThreshold ? 1 : 0
|
|
49
53
|
const maxScroll = contentHeight - layoutHeight
|
|
50
54
|
if (maxScroll > 0 && y >= maxScroll) {
|
|
51
55
|
fromBottomBounce.value = true
|
|
@@ -56,18 +60,18 @@ export function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold
|
|
|
56
60
|
const snapHeader = snapBackHeaderShared.value && !headerFixed
|
|
57
61
|
const snapFooter = snapBackFooterShared.value && !footerFixed
|
|
58
62
|
if (snapHeader || snapFooter) {
|
|
59
|
-
if (
|
|
63
|
+
if (yn <= -headerHeightShared.value) {
|
|
60
64
|
snapUpAccum.value = 0
|
|
61
65
|
if (snapHeader) headerOffset.value = 0
|
|
62
66
|
if (snapFooter) footerOffset.value = 0
|
|
63
67
|
} else if (delta > 0) {
|
|
64
68
|
snapUpAccum.value = 0
|
|
65
|
-
if (
|
|
69
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) {
|
|
66
70
|
if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta))
|
|
67
71
|
if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta))
|
|
68
72
|
}
|
|
69
73
|
} else if (delta < 0 && !fromBottomBounce.value) {
|
|
70
|
-
if (
|
|
74
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.value -= delta
|
|
71
75
|
if (snapUpAccum.value >= 10) {
|
|
72
76
|
snapUpAccum.value = 999
|
|
73
77
|
if (snapHeader) headerOffset.value = withTiming(0, { duration: 200 })
|
|
@@ -4,6 +4,7 @@ import type { SharedValue } from 'react-native-reanimated'
|
|
|
4
4
|
import { withTiming } from 'react-native-reanimated'
|
|
5
5
|
|
|
6
6
|
import { ScrollViewContext } from '../ScrollViewContext'
|
|
7
|
+
import { usesContentInset } from './insetMode'
|
|
7
8
|
|
|
8
9
|
const REMOUNT_RETRY_TOLERANCE = 1
|
|
9
10
|
const REMOUNT_RETRY_MAX_ATTEMPTS = 8
|
|
@@ -73,9 +74,12 @@ export function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThresho
|
|
|
73
74
|
chipHidden.value = x < chipThreshold ? 1 : 0
|
|
74
75
|
return
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
// Same inset-space normalization as the worklet handler: rest = -headerHeight everywhere,
|
|
78
|
+
// raw space only for bounce detection against contentSize.
|
|
79
|
+
const yn = usesContentInset ? y : y - headerHeightShared.value
|
|
80
|
+
const delta = yn - scrollPosition.value
|
|
81
|
+
scrollPosition.value = yn
|
|
82
|
+
chipHidden.value = yn < chipThreshold ? 1 : 0
|
|
79
83
|
const maxScroll = contentHeight - layoutHeight
|
|
80
84
|
if (maxScroll > 0 && y >= maxScroll) {
|
|
81
85
|
fromBottomBounce.current = true
|
|
@@ -86,18 +90,18 @@ export function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThresho
|
|
|
86
90
|
const snapHeader = snapBackHeaderShared.value && !headerFixed
|
|
87
91
|
const snapFooter = snapBackFooterShared.value && !footerFixed
|
|
88
92
|
if (snapHeader || snapFooter) {
|
|
89
|
-
if (
|
|
93
|
+
if (yn <= -headerHeightShared.value) {
|
|
90
94
|
snapUpAccum.current = 0
|
|
91
95
|
if (snapHeader) headerOffset.value = 0
|
|
92
96
|
if (snapFooter) footerOffset.value = 0
|
|
93
97
|
} else if (delta > 0) {
|
|
94
98
|
snapUpAccum.current = 0
|
|
95
|
-
if (
|
|
99
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) {
|
|
96
100
|
if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta))
|
|
97
101
|
if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta))
|
|
98
102
|
}
|
|
99
103
|
} else if (delta < 0 && !fromBottomBounce.current) {
|
|
100
|
-
if (
|
|
104
|
+
if (yn >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.current -= delta
|
|
101
105
|
if (snapUpAccum.current >= 10) {
|
|
102
106
|
snapUpAccum.current = 999
|
|
103
107
|
if (snapHeader) headerOffset.value = withTiming(0, { duration: 200 })
|
|
@@ -5,6 +5,10 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
|
5
5
|
|
|
6
6
|
import { ScrollViewContext } from '../ScrollViewContext'
|
|
7
7
|
import { useKeyboardInset } from '../useKeyboardInset'
|
|
8
|
+
import { usesContentInset } from './insetMode'
|
|
9
|
+
|
|
10
|
+
const ZERO_INSET = { bottom: 0, top: 0 }
|
|
11
|
+
const ZERO_OFFSET = { x: 0, y: 0 }
|
|
8
12
|
|
|
9
13
|
const CHIP_SLIDE = 48
|
|
10
14
|
|
|
@@ -26,7 +30,7 @@ export function useScrollList({ footerFixed: footerFixedProp, headerFixed: heade
|
|
|
26
30
|
const footerFixed = isHorizontal ? true : (footerFixedProp ?? contextFooterFixed)
|
|
27
31
|
|
|
28
32
|
useEffect(() => {
|
|
29
|
-
pullSearchHeightShared.value = pullSearchHeight ?? 0
|
|
33
|
+
pullSearchHeightShared.value = usesContentInset ? (pullSearchHeight ?? 0) : 0
|
|
30
34
|
}, [pullSearchHeight, pullSearchHeightShared])
|
|
31
35
|
|
|
32
36
|
// First render (headerHeight===0): flex:1, hidden until header measures so the key=0→1
|
|
@@ -34,14 +38,20 @@ export function useScrollList({ footerFixed: footerFixedProp, headerFixed: heade
|
|
|
34
38
|
// After header measures: absoluteFill with correct contentOffset — then reveal.
|
|
35
39
|
const containerStyle = useMemo(() => (headerHeight === null ? [{ flex: 1, opacity: 0 }, style] : [StyleSheet.absoluteFill, style]), [headerHeight, style])
|
|
36
40
|
|
|
37
|
-
const
|
|
41
|
+
const insetGeometry = useMemo(() => ({ bottom: (footerFixed ? footerHeight || insets.bottom : insets.bottom) + (keyboardAware ? keyboardHeight : 0), top: headerHeight ?? 0 }), [footerFixed, footerHeight, insets.bottom, headerHeight, keyboardAware, keyboardHeight])
|
|
42
|
+
// Outside inset mode the same geometry is applied as content padding instead; contentInset is
|
|
43
|
+
// zeroed (not omitted) so consumers' offset math — scroll-to-top targets, minHeight — stays
|
|
44
|
+
// correct in the raw 0-based coordinate space those platforms actually scroll in.
|
|
45
|
+
const contentInset = usesContentInset ? insetGeometry : ZERO_INSET
|
|
46
|
+
const contentPadding = useMemo(() => (usesContentInset || isHorizontal ? null : { paddingBottom: insetGeometry.bottom, paddingTop: insetGeometry.top }), [insetGeometry, isHorizontal])
|
|
38
47
|
|
|
39
48
|
// Use hideY offset only when this component mounted with headerHeight already known (mode switch).
|
|
40
49
|
// On initial load, headerHeight is null at mount so we start at showY and let useScrollInit
|
|
41
50
|
// call scrollTo after the phase transition. On mode switch, headerHeight is already set so we
|
|
42
51
|
// can position the FlatList at hideY atomically via contentOffset — no scrollTo needed.
|
|
43
52
|
const [startedWithHeader] = useState(headerHeight !== null)
|
|
44
|
-
const
|
|
53
|
+
const insetContentOffset = useMemo(() => ({ x: 0, y: headerHeight !== null ? -headerHeight + (startedWithHeader && !isHorizontal && pullSearchHeight ? pullSearchHeight : 0) : 0 }), [headerHeight, isHorizontal, pullSearchHeight, startedWithHeader])
|
|
54
|
+
const contentOffset = usesContentInset ? insetContentOffset : ZERO_OFFSET
|
|
45
55
|
|
|
46
56
|
const chipHidden = useSharedValue(1)
|
|
47
57
|
const chipStyle = useAnimatedStyle(() => {
|
|
@@ -65,5 +75,5 @@ export function useScrollList({ footerFixed: footerFixedProp, headerFixed: heade
|
|
|
65
75
|
}
|
|
66
76
|
}, [isHorizontal, headerFixed, headerHeight, insets.top])
|
|
67
77
|
|
|
68
|
-
return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false }
|
|
78
|
+
return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, contentPadding, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false }
|
|
69
79
|
}
|
package/src/useKeyboardInset.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
|
+
import { Platform } from 'react-native'
|
|
2
3
|
import { useKeyboardHandler } from 'react-native-keyboard-controller'
|
|
3
4
|
import { runOnJS } from 'react-native-reanimated'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const useKeyboardInsetNative = () => {
|
|
6
7
|
const [keyboardHeight, setKeyboardHeight] = useState(0)
|
|
7
8
|
useKeyboardHandler(
|
|
8
9
|
{
|
|
@@ -19,3 +20,9 @@ export const useKeyboardInset = () => {
|
|
|
19
20
|
)
|
|
20
21
|
return keyboardHeight
|
|
21
22
|
}
|
|
23
|
+
|
|
24
|
+
const useKeyboardInsetWeb = () => 0
|
|
25
|
+
|
|
26
|
+
// react-native-keyboard-controller has no web implementation — its handler hook must never be
|
|
27
|
+
// called there. Platform is constant for the life of the bundle, so this select is hook-safe.
|
|
28
|
+
export const useKeyboardInset = Platform.OS === 'web' ? useKeyboardInsetWeb : useKeyboardInsetNative
|
package/src/useScrollInit.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type ComponentType, createElement, type ReactElement, useCallback, useC
|
|
|
2
2
|
import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native'
|
|
3
3
|
import { type SharedValue, useSharedValue } from 'react-native-reanimated'
|
|
4
4
|
|
|
5
|
+
import { usesContentInset } from './internal/insetMode'
|
|
5
6
|
import { ScrollViewContext } from './ScrollViewContext'
|
|
6
7
|
|
|
7
8
|
const REMOUNT_SYNC_MAX_ATTEMPTS = 8
|
|
@@ -41,8 +42,10 @@ export type UseScrollInitResult = {
|
|
|
41
42
|
scrollReady: boolean
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
export function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollTo }: UseScrollInitOptions): UseScrollInitResult {
|
|
45
|
+
export function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight: pullSearchHeightProp, scrollTo }: UseScrollInitOptions): UseScrollInitResult {
|
|
45
46
|
const { headerHeight, scrollPosition, setProgressing } = useContext(ScrollViewContext)
|
|
47
|
+
// Pull-search rides iOS overscroll physics; outside inset mode it never engages.
|
|
48
|
+
const pullSearchHeight = usesContentInset ? pullSearchHeightProp : undefined
|
|
46
49
|
const hasPullSearch = pullSearchHeight !== undefined
|
|
47
50
|
// Start 'ready' immediately if headerHeight is already known (e.g. FlatList remount on mode
|
|
48
51
|
// switch). Only enter 'measuring' on the very first mount when headerHeight hasn't been set yet.
|
|
@@ -115,7 +118,9 @@ export function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: extern
|
|
|
115
118
|
// for the first render), so the list stays hidden until a confirmed onScroll event reports the
|
|
116
119
|
// intended resting position, re-issuing the imperative scrollTo in between as needed. This
|
|
117
120
|
// replaces trying to guess how many spurious events to skip.
|
|
118
|
-
|
|
121
|
+
// Remount sync exists for Fabric/iOS contentOffset unreliability; padding platforms mount at a
|
|
122
|
+
// plain 0 offset and need none of it.
|
|
123
|
+
const [remountTarget] = useState<number | null>(() => (usesContentInset && headerHeight !== null ? -headerHeight + (pullSearchHeight ?? 0) : null))
|
|
119
124
|
const remountSyncTarget = useSharedValue<number | null>(remountTarget)
|
|
120
125
|
const [scrollReady, setScrollReady] = useState(remountTarget === null)
|
|
121
126
|
const remountAttempts = useRef(0)
|
|
@@ -195,7 +200,8 @@ export function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: extern
|
|
|
195
200
|
headerInitialized.current = true
|
|
196
201
|
const target = remountTarget ?? -headerHeight
|
|
197
202
|
scrollPosition.value = target
|
|
198
|
-
|
|
203
|
+
// Imperative scroll targets are raw-space: inset mode rests at -headerHeight, padding mode at 0.
|
|
204
|
+
scrollTo(usesContentInset ? target : 0, false)
|
|
199
205
|
if (remountTarget === null) return
|
|
200
206
|
let framesLeft = REMOUNT_PRIME_FRAMES
|
|
201
207
|
let handle: number | null = null
|