react-native-gesture-handler 2.20.2 → 2.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. package/RNGestureHandler.podspec +9 -0
  2. package/android/build.gradle +19 -0
  3. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +6 -2
  4. package/android/src/main/java/com/swmansion/gesturehandler/core/RotationGestureDetector.kt +53 -21
  5. package/apple/RNGestureHandlerPointerTracker.m +4 -2
  6. package/lib/commonjs/RNGestureHandlerModule.web.js +15 -2
  7. package/lib/commonjs/RNGestureHandlerModule.web.js.map +1 -1
  8. package/lib/commonjs/components/Pressable/Pressable.js +2 -6
  9. package/lib/commonjs/components/Pressable/Pressable.js.map +1 -1
  10. package/lib/commonjs/components/ReanimatedDrawerLayout.js +389 -0
  11. package/lib/commonjs/components/ReanimatedDrawerLayout.js.map +1 -0
  12. package/lib/commonjs/components/ReanimatedSwipeable.js +117 -148
  13. package/lib/commonjs/components/ReanimatedSwipeable.js.map +1 -1
  14. package/lib/commonjs/findNodeHandle.js +12 -0
  15. package/lib/commonjs/findNodeHandle.js.map +1 -0
  16. package/lib/commonjs/findNodeHandle.web.js +40 -0
  17. package/lib/commonjs/findNodeHandle.web.js.map +1 -0
  18. package/lib/commonjs/handlers/createHandler.js +4 -2
  19. package/lib/commonjs/handlers/createHandler.js.map +1 -1
  20. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js +51 -0
  21. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -0
  22. package/lib/commonjs/handlers/gestures/GestureDetector/index.js +3 -1
  23. package/lib/commonjs/handlers/gestures/GestureDetector/index.js.map +1 -1
  24. package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js +5 -3
  25. package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
  26. package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js +4 -2
  27. package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
  28. package/lib/commonjs/web/handlers/GestureHandler.js +4 -0
  29. package/lib/commonjs/web/handlers/GestureHandler.js.map +1 -1
  30. package/lib/commonjs/web/handlers/PanGestureHandler.js +59 -0
  31. package/lib/commonjs/web/handlers/PanGestureHandler.js.map +1 -1
  32. package/lib/commonjs/web/interfaces.js +10 -1
  33. package/lib/commonjs/web/interfaces.js.map +1 -1
  34. package/lib/commonjs/web/tools/EventManager.js +6 -0
  35. package/lib/commonjs/web/tools/EventManager.js.map +1 -1
  36. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js +1 -3
  37. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  38. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js +5 -2
  39. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  40. package/lib/commonjs/web/tools/KeyboardEventManager.js +2 -2
  41. package/lib/commonjs/web/tools/KeyboardEventManager.js.map +1 -1
  42. package/lib/commonjs/web/tools/PointerTracker.js +6 -30
  43. package/lib/commonjs/web/tools/PointerTracker.js.map +1 -1
  44. package/lib/commonjs/web/tools/WheelEventManager.js +74 -0
  45. package/lib/commonjs/web/tools/WheelEventManager.js.map +1 -0
  46. package/lib/commonjs/web/utils.js +16 -0
  47. package/lib/commonjs/web/utils.js.map +1 -1
  48. package/lib/module/RNGestureHandlerModule.web.js +16 -3
  49. package/lib/module/RNGestureHandlerModule.web.js.map +1 -1
  50. package/lib/module/components/Pressable/Pressable.js +2 -6
  51. package/lib/module/components/Pressable/Pressable.js.map +1 -1
  52. package/lib/module/components/ReanimatedDrawerLayout.js +365 -0
  53. package/lib/module/components/ReanimatedDrawerLayout.js.map +1 -0
  54. package/lib/module/components/ReanimatedSwipeable.js +119 -145
  55. package/lib/module/components/ReanimatedSwipeable.js.map +1 -1
  56. package/lib/module/findNodeHandle.js +3 -0
  57. package/lib/module/findNodeHandle.js.map +1 -0
  58. package/lib/module/findNodeHandle.web.js +32 -0
  59. package/lib/module/findNodeHandle.web.js.map +1 -0
  60. package/lib/module/handlers/createHandler.js +2 -1
  61. package/lib/module/handlers/createHandler.js.map +1 -1
  62. package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js +34 -0
  63. package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -0
  64. package/lib/module/handlers/gestures/GestureDetector/index.js +2 -1
  65. package/lib/module/handlers/gestures/GestureDetector/index.js.map +1 -1
  66. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js +2 -2
  67. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
  68. package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js +1 -1
  69. package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
  70. package/lib/module/web/handlers/GestureHandler.js +4 -0
  71. package/lib/module/web/handlers/GestureHandler.js.map +1 -1
  72. package/lib/module/web/handlers/PanGestureHandler.js +58 -0
  73. package/lib/module/web/handlers/PanGestureHandler.js.map +1 -1
  74. package/lib/module/web/interfaces.js +8 -0
  75. package/lib/module/web/interfaces.js.map +1 -1
  76. package/lib/module/web/tools/EventManager.js +6 -0
  77. package/lib/module/web/tools/EventManager.js.map +1 -1
  78. package/lib/module/web/tools/GestureHandlerOrchestrator.js +1 -3
  79. package/lib/module/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  80. package/lib/module/web/tools/GestureHandlerWebDelegate.js +3 -1
  81. package/lib/module/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  82. package/lib/module/web/tools/KeyboardEventManager.js +2 -2
  83. package/lib/module/web/tools/KeyboardEventManager.js.map +1 -1
  84. package/lib/module/web/tools/PointerTracker.js +6 -30
  85. package/lib/module/web/tools/PointerTracker.js.map +1 -1
  86. package/lib/module/web/tools/WheelEventManager.js +60 -0
  87. package/lib/module/web/tools/WheelEventManager.js.map +1 -0
  88. package/lib/module/web/utils.js +15 -0
  89. package/lib/module/web/utils.js.map +1 -1
  90. package/lib/typescript/components/ReanimatedDrawerLayout.d.ts +162 -0
  91. package/lib/typescript/components/ReanimatedSwipeable.d.ts +22 -16
  92. package/lib/typescript/findNodeHandle.d.ts +2 -0
  93. package/lib/typescript/findNodeHandle.web.d.ts +2 -0
  94. package/lib/typescript/handlers/gestures/GestureDetector/Wrap.web.d.ts +7 -0
  95. package/lib/typescript/web/handlers/GestureHandler.d.ts +2 -1
  96. package/lib/typescript/web/handlers/PanGestureHandler.d.ts +5 -0
  97. package/lib/typescript/web/interfaces.d.ts +16 -0
  98. package/lib/typescript/web/tools/EventManager.d.ts +2 -0
  99. package/lib/typescript/web/tools/PointerTracker.d.ts +2 -8
  100. package/lib/typescript/web/tools/WheelEventManager.d.ts +11 -0
  101. package/lib/typescript/web/utils.d.ts +2 -1
  102. package/package.json +2 -2
  103. package/src/RNGestureHandlerModule.web.ts +23 -4
  104. package/src/components/Pressable/Pressable.tsx +2 -6
  105. package/src/components/ReanimatedDrawerLayout.tsx +741 -0
  106. package/src/components/ReanimatedSwipeable.tsx +361 -305
  107. package/src/findNodeHandle.ts +3 -0
  108. package/src/findNodeHandle.web.ts +35 -0
  109. package/src/handlers/createHandler.tsx +2 -1
  110. package/src/handlers/gestures/GestureDetector/Wrap.web.tsx +44 -0
  111. package/src/handlers/gestures/GestureDetector/index.tsx +2 -1
  112. package/src/handlers/gestures/GestureDetector/useDetectorUpdater.ts +1 -1
  113. package/src/handlers/gestures/GestureDetector/useViewRefHandler.ts +1 -1
  114. package/src/web/handlers/GestureHandler.ts +5 -1
  115. package/src/web/handlers/PanGestureHandler.ts +69 -1
  116. package/src/web/interfaces.ts +17 -0
  117. package/src/web/tools/EventManager.ts +4 -0
  118. package/src/web/tools/GestureHandlerOrchestrator.ts +1 -7
  119. package/src/web/tools/GestureHandlerWebDelegate.ts +3 -1
  120. package/src/web/tools/KeyboardEventManager.ts +2 -2
  121. package/src/web/tools/PointerTracker.ts +6 -28
  122. package/src/web/tools/WheelEventManager.ts +48 -0
  123. 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}