react-native-gesture-handler 2.23.1 → 2.24.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.
Files changed (73) hide show
  1. package/android/build.gradle +9 -3
  2. package/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerInterface.java +2 -1
  3. package/android/paper/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerRootViewManagerInterface.java +2 -1
  4. package/android/paper77/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerDelegate.java +60 -0
  5. package/android/paper77/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerButtonManagerInterface.java +26 -0
  6. package/android/paper77/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerRootViewManagerDelegate.java +26 -0
  7. package/android/paper77/src/main/java/com/facebook/react/viewmanagers/RNGestureHandlerRootViewManagerInterface.java +16 -0
  8. package/android/paper77/src/main/java/com/swmansion/gesturehandler/NativeRNGestureHandlerModuleSpec.java +67 -0
  9. package/android/paper77/src/main/java/com/swmansion/gesturehandler/ReactContextExtensions.kt +13 -0
  10. package/lib/commonjs/components/GestureComponents.js.map +1 -1
  11. package/lib/commonjs/components/ReanimatedDrawerLayout.js +10 -6
  12. package/lib/commonjs/components/ReanimatedDrawerLayout.js.map +1 -1
  13. package/lib/commonjs/components/Text.js +5 -1
  14. package/lib/commonjs/components/Text.js.map +1 -1
  15. package/lib/commonjs/components/touchables/ExtraButtonProps.js +2 -0
  16. package/lib/commonjs/components/touchables/ExtraButtonProps.js.map +1 -0
  17. package/lib/commonjs/components/touchables/GenericTouchable.js.map +1 -1
  18. package/lib/commonjs/components/touchables/TouchableWithoutFeedback.js +12 -4
  19. package/lib/commonjs/components/touchables/TouchableWithoutFeedback.js.map +1 -1
  20. package/lib/commonjs/handlers/createHandler.js +13 -10
  21. package/lib/commonjs/handlers/createHandler.js.map +1 -1
  22. package/lib/commonjs/handlers/createNativeWrapper.js +5 -3
  23. package/lib/commonjs/handlers/createNativeWrapper.js.map +1 -1
  24. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js +3 -5
  25. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -1
  26. package/lib/commonjs/utils.js +9 -0
  27. package/lib/commonjs/utils.js.map +1 -1
  28. package/lib/commonjs/web/utils.js +23 -3
  29. package/lib/commonjs/web/utils.js.map +1 -1
  30. package/lib/module/components/GestureComponents.js.map +1 -1
  31. package/lib/module/components/ReanimatedDrawerLayout.js +10 -6
  32. package/lib/module/components/ReanimatedDrawerLayout.js.map +1 -1
  33. package/lib/module/components/Text.js +5 -1
  34. package/lib/module/components/Text.js.map +1 -1
  35. package/lib/module/components/touchables/ExtraButtonProps.js +2 -0
  36. package/lib/module/components/touchables/ExtraButtonProps.js.map +1 -0
  37. package/lib/module/components/touchables/GenericTouchable.js.map +1 -1
  38. package/lib/module/components/touchables/TouchableWithoutFeedback.js +12 -4
  39. package/lib/module/components/touchables/TouchableWithoutFeedback.js.map +1 -1
  40. package/lib/module/handlers/createHandler.js +14 -11
  41. package/lib/module/handlers/createHandler.js.map +1 -1
  42. package/lib/module/handlers/createNativeWrapper.js +5 -3
  43. package/lib/module/handlers/createNativeWrapper.js.map +1 -1
  44. package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js +2 -4
  45. package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -1
  46. package/lib/module/utils.js +4 -0
  47. package/lib/module/utils.js.map +1 -1
  48. package/lib/module/web/utils.js +18 -2
  49. package/lib/module/web/utils.js.map +1 -1
  50. package/lib/typescript/components/GestureComponents.d.ts +1 -1
  51. package/lib/typescript/components/ReanimatedDrawerLayout.d.ts +5 -0
  52. package/lib/typescript/components/touchables/ExtraButtonProps.d.ts +7 -0
  53. package/lib/typescript/components/touchables/GenericTouchable.d.ts +0 -2
  54. package/lib/typescript/components/touchables/GenericTouchableProps.d.ts +2 -0
  55. package/lib/typescript/components/touchables/TouchableNativeFeedback.android.d.ts +2 -2
  56. package/lib/typescript/components/touchables/TouchableNativeFeedbackProps.d.ts +2 -6
  57. package/lib/typescript/handlers/gestures/GestureDetector/utils.d.ts +1 -1
  58. package/lib/typescript/utils.d.ts +1 -0
  59. package/lib/typescript/web/utils.d.ts +2 -0
  60. package/package.json +9 -8
  61. package/src/components/GestureComponents.tsx +1 -1
  62. package/src/components/ReanimatedDrawerLayout.tsx +17 -4
  63. package/src/components/Text.tsx +5 -0
  64. package/src/components/touchables/ExtraButtonProps.ts +7 -0
  65. package/src/components/touchables/GenericTouchable.tsx +0 -2
  66. package/src/components/touchables/GenericTouchableProps.ts +2 -0
  67. package/src/components/touchables/TouchableNativeFeedbackProps.tsx +2 -7
  68. package/src/components/touchables/TouchableWithoutFeedback.tsx +20 -2
  69. package/src/handlers/createHandler.tsx +20 -9
  70. package/src/handlers/createNativeWrapper.tsx +7 -3
  71. package/src/handlers/gestures/GestureDetector/Wrap.web.tsx +2 -4
  72. package/src/utils.ts +6 -0
  73. package/src/web/utils.ts +21 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gesture-handler",
