react-native-gesture-handler 2.20.2 → 2.21.1

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 (124) hide show
  1. package/RNGestureHandler.podspec +9 -0
  2. package/ReanimatedDrawerLayout/package.json +6 -0
  3. package/android/build.gradle +19 -0
  4. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +6 -2
  5. package/android/src/main/java/com/swmansion/gesturehandler/core/RotationGestureDetector.kt +53 -21
  6. package/apple/RNGestureHandlerPointerTracker.m +4 -2
  7. package/lib/commonjs/RNGestureHandlerModule.web.js +15 -2
  8. package/lib/commonjs/RNGestureHandlerModule.web.js.map +1 -1
  9. package/lib/commonjs/components/Pressable/Pressable.js +2 -6
  10. package/lib/commonjs/components/Pressable/Pressable.js.map +1 -1
  11. package/lib/commonjs/components/ReanimatedDrawerLayout.js +389 -0
  12. package/lib/commonjs/components/ReanimatedDrawerLayout.js.map +1 -0
  13. package/lib/commonjs/components/ReanimatedSwipeable.js +117 -148
  14. package/lib/commonjs/components/ReanimatedSwipeable.js.map +1 -1
  15. package/lib/commonjs/findNodeHandle.js +12 -0
  16. package/lib/commonjs/findNodeHandle.js.map +1 -0
  17. package/lib/commonjs/findNodeHandle.web.js +40 -0
  18. package/lib/commonjs/findNodeHandle.web.js.map +1 -0
  19. package/lib/commonjs/handlers/createHandler.js +4 -2
  20. package/lib/commonjs/handlers/createHandler.js.map +1 -1
  21. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js +51 -0
  22. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -0
  23. package/lib/commonjs/handlers/gestures/GestureDetector/index.js +3 -1
  24. package/lib/commonjs/handlers/gestures/GestureDetector/index.js.map +1 -1
  25. package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js +5 -3
  26. package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
  27. package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js +4 -2
  28. package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
  29. package/lib/commonjs/web/handlers/GestureHandler.js +4 -0
  30. package/lib/commonjs/web/handlers/GestureHandler.js.map +1 -1
  31. package/lib/commonjs/web/handlers/PanGestureHandler.js +59 -0
  32. package/lib/commonjs/web/handlers/PanGestureHandler.js.map +1 -1
  33. package/lib/commonjs/web/interfaces.js +10 -1
  34. package/lib/commonjs/web/interfaces.js.map +1 -1
  35. package/lib/commonjs/web/tools/EventManager.js +6 -0
  36. package/lib/commonjs/web/tools/EventManager.js.map +1 -1
  37. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js +1 -3
  38. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  39. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js +5 -2
  40. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  41. package/lib/commonjs/web/tools/KeyboardEventManager.js +2 -2
  42. package/lib/commonjs/web/tools/KeyboardEventManager.js.map +1 -1
  43. package/lib/commonjs/web/tools/PointerTracker.js +6 -30
  44. package/lib/commonjs/web/tools/PointerTracker.js.map +1 -1
  45. package/lib/commonjs/web/tools/WheelEventManager.js +74 -0
  46. package/lib/commonjs/web/tools/WheelEventManager.js.map +1 -0
  47. package/lib/commonjs/web/utils.js +16 -0
  48. package/lib/commonjs/web/utils.js.map +1 -1
  49. package/lib/module/RNGestureHandlerModule.web.js +16 -3
  50. package/lib/module/RNGestureHandlerModule.web.js.map +1 -1
  51. package/lib/module/components/Pressable/Pressable.js +2 -6
  52. package/lib/module/components/Pressable/Pressable.js.map +1 -1
  53. package/lib/module/components/ReanimatedDrawerLayout.js +365 -0
  54. package/lib/module/components/ReanimatedDrawerLayout.js.map +1 -0
  55. package/lib/module/components/ReanimatedSwipeable.js +119 -145
  56. package/lib/module/components/ReanimatedSwipeable.js.map +1 -1
  57. package/lib/module/findNodeHandle.js +3 -0
  58. package/lib/module/findNodeHandle.js.map +1 -0
  59. package/lib/module/findNodeHandle.web.js +32 -0
  60. package/lib/module/findNodeHandle.web.js.map +1 -0
  61. package/lib/module/handlers/createHandler.js +2 -1
  62. package/lib/module/handlers/createHandler.js.map +1 -1
  63. package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js +34 -0
  64. package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -0
  65. package/lib/module/handlers/gestures/GestureDetector/index.js +2 -1
  66. package/lib/module/handlers/gestures/GestureDetector/index.js.map +1 -1
  67. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js +2 -2
  68. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
  69. package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js +1 -1
  70. package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
  71. package/lib/module/web/handlers/GestureHandler.js +4 -0
  72. package/lib/module/web/handlers/GestureHandler.js.map +1 -1
  73. package/lib/module/web/handlers/PanGestureHandler.js +58 -0
  74. package/lib/module/web/handlers/PanGestureHandler.js.map +1 -1
  75. package/lib/module/web/interfaces.js +8 -0
  76. package/lib/module/web/interfaces.js.map +1 -1
  77. package/lib/module/web/tools/EventManager.js +6 -0
  78. package/lib/module/web/tools/EventManager.js.map +1 -1
  79. package/lib/module/web/tools/GestureHandlerOrchestrator.js +1 -3
  80. package/lib/module/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  81. package/lib/module/web/tools/GestureHandlerWebDelegate.js +3 -1
  82. package/lib/module/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  83. package/lib/module/web/tools/KeyboardEventManager.js +2 -2
  84. package/lib/module/web/tools/KeyboardEventManager.js.map +1 -1
  85. package/lib/module/web/tools/PointerTracker.js +6 -30
  86. package/lib/module/web/tools/PointerTracker.js.map +1 -1
  87. package/lib/module/web/tools/WheelEventManager.js +60 -0
  88. package/lib/module/web/tools/WheelEventManager.js.map +1 -0
  89. package/lib/module/web/utils.js +15 -0
  90. package/lib/module/web/utils.js.map +1 -1
  91. package/lib/typescript/components/ReanimatedDrawerLayout.d.ts +162 -0
  92. package/lib/typescript/components/ReanimatedSwipeable.d.ts +22 -16
  93. package/lib/typescript/findNodeHandle.d.ts +2 -0
  94. package/lib/typescript/findNodeHandle.web.d.ts +2 -0
  95. package/lib/typescript/handlers/gestures/GestureDetector/Wrap.web.d.ts +7 -0
  96. package/lib/typescript/web/handlers/GestureHandler.d.ts +2 -1
  97. package/lib/typescript/web/handlers/PanGestureHandler.d.ts +5 -0
  98. package/lib/typescript/web/interfaces.d.ts +16 -0
  99. package/lib/typescript/web/tools/EventManager.d.ts +2 -0
  100. package/lib/typescript/web/tools/PointerTracker.d.ts +2 -8
  101. package/lib/typescript/web/tools/WheelEventManager.d.ts +11 -0
  102. package/lib/typescript/web/utils.d.ts +2 -1
  103. package/package.json +3 -2
  104. package/src/RNGestureHandlerModule.web.ts +23 -4
  105. package/src/components/Pressable/Pressable.tsx +2 -6
  106. package/src/components/ReanimatedDrawerLayout.tsx +741 -0
  107. package/src/components/ReanimatedSwipeable.tsx +361 -305
  108. package/src/findNodeHandle.ts +3 -0
  109. package/src/findNodeHandle.web.ts +35 -0
  110. package/src/handlers/createHandler.tsx +2 -1
  111. package/src/handlers/gestures/GestureDetector/Wrap.web.tsx +44 -0
  112. package/src/handlers/gestures/GestureDetector/index.tsx +2 -1
  113. package/src/handlers/gestures/GestureDetector/useDetectorUpdater.ts +1 -1
  114. package/src/handlers/gestures/GestureDetector/useViewRefHandler.ts +1 -1
  115. package/src/web/handlers/GestureHandler.ts +5 -1
  116. package/src/web/handlers/PanGestureHandler.ts +69 -1
  117. package/src/web/interfaces.ts +17 -0
  118. package/src/web/tools/EventManager.ts +4 -0
  119. package/src/web/tools/GestureHandlerOrchestrator.ts +1 -7
  120. package/src/web/tools/GestureHandlerWebDelegate.ts +3 -1
  121. package/src/web/tools/KeyboardEventManager.ts +2 -2
  122. package/src/web/tools/PointerTracker.ts +6 -28
  123. package/src/web/tools/WheelEventManager.ts +48 -0
  124. package/src/web/utils.ts +47 -1
