diff --git a/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.js b/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.jsindex 551ab92bf58db0b79d428dcd2f2df898ef686493..13db26d88bff975bc5d46bb6432cbf9fda3d489a 100644--- a/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.js+++ b/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.js@@ -34,11 +34,13 @@ const Swipeable = props => { enableTrackpadTwoFingerGesture = DEFAULT_ENABLE_TRACKING_TWO_FINGER_GESTURE, dragOffsetFromLeftEdge = DEFAULT_DRAG_OFFSET, dragOffsetFromRightEdge = DEFAULT_DRAG_OFFSET,+ failOffsetY, friction = DEFAULT_FRICTION, overshootFriction = DEFAULT_OVERSHOOT_FRICTION, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, onSwipeableWillOpen,+ onSwipeableRelease, onSwipeableWillClose, onSwipeableOpen, onSwipeableClose,@@ -248,8 +250,13 @@ const Swipeable = props => { toValue = -rightWidth.value; } }+ // Let a UI-thread full-swipe commit take over before the return spring.+ if (onSwipeableRelease?.(appliedTranslation)) {+ return;+ }+ animateRow(toValue, velocityX / friction);- }, [animateRow, friction, leftThreshold, leftWidth, rightThreshold, rightWidth, rowState, userDrag]);+ }, [animateRow, friction, leftThreshold, leftWidth, rightThreshold, rightWidth, rowState, userDrag, onSwipeableRelease, appliedTranslation]); const close = (0, _react.useCallback)(() => { 'worklet'; @@ -285,11 +292,14 @@ const Swipeable = props => { }).onFinalize(() => { dragStarted.value = false; });+ if (failOffsetY !== undefined) {+ pan.failOffsetY(failOffsetY);+ } Object.entries(relationProps).forEach(([relationName, relation]) => { (0, _utils.applyRelationProp)(pan, relationName, relation); }); return pan;- }, [enabled, hitSlop, enableTrackpadTwoFingerGesture, dragOffsetFromRightEdge, dragOffsetFromLeftEdge, updateElementWidths, relationProps, userDrag, rowState, dragStarted, updateAnimatedEvent, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, handleRelease]);+ }, [enabled, hitSlop, enableTrackpadTwoFingerGesture, dragOffsetFromRightEdge, dragOffsetFromLeftEdge, failOffsetY, updateElementWidths, relationProps, userDrag, rowState, dragStarted, updateAnimatedEvent, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, handleRelease]); (0, _react.useImperativeHandle)(ref, () => swipeableMethods, [swipeableMethods]); const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => ({ transform: [{diff --git a/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js b/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.jsindex a2835d5416ffd5cf9a04e98774516b9e6569691e..055afce410d6eb655532e7a09371dfc22eae5e9c 100644--- a/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js+++ b/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js@@ -29,11 +29,13 @@ const Swipeable = props => { enableTrackpadTwoFingerGesture = DEFAULT_ENABLE_TRACKING_TWO_FINGER_GESTURE, dragOffsetFromLeftEdge = DEFAULT_DRAG_OFFSET, dragOffsetFromRightEdge = DEFAULT_DRAG_OFFSET,+ failOffsetY, friction = DEFAULT_FRICTION, overshootFriction = DEFAULT_OVERSHOOT_FRICTION, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, onSwipeableWillOpen,+ onSwipeableRelease, onSwipeableWillClose, onSwipeableOpen, onSwipeableClose,@@ -243,8 +245,13 @@ const Swipeable = props => { toValue = -rightWidth.value; } }+ // Let a UI-thread full-swipe commit take over before the return spring.+ if (onSwipeableRelease?.(appliedTranslation)) {+ return;+ }+ animateRow(toValue, velocityX / friction);- }, [animateRow, friction, leftThreshold, leftWidth, rightThreshold, rightWidth, rowState, userDrag]);+ }, [animateRow, friction, leftThreshold, leftWidth, rightThreshold, rightWidth, rowState, userDrag, onSwipeableRelease, appliedTranslation]); const close = useCallback(() => { 'worklet'; @@ -280,11 +287,14 @@ const Swipeable = props => { }).onFinalize(() => { dragStarted.value = false; });+ if (failOffsetY !== undefined) {+ pan.failOffsetY(failOffsetY);+ } Object.entries(relationProps).forEach(([relationName, relation]) => { applyRelationProp(pan, relationName, relation); }); return pan;- }, [enabled, hitSlop, enableTrackpadTwoFingerGesture, dragOffsetFromRightEdge, dragOffsetFromLeftEdge, updateElementWidths, relationProps, userDrag, rowState, dragStarted, updateAnimatedEvent, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, handleRelease]);+ }, [enabled, hitSlop, enableTrackpadTwoFingerGesture, dragOffsetFromRightEdge, dragOffsetFromLeftEdge, failOffsetY, updateElementWidths, relationProps, userDrag, rowState, dragStarted, updateAnimatedEvent, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, handleRelease]); useImperativeHandle(ref, () => swipeableMethods, [swipeableMethods]); const animatedStyle = useAnimatedStyle(() => ({ transform: [{diff --git a/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts b/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.tsindex ac8b76830d468edfbc29052b452a36221323c3de..6985d54d9d359e825a5a0e6078bb113204e2807b 100644--- a/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts+++ b/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts@@ -64,6 +64,13 @@ export interface SwipeableProps { * a swipe. The default value is 10. */ dragOffsetFromRightEdge?: number;+ /**+ * Range along the Y axis (in points) that, once exceeded before the swipe+ * activates, makes the pan fail. Lets vertically-dominant pans (list+ * scrolling, especially trackpad two-finger pans) skip the swipe entirely.+ * Passed straight to the underlying Pan gesture's `failOffsetY`.+ */+ failOffsetY?: number | [start: number, end: number]; /** * Value indicating if the swipeable panel can be pulled further than the left * actions panel's width. It is set to true by default as long as the left@@ -90,6 +97,13 @@ export interface SwipeableProps { * Called when action panel is closed. */ onSwipeableClose?: (direction: SwipeDirection.LEFT | SwipeDirection.RIGHT) => void;+ /**+ * UI-thread worklet called on release before the built-in spring starts.+ * Return true to consume the release and take ownership of translation.+ * The caller must reset or remove the row after its custom animation.+ */+ onSwipeableRelease?: (translation: SharedValue<number>) => boolean;+ /** * Called when action panel starts animating on open (either right or left). */diff --git a/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx b/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsxindex b6134c908624adc590ebd264e90e558b88e235d5..fa4b1c1749bd0e86cbf5f5546f295d8099e1a481 100644--- a/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx+++ b/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx@@ -58,11 +58,13 @@ const Swipeable = (props: SwipeableProps) => { enableTrackpadTwoFingerGesture = DEFAULT_ENABLE_TRACKING_TWO_FINGER_GESTURE, dragOffsetFromLeftEdge = DEFAULT_DRAG_OFFSET, dragOffsetFromRightEdge = DEFAULT_DRAG_OFFSET,+ failOffsetY, friction = DEFAULT_FRICTION, overshootFriction = DEFAULT_OVERSHOOT_FRICTION, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, onSwipeableWillOpen,+ onSwipeableRelease, onSwipeableWillClose, onSwipeableOpen, onSwipeableClose,@@ -457,6 +459,11 @@ const Swipeable = (props: SwipeableProps) => { } } + // Let a UI-thread full-swipe commit take over before the return spring.+ if (onSwipeableRelease?.(appliedTranslation)) {+ return;+ }+ animateRow(toValue, velocityX / friction); }, [@@ -468,6 +475,8 @@ const Swipeable = (props: SwipeableProps) => { rightWidth, rowState, userDrag,+ onSwipeableRelease,+ appliedTranslation, ] ); @@ -537,6 +546,10 @@ const Swipeable = (props: SwipeableProps) => { dragStarted.value = false; }); + if (failOffsetY !== undefined) {+ pan.failOffsetY(failOffsetY);+ }+ Object.entries(relationProps).forEach(([relationName, relation]) => { applyRelationProp( pan,@@ -552,6 +565,7 @@ const Swipeable = (props: SwipeableProps) => { enableTrackpadTwoFingerGesture, dragOffsetFromRightEdge, dragOffsetFromLeftEdge,+ failOffsetY, updateElementWidths, relationProps, userDrag,diff --git a/src/components/ReanimatedSwipeable/ReanimatedSwipeableProps.ts b/src/components/ReanimatedSwipeable/ReanimatedSwipeableProps.tsindex 0c0e517e0d340faf50ff78c3d48e7a2bbcf808ec..3ab14d240da346b1927792e480f5b1e902b03bf8 100644--- a/src/components/ReanimatedSwipeable/ReanimatedSwipeableProps.ts+++ b/src/components/ReanimatedSwipeable/ReanimatedSwipeableProps.ts@@ -77,6 +77,14 @@ export interface SwipeableProps { */ dragOffsetFromRightEdge?: number; + /**+ * Range along the Y axis (in points) that, once exceeded before the swipe+ * activates, makes the pan fail. Lets vertically-dominant pans (list+ * scrolling, especially trackpad two-finger pans) skip the swipe entirely.+ * Passed straight to the underlying Pan gesture's `failOffsetY`.+ */+ failOffsetY?: number | [start: number, end: number];+ /** * Value indicating if the swipeable panel can be pulled further than the left * actions panel's width. It is set to true by default as long as the left@@ -112,6 +120,13 @@ export interface SwipeableProps { direction: SwipeDirection.LEFT | SwipeDirection.RIGHT ) => void; + /**+ * UI-thread worklet called on release before the built-in spring starts.+ * Return true to consume the release and take ownership of translation.+ * The caller must reset or remove the row after its custom animation.+ */+ onSwipeableRelease?: (translation: SharedValue<number>) => boolean;+ /** * Called when action panel starts animating on open (either right or left). */
diff --git a/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.js b/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.js
index 551ab92bf58db0b79d428dcd2f2df898ef686493..13db26d88bff975bc5d46bb6432cbf9fda3d489a 100644
--- a/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.js
+++ b/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.js
@@ -34,11 +34,13 @@ const Swipeable = props => {
enableTrackpadTwoFingerGesture = DEFAULT_ENABLE_TRACKING_TWO_FINGER_GESTURE,
dragOffsetFromLeftEdge = DEFAULT_DRAG_OFFSET,
dragOffsetFromRightEdge = DEFAULT_DRAG_OFFSET,
+ failOffsetY,
friction = DEFAULT_FRICTION,
overshootFriction = DEFAULT_OVERSHOOT_FRICTION,
onSwipeableOpenStartDrag,
onSwipeableCloseStartDrag,
onSwipeableWillOpen,
+ onSwipeableRelease,
onSwipeableWillClose,
onSwipeableOpen,
onSwipeableClose,
@@ -248,8 +250,13 @@ const Swipeable = props => {
toValue = -rightWidth.value;
}
}
+ // Let a UI-thread full-swipe commit take over before the return spring.
+ if (onSwipeableRelease?.(appliedTranslation)) {
+ return;
+ }
+
animateRow(toValue, velocityX / friction);
- }, [animateRow, friction, leftThreshold, leftWidth, rightThreshold, rightWidth, rowState, userDrag]);
+ }, [animateRow, friction, leftThreshold, leftWidth, rightThreshold, rightWidth, rowState, userDrag, onSwipeableRelease, appliedTranslation]);
const close = (0, _react.useCallback)(() => {
'worklet';
@@ -285,11 +292,14 @@ const Swipeable = props => {
}).onFinalize(() => {
dragStarted.value = false;
});
+ if (failOffsetY !== undefined) {
+ pan.failOffsetY(failOffsetY);
+ }
Object.entries(relationProps).forEach(([relationName, relation]) => {
(0, _utils.applyRelationProp)(pan, relationName, relation);
});
return pan;
- }, [enabled, hitSlop, enableTrackpadTwoFingerGesture, dragOffsetFromRightEdge, dragOffsetFromLeftEdge, updateElementWidths, relationProps, userDrag, rowState, dragStarted, updateAnimatedEvent, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, handleRelease]);
+ }, [enabled, hitSlop, enableTrackpadTwoFingerGesture, dragOffsetFromRightEdge, dragOffsetFromLeftEdge, failOffsetY, updateElementWidths, relationProps, userDrag, rowState, dragStarted, updateAnimatedEvent, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, handleRelease]);
(0, _react.useImperativeHandle)(ref, () => swipeableMethods, [swipeableMethods]);
const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => ({
transform: [{
diff --git a/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js b/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js
index a2835d5416ffd5cf9a04e98774516b9e6569691e..055afce410d6eb655532e7a09371dfc22eae5e9c 100644
--- a/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js
+++ b/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js
@@ -29,11 +29,13 @@ const Swipeable = props => {
enableTrackpadTwoFingerGesture = DEFAULT_ENABLE_TRACKING_TWO_FINGER_GESTURE,
dragOffsetFromLeftEdge = DEFAULT_DRAG_OFFSET,
dragOffsetFromRightEdge = DEFAULT_DRAG_OFFSET,
+ failOffsetY,
friction = DEFAULT_FRICTION,
overshootFriction = DEFAULT_OVERSHOOT_FRICTION,
onSwipeableOpenStartDrag,
onSwipeableCloseStartDrag,
onSwipeableWillOpen,
+ onSwipeableRelease,
onSwipeableWillClose,
onSwipeableOpen,
onSwipeableClose,
@@ -243,8 +245,13 @@ const Swipeable = props => {
toValue = -rightWidth.value;
}
}
+ // Let a UI-thread full-swipe commit take over before the return spring.
+ if (onSwipeableRelease?.(appliedTranslation)) {
+ return;
+ }
+
animateRow(toValue, velocityX / friction);
- }, [animateRow, friction, leftThreshold, leftWidth, rightThreshold, rightWidth, rowState, userDrag]);
+ }, [animateRow, friction, leftThreshold, leftWidth, rightThreshold, rightWidth, rowState, userDrag, onSwipeableRelease, appliedTranslation]);
const close = useCallback(() => {
'worklet';
@@ -280,11 +287,14 @@ const Swipeable = props => {
}).onFinalize(() => {
dragStarted.value = false;
});
+ if (failOffsetY !== undefined) {
+ pan.failOffsetY(failOffsetY);
+ }
Object.entries(relationProps).forEach(([relationName, relation]) => {
applyRelationProp(pan, relationName, relation);
});
return pan;
- }, [enabled, hitSlop, enableTrackpadTwoFingerGesture, dragOffsetFromRightEdge, dragOffsetFromLeftEdge, updateElementWidths, relationProps, userDrag, rowState, dragStarted, updateAnimatedEvent, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, handleRelease]);
+ }, [enabled, hitSlop, enableTrackpadTwoFingerGesture, dragOffsetFromRightEdge, dragOffsetFromLeftEdge, failOffsetY, updateElementWidths, relationProps, userDrag, rowState, dragStarted, updateAnimatedEvent, onSwipeableOpenStartDrag, onSwipeableCloseStartDrag, handleRelease]);
useImperativeHandle(ref, () => swipeableMethods, [swipeableMethods]);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{
diff --git a/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts b/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts
index ac8b76830d468edfbc29052b452a36221323c3de..6985d54d9d359e825a5a0e6078bb113204e2807b 100644
--- a/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts
+++ b/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeableProps.d.ts
@@ -64,6 +64,13 @@ export interface SwipeableProps {
* a swipe. The default value is 10.
*/
dragOffsetFromRightEdge?: number;
+ /**
+ * Range along the Y axis (in points) that, once exceeded before the swipe
+ * activates, makes the pan fail. Lets vertically-dominant pans (list
+ * scrolling, especially trackpad two-finger pans) skip the swipe entirely.
+ * Passed straight to the underlying Pan gesture's `failOffsetY`.
+ */
+ failOffsetY?: number | [start: number, end: number];
/**
* Value indicating if the swipeable panel can be pulled further than the left
* actions panel's width. It is set to true by default as long as the left
@@ -90,6 +97,13 @@ export interface SwipeableProps {
* Called when action panel is closed.
*/
onSwipeableClose?: (direction: SwipeDirection.LEFT | SwipeDirection.RIGHT) => void;
+ /**
+ * UI-thread worklet called on release before the built-in spring starts.
+ * Return true to consume the release and take ownership of translation.
+ * The caller must reset or remove the row after its custom animation.
+ */
+ onSwipeableRelease?: (translation: SharedValue<number>) => boolean;
+
/**
* Called when action panel starts animating on open (either right or left).
*/
diff --git a/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx b/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx
index b6134c908624adc590ebd264e90e558b88e235d5..fa4b1c1749bd0e86cbf5f5546f295d8099e1a481 100644
--- a/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx
+++ b/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx
@@ -58,11 +58,13 @@ const Swipeable = (props: SwipeableProps) => {
enableTrackpadTwoFingerGesture = DEFAULT_ENABLE_TRACKING_TWO_FINGER_GESTURE,
dragOffsetFromLeftEdge = DEFAULT_DRAG_OFFSET,
dragOffsetFromRightEdge = DEFAULT_DRAG_OFFSET,
+ failOffsetY,
friction = DEFAULT_FRICTION,
overshootFriction = DEFAULT_OVERSHOOT_FRICTION,
onSwipeableOpenStartDrag,
onSwipeableCloseStartDrag,
onSwipeableWillOpen,
+ onSwipeableRelease,
onSwipeableWillClose,
onSwipeableOpen,
onSwipeableClose,
@@ -457,6 +459,11 @@ const Swipeable = (props: SwipeableProps) => {
}
}
+ // Let a UI-thread full-swipe commit take over before the return spring.
+ if (onSwipeableRelease?.(appliedTranslation)) {
+ return;
+ }
+
animateRow(toValue, velocityX / friction);
},
[
@@ -468,6 +475,8 @@ const Swipeable = (props: SwipeableProps) => {
rightWidth,
rowState,
userDrag,
+ onSwipeableRelease,
+ appliedTranslation,
]
);
@@ -537,6 +546,10 @@ const Swipeable = (props: SwipeableProps) => {
dragStarted.value = false;
});
+ if (failOffsetY !== undefined) {
+ pan.failOffsetY(failOffsetY);
+ }
+
Object.entries(relationProps).forEach(([relationName, relation]) => {
applyRelationProp(
pan,
@@ -552,6 +565,7 @@ const Swipeable = (props: SwipeableProps) => {
enableTrackpadTwoFingerGesture,
dragOffsetFromRightEdge,
dragOffsetFromLeftEdge,
+ failOffsetY,
updateElementWidths,
relationProps,
userDrag,
diff --git a/src/components/ReanimatedSwipeable/ReanimatedSwipeableProps.ts b/src/components/ReanimatedSwipeable/ReanimatedSwipeableProps.ts
index 0c0e517e0d340faf50ff78c3d48e7a2bbcf808ec..3ab14d240da346b1927792e480f5b1e902b03bf8 100644
--- a/src/components/ReanimatedSwipeable/ReanimatedSwipeableProps.ts
+++ b/src/components/ReanimatedSwipeable/ReanimatedSwipeableProps.ts
@@ -77,6 +77,14 @@ export interface SwipeableProps {
*/
dragOffsetFromRightEdge?: number;
+ /**
+ * Range along the Y axis (in points) that, once exceeded before the swipe
+ * activates, makes the pan fail. Lets vertically-dominant pans (list
+ * scrolling, especially trackpad two-finger pans) skip the swipe entirely.
+ * Passed straight to the underlying Pan gesture's `failOffsetY`.
+ */
+ failOffsetY?: number | [start: number, end: number];
+
/**
* Value indicating if the swipeable panel can be pulled further than the left
* actions panel's width. It is set to true by default as long as the left
@@ -112,6 +120,13 @@ export interface SwipeableProps {
direction: SwipeDirection.LEFT | SwipeDirection.RIGHT
) => void;
+ /**
+ * UI-thread worklet called on release before the built-in spring starts.
+ * Return true to consume the release and take ownership of translation.
+ * The caller must reset or remove the row after its custom animation.
+ */
+ onSwipeableRelease?: (translation: SharedValue<number>) => boolean;
+
/**
* Called when action panel starts animating on open (either right or left).
*/