Skip to content

Commit 4ffdf5c

Browse files
committed
feat: rewrite gesture apis with gesture handler 2 (gorhom#1126)
* chore: updated dependencies * feat: rewrite the gesture api with gh2 gorhom@6a4d296 fix(gorhom#1119): fixed race condition between onmount and keyboard animations
1 parent 54dc2e0 commit 4ffdf5c

File tree

21 files changed

+1113
-976
lines changed

21 files changed

+1113
-976
lines changed

example/bare/ios/Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -382,9 +382,9 @@ PODS:
382382
- React-perflogger (= 0.69.4)
383383
- RNCMaskedView (0.1.11):
384384
- React
385-
- RNGestureHandler (2.5.0):
385+
- RNGestureHandler (2.6.2):
386386
- React-Core
387-
- RNReanimated (2.9.1):
387+
- RNReanimated (2.10.0):
388388
- DoubleConversion
389389
- FBLazyVector
390390
- FBReactNativeSpec
@@ -644,8 +644,8 @@ SPEC CHECKSUMS:
644644
React-runtimeexecutor: 61ee22a8cdf8b6bb2a7fb7b4ba2cc763e5285196
645645
ReactCommon: 8f67bd7e0a6afade0f20718f859dc8c2275f2e83
646646
RNCMaskedView: 0e1bc4bfa8365eba5fbbb71e07fbdc0555249489
647-
RNGestureHandler: bad495418bcbd3ab47017a38d93d290ebd406f50
648-
RNReanimated: 2cf7451318bb9cc430abeec8d67693f9cf4e039c
647+
RNGestureHandler: 4defbd70b2faf3d6761b82fa7880285241762cb0
648+
RNReanimated: 7faa787e8d4493fbc95fab2ad331fa7625828cfa
649649
RNScreens: 4a1af06327774490d97342c00aee0c2bafb497b7
650650
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
651651
Yoga: ff994563b2fd98c982ca58e8cd9db2cdaf4dda74

example/bare/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
"nanoid": "^3.3.3",
2525
"react": "18.0.0",
2626
"react-native": "0.69.4",
27-
"react-native-gesture-handler": "^2.5.0",
27+
"react-native-gesture-handler": "^2.6.2",
2828
"react-native-maps": "^0.30.1",
2929
"react-native-pager-view": "^5.4.9",
30-
"react-native-reanimated": "^2.9.1",
30+
"react-native-reanimated": "^2.10.0",
3131
"react-native-redash": "^16.0.11",
3232
"react-native-safe-area-context": "4.2.4",
3333
"react-native-screens": "^3.15.0",

lint-staged.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
module.exports = {
22
'**/*.js': ['eslint'],
3-
'**/*.{ts,tsx}': [() => 'tsc --skipLibCheck --noEmit', 'eslint'],
3+
'**/*.{ts,tsx}': [() => 'tsc --skipLibCheck --noEmit', 'eslint --fix'],
44
};

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,16 @@
5050
"@types/react-native": "^0.67.7",
5151
"auto-changelog": "^2.4.0",
5252
"copyfiles": "^2.4.1",
53-
"eslint": "^7.32.0",
54-
"eslint-config-prettier": "^8.3.0",
55-
"eslint-plugin-prettier": "^3.4.0",
53+
"eslint": "^8.21.0",
54+
"eslint-config-prettier": "^8.5.0",
55+
"eslint-plugin-prettier": "^4.2.1",
5656
"husky": "^4.3.8",
57-
"lint-staged": "^11.1.2",
58-
"prettier": "^2.3.2",
57+
"lint-staged": "^13.0.3",
58+
"prettier": "^2.7.1",
5959
"react": "~16.9.0",
6060
"react-native": "^0.62.2",
6161
"react-native-builder-bob": "^0.18.1",
62-
"react-native-gesture-handler": "^1.10.3",
62+
"react-native-gesture-handler": "^2.12.0",
6363
"react-native-reanimated": "^3.4.2",
6464
"release-it": "^17.0.1",
6565
"typescript": "^5.0.3"
@@ -70,8 +70,8 @@
7070
"@shopify/flash-list": "*",
7171
"react": "*",
7272
"react-native": "*",
73-
"react-native-gesture-handler": ">=1.10.1",
74-
"react-native-reanimated": ">=2.2.0"
73+
"react-native-gesture-handler": ">=2.5.0",
74+
"react-native-reanimated": ">=2.9.0"
7575
},
7676
"peerDependenciesMeta": {
7777
"@types/react-native": {

src/components/bottomSheet/BottomSheet.tsx

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,10 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
593593
return animatedPosition.value;
594594
}
595595

596+
if (!isAnimatedOnMount.value) {
597+
return snapPoints[_providedIndex];
598+
}
599+
596600
return snapPoints[currentIndex];
597601
},
598602
[
@@ -605,8 +609,10 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
605609
animatedPosition,
606610
animatedSnapPoints,
607611
isInTemporaryPosition,
612+
isAnimatedOnMount,
608613
keyboardBehavior,
609614
keyboardBlurBehavior,
615+
_providedIndex,
610616
]
611617
);
612618
const handleOnChange = useCallback(
@@ -1271,7 +1277,7 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
12711277
nextPosition = animatedClosedPosition.value;
12721278
animatedNextPositionIndex.value = -1;
12731279
} else {
1274-
nextPosition = animatedSnapPoints.value[_providedIndex];
1280+
nextPosition = getNextPosition();
12751281
}
12761282