3
- "version": "2.23.1",
3
+ "version": "2.24.0",
4
4
  "description": "Declarative API exposing native platform touch and gesture system to React Native",
5
5
  "scripts": {
6
6
  "prepare": "bob build && husky install",
@@ -36,6 +36,7 @@
36
36
  "android/src/main/jni/",
37
37
  "android/fabric/src/main/java",
38
38
  "android/paper/src/main/java",
39
+ "android/paper77/src/main/java",
39
40
  "android/common/src/main/java/",
40
41
  "android/reanimated/src/main/java/",
41
42
  "android/noreanimated/src/main/java/",
@@ -74,13 +75,13 @@
74
75
  "@babel/core": "^7.25.2",
75
76
  "@babel/preset-env": "^7.25.3",
76
77
  "@babel/preset-typescript": "^7.12.7",
77
- "@react-native/babel-preset": "0.77.0-rc.3",
78
+ "@react-native/babel-preset": "0.78.0",
78
79
  "@testing-library/react-native": "^12.5.1",
79
80
  "@types/hoist-non-react-statics": "^3.3.1",
80
81
  "@types/invariant": "^2.2.37",
81
82
  "@types/jest": "^27.0.3",
82
- "@types/react": "^18.2.6",
83
- "@types/react-test-renderer": "^17.0.0",
83
+ "@types/react": "^19.0.0",
84
+ "@types/react-test-renderer": "^19.0.0",
84
85
  "@typescript-eslint/eslint-plugin": "^4.33.0",
85
86
  "@typescript-eslint/parser": "^4.33.0",
86
87
  "babel-plugin-module-resolver": "^5.0.2",
@@ -96,11 +97,11 @@
96
97
  "lint-staged": "^12.3.2",
97
98
  "madge": "^6.1.0",
98
99
  "prettier": "3.3.3",
99
- "react": "18.3.1",
100
- "react-native": "0.77.0-rc.3",
100
+ "react": "19.0.0",
101
+ "react-native": "0.78.0",
101
102
  "react-native-builder-bob": "^0.17.1",
102
- "react-native-reanimated": "^3.16.3",
103
- "react-test-renderer": "18.2.0",
103
+ "react-native-reanimated": "^3.12.0",
104
+ "react-test-renderer": "19.0.0",
104
105
  "release-it": "^13.6.5",
105
106
  "typescript": "5.0.4"
106
107
  },
