react-native-gesture-handler 3.0.0-beta.4 → 3.0.0-beta.5

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.
Files changed (162) hide show
  1. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +12 -4
  2. package/android/src/main/java/com/swmansion/gesturehandler/core/NativeViewGestureHandler.kt +6 -2
  3. package/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt +21 -0
  4. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt +113 -49
  5. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +75 -98
  6. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +7 -10
  7. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRegistry.kt +64 -2
  8. package/apple/RNGestureHandler.mm +50 -27
  9. package/apple/RNGestureHandlerButton.h +4 -2
  10. package/apple/RNGestureHandlerButton.mm +106 -27
  11. package/apple/RNGestureHandlerButtonComponentView.mm +17 -2
  12. package/apple/RNGestureHandlerDetector.mm +99 -75
  13. package/apple/RNGestureHandlerModule.mm +11 -14
  14. package/apple/RNGestureHandlerRegistry.h +14 -0
  15. package/apple/RNGestureHandlerRegistry.m +56 -0
  16. package/lib/module/RNGestureHandlerModule.web.js +5 -1
  17. package/lib/module/RNGestureHandlerModule.web.js.map +1 -1
  18. package/lib/module/components/GestureButtons.js +16 -5
  19. package/lib/module/components/GestureButtons.js.map +1 -1
  20. package/lib/module/components/GestureHandlerButton.js.map +1 -1
  21. package/lib/module/components/GestureHandlerButton.web.js +63 -23
  22. package/lib/module/components/GestureHandlerButton.web.js.map +1 -1
  23. package/lib/module/components/Pressable/Pressable.js +1 -0
  24. package/lib/module/components/Pressable/Pressable.js.map +1 -1
  25. package/lib/module/components/ReanimatedDrawerLayout.js.map +1 -1
  26. package/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js +38 -5
  27. package/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js.map +1 -1
  28. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js +1 -2
  29. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
  30. package/lib/module/handlers/gestures/GestureDetector/utils.js +0 -47
  31. package/lib/module/handlers/gestures/GestureDetector/utils.js.map +1 -1
  32. package/lib/module/handlers/gestures/reanimatedWrapper.js +14 -2
  33. package/lib/module/handlers/gestures/reanimatedWrapper.js.map +1 -1
  34. package/lib/module/mocks/module.js +3 -2
  35. package/lib/module/mocks/module.js.map +1 -1
  36. package/lib/module/specs/NativeRNGestureHandlerModule.js.map +1 -1
  37. package/lib/module/specs/RNGestureHandlerButtonNativeComponent.ts +28 -13
  38. package/lib/module/v3/NativeProxy.js +5 -3
  39. package/lib/module/v3/NativeProxy.js.map +1 -1
  40. package/lib/module/v3/NativeProxy.web.js +2 -2
  41. package/lib/module/v3/NativeProxy.web.js.map +1 -1
  42. package/lib/module/v3/components/GestureButtons.js +8 -3
  43. package/lib/module/v3/components/GestureButtons.js.map +1 -1
  44. package/lib/module/v3/components/Touchable/Touchable.js +53 -4
  45. package/lib/module/v3/components/Touchable/Touchable.js.map +1 -1
  46. package/lib/module/v3/detectors/HostGestureDetector.web.js +178 -59
  47. package/lib/module/v3/detectors/HostGestureDetector.web.js.map +1 -1
  48. package/lib/module/v3/detectors/NativeDetector.js +3 -2
  49. package/lib/module/v3/detectors/NativeDetector.js.map +1 -1
  50. package/lib/module/v3/detectors/VirtualDetector/InterceptingGestureDetector.js +3 -4
  51. package/lib/module/v3/detectors/VirtualDetector/InterceptingGestureDetector.js.map +1 -1
  52. package/lib/module/v3/detectors/VirtualDetector/VirtualDetector.js +2 -2
  53. package/lib/module/v3/detectors/VirtualDetector/VirtualDetector.js.map +1 -1
  54. package/lib/module/v3/detectors/useGestureRelationsUpdater.js +23 -0
  55. package/lib/module/v3/detectors/useGestureRelationsUpdater.js.map +1 -0
  56. package/lib/module/v3/detectors/utils.js +10 -8
  57. package/lib/module/v3/detectors/utils.js.map +1 -1
  58. package/lib/module/v3/hooks/useGesture.js +3 -18
  59. package/lib/module/v3/hooks/useGesture.js.map +1 -1
  60. package/lib/module/v3/hooks/utils/configUtils.js +1 -3
  61. package/lib/module/v3/hooks/utils/configUtils.js.map +1 -1
  62. package/lib/module/v3/hooks/utils/eventHandlersUtils.js +31 -29
  63. package/lib/module/v3/hooks/utils/eventHandlersUtils.js.map +1 -1
  64. package/lib/module/v3/hooks/utils/reanimatedUtils.js +8 -2
  65. package/lib/module/v3/hooks/utils/reanimatedUtils.js.map +1 -1
  66. package/lib/module/web/tools/NodeManager.js +44 -0
  67. package/lib/module/web/tools/NodeManager.js.map +1 -1
  68. package/lib/typescript/RNGestureHandlerModule.web.d.ts +1 -1
  69. package/lib/typescript/RNGestureHandlerModule.web.d.ts.map +1 -1
  70. package/lib/typescript/components/GestureButtons.d.ts +14 -6
  71. package/lib/typescript/components/GestureButtons.d.ts.map +1 -1
  72. package/lib/typescript/components/GestureHandlerButton.d.ts +62 -8
  73. package/lib/typescript/components/GestureHandlerButton.d.ts.map +1 -1
  74. package/lib/typescript/components/GestureHandlerButton.web.d.ts +10 -3
  75. package/lib/typescript/components/GestureHandlerButton.web.d.ts.map +1 -1
  76. package/lib/typescript/components/Pressable/Pressable.d.ts.map +1 -1
  77. package/lib/typescript/components/Pressable/PressableProps.d.ts +1 -1
  78. package/lib/typescript/components/Pressable/PressableProps.d.ts.map +1 -1
  79. package/lib/typescript/components/ReanimatedDrawerLayout.d.ts +16 -14
  80. package/lib/typescript/components/ReanimatedDrawerLayout.d.ts.map +1 -1
  81. package/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeable.d.ts +2 -1
  82. package/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeable.d.ts.map +1 -1
  83. package/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts +30 -34
  84. package/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts.map +1 -1
  85. package/lib/typescript/handlers/gestures/GestureDetector/useDetectorUpdater.d.ts.map +1 -1
  86. package/lib/typescript/handlers/gestures/GestureDetector/utils.d.ts +0 -1
  87. package/lib/typescript/handlers/gestures/GestureDetector/utils.d.ts.map +1 -1
  88. package/lib/typescript/handlers/gestures/reanimatedWrapper.d.ts.map +1 -1
  89. package/lib/typescript/mocks/module.d.ts +1 -1
  90. package/lib/typescript/mocks/module.d.ts.map +1 -1
  91. package/lib/typescript/specs/NativeRNGestureHandlerModule.d.ts +2 -2
  92. package/lib/typescript/specs/NativeRNGestureHandlerModule.d.ts.map +1 -1
  93. package/lib/typescript/specs/RNGestureHandlerButtonNativeComponent.d.ts +19 -11
  94. package/lib/typescript/specs/RNGestureHandlerButtonNativeComponent.d.ts.map +1 -1
  95. package/lib/typescript/v3/NativeProxy.d.ts +1 -1
  96. package/lib/typescript/v3/NativeProxy.d.ts.map +1 -1
  97. package/lib/typescript/v3/NativeProxy.web.d.ts +1 -1
  98. package/lib/typescript/v3/NativeProxy.web.d.ts.map +1 -1
  99. package/lib/typescript/v3/components/GestureButtons.d.ts +1 -38
  100. package/lib/typescript/v3/components/GestureButtons.d.ts.map +1 -1
  101. package/lib/typescript/v3/components/GestureButtonsProps.d.ts +1 -1
  102. package/lib/typescript/v3/components/GestureButtonsProps.d.ts.map +1 -1
  103. package/lib/typescript/v3/components/Touchable/Touchable.d.ts.map +1 -1
  104. package/lib/typescript/v3/components/Touchable/TouchableProps.d.ts +39 -1
  105. package/lib/typescript/v3/components/Touchable/TouchableProps.d.ts.map +1 -1
  106. package/lib/typescript/v3/detectors/HostGestureDetector.web.d.ts.map +1 -1
  107. package/lib/typescript/v3/detectors/NativeDetector.d.ts.map +1 -1
  108. package/lib/typescript/v3/detectors/VirtualDetector/InterceptingGestureDetector.d.ts.map +1 -1
  109. package/lib/typescript/v3/detectors/useGestureRelationsUpdater.d.ts +3 -0
  110. package/lib/typescript/v3/detectors/useGestureRelationsUpdater.d.ts.map +1 -0
  111. package/lib/typescript/v3/detectors/utils.d.ts +3 -3
  112. package/lib/typescript/v3/detectors/utils.d.ts.map +1 -1
  113. package/lib/typescript/v3/hooks/useGesture.d.ts.map +1 -1
  114. package/lib/typescript/v3/hooks/utils/configUtils.d.ts.map +1 -1
  115. package/lib/typescript/v3/hooks/utils/eventHandlersUtils.d.ts.map +1 -1
  116. package/lib/typescript/v3/hooks/utils/reanimatedUtils.d.ts +1 -0
  117. package/lib/typescript/v3/hooks/utils/reanimatedUtils.d.ts.map +1 -1
  118. package/lib/typescript/web/tools/NodeManager.d.ts +7 -0
  119. package/lib/typescript/web/tools/NodeManager.d.ts.map +1 -1
  120. package/package.json +3 -3
  121. package/src/RNGestureHandlerModule.web.ts +5 -1
  122. package/src/components/GestureButtons.tsx +23 -7
  123. package/src/components/GestureHandlerButton.tsx +70 -8
  124. package/src/components/GestureHandlerButton.web.tsx +97 -29
  125. package/src/components/Pressable/Pressable.tsx +1 -0
  126. package/src/components/Pressable/PressableProps.tsx +2 -1
  127. package/src/components/ReanimatedDrawerLayout.tsx +27 -23
  128. package/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx +51 -5
  129. package/src/components/ReanimatedSwipeable/ReanimatedSwipeableProps.ts +31 -39
  130. package/src/handlers/gestures/GestureDetector/useDetectorUpdater.ts +1 -2
  131. package/src/handlers/gestures/GestureDetector/utils.ts +0 -52
  132. package/src/handlers/gestures/reanimatedWrapper.ts +20 -2
  133. package/src/mocks/module.tsx +4 -2
  134. package/src/specs/NativeRNGestureHandlerModule.ts +2 -4
  135. package/src/specs/RNGestureHandlerButtonNativeComponent.ts +28 -13
  136. package/src/v3/NativeProxy.ts +9 -7
  137. package/src/v3/NativeProxy.web.ts +2 -2
  138. package/src/v3/components/GestureButtons.tsx +13 -5
  139. package/src/v3/components/GestureButtonsProps.ts +1 -0
  140. package/src/v3/components/Touchable/Touchable.tsx +65 -4
  141. package/src/v3/components/Touchable/TouchableProps.ts +49 -1
  142. package/src/v3/detectors/HostGestureDetector.web.tsx +265 -108
  143. package/src/v3/detectors/NativeDetector.tsx +3 -2
  144. package/src/v3/detectors/VirtualDetector/InterceptingGestureDetector.tsx +3 -4
  145. package/src/v3/detectors/VirtualDetector/VirtualDetector.tsx +2 -2
  146. package/src/v3/detectors/useGestureRelationsUpdater.ts +30 -0
  147. package/src/v3/detectors/utils.ts +28 -12
  148. package/src/v3/hooks/useGesture.ts +4 -14
  149. package/src/v3/hooks/utils/configUtils.ts +2 -3
  150. package/src/v3/hooks/utils/eventHandlersUtils.ts +43 -32
  151. package/src/v3/hooks/utils/reanimatedUtils.ts +10 -10
  152. package/src/web/tools/NodeManager.ts +57 -0
  153. package/lib/module/RNRenderer.js +0 -6
  154. package/lib/module/RNRenderer.js.map +0 -1
  155. package/lib/module/RNRenderer.web.js +0 -6
  156. package/lib/module/RNRenderer.web.js.map +0 -1
  157. package/lib/typescript/RNRenderer.d.ts +0 -2
  158. package/lib/typescript/RNRenderer.d.ts.map +0 -1
  159. package/lib/typescript/RNRenderer.web.d.ts +0 -4
  160. package/lib/typescript/RNRenderer.web.d.ts.map +0 -1
  161. package/src/RNRenderer.ts +0 -3
  162. package/src/RNRenderer.web.ts +0 -3
