react-native-gesture-handler 2.18.1 → 2.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. package/README.md +1 -0
  2. package/android/build.gradle +4 -17
  3. package/android/src/main/AndroidManifest.xml +1 -3
  4. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +21 -21
  5. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +2 -2
  6. package/android/src/main/java/com/swmansion/gesturehandler/core/HoverGestureHandler.kt +5 -0
  7. package/android/src/main/java/com/swmansion/gesturehandler/core/LongPressGestureHandler.kt +80 -4
  8. package/android/src/main/java/com/swmansion/gesturehandler/core/PinchGestureHandler.kt +2 -1
  9. package/android/src/main/java/com/swmansion/gesturehandler/core/ScaleGestureDetector.java +10 -0
  10. package/android/src/main/java/com/swmansion/gesturehandler/core/Vector.kt +2 -2
  11. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +3 -0
  12. package/apple/Handlers/RNFlingHandler.h +1 -0
  13. package/apple/Handlers/RNFlingHandler.m +153 -19
  14. package/apple/Handlers/RNHoverHandler.m +44 -2
  15. package/apple/Handlers/RNLongPressHandler.m +109 -20
  16. package/apple/Handlers/RNManualHandler.m +53 -29
  17. package/apple/Handlers/RNNativeViewHandler.mm +22 -15
  18. package/apple/RNGHUIKit.h +2 -0
  19. package/apple/RNGHVector.h +31 -0
  20. package/apple/RNGHVector.m +67 -0
  21. package/apple/RNGestureHandler.h +7 -0
  22. package/apple/{RNGestureHandler.m → RNGestureHandler.mm} +63 -1
  23. package/apple/RNGestureHandlerButtonComponentView.mm +6 -0
  24. package/apple/RNGestureHandlerDirection.h +25 -0
  25. package/lib/commonjs/PointerType.js +2 -1
  26. package/lib/commonjs/PointerType.js.map +1 -1
  27. package/lib/commonjs/components/Pressable/Pressable.js +67 -70
  28. package/lib/commonjs/components/Pressable/Pressable.js.map +1 -1
  29. package/lib/commonjs/components/Pressable/index.js +0 -8
  30. package/lib/commonjs/components/Pressable/index.js.map +1 -1
  31. package/lib/commonjs/components/ReanimatedSwipeable.js +60 -41
  32. package/lib/commonjs/components/ReanimatedSwipeable.js.map +1 -1
  33. package/lib/commonjs/handlers/LongPressGestureHandler.js +1 -1
  34. package/lib/commonjs/handlers/LongPressGestureHandler.js.map +1 -1
  35. package/lib/commonjs/handlers/gestures/GestureDetector/utils.js +1 -1
  36. package/lib/commonjs/handlers/gestures/GestureDetector/utils.js.map +1 -1
  37. package/lib/commonjs/handlers/gestures/longPressGesture.js +10 -0
  38. package/lib/commonjs/handlers/gestures/longPressGesture.js.map +1 -1
  39. package/lib/commonjs/mocks.js +16 -3
  40. package/lib/commonjs/mocks.js.map +1 -1
  41. package/lib/commonjs/utils.js +4 -0
  42. package/lib/commonjs/utils.js.map +1 -1
  43. package/lib/commonjs/web/constants.js +3 -3
  44. package/lib/commonjs/web/constants.js.map +1 -1
  45. package/lib/commonjs/web/handlers/GestureHandler.js +1 -0
  46. package/lib/commonjs/web/handlers/GestureHandler.js.map +1 -1
  47. package/lib/commonjs/web/handlers/LongPressGestureHandler.js +43 -9
  48. package/lib/commonjs/web/handlers/LongPressGestureHandler.js.map +1 -1
  49. package/lib/commonjs/web/handlers/NativeViewGestureHandler.js +14 -3
  50. package/lib/commonjs/web/handlers/NativeViewGestureHandler.js.map +1 -1
  51. package/lib/commonjs/web/handlers/PanGestureHandler.js +4 -0
  52. package/lib/commonjs/web/handlers/PanGestureHandler.js.map +1 -1
  53. package/lib/commonjs/web/interfaces.js.map +1 -1
  54. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js +55 -8
  55. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  56. package/lib/commonjs/web/tools/KeyboardEventManager.js +110 -0
  57. package/lib/commonjs/web/tools/KeyboardEventManager.js.map +1 -0
  58. package/lib/commonjs/web/tools/Vector.js +4 -2
  59. package/lib/commonjs/web/tools/Vector.js.map +1 -1
  60. package/lib/commonjs/web/utils.js +14 -13
  61. package/lib/commonjs/web/utils.js.map +1 -1
  62. package/lib/module/PointerType.js +2 -1
  63. package/lib/module/PointerType.js.map +1 -1
  64. package/lib/module/components/Pressable/Pressable.js +66 -70
  65. package/lib/module/components/Pressable/Pressable.js.map +1 -1
  66. package/lib/module/components/Pressable/index.js +0 -1
  67. package/lib/module/components/Pressable/index.js.map +1 -1
  68. package/lib/module/components/ReanimatedSwipeable.js +58 -37
  69. package/lib/module/components/ReanimatedSwipeable.js.map +1 -1
  70. package/lib/module/handlers/LongPressGestureHandler.js +1 -1
  71. package/lib/module/handlers/LongPressGestureHandler.js.map +1 -1
  72. package/lib/module/handlers/gestures/GestureDetector/utils.js +2 -2
  73. package/lib/module/handlers/gestures/GestureDetector/utils.js.map +1 -1
  74. package/lib/module/handlers/gestures/longPressGesture.js +10 -0
  75. package/lib/module/handlers/gestures/longPressGesture.js.map +1 -1
  76. package/lib/module/mocks.js +13 -3
  77. package/lib/module/mocks.js.map +1 -1
  78. package/lib/module/utils.js +1 -0
  79. package/lib/module/utils.js.map +1 -1
  80. package/lib/module/web/constants.js +1 -1
  81. package/lib/module/web/constants.js.map +1 -1
  82. package/lib/module/web/handlers/GestureHandler.js +1 -0
  83. package/lib/module/web/handlers/GestureHandler.js.map +1 -1
  84. package/lib/module/web/handlers/LongPressGestureHandler.js +43 -9
  85. package/lib/module/web/handlers/LongPressGestureHandler.js.map +1 -1
  86. package/lib/module/web/handlers/NativeViewGestureHandler.js +14 -3
  87. package/lib/module/web/handlers/NativeViewGestureHandler.js.map +1 -1
  88. package/lib/module/web/handlers/PanGestureHandler.js +4 -0
  89. package/lib/module/web/handlers/PanGestureHandler.js.map +1 -1
  90. package/lib/module/web/interfaces.js.map +1 -1
  91. package/lib/module/web/tools/GestureHandlerWebDelegate.js +54 -8
  92. package/lib/module/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  93. package/lib/module/web/tools/KeyboardEventManager.js +96 -0
  94. package/lib/module/web/tools/KeyboardEventManager.js.map +1 -0
  95. package/lib/module/web/tools/Vector.js +5 -3
  96. package/lib/module/web/tools/Vector.js.map +1 -1
  97. package/lib/module/web/utils.js +14 -13
  98. package/lib/module/web/utils.js.map +1 -1
  99. package/lib/typescript/PointerType.d.ts +2 -1
  100. package/lib/typescript/components/Pressable/index.d.ts +1 -1
  101. package/lib/typescript/handlers/LongPressGestureHandler.d.ts +5 -1
  102. package/lib/typescript/handlers/gestures/longPressGesture.d.ts +5 -0
  103. package/lib/typescript/mocks.d.ts +4 -3
  104. package/lib/typescript/utils.d.ts +1 -0
  105. package/lib/typescript/web/constants.d.ts +1 -1
  106. package/lib/typescript/web/handlers/GestureHandler.d.ts +1 -1
  107. package/lib/typescript/web/handlers/LongPressGestureHandler.d.ts +3 -0
  108. package/lib/typescript/web/handlers/NativeViewGestureHandler.d.ts +1 -0
  109. package/lib/typescript/web/interfaces.d.ts +1 -1
  110. package/lib/typescript/web/tools/GestureHandlerDelegate.d.ts +1 -0
  111. package/lib/typescript/web/tools/GestureHandlerWebDelegate.d.ts +6 -0
  112. package/lib/typescript/web/tools/KeyboardEventManager.d.ts +13 -0
  113. package/package.json +3 -3
  114. package/src/PointerType.ts +1 -0
  115. package/src/components/Pressable/Pressable.tsx +70 -50
  116. package/src/components/Pressable/index.ts +1 -1
  117. package/src/components/ReanimatedSwipeable.tsx +70 -47
  118. package/src/handlers/LongPressGestureHandler.ts +6 -0
  119. package/src/handlers/gestures/GestureDetector/utils.ts +2 -2
  120. package/src/handlers/gestures/longPressGesture.ts +9 -0
  121. package/src/{mocks.ts → mocks.tsx} +8 -3
  122. package/src/utils.ts +2 -0
  123. package/src/web/constants.ts +1 -1
  124. package/src/web/handlers/GestureHandler.ts +3 -1
  125. package/src/web/handlers/LongPressGestureHandler.ts +49 -10
  126. package/src/web/handlers/NativeViewGestureHandler.ts +14 -4
  127. package/src/web/handlers/PanGestureHandler.ts +4 -0
  128. package/src/web/interfaces.ts +1 -1
  129. package/src/web/tools/GestureHandlerDelegate.ts +1 -0
  130. package/src/web/tools/GestureHandlerWebDelegate.ts +67 -8
  131. package/src/web/tools/KeyboardEventManager.ts +91 -0
  132. package/src/web/tools/Vector.ts +4 -3
  133. package/src/web/utils.ts +15 -13