@@ -142,7 +142,7 @@ export const FlatList = React.forwardRef((props, ref) => {
142
142
  RefAttributes<FlatList<ItemT>> &
143
143
  NativeViewGestureHandlerProps
144
144
  >,
145
- ref: ForwardedRef<FlatList<ItemT>>
145
+ ref?: ForwardedRef<FlatList<ItemT>>
146
146
  ) => ReactElement | null;
147
147
  // eslint-disable-next-line @typescript-eslint/no-redeclare
148
148
  export type FlatList<ItemT = any> = typeof FlatList & RNFlatList<ItemT>;
@@ -143,6 +143,12 @@ export interface DrawerLayoutProps {
143
143
  */
144
144
  drawerType?: DrawerType;
145
145
 
146
+ /**
147
+ * Speed of animation that will play when letting go, or dismissing the drawer.
148
+ * This will also be the default animation speed for programatic controlls.
149
+ */
150
+ animationSpeed?: number;
151
+
146
152
  /**
147
153
  * Defines how far from the edge of the content view the gesture should
148
154
  * activate.
@@ -256,6 +262,10 @@ const defaultProps = {
256
262
  statusBarAnimation: 'slide' as StatusBarAnimation,
257
263
  };
258
264
 
265
+ // StatusBar.setHidden and Keyboard.dismiss cannot be directly referenced in worklets.
266
+ const setStatusBarHidden = StatusBar.setHidden;
267
+ const dismissKeyboard = Keyboard.dismiss;
268
+
259
269
  const DrawerLayout = forwardRef<DrawerLayoutMethods, DrawerLayoutProps>(
260
270
  function DrawerLayout(props: DrawerLayoutProps, ref) {
261
271
  const [containerWidth, setContainerWidth] = useState(0);
@@ -288,6 +298,7 @@ const DrawerLayout = forwardRef<DrawerLayoutMethods, DrawerLayoutProps>(
288
298
  onDrawerClose,
289
299
  onDrawerOpen,
290
300
  onDrawerStateChanged,
301
+ animationSpeed: animationSpeedProp,
291
302
  } = props;
292
303
 
293
304
  const isFromLeft = drawerPosition === DrawerPosition.LEFT;
@@ -366,7 +377,7 @@ const DrawerLayout = forwardRef<DrawerLayoutMethods, DrawerLayoutProps>(
366
377
  runOnJS(setDrawerState)(DrawerState.SETTLING);
367
378
 
368
379
  if (hideStatusBar) {
369
- runOnJS(StatusBar.setHidden)(willShow, statusBarAnimation);
380
+ runOnJS(setStatusBarHidden)(willShow, statusBarAnimation);
370
381
  }
371
382
 
372
383
  const normalizedToValue = interpolate(
@@ -388,7 +399,9 @@ const DrawerLayout = forwardRef<DrawerLayoutMethods, DrawerLayoutProps>(
388
399
  {
389
400
  overshootClamping: true,
390
401
  velocity: normalizedInitialVelocity,
391
- mass: animationSpeed ? 1 / animationSpeed : 1,
402
+ mass: animationSpeed
403
+ ? 1 / animationSpeed
404
+ : 1 / (animationSpeedProp ?? 1),
392
405
  damping: 40,
393
406
  stiffness: 500,
394
407
  },
@@ -529,10 +542,10 @@ const DrawerLayout = forwardRef<DrawerLayoutMethods, DrawerLayoutProps>(
529
542
  emitStateChanged(DrawerState.DRAGGING, false);
530
543
  runOnJS(setDrawerState)(DrawerState.DRAGGING);
531
544
  if (keyboardDismissMode === DrawerKeyboardDismissMode.ON_DRAG) {
532
- runOnJS(Keyboard.dismiss)();
545
+ runOnJS(dismissKeyboard)();
533
546
  }
534
547
  if (hideStatusBar) {
535
- runOnJS(StatusBar.setHidden)(true, statusBarAnimation);
548
+ runOnJS(setStatusBarHidden)(true, statusBarAnimation);
536
549
  }
537
550
  })
538
551
  .onUpdate((event) => {
@@ -35,6 +35,11 @@ export const Text = forwardRef(
35
35
  }
36
36
  };
37
37
 
38
+ // This is a special case for `Text` component. After https://github.com/software-mansion/react-native-gesture-handler/pull/3379 we check for
39
+ // `displayName` field. However, `Text` from RN has this field set to `Text`, but is also present in `RNSVGElements` set.
40
+ // We don't want to treat our `Text` as the one from `SVG`, therefore we add special field to ref.
41
+ refHandler.rngh = true;
42
+
38
43
  useEffect(() => {
39
44
  if (Platform.OS !== 'web') {
40
45
  return;
@@ -0,0 +1,7 @@
1
+ export type ExtraButtonProps = {
2
+ borderless?: boolean;
3
+ rippleColor?: number | string | null;
4
+ rippleRadius?: number | null;
5
+ foreground?: boolean;
6
+ exclusive?: boolean;
7
+ };
@@ -10,7 +10,6 @@ import {
10
10
  HandlerStateChangeEvent,
11
11
  } from '../../handlers/gestureHandlerCommon';
12
12
  import type { NativeViewGestureHandlerPayload } from '../../handlers/GestureHandlerEventPayload';
13
- import type { TouchableNativeFeedbackExtraProps } from './TouchableNativeFeedbackProps';
14
13
  import type { GenericTouchableProps } from './GenericTouchableProps';
15
14
 
16
15
  /**
@@ -29,7 +28,6 @@ export const TOUCHABLE_STATE = {
29
28
  type TouchableState = (typeof TOUCHABLE_STATE)[keyof typeof TOUCHABLE_STATE];
30
29
 
31
30
  interface InternalProps {
32
- extraButtonProps: TouchableNativeFeedbackExtraProps;
33
31
  onStateChange?: (oldState: TouchableState, newState: TouchableState) => void;
34
32
  }
35
33
 
@@ -5,6 +5,7 @@ import type {
5
5
  Insets,
6
6
  } from 'react-native';
7
7
  import type { UserSelect } from '../../handlers/gestureHandlerCommon';
8
+ import { ExtraButtonProps } from './ExtraButtonProps';
8
9
 
9
10
  export interface GenericTouchableProps
10
11
  extends Omit<TouchableWithoutFeedbackProps, 'hitSlop'> {
@@ -23,4 +24,5 @@ export interface GenericTouchableProps
23
24
  containerStyle?: StyleProp<ViewStyle>;
24
25
  hitSlop?: Insets | number;
25
26
  userSelect?: UserSelect;
27
+ extraButtonProps?: ExtraButtonProps;
26
28
  }
@@ -1,13 +1,8 @@
1
1
  import type { TouchableNativeFeedbackProps as RNTouchableNativeFeedbackProps } from 'react-native';
2
2
  import type { GenericTouchableProps } from './GenericTouchableProps';
3
+ import { ExtraButtonProps } from './ExtraButtonProps';
3
4
 
4
- export type TouchableNativeFeedbackExtraProps = {
5
- borderless?: boolean;
6
- rippleColor?: number | null;
7
- rippleRadius?: number | null;
8
- foreground?: boolean;
9
- };
10
-
5
+ export type TouchableNativeFeedbackExtraProps = ExtraButtonProps;
11
6
  /**
12
7
  * @deprecated TouchableNativeFeedback will be removed in the future version of Gesture Handler. Use Pressable instead.
13
8
  */
@@ -14,8 +14,26 @@ export type TouchableWithoutFeedbackProps = GenericTouchableProps;
14
14
  const TouchableWithoutFeedback = React.forwardRef<
15
15
  GenericTouchable,
16
16
  PropsWithChildren<TouchableWithoutFeedbackProps>
17
- >((props, ref) => <GenericTouchable ref={ref} {...props} />);
17
+ >(
18
+ (
19
+ {
20
+ delayLongPress = 600,
21
+ extraButtonProps = {
22
+ rippleColor: 'transparent',
23
+ exclusive: true,
24
+ },
25
+ ...rest
26
+ },
18
27
 
19
- TouchableWithoutFeedback.defaultProps = GenericTouchable.defaultProps;
28
+ ref
29
+ ) => (
30
+ <GenericTouchable
31
+ ref={ref}
32
+ delayLongPress={delayLongPress}
33
+ extraButtonProps={extraButtonProps}
34
+ {...rest}
35
+ />
36
+ )
37
+ );
20
38
 
21
39
  export default TouchableWithoutFeedback;
@@ -23,12 +23,19 @@ import {
23
23
  import { filterConfig, scheduleFlushOperations } from './utils';
24
24
  import findNodeHandle from '../findNodeHandle';
25
25
  import { ValueOf } from '../typeUtils';
26
- import { deepEqual, isFabric, isTestEnv, tagMessage } from '../utils';
26
+ import {
27
+ deepEqual,
28
+ isFabric,
29
+ isReact19,
30
+ isTestEnv,
31
+ tagMessage,
32
+ } from '../utils';
27
33
  import { ActionType } from '../ActionType';
28
34
  import { PressabilityDebugView } from './PressabilityDebugView';
29
35
  import GestureHandlerRootViewContext from '../GestureHandlerRootViewContext';
30
36
  import { ghQueueMicrotask } from '../ghQueueMicrotask';
31
37
  import { MountRegistry } from '../mountRegistry';
38
+ import { ReactElement } from 'react';
32
39
 
33
40
  const UIManagerAny = UIManager as any;
34
41
 
@@ -308,14 +315,18 @@ export default function createHandler<
308
315
  this.viewNode = node;
309
316
 
310
317
  const child = React.Children.only(this.props.children);
311
- // TODO(TS) fix ref type
312
- const { ref }: any = child;
313
- if (ref !== null) {
314
- if (typeof ref === 'function') {
315
- ref(node);
316
- } else {
317
- ref.current = node;
318
- }
318
+ // @ts-ignore Since React 19 ref is accessible as standard prop
319
+ // https://react.dev/blog/2024/04/25/react-19-upgrade-guide#deprecated-element-ref
320
+ const ref = isReact19() ? (child as ReactElement).props?.ref : child?.ref;
321
+
322
+ if (!ref) {
323
+ return;
324
+ }
325
+
326
+ if (typeof ref === 'function') {
327
+ ref(node);
328
+ } else {
329
+ ref.current = node;
319
330
  }
320
331
  };
321
332
 
@@ -44,11 +44,15 @@ export default function createNativeWrapper<P>(
44
44
  },
45
45
  {
46
46
  gestureHandlerProps: { ...config }, // Watch out not to modify config
47
- childProps: { enabled: props.enabled } as P,
47
+ childProps: {
48
+ enabled: props.enabled,
49
+ hitSlop: props.hitSlop,
50
+ testID: props.testID,
51
+ } as P,
48
52
  }
49
53
  );
50
- const _ref = useRef<React.ComponentType<P>>();
51
- const _gestureHandlerRef = useRef<React.ComponentType<P>>();
54
+ const _ref = useRef<React.ComponentType<P>>(null);
55
+ const _gestureHandlerRef = useRef<React.ComponentType<P>>(null);
52
56
  useImperativeHandle(
53
57
  ref,
54
58
  // @ts-ignore TODO(TS) decide how nulls work in this context
@@ -1,6 +1,7 @@
1
1
  import React, { forwardRef } from 'react';
2
2
  import type { LegacyRef, PropsWithChildren } from 'react';
3
3
  import { tagMessage } from '../../../utils';
4
+ import { isRNSVGNode } from '../../../web/utils';
4
5
 
5
6
  export const Wrap = forwardRef<HTMLDivElement, PropsWithChildren<{}>>(
6
7
  ({ children }, ref) => {
@@ -8,10 +9,7 @@ export const Wrap = forwardRef<HTMLDivElement, PropsWithChildren<{}>>(
8
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
10
  const child: any = React.Children.only(children);
10
11
 
11
- const isRNSVGNode =
12
- Object.getPrototypeOf(child?.type)?.name === 'WebShape';
13
-
14
- if (isRNSVGNode) {
12
+ if (isRNSVGNode(child)) {
15
13
  const clone = React.cloneElement(
16
14
  child,
17
15
  { ref },
package/src/utils.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import React from 'react';
2
+
1
3
  export function toArray<T>(object: T | T[]): T[] {
2
4
  if (!Array.isArray(object)) {
3
5
  return [object];
@@ -50,6 +52,10 @@ export function isFabric(): boolean {
50
52
  return !!global?.nativeFabricUIManager;
51
53
  }
52
54
 
55
+ export function isReact19() {
56
+ return React.version.startsWith('19.');
57
+ }
58
+
53
59
  export function isRemoteDebuggingEnabled(): boolean {
54
60
  // react-native-reanimated checks if in remote debugging in the same way
55
61
  // @ts-ignore global is available but node types are not included
package/src/web/utils.ts CHANGED
@@ -233,7 +233,7 @@ function spherical2tilt(altitudeAngle: number, azimuthAngle: number) {
233
233
  return { tiltX, tiltY };
234
234
  }
235
235
 
236
- const RNSVGElements = [
236
+ export const RNSVGElements = new Set([
237
237
  'Circle',
238
238
  'ClipPath',
239
239
  'Ellipse',
@@ -254,7 +254,7 @@ const RNSVGElements = [
254
254
  'Text',
255
255
  'TextPath',
256
256
  'Use',
257
- ];
257
+ ]);
258
258
 
259
259
  // This function helps us determine whether given node is SVGElement or not. In our implementation of
260
260
  // findNodeHandle, we can encounter such element in 2 forms - SVG tag or ref to SVG Element. Since Gesture Handler
@@ -269,7 +269,25 @@ export function isRNSVGElement(viewRef: SVGRef | GestureHandlerRef) {
269
269
  const componentClassName = Object.getPrototypeOf(viewRef).constructor.name;
270
270
 
271
271
  return (
272
- RNSVGElements.indexOf(componentClassName) >= 0 &&
272
+ RNSVGElements.has(componentClassName) &&
273
273
  Object.hasOwn(viewRef, 'elementRef')
274
274
  );
275
275
  }
276
+
277
+ // This function checks if given node is SVGElement. Unlike the function above, this one
278
+ // operates on React Nodes, not DOM nodes.
279
+ //
280
+ // Second condition was introduced to handle case where SVG element was wrapped with
281
+ // `createAnimatedComponent` from Reanimated.
282
+ export function isRNSVGNode(node: any) {
283
+ // If `ref` has `rngh` field, it means that component comes from Gesture Handler. This is a special case for
284
+ // `Text` component, which is present in `RNSVGElements` set, yet we don't want to treat it as SVG.
285
+ if (node.ref?.rngh) {
286
+ return false;
287
+ }
288
+
289
+ return (
290
+ Object.getPrototypeOf(node?.type)?.name === 'WebShape' ||
291
+ RNSVGElements.has(node?.type?.displayName)
292
+ );
293
+ }