@@ -3,17 +3,14 @@ import type { StyleProp, ViewStyle } from 'react-native';
3
3
  import type { SharedValue } from 'react-native-reanimated';
4
4
 
5
5
  import type { HitSlop } from '../../handlers/gestureHandlerCommon';
6
- import type { AnyGesture } from '../../v3/types';
6
+ import type { AnyGesture, WithSharedValue } from '../../v3/types';
7
7
 
8
8
  export enum SwipeDirection {
9
9
  LEFT = 'left',
10
10
  RIGHT = 'right',
11
11
  }
12
12
 
13
- export interface SwipeableProps {
14
- /**
15
- *
16
- */
13
+ export type SwipeableProps = {
17
14
  ref?: React.Ref<SwipeableMethods>;
18
15
 
19
16
  /**
@@ -23,27 +20,6 @@ export interface SwipeableProps {
23
20
 
24
21
  children?: React.ReactNode;
25
22
 
26
- /**
27
- * Indicates whether `ReanimatedSwipeable` should be analyzing stream of touch events or not.
28
- * @see https://docs.swmansion.com/react-native-gesture-handler/docs/gestures/pan-gesture#enabledvalue-boolean
29
- */
30
- enabled?: boolean;
31
-
32
- /**
33
- * This parameter enables control over what part of the connected view area can be used to begin recognizing the gesture.
34
- * When a negative number is provided the bounds of the view will reduce the area by the given number of points in each of the sides evenly.
35
- * @see https://docs.swmansion.com/react-native-gesture-handler/docs/gestures/pan-gesture#hitslopsettings
36
- */
37
- hitSlop?: HitSlop;
38
-
39
- /**
40
- * Enables two-finger gestures on supported devices, for example iPads with
41
- * trackpads. If not enabled the gesture will require click + drag, with
42
- * `enableTrackpadTwoFingerGesture` swiping with two fingers will also trigger
43
- * the gesture.
44
- */
45
- enableTrackpadTwoFingerGesture?: boolean;
46
-
47
23
  /**
48
24
  * Specifies how much the visual interaction will be delayed compared to the
49
25
  * gesture distance. e.g. value of 1 will indicate that the swipeable panel
@@ -66,18 +42,6 @@ export interface SwipeableProps {
66
42
  */
67
43
  rightThreshold?: number;
68
44
 
69
- /**
70
- * Distance that the panel must be dragged from the left edge to be considered
71
- * a swipe. The default value is 10.
72
- */
73
- dragOffsetFromLeftEdge?: number;
74
-
75
- /**
76
- * Distance that the panel must be dragged from the right edge to be considered
77
- * a swipe. The default value is 10.
78
- */
79
- dragOffsetFromRightEdge?: number;
80
-
81
45
  /**
82
46
  * Value indicating if the swipeable panel can be pulled further than the left
83
47
  * actions panel's width. It is set to true by default as long as the left
@@ -204,7 +168,35 @@ export interface SwipeableProps {
204
168
  * used with the swipeable's gesture handler.
205
169
  */
206
170
  block?: AnyGesture | AnyGesture[];
207
- }
171
+ } & WithSharedValue<{
172
+ /**
173
+ * Indicates whether `ReanimatedSwipeable` should be analyzing stream of touch events or not.
174
+ * @see https://docs.swmansion.com/react-native-gesture-handler/docs/gestures/pan-gesture#enabledvalue-boolean
175
+ */
176
+ enabled?: boolean | undefined;
177
+ /**
178
+ * This parameter enables control over what part of the connected view area can be used to begin recognizing the gesture.
179
+ * When a negative number is provided the bounds of the view will reduce the area by the given number of points in each of the sides evenly.
180
+ * @see https://docs.swmansion.com/react-native-gesture-handler/docs/gestures/pan-gesture#hitslopsettings
181
+ */
182
+ hitSlop?: HitSlop | undefined;
183
+ /**
184
+ * Enables two-finger gestures on supported devices, for example iPads with
185
+ * trackpads. If not enabled the gesture will require click + drag, with
186
+ * `enableTrackpadTwoFingerGesture` swiping with two fingers will also trigger
187
+ * the gesture.
188
+ */
189
+ enableTrackpadTwoFingerGesture?: boolean | undefined;
190
+ /**
191
+ * The horizontal offset from the starting point required to trigger a right-swipe gesture. Defaults to 10.
192
+ */
193
+ dragOffsetFromLeft?: number;
194
+
195
+ /**
196
+ * The horizontal offset from the starting point required to trigger a left-swipe gesture. Defaults to -10.
197
+ */
198
+ dragOffsetFromRight?: number;
199
+ }>;
208
200
 
