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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gesture-handler",
3
- "version": "3.0.0-beta.4",
3
+ "version": "3.0.0-beta.5",
4
4
  "description": "Declarative API exposing native platform touch and gesture system to React Native",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -77,7 +77,7 @@
77
77
  "@react-native/jest-preset": "0.85.0",
78
78
  "@testing-library/react-native": "^12.5.1",
79
79
  "@types/invariant": "^2.2.37",
80
- "@types/jest": "^27.0.3",
80
+ "@types/jest": "^29.5.12",
81
81
  "@types/react": "^19.2.0",
82
82
  "@typescript-eslint/eslint-plugin": "^6.9.0",
83
83
  "@typescript-eslint/parser": "^6.9.0",
@@ -90,7 +90,7 @@
90
90
  "eslint-plugin-prettier": "^5.0.1",
91
91
  "eslint-plugin-react": "^7.37.5",
92
92
  "husky": "^8.0.1",
93
- "jest": "^28.1.0",
93
+ "jest": "^29.7.0",
94
94
  "lint-staged": "^12.3.2",
95
95
  "madge": "^6.1.0",
96
96
  "prettier": "3.3.3",
@@ -82,6 +82,9 @@ export default {
82
82
  NodeManager.dropGestureHandler(handlerTag);
83
83
  },
