@yahoo/uds-mobile 2.3.3 → 2.4.0
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/android/build.gradle +39 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/yahoo/uds/screencornerradius/UDSScreenCornerRadiusModule.kt +44 -0
- package/dist/bin/generateTheme.mjs +10 -0
- package/dist/bin/mobile/scripts/utils/configToRNMappings.mjs +4 -1
- package/dist/components/AndroidBackHandler.cjs +31 -0
- package/dist/components/AndroidBackHandler.d.cts +21 -0
- package/dist/components/AndroidBackHandler.d.cts.map +1 -0
- package/dist/components/AndroidBackHandler.d.ts +21 -0
- package/dist/components/AndroidBackHandler.d.ts.map +1 -0
- package/dist/components/AndroidBackHandler.js +30 -0
- package/dist/components/AndroidBackHandler.js.map +1 -0
- package/dist/components/Avatar.cjs +1 -1
- package/dist/components/Avatar.js +1 -1
- package/dist/components/Badge.cjs +1 -1
- package/dist/components/Badge.js +1 -1
- package/dist/components/BottomSheet/BottomSheet.cjs +336 -0
- package/dist/components/BottomSheet/BottomSheet.d.cts +33 -0
- package/dist/components/BottomSheet/BottomSheet.d.cts.map +1 -0
- package/dist/components/BottomSheet/BottomSheet.d.ts +33 -0
- package/dist/components/BottomSheet/BottomSheet.d.ts.map +1 -0
- package/dist/components/BottomSheet/BottomSheet.js +334 -0
- package/dist/components/BottomSheet/BottomSheet.js.map +1 -0
- package/dist/components/BottomSheet/BottomSheetContent.cjs +65 -0
- package/dist/components/BottomSheet/BottomSheetContent.d.cts +22 -0
- package/dist/components/BottomSheet/BottomSheetContent.d.cts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetContent.d.ts +22 -0
- package/dist/components/BottomSheet/BottomSheetContent.d.ts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetContent.js +63 -0
- package/dist/components/BottomSheet/BottomSheetContent.js.map +1 -0
- package/dist/components/BottomSheet/BottomSheetDismiss.cjs +28 -0
- package/dist/components/BottomSheet/BottomSheetDismiss.d.cts +25 -0
- package/dist/components/BottomSheet/BottomSheetDismiss.d.cts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetDismiss.d.ts +25 -0
- package/dist/components/BottomSheet/BottomSheetDismiss.d.ts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetDismiss.js +27 -0
- package/dist/components/BottomSheet/BottomSheetDismiss.js.map +1 -0
- package/dist/components/BottomSheet/BottomSheetHandle.cjs +56 -0
- package/dist/components/BottomSheet/BottomSheetHandle.d.cts +14 -0
- package/dist/components/BottomSheet/BottomSheetHandle.d.cts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetHandle.d.ts +14 -0
- package/dist/components/BottomSheet/BottomSheetHandle.d.ts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetHandle.js +55 -0
- package/dist/components/BottomSheet/BottomSheetHandle.js.map +1 -0
- package/dist/components/BottomSheet/BottomSheetHeader.cjs +69 -0
- package/dist/components/BottomSheet/BottomSheetHeader.d.cts +16 -0
- package/dist/components/BottomSheet/BottomSheetHeader.d.cts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetHeader.d.ts +16 -0
- package/dist/components/BottomSheet/BottomSheetHeader.d.ts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetHeader.js +68 -0
- package/dist/components/BottomSheet/BottomSheetHeader.js.map +1 -0
- package/dist/components/BottomSheet/BottomSheetInternalProvider.cjs +26 -0
- package/dist/components/BottomSheet/BottomSheetInternalProvider.d.cts +23 -0
- package/dist/components/BottomSheet/BottomSheetInternalProvider.d.cts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetInternalProvider.d.ts +23 -0
- package/dist/components/BottomSheet/BottomSheetInternalProvider.d.ts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetInternalProvider.js +24 -0
- package/dist/components/BottomSheet/BottomSheetInternalProvider.js.map +1 -0
- package/dist/components/BottomSheet/BottomSheetProvider.cjs +33 -0
- package/dist/components/BottomSheet/BottomSheetProvider.d.cts +24 -0
- package/dist/components/BottomSheet/BottomSheetProvider.d.cts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetProvider.d.ts +24 -0
- package/dist/components/BottomSheet/BottomSheetProvider.d.ts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetProvider.js +31 -0
- package/dist/components/BottomSheet/BottomSheetProvider.js.map +1 -0
- package/dist/components/BottomSheet/BottomSheetTrigger.cjs +28 -0
- package/dist/components/BottomSheet/BottomSheetTrigger.d.cts +25 -0
- package/dist/components/BottomSheet/BottomSheetTrigger.d.cts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetTrigger.d.ts +25 -0
- package/dist/components/BottomSheet/BottomSheetTrigger.d.ts.map +1 -0
- package/dist/components/BottomSheet/BottomSheetTrigger.js +27 -0
- package/dist/components/BottomSheet/BottomSheetTrigger.js.map +1 -0
- package/dist/components/BottomSheet/index.cjs +19 -0
- package/dist/components/BottomSheet/index.d.cts +11 -0
- package/dist/components/BottomSheet/index.d.ts +11 -0
- package/dist/components/BottomSheet/index.js +11 -0
- package/dist/components/BottomSheet/types.cjs +1 -0
- package/dist/components/BottomSheet/types.d.cts +173 -0
- package/dist/components/BottomSheet/types.d.cts.map +1 -0
- package/dist/components/BottomSheet/types.d.ts +173 -0
- package/dist/components/BottomSheet/types.d.ts.map +1 -0
- package/dist/components/BottomSheet/types.js +1 -0
- package/dist/components/BottomSheet/useBottomSheetDrag.cjs +191 -0
- package/dist/components/BottomSheet/useBottomSheetDrag.d.cts +69 -0
- package/dist/components/BottomSheet/useBottomSheetDrag.d.cts.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetDrag.d.ts +69 -0
- package/dist/components/BottomSheet/useBottomSheetDrag.d.ts.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetDrag.js +190 -0
- package/dist/components/BottomSheet/useBottomSheetDrag.js.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetScroll.cjs +61 -0
- package/dist/components/BottomSheet/useBottomSheetScroll.d.cts +48 -0
- package/dist/components/BottomSheet/useBottomSheetScroll.d.cts.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetScroll.d.ts +48 -0
- package/dist/components/BottomSheet/useBottomSheetScroll.d.ts.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetScroll.js +60 -0
- package/dist/components/BottomSheet/useBottomSheetScroll.js.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetSnapModel.cjs +142 -0
- package/dist/components/BottomSheet/useBottomSheetSnapModel.d.cts +79 -0
- package/dist/components/BottomSheet/useBottomSheetSnapModel.d.cts.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetSnapModel.d.ts +79 -0
- package/dist/components/BottomSheet/useBottomSheetSnapModel.d.ts.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetSnapModel.js +140 -0
- package/dist/components/BottomSheet/useBottomSheetSnapModel.js.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetStore.cjs +79 -0
- package/dist/components/BottomSheet/useBottomSheetStore.d.cts +45 -0
- package/dist/components/BottomSheet/useBottomSheetStore.d.cts.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetStore.d.ts +45 -0
- package/dist/components/BottomSheet/useBottomSheetStore.d.ts.map +1 -0
- package/dist/components/BottomSheet/useBottomSheetStore.js +77 -0
- package/dist/components/BottomSheet/useBottomSheetStore.js.map +1 -0
- package/dist/components/BottomSheet/useExpansionMargins.cjs +57 -0
- package/dist/components/BottomSheet/useExpansionMargins.d.cts +59 -0
- package/dist/components/BottomSheet/useExpansionMargins.d.cts.map +1 -0
- package/dist/components/BottomSheet/useExpansionMargins.d.ts +59 -0
- package/dist/components/BottomSheet/useExpansionMargins.d.ts.map +1 -0
- package/dist/components/BottomSheet/useExpansionMargins.js +56 -0
- package/dist/components/BottomSheet/useExpansionMargins.js.map +1 -0
- package/dist/components/BottomSheet/useKeyboardAvoidance.cjs +53 -0
- package/dist/components/BottomSheet/useKeyboardAvoidance.d.cts +31 -0
- package/dist/components/BottomSheet/useKeyboardAvoidance.d.cts.map +1 -0
- package/dist/components/BottomSheet/useKeyboardAvoidance.d.ts +31 -0
- package/dist/components/BottomSheet/useKeyboardAvoidance.d.ts.map +1 -0
- package/dist/components/BottomSheet/useKeyboardAvoidance.js +52 -0
- package/dist/components/BottomSheet/useKeyboardAvoidance.js.map +1 -0
- package/dist/components/BottomSheet/utils.cjs +77 -0
- package/dist/components/BottomSheet/utils.d.cts +50 -0
- package/dist/components/BottomSheet/utils.d.cts.map +1 -0
- package/dist/components/BottomSheet/utils.d.ts +50 -0
- package/dist/components/BottomSheet/utils.d.ts.map +1 -0
- package/dist/components/BottomSheet/utils.js +72 -0
- package/dist/components/BottomSheet/utils.js.map +1 -0
- package/dist/components/Box.cjs +1 -1
- package/dist/components/Box.js +1 -1
- package/dist/components/Button.cjs +1 -1
- package/dist/components/Button.js +1 -1
- package/dist/components/Checkbox.cjs +1 -1
- package/dist/components/Checkbox.js +1 -1
- package/dist/components/Chip.cjs +1 -1
- package/dist/components/Chip.js +1 -1
- package/dist/components/Icon.cjs +1 -1
- package/dist/components/Icon.js +1 -1
- package/dist/components/IconButton.cjs +3 -3
- package/dist/components/IconButton.js +3 -3
- package/dist/components/IconButton.js.map +1 -1
- package/dist/components/Image.cjs +1 -1
- package/dist/components/Image.js +1 -1
- package/dist/components/Input.cjs +1 -1
- package/dist/components/Input.js +1 -1
- package/dist/components/Link.cjs +1 -1
- package/dist/components/Link.js +1 -1
- package/dist/components/Pressable.cjs +1 -1
- package/dist/components/Pressable.js +1 -1
- package/dist/components/Radio.cjs +1 -1
- package/dist/components/Radio.js +1 -1
- package/dist/components/Screen.cjs +1 -1
- package/dist/components/Screen.js +1 -1
- package/dist/components/Scrim.cjs +73 -0
- package/dist/components/Scrim.d.cts +43 -0
- package/dist/components/Scrim.d.cts.map +1 -0
- package/dist/components/Scrim.d.ts +43 -0
- package/dist/components/Scrim.d.ts.map +1 -0
- package/dist/components/Scrim.js +71 -0
- package/dist/components/Scrim.js.map +1 -0
- package/dist/components/Switch.cjs +1 -1
- package/dist/components/Switch.js +1 -1
- package/dist/components/Text.cjs +1 -1
- package/dist/components/Text.js +1 -1
- package/dist/components/UDSProvider.cjs +38 -0
- package/dist/components/UDSProvider.d.cts +30 -0
- package/dist/components/UDSProvider.d.cts.map +1 -0
- package/dist/components/UDSProvider.d.ts +30 -0
- package/dist/components/UDSProvider.d.ts.map +1 -0
- package/dist/components/UDSProvider.js +37 -0
- package/dist/components/UDSProvider.js.map +1 -0
- package/dist/native/UDSScreenCornerRadiusModule.cjs +30 -0
- package/dist/native/UDSScreenCornerRadiusModule.d.cts +12 -0
- package/dist/native/UDSScreenCornerRadiusModule.d.cts.map +1 -0
- package/dist/native/UDSScreenCornerRadiusModule.d.ts +12 -0
- package/dist/native/UDSScreenCornerRadiusModule.d.ts.map +1 -0
- package/dist/native/UDSScreenCornerRadiusModule.js +17 -0
- package/dist/native/UDSScreenCornerRadiusModule.js.map +1 -0
- package/dist/portal.cjs +81 -0
- package/dist/portal.d.cts +18 -0
- package/dist/portal.d.cts.map +1 -0
- package/dist/portal.d.ts +18 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +79 -0
- package/dist/portal.js.map +1 -0
- package/dist/types/dist/index.d.cts.map +1 -1
- package/dist/types/dist/index.d.ts.map +1 -1
- package/expo-module.config.json +9 -0
- package/generated/styles.cjs +10 -0
- package/generated/styles.d.ts +15 -2
- package/generated/styles.mjs +10 -0
- package/generated/unistyles.d.ts +21 -0
- package/ios/UDSMobile.podspec +25 -0
- package/ios/UDSScreenCornerRadiusModule.swift +218 -0
- package/package.json +40 -1
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
3
|
+
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
4
|
+
const require_components_BottomSheet_utils = require('./utils.cjs');
|
|
5
|
+
let react = require("react");
|
|
6
|
+
let react_native = require("react-native");
|
|
7
|
+
let react_native_reanimated = require("react-native-reanimated");
|
|
8
|
+
let react_native_gesture_handler = require("react-native-gesture-handler");
|
|
9
|
+
|
|
10
|
+
//#region src/components/BottomSheet/useBottomSheetDrag.ts
|
|
11
|
+
/**
|
|
12
|
+
* Velocity threshold (px/s) above which a downward fling dismisses the sheet.
|
|
13
|
+
*/
|
|
14
|
+
const DRAG_DISMISS_VELOCITY = 500;
|
|
15
|
+
/**
|
|
16
|
+
* Duration (ms) after opening during which drag is blocked to prevent
|
|
17
|
+
* accidentally stealing scroll intent.
|
|
18
|
+
*/
|
|
19
|
+
const OPEN_LOCK_TIMEOUT_MS = 250;
|
|
20
|
+
/**
|
|
21
|
+
* Horizontal movement (px) that causes the pan gesture to fail,
|
|
22
|
+
* handing control to horizontal scrolling or other gestures.
|
|
23
|
+
*/
|
|
24
|
+
const HORIZONTAL_FAIL_THRESHOLD = 20;
|
|
25
|
+
/**
|
|
26
|
+
* Minimum vertical movement (px) required before deciding whether to
|
|
27
|
+
* activate the pan gesture or let the scroll view handle the touch.
|
|
28
|
+
*/
|
|
29
|
+
const VERTICAL_ACTIVATION_THRESHOLD = 10;
|
|
30
|
+
/**
|
|
31
|
+
* Approximate height (px) of the header area (paddingTop + handle + header row).
|
|
32
|
+
* Touches above this threshold always activate the pan for sheet collapse.
|
|
33
|
+
* Touches below it may defer to the scroll view on Android.
|
|
34
|
+
*/
|
|
35
|
+
const HEADER_AREA_HEIGHT = 64;
|
|
36
|
+
/** How much drag past max snap is visible (0–1). Lower = more resistance. */
|
|
37
|
+
const RUBBER_BAND_FACTOR = .2;
|
|
38
|
+
/** Max enableOverstretch distance (px) past the max snap point. */
|
|
39
|
+
const MAX_OVERSTRETCH_PX = 30;
|
|
40
|
+
/**
|
|
41
|
+
* Builds the pan gesture that drives sheet dragging.
|
|
42
|
+
*
|
|
43
|
+
* Handles:
|
|
44
|
+
* - Drag-to-snap (nearest or velocity-based)
|
|
45
|
+
* - Drag-to-dismiss (past lowest snap threshold or fast fling)
|
|
46
|
+
* - Fresh-open lock (prevents accidental drag right after opening)
|
|
47
|
+
* - Scroll coordination (gates between sheet drag and content scroll)
|
|
48
|
+
*
|
|
49
|
+
* Scroll is only unlocked at the max snap point. At non-max snaps, dragging
|
|
50
|
+
* anywhere on the sheet expands or collapses it. At max snap, content is
|
|
51
|
+
* scrollable; dragging down from scroll-top collapses the sheet.
|
|
52
|
+
*
|
|
53
|
+
* All gesture callbacks run on the UI thread via Reanimated worklets.
|
|
54
|
+
*/
|
|
55
|
+
function useBottomSheetDrag({ enableDrag, dismissible, enableOverstretch, resolvedSnapPoints, containerHeight, bottomInsetPx, translateY, activeSnapIndex, isAnimating, animateToSnapWorklet, animateToCloseWorklet, onDismiss, scrollLocked, scrollOffsetY, getTranslateYForSnap }) {
|
|
56
|
+
const isDragActive = (0, react_native_reanimated.useSharedValue)(false);
|
|
57
|
+
const touchStartX = (0, react_native_reanimated.useSharedValue)(0);
|
|
58
|
+
const touchStartY = (0, react_native_reanimated.useSharedValue)(0);
|
|
59
|
+
const dragStartTranslateY = (0, react_native_reanimated.useSharedValue)(0);
|
|
60
|
+
const dragStartIndex = (0, react_native_reanimated.useSharedValue)(0);
|
|
61
|
+
const openTimeSV = (0, react_native_reanimated.useSharedValue)(0);
|
|
62
|
+
const panGestureRef = (0, react.useRef)(void 0);
|
|
63
|
+
const snapCount = resolvedSnapPoints.length;
|
|
64
|
+
const maxSnapIndex = snapCount - 1;
|
|
65
|
+
/** Compute all snap translateY positions — worklet. */
|
|
66
|
+
const getSnapTranslateYs = (0, react.useCallback)(() => {
|
|
67
|
+
"worklet";
|
|
68
|
+
const result = [];
|
|
69
|
+
for (let i = 0; i < snapCount; i++) result.push(getTranslateYForSnap(i));
|
|
70
|
+
return result;
|
|
71
|
+
}, [snapCount, getTranslateYForSnap]);
|
|
72
|
+
/** Dismiss threshold: 25% of the lowest snap height. */
|
|
73
|
+
const getDismissThreshold = (0, react.useCallback)(() => {
|
|
74
|
+
"worklet";
|
|
75
|
+
if (snapCount === 0) return 50;
|
|
76
|
+
return require_components_BottomSheet_utils.resolveHeightToPx(resolvedSnapPoints[0], containerHeight.value) * .25;
|
|
77
|
+
}, [
|
|
78
|
+
resolvedSnapPoints,
|
|
79
|
+
snapCount,
|
|
80
|
+
containerHeight
|
|
81
|
+
]);
|
|
82
|
+
const handleDismissJS = (0, react.useCallback)(() => {
|
|
83
|
+
onDismiss();
|
|
84
|
+
}, [onDismiss]);
|
|
85
|
+
return {
|
|
86
|
+
panGesture: react_native_gesture_handler.Gesture.Pan().withRef(panGestureRef).manualActivation(true).onTouchesDown((e, _stateManager) => {
|
|
87
|
+
"worklet";
|
|
88
|
+
const touch = e.allTouches[0];
|
|
89
|
+
if (touch) {
|
|
90
|
+
touchStartX.value = touch.absoluteX;
|
|
91
|
+
touchStartY.value = touch.absoluteY;
|
|
92
|
+
}
|
|
93
|
+
}).onTouchesMove((e, stateManager) => {
|
|
94
|
+
"worklet";
|
|
95
|
+
const touch = e.allTouches[0];
|
|
96
|
+
if (!touch) return;
|
|
97
|
+
const dx = touch.absoluteX - touchStartX.value;
|
|
98
|
+
const dy = touch.absoluteY - touchStartY.value;
|
|
99
|
+
if (Math.abs(dx) > HORIZONTAL_FAIL_THRESHOLD) {
|
|
100
|
+
stateManager.fail();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (Math.abs(dy) < VERTICAL_ACTIVATION_THRESHOLD) return;
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
if (openTimeSV.value > 0 && now - openTimeSV.value < OPEN_LOCK_TIMEOUT_MS) {
|
|
106
|
+
stateManager.fail();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (isAnimating.value) {
|
|
110
|
+
stateManager.fail();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (activeSnapIndex.value === maxSnapIndex) {
|
|
114
|
+
if (dy < 0) {
|
|
115
|
+
if (!enableOverstretch) {
|
|
116
|
+
stateManager.fail();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (touchStartY.value - translateY.value > HEADER_AREA_HEIGHT && (react_native.Platform.OS === "android" || scrollOffsetY.value > 1)) {
|
|
120
|
+
stateManager.fail();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
} else if (react_native.Platform.OS === "android" && scrollOffsetY.value > 1) {
|
|
124
|
+
if (touchStartY.value - translateY.value > HEADER_AREA_HEIGHT) {
|
|
125
|
+
stateManager.fail();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
stateManager.activate();
|
|
131
|
+
}).onStart(() => {
|
|
132
|
+
"worklet";
|
|
133
|
+
isDragActive.value = true;
|
|
134
|
+
dragStartTranslateY.value = translateY.value;
|
|
135
|
+
dragStartIndex.value = activeSnapIndex.value;
|
|
136
|
+
scrollLocked.value = true;
|
|
137
|
+
}).onUpdate((event) => {
|
|
138
|
+
"worklet";
|
|
139
|
+
if (!isDragActive.value) return;
|
|
140
|
+
const maxSnapTranslateY = snapCount > 0 ? getTranslateYForSnap(snapCount - 1) : 0;
|
|
141
|
+
const closedTranslateY = containerHeight.value + bottomInsetPx;
|
|
142
|
+
const nextY = dragStartTranslateY.value + event.translationY;
|
|
143
|
+
if (enableOverstretch && nextY < maxSnapTranslateY) {
|
|
144
|
+
const overflow = maxSnapTranslateY - nextY;
|
|
145
|
+
translateY.value = maxSnapTranslateY - Math.min(overflow * RUBBER_BAND_FACTOR, MAX_OVERSTRETCH_PX);
|
|
146
|
+
} else translateY.value = require_components_BottomSheet_utils.clamp(nextY, maxSnapTranslateY, closedTranslateY);
|
|
147
|
+
}).onEnd((event) => {
|
|
148
|
+
"worklet";
|
|
149
|
+
if (!isDragActive.value) return;
|
|
150
|
+
isDragActive.value = false;
|
|
151
|
+
const currentY = translateY.value;
|
|
152
|
+
const startIndex = dragStartIndex.value;
|
|
153
|
+
const snapTranslateYs = getSnapTranslateYs();
|
|
154
|
+
const startSnapY = snapTranslateYs[startIndex] ?? containerHeight.value;
|
|
155
|
+
const draggedDistance = dragStartTranslateY.value - currentY;
|
|
156
|
+
const draggedDown = draggedDistance < 0;
|
|
157
|
+
const draggedUp = draggedDistance > 0;
|
|
158
|
+
const velocityPxPerSec = Math.abs(event.velocityY);
|
|
159
|
+
const isAtLowestSnap = startIndex === 0;
|
|
160
|
+
const dismissThreshold = getDismissThreshold();
|
|
161
|
+
const draggedDownAmount = currentY - startSnapY;
|
|
162
|
+
if (dismissible && isAtLowestSnap && draggedDown && (velocityPxPerSec >= DRAG_DISMISS_VELOCITY || draggedDownAmount >= dismissThreshold)) {
|
|
163
|
+
scrollLocked.value = true;
|
|
164
|
+
animateToCloseWorklet();
|
|
165
|
+
(0, react_native_reanimated.runOnJS)(handleDismissJS)();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (velocityPxPerSec >= DRAG_DISMISS_VELOCITY && (draggedUp || draggedDown)) {
|
|
169
|
+
const nextIndex = require_components_BottomSheet_utils.clampIndex(startIndex + (draggedUp ? 1 : -1), snapCount);
|
|
170
|
+
scrollLocked.value = nextIndex !== maxSnapIndex;
|
|
171
|
+
animateToSnapWorklet(nextIndex);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const nextIndexInDirection = draggedUp ? Math.min(startIndex + 1, maxSnapIndex) : Math.max(startIndex - 1, 0);
|
|
175
|
+
const nextSnapY = snapTranslateYs[nextIndexInDirection] ?? startSnapY;
|
|
176
|
+
const distBetweenSnaps = Math.abs(nextSnapY - startSnapY);
|
|
177
|
+
const dragDist = Math.abs(currentY - startSnapY);
|
|
178
|
+
const targetIndex = distBetweenSnaps > 0 && dragDist > distBetweenSnaps * .25 ? nextIndexInDirection : startIndex;
|
|
179
|
+
scrollLocked.value = targetIndex !== maxSnapIndex;
|
|
180
|
+
animateToSnapWorklet(targetIndex);
|
|
181
|
+
}).onFinalize(() => {
|
|
182
|
+
"worklet";
|
|
183
|
+
if (isDragActive.value) isDragActive.value = false;
|
|
184
|
+
}).enabled(enableDrag),
|
|
185
|
+
panGestureRef,
|
|
186
|
+
openTimeSV
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
//#endregion
|
|
191
|
+
exports.useBottomSheetDrag = useBottomSheetDrag;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
import { BottomSheetHeight } from "./types.cjs";
|
|
3
|
+
import { GestureType } from "react-native-gesture-handler";
|
|
4
|
+
import { SharedValue } from "react-native-reanimated";
|
|
5
|
+
|
|
6
|
+
//#region src/components/BottomSheet/useBottomSheetDrag.d.ts
|
|
7
|
+
interface UseBottomSheetDragParams {
|
|
8
|
+
enableDrag: boolean;
|
|
9
|
+
dismissible: boolean;
|
|
10
|
+
enableOverstretch: boolean;
|
|
11
|
+
resolvedSnapPoints: BottomSheetHeight[];
|
|
12
|
+
containerHeight: SharedValue<number>;
|
|
13
|
+
bottomInsetPx: number;
|
|
14
|
+
translateY: SharedValue<number>;
|
|
15
|
+
activeSnapIndex: SharedValue<number>;
|
|
16
|
+
isAnimating: SharedValue<boolean>;
|
|
17
|
+
animateToSnapWorklet: (index: number) => void;
|
|
18
|
+
animateToCloseWorklet: () => void;
|
|
19
|
+
onDismiss: () => void;
|
|
20
|
+
/** Scroll lock — written here, read by BottomSheetContent. */
|
|
21
|
+
scrollLocked: SharedValue<boolean>;
|
|
22
|
+
/** Current content scroll offset (UI-thread shared value). */
|
|
23
|
+
scrollOffsetY: SharedValue<number>;
|
|
24
|
+
/** Compute translateY for a given snap index (worklet). Provided by the snap model. */
|
|
25
|
+
getTranslateYForSnap: (index: number) => number;
|
|
26
|
+
}
|
|
27
|
+
interface UseBottomSheetDragResult {
|
|
28
|
+
/** The pan gesture to attach to GestureDetector. */
|
|
29
|
+
panGesture: GestureType;
|
|
30
|
+
/** Ref to the pan gesture — for simultaneousWithExternalGesture in scroll. */
|
|
31
|
+
panGestureRef: React.RefObject<GestureType | undefined>;
|
|
32
|
+
/** Shared value — set to Date.now() when sheet opens, for fresh-open lock. */
|
|
33
|
+
openTimeSV: SharedValue<number>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Builds the pan gesture that drives sheet dragging.
|
|
37
|
+
*
|
|
38
|
+
* Handles:
|
|
39
|
+
* - Drag-to-snap (nearest or velocity-based)
|
|
40
|
+
* - Drag-to-dismiss (past lowest snap threshold or fast fling)
|
|
41
|
+
* - Fresh-open lock (prevents accidental drag right after opening)
|
|
42
|
+
* - Scroll coordination (gates between sheet drag and content scroll)
|
|
43
|
+
*
|
|
44
|
+
* Scroll is only unlocked at the max snap point. At non-max snaps, dragging
|
|
45
|
+
* anywhere on the sheet expands or collapses it. At max snap, content is
|
|
46
|
+
* scrollable; dragging down from scroll-top collapses the sheet.
|
|
47
|
+
*
|
|
48
|
+
* All gesture callbacks run on the UI thread via Reanimated worklets.
|
|
49
|
+
*/
|
|
50
|
+
declare function useBottomSheetDrag({
|
|
51
|
+
enableDrag,
|
|
52
|
+
dismissible,
|
|
53
|
+
enableOverstretch,
|
|
54
|
+
resolvedSnapPoints,
|
|
55
|
+
containerHeight,
|
|
56
|
+
bottomInsetPx,
|
|
57
|
+
translateY,
|
|
58
|
+
activeSnapIndex,
|
|
59
|
+
isAnimating,
|
|
60
|
+
animateToSnapWorklet,
|
|
61
|
+
animateToCloseWorklet,
|
|
62
|
+
onDismiss,
|
|
63
|
+
scrollLocked,
|
|
64
|
+
scrollOffsetY,
|
|
65
|
+
getTranslateYForSnap
|
|
66
|
+
}: UseBottomSheetDragParams): UseBottomSheetDragResult;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { useBottomSheetDrag };
|
|
69
|
+
//# sourceMappingURL=useBottomSheetDrag.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBottomSheetDrag.d.cts","names":[],"sources":["../../../src/components/BottomSheet/useBottomSheetDrag.ts"],"mappings":";;;;;;UA8CU,wBAAA;EACR,UAAA;EACA,WAAA;EACA,iBAAA;EACA,kBAAA,EAAoB,iBAAA;EACpB,eAAA,EAAiB,WAAA;EACjB,aAAA;EACA,UAAA,EAAY,WAAA;EACZ,eAAA,EAAiB,WAAA;EACjB,WAAA,EAAa,WAAA;EACb,oBAAA,GAAuB,KAAA;EACvB,qBAAA;EACA,SAAA;EAI0B;EAF1B,YAAA,EAAc,WAAA;EAZd;EAcA,aAAA,EAAe,WAAA;EAZf;EAcA,oBAAA,GAAuB,KAAA;AAAA;AAAA,UAGf,wBAAA;EAfR;EAiBA,UAAA,EAAY,WAAA;EAhBA;EAkBZ,aAAA,EAAe,KAAA,CAAM,SAAA,CAAU,WAAA;EAjBd;EAmBjB,UAAA,EAAY,WAAA;AAAA;;;;;;;;;;;;;AATwB;;;iBA2B7B,kBAAA,CAAA;EACP,UAAA;EACA,WAAA;EACA,iBAAA;EACA,kBAAA;EACA,eAAA;EACA,aAAA;EACA,UAAA;EACA,eAAA;EACA,WAAA;EACA,oBAAA;EACA,qBAAA;EACA,SAAA;EACA,YAAA;EACA,aAAA;EACA;AAAA,GACC,wBAAA,GAA2B,wBAAA"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
import { BottomSheetHeight } from "./types.js";
|
|
3
|
+
import { SharedValue } from "react-native-reanimated";
|
|
4
|
+
import { GestureType } from "react-native-gesture-handler";
|
|
5
|
+
|
|
6
|
+
//#region src/components/BottomSheet/useBottomSheetDrag.d.ts
|
|
7
|
+
interface UseBottomSheetDragParams {
|
|
8
|
+
enableDrag: boolean;
|
|
9
|
+
dismissible: boolean;
|
|
10
|
+
enableOverstretch: boolean;
|
|
11
|
+
resolvedSnapPoints: BottomSheetHeight[];
|
|
12
|
+
containerHeight: SharedValue<number>;
|
|
13
|
+
bottomInsetPx: number;
|
|
14
|
+
translateY: SharedValue<number>;
|
|
15
|
+
activeSnapIndex: SharedValue<number>;
|
|
16
|
+
isAnimating: SharedValue<boolean>;
|
|
17
|
+
animateToSnapWorklet: (index: number) => void;
|
|
18
|
+
animateToCloseWorklet: () => void;
|
|
19
|
+
onDismiss: () => void;
|
|
20
|
+
/** Scroll lock — written here, read by BottomSheetContent. */
|
|
21
|
+
scrollLocked: SharedValue<boolean>;
|
|
22
|
+
/** Current content scroll offset (UI-thread shared value). */
|
|
23
|
+
scrollOffsetY: SharedValue<number>;
|
|
24
|
+
/** Compute translateY for a given snap index (worklet). Provided by the snap model. */
|
|
25
|
+
getTranslateYForSnap: (index: number) => number;
|
|
26
|
+
}
|
|
27
|
+
interface UseBottomSheetDragResult {
|
|
28
|
+
/** The pan gesture to attach to GestureDetector. */
|
|
29
|
+
panGesture: GestureType;
|
|
30
|
+
/** Ref to the pan gesture — for simultaneousWithExternalGesture in scroll. */
|
|
31
|
+
panGestureRef: React.RefObject<GestureType | undefined>;
|
|
32
|
+
/** Shared value — set to Date.now() when sheet opens, for fresh-open lock. */
|
|
33
|
+
openTimeSV: SharedValue<number>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Builds the pan gesture that drives sheet dragging.
|
|
37
|
+
*
|
|
38
|
+
* Handles:
|
|
39
|
+
* - Drag-to-snap (nearest or velocity-based)
|
|
40
|
+
* - Drag-to-dismiss (past lowest snap threshold or fast fling)
|
|
41
|
+
* - Fresh-open lock (prevents accidental drag right after opening)
|
|
42
|
+
* - Scroll coordination (gates between sheet drag and content scroll)
|
|
43
|
+
*
|
|
44
|
+
* Scroll is only unlocked at the max snap point. At non-max snaps, dragging
|
|
45
|
+
* anywhere on the sheet expands or collapses it. At max snap, content is
|
|
46
|
+
* scrollable; dragging down from scroll-top collapses the sheet.
|
|
47
|
+
*
|
|
48
|
+
* All gesture callbacks run on the UI thread via Reanimated worklets.
|
|
49
|
+
*/
|
|
50
|
+
declare function useBottomSheetDrag({
|
|
51
|
+
enableDrag,
|
|
52
|
+
dismissible,
|
|
53
|
+
enableOverstretch,
|
|
54
|
+
resolvedSnapPoints,
|
|
55
|
+
containerHeight,
|
|
56
|
+
bottomInsetPx,
|
|
57
|
+
translateY,
|
|
58
|
+
activeSnapIndex,
|
|
59
|
+
isAnimating,
|
|
60
|
+
animateToSnapWorklet,
|
|
61
|
+
animateToCloseWorklet,
|
|
62
|
+
onDismiss,
|
|
63
|
+
scrollLocked,
|
|
64
|
+
scrollOffsetY,
|
|
65
|
+
getTranslateYForSnap
|
|
66
|
+
}: UseBottomSheetDragParams): UseBottomSheetDragResult;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { useBottomSheetDrag };
|
|
69
|
+
//# sourceMappingURL=useBottomSheetDrag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBottomSheetDrag.d.ts","names":[],"sources":["../../../src/components/BottomSheet/useBottomSheetDrag.ts"],"mappings":";;;;;;UA8CU,wBAAA;EACR,UAAA;EACA,WAAA;EACA,iBAAA;EACA,kBAAA,EAAoB,iBAAA;EACpB,eAAA,EAAiB,WAAA;EACjB,aAAA;EACA,UAAA,EAAY,WAAA;EACZ,eAAA,EAAiB,WAAA;EACjB,WAAA,EAAa,WAAA;EACb,oBAAA,GAAuB,KAAA;EACvB,qBAAA;EACA,SAAA;EAI0B;EAF1B,YAAA,EAAc,WAAA;EAZd;EAcA,aAAA,EAAe,WAAA;EAZf;EAcA,oBAAA,GAAuB,KAAA;AAAA;AAAA,UAGf,wBAAA;EAfR;EAiBA,UAAA,EAAY,WAAA;EAhBA;EAkBZ,aAAA,EAAe,KAAA,CAAM,SAAA,CAAU,WAAA;EAjBd;EAmBjB,UAAA,EAAY,WAAA;AAAA;;;;;;;;;;;;;AATwB;;;iBA2B7B,kBAAA,CAAA;EACP,UAAA;EACA,WAAA;EACA,iBAAA;EACA,kBAAA;EACA,eAAA;EACA,aAAA;EACA,UAAA;EACA,eAAA;EACA,WAAA;EACA,oBAAA;EACA,qBAAA;EACA,SAAA;EACA,YAAA;EACA,aAAA;EACA;AAAA,GACC,wBAAA,GAA2B,wBAAA"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
|
+
import { clamp, clampIndex, resolveHeightToPx } from "./utils.js";
|
|
3
|
+
import { useCallback, useRef } from "react";
|
|
4
|
+
import { Platform } from "react-native";
|
|
5
|
+
import { runOnJS, useSharedValue } from "react-native-reanimated";
|
|
6
|
+
import { Gesture } from "react-native-gesture-handler";
|
|
7
|
+
|
|
8
|
+
//#region src/components/BottomSheet/useBottomSheetDrag.ts
|
|
9
|
+
/**
|
|
10
|
+
* Velocity threshold (px/s) above which a downward fling dismisses the sheet.
|
|
11
|
+
*/
|
|
12
|
+
const DRAG_DISMISS_VELOCITY = 500;
|
|
13
|
+
/**
|
|
14
|
+
* Duration (ms) after opening during which drag is blocked to prevent
|
|
15
|
+
* accidentally stealing scroll intent.
|
|
16
|
+
*/
|
|
17
|
+
const OPEN_LOCK_TIMEOUT_MS = 250;
|
|
18
|
+
/**
|
|
19
|
+
* Horizontal movement (px) that causes the pan gesture to fail,
|
|
20
|
+
* handing control to horizontal scrolling or other gestures.
|
|
21
|
+
*/
|
|
22
|
+
const HORIZONTAL_FAIL_THRESHOLD = 20;
|
|
23
|
+
/**
|
|
24
|
+
* Minimum vertical movement (px) required before deciding whether to
|
|
25
|
+
* activate the pan gesture or let the scroll view handle the touch.
|
|
26
|
+
*/
|
|
27
|
+
const VERTICAL_ACTIVATION_THRESHOLD = 10;
|
|
28
|
+
/**
|
|
29
|
+
* Approximate height (px) of the header area (paddingTop + handle + header row).
|
|
30
|
+
* Touches above this threshold always activate the pan for sheet collapse.
|
|
31
|
+
* Touches below it may defer to the scroll view on Android.
|
|
32
|
+
*/
|
|
33
|
+
const HEADER_AREA_HEIGHT = 64;
|
|
34
|
+
/** How much drag past max snap is visible (0–1). Lower = more resistance. */
|
|
35
|
+
const RUBBER_BAND_FACTOR = .2;
|
|
36
|
+
/** Max enableOverstretch distance (px) past the max snap point. */
|
|
37
|
+
const MAX_OVERSTRETCH_PX = 30;
|
|
38
|
+
/**
|
|
39
|
+
* Builds the pan gesture that drives sheet dragging.
|
|
40
|
+
*
|
|
41
|
+
* Handles:
|
|
42
|
+
* - Drag-to-snap (nearest or velocity-based)
|
|
43
|
+
* - Drag-to-dismiss (past lowest snap threshold or fast fling)
|
|
44
|
+
* - Fresh-open lock (prevents accidental drag right after opening)
|
|
45
|
+
* - Scroll coordination (gates between sheet drag and content scroll)
|
|
46
|
+
*
|
|
47
|
+
* Scroll is only unlocked at the max snap point. At non-max snaps, dragging
|
|
48
|
+
* anywhere on the sheet expands or collapses it. At max snap, content is
|
|
49
|
+
* scrollable; dragging down from scroll-top collapses the sheet.
|
|
50
|
+
*
|
|
51
|
+
* All gesture callbacks run on the UI thread via Reanimated worklets.
|
|
52
|
+
*/
|
|
53
|
+
function useBottomSheetDrag({ enableDrag, dismissible, enableOverstretch, resolvedSnapPoints, containerHeight, bottomInsetPx, translateY, activeSnapIndex, isAnimating, animateToSnapWorklet, animateToCloseWorklet, onDismiss, scrollLocked, scrollOffsetY, getTranslateYForSnap }) {
|
|
54
|
+
const isDragActive = useSharedValue(false);
|
|
55
|
+
const touchStartX = useSharedValue(0);
|
|
56
|
+
const touchStartY = useSharedValue(0);
|
|
57
|
+
const dragStartTranslateY = useSharedValue(0);
|
|
58
|
+
const dragStartIndex = useSharedValue(0);
|
|
59
|
+
const openTimeSV = useSharedValue(0);
|
|
60
|
+
const panGestureRef = useRef(void 0);
|
|
61
|
+
const snapCount = resolvedSnapPoints.length;
|
|
62
|
+
const maxSnapIndex = snapCount - 1;
|
|
63
|
+
/** Compute all snap translateY positions — worklet. */
|
|
64
|
+
const getSnapTranslateYs = useCallback(() => {
|
|
65
|
+
"worklet";
|
|
66
|
+
const result = [];
|
|
67
|
+
for (let i = 0; i < snapCount; i++) result.push(getTranslateYForSnap(i));
|
|
68
|
+
return result;
|
|
69
|
+
}, [snapCount, getTranslateYForSnap]);
|
|
70
|
+
/** Dismiss threshold: 25% of the lowest snap height. */
|
|
71
|
+
const getDismissThreshold = useCallback(() => {
|
|
72
|
+
"worklet";
|
|
73
|
+
if (snapCount === 0) return 50;
|
|
74
|
+
return resolveHeightToPx(resolvedSnapPoints[0], containerHeight.value) * .25;
|
|
75
|
+
}, [
|
|
76
|
+
resolvedSnapPoints,
|
|
77
|
+
snapCount,
|
|
78
|
+
containerHeight
|
|
79
|
+
]);
|
|
80
|
+
const handleDismissJS = useCallback(() => {
|
|
81
|
+
onDismiss();
|
|
82
|
+
}, [onDismiss]);
|
|
83
|
+
return {
|
|
84
|
+
panGesture: Gesture.Pan().withRef(panGestureRef).manualActivation(true).onTouchesDown((e, _stateManager) => {
|
|
85
|
+
"worklet";
|
|
86
|
+
const touch = e.allTouches[0];
|
|
87
|
+
if (touch) {
|
|
88
|
+
touchStartX.value = touch.absoluteX;
|
|
89
|
+
touchStartY.value = touch.absoluteY;
|
|
90
|
+
}
|
|
91
|
+
}).onTouchesMove((e, stateManager) => {
|
|
92
|
+
"worklet";
|
|
93
|
+
const touch = e.allTouches[0];
|
|
94
|
+
if (!touch) return;
|
|
95
|
+
const dx = touch.absoluteX - touchStartX.value;
|
|
96
|
+
const dy = touch.absoluteY - touchStartY.value;
|
|
97
|
+
if (Math.abs(dx) > HORIZONTAL_FAIL_THRESHOLD) {
|
|
98
|
+
stateManager.fail();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (Math.abs(dy) < VERTICAL_ACTIVATION_THRESHOLD) return;
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
if (openTimeSV.value > 0 && now - openTimeSV.value < OPEN_LOCK_TIMEOUT_MS) {
|
|
104
|
+
stateManager.fail();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (isAnimating.value) {
|
|
108
|
+
stateManager.fail();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (activeSnapIndex.value === maxSnapIndex) {
|
|
112
|
+
if (dy < 0) {
|
|
113
|
+
if (!enableOverstretch) {
|
|
114
|
+
stateManager.fail();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (touchStartY.value - translateY.value > HEADER_AREA_HEIGHT && (Platform.OS === "android" || scrollOffsetY.value > 1)) {
|
|
118
|
+
stateManager.fail();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
} else if (Platform.OS === "android" && scrollOffsetY.value > 1) {
|
|
122
|
+
if (touchStartY.value - translateY.value > HEADER_AREA_HEIGHT) {
|
|
123
|
+
stateManager.fail();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
stateManager.activate();
|
|
129
|
+
}).onStart(() => {
|
|
130
|
+
"worklet";
|
|
131
|
+
isDragActive.value = true;
|
|
132
|
+
dragStartTranslateY.value = translateY.value;
|
|
133
|
+
dragStartIndex.value = activeSnapIndex.value;
|
|
134
|
+
scrollLocked.value = true;
|
|
135
|
+
}).onUpdate((event) => {
|
|
136
|
+
"worklet";
|
|
137
|
+
if (!isDragActive.value) return;
|
|
138
|
+
const maxSnapTranslateY = snapCount > 0 ? getTranslateYForSnap(snapCount - 1) : 0;
|
|
139
|
+
const closedTranslateY = containerHeight.value + bottomInsetPx;
|
|
140
|
+
const nextY = dragStartTranslateY.value + event.translationY;
|
|
141
|
+
if (enableOverstretch && nextY < maxSnapTranslateY) {
|
|
142
|
+
const overflow = maxSnapTranslateY - nextY;
|
|
143
|
+
translateY.value = maxSnapTranslateY - Math.min(overflow * RUBBER_BAND_FACTOR, MAX_OVERSTRETCH_PX);
|
|
144
|
+
} else translateY.value = clamp(nextY, maxSnapTranslateY, closedTranslateY);
|
|
145
|
+
}).onEnd((event) => {
|
|
146
|
+
"worklet";
|
|
147
|
+
if (!isDragActive.value) return;
|
|
148
|
+
isDragActive.value = false;
|
|
149
|
+
const currentY = translateY.value;
|
|
150
|
+
const startIndex = dragStartIndex.value;
|
|
151
|
+
const snapTranslateYs = getSnapTranslateYs();
|
|
152
|
+
const startSnapY = snapTranslateYs[startIndex] ?? containerHeight.value;
|
|
153
|
+
const draggedDistance = dragStartTranslateY.value - currentY;
|
|
154
|
+
const draggedDown = draggedDistance < 0;
|
|
155
|
+
const draggedUp = draggedDistance > 0;
|
|
156
|
+
const velocityPxPerSec = Math.abs(event.velocityY);
|
|
157
|
+
const isAtLowestSnap = startIndex === 0;
|
|
158
|
+
const dismissThreshold = getDismissThreshold();
|
|
159
|
+
const draggedDownAmount = currentY - startSnapY;
|
|
160
|
+
if (dismissible && isAtLowestSnap && draggedDown && (velocityPxPerSec >= DRAG_DISMISS_VELOCITY || draggedDownAmount >= dismissThreshold)) {
|
|
161
|
+
scrollLocked.value = true;
|
|
162
|
+
animateToCloseWorklet();
|
|
163
|
+
runOnJS(handleDismissJS)();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (velocityPxPerSec >= DRAG_DISMISS_VELOCITY && (draggedUp || draggedDown)) {
|
|
167
|
+
const nextIndex = clampIndex(startIndex + (draggedUp ? 1 : -1), snapCount);
|
|
168
|
+
scrollLocked.value = nextIndex !== maxSnapIndex;
|
|
169
|
+
animateToSnapWorklet(nextIndex);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const nextIndexInDirection = draggedUp ? Math.min(startIndex + 1, maxSnapIndex) : Math.max(startIndex - 1, 0);
|
|
173
|
+
const nextSnapY = snapTranslateYs[nextIndexInDirection] ?? startSnapY;
|
|
174
|
+
const distBetweenSnaps = Math.abs(nextSnapY - startSnapY);
|
|
175
|
+
const dragDist = Math.abs(currentY - startSnapY);
|
|
176
|
+
const targetIndex = distBetweenSnaps > 0 && dragDist > distBetweenSnaps * .25 ? nextIndexInDirection : startIndex;
|
|
177
|
+
scrollLocked.value = targetIndex !== maxSnapIndex;
|
|
178
|
+
animateToSnapWorklet(targetIndex);
|
|
179
|
+
}).onFinalize(() => {
|
|
180
|
+
"worklet";
|
|
181
|
+
if (isDragActive.value) isDragActive.value = false;
|
|
182
|
+
}).enabled(enableDrag),
|
|
183
|
+
panGestureRef,
|
|
184
|
+
openTimeSV
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//#endregion
|
|
189
|
+
export { useBottomSheetDrag };
|
|
190
|
+
//# sourceMappingURL=useBottomSheetDrag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBottomSheetDrag.js","names":[],"sources":["../../../src/components/BottomSheet/useBottomSheetDrag.ts"],"sourcesContent":["import { useCallback, useRef } from 'react';\nimport { Platform } from 'react-native';\nimport type { GestureType } from 'react-native-gesture-handler';\nimport { Gesture } from 'react-native-gesture-handler';\nimport type { SharedValue } from 'react-native-reanimated';\nimport { runOnJS, useSharedValue } from 'react-native-reanimated';\n\nimport type { BottomSheetHeight } from './types';\nimport { clamp, clampIndex, resolveHeightToPx } from './utils';\n\n/**\n * Velocity threshold (px/s) above which a downward fling dismisses the sheet.\n */\nconst DRAG_DISMISS_VELOCITY = 500;\n\n/**\n * Duration (ms) after opening during which drag is blocked to prevent\n * accidentally stealing scroll intent.\n */\nconst OPEN_LOCK_TIMEOUT_MS = 250;\n\n/**\n * Horizontal movement (px) that causes the pan gesture to fail,\n * handing control to horizontal scrolling or other gestures.\n */\nconst HORIZONTAL_FAIL_THRESHOLD = 20;\n\n/**\n * Minimum vertical movement (px) required before deciding whether to\n * activate the pan gesture or let the scroll view handle the touch.\n */\nconst VERTICAL_ACTIVATION_THRESHOLD = 10;\n\n/**\n * Approximate height (px) of the header area (paddingTop + handle + header row).\n * Touches above this threshold always activate the pan for sheet collapse.\n * Touches below it may defer to the scroll view on Android.\n */\nconst HEADER_AREA_HEIGHT = 64;\n\n/** How much drag past max snap is visible (0–1). Lower = more resistance. */\nconst RUBBER_BAND_FACTOR = 0.2;\n\n/** Max enableOverstretch distance (px) past the max snap point. */\nconst MAX_OVERSTRETCH_PX = 30;\n\ninterface UseBottomSheetDragParams {\n enableDrag: boolean;\n dismissible: boolean;\n enableOverstretch: boolean;\n resolvedSnapPoints: BottomSheetHeight[];\n containerHeight: SharedValue<number>;\n bottomInsetPx: number;\n translateY: SharedValue<number>;\n activeSnapIndex: SharedValue<number>;\n isAnimating: SharedValue<boolean>;\n animateToSnapWorklet: (index: number) => void;\n animateToCloseWorklet: () => void;\n onDismiss: () => void;\n /** Scroll lock — written here, read by BottomSheetContent. */\n scrollLocked: SharedValue<boolean>;\n /** Current content scroll offset (UI-thread shared value). */\n scrollOffsetY: SharedValue<number>;\n /** Compute translateY for a given snap index (worklet). Provided by the snap model. */\n getTranslateYForSnap: (index: number) => number;\n}\n\ninterface UseBottomSheetDragResult {\n /** The pan gesture to attach to GestureDetector. */\n panGesture: GestureType;\n /** Ref to the pan gesture — for simultaneousWithExternalGesture in scroll. */\n panGestureRef: React.RefObject<GestureType | undefined>;\n /** Shared value — set to Date.now() when sheet opens, for fresh-open lock. */\n openTimeSV: SharedValue<number>;\n}\n\n/**\n * Builds the pan gesture that drives sheet dragging.\n *\n * Handles:\n * - Drag-to-snap (nearest or velocity-based)\n * - Drag-to-dismiss (past lowest snap threshold or fast fling)\n * - Fresh-open lock (prevents accidental drag right after opening)\n * - Scroll coordination (gates between sheet drag and content scroll)\n *\n * Scroll is only unlocked at the max snap point. At non-max snaps, dragging\n * anywhere on the sheet expands or collapses it. At max snap, content is\n * scrollable; dragging down from scroll-top collapses the sheet.\n *\n * All gesture callbacks run on the UI thread via Reanimated worklets.\n */\nfunction useBottomSheetDrag({\n enableDrag,\n dismissible,\n enableOverstretch,\n resolvedSnapPoints,\n containerHeight,\n bottomInsetPx,\n translateY,\n activeSnapIndex,\n isAnimating,\n animateToSnapWorklet,\n animateToCloseWorklet,\n onDismiss,\n scrollLocked,\n scrollOffsetY,\n getTranslateYForSnap,\n}: UseBottomSheetDragParams): UseBottomSheetDragResult {\n // Shared values for drag state (UI thread).\n const isDragActive = useSharedValue(false);\n const touchStartX = useSharedValue(0);\n const touchStartY = useSharedValue(0);\n const dragStartTranslateY = useSharedValue(0);\n const dragStartIndex = useSharedValue(0);\n const openTimeSV = useSharedValue(0);\n\n // Ref for the pan gesture — passed to BottomSheetContent for gesture composition.\n const panGestureRef = useRef<GestureType | undefined>(undefined);\n\n const snapCount = resolvedSnapPoints.length;\n const maxSnapIndex = snapCount - 1;\n\n /** Compute all snap translateY positions — worklet. */\n const getSnapTranslateYs = useCallback((): number[] => {\n 'worklet';\n const result: number[] = [];\n for (let i = 0; i < snapCount; i++) {\n result.push(getTranslateYForSnap(i));\n }\n return result;\n }, [snapCount, getTranslateYForSnap]);\n\n /** Dismiss threshold: 25% of the lowest snap height. */\n const getDismissThreshold = useCallback((): number => {\n 'worklet';\n if (snapCount === 0) {\n return 50;\n }\n const lowestSnapHeight = resolveHeightToPx(resolvedSnapPoints[0]!, containerHeight.value);\n return lowestSnapHeight * 0.25;\n }, [resolvedSnapPoints, snapCount, containerHeight]);\n\n const handleDismissJS = useCallback(() => {\n onDismiss();\n }, [onDismiss]);\n\n const panGesture = Gesture.Pan()\n .withRef(panGestureRef)\n .manualActivation(true)\n .onTouchesDown((e, _stateManager) => {\n 'worklet';\n const touch = e.allTouches[0];\n if (touch) {\n touchStartX.value = touch.absoluteX;\n touchStartY.value = touch.absoluteY;\n }\n })\n .onTouchesMove((e, stateManager) => {\n 'worklet';\n const touch = e.allTouches[0];\n if (!touch) {\n return;\n }\n\n const dx = touch.absoluteX - touchStartX.value;\n const dy = touch.absoluteY - touchStartY.value;\n\n // Fail on horizontal movement (replaces failOffsetX).\n if (Math.abs(dx) > HORIZONTAL_FAIL_THRESHOLD) {\n stateManager.fail();\n return;\n }\n\n // Wait for sufficient vertical movement (replaces activeOffsetY).\n if (Math.abs(dy) < VERTICAL_ACTIVATION_THRESHOLD) {\n return;\n }\n\n // Fresh-open lock.\n const now = Date.now();\n if (openTimeSV.value > 0 && now - openTimeSV.value < OPEN_LOCK_TIMEOUT_MS) {\n stateManager.fail();\n return;\n }\n\n // Don't activate during animation.\n if (isAnimating.value) {\n stateManager.fail();\n return;\n }\n\n const isAtMaxSnap = activeSnapIndex.value === maxSnapIndex;\n\n if (isAtMaxSnap) {\n if (dy < 0) {\n if (!enableOverstretch) {\n // No enableOverstretch — let the scroll view handle all upward drags.\n stateManager.fail();\n return;\n }\n // Header/handle: allow rubber-band enableOverstretch.\n // Content area: defer to scroll view (always on Android,\n // only when scrolled on iOS).\n const localY = touchStartY.value - translateY.value;\n const isInContentArea = localY > HEADER_AREA_HEIGHT;\n if (isInContentArea && (Platform.OS === 'android' || scrollOffsetY.value > 1)) {\n stateManager.fail();\n return;\n }\n } else {\n // Dragging down on Android: defer to scroll view in content\n // area when scrolled. Header drags always collapse.\n if (Platform.OS === 'android' && scrollOffsetY.value > 1) {\n const localY = touchStartY.value - translateY.value;\n if (localY > HEADER_AREA_HEIGHT) {\n stateManager.fail();\n return;\n }\n }\n }\n }\n\n stateManager.activate();\n })\n .onStart(() => {\n 'worklet';\n isDragActive.value = true;\n dragStartTranslateY.value = translateY.value;\n dragStartIndex.value = activeSnapIndex.value;\n scrollLocked.value = true;\n })\n .onUpdate((event) => {\n 'worklet';\n if (!isDragActive.value) {\n return;\n }\n\n const maxSnapTranslateY = snapCount > 0 ? getTranslateYForSnap(snapCount - 1) : 0;\n const closedTranslateY = containerHeight.value + bottomInsetPx;\n\n const nextY = dragStartTranslateY.value + event.translationY;\n\n if (enableOverstretch && nextY < maxSnapTranslateY) {\n // Rubber-band enableOverstretch past max snap.\n const overflow = maxSnapTranslateY - nextY;\n const dampedOverflow = Math.min(overflow * RUBBER_BAND_FACTOR, MAX_OVERSTRETCH_PX);\n translateY.value = maxSnapTranslateY - dampedOverflow;\n } else {\n translateY.value = clamp(nextY, maxSnapTranslateY, closedTranslateY);\n }\n })\n .onEnd((event) => {\n 'worklet';\n if (!isDragActive.value) {\n return;\n }\n isDragActive.value = false;\n\n const currentY = translateY.value;\n const startIndex = dragStartIndex.value;\n const snapTranslateYs = getSnapTranslateYs();\n\n // Compute drag metrics.\n const startSnapY = snapTranslateYs[startIndex] ?? containerHeight.value;\n const draggedDistance = dragStartTranslateY.value - currentY;\n const draggedDown = draggedDistance < 0;\n const draggedUp = draggedDistance > 0;\n const velocityPxPerSec = Math.abs(event.velocityY);\n\n // 1. Dismiss check: at lowest snap, dragged down, meets threshold.\n const isAtLowestSnap = startIndex === 0;\n const dismissThreshold = getDismissThreshold();\n const draggedDownAmount = currentY - startSnapY;\n\n if (\n dismissible &&\n isAtLowestSnap &&\n draggedDown &&\n (velocityPxPerSec >= DRAG_DISMISS_VELOCITY || draggedDownAmount >= dismissThreshold)\n ) {\n scrollLocked.value = true;\n animateToCloseWorklet();\n runOnJS(handleDismissJS)();\n return;\n }\n\n // 2. Velocity snap: fast enough → move one snap in drag direction.\n if (velocityPxPerSec >= DRAG_DISMISS_VELOCITY && (draggedUp || draggedDown)) {\n const direction = draggedUp ? 1 : -1;\n const nextIndex = clampIndex(startIndex + direction, snapCount);\n scrollLocked.value = nextIndex !== maxSnapIndex;\n animateToSnapWorklet(nextIndex);\n return;\n }\n\n // 3. Direction-aware snap: if the user dragged past 25% of the distance\n // to the next snap in their drag direction, move to that snap.\n // Otherwise snap back to the starting snap.\n const nextIndexInDirection = draggedUp\n ? Math.min(startIndex + 1, maxSnapIndex)\n : Math.max(startIndex - 1, 0);\n const nextSnapY = snapTranslateYs[nextIndexInDirection] ?? startSnapY;\n const distBetweenSnaps = Math.abs(nextSnapY - startSnapY);\n const dragDist = Math.abs(currentY - startSnapY);\n\n const targetIndex =\n distBetweenSnaps > 0 && dragDist > distBetweenSnaps * 0.25\n ? nextIndexInDirection\n : startIndex;\n\n scrollLocked.value = targetIndex !== maxSnapIndex;\n animateToSnapWorklet(targetIndex);\n })\n .onFinalize(() => {\n 'worklet';\n if (isDragActive.value) {\n isDragActive.value = false;\n }\n })\n .enabled(enableDrag);\n\n return {\n panGesture,\n panGestureRef,\n openTimeSV,\n };\n}\n\nexport { useBottomSheetDrag };\n"],"mappings":";;;;;;;;;;;AAaA,MAAM,wBAAwB;;;;;AAM9B,MAAM,uBAAuB;;;;;AAM7B,MAAM,4BAA4B;;;;;AAMlC,MAAM,gCAAgC;;;;;;AAOtC,MAAM,qBAAqB;;AAG3B,MAAM,qBAAqB;;AAG3B,MAAM,qBAAqB;;;;;;;;;;;;;;;;AA+C3B,SAAS,mBAAmB,EAC1B,YACA,aACA,mBACA,oBACA,iBACA,eACA,YACA,iBACA,aACA,sBACA,uBACA,WACA,cACA,eACA,wBACqD;CAErD,MAAM,eAAe,eAAe,MAAM;CAC1C,MAAM,cAAc,eAAe,EAAE;CACrC,MAAM,cAAc,eAAe,EAAE;CACrC,MAAM,sBAAsB,eAAe,EAAE;CAC7C,MAAM,iBAAiB,eAAe,EAAE;CACxC,MAAM,aAAa,eAAe,EAAE;CAGpC,MAAM,gBAAgB,OAAgC,OAAU;CAEhE,MAAM,YAAY,mBAAmB;CACrC,MAAM,eAAe,YAAY;;CAGjC,MAAM,qBAAqB,kBAA4B;AACrD;EACA,MAAM,SAAmB,EAAE;AAC3B,OAAK,IAAI,IAAI,GAAG,IAAI,WAAW,IAC7B,QAAO,KAAK,qBAAqB,EAAE,CAAC;AAEtC,SAAO;IACN,CAAC,WAAW,qBAAqB,CAAC;;CAGrC,MAAM,sBAAsB,kBAA0B;AACpD;AACA,MAAI,cAAc,EAChB,QAAO;AAGT,SADyB,kBAAkB,mBAAmB,IAAK,gBAAgB,MAAM,GAC/D;IACzB;EAAC;EAAoB;EAAW;EAAgB,CAAC;CAEpD,MAAM,kBAAkB,kBAAkB;AACxC,aAAW;IACV,CAAC,UAAU,CAAC;AAiLf,QAAO;EACL,YAhLiB,QAAQ,KAAK,CAC7B,QAAQ,cAAc,CACtB,iBAAiB,KAAK,CACtB,eAAe,GAAG,kBAAkB;AACnC;GACA,MAAM,QAAQ,EAAE,WAAW;AAC3B,OAAI,OAAO;AACT,gBAAY,QAAQ,MAAM;AAC1B,gBAAY,QAAQ,MAAM;;IAE5B,CACD,eAAe,GAAG,iBAAiB;AAClC;GACA,MAAM,QAAQ,EAAE,WAAW;AAC3B,OAAI,CAAC,MACH;GAGF,MAAM,KAAK,MAAM,YAAY,YAAY;GACzC,MAAM,KAAK,MAAM,YAAY,YAAY;AAGzC,OAAI,KAAK,IAAI,GAAG,GAAG,2BAA2B;AAC5C,iBAAa,MAAM;AACnB;;AAIF,OAAI,KAAK,IAAI,GAAG,GAAG,8BACjB;GAIF,MAAM,MAAM,KAAK,KAAK;AACtB,OAAI,WAAW,QAAQ,KAAK,MAAM,WAAW,QAAQ,sBAAsB;AACzE,iBAAa,MAAM;AACnB;;AAIF,OAAI,YAAY,OAAO;AACrB,iBAAa,MAAM;AACnB;;AAKF,OAFoB,gBAAgB,UAAU,cAG5C;QAAI,KAAK,GAAG;AACV,SAAI,CAAC,mBAAmB;AAEtB,mBAAa,MAAM;AACnB;;AAOF,SAFe,YAAY,QAAQ,WAAW,QACb,uBACT,SAAS,OAAO,aAAa,cAAc,QAAQ,IAAI;AAC7E,mBAAa,MAAM;AACnB;;eAKE,SAAS,OAAO,aAAa,cAAc,QAAQ,GAErD;SADe,YAAY,QAAQ,WAAW,QACjC,oBAAoB;AAC/B,mBAAa,MAAM;AACnB;;;;AAMR,gBAAa,UAAU;IACvB,CACD,cAAc;AACb;AACA,gBAAa,QAAQ;AACrB,uBAAoB,QAAQ,WAAW;AACvC,kBAAe,QAAQ,gBAAgB;AACvC,gBAAa,QAAQ;IACrB,CACD,UAAU,UAAU;AACnB;AACA,OAAI,CAAC,aAAa,MAChB;GAGF,MAAM,oBAAoB,YAAY,IAAI,qBAAqB,YAAY,EAAE,GAAG;GAChF,MAAM,mBAAmB,gBAAgB,QAAQ;GAEjD,MAAM,QAAQ,oBAAoB,QAAQ,MAAM;AAEhD,OAAI,qBAAqB,QAAQ,mBAAmB;IAElD,MAAM,WAAW,oBAAoB;AAErC,eAAW,QAAQ,oBADI,KAAK,IAAI,WAAW,oBAAoB,mBAAmB;SAGlF,YAAW,QAAQ,MAAM,OAAO,mBAAmB,iBAAiB;IAEtE,CACD,OAAO,UAAU;AAChB;AACA,OAAI,CAAC,aAAa,MAChB;AAEF,gBAAa,QAAQ;GAErB,MAAM,WAAW,WAAW;GAC5B,MAAM,aAAa,eAAe;GAClC,MAAM,kBAAkB,oBAAoB;GAG5C,MAAM,aAAa,gBAAgB,eAAe,gBAAgB;GAClE,MAAM,kBAAkB,oBAAoB,QAAQ;GACpD,MAAM,cAAc,kBAAkB;GACtC,MAAM,YAAY,kBAAkB;GACpC,MAAM,mBAAmB,KAAK,IAAI,MAAM,UAAU;GAGlD,MAAM,iBAAiB,eAAe;GACtC,MAAM,mBAAmB,qBAAqB;GAC9C,MAAM,oBAAoB,WAAW;AAErC,OACE,eACA,kBACA,gBACC,oBAAoB,yBAAyB,qBAAqB,mBACnE;AACA,iBAAa,QAAQ;AACrB,2BAAuB;AACvB,YAAQ,gBAAgB,EAAE;AAC1B;;AAIF,OAAI,oBAAoB,0BAA0B,aAAa,cAAc;IAE3E,MAAM,YAAY,WAAW,cADX,YAAY,IAAI,KACmB,UAAU;AAC/D,iBAAa,QAAQ,cAAc;AACnC,yBAAqB,UAAU;AAC/B;;GAMF,MAAM,uBAAuB,YACzB,KAAK,IAAI,aAAa,GAAG,aAAa,GACtC,KAAK,IAAI,aAAa,GAAG,EAAE;GAC/B,MAAM,YAAY,gBAAgB,yBAAyB;GAC3D,MAAM,mBAAmB,KAAK,IAAI,YAAY,WAAW;GACzD,MAAM,WAAW,KAAK,IAAI,WAAW,WAAW;GAEhD,MAAM,cACJ,mBAAmB,KAAK,WAAW,mBAAmB,MAClD,uBACA;AAEN,gBAAa,QAAQ,gBAAgB;AACrC,wBAAqB,YAAY;IACjC,CACD,iBAAiB;AAChB;AACA,OAAI,aAAa,MACf,cAAa,QAAQ;IAEvB,CACD,QAAQ,WAAW;EAIpB;EACA;EACD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
3
|
+
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
4
|
+
const require_components_BottomSheet_BottomSheetInternalProvider = require('./BottomSheetInternalProvider.cjs');
|
|
5
|
+
let react_native_reanimated = require("react-native-reanimated");
|
|
6
|
+
let react_native_gesture_handler = require("react-native-gesture-handler");
|
|
7
|
+
|
|
8
|
+
//#region src/components/BottomSheet/useBottomSheetScroll.ts
|
|
9
|
+
/**
|
|
10
|
+
* Low-level hook for consumers who need a custom scrollable (e.g. `FlatList`,
|
|
11
|
+
* `SectionList`) instead of `<BottomSheetContent>`.
|
|
12
|
+
*
|
|
13
|
+
* Returns all the props needed to wire a scrollable into the sheet's
|
|
14
|
+
* scroll-lock coordination (scrollEnabled toggle, offset tracking, gesture composition).
|
|
15
|
+
*
|
|
16
|
+
* Must be rendered inside a `<BottomSheet>`.
|
|
17
|
+
*
|
|
18
|
+
* @throws If used outside a `<BottomSheet>` component tree.
|
|
19
|
+
*
|
|
20
|
+
* @returns `{ scrollHandler, scrollableRef, nativeGesture, animatedProps }`
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* const { scrollHandler, scrollableRef, nativeGesture, animatedProps } = useBottomSheetScroll();
|
|
25
|
+
*
|
|
26
|
+
* <GestureDetector gesture={nativeGesture}>
|
|
27
|
+
* <Animated.FlatList
|
|
28
|
+
* ref={scrollableRef}
|
|
29
|
+
* onScroll={scrollHandler}
|
|
30
|
+
* scrollEventThrottle={16}
|
|
31
|
+
* animatedProps={animatedProps}
|
|
32
|
+
* bounces={false}
|
|
33
|
+
* overScrollMode="never"
|
|
34
|
+
* {...otherProps}
|
|
35
|
+
* />
|
|
36
|
+
* </GestureDetector>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
function useBottomSheetScroll() {
|
|
40
|
+
const internalCtx = require_components_BottomSheet_BottomSheetInternalProvider.useBottomSheetInternalContext();
|
|
41
|
+
const scrollableRef = (0, react_native_reanimated.useAnimatedRef)();
|
|
42
|
+
const animatedProps = (0, react_native_reanimated.useAnimatedProps)(() => ({
|
|
43
|
+
scrollEnabled: !internalCtx?.scrollLocked.value,
|
|
44
|
+
decelerationRate: internalCtx?.scrollLocked.value ? 0 : .998
|
|
45
|
+
}));
|
|
46
|
+
const scrollHandler = (0, react_native_reanimated.useAnimatedScrollHandler)({ onScroll: (event) => {
|
|
47
|
+
if (!internalCtx) return;
|
|
48
|
+
internalCtx.scrollOffsetY.value = event.contentOffset.y;
|
|
49
|
+
} });
|
|
50
|
+
if (!internalCtx) throw new Error("useBottomSheetScroll must be used inside a BottomSheet component.");
|
|
51
|
+
const { panGestureRef } = internalCtx;
|
|
52
|
+
return {
|
|
53
|
+
scrollHandler,
|
|
54
|
+
scrollableRef,
|
|
55
|
+
nativeGesture: react_native_gesture_handler.Gesture.Native().simultaneousWithExternalGesture(panGestureRef),
|
|
56
|
+
animatedProps
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
exports.useBottomSheetScroll = useBottomSheetScroll;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
|
|
2
|
+
import * as react_native_reanimated0 from "react-native-reanimated";
|
|
3
|
+
import Animated from "react-native-reanimated";
|
|
4
|
+
import * as react_native_gesture_handler_lib_typescript_handlers_gestures_nativeGesture0 from "react-native-gesture-handler/lib/typescript/handlers/gestures/nativeGesture";
|
|
5
|
+
|
|
6
|
+
//#region src/components/BottomSheet/useBottomSheetScroll.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Low-level hook for consumers who need a custom scrollable (e.g. `FlatList`,
|
|
9
|
+
* `SectionList`) instead of `<BottomSheetContent>`.
|
|
10
|
+
*
|
|
11
|
+
* Returns all the props needed to wire a scrollable into the sheet's
|
|
12
|
+
* scroll-lock coordination (scrollEnabled toggle, offset tracking, gesture composition).
|
|
13
|
+
*
|
|
14
|
+
* Must be rendered inside a `<BottomSheet>`.
|
|
15
|
+
*
|
|
16
|
+
* @throws If used outside a `<BottomSheet>` component tree.
|
|
17
|
+
*
|
|
18
|
+
* @returns `{ scrollHandler, scrollableRef, nativeGesture, animatedProps }`
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const { scrollHandler, scrollableRef, nativeGesture, animatedProps } = useBottomSheetScroll();
|
|
23
|
+
*
|
|
24
|
+
* <GestureDetector gesture={nativeGesture}>
|
|
25
|
+
* <Animated.FlatList
|
|
26
|
+
* ref={scrollableRef}
|
|
27
|
+
* onScroll={scrollHandler}
|
|
28
|
+
* scrollEventThrottle={16}
|
|
29
|
+
* animatedProps={animatedProps}
|
|
30
|
+
* bounces={false}
|
|
31
|
+
* overScrollMode="never"
|
|
32
|
+
* {...otherProps}
|
|
33
|
+
* />
|
|
34
|
+
* </GestureDetector>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function useBottomSheetScroll(): {
|
|
38
|
+
scrollHandler: react_native_reanimated0.ScrollHandlerProcessed<Record<string, unknown>>;
|
|
39
|
+
scrollableRef: react_native_reanimated0.AnimatedRef<Animated.ScrollView>;
|
|
40
|
+
nativeGesture: react_native_gesture_handler_lib_typescript_handlers_gestures_nativeGesture0.NativeGesture;
|
|
41
|
+
animatedProps: Partial<{
|
|
42
|
+
scrollEnabled: boolean;
|
|
43
|
+
decelerationRate: number;
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
//#endregion
|
|
47
|
+
export { useBottomSheetScroll };
|
|
48
|
+
//# sourceMappingURL=useBottomSheetScroll.d.cts.map
|