@swmansion/react-native-bottom-sheet 0.5.5 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -5
- package/lib/module/BottomSheetBase.js +10 -13
- package/lib/module/BottomSheetBase.js.map +1 -1
- package/lib/module/BottomSheetContext.js.map +1 -1
- package/lib/module/BottomSheetFlatList.js +3 -42
- package/lib/module/BottomSheetFlatList.js.map +1 -1
- package/lib/module/BottomSheetScrollView.js +3 -43
- package/lib/module/BottomSheetScrollView.js.map +1 -1
- package/lib/module/bottomSheetScrollable.js +35 -0
- package/lib/module/bottomSheetScrollable.js.map +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/useBottomSheetPanGesture.js +53 -22
- package/lib/module/useBottomSheetPanGesture.js.map +1 -1
- package/lib/module/useBottomSheetScrollable.js +14 -17
- package/lib/module/useBottomSheetScrollable.js.map +1 -1
- package/lib/typescript/src/BottomSheetBase.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetContext.d.ts +6 -4
- package/lib/typescript/src/BottomSheetContext.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetFlatList.d.ts +7 -12
- package/lib/typescript/src/BottomSheetFlatList.d.ts.map +1 -1
- package/lib/typescript/src/BottomSheetScrollView.d.ts +7 -14
- package/lib/typescript/src/BottomSheetScrollView.d.ts.map +1 -1
- package/lib/typescript/src/bottomSheetScrollable.d.ts +9 -0
- package/lib/typescript/src/bottomSheetScrollable.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/useBottomSheetPanGesture.d.ts +4 -6
- package/lib/typescript/src/useBottomSheetPanGesture.d.ts.map +1 -1
- package/lib/typescript/src/useBottomSheetScrollable.d.ts +1 -1
- package/lib/typescript/src/useBottomSheetScrollable.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/BottomSheetBase.tsx +16 -14
- package/src/BottomSheetContext.tsx +7 -4
- package/src/BottomSheetFlatList.tsx +16 -58
- package/src/BottomSheetScrollView.tsx +17 -61
- package/src/bottomSheetScrollable.tsx +42 -0
- package/src/index.tsx +3 -9
- package/src/useBottomSheetPanGesture.ts +64 -52
- package/src/useBottomSheetScrollable.ts +16 -23
|
@@ -4,11 +4,11 @@ import { scheduleOnRN } from 'react-native-worklets';
|
|
|
4
4
|
import {
|
|
5
5
|
measure,
|
|
6
6
|
scrollTo,
|
|
7
|
-
type AnimatedRef,
|
|
8
7
|
type SharedValue,
|
|
9
8
|
useSharedValue,
|
|
10
9
|
} from 'react-native-reanimated';
|
|
11
10
|
|
|
11
|
+
import type { ScrollableEntry } from './BottomSheetContext';
|
|
12
12
|
import { findSnapTarget } from './bottomSheetUtils';
|
|
13
13
|
|
|
14
14
|
interface BottomSheetPanGestureParams {
|
|
@@ -18,11 +18,8 @@ interface BottomSheetPanGestureParams {
|
|
|
18
18
|
detentsValue: SharedValue<number[]>;
|
|
19
19
|
isDraggableValue: SharedValue<boolean[]>;
|
|
20
20
|
currentIndex: SharedValue<number>;
|
|
21
|
-
|
|
22
|
-
hasScrollable: SharedValue<boolean>;
|
|
23
|
-
isScrollableGestureActive: SharedValue<boolean>;
|
|
21
|
+
scrollableEntries: ScrollableEntry[];
|
|
24
22
|
isScrollableLocked: SharedValue<boolean>;
|
|
25
|
-
scrollableRef: AnimatedRef<any>;
|
|
26
23
|
handleIndexChange: (nextIndex: number) => void;
|
|
27
24
|
animateToIndex: (targetIndex: number, velocity?: number) => void;
|
|
28
25
|
}
|
|
@@ -34,11 +31,8 @@ export const useBottomSheetPanGesture = ({
|
|
|
34
31
|
detentsValue,
|
|
35
32
|
isDraggableValue,
|
|
36
33
|
currentIndex,
|
|
37
|
-
|
|
38
|
-
hasScrollable,
|
|
39
|
-
isScrollableGestureActive,
|
|
34
|
+
scrollableEntries,
|
|
40
35
|
isScrollableLocked,
|
|
41
|
-
scrollableRef,
|
|
42
36
|
handleIndexChange,
|
|
43
37
|
animateToIndex,
|
|
44
38
|
}: BottomSheetPanGestureParams): PanGesture => {
|
|
@@ -48,7 +42,7 @@ export const useBottomSheetPanGesture = ({
|
|
|
48
42
|
const panStartY = useSharedValue(0);
|
|
49
43
|
const panActivated = useSharedValue(false);
|
|
50
44
|
const dragStartTranslateY = useSharedValue(0);
|
|
51
|
-
const
|
|
45
|
+
const activeScrollableIndex = useSharedValue(-1);
|
|
52
46
|
|
|
53
47
|
return Gesture.Pan()
|
|
54
48
|
.manualActivation(true)
|
|
@@ -58,21 +52,26 @@ export const useBottomSheetPanGesture = ({
|
|
|
58
52
|
isDraggingSheet.set(false);
|
|
59
53
|
isDraggingFromScrollable.set(false);
|
|
60
54
|
isScrollableLocked.set(false);
|
|
61
|
-
|
|
55
|
+
activeScrollableIndex.set(-1);
|
|
62
56
|
const touch = event.changedTouches[0] ?? event.allTouches[0];
|
|
63
57
|
if (touch !== undefined) {
|
|
64
58
|
panStartX.set(touch.absoluteX);
|
|
65
59
|
panStartY.set(touch.absoluteY);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
const entries = scrollableEntries;
|
|
61
|
+
for (let i = 0; i < entries.length; i++) {
|
|
62
|
+
const entry = entries[i];
|
|
63
|
+
if (entry === undefined) continue;
|
|
64
|
+
const layout = measure(entry.ref);
|
|
65
|
+
if (layout === null) continue;
|
|
66
|
+
const withinX =
|
|
67
|
+
touch.absoluteX >= layout.pageX &&
|
|
68
|
+
touch.absoluteX <= layout.pageX + layout.width;
|
|
69
|
+
const withinY =
|
|
70
|
+
touch.absoluteY >= layout.pageY &&
|
|
71
|
+
touch.absoluteY <= layout.pageY + layout.height;
|
|
72
|
+
if (withinX && withinY) {
|
|
73
|
+
activeScrollableIndex.set(i);
|
|
74
|
+
break;
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
77
|
}
|
|
@@ -84,14 +83,38 @@ export const useBottomSheetPanGesture = ({
|
|
|
84
83
|
if (!touch) return;
|
|
85
84
|
const deltaX = touch.absoluteX - panStartX.value;
|
|
86
85
|
const deltaY = touch.absoluteY - panStartY.value;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
// When multiple scrollables overlap (e.g. stacked views), the hit-test
|
|
87
|
+
// in onTouchesDown may pick the wrong one. Prefer the scrollable whose
|
|
88
|
+
// native gesture is already active — that is definitively the one
|
|
89
|
+
// receiving touches (respects pointerEvents, z-order, etc.).
|
|
90
|
+
// If multiple scrollables are registered but none has confirmed via
|
|
91
|
+
// isGestureActive yet, defer the decision to avoid acting on a
|
|
92
|
+
// potentially incorrect hit-test result.
|
|
93
|
+
const entries = scrollableEntries;
|
|
94
|
+
let gestureActiveIdx = -1;
|
|
95
|
+
for (let i = 0; i < entries.length; i++) {
|
|
96
|
+
const entry = entries[i];
|
|
97
|
+
if (entry !== undefined && entry.isGestureActive.value) {
|
|
98
|
+
gestureActiveIdx = i;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (gestureActiveIdx !== -1) {
|
|
103
|
+
activeScrollableIndex.set(gestureActiveIdx);
|
|
104
|
+
} else if (entries.length > 1) {
|
|
93
105
|
return;
|
|
94
106
|
}
|
|
107
|
+
const activeIdx = activeScrollableIndex.value;
|
|
108
|
+
if (activeIdx !== -1) {
|
|
109
|
+
const active = scrollableEntries[activeIdx];
|
|
110
|
+
if (
|
|
111
|
+
active !== undefined &&
|
|
112
|
+
active.scrollOffset.value > 0 &&
|
|
113
|
+
translateY.value <= 0
|
|
114
|
+
) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
95
118
|
if (Math.abs(deltaX) > Math.abs(deltaY)) {
|
|
96
119
|
stateManager.fail();
|
|
97
120
|
return;
|
|
@@ -113,36 +136,30 @@ export const useBottomSheetPanGesture = ({
|
|
|
113
136
|
})
|
|
114
137
|
.onUpdate((event) => {
|
|
115
138
|
'worklet';
|
|
139
|
+
const activeIdx = activeScrollableIndex.value;
|
|
140
|
+
const hasActive = activeIdx !== -1;
|
|
141
|
+
const active = hasActive ? scrollableEntries[activeIdx] : undefined;
|
|
142
|
+
const activeOffset = active !== undefined ? active.scrollOffset.value : 0;
|
|
143
|
+
|
|
116
144
|
if (isDraggingSheet.value) {
|
|
117
|
-
if (isDraggingFromScrollable.value) {
|
|
118
|
-
scrollTo(
|
|
145
|
+
if (isDraggingFromScrollable.value && active !== undefined) {
|
|
146
|
+
scrollTo(active.ref, 0, 0, false);
|
|
119
147
|
}
|
|
120
148
|
} else {
|
|
121
149
|
const isDraggingDown = event.translationY > 0;
|
|
122
150
|
const canStartDrag =
|
|
123
|
-
!
|
|
124
|
-
scrollOffset.value <= 0 ||
|
|
125
|
-
translateY.value > 0 ||
|
|
126
|
-
!isTouchWithinScrollable.value;
|
|
151
|
+
!hasActive || activeOffset <= 0 || translateY.value > 0;
|
|
127
152
|
if (!canStartDrag || (!isDraggingDown && translateY.value <= 0)) {
|
|
128
153
|
return;
|
|
129
154
|
}
|
|
130
155
|
const isScrollableActive =
|
|
131
|
-
|
|
156
|
+
hasActive && active !== undefined && active.isGestureActive.value;
|
|
132
157
|
isDraggingSheet.set(true);
|
|
133
|
-
isDraggingFromScrollable.set(
|
|
134
|
-
isScrollableActive &&
|
|
135
|
-
isTouchWithinScrollable.value &&
|
|
136
|
-
scrollOffset.value <= 0
|
|
137
|
-
);
|
|
158
|
+
isDraggingFromScrollable.set(isScrollableActive && activeOffset <= 0);
|
|
138
159
|
dragStartTranslateY.set(translateY.value - event.translationY);
|
|
139
|
-
isScrollableLocked.set(
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
hasScrollable.value &&
|
|
143
|
-
scrollOffset.value <= 0
|
|
144
|
-
) {
|
|
145
|
-
scrollTo(scrollableRef, 0, 0, false);
|
|
160
|
+
isScrollableLocked.set(hasActive);
|
|
161
|
+
if (hasActive && active !== undefined && activeOffset <= 0) {
|
|
162
|
+
scrollTo(active.ref, 0, 0, false);
|
|
146
163
|
}
|
|
147
164
|
}
|
|
148
165
|
const rawTranslate = dragStartTranslateY.value + event.translationY;
|
|
@@ -163,12 +180,7 @@ export const useBottomSheetPanGesture = ({
|
|
|
163
180
|
maxDraggableTranslateY
|
|
164
181
|
);
|
|
165
182
|
translateY.set(nextTranslate);
|
|
166
|
-
if (
|
|
167
|
-
isDraggingSheet.value &&
|
|
168
|
-
rawTranslate < 0 &&
|
|
169
|
-
isTouchWithinScrollable.value &&
|
|
170
|
-
hasScrollable.value
|
|
171
|
-
) {
|
|
183
|
+
if (isDraggingSheet.value && rawTranslate < 0 && hasActive) {
|
|
172
184
|
isDraggingSheet.set(false);
|
|
173
185
|
isScrollableLocked.set(false);
|
|
174
186
|
let targetSnapIndex = -1;
|
|
@@ -5,7 +5,9 @@ import { isWorkletFunction, scheduleOnRN } from 'react-native-worklets';
|
|
|
5
5
|
import {
|
|
6
6
|
type SharedValue,
|
|
7
7
|
useAnimatedProps,
|
|
8
|
+
useAnimatedRef,
|
|
8
9
|
useAnimatedScrollHandler,
|
|
10
|
+
useSharedValue,
|
|
9
11
|
} from 'react-native-reanimated';
|
|
10
12
|
|
|
11
13
|
import { useBottomSheetContext } from './BottomSheetContext';
|
|
@@ -16,14 +18,11 @@ export const useBottomSheetScrollable = (
|
|
|
16
18
|
baseScrollEnabled: boolean | SharedValue<boolean | undefined> = true,
|
|
17
19
|
onScroll?: ScrollHandler
|
|
18
20
|
) => {
|
|
19
|
-
const {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
isScrollableLocked,
|
|
25
|
-
panGesture,
|
|
26
|
-
} = useBottomSheetContext();
|
|
21
|
+
const { isScrollableLocked, registerScrollable, panGesture } =
|
|
22
|
+
useBottomSheetContext();
|
|
23
|
+
const scrollableRef = useAnimatedRef();
|
|
24
|
+
const scrollOffset = useSharedValue(0);
|
|
25
|
+
const isGestureActive = useSharedValue(false);
|
|
27
26
|
const isWorkletScrollHandler =
|
|
28
27
|
onScroll !== undefined ? isWorkletFunction(onScroll) : false;
|
|
29
28
|
const scrollHandler = useAnimatedScrollHandler({
|
|
@@ -42,11 +41,11 @@ export const useBottomSheetScrollable = (
|
|
|
42
41
|
.simultaneousWithExternalGesture(panGesture)
|
|
43
42
|
.onStart(() => {
|
|
44
43
|
'worklet';
|
|
45
|
-
|
|
44
|
+
isGestureActive.set(true);
|
|
46
45
|
})
|
|
47
46
|
.onFinalize(() => {
|
|
48
47
|
'worklet';
|
|
49
|
-
|
|
48
|
+
isGestureActive.set(false);
|
|
50
49
|
});
|
|
51
50
|
const animatedProps = useAnimatedProps(() => {
|
|
52
51
|
const resolvedScrollEnabled =
|
|
@@ -58,18 +57,12 @@ export const useBottomSheetScrollable = (
|
|
|
58
57
|
};
|
|
59
58
|
});
|
|
60
59
|
useEffect(() => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return () => {
|
|
69
|
-
hasScrollable.set(false);
|
|
70
|
-
isScrollableGestureActive.set(false);
|
|
71
|
-
isScrollableLocked.set(false);
|
|
72
|
-
};
|
|
73
|
-
}, [hasScrollable, isScrollableGestureActive, isScrollableLocked]);
|
|
60
|
+
const unregister = registerScrollable({
|
|
61
|
+
ref: scrollableRef,
|
|
62
|
+
scrollOffset,
|
|
63
|
+
isGestureActive,
|
|
64
|
+
});
|
|
65
|
+
return unregister;
|
|
66
|
+
}, [registerScrollable, scrollableRef, scrollOffset, isGestureActive]);
|
|
74
67
|
return { scrollHandler, scrollableRef, nativeGesture, animatedProps };
|
|
75
68
|
};
|