diff --git a/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx b/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx index d01579b13..a4944554e 100644 --- a/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx +++ b/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx @@ -5,20 +5,19 @@ import { useBottomSheetGestureHandlers, useBottomSheetInternal, } from '../../hooks'; +import { GESTURE_SOURCE } from '../../constants'; import type { BottomSheetDraggableViewProps } from './types'; import { styles } from './styles'; const BottomSheetDraggableViewComponent = ({ + gestureType = GESTURE_SOURCE.CONTENT, nativeGestureRef, refreshControlGestureRef, style, children, ...rest }: BottomSheetDraggableViewProps) => { - // refs - const panGestureRef = useRef(null); - - // hooks + //#region hooks const { enableContentPanningGesture, simultaneousHandlers: _providedSimultaneousHandlers, @@ -28,9 +27,19 @@ const BottomSheetDraggableViewComponent = ({ failOffsetX, failOffsetY, } = useBottomSheetInternal(); - const { contentPanGestureHandler } = useBottomSheetGestureHandlers(); + const { contentPanGestureHandler, scrollablePanGestureHandler } = + useBottomSheetGestureHandlers(); + //#endregion - // variables + //#region variables + const panGestureRef = useRef(null); + const gestureHandler = useMemo( + () => + gestureType === GESTURE_SOURCE.CONTENT + ? contentPanGestureHandler + : scrollablePanGestureHandler, + [gestureType, contentPanGestureHandler, scrollablePanGestureHandler] + ); const simultaneousHandlers = useMemo(() => { const refs = []; @@ -56,19 +65,19 @@ const BottomSheetDraggableViewComponent = ({ nativeGestureRef, refreshControlGestureRef, ]); + //#endregion - // styles + //#region styles const containerStyle = useMemo(() => { if (!style) { return styles.container; } - if (Array.isArray(style)) { return [styles.container, ...style]; } - return [styles.container, style]; }, [style]); + //#endregion return ( | null; refreshControlGestureRef?: Ref | null; + children: ReactNode[] | ReactNode; }; diff --git a/src/components/bottomSheetGestureHandlersProvider/BottomSheetGestureHandlersProvider.tsx b/src/components/bottomSheetGestureHandlersProvider/BottomSheetGestureHandlersProvider.tsx index ee72c35d7..e3314b812 100644 --- a/src/components/bottomSheetGestureHandlersProvider/BottomSheetGestureHandlersProvider.tsx +++ b/src/components/bottomSheetGestureHandlersProvider/BottomSheetGestureHandlersProvider.tsx @@ -7,39 +7,71 @@ import { } from '../../hooks'; import { BottomSheetGestureHandlersContext } from '../../contexts'; import type { BottomSheetGestureHandlersProviderProps } from './types'; +import { useSharedValue } from 'react-native-reanimated'; const BottomSheetGestureHandlersProvider = ({ gestureEventsHandlersHook: useGestureEventsHandlers = useGestureEventsHandlersDefault, children, }: BottomSheetGestureHandlersProviderProps) => { - // hooks + //#region variables + const animatedGestureSource = useSharedValue( + GESTURE_SOURCE.UNDETERMINED + ); + //#endregion + + //#region hooks const { animatedContentGestureState, animatedHandleGestureState } = useBottomSheetInternal(); const { handleOnStart, handleOnActive, handleOnEnd } = useGestureEventsHandlers(); + //#endregion - // gestures + //#region gestures const contentPanGestureHandler = useGestureHandler( + GESTURE_SOURCE.CONTENT, + animatedContentGestureState, + animatedGestureSource, + handleOnStart, + handleOnActive, + handleOnEnd + ); + + const scrollablePanGestureHandler = useGestureHandler( GESTURE_SOURCE.SCROLLABLE, animatedContentGestureState, + animatedGestureSource, handleOnStart, handleOnActive, handleOnEnd ); + const handlePanGestureHandler = useGestureHandler( GESTURE_SOURCE.HANDLE, animatedHandleGestureState, + animatedGestureSource, handleOnStart, handleOnActive, handleOnEnd ); + //#endregion - // context value + //#region context const contextValue = useMemo( - () => ({ contentPanGestureHandler, handlePanGestureHandler }), - [contentPanGestureHandler, handlePanGestureHandler] + () => ({ + contentPanGestureHandler, + handlePanGestureHandler, + scrollablePanGestureHandler, + animatedGestureSource, + }), + [ + contentPanGestureHandler, + handlePanGestureHandler, + scrollablePanGestureHandler, + animatedGestureSource, + ] ); + //#endregion return ( {children} diff --git a/src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx b/src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx index 744eb970c..c6a75072a 100644 --- a/src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx +++ b/src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx @@ -10,6 +10,7 @@ import { useBottomSheetInternal, } from '../../hooks'; import { + GESTURE_SOURCE, SCROLLABLE_DECELERATION_RATE_MAPPER, SCROLLABLE_STATE, SCROLLABLE_TYPE, @@ -122,6 +123,7 @@ export function createBottomSheetScrollableComponent( {onRefresh ? ( @@ -143,6 +145,7 @@ export function createBottomSheetScrollableComponent( return ( void; handlePanGestureHandler: (event: PanGestureHandlerGestureEvent) => void; + scrollablePanGestureHandler: (event: PanGestureHandlerGestureEvent) => void; } export const BottomSheetGestureHandlersContext = diff --git a/src/hooks/useGestureHandler.ts b/src/hooks/useGestureHandler.ts index 91f82a658..eb50e72be 100644 --- a/src/hooks/useGestureHandler.ts +++ b/src/hooks/useGestureHandler.ts @@ -4,41 +4,93 @@ import { PanGestureHandlerGestureEvent, } from 'react-native-gesture-handler'; import { GESTURE_SOURCE } from '../constants'; -import type { GestureEventHandlerCallbackType } from '../types'; +import type { + GestureEventContextType, + GestureEventHandlerCallbackType, +} from '../types'; + +const resetContext = (context: any) => { + 'worklet'; + + Object.keys(context).map(key => { + context[key] = undefined; + }); +}; export const useGestureHandler = ( type: GESTURE_SOURCE, state: Animated.SharedValue, + gestureSource: Animated.SharedValue, handleOnStart: GestureEventHandlerCallbackType, handleOnActive: GestureEventHandlerCallbackType, handleOnEnd: GestureEventHandlerCallbackType ): ((event: PanGestureHandlerGestureEvent) => void) => { - const gestureHandler = - useAnimatedGestureHandler( - { - onStart: (payload, context) => { - state.value = payload.state; + const gestureHandler = useAnimatedGestureHandler< + PanGestureHandlerGestureEvent, + GestureEventContextType + >( + { + onActive: (payload, context) => { + if (!context.didStart) { + context.didStart = true; + + state.value = State.BEGAN; + gestureSource.value = type; + handleOnStart(type, payload, context); - }, - onActive: (payload, context) => { - state.value = payload.state; - handleOnActive(type, payload, context); - }, - onEnd: (payload, context) => { - state.value = payload.state; - handleOnEnd(type, payload, context); - }, - onCancel: payload => { - state.value = payload.state; - }, - onFail: payload => { - state.value = payload.state; - }, - onFinish: payload => { - state.value = payload.state; - }, + return; + } + + if (gestureSource.value !== type) { + return; + } + + state.value = payload.state; + handleOnActive(type, payload, context); + }, + onEnd: (payload, context) => { + if (gestureSource.value !== type) { + return; + } + + state.value = payload.state; + gestureSource.value = GESTURE_SOURCE.UNDETERMINED; + + handleOnEnd(type, payload, context); + resetContext(context); + }, + onCancel: (payload, context) => { + if (gestureSource.value !== type) { + return; + } + + state.value = payload.state; + gestureSource.value = GESTURE_SOURCE.UNDETERMINED; + + resetContext(context); + }, + onFail: (payload, context) => { + if (gestureSource.value !== type) { + return; + } + + state.value = payload.state; + gestureSource.value = GESTURE_SOURCE.UNDETERMINED; + + resetContext(context); + }, + onFinish: (payload, context) => { + if (gestureSource.value !== type) { + return; + } + + state.value = payload.state; + gestureSource.value = GESTURE_SOURCE.UNDETERMINED; + + resetContext(context); }, - [type, state, handleOnStart, handleOnActive, handleOnEnd] - ); + }, + [type, state, handleOnStart, handleOnActive, handleOnEnd] + ); return gestureHandler; }; diff --git a/src/types.d.ts b/src/types.d.ts index bf062c8f8..52fe47291 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -132,7 +132,11 @@ export interface Insets { export type GestureEventPayloadType = GestureEventPayload & PanGestureHandlerEventPayload; -type GestureEventHandlerCallbackType = ( +export type GestureEventContextType = { + didStart?: boolean; +}; + +export type GestureEventHandlerCallbackType = ( source: GESTURE_SOURCE, payload: GestureEventPayloadType, context: C