diff --git a/native/components/gesture-touchable-opacity.react.js b/native/components/gesture-touchable-opacity.react.js --- a/native/components/gesture-touchable-opacity.react.js +++ b/native/components/gesture-touchable-opacity.react.js @@ -2,17 +2,10 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; -import { - LongPressGestureHandler, - TapGestureHandler, - State as GestureState, - type LongPressGestureEvent, - type TapGestureEvent, -} from 'react-native-gesture-handler'; +import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import Animated, { Easing, useSharedValue, - useAnimatedGestureHandler, runOnJS, withTiming, useAnimatedStyle, @@ -44,10 +37,7 @@ +overlay?: React.Node, +disabled?: boolean, }; -function ForwardedGestureTouchableOpacity( - props: Props, - ref: React.RefSetter, -) { +function GestureTouchableOpacity(props: Props): React.Node { const { onPress: innerOnPress, onLongPress: innerOnLongPress } = props; const onPress = React.useCallback(() => { innerOnPress && innerOnPress(); @@ -59,85 +49,70 @@ const { stickyActive, disabled } = props; const activeValue = useSharedValueForBoolean(!!stickyActive); - const disabledValue = useSharedValueForBoolean(!!disabled); const stickyActiveEnabled = stickyActive !== null && stickyActive !== undefined; - const longPressState = useSharedValue<$Values>( - GestureState.UNDETERMINED, - ); - const tapState = useSharedValue<$Values>( - GestureState.UNDETERMINED, - ); - const longPressEvent = useAnimatedGestureHandler( - { - onStart: () => { - longPressState.value = GestureState.BEGAN; - }, - onActive: () => { - longPressState.value = GestureState.ACTIVE; - if (disabledValue.value) { - return; - } + const gesturePending = useSharedValue(false); + const longPressGesture = React.useMemo(() => { + if (!innerOnLongPress) { + return null; + } + return Gesture.LongPress() + .enabled(!disabled) + .minDuration(370) + .maxDistance(50) + .onBegin(() => { + gesturePending.value = true; + }) + .onStart(() => { if (stickyActiveEnabled) { activeValue.value = true; } runOnJS(onLongPress)(); - }, - onEnd: () => { - longPressState.value = GestureState.END; - }, - onFail: () => { - longPressState.value = GestureState.FAILED; - }, - onCancel: () => { - longPressState.value = GestureState.CANCELLED; - }, - onFinish: () => { - longPressState.value = GestureState.END; - }, - }, - [stickyActiveEnabled, onLongPress], - ); - const tapEvent = useAnimatedGestureHandler( - { - onStart: () => { - tapState.value = GestureState.BEGAN; - }, - onActive: () => { - tapState.value = GestureState.ACTIVE; - }, - onEnd: () => { - tapState.value = GestureState.END; - if (disabledValue.value) { - return; - } - if (stickyActiveEnabled) { - activeValue.value = true; - } - runOnJS(onPress)(); - }, - onFail: () => { - tapState.value = GestureState.FAILED; - }, - onCancel: () => { - tapState.value = GestureState.CANCELLED; - }, - onFinish: () => { - tapState.value = GestureState.END; - }, - }, - [stickyActiveEnabled, onPress], + }) + .onTouchesCancelled(() => { + gesturePending.value = false; + }) + .onFinalize(() => { + gesturePending.value = false; + }); + }, [ + disabled, + activeValue, + innerOnLongPress, + onLongPress, + stickyActiveEnabled, + gesturePending, + ]); + + const tapGesture = React.useMemo( + () => + Gesture.Tap() + .enabled(!disabled) + .maxDuration(100000) + .maxDistance(50) + .onBegin(() => { + gesturePending.value = true; + }) + .onEnd(() => { + if (stickyActiveEnabled) { + activeValue.value = true; + } + runOnJS(onPress)(); + }) + .onTouchesCancelled(() => { + gesturePending.value = false; + }) + .onFinalize(() => { + gesturePending.value = false; + }), + [disabled, activeValue, onPress, stickyActiveEnabled, gesturePending], ); const curOpacity = useSharedValue(1); const transformStyle = useAnimatedStyle(() => { - const gestureActive = - longPressState.value === GestureState.ACTIVE || - tapState.value === GestureState.BEGAN || - tapState.value === GestureState.ACTIVE || - activeValue.value; + const gestureActive = gesturePending.value || activeValue.value; if (gestureActive) { curOpacity.value = withTiming(activeOpacity, pressAnimationSpec); } else { @@ -158,42 +133,28 @@ return { flex }; }, [props.style]); - const tapHandler = ( - + const composedGesture = React.useMemo(() => { + if (!longPressGesture) { + return tapGesture; + } + return Gesture.Exclusive(longPressGesture, tapGesture); + }, [longPressGesture, tapGesture]); + + const childrenWrapperView = React.useMemo( + () => [transformStyle, props.style, props.animatedStyle], + [transformStyle, props.style, props.animatedStyle], + ); + + return ( + - + {props.children} {props.overlay} - - ); - if (!innerOnLongPress) { - return tapHandler; - } - - return ( - - {tapHandler} - + ); } -const GestureTouchableOpacity: React.ComponentType = React.forwardRef< - Props, - TapGestureHandler, ->(ForwardedGestureTouchableOpacity); -GestureTouchableOpacity.displayName = 'GestureTouchableOpacity'; - export default GestureTouchableOpacity; diff --git a/native/flow-typed/npm/react-native-gesture-handler_v2.x.x.js b/native/flow-typed/npm/react-native-gesture-handler_v2.x.x.js --- a/native/flow-typed/npm/react-native-gesture-handler_v2.x.x.js +++ b/native/flow-typed/npm/react-native-gesture-handler_v2.x.x.js @@ -659,9 +659,55 @@ oldState: $Values; } -declare type GestureStateChangeEvent = +declare export type GestureStateChangeEvent = HandlerStateChangeEventPayload & GestureStateChangeEventPayloadT; +declare export type TouchData = { + +id: number, + +x: number, + +y: number, + +absoluteX: number, + +absoluteY: number, +}; + +declare export const TouchEventType: { + +UNDETERMINED: 0, + +TOUCHES_DOWN: 1, + +TOUCHES_MOVE: 2, + +TOUCHES_UP: 3, + +TOUCHES_CANCELLED: 4, +}; + +declare export const PointerType: { + +TOUCH: 0, + +STYLUS: 1, + +MOUSE: 2, + +KEY: 3, + +OTHER: 4, +}; + +declare export type GestureTouchEvent = { + +handlerTag: number, + +numberOfTouches: number, + +state: $Values, + +eventType: $Values, + +allTouches: $ReadOnlyArray, + +changedTouches: $ReadOnlyArray, + +pointerType: $Values, +}; + +declare export type GestureStateManagerType = { + +begin: () => void, + +activate: () => void, + +fail: () => void, + +end: () => void, +}; + +type TouchEventHandlerType = ( + event: GestureTouchEvent, + stateManager: GestureStateManagerType, +) => void; + declare class BaseGesture { enabled(isEnabled: boolean): this; onBegin( @@ -682,16 +728,20 @@ success: boolean, ) => void, ): this; + onTouchesDown(callback: TouchEventHandlerType): this; + onTouchesMove(callback: TouchEventHandlerType): this; + onTouchesUp(callback: TouchEventHandlerType): this; + onTouchesCancelled(callback: TouchEventHandlerType): this; initialize(): void; toGestureArray(): Array; prepare(): void; } declare type TapGestureHandlerEventPayload = { - x: number, - y: number, - absoluteX: number, - absoluteY: number, + +x: number, + +y: number, + +absoluteX: number, + +absoluteY: number, }; declare export class TapGesture @@ -706,6 +756,21 @@ maxDeltaY(delta: number): this; } +declare type LongPressGestureHandlerEventPayload = { + +x: number, + +y: number, + +absoluteX: number, + +absoluteY: number, + +duration: number, +}; + +declare export class LongPressGesture + extends BaseGesture +{ + maxDistance(maxDist: number): this; + minDuration(duration: number): this; +} + declare type GestureUpdateEvent = GestureEventPayload & GestureEventPayloadT; @@ -722,19 +787,19 @@ } declare type PanGestureChangeEventPayload = { - changeX: number, - changeY: number, + +changeX: number, + +changeY: number, }; declare type PanGestureHandlerEventPayload = { - x: number, - y: number, - absoluteX: number, - absoluteY: number, - translationX: number, - translationY: number, - velocityX: number, - velocityY: number, + +x: number, + +y: number, + +absoluteX: number, + +absoluteY: number, + +translationX: number, + +translationY: number, + +velocityX: number, + +velocityY: number, }; declare class PanGesture @@ -767,14 +832,14 @@ } declare type PinchGestureChangeEventPayload = { - scaleChange: number, + +scaleChange: number, }; declare type PinchGestureHandlerEventPayload = { - scale: number, - focalX: number, - focalY: number, - velocity: number, + +scale: number, + +focalX: number, + +focalY: number, + +velocity: number, }; declare class PinchGesture @@ -831,6 +896,7 @@ declare const GestureObject: {| +Tap: () => TapGesture, + +LongPress: () => LongPressGesture, +Pan: () => PanGesture, +Pinch: () => PinchGesture, +Exclusive: (...gestures: $ReadOnlyArray) => ExclusiveGesture,