Skip to content

Commit bfae4a2

Browse files
authored
Rename pressOutAfterAnimation to immediatePressOut (#4594)
## Description The prop that holds `onPressOut` for a platform-deferred quick tap is renamed to `immediatePressOut` and its meaning inverted, so the hold is the default and `true` opts out of it. Defaults stay behaviour-compatible with RN: - `Pressable`: `false`, `onPressOut` is held so `pressed` lasts at least 130 ms (RN `minPressDuration`). - `Touchable`: `true`, no hold (RN `TouchableOpacity`/`TouchableHighlight` pass `minPressDuration: 0`). - native button / codegen: `true`, so a button rendered without the prop keeps its current timing. Also includes a docs touch-up: the `Available from` badge next to a heading is restyled as an outline chip aligned to the right, so it no longer reads as a third platform pill, and both pages get `version="3.4.0"` on the new prop. ## Test plan Read docs 🤓
1 parent 6c2a8df commit bfae4a2

14 files changed

Lines changed: 67 additions & 50 deletions

File tree

‎packages/docs-gesture-handler/docs/components/pressable.mdx‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Either view styles or a function that receives a boolean reflecting whether the
5252
onPress?: null | ((event: PressableEvent) => void);
5353
```
5454

55-
Called when a single tap gesture is detected. It follows [`onPressOut`](#onpressout), unless [`pressOutAfterAnimation`](#pressoutafteranimation) delays `onPressOut`. Details about the event object can be found in the [PressableEvent](#pressableevent) section below.
55+
Called when a single tap gesture is detected. It follows [`onPressOut`](#onpressout), unless [`immediatePressOut`](#immediatepressout) is off and the platform deferred a quick tap. Details about the event object can be found in the [PressableEvent](#pressableevent) section below.
5656

5757
### onPressIn
5858

@@ -68,7 +68,7 @@ Called before `onPress` when a touch is engaged. Details about the event object
6868
onPressOut?: null | ((event: PressableEvent) => void);
6969
```
7070

71-
Called when a touch is released. When [`pressOutAfterAnimation`](#pressoutafteranimation) is set to `true`, a quick tap the platform deferred calls it after [`onPress`](#onpress). Details about the event object can be found in the [PressableEvent](#pressableevent) section below.
71+
Called when a touch is released. Unless [`immediatePressOut`](#immediatepressout) is set to `true`, a quick tap the platform deferred calls it after [`onPress`](#onpress). Details about the event object can be found in the [PressableEvent](#pressableevent) section below.
7272

7373
### onLongPress
7474

@@ -201,19 +201,19 @@ needsOffscreenAlphaCompositing?: boolean;
201201

202202
Whether the view should render with an offscreen alpha-compositing buffer when its `opacity` is less than `1`. Defaults to `false`.
203203

204-
<Badges platforms={['android', 'ios']}>
205-
### pressOutAfterAnimation [A][I]
204+
<Badges platforms={['android', 'ios']} version="3.4.0">
205+
### immediatePressOut [A][I]
206206
</Badges>
207207

208208
```ts
209-
pressOutAfterAnimation?: boolean;
209+
immediatePressOut?: boolean;
210210
```
211211

212-
In some cases the platform defers the press, so a quick tap calls `onPressIn` and `onPressOut` almost at the same time and the `pressed` state never shows up.
212+
In some cases the platform defers the press, so a quick tap would call `onPressIn` and `onPressOut` almost at the same time and the `pressed` state would never show up. By default, `onPressOut` for such a tap is held back so that `pressed` lasts at least 130 ms, matching React Native's `Pressable`. `onPress` is never delayed, so on such a tap it is called before `onPressOut`. Longer presses and presses that were not deferred by the platform keep the usual order.
213213

214-
When set to `true`, `onPressOut` for such a tap is held back so that `pressed` lasts at least 130 ms, matching React Native's `Pressable`. `onPress` is never delayed, so on such a tap it is called before `onPressOut`. Longer presses and presses that were not deferred by the platform keep the usual order.
214+
When set to `true`, `onPressOut` is called as soon as the press ends, even for such a tap.
215215

216-
By default set to `true`. Has no effect on web, where presses are never deferred, or when `simultaneousWith`, `requireToFail` or `block` is passed.
216+
By default set to `false`. Has no effect on web, where presses are never deferred, or when `simultaneousWith`, `requireToFail` or `block` is passed.
217217

218218
### testOnly_pressed
219219

‎packages/docs-gesture-handler/docs/components/touchable.mdx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -586,19 +586,19 @@ cancelOnLeave?: boolean;
586586

587587
Whether the touch should be canceled when the pointer leaves the component. By default set to `true`. On web this prop doesn't have any effect and behaves as if `true` was set.
588588

589-
<Badges platforms={['android', 'ios']}>
590-
### pressOutAfterAnimation [A][I]
589+
<Badges platforms={['android', 'ios']} version="3.4.0">
590+
### immediatePressOut [A][I]
591591
</Badges>
592592

593593
```ts
594-
pressOutAfterAnimation?: boolean;
594+
immediatePressOut?: boolean;
595595
```
596596

597597
In some cases the platform defers the press, so a quick tap calls `onPressIn` and `onPressOut` almost at the same time and feedback driven from these callbacks never shows up.
598598

599-
When set to `true`, `onPressOut` for such a tap is held back until the press-in animation (see [`animationDuration`](#animationduration)) finishes. `onPress` is never delayed, so on such a tap it is called before `onPressOut`. Longer presses and presses that were not deferred by the platform keep the usual order.
599+
When set to `false`, `onPressOut` for such a tap is held back until the press-in animation (see [`animationDuration`](#animationduration)) finishes. `onPress` is never delayed, so on such a tap it is called before `onPressOut`. Longer presses and presses that were not deferred by the platform keep the usual order.
600600

601-
By default set to `false`. Has no effect on web, where presses are never deferred.
601+
By default set to `true`. Has no effect on web, where presses are never deferred.
602602

603603
<Badges platforms={['android']}>
604604
### needsOffscreenAlphaCompositing [A]

‎packages/docs-gesture-handler/src/css/overrides.css‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,19 @@ footer
259259
align-items: center;
260260
}
261261
}
262+
263+
[class*='_headerWithBadges_'] [class*='_badge_'][class*='_versionBadge_'] {
264+
background: transparent;
265+
margin-left: auto;
266+
color: var(--swm-navy-light-60);
267+
border: 1px solid var(--swm-navy-light-20);
268+
font-weight: 400;
269+
padding: calc(0.25rem - 1px) 0.5rem;
270+
}
271+
272+
[data-theme='dark']
273+
[class*='_headerWithBadges_']
274+
[class*='_badge_'][class*='_versionBadge_'] {
275+
color: var(--swm-navy-light-40);
276+
border-color: var(--swm-light-off-navy);
277+
}

‎packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class RNGestureHandlerButtonViewManager :
7979
view.managedHandlerTestID = null
8080
view.managedHandlerHitSlop = null
8181
view.moduleId = null
82-
view.pressOutAfterAnimation = false
82+
view.immediatePressOut = true
8383
view.resetHoverState()
8484
// Has to come last — every setter above flags the view as needing a managed handler update.
8585
view.managedHandlerNeedsUpdate = false
@@ -358,9 +358,9 @@ class RNGestureHandlerButtonViewManager :
358358
view.longPressDuration = value
359359
}
360360

361-
@ReactProp(name = "pressOutAfterAnimation")
362-
override fun setPressOutAfterAnimation(view: ButtonViewGroup, value: Boolean) {
363-
view.pressOutAfterAnimation = value
361+
@ReactProp(name = "immediatePressOut")
362+
override fun setImmediatePressOut(view: ButtonViewGroup, value: Boolean) {
363+
view.immediatePressOut = value
364364
}
365365

366366
@ReactProp(name = "longPressAnimationOutDuration")
@@ -573,7 +573,7 @@ class RNGestureHandlerButtonViewManager :
573573
var tapAnimationInDuration: Int = 50
574574
var tapAnimationOutDuration: Int = 100
575575
var longPressDuration: Int = -1
576-
var pressOutAfterAnimation = false
576+
var immediatePressOut = true
577577
var longPressAnimationOutDuration: Int = -1
578578
get() = if (field < 0) tapAnimationOutDuration else field
579579
var activeOpacity: Float = 1.0f
@@ -616,7 +616,7 @@ class RNGestureHandlerButtonViewManager :
616616
private var pressInTimestamp = 0L
617617
private var pendingPressOut: Runnable? = null
618618

619-
// PressOut event held back by `pressOutAfterAnimation` (the animation has its own hold above).
619+
// PressOut event held back unless `immediatePressOut` (the animation has its own hold above).
620620
private var pendingPressOutEvent: Runnable? = null
621621

622622
// InteractionFinished that arrived while PressOut was held; delivered right after it.
@@ -881,13 +881,13 @@ class RNGestureHandlerButtonViewManager :
881881
eventDispatcher.dispatchEvent(event)
882882
}
883883

884-
// With `pressOutAfterAnimation`, a press the platform deferred (tap timeout inside a scrolling
884+
// Unless `immediatePressOut`, a press the platform deferred (tap timeout inside a scrolling
885885
// container) holds PressOut until the press-in animation has played, like `animatePressOut`
886886
// holds the animation. An immediate press keeps its native timing. Press is never delayed, so
887887
// on such taps PressOut follows it. The event is built now so it carries the release position.
888888
private fun dispatchPressOut(handler: NativeViewGestureHandler) {
889889
val remaining = tapAnimationInDuration - (SystemClock.uptimeMillis() - pressInTimestamp)
890-
if (!pressOutAfterAnimation || !pressDeferredByPlatform || remaining <= 0) {
890+
if (immediatePressOut || !pressDeferredByPlatform || remaining <= 0) {
891891
dispatchJSEvent(EventType.PressOut, handler)
892892
return
893893
}

‎packages/react-native-gesture-handler/apple/RNGestureHandlerButton.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ typedef NS_ENUM(NSInteger, RNGHButtonEventType) {
5151
@property (nonatomic, assign) NSInteger tapAnimationInDuration;
5252
@property (nonatomic, assign) NSInteger tapAnimationOutDuration;
5353
@property (nonatomic, assign) NSInteger longPressDuration;
54-
@property (nonatomic, assign) BOOL pressOutAfterAnimation;
54+
@property (nonatomic, assign) BOOL immediatePressOut;
5555
@property (nonatomic, assign) NSInteger longPressAnimationOutDuration;
5656
@property (nonatomic, assign) CGFloat activeOpacity;
5757
@property (nonatomic, assign) CGFloat defaultOpacity;

‎packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ @implementation RNGestureHandlerButton {
5151
UIEdgeInsets _underlayBorderInsets; // border widths for padding-box inset
5252
NSTimeInterval _pressInTimestamp;
5353
dispatch_block_t _pendingPressOutBlock;
54-
// `onPressOut` held back by `pressOutAfterAnimation` (the press-out animation has its own block above).
54+
// `onPressOut` held back unless `immediatePressOut` (the press-out animation has its own block above).
5555
// The payload is kept apart from the block: a cancelled dispatch block never runs its body.
5656
dispatch_block_t _pendingPressOutEventBlock;
5757
RNGestureHandlerEventExtraData *_pendingPressOutExtraData;
@@ -109,7 +109,7 @@ - (void)commonInit
109109
_tapAnimationInDuration = 50;
110110
_tapAnimationOutDuration = 100;
111111
_longPressDuration = -1;
112-
_pressOutAfterAnimation = NO;
112+
_immediatePressOut = YES;
113113
_pressDeferredByPlatform = NO;
114114
_longPressAnimationOutDuration = -1;
115115
_activeOpacity = 1.0;
@@ -763,14 +763,14 @@ - (BOOL)isInsideDelayingScrollView
763763
}
764764
#endif
765765

766-
// With `pressOutAfterAnimation`, a press UIKit deferred (`delaysContentTouches` in a scroll view)
766+
// Unless `immediatePressOut`, a press UIKit deferred (`delaysContentTouches` in a scroll view)
767767
// holds `onPressOut` until the press-in animation has played, like `handleAnimatePressOut` holds
768768
// the animation. An immediate press keeps its native timing. `onPress` is never delayed, so on
769769
// such taps `onPressOut` follows it.
770770
- (void)dispatchPressOutWithExtraData:(RNGestureHandlerEventExtraData *)extraData
771771
{
772772
NSTimeInterval remaining = _tapAnimationInDuration - (CACurrentMediaTime() - _pressInTimestamp) * 1000.0;
773-
if (!_pressOutAfterAnimation || !_pressDeferredByPlatform || remaining <= 0) {
773+
if (_immediatePressOut || !_pressDeferredByPlatform || remaining <= 0) {
774774
[self dispatchButtonEvent:RNGHButtonEventTypePressOut withExtraData:extraData];
775775
return;
776776
}
@@ -1385,7 +1385,7 @@ - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
13851385
}
13861386

13871387
_isTouchInsideBounds = YES;
1388-
_pressDeferredByPlatform = _pressOutAfterAnimation && [self isInsideDelayingScrollView];
1388+
_pressDeferredByPlatform = !_immediatePressOut && [self isInsideDelayingScrollView];
13891389
// A pencil's hover-out arrives just before touch-down but only schedules the
13901390
// clear, so `_isHovered` still reflects the open hover. Under Reduce Motion
13911391
// that delay is zero and the block can land first — the press then brackets

‎packages/react-native-gesture-handler/apple/RNGestureHandlerButtonComponentView.mm‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
546546
_buttonView.tapAnimationInDuration = newProps.tapAnimationInDuration > 0 ? newProps.tapAnimationInDuration : 0;
547547
_buttonView.tapAnimationOutDuration = newProps.tapAnimationOutDuration > 0 ? newProps.tapAnimationOutDuration : 0;
548548
_buttonView.longPressDuration = newProps.longPressDuration;
549-
_buttonView.pressOutAfterAnimation = newProps.pressOutAfterAnimation;
549+
_buttonView.immediatePressOut = newProps.immediatePressOut;
550550
_buttonView.longPressAnimationOutDuration = newProps.longPressAnimationOutDuration;
551551
_buttonView.activeOpacity = newProps.activeOpacity;
552552
_buttonView.defaultOpacity = newProps.defaultOpacity;

‎packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,12 @@ export interface ButtonProps extends ViewProps, AccessibilityProps {
146146
longPressDuration?: number | undefined;
147147

148148
/**
149-
* When the platform deferred the press (e.g. the tap timeout inside a
150-
* scrolling container), hold the press-out event until the press-in
151-
* animation has played, so feedback driven from the callbacks stays
152-
* visible. The press event is not delayed.
149+
* Set to `false` to hold the press-out event for a press the platform
150+
* deferred (e.g. the tap timeout inside a scrolling container) until the
151+
* press-in animation has played, so feedback driven from the callbacks
152+
* stays visible. The press event is not delayed.
153153
*/
154-
pressOutAfterAnimation?: boolean | undefined;
154+
immediatePressOut?: boolean | undefined;
155155

156156
/**
157157
* Duration of the press-out animation, in milliseconds, when the

‎packages/react-native-gesture-handler/src/components/Pressable/PressableProps.tsx‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,13 @@ export interface PressableProps extends CommonPressableProps {
7373
block?: AnyGesture | AnyGesture[];
7474

7575
/**
76-
* When the platform deferred the press, holds `onPressOut` so a quick tap keeps `pressed` for at
77-
* least 130 ms, like React Native's `minPressDuration`. `onPress` is not delayed and may precede
78-
* `onPressOut` on such taps. By default set to true. Has no effect on web or when any of
79-
* `simultaneousWith`, `requireToFail` or `block` is passed.
76+
* When the platform deferred the press, `onPressOut` is held so a quick tap keeps `pressed` for
77+
* at least 130 ms, like React Native's `minPressDuration`. `onPress` is not delayed and may
78+
* precede `onPressOut` on such taps. Set to `true` to call `onPressOut` as soon as the press ends
79+
* instead. By default set to false. Has no effect on web or when any of `simultaneousWith`,
80+
* `requireToFail` or `block` is passed.
8081
*/
81-
pressOutAfterAnimation?: boolean | undefined;
82+
immediatePressOut?: boolean | undefined;
8283
}
8384

8485
interface CommonPressableProps

‎packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonNativeComponent.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ interface NativeProps extends ViewProps {
6060
tapAnimationInDuration?: CodegenTypes.WithDefault<CodegenTypes.Int32, 50>;
6161
tapAnimationOutDuration?: CodegenTypes.WithDefault<CodegenTypes.Int32, 100>;
6262
longPressDuration?: CodegenTypes.WithDefault<CodegenTypes.Int32, -1>;
63-
pressOutAfterAnimation?: CodegenTypes.WithDefault<boolean, false>;
63+
immediatePressOut?: CodegenTypes.WithDefault<boolean, true>;
6464
longPressAnimationOutDuration?: CodegenTypes.WithDefault<
6565
CodegenTypes.Int32,
6666
-1

0 commit comments

Comments
 (0)