@@ -3,10 +3,10 @@ import { State } from '../../State';
3
3
  import { Config, AdaptedEvent } from '../interfaces';
4
4
  import EventManager from '../tools/EventManager';
5
5
  import PointerTracker from '../tools/PointerTracker';
6
- import { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate';
7
6
  import IGestureHandler from './IGestureHandler';
8
7
  import { MouseButton } from '../../handlers/gestureHandlerCommon';
9
8
  import { PointerType } from '../../PointerType';
9
+ import { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate';
10
10
  export default abstract class GestureHandler implements IGestureHandler {
11
11
  private lastSentState;
12
12
  protected currentState: State;
@@ -6,6 +6,7 @@ export default class LongPressGestureHandler extends GestureHandler {
6
6
  private minDurationMs;
7
7
  private defaultMaxDistSq;
8
8
  private maxDistSq;
9
+ private numberOfPointers;
9
10
  private startX;
10
11
  private startY;
11
12
  private startTime;
@@ -19,8 +20,10 @@ export default class LongPressGestureHandler extends GestureHandler {
19
20
  protected resetConfig(): void;
20
21
  protected onStateChange(_newState: State, _oldState: State): void;
21
22
  protected onPointerDown(event: AdaptedEvent): void;
23
+ protected onPointerAdd(event: AdaptedEvent): void;
22
24
  protected onPointerMove(event: AdaptedEvent): void;
23
25
  protected onPointerUp(event: AdaptedEvent): void;
26
+ protected onPointerRemove(event: AdaptedEvent): void;
24
27
  private tryBegin;
25
28
  private tryActivate;
26
29
  private checkDistanceFail;
@@ -10,6 +10,7 @@ export default class NativeViewGestureHandler extends GestureHandler {
10
10
  private minDistSq;
11
11
  init(ref: number, propsRef: React.RefObject<unknown>): void;
12
12
  updateGestureConfig({ enabled, ...props }: Config): void;
13
+ private restoreViewStyles;
13
14
  protected resetConfig(): void;
14
15
  protected onPointerDown(event: AdaptedEvent): void;
15
16
  protected onPointerAdd(event: AdaptedEvent): void;
@@ -17,7 +17,7 @@ export interface Handler {
17
17
  }
18
18
  type ConfigArgs = number | boolean | HitSlop | UserSelect | TouchAction | ActiveCursor | Directions | Handler[] | null | undefined;
19
19
  export interface Config extends Record<string, ConfigArgs> {
20
- enabled?: boolean;
20
+ enabled: boolean;
21
21
  simultaneousHandlers?: Handler[] | null;
22
22
  waitFor?: Handler[] | null;
23
23
  blocksHandlers?: Handler[] | null;
@@ -19,5 +19,6 @@ export interface GestureHandlerDelegate<TComponent, THandler> {
19
19
  onEnd(): void;
20
20
  onCancel(): void;
21
21
  onFail(): void;
22
+ onEnabledChange(enabled: boolean): void;
22
23
  destroy(config: Config): void;
23
24
  }
@@ -2,9 +2,11 @@ import type IGestureHandler from '../handlers/IGestureHandler';
2
2
  import { GestureHandlerDelegate, MeasureResult } from './GestureHandlerDelegate';
3
3
  import { Config } from '../interfaces';
4
4
  export declare class GestureHandlerWebDelegate implements GestureHandlerDelegate<HTMLElement, IGestureHandler> {
5
+ private isInitialized;
5
6
  private view;
6
7
  private gestureHandler;
7
8
  private eventManagers;
9
+ private defaultViewStyles;
8
10
  getView(): HTMLElement;
9
11
  init(viewRef: number, handler: IGestureHandler): void;
10
12
  isPointerInBounds({ x, y }: {
@@ -19,6 +21,10 @@ export declare class GestureHandlerWebDelegate implements GestureHandlerDelegate
19
21
  private removeContextMenuListeners;
20
22
  private disableContextMenu;
21
23
  private enableContextMenu;
24
+ private setUserSelect;
25
+ private setTouchAction;
26
+ private setContextMenu;
27
+ onEnabledChange(enabled: boolean): void;
22
28
  onBegin(): void;
23
29
  onActivate(): void;
24
30
  onEnd(): void;
@@ -0,0 +1,13 @@
1
+ import { AdaptedEvent, EventTypes } from '../interfaces';
2
+ import EventManager from './EventManager';
3
+ export default class KeyboardEventManager extends EventManager<HTMLElement> {
4
+ private activationKeys;
5
+ private cancelationKeys;
6
+ private isPressed;
7
+ private keyDownCallback;
8
+ private keyUpCallback;
9
+ private dispatchEvent;
10
+ registerListeners(): void;
11
+ unregisterListeners(): void;
12
+ protected mapEvent(event: KeyboardEvent, eventType: EventTypes): AdaptedEvent;
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gesture-handler",
3
- "version": "2.18.1",
3
+ "version": "2.19.0",
4
4
  "description": "Experimental implementation of a new declarative API for gesture handling in react-native",
5
5
  "scripts": {
6
6
  "prepare": "bob build && husky install",
@@ -11,7 +11,7 @@
11
11
  "ts-check": "yarn tsc --noEmit",
12
12
  "format:js": "prettier --write --list-different './{src,example,FabricExample,MacOSExample}/**/*.{js,jsx,ts,tsx}'",
13
13
  "format:android": "node ./scripts/format-android.js",
14
- "format:ios": "find apple/ -iname *.h -o -iname *.m -o -iname *.cpp -o -iname *.mm | xargs clang-format -i",
14
+ "format:apple": "find apple/ -iname *.h -o -iname *.m -o -iname *.cpp -o -iname *.mm | xargs clang-format -i",
15
15
  "lint:js": "eslint --ext '.js,.ts,.tsx' src/ example/src FabricExample/src MacOSExample/src && yarn prettier --check './{src,example,FabricExample,MacOSExample}/**/*.{js,jsx,ts,tsx}'",
16
16
  "lint:js-root": "eslint --ext '.js,.ts,.tsx' src/ && yarn prettier --check './src/**/*.{js,jsx,ts,tsx}'",
17
17
  "lint:android": "./android/gradlew -p android spotlessCheck -q",
@@ -114,7 +114,7 @@
114
114
  "lint-staged": {
115
115
  "./{src,example,FabricExample,MacOSExample}/**/*.{ts,tsx}": "yarn format:js",
116
116
  "android/**/*.kt": "yarn format:android",
117
- "apple/**/*.{h,m,mm,cpp}": "yarn format:ios",
117
+ "apple/**/*.{h,m,mm,cpp}": "yarn format:apple",
118
118
  "src/specs/*.ts": "yarn sync-architectures"
119
119
  },
120
120
  "release-it": {
@@ -2,5 +2,6 @@ export enum PointerType {
2
2
  TOUCH,
3
3
  STYLUS,
4
4
  MOUSE,
5
+ KEY,
5
6
  OTHER,
6
7
  }
@@ -22,13 +22,34 @@ import {
22
22
  } from './utils';
23
23
  import { PressabilityDebugView } from '../../handlers/PressabilityDebugView';
24
24
  import { GestureTouchEvent } from '../../handlers/gestureHandlerCommon';
25
+ import { INT32_MAX } from '../../utils';
25
26
 
26
27
  const DEFAULT_LONG_PRESS_DURATION = 500;
27
28
 
28
29
  export default function Pressable(props: PressableProps) {
29
- const [pressedState, setPressedState] = useState(
30
- props.testOnly_pressed ?? false
31
- );
30
+ const {
31
+ testOnly_pressed,
32
+ hitSlop,
33
+ pressRetentionOffset,
34
+ delayHoverIn,
35
+ onHoverIn,
36
+ delayHoverOut,
37
+ onHoverOut,
38
+ delayLongPress,
39
+ unstable_pressDelay,
40
+ onPress,
41
+ onPressIn,
42
+ onPressOut,
43
+ onLongPress,
44
+ style,
45
+ children,
46
+ android_disableSound,
47
+ android_ripple,
48
+ disabled,
49
+ ...remainingProps
50
+ } = props;
51
+
52
+ const [pressedState, setPressedState] = useState(testOnly_pressed ?? false);
32
53
 
33
54
  const pressableRef = useRef<View>(null);
34
55
 
@@ -39,18 +60,16 @@ export default function Pressable(props: PressableProps) {
39
60
 
40
61
  const normalizedHitSlop: Insets = useMemo(
41
62
  () =>
42
- typeof props.hitSlop === 'number'
43
- ? numberAsInset(props.hitSlop)
44
- : props.hitSlop ?? {},
45
- [props.hitSlop]
63
+ typeof hitSlop === 'number' ? numberAsInset(hitSlop) : hitSlop ?? {},
64
+ [hitSlop]
46
65
  );
47
66
 
48
67
  const normalizedPressRetentionOffset: Insets = useMemo(
49
68
  () =>
50
- typeof props.pressRetentionOffset === 'number'
51
- ? numberAsInset(props.pressRetentionOffset)
52
- : props.pressRetentionOffset ?? {},
53
- [props.pressRetentionOffset]
69
+ typeof pressRetentionOffset === 'number'
70
+ ? numberAsInset(pressRetentionOffset)
71
+ : pressRetentionOffset ?? {},
72
+ [pressRetentionOffset]
54
73
  );
55
74
 
56
75
  const hoverInTimeout = useRef<number | null>(null);
@@ -65,29 +84,29 @@ export default function Pressable(props: PressableProps) {
65
84
  if (hoverOutTimeout.current) {
66
85
  clearTimeout(hoverOutTimeout.current);
67
86
  }
68
- if (props.delayHoverIn) {
87
+ if (delayHoverIn) {
69
88
  hoverInTimeout.current = setTimeout(
70
- () => props.onHoverIn?.(gestureToPressableEvent(event)),
71
- props.delayHoverIn
89
+ () => onHoverIn?.(gestureToPressableEvent(event)),
90
+ delayHoverIn
72
91
  );
73
92
  return;
74
93
  }
75
- props.onHoverIn?.(gestureToPressableEvent(event));
94
+ onHoverIn?.(gestureToPressableEvent(event));
76
95
  })
77
96
  .onFinalize((event) => {
78
97
  if (hoverInTimeout.current) {
79
98
  clearTimeout(hoverInTimeout.current);
80
99
  }
81
- if (props.delayHoverOut) {
100
+ if (delayHoverOut) {
82
101
  hoverOutTimeout.current = setTimeout(
83
- () => props.onHoverOut?.(gestureToPressableEvent(event)),
84
- props.delayHoverOut
102
+ () => onHoverOut?.(gestureToPressableEvent(event)),
103
+ delayHoverOut
85
104
  );
86
105
  return;
87
106
  }
88
- props.onHoverOut?.(gestureToPressableEvent(event));
107
+ onHoverOut?.(gestureToPressableEvent(event));
89
108
  }),
90
- [props]
109
+ [delayHoverIn, delayHoverOut, onHoverIn, onHoverOut]
91
110
  );
92
111
 
93
112
  const pressDelayTimeoutRef = useRef<number | null>(null);
@@ -108,12 +127,12 @@ export default function Pressable(props: PressableProps) {
108
127
 
109
128
  deferredEventPayload.current = null;
110
129
 
111
- props.onPressIn?.(event);
130
+ onPressIn?.(event);
112
131
  isPressCallbackEnabled.current = true;
113
132
  pressDelayTimeoutRef.current = null;
114
133
  setPressedState(true);
115
134
  },
116
- [props]
135
+ [onPressIn]
117
136
  );
118
137
 
119
138
  const pressOutHandler = useCallback(
@@ -126,7 +145,7 @@ export default function Pressable(props: PressableProps) {
126
145
  return;
127
146
  }
128
147
 
129
- if (props.unstable_pressDelay && pressDelayTimeoutRef.current !== null) {
148
+ if (unstable_pressDelay && pressDelayTimeoutRef.current !== null) {
130
149
  // When delay is preemptively finished by lifting touches,
131
150
  // we want to immediately activate it's effects - pressInHandler,
132
151
  // even though we are located at the pressOutHandler
@@ -135,14 +154,14 @@ export default function Pressable(props: PressableProps) {
135
154
  }
136
155
 
137
156
  if (deferredEventPayload.current) {
138
- props.onPressIn?.(deferredEventPayload.current);
157
+ onPressIn?.(deferredEventPayload.current);
139
158
  deferredEventPayload.current = null;
140
159
  }
141
160
 
142
- props.onPressOut?.(event);
161
+ onPressOut?.(event);
143
162
 
144
163
  if (isPressCallbackEnabled.current) {
145
- props.onPress?.(event);
164
+ onPress?.(event);
146
165
  }
147
166
 
148
167
  if (longPressTimeoutRef.current) {
@@ -155,7 +174,7 @@ export default function Pressable(props: PressableProps) {
155
174
  isPressCallbackEnabled.current = true;
156
175
  setPressedState(false);
157
176
  },
158
- [pressInHandler, props]
177
+ [onPress, onPressIn, onPressOut, pressInHandler, unstable_pressDelay]
159
178
  );
160
179
 
161
180
  const handlingOnTouchesDown = useRef<boolean>(false);
@@ -169,7 +188,7 @@ export default function Pressable(props: PressableProps) {
169
188
  }
170
189
 
171
190
  if (hasPassedBoundsChecks.current) {
172
- props.onLongPress?.(gestureTouchToPressableEvent(event));
191
+ onLongPress?.(gestureTouchToPressableEvent(event));
173
192
  isPressCallbackEnabled.current = false;
174
193
  }
175
194
 
@@ -178,19 +197,19 @@ export default function Pressable(props: PressableProps) {
178
197
  longPressTimeoutRef.current = null;
179
198
  }
180
199
  },
181
- [props]
200
+ [onLongPress]
182
201
  );
183
202
 
184
203
  const longPressTimeoutRef = useRef<number | null>(null);
185
204
  const longPressMinDuration =
186
- (props.delayLongPress ?? DEFAULT_LONG_PRESS_DURATION) +
187
- (props.unstable_pressDelay ?? 0);
205
+ (delayLongPress ?? DEFAULT_LONG_PRESS_DURATION) +
206
+ (unstable_pressDelay ?? 0);
188
207
 
189
208
  const pressAndTouchGesture = useMemo(
190
209
  () =>
191
210
  Gesture.LongPress()
192
- .minDuration(Number.MAX_SAFE_INTEGER) // Stops long press from blocking native gesture
193
- .maxDistance(Number.MAX_SAFE_INTEGER) // Stops long press from cancelling after set distance
211
+ .minDuration(INT32_MAX) // Stops long press from blocking native gesture
212
+ .maxDistance(INT32_MAX) // Stops long press from cancelling after set distance
194
213
  .cancelsTouchesInView(false)
195
214
  .onTouchesDown((event) => {
196
215
  handlingOnTouchesDown.current = true;
@@ -224,10 +243,10 @@ export default function Pressable(props: PressableProps) {
224
243
  );
225
244
  }
226
245
 
227
- if (props.unstable_pressDelay) {
246
+ if (unstable_pressDelay) {
228
247
  pressDelayTimeoutRef.current = setTimeout(() => {
229
248
  pressInHandler(gestureTouchToPressableEvent(event));
230
- }, props.unstable_pressDelay);
249
+ }, unstable_pressDelay);
231
250
  } else {
232
251
  pressInHandler(gestureTouchToPressableEvent(event));
233
252
  }
@@ -275,7 +294,7 @@ export default function Pressable(props: PressableProps) {
275
294
  normalizedHitSlop,
276
295
  pressInHandler,
277
296
  pressOutHandler,
278
- props.unstable_pressDelay,
297
+ unstable_pressDelay,
279
298
  ]
280
299
  );
281
300
 
@@ -333,7 +352,7 @@ export default function Pressable(props: PressableProps) {
333
352
  normalizedPressRetentionOffset
334
353
  );
335
354
 
336
- const isPressableEnabled = props.disabled !== true;
355
+ const isPressableEnabled = disabled !== true;
337
356
 
338
357
  const gestures = [pressAndTouchGesture, hoverGesture, buttonGesture];
339
358
 
@@ -353,40 +372,41 @@ export default function Pressable(props: PressableProps) {
353
372
 
354
373
  const gesture = Gesture.Simultaneous(...gestures);
355
374
 
356
- const defaultRippleColor = props.android_ripple ? undefined : 'transparent';
375
+ const defaultRippleColor = android_ripple ? undefined : 'transparent';
357
376
 
358
377
  // `cursor: 'pointer'` on `RNButton` crashes iOS
359
378
  const pointerStyle: StyleProp<ViewStyle> =
360
379
  Platform.OS === 'web' ? { cursor: 'pointer' } : {};
361
380
 
362
381
  const styleProp =
363
- typeof props.style === 'function'
364
- ? props.style({ pressed: pressedState })
365
- : props.style;
382
+ typeof style === 'function' ? style({ pressed: pressedState }) : style;
366
383
 
367
384
  const childrenProp =
368
- typeof props.children === 'function'
369
- ? props.children({ pressed: pressedState })
370
- : props.children;
385
+ typeof children === 'function'
386
+ ? children({ pressed: pressedState })
387
+ : children;
371
388
 
372
389
  const flattenedStyles = StyleSheet.flatten(styleProp ?? {});
373
390
 
374
391
  const [innerStyles, outerStyles] = splitStyles(flattenedStyles);
375
392
 
376
393
  return (
377
- <View style={outerStyles}>
394
+ <View {...remainingProps} style={outerStyles}>
378
395
  <GestureDetector gesture={gesture}>
379
396
  <NativeButton
380
397
  ref={pressableRef}
381
- testID={props.testID}
382
398
  hitSlop={appliedHitSlop}
383
399
  enabled={isPressableEnabled}
384
- touchSoundDisabled={props.android_disableSound ?? undefined}
400
+ touchSoundDisabled={android_disableSound ?? undefined}
385
401
  rippleColor={processColor(
386
- props.android_ripple?.color ?? defaultRippleColor
402
+ android_ripple?.color ?? defaultRippleColor
387
403
  )}
388
- rippleRadius={props.android_ripple?.radius ?? undefined}
389
- style={[StyleSheet.absoluteFill, pointerStyle, innerStyles]}>
404
+ rippleRadius={android_ripple?.radius ?? undefined}
405
+ style={[
406
+ { width: '100%', height: '100%' },
407
+ pointerStyle,
408
+ innerStyles,
409
+ ]}>
390
410
  {childrenProp}
391
411
  {__DEV__ ? (
392
412
  <PressabilityDebugView color="red" hitSlop={normalizedHitSlop} />
@@ -1,2 +1,2 @@
1
- export { PressableProps } from './PressableProps';
1
+ export type { PressableProps } from './PressableProps';
2
2
  export { default } from './Pressable';
@@ -199,6 +199,26 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
199
199
  props: SwipeableProps,
200
200
  ref: ForwardedRef<SwipeableMethods>
201
201
  ) {
202
+ const {
203
+ leftThreshold,
204
+ rightThreshold,
205
+ onSwipeableOpenStartDrag,
206
+ onSwipeableCloseStartDrag,
207
+ enableTrackpadTwoFingerGesture,
208
+ enabled,
209
+ containerStyle,
210
+ childrenContainerStyle,
211
+ animationOptions,
212
+ overshootLeft,
213
+ overshootRight,
214
+ onSwipeableWillOpen,
215
+ onSwipeableWillClose,
216
+ onSwipeableOpen,
217
+ onSwipeableClose,
218
+ testID,
219
+ ...remainingProps
220
+ } = props;
221
+
202
222
  const rowState = useSharedValue<number>(0);
203
223
 
204
224
  const userDrag = useSharedValue<number>(0);
@@ -240,8 +260,8 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
240
260
  overshootFriction = defaultProps.overshootFriction,
241
261
  } = props;
242
262
 
243
- const overshootLeftProp = props.overshootLeft;
244
- const overshootRightProp = props.overshootRight;
263
+ const overshootLeftProp = overshootLeft;
264
+ const overshootRightProp = overshootRight;
245
265
 
246
266
  const calculateCurrentOffset = useCallback(() => {
247
267
  'worklet';
@@ -317,45 +337,40 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
317
337
 
318
338
  const dispatchImmediateEvents = useCallback(
319
339
  (fromValue: number, toValue: number) => {
320
- if (toValue > 0 && props.onSwipeableWillOpen) {
321
- props.onSwipeableWillOpen('left');
322
- } else if (toValue < 0 && props.onSwipeableWillOpen) {
323
- props.onSwipeableWillOpen('right');
324
- } else if (props.onSwipeableWillClose) {
340
+ if (toValue > 0 && onSwipeableWillOpen) {
341
+ onSwipeableWillOpen('left');
342
+ } else if (toValue < 0 && onSwipeableWillOpen) {
343
+ onSwipeableWillOpen('right');
344
+ } else if (onSwipeableWillClose) {
325
345
  const closingDirection = fromValue > 0 ? 'left' : 'right';
326
- props.onSwipeableWillClose(closingDirection);
346
+ onSwipeableWillClose(closingDirection);
327
347
  }
328
348
  },
329
- [
330
- props,
331
- props.onSwipeableWillClose,
332
- props.onSwipeableWillOpen,
333
- swipeableMethods,
334
- ]
349
+ [onSwipeableWillClose, onSwipeableWillOpen]
335
350
  );
336
351
 
337
352
  const dispatchEndEvents = useCallback(
338
353
  (fromValue: number, toValue: number) => {
339
- if (toValue > 0 && props.onSwipeableOpen) {
340
- props.onSwipeableOpen('left', swipeableMethods.current);
341
- } else if (toValue < 0 && props.onSwipeableOpen) {
342
- props.onSwipeableOpen('right', swipeableMethods.current);
343
- } else if (props.onSwipeableClose) {
354
+ if (toValue > 0 && onSwipeableOpen) {
355
+ onSwipeableOpen('left', swipeableMethods.current);
356
+ } else if (toValue < 0 && onSwipeableOpen) {
357
+ onSwipeableOpen('right', swipeableMethods.current);
358
+ } else if (onSwipeableClose) {
344
359
  const closingDirection = fromValue > 0 ? 'left' : 'right';
345
- props.onSwipeableClose(closingDirection, swipeableMethods.current);
360
+ onSwipeableClose(closingDirection, swipeableMethods.current);
346
361
  }
347
362
  },
348
- [props, props.onSwipeableClose, props.onSwipeableOpen, swipeableMethods]
363
+ [onSwipeableClose, onSwipeableOpen]
349
364
  );
350
365
 
351
- const animationOptionsProp = props.animationOptions;
366
+ const animationOptionsProp = animationOptions;
352
367
 
353
368
  const animateRow = useCallback(
354
369
  (fromValue: number, toValue: number, velocityX?: number) => {
355
370
  'worklet';
356
371
  rowState.value = Math.sign(toValue);
357
372
 
358
- const springConfig = {
373
+ const translationSpringConfig = {
359
374
  duration: 1000,
360
375
  dampingRatio: 0.9,
361
376
  stiffness: 500,
@@ -364,9 +379,14 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
364
379
  ...animationOptionsProp,
365
380
  };
366
381
 
382
+ const progressSpringConfig = {
383
+ ...translationSpringConfig,
384
+ velocity: 0,
385
+ };
386
+
367
387
  appliedTranslation.value = withSpring(
368
388
  toValue,
369
- springConfig,
389
+ translationSpringConfig,
370
390
  (isFinished) => {
371
391
  if (isFinished) {
372
392
  runOnJS(dispatchEndEvents)(fromValue, toValue);
@@ -376,23 +396,27 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
376
396
 
377
397
  const progressTarget = toValue === 0 ? 0 : 1;
378
398
 
379
- // Velocity is in px, while progress is in %
380
- springConfig.velocity = 0;
381
-
382
399
  showLeftProgress.value =
383
- leftWidth.value > 0 ? withSpring(progressTarget, springConfig) : 0;
400
+ leftWidth.value > 0
401
+ ? withSpring(progressTarget, progressSpringConfig)
402
+ : 0;
384
403
  showRightProgress.value =
385
- rightWidth.value > 0 ? withSpring(progressTarget, springConfig) : 0;
404
+ rightWidth.value > 0
405
+ ? withSpring(progressTarget, progressSpringConfig)
406
+ : 0;
386
407
 
387
408
  runOnJS(dispatchImmediateEvents)(fromValue, toValue);
388
409
  },
389
410
  [
390
- showLeftProgress,
411
+ rowState,
412
+ animationOptionsProp,
391
413
  appliedTranslation,
392
- dispatchEndEvents,
414
+ showLeftProgress,
415
+ leftWidth.value,
416
+ showRightProgress,
417
+ rightWidth.value,
393
418
  dispatchImmediateEvents,
394
- animationOptionsProp,
395
- rowState,
419
+ dispatchEndEvents,
396
420
  ]
397
421
  );
398
422
 
@@ -483,8 +507,8 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
483
507
  </Animated.View>
484
508
  );
485
509
 
486
- const leftThresholdProp = props.leftThreshold;
487
- const rightThresholdProp = props.rightThreshold;
510
+ const leftThresholdProp = leftThreshold;
511
+ const rightThresholdProp = rightThreshold;
488
512
 
489
513
  const handleRelease = (
490
514
  event: GestureStateChangeEvent<PanGestureHandlerEventPayload>
@@ -535,9 +559,6 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
535
559
  }
536
560
  });
537
561
 
538
- const onSwipeableOpenStartDrag = props.onSwipeableOpenStartDrag;
539
- const onSwipeableCloseStartDrag = props.onSwipeableCloseStartDrag;
540
-
541
562
  const panGesture = Gesture.Pan()
542
563
  .onUpdate((event: GestureUpdateEvent<PanGestureHandlerEventPayload>) => {
543
564
  userDrag.value = event.translationX;
@@ -564,10 +585,8 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
564
585
  }
565
586
  );
566
587
 
567
- if (props.enableTrackpadTwoFingerGesture) {
568
- panGesture.enableTrackpadTwoFingerGesture(
569
- props.enableTrackpadTwoFingerGesture
570
- );
588
+ if (enableTrackpadTwoFingerGesture) {
589
+ panGesture.enableTrackpadTwoFingerGesture(enableTrackpadTwoFingerGesture);
571
590
  }
572
591
 
573
592
  panGesture.activeOffsetX([
@@ -580,7 +599,7 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
580
599
  swipeableMethods,
581
600
  ]);
582
601
 
583
- panGesture.enabled(props.enabled !== false);
602
+ panGesture.enabled(enabled !== false);
584
603
 
585
604
  const animatedStyle = useAnimatedStyle(
586
605
  () => ({
@@ -590,12 +609,10 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
590
609
  [appliedTranslation, rowState]
591
610
  );
592
611
 
593
- const containerStyle = props.containerStyle;
594
- const childrenContainerStyle = props.childrenContainerStyle;
595
-
596
- return (
612
+ const swipeableComponent = (
597
613
  <GestureDetector gesture={panGesture} touchAction="pan-y">
598
614
  <Animated.View
615
+ {...remainingProps}
599
616
  onLayout={onRowLayout}
600
617
  style={[styles.container, containerStyle]}>
601
618
  {leftElement}
@@ -608,6 +625,12 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
608
625
  </Animated.View>
609
626
  </GestureDetector>
610
627
  );
628
+
629
+ return testID ? (
630
+ <View testID={testID}>{swipeableComponent}</View>
631
+ ) : (
632
+ swipeableComponent
633
+ );
611
634
  }
612
635
  );
613
636
 
@@ -8,6 +8,7 @@ import {
8
8
  export const longPressGestureHandlerProps = [
9
9
  'minDurationMs',
10
10
  'maxDist',
11
+ 'numberOfPointers',
11
12
  ] as const;
12
13
 
13
14
  export interface LongPressGestureConfig {
@@ -24,6 +25,11 @@ export interface LongPressGestureConfig {
24
25
  * will fail to recognize the gesture. The default value is 10.
25
26
  */
26
27
  maxDist?: number;
28
+
29
+ /**
30
+ * Determine exact number of points required to handle the long press gesture.
31
+ */
32
+ numberOfPointers?: number;
27
33
  }
28
34
 
29
35
  export interface LongPressGestureHandlerProps
@@ -1,6 +1,6 @@
1
1
  import { Platform } from 'react-native';
2
2
 
3
- import { tagMessage } from '../../../utils';
3
+ import { isJestEnv, tagMessage } from '../../../utils';
4
4
  import { GestureRef, BaseGesture, GestureType } from '../gesture';
5
5
 
6
6
  import { flingGestureHandlerProps } from '../../FlingGestureHandler';
@@ -100,7 +100,7 @@ export function checkGestureCallbacksForWorklets(gesture: GestureType) {
100
100
  const areAllNotWorklets = !areSomeWorklets && areSomeNotWorklets;
101
101
  // If none of the callbacks are worklets and the gesture is not explicitly marked with
102
102
  // `.runOnJS(true)` show a warning
103
- if (areAllNotWorklets) {
103
+ if (areAllNotWorklets && !isJestEnv()) {
104
104
  console.warn(
105
105
  tagMessage(
106
106
  `None of the callbacks in the gesture are worklets. If you wish to run them on the JS thread use '.runOnJS(true)' modifier on the gesture to make this explicit. Otherwise, mark the callbacks as 'worklet' to run them on the UI thread.`