Skip to content

Commit

Permalink
feat: migrate drawer to new RNGH API (#11776)
Browse files Browse the repository at this point in the history
This PR moves the drawer to the new API.

https://github.com/react-navigation/react-navigation/assets/25709300/bb3714e0-fadc-44c2-bcf4-f9f21510071d

---------

Co-authored-by: Satyajit Sahoo <satyajit.happy@gmail.com>
  • Loading branch information
osdnk and satya164 committed Mar 25, 2024
1 parent 7ed2bc1 commit 5d7d81e
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 92 deletions.
1 change: 0 additions & 1 deletion packages/drawer/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export { DrawerStatusContext } from './utils/DrawerStatusContext';
export { getDrawerStatusFromState } from './utils/getDrawerStatusFromState';
export { useDrawerStatus } from './utils/useDrawerStatus';
export {
DrawerGestureContext,
DrawerProgressContext,
useDrawerProgress,
} from 'react-native-drawer-layout';
Expand Down
12 changes: 4 additions & 8 deletions packages/drawer/src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ import type {
Theme,
} from '@react-navigation/native';
import type { StyleProp, TextStyle, ViewStyle } from 'react-native';
import type {
PanGestureHandler,
PanGestureHandlerProperties,
} from 'react-native-gesture-handler';
import type { PanGesture } from 'react-native-gesture-handler';

export type Scene = {
route: Route<string>;
Expand Down Expand Up @@ -174,10 +171,9 @@ export type DrawerNavigationOptions = HeaderOptions & {
sceneContainerStyle?: StyleProp<ViewStyle>;

/**
* Props to pass to the underlying pan gesture handler.
* Not supported on Web.
* Function to modify the pan gesture handler via RNGH properties API.
*/
gestureHandlerProps?: PanGestureHandlerProperties;
configureGestureHandler?: (gesture: PanGesture) => PanGesture;

/**
* Whether you can use swipe gestures to open or close the drawer.
Expand Down Expand Up @@ -321,7 +317,7 @@ export type DrawerProps = {
drawerPosition: 'left' | 'right';
drawerStyle?: StyleProp<ViewStyle>;
drawerType: 'front' | 'back' | 'slide' | 'permanent';
gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
configureGestureHandler?: (gesture: PanGesture) => PanGesture;
hideStatusBarOnOpen: boolean;
keyboardDismissMode: 'none' | 'on-drag';
onClose: () => void;
Expand Down
4 changes: 2 additions & 2 deletions packages/drawer/src/views/DrawerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function DrawerViewBase({
drawerStatusBarAnimation,
drawerStyle,
drawerType = Platform.select({ ios: 'slide', default: 'front' }),
gestureHandlerProps,
configureGestureHandler,
keyboardDismissMode,
overlayColor = 'rgba(0, 0, 0, 0.5)',
swipeEdgeWidth,
Expand Down Expand Up @@ -275,7 +275,7 @@ function DrawerViewBase({
onTransitionStart={handleTransitionStart}
onTransitionEnd={handleTransitionEnd}
layout={dimensions}
gestureHandlerProps={gestureHandlerProps}
configureGestureHandler={configureGestureHandler}
swipeEnabled={swipeEnabled}
swipeEdgeWidth={swipeEdgeWidth}
swipeMinDistance={swipeMinDistance}
Expand Down
1 change: 0 additions & 1 deletion packages/react-native-drawer-layout/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { DrawerGestureContext } from './utils/DrawerGestureContext';
export { DrawerProgressContext } from './utils/DrawerProgressContext';
export { useDrawerProgress } from './utils/useDrawerProgress';
export { Drawer } from './views/Drawer';
7 changes: 3 additions & 4 deletions packages/react-native-drawer-layout/src/types.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import type { StyleProp, View, ViewStyle } from 'react-native';
import type { PanGestureHandler } from 'react-native-gesture-handler';
import type { PanGesture } from 'react-native-gesture-handler';
import type { SharedValue } from 'react-native-reanimated';

export type Layout = { width: number; height: number };
Expand Down Expand Up @@ -137,10 +137,9 @@ export type DrawerProps = {
swipeMinVelocity?: number;

/**
* Props to pass to the underlying pan gesture handler.
* This is not supported on Web.
* Function to modify the pan gesture handler via RNGH properties API.
*/
gestureHandlerProps?: React.ComponentProps<typeof PanGestureHandler>;
configureGestureHandler?: (gesture: PanGesture) => PanGesture;

/**
* Style object for the wrapper view.
Expand Down

This file was deleted.

69 changes: 30 additions & 39 deletions packages/react-native-drawer-layout/src/views/Drawer.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import Animated, {
interpolate,
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useDerivedValue,
useSharedValue,
Expand All @@ -24,10 +23,10 @@ import type { DrawerProps } from '../types';
import { DrawerProgressContext } from '../utils/DrawerProgressContext';
import { getDrawerWidth } from '../utils/getDrawerWidth';
import {
Gesture,
GestureDetector,
GestureHandlerRootView,
GestureState,
PanGestureHandler,
type PanGestureHandlerGestureEvent,
} from './GestureHandler';
import { Overlay } from './Overlay';

Expand All @@ -47,7 +46,7 @@ export function Drawer({
drawerPosition = I18nManager.getConstants().isRTL ? 'right' : 'left',
drawerStyle,
drawerType = 'front',
gestureHandlerProps,
configureGestureHandler,
hideStatusBarOnOpen = false,
keyboardDismissMode = 'on-drag',
onClose,
Expand Down Expand Up @@ -213,34 +212,28 @@ export function Drawer({

React.useEffect(() => toggleDrawer(open), [open, toggleDrawer]);

const onGestureEvent = useAnimatedGestureHandler<
PanGestureHandlerGestureEvent,
{ startX: number; hasCalledOnStart: boolean }
>({
onStart: (event, ctx) => {
ctx.hasCalledOnStart = false;
ctx.startX = translationX.value;
const startX = useSharedValue(0);

let pan = Gesture?.Pan()
.onBegin((event) => {
startX.value = translationX.value;
gestureState.value = event.state;
touchStartX.value = event.x;
},
onCancel: () => {
runOnJS(onGestureAbort)();
},
onActive: (event, ctx) => {
})
.onStart(() => {
runOnJS(onGestureBegin)();
})
.onChange((event) => {
touchX.value = event.x;
translationX.value = ctx.startX + event.translationX;
translationX.value = startX.value + event.translationX;
gestureState.value = event.state;
})
.onEnd((event, success) => {
gestureState.value = event.state;

// onStart will _always_ be called, even when the activation
// criteria isn't met yet. This makes sure onGestureBegin is only
// called when the criteria is really met.
if (!ctx.hasCalledOnStart) {
ctx.hasCalledOnStart = true;
runOnJS(onGestureBegin)();
if (!success) {
runOnJS(onGestureAbort)();
}
},
onEnd: (event) => {
gestureState.value = event.state;

const nextOpen =
(Math.abs(event.translationX) > SWIPE_MIN_OFFSET &&
Expand All @@ -254,11 +247,16 @@ export function Drawer({
: open;

toggleDrawer(nextOpen, event.velocityX);
},
onFinish: () => {
runOnJS(onGestureFinish)();
},
});
})
.activeOffsetX([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET])
.failOffsetY([-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET])
.hitSlop(hitSlop)
.enabled(drawerType !== 'permanent' && swipeEnabled);

if (pan && configureGestureHandler) {
pan = configureGestureHandler(pan);
}

const translateX = useDerivedValue(() => {
// Comment stolen from react-native-gesture-handler/DrawerLayout
Expand Down Expand Up @@ -358,14 +356,7 @@ export function Drawer({
return (
<GestureHandlerRootView style={[styles.container, style]}>
<DrawerProgressContext.Provider value={progress}>
<PanGestureHandler
activeOffsetX={[-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]}
failOffsetY={[-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]}
hitSlop={hitSlop}
enabled={drawerType !== 'permanent' && swipeEnabled}
onGestureEvent={onGestureEvent}
{...gestureHandlerProps}
>
<GestureDetector gesture={pan}>
{/* Immediate child of gesture handler needs to be an Animated.View */}
<Animated.View
style={[
Expand Down Expand Up @@ -419,7 +410,7 @@ export function Drawer({
{renderDrawerContent()}
</Animated.View>
</Animated.View>
</PanGestureHandler>
</GestureDetector>
</DrawerProgressContext.Provider>
</GestureHandlerRootView>
);
Expand Down
34 changes: 20 additions & 14 deletions packages/react-native-drawer-layout/src/views/GestureHandler.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import * as React from 'react';
import { View } from 'react-native';
import type {
PanGestureHandlerProperties,
TapGestureHandlerProperties,
} from 'react-native-gesture-handler';
import type { GestureType } from 'react-native-gesture-handler';

const Dummy: any = ({ children }: { children: React.ReactNode }) => (
<>{children}</>
);
// FIXME: Inline this type instead of getting it from react-native-gesture-handler
// Otherwise, we get a type error:
// Exported variable 'GestureDetector' has or is using name 'GestureDetectorProps' from external module ".." but cannot be named.
type GestureDetectorProps = {
gesture: GestureType | undefined;
userSelect?: 'none' | 'auto' | 'text';
children: React.ReactNode;
};

export const PanGestureHandler =
Dummy as React.ComponentType<PanGestureHandlerProperties>;

export const TapGestureHandler =
Dummy as React.ComponentType<TapGestureHandlerProperties>;
export const GestureDetector = ({
gesture: _0,
userSelect: _1,
children,
}: GestureDetectorProps) => {
return <>{children}</>;
};

export const GestureHandlerRootView = View;

export const Gesture:
| typeof import('react-native-gesture-handler').Gesture
| undefined = undefined;

export const enum GestureState {
UNDETERMINED = 0,
FAILED = 1,
Expand All @@ -25,5 +33,3 @@ export const enum GestureState {
ACTIVE = 4,
END = 5,
}

export type { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
Original file line number Diff line number Diff line change
@@ -1,24 +1,7 @@
import * as React from 'react';
import {
PanGestureHandler as PanGestureHandlerNative,
type PanGestureHandlerProperties,
} from 'react-native-gesture-handler';

import { DrawerGestureContext } from '../utils/DrawerGestureContext';

export function PanGestureHandler(props: PanGestureHandlerProperties) {
const gestureRef = React.useRef<PanGestureHandlerNative>(null);

return (
<DrawerGestureContext.Provider value={gestureRef}>
<PanGestureHandlerNative {...props} />
</DrawerGestureContext.Provider>
);
}

export type { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
export {
Gesture,
GestureDetector,
GestureHandlerRootView,
State as GestureState,
TapGestureHandler,
} from 'react-native-gesture-handler';

0 comments on commit 5d7d81e

Please sign in to comment.