@@ -7,7 +7,7 @@ import React, {
7
7
  forwardRef,
8
8
  useCallback,
9
9
  useImperativeHandle,
10
- useRef,
10
+ useMemo,
11
11
  } from 'react';
12
12
  import { GestureObjects as Gesture } from '../handlers/gestures/gestureObjects';
13
13
  import { GestureDetector } from '../handlers/gestures/GestureDetector';
@@ -18,7 +18,6 @@ import {
18
18
  import type { PanGestureHandlerProps } from '../handlers/PanGestureHandler';
19
19
  import type { PanGestureHandlerEventPayload } from '../handlers/GestureHandlerEventPayload';
20
20
  import Animated, {
21
- Extrapolation,
22
21
  SharedValue,
23
22
  interpolate,
24
23
  runOnJS,
@@ -42,6 +41,11 @@ type SwipeableExcludes = Exclude<
42
41
  'onGestureEvent' | 'onHandlerStateChange'
43
42
  >;
44
43
 
44
+ enum SwipeDirection {
45
+ LEFT = 'left',
46
+ RIGHT = 'right',
47
+ }
48
+
45
49
  export interface SwipeableProps
46
50
  extends Pick<PanGestureHandlerProps, SwipeableExcludes> {
47
51
  /**
@@ -111,65 +115,74 @@ export interface SwipeableProps
111
115
  * Called when action panel gets open (either right or left).
112
116
  */
113
117
  onSwipeableOpen?: (
114
- direction: 'left' | 'right',
115
- swipeable: SwipeableMethods
118
+ direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
116
119
  ) => void;
117
120
 
118
121
  /**
119
122
  * Called when action panel is closed.
120
123
  */
121
124
  onSwipeableClose?: (
122
- direction: 'left' | 'right',
123
- swipeable: SwipeableMethods
125
+ direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
124
126
  ) => void;
125
127
 
126
128
  /**
127
129
  * Called when action panel starts animating on open (either right or left).
128
130
  */
129
- onSwipeableWillOpen?: (direction: 'left' | 'right') => void;
131
+ onSwipeableWillOpen?: (
132
+ direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
133
+ ) => void;
130
134
 
131
135
  /**
132
136
  * Called when action panel starts animating on close.
133
137
  */
134
- onSwipeableWillClose?: (direction: 'left' | 'right') => void;
138
+ onSwipeableWillClose?: (
139
+ direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
140
+ ) => void;
135
141
 
136
142
  /**
137
143
  * Called when action panel starts being shown on dragging to open.
138
144
  */
139
- onSwipeableOpenStartDrag?: (direction: 'left' | 'right') => void;
145
+ onSwipeableOpenStartDrag?: (
146
+ direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
147
+ ) => void;
140
148
 
141
149
  /**
142
150
  * Called when action panel starts being shown on dragging to close.
143
151
  */
144
- onSwipeableCloseStartDrag?: (direction: 'left' | 'right') => void;
152
+ onSwipeableCloseStartDrag?: (
153
+ direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
154
+ ) => void;
145
155
 
146
156
  /**
157
+ * `progress`: Equals `0` when `swipeable` is closed, `1` when `swipeable` is opened.
158
+ * - When the element overshoots it's opened position the value tends towards `Infinity`.
159
+ * - Goes back to `1` when `swipeable` is released.
147
160
  *
148
- * This map describes the values to use as inputRange for extra interpolation:
149
- * AnimatedValue: [startValue, endValue]
150
- *
151
- * progressAnimatedValue: [0, 1] dragAnimatedValue: [0, +]
161
+ * `translation`: a horizontal offset of the `swipeable` relative to its closed position.\
162
+ * `swipeableMethods`: provides an object exposing methods for controlling the `swipeable`.
152
163
  *
153
164
  * To support `rtl` flexbox layouts use `flexDirection` styling.
154
165
  * */
155
166
  renderLeftActions?: (
156
- progressAnimatedValue: SharedValue<number>,
157
- dragAnimatedValue: SharedValue<number>,
158
- swipeable: SwipeableMethods
167
+ progress: SharedValue<number>,
168
+ translation: SharedValue<number>,
169
+ swipeableMethods: SwipeableMethods
159
170
  ) => React.ReactNode;
171
+
160
172
  /**
173
+ * `progress`: Equals `0` when `swipeable` is closed, `1` when `swipeable` is opened.
174
+ * - When the element overshoots it's opened position the value tends towards `Infinity`.
175
+ * - Goes back to `1` when `swipeable` is released.
161
176
  *
162
- * This map describes the values to use as inputRange for extra interpolation:
163
- * AnimatedValue: [startValue, endValue]
164
- *
165
- * progressAnimatedValue: [0, 1] dragAnimatedValue: [0, -]
177
+ * `translation`: a horizontal offset of the `swipeable` relative to its closed position.\
178
+ * `swipeableMethods`: provides an object exposing methods for controlling the `swipeable`.
166
179
  *
167
180
  * To support `rtl` flexbox layouts use `flexDirection` styling.
168
181
  * */
169
182
  renderRightActions?: (
170
- progressAnimatedValue: SharedValue<number>,
171
- dragAnimatedValue: SharedValue<number>,
172
- swipeable: SwipeableMethods
183
+ progress: SharedValue<number>,
184
+ translation: SharedValue<number>,
185
+ swipeableMethods: SwipeableMethods
173
186
  ) => React.ReactNode;
174
187
 
175
188
  animationOptions?: Record<string, unknown>;
@@ -199,86 +212,69 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
199
212
  props: SwipeableProps,
200
213
  ref: ForwardedRef<SwipeableMethods>
201
214
  ) {
215
+ const defaultProps = {
216
+ friction: 1,
217
+ overshootFriction: 1,
218
+ dragOffset: 10,
219
+ enableTrackpadTwoFingerGesture: false,
220
+ };
221
+
202
222
  const {
203
223
  leftThreshold,
204
224
  rightThreshold,
205
- onSwipeableOpenStartDrag,
206
- onSwipeableCloseStartDrag,
207
- enableTrackpadTwoFingerGesture,
208
225
  enabled,
209
226
  containerStyle,
210
227
  childrenContainerStyle,
211
228
  animationOptions,
212
229
  overshootLeft,
213
230
  overshootRight,
231
+ testID,
232
+ children,
233
+ enableTrackpadTwoFingerGesture = defaultProps.enableTrackpadTwoFingerGesture,
234
+ dragOffsetFromLeftEdge = defaultProps.dragOffset,
235
+ dragOffsetFromRightEdge = defaultProps.dragOffset,
236
+ friction = defaultProps.friction,
237
+ overshootFriction = defaultProps.overshootFriction,
238
+ onSwipeableOpenStartDrag,
239
+ onSwipeableCloseStartDrag,
214
240
  onSwipeableWillOpen,
215
241
  onSwipeableWillClose,
216
242
  onSwipeableOpen,
217
243
  onSwipeableClose,
218
- testID,
244
+ renderLeftActions,
245
+ renderRightActions,
219
246
  ...remainingProps
220
247
  } = props;
221
248
 
222
249
  const rowState = useSharedValue<number>(0);
223
250
 
224
251
  const userDrag = useSharedValue<number>(0);
252
+
225
253
  const appliedTranslation = useSharedValue<number>(0);
226
254
 
227
255
  const rowWidth = useSharedValue<number>(0);
228
256
  const leftWidth = useSharedValue<number>(0);
229
257
  const rightWidth = useSharedValue<number>(0);
230
- const rightOffset = useSharedValue<number>(0);
231
258
 
232
- const leftActionTranslate = useSharedValue<number>(0);
233
- const rightActionTranslate = useSharedValue<number>(0);
259
+ // used for synchronizing layout measurements between JS and UI
260
+ const rightOffset = useSharedValue<number | null>(null);
234
261
 
235
262
  const showLeftProgress = useSharedValue<number>(0);
236
263
  const showRightProgress = useSharedValue<number>(0);
237
264
 
238
- const swipeableMethods = useRef<SwipeableMethods>({
239
- close: () => {
240
- 'worklet';
241
- },
242
- openLeft: () => {
243
- 'worklet';
244
- },
245
- openRight: () => {
246
- 'worklet';
247
- },
248
- reset: () => {
249
- 'worklet';
250
- },
251
- });
252
-
253
- const defaultProps = {
254
- friction: 1,
255
- overshootFriction: 1,
256
- };
257
-
258
- const {
259
- friction = defaultProps.friction,
260
- overshootFriction = defaultProps.overshootFriction,
261
- } = props;
262
-
263
- const overshootLeftProp = overshootLeft;
264
- const overshootRightProp = overshootRight;
265
-
266
- const calculateCurrentOffset = useCallback(() => {
265
+ const updateRightElementWidth = useCallback(() => {
267
266
  'worklet';
268
- if (rowState.value === 1) {
269
- return leftWidth.value;
270
- } else if (rowState.value === -1) {
271
- return -rowWidth.value - rightOffset.value;
267
+ if (rightOffset.value === null) {
268
+ rightOffset.value = rowWidth.value;
272
269
  }
273
- return 0;
274
- }, [leftWidth, rightOffset, rowState, rowWidth]);
270
+ rightWidth.value = Math.max(0, rowWidth.value - rightOffset.value);
271
+ }, [rightOffset, rightWidth, rowWidth]);
275
272
 
276
- const updateAnimatedEvent = () => {
273
+ const updateAnimatedEvent = useCallback(() => {
277
274
  'worklet';
278
- rightWidth.value = Math.max(0, rowWidth.value - rightOffset.value);
279
275
 
280
- const overshootLeft = overshootLeftProp ?? leftWidth.value > 0;
281
- const overshootRight = overshootRightProp ?? rightWidth.value > 0;
276
+ const shouldOvershootLeft = overshootLeft ?? leftWidth.value > 0;
277
+ const shouldOvershootRight = overshootRight ?? rightWidth.value > 0;
282
278
 
283
279
  const startOffset =
284
280
  rowState.value === 1
@@ -298,10 +294,11 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
298
294
  leftWidth.value + 1,
299
295
  ],
300
296
  [
301
- -rightWidth.value - (overshootRight ? 1 / overshootFriction : 0),
297
+ -rightWidth.value -
298
+ (shouldOvershootRight ? 1 / overshootFriction : 0),
302
299
  -rightWidth.value,
303
300
  leftWidth.value,
304
- leftWidth.value + (overshootLeft ? 1 / overshootFriction : 0),
301
+ leftWidth.value + (shouldOvershootLeft ? 1 / overshootFriction : 0),
305
302
  ]
306
303
  );
307
304
 
@@ -313,12 +310,7 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
313
310
  [0, 0, 1]
314
311
  )
315
312
  : 0;
316
- leftActionTranslate.value = interpolate(
317
- showLeftProgress.value,
318
- [0, Number.MIN_VALUE],
319
- [-10000, 0],
320
- Extrapolation.CLAMP
321
- );
313
+
322
314
  showRightProgress.value =
323
315
  rightWidth.value > 0
324
316
  ? interpolate(
@@ -327,23 +319,31 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
327
319
  [1, 0, 0]
328
320
  )
329
321
  : 0;
330
- rightActionTranslate.value = interpolate(
331
- showRightProgress.value,
332
- [0, Number.MIN_VALUE],
333
- [-10000, 0],
334
- Extrapolation.CLAMP
335
- );
336
- };
322
+ }, [
323
+ appliedTranslation,
324
+ friction,
325
+ leftWidth,
326
+ overshootFriction,
327
+ rightWidth,
328
+ rowState,
329
+ showLeftProgress,
330
+ showRightProgress,
331
+ userDrag,
332
+ overshootLeft,
333
+ overshootRight,
334
+ ]);
337
335
 
338
336
  const dispatchImmediateEvents = useCallback(
339
337
  (fromValue: number, toValue: number) => {
338
+ 'worklet';
340
339
  if (toValue > 0 && onSwipeableWillOpen) {
341
- onSwipeableWillOpen('left');
340
+ runOnJS(onSwipeableWillOpen)(SwipeDirection.RIGHT);
342
341
  } else if (toValue < 0 && onSwipeableWillOpen) {
343
- onSwipeableWillOpen('right');
342
+ runOnJS(onSwipeableWillOpen)(SwipeDirection.LEFT);
344
343
  } else if (onSwipeableWillClose) {
345
- const closingDirection = fromValue > 0 ? 'left' : 'right';
346
- onSwipeableWillClose(closingDirection);
344
+ runOnJS(onSwipeableWillClose)(
345
+ fromValue > 0 ? SwipeDirection.LEFT : SwipeDirection.RIGHT
346
+ );
347
347
  }
348
348
  },
349
349
  [onSwipeableWillClose, onSwipeableWillOpen]
@@ -351,255 +351,311 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
351
351
 
352
352
  const dispatchEndEvents = useCallback(
353
353
  (fromValue: number, toValue: number) => {
354
+ 'worklet';
354
355
  if (toValue > 0 && onSwipeableOpen) {
355
- onSwipeableOpen('left', swipeableMethods.current);
356
+ runOnJS(onSwipeableOpen)(SwipeDirection.RIGHT);
356
357
  } else if (toValue < 0 && onSwipeableOpen) {
357
- onSwipeableOpen('right', swipeableMethods.current);
358
+ runOnJS(onSwipeableOpen)(SwipeDirection.LEFT);
358
359
  } else if (onSwipeableClose) {
359
- const closingDirection = fromValue > 0 ? 'left' : 'right';
360
- onSwipeableClose(closingDirection, swipeableMethods.current);
360
+ runOnJS(onSwipeableClose)(
361
+ fromValue > 0 ? SwipeDirection.LEFT : SwipeDirection.RIGHT
362
+ );
361
363
  }
362
364
  },
363
365
  [onSwipeableClose, onSwipeableOpen]
364
366
  );
365
367
 
366
- const animationOptionsProp = animationOptions;
367
-
368
- const animateRow = useCallback(
369
- (fromValue: number, toValue: number, velocityX?: number) => {
370
- 'worklet';
371
- rowState.value = Math.sign(toValue);
372
-
373
- const translationSpringConfig = {
374
- duration: 1000,
375
- dampingRatio: 0.9,
376
- stiffness: 500,
377
- velocity: velocityX,
378
- overshootClamping: true,
379
- ...animationOptionsProp,
380
- };
381
-
382
- const progressSpringConfig = {
383
- ...translationSpringConfig,
384
- velocity: 0,
385
- };
386
-
387
- appliedTranslation.value = withSpring(
388
- toValue,
389
- translationSpringConfig,
390
- (isFinished) => {
391
- if (isFinished) {
392
- runOnJS(dispatchEndEvents)(fromValue, toValue);
368
+ const animateRow: (toValue: number, velocityX?: number) => void =
369
+ useCallback(
370
+ (toValue: number, velocityX?: number) => {
371
+ 'worklet';
372
+
373
+ const translationSpringConfig = {
374
+ duration: 1000,
375
+ dampingRatio: 0.9,
376
+ stiffness: 500,
377
+ velocity: velocityX,
378
+ overshootClamping: true,
379
+ ...animationOptions,
380
+ };
381
+
382
+ const isClosing = toValue === 0;
383
+ const moveToRight = isClosing ? rowState.value < 0 : toValue > 0;
384
+
385
+ const usedWidth = isClosing
386
+ ? moveToRight
387
+ ? rightWidth.value
388
+ : leftWidth.value
389
+ : moveToRight
390
+ ? leftWidth.value
391
+ : rightWidth.value;
392
+
393
+ const progressSpringConfig = {
394
+ ...translationSpringConfig,
395
+ restDisplacementThreshold: 0.01,
396
+ restSpeedThreshold: 0.01,
397
+ velocity:
398
+ velocityX &&
399
+ interpolate(velocityX, [-usedWidth, usedWidth], [-1, 1]),
400
+ };
401
+
402
+ const frozenRowState = rowState.value;
403
+
404
+ appliedTranslation.value = withSpring(
405
+ toValue,
406
+ translationSpringConfig,
407
+ (isFinished) => {
408
+ if (isFinished) {
409
+ dispatchEndEvents(frozenRowState, toValue);
410
+ }
393
411
  }
394
- }
395
- );
412
+ );
396
413
 
397
- const progressTarget = toValue === 0 ? 0 : 1;
414
+ const progressTarget = toValue === 0 ? 0 : 1;
398
415
 
399
- showLeftProgress.value =
400
- leftWidth.value > 0
401
- ? withSpring(progressTarget, progressSpringConfig)
402
- : 0;
403
- showRightProgress.value =
404
- rightWidth.value > 0
405
- ? withSpring(progressTarget, progressSpringConfig)
406
- : 0;
416
+ showLeftProgress.value =
417
+ leftWidth.value > 0
418
+ ? withSpring(progressTarget, progressSpringConfig)
419
+ : 0;
420
+ showRightProgress.value =
421
+ rightWidth.value > 0
422
+ ? withSpring(progressTarget, progressSpringConfig)
423
+ : 0;
407
424
 
408
- runOnJS(dispatchImmediateEvents)(fromValue, toValue);
409
- },
410
- [
411
- rowState,
412
- animationOptionsProp,
413
- appliedTranslation,
414
- showLeftProgress,
415
- leftWidth.value,
416
- showRightProgress,
417
- rightWidth.value,
418
- dispatchImmediateEvents,
419
- dispatchEndEvents,
420
- ]
421
- );
425
+ dispatchImmediateEvents(frozenRowState, toValue);
422
426
 
423
- const onRowLayout = ({ nativeEvent }: LayoutChangeEvent) => {
424
- rowWidth.value = nativeEvent.layout.width;
425
- };
426
-
427
- const {
428
- children,
429
- renderLeftActions,
430
- renderRightActions,
431
- dragOffsetFromLeftEdge = 10,
432
- dragOffsetFromRightEdge = 10,
433
- } = props;
434
-
435
- swipeableMethods.current = {
436
- close() {
437
- 'worklet';
438
- animateRow(calculateCurrentOffset(), 0);
439
- },
440
- openLeft() {
441
- 'worklet';
442
- animateRow(calculateCurrentOffset(), leftWidth.value);
443
- },
444
- openRight() {
445
- 'worklet';
446
- rightWidth.value = rowWidth.value - rightOffset.value;
447
- animateRow(calculateCurrentOffset(), -rightWidth.value);
448
- },
449
- reset() {
450
- 'worklet';
451
- userDrag.value = 0;
452
- showLeftProgress.value = 0;
453
- appliedTranslation.value = 0;
454
- rowState.value = 0;
455
- },
456
- };
427
+ rowState.value = Math.sign(toValue);
428
+ },
429
+ [
430
+ rowState,
431
+ animationOptions,
432
+ appliedTranslation,
433
+ showLeftProgress,
434
+ leftWidth,
435
+ showRightProgress,
436
+ rightWidth,
437
+ dispatchImmediateEvents,
438
+ dispatchEndEvents,
439
+ ]
440
+ );
457
441
 
458
- const leftAnimatedStyle = useAnimatedStyle(
442
+ const swipeableMethods = useMemo<SwipeableMethods>(
459
443
  () => ({
460
- transform: [
461
- {
462
- translateX: leftActionTranslate.value,
463
- },
464
- ],
444
+ close() {
445
+ 'worklet';
446
+ animateRow(0);
447
+ },
448
+ openLeft() {
449
+ 'worklet';
450
+ animateRow(leftWidth.value);
451
+ },
452
+ openRight() {
453
+ 'worklet';
454
+ // rightOffset and rowWidth are already much sooner than rightWidth
455
+ animateRow((rightOffset.value ?? 0) - rowWidth.value);
456
+ },
457
+ reset() {
458
+ 'worklet';
459
+ userDrag.value = 0;
460
+ showLeftProgress.value = 0;
461
+ appliedTranslation.value = 0;
462
+ rowState.value = 0;
463
+ },
465
464
  }),
466
- [leftActionTranslate]
465
+ [
466
+ leftWidth,
467
+ rightOffset,
468
+ rowWidth,
469
+ userDrag,
470
+ showLeftProgress,
471
+ appliedTranslation,
472
+ rowState,
473
+ animateRow,
474
+ ]
467
475
  );
468
476
 
469
- const leftElement = renderLeftActions && (
470
- <Animated.View style={[styles.leftActions, leftAnimatedStyle]}>
471
- {renderLeftActions(
472
- showLeftProgress,
473
- appliedTranslation,
474
- swipeableMethods.current
475
- )}
476
- <View
477
- onLayout={({ nativeEvent }) =>
478
- (leftWidth.value = nativeEvent.layout.x)
479
- }
480
- />
481
- </Animated.View>
477
+ const onRowLayout = useCallback(
478
+ ({ nativeEvent }: LayoutChangeEvent) => {
479
+ rowWidth.value = nativeEvent.layout.width;
480
+ },
481
+ [rowWidth]
482
482
  );
483
483
 
484
- const rightAnimatedStyle = useAnimatedStyle(
485
- () => ({
486
- transform: [
487
- {
488
- translateX: rightActionTranslate.value,
489
- },
490
- ],
491
- }),
492
- [rightActionTranslate]
484
+ const leftElement = useCallback(
485
+ () => (
486
+ <Animated.View style={[styles.leftActions]}>
487
+ {renderLeftActions?.(
488
+ showLeftProgress,
489
+ appliedTranslation,
490
+ swipeableMethods
491
+ )}
492
+ <View
493
+ onLayout={({ nativeEvent }) =>
494
+ (leftWidth.value = nativeEvent.layout.x)
495
+ }
496
+ />
497
+ </Animated.View>
498
+ ),
499
+ [
500
+ appliedTranslation,
501
+ leftWidth,
502
+ renderLeftActions,
503
+ showLeftProgress,
504
+ swipeableMethods,
505
+ ]
493
506
  );
494
507
 
495
- const rightElement = renderRightActions && (
496
- <Animated.View style={[styles.rightActions, rightAnimatedStyle]}>
497
- {renderRightActions(
498
- showRightProgress,
499
- appliedTranslation,
500
- swipeableMethods.current
501
- )}
502
- <View
503
- onLayout={({ nativeEvent }) =>
504
- (rightOffset.value = nativeEvent.layout.x)
505
- }
506
- />
507
- </Animated.View>
508
+ const rightElement = useCallback(
509
+ () => (
510
+ <Animated.View style={[styles.rightActions]}>
511
+ {renderRightActions?.(
512
+ showRightProgress,
513
+ appliedTranslation,
514
+ swipeableMethods
515
+ )}
516
+ <View
517
+ onLayout={({ nativeEvent }) => {
518
+ rightOffset.value = nativeEvent.layout.x;
519
+ }}
520
+ />
521
+ </Animated.View>
522
+ ),
523
+ [
524
+ appliedTranslation,
525
+ renderRightActions,
526
+ rightOffset,
527
+ showRightProgress,
528
+ swipeableMethods,
529
+ ]
508
530
  );
509
531
 
510
- const leftThresholdProp = leftThreshold;
511
- const rightThresholdProp = rightThreshold;
512
-
513
- const handleRelease = (
514
- event: GestureStateChangeEvent<PanGestureHandlerEventPayload>
515
- ) => {
516
- 'worklet';
517
- const { velocityX } = event;
518
- userDrag.value = event.translationX;
532
+ const handleRelease = useCallback(
533
+ (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
534
+ 'worklet';
535
+ const { velocityX } = event;
536
+ userDrag.value = event.translationX;
519
537
 
520
- rightWidth.value = rowWidth.value - rightOffset.value;
538
+ updateRightElementWidth();
521
539
 
522
- const leftThreshold = leftThresholdProp ?? leftWidth.value / 2;
523
- const rightThreshold = rightThresholdProp ?? rightWidth.value / 2;
540
+ const leftThresholdProp = leftThreshold ?? leftWidth.value / 2;
541
+ const rightThresholdProp = rightThreshold ?? rightWidth.value / 2;
524
542
 
525
- const startOffsetX = calculateCurrentOffset() + userDrag.value / friction;
526
- const translationX = (userDrag.value + DRAG_TOSS * velocityX) / friction;
543
+ const translationX =
544
+ (userDrag.value + DRAG_TOSS * velocityX) / friction;
527
545
 
528
- let toValue = 0;
546
+ let toValue = 0;
529
547
 
530
- if (rowState.value === 0) {
531
- if (translationX > leftThreshold) {
532
- toValue = leftWidth.value;
533
- } else if (translationX < -rightThreshold) {
534
- toValue = -rightWidth.value;
535
- }
536
- } else if (rowState.value === 1) {
537
- // Swiped to left
538
- if (translationX > -leftThreshold) {
539
- toValue = leftWidth.value;
540
- }
541
- } else {
542
- // Swiped to right
543
- if (translationX < rightThreshold) {
544
- toValue = -rightWidth.value;
548
+ if (rowState.value === 0) {
549
+ if (translationX > leftThresholdProp) {
550
+ toValue = leftWidth.value;
551
+ } else if (translationX < -rightThresholdProp) {
552
+ toValue = -rightWidth.value;
553
+ }
554
+ } else if (rowState.value === 1) {
555
+ // Swiped to left
556
+ if (translationX > -leftThresholdProp) {
557
+ toValue = leftWidth.value;
558
+ }
559
+ } else {
560
+ // Swiped to right
561
+ if (translationX < rightThresholdProp) {
562
+ toValue = -rightWidth.value;
563
+ }
545
564
  }
546
- }
547
565
 
548
- animateRow(startOffsetX, toValue, velocityX / friction);
549
- };
566
+ animateRow(toValue, velocityX / friction);
567
+ },
568
+ [
569
+ animateRow,
570
+ friction,
571
+ leftThreshold,
572
+ leftWidth,
573
+ rightThreshold,
574
+ rightWidth,
575
+ rowState,
576
+ userDrag,
577
+ updateRightElementWidth,
578
+ ]
579
+ );
550
580
 
551
- const close = () => {
581
+ const close = useCallback(() => {
552
582
  'worklet';
553
- animateRow(calculateCurrentOffset(), 0);
554
- };
555
-
556
- const tapGesture = Gesture.Tap().onStart(() => {
557
- if (rowState.value !== 0) {
558
- close();
559
- }
560
- });
561
-
562
- const panGesture = Gesture.Pan()
563
- .onUpdate((event: GestureUpdateEvent<PanGestureHandlerEventPayload>) => {
564
- userDrag.value = event.translationX;
565
-
566
- const direction =
567
- rowState.value === -1
568
- ? 'right'
569
- : rowState.value === 1
570
- ? 'left'
571
- : event.translationX > 0
572
- ? 'left'
573
- : 'right';
574
-
575
- if (rowState.value === 0 && onSwipeableOpenStartDrag) {
576
- runOnJS(onSwipeableOpenStartDrag)(direction);
577
- } else if (rowState.value !== 0 && onSwipeableCloseStartDrag) {
578
- runOnJS(onSwipeableCloseStartDrag)(direction);
579
- }
580
- updateAnimatedEvent();
581
- })
582
- .onEnd(
583
- (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
584
- handleRelease(event);
585
- }
586
- );
587
-
588
- if (enableTrackpadTwoFingerGesture) {
589
- panGesture.enableTrackpadTwoFingerGesture(enableTrackpadTwoFingerGesture);
590
- }
591
-
592
- panGesture.activeOffsetX([
593
- -dragOffsetFromRightEdge,
594
- dragOffsetFromLeftEdge,
595
- ]);
596
- tapGesture.shouldCancelWhenOutside(true);
583
+ animateRow(0);
584
+ }, [animateRow]);
585
+
586
+ const dragStarted = useSharedValue<boolean>(false);
587
+
588
+ const tapGesture = useMemo(
589
+ () =>
590
+ Gesture.Tap()
591
+ .shouldCancelWhenOutside(true)
592
+ .onStart(() => {
593
+ if (rowState.value !== 0) {
594
+ close();
595
+ }
596
+ }),
597
+ [close, rowState]
598
+ );
597
599
 
598
- useImperativeHandle(ref, () => swipeableMethods.current, [
599
- swipeableMethods,
600
- ]);
600
+ const panGesture = useMemo(
601
+ () =>
602
+ Gesture.Pan()
603
+ .enabled(enabled !== false)
604
+ .enableTrackpadTwoFingerGesture(enableTrackpadTwoFingerGesture)
605
+ .activeOffsetX([-dragOffsetFromRightEdge, dragOffsetFromLeftEdge])
606
+ .onStart(() => {
607
+ updateRightElementWidth();
608
+ })
609
+ .onUpdate(
610
+ (event: GestureUpdateEvent<PanGestureHandlerEventPayload>) => {
611
+ userDrag.value = event.translationX;
612
+
613
+ const direction =
614
+ rowState.value === -1
615
+ ? SwipeDirection.RIGHT
616
+ : rowState.value === 1
617
+ ? SwipeDirection.LEFT
618
+ : event.translationX > 0
619
+ ? SwipeDirection.RIGHT
620
+ : SwipeDirection.LEFT;
621
+
622
+ if (!dragStarted.value) {
623
+ dragStarted.value = true;
624
+ if (rowState.value === 0 && onSwipeableOpenStartDrag) {
625
+ runOnJS(onSwipeableOpenStartDrag)(direction);
626
+ } else if (onSwipeableCloseStartDrag) {
627
+ runOnJS(onSwipeableCloseStartDrag)(direction);
628
+ }
629
+ }
630
+
631
+ updateAnimatedEvent();
632
+ }
633
+ )
634
+ .onEnd(
635
+ (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
636
+ handleRelease(event);
637
+ }
638
+ )
639
+ .onFinalize(() => {
640
+ dragStarted.value = false;
641
+ }),
642
+ [
643
+ dragOffsetFromLeftEdge,
644
+ dragOffsetFromRightEdge,
645
+ dragStarted,
646
+ enableTrackpadTwoFingerGesture,
647
+ enabled,
648
+ handleRelease,
649
+ onSwipeableCloseStartDrag,
650
+ onSwipeableOpenStartDrag,
651
+ rowState,
652
+ updateAnimatedEvent,
653
+ updateRightElementWidth,
654
+ userDrag,
655
+ ]
656
+ );
601
657
 
602
- panGesture.enabled(enabled !== false);
658
+ useImperativeHandle(ref, () => swipeableMethods, [swipeableMethods]);
603
659
 
604
660
  const animatedStyle = useAnimatedStyle(
605
661
  () => ({
@@ -615,8 +671,8 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
615
671
  {...remainingProps}
616
672
  onLayout={onRowLayout}
617
673
  style={[styles.container, containerStyle]}>
618
- {leftElement}
619
- {rightElement}
674
+ {leftElement()}
675
+ {rightElement()}
620
676
  <GestureDetector gesture={tapGesture} touchAction="pan-y">
621
677
  <Animated.View style={[animatedStyle, childrenContainerStyle]}>
622
678
  {children}