react-native-gesture-handler 3.2.0-nightly-20260811-cc704f09a → 3.2.0
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/android/build.gradle +21 -6
- package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +30 -66
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt +4 -2
- package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +1 -2
- package/apple/RNGestureHandler.mm +31 -23
- package/apple/RNGestureHandlerButtonComponentView.mm +10 -6
- package/lib/module/components/GestureHandlerButton.web.js +3 -2
- package/lib/module/components/GestureHandlerButton.web.js.map +1 -1
- package/lib/module/components/ReanimatedDrawerLayout.js +3 -0
- package/lib/module/components/ReanimatedDrawerLayout.js.map +1 -1
- package/lib/module/components/touchables/TouchableOpacity.js +1 -0
- package/lib/module/components/touchables/TouchableOpacity.js.map +1 -1
- package/lib/module/handlers/hitSlop.js +101 -0
- package/lib/module/handlers/hitSlop.js.map +1 -0
- package/lib/module/handlers/utils.js +3 -7
- package/lib/module/handlers/utils.js.map +1 -1
- package/lib/module/specs/RNGestureHandlerDetectorNativeComponent.ts +1 -1
- package/lib/module/specs/RNGestureHandlerRootViewNativeComponent.ts +1 -1
- package/lib/module/v3/components/Pressable.js +23 -312
- package/lib/module/v3/components/Pressable.js.map +1 -1
- package/lib/module/v3/components/PressableWithTouchable.js +259 -0
- package/lib/module/v3/components/PressableWithTouchable.js.map +1 -0
- package/lib/module/v3/components/StatefulPressable.js +338 -0
- package/lib/module/v3/components/StatefulPressable.js.map +1 -0
- package/lib/module/v3/components/pointerStyle.js +6 -0
- package/lib/module/v3/components/pointerStyle.js.map +1 -0
- package/lib/module/v3/components/pointerStyle.web.js +9 -0
- package/lib/module/v3/components/pointerStyle.web.js.map +1 -0
- package/lib/module/v3/hooks/utils/configUtils.js +3 -1
- package/lib/module/v3/hooks/utils/configUtils.js.map +1 -1
- package/lib/module/v3/hooks/utils/reanimatedUtils.js +14 -3
- package/lib/module/v3/hooks/utils/reanimatedUtils.js.map +1 -1
- package/lib/module/web/handlers/GestureHandler.js +21 -44
- package/lib/module/web/handlers/GestureHandler.js.map +1 -1
- package/lib/module/web/interfaces.js.map +1 -1
- package/lib/typescript/components/GestureComponents.web.d.ts +12 -4
- package/lib/typescript/components/GestureComponents.web.d.ts.map +1 -1
- package/lib/typescript/components/GestureHandlerButton.web.d.ts.map +1 -1
- package/lib/typescript/components/ReanimatedDrawerLayout.d.ts.map +1 -1
- package/lib/typescript/components/touchables/TouchableHighlight.d.ts +1 -1
- package/lib/typescript/components/touchables/TouchableHighlight.d.ts.map +1 -1
- package/lib/typescript/components/touchables/TouchableOpacity.d.ts.map +1 -1
- package/lib/typescript/handlers/hitSlop.d.ts +51 -0
- package/lib/typescript/handlers/hitSlop.d.ts.map +1 -0
- package/lib/typescript/handlers/utils.d.ts.map +1 -1
- package/lib/typescript/mocks/hostDetector.d.ts +5 -2
- package/lib/typescript/mocks/hostDetector.d.ts.map +1 -1
- package/lib/typescript/specs/RNGestureHandlerDetectorNativeComponent.d.ts +1 -1
- package/lib/typescript/specs/RNGestureHandlerDetectorNativeComponent.d.ts.map +1 -1
- package/lib/typescript/specs/RNGestureHandlerRootViewNativeComponent.d.ts +1 -1
- package/lib/typescript/specs/RNGestureHandlerRootViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/v3/components/GestureComponents.web.d.ts +16 -8
- package/lib/typescript/v3/components/GestureComponents.web.d.ts.map +1 -1
- package/lib/typescript/v3/components/Pressable.d.ts +13 -0
- package/lib/typescript/v3/components/Pressable.d.ts.map +1 -1
- package/lib/typescript/v3/components/PressableWithTouchable.d.ts +5 -0
- package/lib/typescript/v3/components/PressableWithTouchable.d.ts.map +1 -0
- package/lib/typescript/v3/components/StatefulPressable.d.ts +5 -0
- package/lib/typescript/v3/components/StatefulPressable.d.ts.map +1 -0
- package/lib/typescript/v3/components/pointerStyle.d.ts +3 -0
- package/lib/typescript/v3/components/pointerStyle.d.ts.map +1 -0
- package/lib/typescript/v3/components/pointerStyle.web.d.ts +3 -0
- package/lib/typescript/v3/components/pointerStyle.web.d.ts.map +1 -0
- package/lib/typescript/v3/detectors/ReanimatedNativeDetector.d.ts +3 -1
- package/lib/typescript/v3/detectors/ReanimatedNativeDetector.d.ts.map +1 -1
- package/lib/typescript/v3/detectors/ReanimatedNativeDetector.web.d.ts +3 -1
- package/lib/typescript/v3/detectors/ReanimatedNativeDetector.web.d.ts.map +1 -1
- package/lib/typescript/v3/detectors/common.d.ts +2 -2
- package/lib/typescript/v3/detectors/common.d.ts.map +1 -1
- package/lib/typescript/v3/hooks/utils/configUtils.d.ts.map +1 -1
- package/lib/typescript/v3/hooks/utils/reanimatedUtils.d.ts.map +1 -1
- package/lib/typescript/v3/types/ConfigTypes.d.ts +2 -1
- package/lib/typescript/v3/types/ConfigTypes.d.ts.map +1 -1
- package/lib/typescript/v3/types/EventTypes.d.ts +3 -2
- package/lib/typescript/v3/types/EventTypes.d.ts.map +1 -1
- package/lib/typescript/web/handlers/GestureHandler.d.ts +0 -1
- package/lib/typescript/web/handlers/GestureHandler.d.ts.map +1 -1
- package/lib/typescript/web/interfaces.d.ts +3 -12
- package/lib/typescript/web/interfaces.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/components/GestureHandlerButton.web.tsx +3 -2
- package/src/components/ReanimatedDrawerLayout.tsx +3 -0
- package/src/components/touchables/TouchableOpacity.tsx +1 -0
- package/src/handlers/hitSlop.ts +146 -0
- package/src/handlers/utils.ts +4 -2
- package/src/specs/RNGestureHandlerDetectorNativeComponent.ts +1 -1
- package/src/specs/RNGestureHandlerRootViewNativeComponent.ts +1 -1
- package/src/v3/components/Pressable.tsx +28 -448
- package/src/v3/components/PressableWithTouchable.tsx +326 -0
- package/src/v3/components/StatefulPressable.tsx +476 -0
- package/src/v3/components/pointerStyle.ts +5 -0
- package/src/v3/components/pointerStyle.web.ts +6 -0
- package/src/v3/hooks/utils/configUtils.ts +7 -1
- package/src/v3/hooks/utils/reanimatedUtils.ts +17 -8
- package/src/v3/types/ConfigTypes.ts +2 -1
- package/src/v3/types/EventTypes.ts +7 -3
- package/src/web/handlers/GestureHandler.ts +26 -77
- package/src/web/interfaces.ts +3 -13
package/src/handlers/utils.ts
CHANGED
|
@@ -4,7 +4,9 @@ import { findNodeHandle as findNodeHandleRN, Platform } from 'react-native';
|
|
|
4
4
|
import { ghQueueMicrotask } from '../ghQueueMicrotask';
|
|
5
5
|
import RNGestureHandlerModule from '../RNGestureHandlerModule';
|
|
6
6
|
import { toArray } from '../utils';
|
|
7
|
+
import type { HitSlop } from './gestureHandlerCommon';
|
|
7
8
|
import { handlerIDToTag } from './handlersRegistry';
|
|
9
|
+
import { normalizeHitSlop } from './hitSlop';
|
|
8
10
|
|
|
9
11
|
function isConfigParam(param: unknown, name: string) {
|
|
10
12
|
// param !== Object(param) returns false if `param` is a function
|
|
@@ -34,8 +36,8 @@ export function filterConfig(
|
|
|
34
36
|
if (isConfigParam(value, key)) {
|
|
35
37
|
if (key === 'simultaneousHandlers' || key === 'waitFor') {
|
|
36
38
|
value = transformIntoHandlerTags(props[key]);
|
|
37
|
-
} else if (key === 'hitSlop'
|
|
38
|
-
value =
|
|
39
|
+
} else if (key === 'hitSlop') {
|
|
40
|
+
value = normalizeHitSlop(value as HitSlop);
|
|
39
41
|
}
|
|
40
42
|
filteredConfig[key] = value;
|
|
41
43
|
}
|
|
@@ -67,7 +67,7 @@ export interface NativeProps extends ViewProps {
|
|
|
67
67
|
| undefined;
|
|
68
68
|
|
|
69
69
|
handlerTags: CodegenTypes.Int32[];
|
|
70
|
-
moduleId
|
|
70
|
+
moduleId?: CodegenTypes.WithDefault<CodegenTypes.Int32, -1>;
|
|
71
71
|
virtualChildren: VirtualChildrenProps[];
|
|
72
72
|
|
|
73
73
|
pointerEvents?: CodegenTypes.WithDefault<
|
|
@@ -1,452 +1,32 @@
|
|
|
1
|
-
import React
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
PressableProps,
|
|
21
|
-
} from '../../components/Pressable/PressableProps';
|
|
22
|
-
import {
|
|
23
|
-
getStatesConfig,
|
|
24
|
-
StateMachineEvent,
|
|
25
|
-
} from '../../components/Pressable/stateDefinitions';
|
|
26
|
-
import { PressableStateMachine } from '../../components/Pressable/StateMachine';
|
|
27
|
-
import {
|
|
28
|
-
addInsets,
|
|
29
|
-
gestureToPressableEvent,
|
|
30
|
-
gestureTouchToPressableEvent,
|
|
31
|
-
isTouchWithinInset,
|
|
32
|
-
numberAsInset,
|
|
33
|
-
viewCenterToPressableEvent,
|
|
34
|
-
} from '../../components/Pressable/utils';
|
|
35
|
-
import { getTVProps } from '../../components/utils';
|
|
36
|
-
import { PressabilityDebugView } from '../../handlers/PressabilityDebugView';
|
|
37
|
-
import { useIsScreenReaderEnabled } from '../../useIsScreenReaderEnabled';
|
|
38
|
-
import { INT32_MAX, isTestEnv } from '../../utils';
|
|
39
|
-
import { GestureDetector } from '../detectors';
|
|
40
|
-
import {
|
|
41
|
-
useHoverGesture,
|
|
42
|
-
useLongPressGesture,
|
|
43
|
-
useNativeGesture,
|
|
44
|
-
useSimultaneousGestures,
|
|
45
|
-
} from '../hooks';
|
|
46
|
-
import {
|
|
47
|
-
isKeyboardDismissingTap,
|
|
48
|
-
JSResponderContext,
|
|
49
|
-
} from '../scrollViewInterop';
|
|
50
|
-
import { PureNativeButton } from './GestureButtons';
|
|
51
|
-
|
|
52
|
-
const DEFAULT_LONG_PRESS_DURATION = 500;
|
|
53
|
-
const IS_TEST_ENV = isTestEnv();
|
|
54
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { PressableProps } from '../../components/Pressable/PressableProps';
|
|
4
|
+
import PressableWithTouchable from './PressableWithTouchable';
|
|
5
|
+
import StatefulPressable from './StatefulPressable';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `Pressable` dispatches between two implementations:
|
|
9
|
+
*
|
|
10
|
+
* - {@link StatefulPressable} — the state-machine engine, used whenever any of
|
|
11
|
+
* the `simultaneousWith` / `requireToFail` / `block` relation props is passed,
|
|
12
|
+
* since coordinating the press with an external gesture needs the composed
|
|
13
|
+
* gesture recognizers.
|
|
14
|
+
* - {@link PressableWithTouchable} — the simpler engine built on the native
|
|
15
|
+
* button `Touchable`, used for everything else (the common case).
|
|
16
|
+
*
|
|
17
|
+
* The choice is re-evaluated each render: toggling a relation prop at runtime
|
|
18
|
+
* swaps engines, which remounts and drops any in-progress press.
|
|
19
|
+
*/
|
|
55
20
|
const Pressable = (props: PressableProps) => {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
onHoverOut,
|
|
66
|
-
onPress,
|
|
67
|
-
onPressIn,
|
|
68
|
-
onPressOut,
|
|
69
|
-
onLongPress,
|
|
70
|
-
onLayout,
|
|
71
|
-
style,
|
|
72
|
-
children,
|
|
73
|
-
android_disableSound,
|
|
74
|
-
android_ripple,
|
|
75
|
-
disabled,
|
|
76
|
-
accessible,
|
|
77
|
-
simultaneousWith,
|
|
78
|
-
requireToFail,
|
|
79
|
-
block,
|
|
80
|
-
ref,
|
|
81
|
-
...remainingProps
|
|
82
|
-
} = props;
|
|
83
|
-
|
|
84
|
-
const [pressedState, setPressedState] = useState(testOnly_pressed ?? false);
|
|
85
|
-
|
|
86
|
-
const longPressTimeoutRef = useRef<number | null>(null);
|
|
87
|
-
const pressDelayTimeoutRef = useRef<number | null>(null);
|
|
88
|
-
const isOnPressAllowed = useRef<boolean>(true);
|
|
89
|
-
const jsResponderContext = use(JSResponderContext);
|
|
90
|
-
const isCurrentlyPressed = useRef<boolean>(false);
|
|
91
|
-
const dimensions = useRef<PressableDimensions>({
|
|
92
|
-
width: 0,
|
|
93
|
-
height: 0,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// When the touch that begins a press is the one dismissing the keyboard
|
|
97
|
-
// (keyboardShouldPersistTaps="never"), the press is swallowed to match RN's
|
|
98
|
-
// touchables.
|
|
99
|
-
const dropKeyboardTapRef = useRef<boolean | null>(null);
|
|
100
|
-
|
|
101
|
-
const normalizedHitSlop: Insets = useMemo(
|
|
102
|
-
() =>
|
|
103
|
-
typeof hitSlop === 'number'
|
|
104
|
-
? numberAsInset(hitSlop)
|
|
105
|
-
: (hitSlop ?? numberAsInset(0)),
|
|
106
|
-
[hitSlop]
|
|
107
|
-
);
|
|
108
|
-
const normalizedPressRetentionOffset: Insets = useMemo(
|
|
109
|
-
() =>
|
|
110
|
-
typeof pressRetentionOffset === 'number'
|
|
111
|
-
? numberAsInset(pressRetentionOffset)
|
|
112
|
-
: (pressRetentionOffset ?? {}),
|
|
113
|
-
[pressRetentionOffset]
|
|
114
|
-
);
|
|
115
|
-
const appliedHitSlop = addInsets(
|
|
116
|
-
normalizedHitSlop,
|
|
117
|
-
normalizedPressRetentionOffset
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
const cancelLongPress = useCallback(() => {
|
|
121
|
-
if (longPressTimeoutRef.current) {
|
|
122
|
-
clearTimeout(longPressTimeoutRef.current);
|
|
123
|
-
longPressTimeoutRef.current = null;
|
|
124
|
-
isOnPressAllowed.current = true;
|
|
125
|
-
}
|
|
126
|
-
}, []);
|
|
127
|
-
|
|
128
|
-
const cancelDelayedPress = useCallback(() => {
|
|
129
|
-
if (pressDelayTimeoutRef.current) {
|
|
130
|
-
clearTimeout(pressDelayTimeoutRef.current);
|
|
131
|
-
pressDelayTimeoutRef.current = null;
|
|
132
|
-
}
|
|
133
|
-
}, []);
|
|
134
|
-
|
|
135
|
-
const startLongPress = useCallback(
|
|
136
|
-
(event: PressableEvent) => {
|
|
137
|
-
if (onLongPress) {
|
|
138
|
-
cancelLongPress();
|
|
139
|
-
longPressTimeoutRef.current = setTimeout(() => {
|
|
140
|
-
isOnPressAllowed.current = false;
|
|
141
|
-
onLongPress(event);
|
|
142
|
-
}, delayLongPress ?? DEFAULT_LONG_PRESS_DURATION);
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
[onLongPress, cancelLongPress, delayLongPress]
|
|
146
|
-
);
|
|
147
|
-
const innerHandlePressIn = useCallback(
|
|
148
|
-
(event: PressableEvent) => {
|
|
149
|
-
onPressIn?.(event);
|
|
150
|
-
startLongPress(event);
|
|
151
|
-
setPressedState(true);
|
|
152
|
-
if (pressDelayTimeoutRef.current) {
|
|
153
|
-
clearTimeout(pressDelayTimeoutRef.current);
|
|
154
|
-
pressDelayTimeoutRef.current = null;
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
[onPressIn, startLongPress]
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
const handleFinalize = useCallback(() => {
|
|
161
|
-
isCurrentlyPressed.current = false;
|
|
162
|
-
dropKeyboardTapRef.current = null;
|
|
163
|
-
cancelLongPress();
|
|
164
|
-
cancelDelayedPress();
|
|
165
|
-
setPressedState(false);
|
|
166
|
-
}, [cancelDelayedPress, cancelLongPress]);
|
|
167
|
-
|
|
168
|
-
const captureKeyboardDismiss = useCallback(() => {
|
|
169
|
-
dropKeyboardTapRef.current ??= isKeyboardDismissingTap(jsResponderContext);
|
|
170
|
-
}, [jsResponderContext]);
|
|
171
|
-
|
|
172
|
-
const handlePressIn = useCallback(
|
|
173
|
-
(event: PressableEvent, skipBoundsCheck = false) => {
|
|
174
|
-
if (
|
|
175
|
-
!skipBoundsCheck &&
|
|
176
|
-
!isTouchWithinInset(
|
|
177
|
-
dimensions.current,
|
|
178
|
-
normalizedHitSlop,
|
|
179
|
-
event.nativeEvent.changedTouches.at(-1)
|
|
180
|
-
)
|
|
181
|
-
) {
|
|
182
|
-
// Ignoring pressIn within pressRetentionOffset
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
isCurrentlyPressed.current = true;
|
|
187
|
-
if (unstable_pressDelay) {
|
|
188
|
-
pressDelayTimeoutRef.current = setTimeout(() => {
|
|
189
|
-
innerHandlePressIn(event);
|
|
190
|
-
}, unstable_pressDelay);
|
|
191
|
-
} else {
|
|
192
|
-
innerHandlePressIn(event);
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
[innerHandlePressIn, normalizedHitSlop, unstable_pressDelay]
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
const handlePressOut = useCallback(
|
|
199
|
-
(event: PressableEvent, success: boolean = true) => {
|
|
200
|
-
if (!isCurrentlyPressed.current) {
|
|
201
|
-
// Some prop configurations may lead to handlePressOut being called mutliple times.
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
isCurrentlyPressed.current = false;
|
|
206
|
-
|
|
207
|
-
if (pressDelayTimeoutRef.current) {
|
|
208
|
-
innerHandlePressIn(event);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
onPressOut?.(event);
|
|
212
|
-
|
|
213
|
-
if (isOnPressAllowed.current && success) {
|
|
214
|
-
onPress?.(event);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
handleFinalize();
|
|
218
|
-
},
|
|
219
|
-
[handleFinalize, innerHandlePressIn, onPress, onPressOut]
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
const stateMachine = useMemo(() => new PressableStateMachine(), []);
|
|
223
|
-
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
|
224
|
-
|
|
225
|
-
useEffect(() => {
|
|
226
|
-
const configuration = getStatesConfig(
|
|
227
|
-
handlePressIn,
|
|
228
|
-
handlePressOut,
|
|
229
|
-
isScreenReaderEnabled
|
|
230
|
-
);
|
|
231
|
-
stateMachine.setStates(configuration);
|
|
232
|
-
}, [handlePressIn, handlePressOut, stateMachine, isScreenReaderEnabled]);
|
|
233
|
-
|
|
234
|
-
const hoverInTimeout = useRef<number | null>(null);
|
|
235
|
-
const hoverOutTimeout = useRef<number | null>(null);
|
|
236
|
-
|
|
237
|
-
const hoverGesture = useHoverGesture({
|
|
238
|
-
manualActivation: true, // Prevents Hover blocking Native gesture on web
|
|
239
|
-
cancelsTouchesInView: false,
|
|
240
|
-
onBegin: (event) => {
|
|
241
|
-
if (hoverOutTimeout.current) {
|
|
242
|
-
clearTimeout(hoverOutTimeout.current);
|
|
243
|
-
}
|
|
244
|
-
if (delayHoverIn) {
|
|
245
|
-
hoverInTimeout.current = setTimeout(
|
|
246
|
-
() => onHoverIn?.(gestureToPressableEvent(event)),
|
|
247
|
-
delayHoverIn
|
|
248
|
-
);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
onHoverIn?.(gestureToPressableEvent(event));
|
|
252
|
-
},
|
|
253
|
-
onFinalize: (event) => {
|
|
254
|
-
if (hoverInTimeout.current) {
|
|
255
|
-
clearTimeout(hoverInTimeout.current);
|
|
256
|
-
}
|
|
257
|
-
if (delayHoverOut) {
|
|
258
|
-
hoverOutTimeout.current = setTimeout(
|
|
259
|
-
() => onHoverOut?.(gestureToPressableEvent(event)),
|
|
260
|
-
delayHoverOut
|
|
261
|
-
);
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
onHoverOut?.(gestureToPressableEvent(event));
|
|
265
|
-
},
|
|
266
|
-
enabled: disabled !== true,
|
|
267
|
-
disableReanimated: true,
|
|
268
|
-
simultaneousWith,
|
|
269
|
-
block,
|
|
270
|
-
requireToFail,
|
|
271
|
-
hitSlop: appliedHitSlop,
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
const pressAndTouchGesture = useLongPressGesture({
|
|
275
|
-
minDuration: Platform.OS === 'web' ? 0 : INT32_MAX, // Long press handles finalize on web, thus it must activate right away
|
|
276
|
-
maxDistance: INT32_MAX, // Stops long press from cancelling on touch move
|
|
277
|
-
cancelsTouchesInView: false,
|
|
278
|
-
onTouchesDown: (event) => {
|
|
279
|
-
captureKeyboardDismiss();
|
|
280
|
-
|
|
281
|
-
if (dropKeyboardTapRef.current) {
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const pressableEvent = gestureTouchToPressableEvent(event);
|
|
286
|
-
stateMachine.handleEvent(
|
|
287
|
-
StateMachineEvent.LONG_PRESS_TOUCHES_DOWN,
|
|
288
|
-
pressableEvent
|
|
289
|
-
);
|
|
290
|
-
},
|
|
291
|
-
onTouchesUp: () => {
|
|
292
|
-
if (Platform.OS === 'android' && !isScreenReaderEnabled) {
|
|
293
|
-
// Prevents potential soft-locks
|
|
294
|
-
stateMachine.reset();
|
|
295
|
-
handleFinalize();
|
|
296
|
-
}
|
|
297
|
-
},
|
|
298
|
-
onTouchesCancel: (event) => {
|
|
299
|
-
const pressableEvent = gestureTouchToPressableEvent(event);
|
|
300
|
-
stateMachine.reset();
|
|
301
|
-
handlePressOut(pressableEvent, false);
|
|
302
|
-
},
|
|
303
|
-
onFinalize: (event) => {
|
|
304
|
-
if (Platform.OS !== 'web') {
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
stateMachine.handleEvent(
|
|
309
|
-
event.canceled ? StateMachineEvent.CANCEL : StateMachineEvent.FINALIZE
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
handleFinalize();
|
|
313
|
-
},
|
|
314
|
-
enabled: disabled !== true,
|
|
315
|
-
disableReanimated: true,
|
|
316
|
-
simultaneousWith: simultaneousWith,
|
|
317
|
-
block: block,
|
|
318
|
-
requireToFail: requireToFail,
|
|
319
|
-
hitSlop: appliedHitSlop,
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
// RNButton is placed inside ButtonGesture to enable Android's ripple and to capture non-propagating events
|
|
323
|
-
const buttonGesture = useNativeGesture({
|
|
324
|
-
onTouchesCancel: (event) => {
|
|
325
|
-
if (Platform.OS !== 'macos' && Platform.OS !== 'web') {
|
|
326
|
-
// On MacOS cancel occurs in middle of gesture
|
|
327
|
-
// On Web cancel occurs on mouse move, which is unwanted
|
|
328
|
-
const pressableEvent = gestureTouchToPressableEvent(event);
|
|
329
|
-
stateMachine.reset();
|
|
330
|
-
handlePressOut(pressableEvent, false);
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
onBegin: () => {
|
|
334
|
-
captureKeyboardDismiss();
|
|
335
|
-
|
|
336
|
-
if (dropKeyboardTapRef.current) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (Platform.isTV) {
|
|
341
|
-
// tvOS drives this native gesture from the focus-engine Select press.
|
|
342
|
-
// The press state machine is touch-based and never
|
|
343
|
-
// receives LONG_PRESS_TOUCHES_DOWN here, so bypass it and drive the press handlers directly.
|
|
344
|
-
// A focus-driven press has no coordinates, so skip the hit-slop bounds check entirely.
|
|
345
|
-
handlePressIn(viewCenterToPressableEvent(dimensions.current), true);
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
if (Platform.OS === 'android' && isScreenReaderEnabled) {
|
|
349
|
-
stateMachine.handleEvent(
|
|
350
|
-
StateMachineEvent.NATIVE_BEGIN,
|
|
351
|
-
viewCenterToPressableEvent(dimensions.current)
|
|
352
|
-
);
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
stateMachine.handleEvent(StateMachineEvent.NATIVE_BEGIN);
|
|
356
|
-
},
|
|
357
|
-
onActivate: () => {
|
|
358
|
-
if (!Platform.isTV && Platform.OS !== 'android') {
|
|
359
|
-
stateMachine.handleEvent(StateMachineEvent.NATIVE_START);
|
|
360
|
-
}
|
|
361
|
-
},
|
|
362
|
-
onFinalize: (event) => {
|
|
363
|
-
// On Web we use LongPress.onFinalize instead of Native.onFinalize,
|
|
364
|
-
// as Native cancels on mouse move, and LongPress does not.
|
|
365
|
-
if (Platform.OS === 'web') {
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (Platform.isTV) {
|
|
370
|
-
handlePressOut(
|
|
371
|
-
viewCenterToPressableEvent(dimensions.current),
|
|
372
|
-
!event.canceled
|
|
373
|
-
);
|
|
374
|
-
handleFinalize();
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
stateMachine.handleEvent(
|
|
379
|
-
event.canceled ? StateMachineEvent.CANCEL : StateMachineEvent.FINALIZE
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
handleFinalize();
|
|
383
|
-
},
|
|
384
|
-
enabled: disabled !== true,
|
|
385
|
-
disableReanimated: true,
|
|
386
|
-
simultaneousWith,
|
|
387
|
-
block,
|
|
388
|
-
requireToFail,
|
|
389
|
-
hitSlop: appliedHitSlop,
|
|
390
|
-
shouldActivateOnStart: Platform.OS === 'web',
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
const gesture = useSimultaneousGestures(
|
|
394
|
-
buttonGesture,
|
|
395
|
-
pressAndTouchGesture,
|
|
396
|
-
hoverGesture
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
// `cursor: 'pointer'` on `RNButton` crashes iOS
|
|
400
|
-
const pointerStyle: StyleProp<ViewStyle> =
|
|
401
|
-
Platform.OS === 'web' ? { cursor: 'pointer' } : {};
|
|
402
|
-
|
|
403
|
-
const styleProp =
|
|
404
|
-
typeof style === 'function' ? style({ pressed: pressedState }) : style;
|
|
405
|
-
|
|
406
|
-
const childrenProp =
|
|
407
|
-
typeof children === 'function'
|
|
408
|
-
? children({ pressed: pressedState })
|
|
409
|
-
: children;
|
|
410
|
-
|
|
411
|
-
const rippleColor = useMemo(() => {
|
|
412
|
-
const defaultRippleColor = android_ripple ? undefined : 'transparent';
|
|
413
|
-
return android_ripple?.color ?? defaultRippleColor;
|
|
414
|
-
}, [android_ripple]);
|
|
415
|
-
|
|
416
|
-
const setDimensions = useCallback(
|
|
417
|
-
(event: LayoutChangeEvent) => {
|
|
418
|
-
onLayout?.(event);
|
|
419
|
-
dimensions.current = event.nativeEvent.layout;
|
|
420
|
-
},
|
|
421
|
-
[onLayout]
|
|
422
|
-
);
|
|
423
|
-
|
|
424
|
-
const tvProps = getTVProps(remainingProps);
|
|
425
|
-
|
|
426
|
-
return (
|
|
427
|
-
<GestureDetector gesture={gesture}>
|
|
428
|
-
<PureNativeButton
|
|
429
|
-
{...remainingProps}
|
|
430
|
-
ref={ref as React.Ref<React.ComponentRef<typeof PureNativeButton>>}
|
|
431
|
-
{...tvProps}
|
|
432
|
-
onLayout={setDimensions}
|
|
433
|
-
accessible={accessible !== false}
|
|
434
|
-
hitSlop={appliedHitSlop}
|
|
435
|
-
enabled={disabled !== true}
|
|
436
|
-
touchSoundDisabled={android_disableSound ?? undefined}
|
|
437
|
-
rippleColor={rippleColor}
|
|
438
|
-
rippleRadius={android_ripple?.radius ?? undefined}
|
|
439
|
-
style={[pointerStyle, styleProp]}
|
|
440
|
-
testOnly_onPress={IS_TEST_ENV ? onPress : undefined}
|
|
441
|
-
testOnly_onPressIn={IS_TEST_ENV ? onPressIn : undefined}
|
|
442
|
-
testOnly_onPressOut={IS_TEST_ENV ? onPressOut : undefined}
|
|
443
|
-
testOnly_onLongPress={IS_TEST_ENV ? onLongPress : undefined}>
|
|
444
|
-
{childrenProp}
|
|
445
|
-
{__DEV__ ? (
|
|
446
|
-
<PressabilityDebugView color="red" hitSlop={normalizedHitSlop} />
|
|
447
|
-
) : null}
|
|
448
|
-
</PureNativeButton>
|
|
449
|
-
</GestureDetector>
|
|
21
|
+
const usesRelations =
|
|
22
|
+
props.simultaneousWith != null ||
|
|
23
|
+
props.requireToFail != null ||
|
|
24
|
+
props.block != null;
|
|
25
|
+
|
|
26
|
+
return usesRelations ? (
|
|
27
|
+
<StatefulPressable {...props} />
|
|
28
|
+
) : (
|
|
29
|
+
<PressableWithTouchable {...props} />
|
|
450
30
|
);
|
|
451
31
|
};
|
|
452
32
|
|