209
201
  export interface SwipeableMethods {
210
202
  close: () => void;
@@ -10,7 +10,7 @@ import { dropHandlers } from './dropHandlers';
10
10
  import { needsToReattach } from './needsToReattach';
11
11
  import type { AttachedGestureState, GestureDetectorState } from './types';
12
12
  import { updateHandlers } from './updateHandlers';
13
- import { useForceRender, validateDetectorChildren } from './utils';
13
+ import { useForceRender } from './utils';
14
14
 
15
15
  // Returns a function that's responsible for updating the attached gestures
16
16
  // If the view has changed, it will reattach the handlers to the new view
@@ -34,7 +34,6 @@ export function useDetectorUpdater(
34
34
  didUnderlyingViewChange ||
35
35
  needsToReattach(preparedGesture, gesturesToAttach)
36
36
  ) {
37
- validateDetectorChildren(state.viewRef);
38
37
  dropHandlers(preparedGesture);
39
38
  attachHandlers({
40
39
  preparedGesture,
@@ -1,7 +1,5 @@
1
1
  import { useCallback, useRef, useState } from 'react';
2
- import { Platform } from 'react-native';
3
2
 
4
- import { RNRenderer } from '../../../RNRenderer';
5
3
  import { isTestEnv, tagMessage } from '../../../utils';
6
4
  import type {
7
5
  GestureHandlerNativeEvent,
@@ -124,56 +122,6 @@ export function checkGestureCallbacksForWorklets(gesture: GestureType) {
124
122
  }
125
123
  }
126
124
 
127
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
- export function validateDetectorChildren(ref: any) {
129
- // Finds the first native view under the Wrap component and traverses the fiber tree upwards
130
- // to check whether there is more than one native view as a pseudo-direct child of GestureDetector
131
- // i.e. this is not ok:
132
- // Wrap
133
- // |
134
- // / \
135
- // / \
136
- // / \
137
- // / \
138
- // NativeView NativeView
139
- //
140
- // but this is fine:
141
- // Wrap
142
- // |
143
- // NativeView
144
- // |
145
- // / \
146
- // / \
147
- // / \
148
- // / \
149
- // NativeView NativeView
150
- if (__DEV__ && Platform.OS !== 'web') {
151
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
152
- const wrapType =
153
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
154
- ref._reactInternals.elementType;
155
-
156
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
157
- let instance =
158
- RNRenderer.findHostInstance_DEPRECATED(
159
- ref
160
- )._internalFiberInstanceHandleDEV;
161
-
162
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
163
- while (instance && instance.elementType !== wrapType) {
164
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
165
- if (instance.sibling) {
166
- throw new Error(
167
- 'GestureDetector has more than one native view as its children. This can happen if you are using a custom component that renders multiple views, like React.Fragment. You should wrap content of GestureDetector with a <View> or <Animated.View>.'
168
- );
169
- }
170
-
171
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
172
- instance = instance.return;
173
- }
174
- }
175
- }
176
-
177
125
  export function useForceRender() {
178
126
  const [renderState, setRenderState] = useState(false);
179
127
  const forceRender = useCallback(() => {
@@ -1,5 +1,6 @@
1
1
  import type { ComponentClass } from 'react';
2
2
 
3
+ import { ghQueueMicrotask } from '../../ghQueueMicrotask';
3
4
  import { tagMessage } from '../../utils';
4
5
  import { NativeProxy } from '../../v3/NativeProxy';
5
6
  import type {
@@ -82,7 +83,25 @@ let Reanimated:
82
83
 
83
84
  try {
84
85
  Reanimated = require('react-native-reanimated');
85
- NativeProxy.setReanimatedAvailable(true);
86
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
87
+ const Worklets = require('react-native-worklets');
88
+
89
+ // Make sure worklets are initialized before attempting to install UI runtime bindings
90
+ Worklets?.scheduleOnUI(() => {
91
+ 'worklet';
92
+ });
93
+
94
+ ghQueueMicrotask(() => {
95
+ const decorated = NativeProxy.installUIRuntimeBindings();
96
+
97
+ if (!decorated) {
98
+ console.warn(
99
+ tagMessage(
100
+ 'Failed to install UI runtime bindings. Please report this at https://github.com/software-mansion/react-native-gesture-handler/issues.'
101
+ )
102
+ );
103
+ }
104
+ });
86
105
  } catch (e) {
87
106
  // When 'react-native-reanimated' is not available we want to quietly continue
88
107
  // @ts-ignore TS demands the variable to be initialized
@@ -93,7 +112,6 @@ if (!Reanimated?.useSharedValue) {
93
112
  // @ts-ignore Make sure the loaded module is actually Reanimated, if it's not
94
113
  // reset the module to undefined so we can fallback to the default implementation
95
114
  Reanimated = undefined;
96
- NativeProxy.setReanimatedAvailable(false);
97
115
  }
98
116
 
99
117
  if (Reanimated !== undefined && !Reanimated.setGestureState) {
@@ -2,6 +2,8 @@ const NOOP = () => {
2
2
  // Do nothing
3
3
  };
4
4
 
5
+ const NOOPTrue = () => true;
6
+
5
7
  const attachGestureHandler = NOOP;
6
8
  const createGestureHandler = NOOP;
7
9
  const dropGestureHandler = NOOP;
@@ -9,7 +11,7 @@ const setGestureHandlerConfig = NOOP;
9
11
  const updateGestureHandlerConfig = NOOP;
10
12
  const flushOperations = NOOP;
11
13
  const configureRelations = NOOP;
12
- const setReanimatedAvailable = NOOP;
14
+ const installUIRuntimeBindings = NOOPTrue;
13
15
  const install = NOOP;
14
16
 
15
17
  export default {
@@ -19,7 +21,7 @@ export default {
19
21
  setGestureHandlerConfig,
20
22
  updateGestureHandlerConfig,
21
23
  configureRelations,
22
- setReanimatedAvailable,
24
+ installUIRuntimeBindings,
23
25
  flushOperations,
24
26
  install,
25
27
  } as const;
@@ -3,15 +3,13 @@ import { TurboModuleRegistry } from 'react-native';
3
3
  import type { Double } from 'react-native/Libraries/Types/CodegenTypes';
4
4
 
5
5
  export interface Spec extends TurboModule {
6
- // This method returns a boolean only to force the codegen to generate
7
- // a synchronous method. The returned value doesn't have any meaning.
8
6
  createGestureHandler: (
9
7
  handlerName: string,
10
8
  handlerTag: Double,
11
9
  // Record<> is not supported by codegen
12
10
  // eslint-disable-next-line @typescript-eslint/ban-types
13
11
  config: Object
14
- ) => boolean;
12
+ ) => void;
15
13
  attachGestureHandler: (
16
14
  handlerTag: Double,
17
15
  newView: Double,
@@ -25,7 +23,7 @@ export interface Spec extends TurboModule {
25
23
  configureRelations: (handlerTag: Double, relations: Object) => void;
26
24
  dropGestureHandler: (handlerTag: Double) => void;
27
25
  flushOperations: () => void;
28
- setReanimatedAvailable: (isAvailable: boolean) => void;
26
+ installUIRuntimeBindings: () => boolean;
29
27
  }
30
28
 
31
29
  export default TurboModuleRegistry.getEnforcing<Spec>('RNGestureHandlerModule');
@@ -2,6 +2,7 @@ import type { ColorValue, ViewProps } from 'react-native';
2
2
  import type {
3
3
  Float,
4
4
  Int32,
5
+ UnsafeMixed,
5
6
  WithDefault,
6
7
  } from 'react-native/Libraries/Types/CodegenTypes';
7
8
  import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
@@ -19,8 +20,11 @@ interface NativeProps extends ViewProps {
19
20
  'box-none' | 'none' | 'box-only' | 'auto',
20
21
  'auto'
21
22
  >;
22
- pressAndHoldAnimationDuration?: WithDefault<Int32, -1>;
23
- tapAnimationDuration?: WithDefault<Int32, 100>;
23
+ tapAnimationInDuration?: WithDefault<Int32, 50>;
24
+ tapAnimationOutDuration?: WithDefault<Int32, 100>;
25
+ longPressDuration?: WithDefault<Int32, -1>;
26
+ longPressAnimationOutDuration?: WithDefault<Int32, -1>;
27
+ needsOffscreenAlphaCompositing?: WithDefault<boolean, false>;
24
28
  activeOpacity?: WithDefault<Float, 1>;
25
29
  activeScale?: WithDefault<Float, 1>;
26
30
  activeUnderlayOpacity?: WithDefault<Float, 0>;
@@ -54,17 +58,28 @@ interface NativeProps extends ViewProps {
54
58
  borderBlockEndColor?: ColorValue;
55
59
  borderBlockStartColor?: ColorValue;
56
60
 
57
- // Border radius — logical variants beyond what ViewProps provides
58
- // WithDefault -1 so the codegen sends -1 (our "unset" sentinel) instead of 0
59
- // when the prop is absent, letting physical / general radii take effect.
60
- borderTopStartRadius?: WithDefault<Float, -1>;
61
- borderTopEndRadius?: WithDefault<Float, -1>;
62
- borderBottomStartRadius?: WithDefault<Float, -1>;
63
- borderBottomEndRadius?: WithDefault<Float, -1>;
64
- borderEndEndRadius?: WithDefault<Float, -1>;
65
- borderEndStartRadius?: WithDefault<Float, -1>;
66
- borderStartEndRadius?: WithDefault<Float, -1>;
67
- borderStartStartRadius?: WithDefault<Float, -1>;
61
+ // Border radius — declared as UnsafeMixed (folly::dynamic on iOS,
62
+ // DynamicFromObject on Android) so codegen forwards the raw value
63
+ // without coercing to Float. This lets the Android view manager parse
64
+ // both numeric points and percentage strings via
65
+ // LengthPercentage.setFromDynamic, matching RN's standard View. The
66
+ // non-logical variants are declared explicitly so they're dispatched
67
+ // through our delegate instead of falling through to
68
+ // BaseViewManagerDelegate, which casts to Double and would crash on a
69
+ // string value.
70
+ borderRadius?: UnsafeMixed;
71
+ borderTopLeftRadius?: UnsafeMixed;
72
+ borderTopRightRadius?: UnsafeMixed;
73
+ borderBottomLeftRadius?: UnsafeMixed;
74
+ borderBottomRightRadius?: UnsafeMixed;
75
+ borderTopStartRadius?: UnsafeMixed;
76
+ borderTopEndRadius?: UnsafeMixed;
77
+ borderBottomStartRadius?: UnsafeMixed;
78
+ borderBottomEndRadius?: UnsafeMixed;
79
+ borderEndEndRadius?: UnsafeMixed;
80
+ borderEndStartRadius?: UnsafeMixed;
81
+ borderStartEndRadius?: UnsafeMixed;
82
+ borderStartStartRadius?: UnsafeMixed;
68
83
  }
69
84
 
70
85
  export default codegenNativeComponent<NativeProps>('RNGestureHandlerButton');
@@ -16,11 +16,13 @@ export const NativeProxy = {
16
16
  handlerTag: number,
17
17
  config?: T
18
18
  ) => {
19
- RNGestureHandlerModule.createGestureHandler(
20
- handlerName,
21
- handlerTag,
22
- config || {}
23
- );
19
+ scheduleOperationToBeFlushed(() => {
20
+ RNGestureHandlerModule.createGestureHandler(
21
+ handlerName,
22
+ handlerTag,
23
+ config || {}
24
+ );
25
+ });
24
26
  },
25
27
  setGestureHandlerConfig: <
26
28
  TConfig,
@@ -59,7 +61,7 @@ export const NativeProxy = {
59
61
  RNGestureHandlerModule.configureRelations(handlerTag, relations);
60
62
  });
61
63
  },
62
- setReanimatedAvailable: (isAvailable: boolean) => {
63
- RNGestureHandlerModule.setReanimatedAvailable(isAvailable);
64
+ installUIRuntimeBindings: () => {
65
+ return RNGestureHandlerModule.installUIRuntimeBindings();
64
66
  },
65
67
  } as const;
@@ -43,7 +43,7 @@ export const NativeProxy = {
43
43
  configureRelations: (handlerTag: number, relations: GestureRelations) => {
44
44
  RNGestureHandlerModule.configureRelations(handlerTag, relations);
45
45
  },
46
- setReanimatedAvailable: (isAvailable: boolean) => {
47
- RNGestureHandlerModule.setReanimatedAvailable(isAvailable);
46
+ installUIRuntimeBindings: () => {
47
+ return RNGestureHandlerModule.installUIRuntimeBindings();
48
48
  },
49
49
  } as const;
@@ -15,17 +15,25 @@ import type {
15
15
  type CallbackEventType = GestureEvent<NativeHandlerData>;
16
16
  type EndCallbackEventType = GestureEndEvent<NativeHandlerData>;
17
17
 
18
- /**
19
- * @deprecated `RawButton` is deprecated, use `Clickable` instead
20
- */
21
- export const RawButton = createNativeWrapper<
18
+ type RawButtonInnerProps = RawButtonProps & {
19
+ needsOffscreenAlphaCompositing?: boolean | undefined;
20
+ };
21
+
22
+ const RawButtonInner = createNativeWrapper<
22
23
  React.ComponentRef<typeof GestureHandlerButton>,
23
- RawButtonProps
24
+ RawButtonInnerProps
24
25
  >(GestureHandlerButton, {
25
26
  shouldCancelWhenOutside: false,
26
27
  shouldActivateOnStart: false,
27
28
  });
28
29
 
30
+ /**
31
+ * @deprecated `RawButton` is deprecated, use `Clickable` instead
32
+ */
33
+ export const RawButton = (props: RawButtonProps) => (
34
+ <RawButtonInner {...props} needsOffscreenAlphaCompositing />
35
+ );
36
+
29
37
  /**
30
38
  * @deprecated `BaseButton` is deprecated, use `Touchable` instead
31
39
  */
@@ -16,6 +16,7 @@ export interface RawButtonProps
16
16
  | 'activeOpacity'
17
17
  | 'activeScale'
18
18
  | 'activeUnderlayOpacity'
19
+ | 'needsOffscreenAlphaCompositing'
19
20
  >,
20
21
  Omit<
21
22
  NativeWrapperProperties<React.ComponentRef<typeof GestureHandlerButton>>,
@@ -5,6 +5,7 @@ import GestureHandlerButton from '../../../components/GestureHandlerButton';
5
5
  import { NativeDetector } from '../../detectors/NativeDetector';
6
6
  import { useNativeGesture } from '../../hooks';
7
7
  import type {
8
+ AnimationDuration,
8
9
  CallbackEventType,
9
10
  EndCallbackEventType,
10
11
  TouchableProps,
@@ -12,6 +13,8 @@ import type {
12
13
 
13
14
  const isAndroid = Platform.OS === 'android';
14
15
  const TRANSPARENT_RIPPLE = { rippleColor: 'transparent' as const };
16
+ const DEFAULT_IN_DURATION_MS = 50;
17
+ const DEFAULT_OUT_DURATION_MS = 100;
15
18
 
16
19
  enum PointerState {
17
20
  UNKNOWN,
@@ -19,11 +22,60 @@ enum PointerState {
19
22
  OUTSIDE,
20
23
  }
21
24
 
25
+ // Clamp user-supplied durations to finite, non-negative milliseconds.
26
+ // Negative, NaN, or Infinity values would produce invalid CSS transitions
27
+ // on web and negative setTimeout delays in branch 3 of the press-out path.
28
+ function sanitizeDuration(value: number): number {
29
+ return Number.isFinite(value) && value >= 0 ? value : 0;
30
+ }
31
+
32
+ function resolveAnimationDuration(value: AnimationDuration | undefined) {
33
+ if (value === undefined) {
34
+ return {
35
+ tapAnimationInDuration: DEFAULT_IN_DURATION_MS,
36
+ tapAnimationOutDuration: DEFAULT_OUT_DURATION_MS,
37
+ longPressAnimationOutDuration: DEFAULT_OUT_DURATION_MS,
38
+ hoverAnimationInDuration: DEFAULT_IN_DURATION_MS,
39
+ hoverAnimationOutDuration: DEFAULT_OUT_DURATION_MS,
40
+ };
41
+ }
42
+
43
+ if (typeof value === 'number') {
44
+ const sanitized = sanitizeDuration(value);
45
+ return {
46
+ tapAnimationInDuration: sanitized,
47
+ tapAnimationOutDuration: sanitized,
48
+ longPressAnimationOutDuration: sanitized,
49
+ hoverAnimationInDuration: sanitized,
50
+ hoverAnimationOutDuration: sanitized,
51
+ };
52
+ }
53
+
54
+ // The union guarantees variant 2 supplies top-level `in`/`out`, variant 3
55
+ // supplies both category objects — so per-category fallback to base is
56
+ // always defined for well-typed input; the 0 fallbacks here are unreachable.
57
+ const baseIn = 'in' in value ? value.in : 0;
58
+ const baseOut = 'out' in value ? value.out : 0;
59
+ const tapOut = value.tap?.out ?? baseOut;
60
+
61
+ return {
62
+ tapAnimationInDuration: sanitizeDuration(value.tap?.in ?? baseIn),
63
+ tapAnimationOutDuration: sanitizeDuration(tapOut),
64
+ longPressAnimationOutDuration: sanitizeDuration(
65
+ value.longPress?.out ?? tapOut
66
+ ),
67
+ hoverAnimationInDuration: sanitizeDuration(value.hover?.in ?? baseIn),
68
+ hoverAnimationOutDuration: sanitizeDuration(value.hover?.out ?? baseOut),
69
+ };
70
+ }
71
+
22
72
  export const Touchable = (props: TouchableProps) => {
23
73
  const {
24
- underlayColor = 'black',
74
+ underlayColor = 'transparent',
25
75
  defaultUnderlayOpacity = 0,
76
+ activeUnderlayOpacity = 0.105,
26
77
  defaultOpacity = 1,
78
+ animationDuration,
27
79
  androidRipple,
28
80
  delayLongPress = 600,
29
81
  onLongPress,
@@ -37,6 +89,9 @@ export const Touchable = (props: TouchableProps) => {
37
89
  ...rest
38
90
  } = props;
39
91
 
92
+ const resolvedDurations = resolveAnimationDuration(animationDuration);
93
+ const resolvedDelayLongPress = sanitizeDuration(delayLongPress);
94
+
40
95
  const shouldUseNativeRipple = isAndroid && androidRipple !== undefined;
41
96
 
42
97
  const pointerState = useRef<PointerState>(PointerState.UNKNOWN);
@@ -54,9 +109,12 @@ export const Touchable = (props: TouchableProps) => {
54
109
  longPressDetected.current = false;
55
110
 
56
111
  if (onLongPress && !longPressTimeout.current) {
57
- longPressTimeout.current = setTimeout(wrappedLongPress, delayLongPress);
112
+ longPressTimeout.current = setTimeout(
113
+ wrappedLongPress,
114
+ resolvedDelayLongPress
115
+ );
58
116
  }
59
- }, [onLongPress, delayLongPress, wrappedLongPress]);
117
+ }, [onLongPress, resolvedDelayLongPress, wrappedLongPress]);
60
118
 
61
119
  const onBegin = useCallback(
62
120
  (e: CallbackEventType) => {
@@ -155,11 +213,14 @@ export const Touchable = (props: TouchableProps) => {
155
213
  <GestureHandlerButton
156
214
  {...rest}
157
215
  {...rippleProps}
216
+ {...resolvedDurations}
158
217
  ref={ref ?? null}
159
218
  enabled={!disabled}
160
219
  defaultOpacity={defaultOpacity}
161
220
  defaultUnderlayOpacity={defaultUnderlayOpacity}
162
- underlayColor={underlayColor}>
221
+ activeUnderlayOpacity={activeUnderlayOpacity}
222
+ underlayColor={underlayColor}
223
+ longPressDuration={resolvedDelayLongPress}>
163
224
  {children}
164
225
  </GestureHandlerButton>
165
226
  </NativeDetector>
@@ -17,11 +17,59 @@ type PressableAndroidRippleConfig = {
17
17
 
18
18
  type RippleProps = 'rippleColor' | 'rippleRadius' | 'borderless' | 'foreground';
19
19
 
20
- export type TouchableProps = Omit<ButtonProps, RippleProps | 'enabled'> &
20
+ type DurationProps =
21
+ | 'tapAnimationInDuration'
22
+ | 'tapAnimationOutDuration'
23
+ | 'longPressDuration'
24
+ | 'longPressAnimationOutDuration'
25
+ | 'hoverAnimationInDuration'
26
+ | 'hoverAnimationOutDuration';
27
+
28
+ type InOutDuration = { in: number; out: number };
29
+ type LongPressDuration = { out: number };
30
+
31
+ /**
32
+ * Configuration for press / hover animation timing.
33
+ *
34
+ * - A single number applies to every phase of every category.
35
+ * - An object with top-level `in` / `out` sets the baseline; `tap` and
36
+ * `hover` may override either side or both — any field left out
37
+ * inherits the top-level value.
38
+ * - Alternatively, both categories may be specified in full without a
39
+ * top-level baseline.
40
+ *
41
+ * `longPress` optionally customizes the press-out duration once the
42
+ * press has been held past `delayLongPress`. If omitted, the long-press
43
+ * release falls back to the resolved tap-out timing.
44
+ */
45
+ export type AnimationDuration =
46
+ | number
47
+ | (InOutDuration & {
48
+ tap?: Partial<InOutDuration>;
49
+ hover?: Partial<InOutDuration>;
50
+ longPress?: LongPressDuration;
51
+ })
52
+ | {
53
+ tap: InOutDuration;
54
+ hover: InOutDuration;
55
+ longPress?: LongPressDuration;
56
+ };
57
+
58
+ export type TouchableProps = Omit<
59
+ ButtonProps,
60
+ RippleProps | 'enabled' | DurationProps
61
+ > &
21
62
  Omit<
22
63
  BaseButtonProps,
23
64
  keyof RawButtonProps | 'onActiveStateChange' | 'onPress'
24
65
  > & {
66
+ /**
67
+ * Press and hover animation durations, in milliseconds. Pass a single
68
+ * number to apply it to every phase, or an object to customize per phase
69
+ * and per category. Defaults to 50ms for the in phase and 100ms for the
70
+ * out phase.
71
+ */
72
+ animationDuration?: AnimationDuration | undefined;
25
73
  /**
26
74
  * Configuration for the ripple effect on Android.
27
75
  */