12771283
runOnJS(print)({
@@ -1425,41 +1431,52 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
14251431
)
14261432
: Math.abs(_keyboardHeight - animatedContainerOffset.value.bottom);
14271433

1434+
/**
1435+
* if keyboard state is equal to the previous state, then exit the method
1436+
*/
1437+
if (
1438+
_keyboardState === _previousKeyboardState &&
1439+
_keyboardHeight === _previousKeyboardHeight
1440+
) {
1441+
return;
1442+
}
1443+
1444+
/**
1445+
* if user is interacting with sheet, then exit the method
1446+
*/
14281447
const hasActiveGesture =
14291448
animatedContentGestureState.value === State.ACTIVE ||
14301449
animatedContentGestureState.value === State.BEGAN ||
14311450
animatedHandleGestureState.value === State.ACTIVE ||
14321451
animatedHandleGestureState.value === State.BEGAN;
1452+
if (hasActiveGesture) {
1453+
return;
1454+
}
1455+
1456+
/**
1457+
* if sheet not animated on mount yet, then exit the method
1458+
*/
1459+
if (!isAnimatedOnMount.value) {
1460+
return;
1461+
}
1462+
1463+
/**
1464+
* if new keyboard state is hidden and blur behavior is none, then exit the method
1465+
*/
1466+
if (
1467+
_keyboardState === KEYBOARD_STATE.HIDDEN &&
1468+
keyboardBlurBehavior === KEYBOARD_BLUR_BEHAVIOR.none
1469+
) {
1470+
return;
1471+
}
14331472

1473+
/**
1474+
* if platform is android and the input mode is resize, then exit the method
1475+
*/
14341476
if (
1435-
/**
1436-
* if keyboard state is equal to the previous state, then exit the method
1437-
*/
1438-
(_keyboardState === _previousKeyboardState &&
1439-
_keyboardHeight === _previousKeyboardHeight) ||
1440-
/**
1441-
* if user is interacting with sheet, then exit the method
1442-
*/
1443-
hasActiveGesture ||
1444-
/**
1445-
* if sheet not animated on mount yet, then exit the method
1446-
*/
1447-
!isAnimatedOnMount.value ||
1448-
/**
1449-
* if new keyboard state is hidden and blur behavior is none, then exit the method
1450-
*/
1451-
(_keyboardState === KEYBOARD_STATE.HIDDEN &&
1452-
keyboardBlurBehavior === KEYBOARD_BLUR_BEHAVIOR.none) ||
1453-
/**
1454-
* if platform is android and the input mode is resize, then exit the method
1455-
*/
1456-
(Platform.OS === 'android' &&
1457-
keyboardBehavior === KEYBOARD_BEHAVIOR.interactive &&
1458-
android_keyboardInputMode === KEYBOARD_INPUT_MODE.adjustResize) ||
1459-
/**
1460-
* if the sheet is closing, then exit then method
1461-
*/
1462-
animatedNextPositionIndex.value === -1
1477+
Platform.OS === 'android' &&
1478+
keyboardBehavior === KEYBOARD_BEHAVIOR.interactive &&
1479+
android_keyboardInputMode === KEYBOARD_INPUT_MODE.adjustResize
14631480
) {
14641481
animatedKeyboardHeightInContainer.value = 0;
14651482
return;
@@ -1706,13 +1723,15 @@ const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
17061723
// isScrollableRefreshable,
17071724
// animatedScrollableContentOffsetY,
17081725
// keyboardState,
1709-
// animatedIndex,
1710-
// animatedCurrentIndex,
1711-
// animatedPosition,
1712-
animatedContainerHeight,
1713-
animatedSheetHeight,
1714-
animatedHandleHeight,
1715-
animatedContentHeight,
1726+
animatedIndex,
1727+
animatedCurrentIndex,
1728+
animatedPosition,
1729+
animatedHandleGestureState,
1730+
animatedContentGestureState,
1731+
// animatedContainerHeight,
1732+
// animatedSheetHeight,
1733+
// animatedHandleHeight,
1734+
// animatedContentHeight,
17161735
// // keyboardHeight,
17171736
// isLayoutCalculated,
17181737
// isContentHeightFixed,

src/components/bottomSheet/types.d.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
WithSpringConfig,
77
WithTimingConfig,
88
} from 'react-native-reanimated';
9-
import type { PanGestureHandlerProps } from 'react-native-gesture-handler';
9+
import type { PanGesture } from 'react-native-gesture-handler';
1010
import type { BottomSheetHandleProps } from '../bottomSheetHandle';
1111
import type { BottomSheetBackdropProps } from '../bottomSheetBackdrop';
1212
import type { BottomSheetBackgroundProps } from '../bottomSheetBackground';
@@ -24,17 +24,7 @@ import type {
2424

2525
export interface BottomSheetProps
2626
extends BottomSheetAnimationConfigs,
27-
Partial<
28-
Pick<
29-
PanGestureHandlerProps,
30-
| 'activeOffsetY'
31-
| 'activeOffsetX'
32-
| 'failOffsetY'
33-
| 'failOffsetX'
34-
| 'waitFor'
35-
| 'simultaneousHandlers'
36-
>
37-
>,
27+
Partial<BottomSheetGestureProps>,
3828
Omit<NullableAccessibilityProps, 'accessibilityHint'> {
3929
//#region configuration
4030
/**
@@ -346,3 +336,16 @@ export type AnimateToPositionType = (
346336
velocity?: number,
347337
configs?: WithTimingConfig | WithSpringConfig
348338
) => void;
339+
340+
export type BottomSheetGestureProps = {
341+
activeOffsetX: Parameters<PanGesture['activeOffsetX']>[0];
342+
activeOffsetY: Parameters<PanGesture['activeOffsetY']>[0];
343+
344+
failOffsetY: Parameters<PanGesture['failOffsetY']>[0];
345+
failOffsetX: Parameters<PanGesture['failOffsetX']>[0];
346+
347+
simultaneousHandlers: Parameters<
348+
PanGesture['simultaneousWithExternalGesture']
349+
>[0];
350+
waitFor: Parameters<PanGesture['requireExternalGestureToFail']>[0];
351+
};

src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
import React, { useMemo, useRef, memo } from 'react';
1+
import React, { useMemo, memo } from 'react';
22
import Animated from 'react-native-reanimated';
3-
import { PanGestureHandler } from 'react-native-gesture-handler';
3+
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
44
import {
55
useBottomSheetGestureHandlers,
66
useBottomSheetInternal,
77
} from '../../hooks';
8-
import { GESTURE_SOURCE } from '../../constants';
98
import type { BottomSheetDraggableViewProps } from './types';
9+
import { BottomSheetDraggableContext } from '../../contexts/gesture';
1010

1111
const BottomSheetDraggableViewComponent = ({
12-
gestureType = GESTURE_SOURCE.CONTENT,
1312
nativeGestureRef,
1413
refreshControlGestureRef,
1514
style,
@@ -26,19 +25,10 @@ const BottomSheetDraggableViewComponent = ({
2625
failOffsetX,
2726
failOffsetY,
2827
} = useBottomSheetInternal();
29-
const { contentPanGestureHandler, scrollablePanGestureHandler } =
30-
useBottomSheetGestureHandlers();
28+
const { contentPanGestureHandler } = useBottomSheetGestureHandlers();
3129
//#endregion
3230

3331
//#region variables
34-
const panGestureRef = useRef<PanGestureHandler>(null);
35-
const gestureHandler = useMemo(
36-
() =>
37-
gestureType === GESTURE_SOURCE.CONTENT
38-
? contentPanGestureHandler
39-
: scrollablePanGestureHandler,
40-
[gestureType, contentPanGestureHandler, scrollablePanGestureHandler]
41-
);
4232
const simultaneousHandlers = useMemo(() => {
4333
const refs = [];
4434

@@ -64,25 +54,66 @@ const BottomSheetDraggableViewComponent = ({
6454
nativeGestureRef,
6555
refreshControlGestureRef,
6656
]);
57+
const draggableGesture = useMemo(() => {
58+
let gesture = Gesture.Pan()
59+
.enabled(enableContentPanningGesture)
60+
.shouldCancelWhenOutside(false)
61+
.runOnJS(false)
62+
.onStart(contentPanGestureHandler.handleOnStart)
63+
.onChange(contentPanGestureHandler.handleOnChange)
64+
.onEnd(contentPanGestureHandler.handleOnEnd)
65+
.onFinalize(contentPanGestureHandler.handleOnFinalize);
66+
67+
if (waitFor) {
68+
gesture = gesture.requireExternalGestureToFail(waitFor);
69+
}
70+
71+
if (simultaneousHandlers) {
72+
gesture = gesture.simultaneousWithExternalGesture(
73+
simultaneousHandlers as any
74+
);
75+
}
76+
77+
if (activeOffsetX) {
78+
gesture = gesture.activeOffsetX(activeOffsetX);
79+
}
80+
81+
if (activeOffsetY) {
82+
gesture = gesture.activeOffsetY(activeOffsetY);
83+
}
84+
85+
if (failOffsetX) {
86+
gesture = gesture.failOffsetX(failOffsetX);
87+
}
88+
89+
if (failOffsetY) {
90+
gesture = gesture.failOffsetY(failOffsetY);
91+
}
92+
93+
return gesture;
94+
}, [
95+
activeOffsetX,
96+
activeOffsetY,
97+
enableContentPanningGesture,
98+
failOffsetX,
99+
failOffsetY,
100+
simultaneousHandlers,
101+
waitFor,
102+
contentPanGestureHandler.handleOnChange,
103+
contentPanGestureHandler.handleOnEnd,
104+
contentPanGestureHandler.handleOnFinalize,
105+
contentPanGestureHandler.handleOnStart,
106+
]);
67107
//#endregion
68108

69109
return (
70-
<PanGestureHandler
71-
ref={panGestureRef}
72-
enabled={enableContentPanningGesture}
73-
simultaneousHandlers={simultaneousHandlers}
74-
shouldCancelWhenOutside={false}
75-
waitFor={waitFor}
76-
onGestureEvent={gestureHandler}
77-
activeOffsetX={activeOffsetX}
78-
activeOffsetY={activeOffsetY}
79-
failOffsetX={failOffsetX}
80-
failOffsetY={failOffsetY}
81-
>
82-
<Animated.View style={style} {...rest}>
83-
{children}
84-
</Animated.View>
85-
</PanGestureHandler>
110+
<GestureDetector gesture={draggableGesture}>
111+
<BottomSheetDraggableContext.Provider value={draggableGesture}>
112+
<Animated.View style={style} {...rest}>
113+
{children}
114+
</Animated.View>
115+
</BottomSheetDraggableContext.Provider>
116+
</GestureDetector>
86117
);
87118
};
88119

0 commit comments

Comments
 (0)