84
84
  configureRelations(handlerTag: number, relations: GestureRelations) {
85
+ if (!NodeManager.hasHandler(handlerTag)) {
86
+ return;
87
+ }
85
88
  InteractionManager.instance.configureInteractions(
86
89
  NodeManager.getHandler(handlerTag),
87
90
  relations
@@ -89,7 +92,8 @@ export default {
89
92
  },
90
93
  // eslint-disable-next-line @typescript-eslint/no-empty-function
91
94
  flushOperations() {},
92
- setReanimatedAvailable(_isAvailable: boolean) {
95
+ installUIRuntimeBindings() {
93
96
  // No-op on web
97
+ return true;
94
98
  },
95
99
  };
@@ -18,19 +18,30 @@ import type {
18
18
  LegacyRectButtonProps,
19
19
  RectButtonWithRefProps,
20
20
  } from './GestureButtonsProps';
21
- import GestureHandlerButton from './GestureHandlerButton';
21
+ import GestureHandlerButton, { type ButtonProps } from './GestureHandlerButton';
22
22
 
23
- /**
24
- * @deprecated use `RawButton` instead
25
- */
26
- export const LegacyRawButton = createNativeWrapper<LegacyRawButtonProps>(
27
- GestureHandlerButton as unknown as HostComponent<LegacyRawButtonProps>,
23
+ type LegacyRawButtonInnerProps = LegacyRawButtonProps & {
24
+ needsOffscreenAlphaCompositing?: boolean;
25
+ };
26
+
27
+ const LegacyRawButtonInner = createNativeWrapper<LegacyRawButtonInnerProps>(
28
+ GestureHandlerButton as unknown as HostComponent<LegacyRawButtonInnerProps>,
28
29
  {
29
30
  shouldCancelWhenOutside: false,
30
31
  shouldActivateOnStart: Platform.OS === 'web',
31
32
  }
32
33
  );
33
34
 
35
+ /**
36
+ * @deprecated use `RawButton` instead
37
+ */
38
+ export const LegacyRawButton = (
39
+ props: Omit<
40
+ React.ComponentProps<typeof LegacyRawButtonInner>,
41
+ 'needsOffscreenAlphaCompositing'
42
+ >
43
+ ) => <LegacyRawButtonInner {...props} needsOffscreenAlphaCompositing />;
44
+
34
45
  class InnerBaseButton extends React.Component<BaseButtonWithRefProps> {
35
46
  static defaultProps = {
36
47
  delayLongPress: 600,
@@ -280,4 +291,9 @@ export const LegacyBorderlessButton = ({
280
291
  ref?: React.Ref<React.ComponentType<any>> | undefined;
281
292
  }) => <InnerBorderlessButton innerRef={ref} {...props} />;
282
293
 
283
- export { default as LegacyPureNativeButton } from './GestureHandlerButton';
294
+ /**
295
+ * @deprecated use `PureNativeButton` instead
296
+ */
297
+ export const LegacyPureNativeButton = (
298
+ props: Omit<ButtonProps, 'needsOffscreenAlphaCompositing'>
299
+ ) => <GestureHandlerButton {...props} needsOffscreenAlphaCompositing />;
@@ -60,18 +60,31 @@ export interface ButtonProps extends ViewProps, AccessibilityProps {
60
60
  touchSoundDisabled?: boolean | undefined;
61
61
 
62
62
  /**
63
- * Duration of the press-in animation when the button is held down, in
64
- * milliseconds. Defaults to `tapAnimationDuration` when not set (or set
65
- * to any negative value).
63
+ * Minimum duration (in milliseconds) of the press-in animation on a
64
+ * quick tap. Defaults to 50ms.
66
65
  */
67
- pressAndHoldAnimationDuration?: number | undefined;
66
+ tapAnimationInDuration?: number | undefined;
68
67
 
69
68
  /**
70
- * Minimum duration (in milliseconds) that the press animation must run
71
- * before the press-out animation is allowed to start. Ensures the pressed
72
- * state is visible on quick taps. Defaults to 100ms.
69
+ * Minimum duration (in milliseconds) of the press-out animation on a
70
+ * quick tap. Defaults to 100ms.
73
71
  */
74
- tapAnimationDuration?: number | undefined;
72
+ tapAnimationOutDuration?: number | undefined;
73
+
74
+ /**
75
+ * Threshold (in milliseconds) at which the press-out animation
76
+ * switches from the tap-out timing to `longPressAnimationOutDuration`.
77
+ * Set to any negative value to disable the switch.
78
+ */
79
+ longPressDuration?: number | undefined;
80
+
81
+ /**
82
+ * Duration of the press-out animation, in milliseconds, when the
83
+ * button is released after being held past `longPressDuration`.
84
+ * Defaults to `tapAnimationOutDuration` when not set (or set to any
85
+ * negative value).
86
+ */
87
+ longPressAnimationOutDuration?: number | undefined;
75
88
 
76
89
  /**
77
90
  * Opacity applied to the button when it is pressed.
@@ -88,6 +101,44 @@ export interface ButtonProps extends ViewProps, AccessibilityProps {
88
101
  */
89
102
  activeUnderlayOpacity?: number | undefined;
90
103
 
104
+ /**
105
+ * Web only.
106
+ *
107
+ * Opacity applied to the button when it is hovered. Defaults to
108
+ * `defaultOpacity` when not set.
109
+ */
110
+ hoverOpacity?: number | undefined;
111
+
112
+ /**
113
+ * Web only.
114
+ *
115
+ * Scale applied to the button when it is hovered. Defaults to
116
+ * `defaultScale` when not set.
117
+ */
118
+ hoverScale?: number | undefined;
119
+
120
+ /**
121
+ * Web only.
122
+ *
123
+ * Opacity applied to the underlay when the button is hovered. Defaults
124
+ * to `defaultUnderlayOpacity` when not set.
125
+ */
126
+ hoverUnderlayOpacity?: number | undefined;
127
+
128
+ /**
129
+ * Web only.
130
+ *
131
+ * Duration of the hover-in animation, in milliseconds. Defaults to 50ms.
132
+ */
133
+ hoverAnimationInDuration?: number | undefined;
134
+
135
+ /**
136
+ * Web only.
137
+ *
138
+ * Duration of the hover-out animation, in milliseconds. Defaults to 100ms.
139
+ */
140
+ hoverAnimationOutDuration?: number | undefined;
141
+
91
142
  /**
92
143
  * Opacity applied to the button when it is not pressed.
93
144
  */
@@ -108,6 +159,17 @@ export interface ButtonProps extends ViewProps, AccessibilityProps {
108
159
  */
109
160
  underlayColor?: ColorValue | undefined;
110
161
 
162
+ /**
163
+ * Android only.
164
+ *
165
+ * Whether the view should render with an offscreen alpha-compositing buffer
166
+ * when its `opacity` is less than 1. Defaults to `false` — without the
167
+ * offscreen buffer, children that draw past the view's bounds (e.g. anti-
168
+ * aliased border edges) are not clipped by the layer when `opacity < 1`.
169
+ * Set to `true` to opt back into the standard Android behavior.
170
+ */
171
+ needsOffscreenAlphaCompositing?: boolean | undefined;
172
+
111
173
  /**
112
174
  * Style object, use it to set additional styles.
113
175
  */
@@ -8,11 +8,18 @@ import { GestureLifecycleEvent } from '../web/tools/GestureLifecycleEvents';
8
8
  type ButtonProps = ViewProps & {
9
9
  ref?: React.Ref<React.ComponentRef<typeof View>>;
10
10
  enabled?: boolean;
11
- pressAndHoldAnimationDuration?: number;
12
- tapAnimationDuration?: number;
11
+ tapAnimationInDuration?: number;
12
+ tapAnimationOutDuration?: number;
13
+ longPressDuration?: number;
14
+ longPressAnimationOutDuration?: number;
15
+ hoverAnimationInDuration?: number;
16
+ hoverAnimationOutDuration?: number;
13
17
  activeOpacity?: number;
14
18
  activeScale?: number;
15
19
  activeUnderlayOpacity?: number;
20
+ hoverOpacity?: number;
21
+ hoverScale?: number;
22
+ hoverUnderlayOpacity?: number;
16
23
  defaultOpacity?: number;
17
24
  defaultScale?: number;
18
25
  defaultUnderlayOpacity?: number;
@@ -22,11 +29,18 @@ type ButtonProps = ViewProps & {
22
29
  export const ButtonComponent = ({
23
30
  ref: externalRef,
24
31
  enabled = true,
25
- pressAndHoldAnimationDuration: pressAndHoldAnimationDurationProp = -1,
26
- tapAnimationDuration: tapAnimationDurationProp = 100,
32
+ tapAnimationInDuration = 50,
33
+ tapAnimationOutDuration = 100,
34
+ longPressDuration = -1,
35
+ longPressAnimationOutDuration = 100,
36
+ hoverAnimationInDuration = 50,
37
+ hoverAnimationOutDuration = 100,
27
38
  activeOpacity = 1,
28
39
  activeScale = 1,
29
40
  activeUnderlayOpacity = 0,
41
+ hoverOpacity: hoverOpacityProp,
42
+ hoverScale: hoverScaleProp,
43
+ hoverUnderlayOpacity: hoverUnderlayOpacityProp,
30
44
  defaultOpacity = 1,
31
45
  defaultScale = 1,
32
46
  defaultUnderlayOpacity = 0,
@@ -35,16 +49,15 @@ export const ButtonComponent = ({
35
49
  children,
36
50
  ...rest
37
51
  }: ButtonProps) => {
38
- const tapAnimationDuration =
39
- tapAnimationDurationProp < 0 ? 0 : tapAnimationDurationProp;
40
- const pressAndHoldAnimationDuration =
41
- pressAndHoldAnimationDurationProp < 0
42
- ? tapAnimationDuration
43
- : pressAndHoldAnimationDurationProp;
52
+ const hoverOpacity = hoverOpacityProp ?? defaultOpacity;
53
+ const hoverScale = hoverScaleProp ?? defaultScale;
54
+ const hoverUnderlayOpacity =
55
+ hoverUnderlayOpacityProp ?? defaultUnderlayOpacity;
44
56
 
45
57
  const [pressed, setPressed] = React.useState(false);
58
+ const [hovered, setHovered] = React.useState(false);
46
59
  const [currentDuration, setCurrentDuration] = React.useState(
47
- pressAndHoldAnimationDuration
60
+ tapAnimationInDuration
48
61
  );
49
62
  const pressInTimestamp = React.useRef(0);
50
63
  const pressOutTimer = React.useRef<ReturnType<typeof setTimeout> | null>(
@@ -114,10 +127,10 @@ export const ButtonComponent = ({
114
127
  pressOutTimer.current = null;
115
128
  }
116
129
  pressInTimestamp.current = performance.now();
117
- setCurrentDuration(pressAndHoldAnimationDuration);
130
+ setCurrentDuration(tapAnimationInDuration);
118
131
  setPressed(true);
119
132
  },
120
- [enabled, pressAndHoldAnimationDuration]
133
+ [enabled, tapAnimationInDuration]
121
134
  );
122
135
 
123
136
  const pressOut = React.useCallback(
@@ -137,34 +150,88 @@ export const ButtonComponent = ({
137
150
  const elapsed = performance.now() - pressInTimestamp.current;
138
151
  pressInTimestamp.current = 0;
139
152
 
140
- if (elapsed >= pressAndHoldAnimationDuration) {
141
- setCurrentDuration(pressAndHoldAnimationDuration);
153
+ if (longPressDuration >= 0 && elapsed >= longPressDuration) {
154
+ // Long-press release — use the configured long-press out duration.
155
+ setCurrentDuration(longPressAnimationOutDuration);
142
156
  setPressed(false);
143
- // elapsed * 2 to ensure there is at least half of the tapAnimationDuration left for the animation to play
144
- } else if (elapsed * 2 >= tapAnimationDuration) {
157
+ } else if (elapsed >= tapAnimationInDuration) {
158
+ // Press-in animation fully finished - release with the configured out duration.
159
+ setCurrentDuration(tapAnimationOutDuration);
160
+ setPressed(false);
161
+ // elapsed * 2 to ensure there is at least half of the tapAnimationOutDuration left for the animation to play
162
+ } else if (elapsed * 2 >= tapAnimationOutDuration) {
145
163
  setCurrentDuration(elapsed);
146
164
  setPressed(false);
147
165
  } else {
148
- // Let the in-progress CSS press-in transition continue; schedule press-out after remaining time
149
- const remaining = tapAnimationDuration - elapsed;
166
+ // Let the in-progress CSS press-in transition continue; schedule press-out after remaining time.
167
+ const remaining = tapAnimationInDuration - elapsed;
150
168
  pressOutTimer.current = setTimeout(() => {
151
169
  pressOutTimer.current = null;
152
- setCurrentDuration(tapAnimationDuration);
170
+ setCurrentDuration(tapAnimationOutDuration);
153
171
  setPressed(false);
154
172
  }, remaining);
155
173
  }
156
174
  },
157
- [pressAndHoldAnimationDuration, tapAnimationDuration]
175
+ [
176
+ longPressDuration,
177
+ longPressAnimationOutDuration,
178
+ tapAnimationInDuration,
179
+ tapAnimationOutDuration,
180
+ ]
181
+ );
182
+
183
+ const handlePointerEnter = React.useCallback(
184
+ (event: NativeSyntheticEvent<{ pointerType?: string }>) => {
185
+ if (!enabled || event.nativeEvent.pointerType === 'touch') {
186
+ return;
187
+ }
188
+ // Skip duration update while pressed so the press transition owns it.
189
+ if (!pressed) {
190
+ setCurrentDuration(hoverAnimationInDuration);
191
+ }
192
+ setHovered(true);
193
+ },
194
+ [enabled, pressed, hoverAnimationInDuration]
158
195
  );
159
196
 
197
+ const handlePointerLeave = React.useCallback(
198
+ (event: NativeSyntheticEvent<{ pointerType?: string }>) => {
199
+ pressOut(event);
200
+ if (event.nativeEvent.pointerType === 'touch') {
201
+ return;
202
+ }
203
+ if (!pressed) {
204
+ setCurrentDuration(hoverAnimationOutDuration);
205
+ }
206
+ setHovered(false);
207
+ },
208
+ [pressOut, pressed, hoverAnimationOutDuration]
209
+ );
210
+
211
+ // Mask hover at render rather than clearing the state. Avoids a state
212
+ // write inside an effect, and lets hover resume naturally when `enabled`
213
+ // flips back to true while the pointer is still inside.
214
+ const effectiveHovered = hovered && enabled;
215
+
160
216
  const currentUnderlayOpacity = pressed
161
217
  ? activeUnderlayOpacity
162
- : defaultUnderlayOpacity;
163
- const hasUnderlay = underlayColor != null;
164
- const hasOpacity = activeOpacity !== 1 || defaultOpacity !== 1;
165
- const currentOpacity = pressed ? activeOpacity : defaultOpacity;
166
- const hasScale = activeScale !== 1 || defaultScale !== 1;
167
- const currentScale = pressed ? activeScale : defaultScale;
218
+ : effectiveHovered
219
+ ? hoverUnderlayOpacity
220
+ : defaultUnderlayOpacity;
221
+ const hasUnderlay = underlayColor != null && underlayColor !== 'transparent';
222
+ const hasOpacity =
223
+ activeOpacity !== 1 || hoverOpacity !== 1 || defaultOpacity !== 1;
224
+ const currentOpacity = pressed
225
+ ? activeOpacity
226
+ : effectiveHovered
227
+ ? hoverOpacity
228
+ : defaultOpacity;
229
+ const hasScale = activeScale !== 1 || hoverScale !== 1 || defaultScale !== 1;
230
+ const currentScale = pressed
231
+ ? activeScale
232
+ : effectiveHovered
233
+ ? hoverScale
234
+ : defaultScale;
168
235
 
169
236
  const easing = 'cubic-bezier(0.5, 1, 0.89, 1)';
170
237
  const transitionProps: string[] = [];
@@ -178,6 +245,7 @@ export const ButtonComponent = ({
178
245
 
179
246
  return (
180
247
  <View
248
+ {...rest}
181
249
  ref={setRef}
182
250
  accessibilityRole="button"
183
251
  style={[
@@ -191,11 +259,11 @@ export const ButtonComponent = ({
191
259
  ...(hasUnderlay && { overflow: 'hidden' }),
192
260
  },
193
261
  ]}
262
+ onPointerEnter={handlePointerEnter}
194
263
  onPointerDown={pressIn}
195
264
  onPointerUp={pressOut}
196
265
  onPointerCancel={pressOut}
197
- onPointerLeave={pressOut}
198
- {...rest}>
266
+ onPointerLeave={handlePointerLeave}>
199
267
  {hasUnderlay && (
200
268
  <View
201
269
  style={{
@@ -377,6 +377,7 @@ const LegacyPressable = (props: LegacyPressableProps) => {
377
377
  <GestureDetector gesture={gesture}>
378
378
  <NativeButton
379
379
  {...remainingProps}
380
+ needsOffscreenAlphaCompositing
380
381
  onLayout={setDimensions}
381
382
  accessible={accessible !== false}
382
383
  hitSlop={appliedHitSlop}
@@ -32,7 +32,8 @@ export type InnerPressableEvent = {
32
32
 
33
33
  export type PressableEvent = { nativeEvent: InnerPressableEvent };
34
34
 
35
- export interface LegacyPressableProps extends CommonPressableProps {
35
+ export interface LegacyPressableProps
36
+ extends Omit<CommonPressableProps, 'needsOffscreenAlphaCompositing'> {
36
37
  /**
37
38
  * A gesture object or an array of gesture objects containing the configuration and callbacks to be
38
39
  * used with the Pressable's gesture handlers.
@@ -44,6 +44,7 @@ import { MouseButton } from '../handlers/gestureHandlerCommon';
44
44
  import { GestureDetector } from '../v3/detectors';
45
45
  import type { PanGestureActiveEvent } from '../v3/hooks/gestures';
46
46
  import { usePanGesture, useTapGesture } from '../v3/hooks/gestures';
47
+ import type { WithSharedValue } from '../v3/types';
47
48
 
48
49
  const DRAG_TOSS = 0.05;
49
50
 
@@ -75,7 +76,7 @@ export enum DrawerKeyboardDismissMode {
75
76
  ON_DRAG,
76
77
  }
77
78
 
78
- export interface DrawerLayoutProps {
79
+ export type DrawerLayoutProps = {
79
80
  /**
80
81
  * This attribute is present in the native android implementation already and is one
81
82
  * of the required params. The gesture handler version of DrawerLayout makes it
@@ -193,14 +194,6 @@ export interface DrawerLayoutProps {
193
194
  */
194
195
  drawerContainerStyle?: StyleProp<ViewStyle>;
195
196
 
196
- /**
197
- * Enables two-finger gestures on supported devices, for example iPads with
198
- * trackpads. If not enabled the gesture will require click + drag, with
199
- * `enableTrackpadTwoFingerGesture` swiping with two fingers will also trigger
200
- * the gesture.
201
- */
202
- enableTrackpadTwoFingerGesture?: boolean;
203
-
204
197
  onDrawerSlide?: (position: number) => void;
205
198
 
206
199
  // Implicit `children` prop has been removed in @types/react^18.0.
@@ -216,25 +209,36 @@ export interface DrawerLayoutProps {
216
209
  */
217
210
  userSelect?: UserSelect;
218
211
 
219
- /**
220
- * @default 'auto'
221
- * Sets the displayed cursor pictogram when the drawer is being dragged.
222
- * Values: see CSS cursor values
223
- */
224
- activeCursor?: ActiveCursor;
225
-
226
- /**
227
- * @default 'MouseButton.LEFT'
228
- * Allows to choose which mouse button should underlying pan handler react to.
229
- */
230
- mouseButton?: MouseButton;
231
-
232
212
  /**
233
213
  * @default 'false if MouseButton.RIGHT is specified'
234
214
  * Allows to enable/disable context menu.
235
215
  */
236
216
  enableContextMenu?: boolean;
237
- }
217
+ } & WithSharedValue<
218
+ {
219
+ /**
220
+ * Enables two-finger gestures on supported devices, for example iPads with
221
+ * trackpads. If not enabled the gesture will require click + drag, with
222
+ * `enableTrackpadTwoFingerGesture` swiping with two fingers will also trigger
223
+ * the gesture.
224
+ */
225
+ enableTrackpadTwoFingerGesture?: boolean;
226
+
227
+ /**
228
+ * @default 'auto'
229
+ * Sets the displayed cursor pictogram when the drawer is being dragged.
230
+ * Values: see CSS cursor values
231
+ */
232
+ activeCursor?: ActiveCursor;
233
+
234
+ /**
235
+ * @default 'MouseButton.LEFT'
236
+ * Allows to choose which mouse button should underlying pan handler react to.
237
+ */
238
+ mouseButton?: MouseButton;
239
+ },
240
+ ActiveCursor | MouseButton
241
+ >;
238
242
 
239
243
  export type DrawerMovementOption = {
240
244
  initialVelocity?: number;
@@ -1,5 +1,10 @@
1
1
  import type { ForwardedRef } from 'react';
2
- import { useCallback, useImperativeHandle, useMemo } from 'react';
2
+ import React, {
3
+ useCallback,
4
+ useEffect,
5
+ useImperativeHandle,
6
+ useMemo,
7
+ } from 'react';
3
8
  import type { LayoutChangeEvent } from 'react-native';
4
9
  import { I18nManager, StyleSheet, View } from 'react-native';
5
10
  import Animated, {
@@ -14,9 +19,16 @@ import Animated, {
14
19
  withSpring,
15
20
  } from 'react-native-reanimated';
16
21
 
22
+ import { Reanimated } from '../../handlers/gestures/reanimatedWrapper';
23
+ import { tagMessage } from '../../utils';
17
24
  import { GestureDetector } from '../../v3/detectors';
18
25
  import type { PanGestureActiveEvent } from '../../v3/hooks/gestures';
19
26
  import { usePanGesture, useTapGesture } from '../../v3/hooks/gestures';
27
+ import {
28
+ maybeUnpackValue,
29
+ SHARED_VALUE_OFFSET,
30
+ } from '../../v3/hooks/utils/reanimatedUtils';
31
+ import type { SharedValueOrT } from '../../v3/types';
20
32
  import type {
21
33
  SwipeableMethods,
22
34
  SwipeableProps,
@@ -44,8 +56,8 @@ const Swipeable = (props: SwipeableProps) => {
44
56
  testID,
45
57
  children,
46
58
  enableTrackpadTwoFingerGesture = DEFAULT_ENABLE_TRACKING_TWO_FINGER_GESTURE,
47
- dragOffsetFromLeftEdge = DEFAULT_DRAG_OFFSET,
48
- dragOffsetFromRightEdge = DEFAULT_DRAG_OFFSET,
59
+ dragOffsetFromLeft = DEFAULT_DRAG_OFFSET,
60
+ dragOffsetFromRight = -DEFAULT_DRAG_OFFSET,
49
61
  friction = DEFAULT_FRICTION,
50
62
  overshootFriction = DEFAULT_OVERSHOOT_FRICTION,
51
63
  onSwipeableOpenStartDrag,
@@ -63,6 +75,40 @@ const Swipeable = (props: SwipeableProps) => {
63
75
  ...remainingProps
64
76
  } = props;
65
77
 
78
+ if (__DEV__) {
79
+ const checkValue = (value: SharedValueOrT<number>) => {
80
+ 'worklet';
81
+ if (maybeUnpackValue<number>(value) > 0) {
82
+ throw new Error(
83
+ tagMessage('dragOffsetFromRight should be non-positive.')
84
+ );
85
+ }
86
+ };
87
+
88
+ checkValue(dragOffsetFromRight);
89
+
90
+ // eslint-disable-next-line react-hooks/rules-of-hooks
91
+ useEffect(() => {
92
+ if (!Reanimated?.isSharedValue<number>(dragOffsetFromRight)) {
93
+ return;
94
+ }
95
+
96
+ const listenerId = Math.random() + SHARED_VALUE_OFFSET;
97
+
98
+ Reanimated?.runOnUI(() => {
99
+ 'worklet';
100
+ dragOffsetFromRight.addListener(listenerId, checkValue);
101
+ })();
102
+
103
+ return () => {
104
+ Reanimated?.runOnUI(() => {
105
+ 'worklet';
106
+ dragOffsetFromRight.removeListener(listenerId);
107
+ })();
108
+ };
109
+ }, [dragOffsetFromRight, checkValue]);
110
+ }
111
+
66
112
  const shouldEnableTap = useSharedValue<boolean>(false);
67
113
  const rowState = useSharedValue<number>(0);
68
114
 
@@ -468,9 +514,9 @@ const Swipeable = (props: SwipeableProps) => {
468
514
  });
469
515
 
470
516
  const panGesture = usePanGesture({
471
- enabled: enabled !== false,
517
+ enabled: enabled ?? true,
472
518
  enableTrackpadTwoFingerGesture: enableTrackpadTwoFingerGesture,
473
- activeOffsetX: [-dragOffsetFromRightEdge, dragOffsetFromLeftEdge],
519
+ activeOffsetX: [dragOffsetFromRight, dragOffsetFromLeft],
474
520
  simultaneousWith,
475
521
  requireToFail,
476
522
  block,