Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions packages/docs-gesture-handler/docs/components/pressable.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Either view styles or a function that receives a boolean reflecting whether the
onPress?: null | ((event: PressableEvent) => void);
```

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.
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.

### onPressIn

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

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.
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.

### onLongPress

Expand Down Expand Up @@ -201,19 +201,19 @@ needsOffscreenAlphaCompositing?: boolean;

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

<Badges platforms={['android', 'ios']}>
### pressOutAfterAnimation [A][I]
<Badges platforms={['android', 'ios']} version="3.4.0">
### immediatePressOut [A][I]
</Badges>

```ts
pressOutAfterAnimation?: boolean;
immediatePressOut?: boolean;
```

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.
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.

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.
When set to `true`, `onPressOut` is called as soon as the press ends, even for such a tap.

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

### testOnly_pressed

Expand Down
10 changes: 5 additions & 5 deletions packages/docs-gesture-handler/docs/components/touchable.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -586,19 +586,19 @@ cancelOnLeave?: boolean;

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.

<Badges platforms={['android', 'ios']}>
### pressOutAfterAnimation [A][I]
<Badges platforms={['android', 'ios']} version="3.4.0">
### immediatePressOut [A][I]
</Badges>

```ts
pressOutAfterAnimation?: boolean;
immediatePressOut?: boolean;
```

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.

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.
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.

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

<Badges platforms={['android']}>
### needsOffscreenAlphaCompositing [A]
Expand Down
16 changes: 16 additions & 0 deletions packages/docs-gesture-handler/src/css/overrides.css
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,19 @@ footer
align-items: center;
}
}

[class*='_headerWithBadges_'] [class*='_badge_'][class*='_versionBadge_'] {
background: transparent;
margin-left: auto;
color: var(--swm-navy-light-60);
border: 1px solid var(--swm-navy-light-20);
font-weight: 400;
padding: calc(0.25rem - 1px) 0.5rem;
}

[data-theme='dark']
[class*='_headerWithBadges_']
[class*='_badge_'][class*='_versionBadge_'] {
color: var(--swm-navy-light-40);
border-color: var(--swm-light-off-navy);
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class RNGestureHandlerButtonViewManager :
view.managedHandlerTestID = null
view.managedHandlerHitSlop = null
view.moduleId = null
view.pressOutAfterAnimation = false
view.immediatePressOut = true
view.resetHoverState()
// Has to come last — every setter above flags the view as needing a managed handler update.
view.managedHandlerNeedsUpdate = false
Expand Down Expand Up @@ -358,9 +358,9 @@ class RNGestureHandlerButtonViewManager :
view.longPressDuration = value
}

@ReactProp(name = "pressOutAfterAnimation")
override fun setPressOutAfterAnimation(view: ButtonViewGroup, value: Boolean) {
view.pressOutAfterAnimation = value
@ReactProp(name = "immediatePressOut")
override fun setImmediatePressOut(view: ButtonViewGroup, value: Boolean) {
view.immediatePressOut = value
}

@ReactProp(name = "longPressAnimationOutDuration")
Expand Down Expand Up @@ -573,7 +573,7 @@ class RNGestureHandlerButtonViewManager :
var tapAnimationInDuration: Int = 50
var tapAnimationOutDuration: Int = 100
var longPressDuration: Int = -1
var pressOutAfterAnimation = false
var immediatePressOut = true
var longPressAnimationOutDuration: Int = -1
get() = if (field < 0) tapAnimationOutDuration else field
var activeOpacity: Float = 1.0f
Expand Down Expand Up @@ -616,7 +616,7 @@ class RNGestureHandlerButtonViewManager :
private var pressInTimestamp = 0L
private var pendingPressOut: Runnable? = null

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

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

// With `pressOutAfterAnimation`, a press the platform deferred (tap timeout inside a scrolling
// Unless `immediatePressOut`, a press the platform deferred (tap timeout inside a scrolling
// container) holds PressOut until the press-in animation has played, like `animatePressOut`
// holds the animation. An immediate press keeps its native timing. Press is never delayed, so
// on such taps PressOut follows it. The event is built now so it carries the release position.
private fun dispatchPressOut(handler: NativeViewGestureHandler) {
val remaining = tapAnimationInDuration - (SystemClock.uptimeMillis() - pressInTimestamp)
if (!pressOutAfterAnimation || !pressDeferredByPlatform || remaining <= 0) {
if (immediatePressOut || !pressDeferredByPlatform || remaining <= 0) {
dispatchJSEvent(EventType.PressOut, handler)
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ typedef NS_ENUM(NSInteger, RNGHButtonEventType) {
@property (nonatomic, assign) NSInteger tapAnimationInDuration;
@property (nonatomic, assign) NSInteger tapAnimationOutDuration;
@property (nonatomic, assign) NSInteger longPressDuration;
@property (nonatomic, assign) BOOL pressOutAfterAnimation;
@property (nonatomic, assign) BOOL immediatePressOut;
@property (nonatomic, assign) NSInteger longPressAnimationOutDuration;
@property (nonatomic, assign) CGFloat activeOpacity;
@property (nonatomic, assign) CGFloat defaultOpacity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ @implementation RNGestureHandlerButton {
UIEdgeInsets _underlayBorderInsets; // border widths for padding-box inset
NSTimeInterval _pressInTimestamp;
dispatch_block_t _pendingPressOutBlock;
// `onPressOut` held back by `pressOutAfterAnimation` (the press-out animation has its own block above).
// `onPressOut` held back unless `immediatePressOut` (the press-out animation has its own block above).
// The payload is kept apart from the block: a cancelled dispatch block never runs its body.
dispatch_block_t _pendingPressOutEventBlock;
RNGestureHandlerEventExtraData *_pendingPressOutExtraData;
Expand Down Expand Up @@ -109,7 +109,7 @@ - (void)commonInit
_tapAnimationInDuration = 50;
_tapAnimationOutDuration = 100;
_longPressDuration = -1;
_pressOutAfterAnimation = NO;
_immediatePressOut = YES;
_pressDeferredByPlatform = NO;
_longPressAnimationOutDuration = -1;
_activeOpacity = 1.0;
Expand Down Expand Up @@ -763,14 +763,14 @@ - (BOOL)isInsideDelayingScrollView
}
#endif

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

_isTouchInsideBounds = YES;
_pressDeferredByPlatform = _pressOutAfterAnimation && [self isInsideDelayingScrollView];
_pressDeferredByPlatform = !_immediatePressOut && [self isInsideDelayingScrollView];
// A pencil's hover-out arrives just before touch-down but only schedules the
// clear, so `_isHovered` still reflects the open hover. Under Reduce Motion
// that delay is zero and the block can land first — the press then brackets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
_buttonView.tapAnimationInDuration = newProps.tapAnimationInDuration > 0 ? newProps.tapAnimationInDuration : 0;
_buttonView.tapAnimationOutDuration = newProps.tapAnimationOutDuration > 0 ? newProps.tapAnimationOutDuration : 0;
_buttonView.longPressDuration = newProps.longPressDuration;
_buttonView.pressOutAfterAnimation = newProps.pressOutAfterAnimation;
_buttonView.immediatePressOut = newProps.immediatePressOut;
_buttonView.longPressAnimationOutDuration = newProps.longPressAnimationOutDuration;
_buttonView.activeOpacity = newProps.activeOpacity;
_buttonView.defaultOpacity = newProps.defaultOpacity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,12 @@ export interface ButtonProps extends ViewProps, AccessibilityProps {
longPressDuration?: number | undefined;

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

/**
* Duration of the press-out animation, in milliseconds, when the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,13 @@ export interface PressableProps extends CommonPressableProps {
block?: AnyGesture | AnyGesture[];

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

interface CommonPressableProps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ interface NativeProps extends ViewProps {
tapAnimationInDuration?: CodegenTypes.WithDefault<CodegenTypes.Int32, 50>;
tapAnimationOutDuration?: CodegenTypes.WithDefault<CodegenTypes.Int32, 100>;
longPressDuration?: CodegenTypes.WithDefault<CodegenTypes.Int32, -1>;
pressOutAfterAnimation?: CodegenTypes.WithDefault<boolean, false>;
immediatePressOut?: CodegenTypes.WithDefault<boolean, true>;
longPressAnimationOutDuration?: CodegenTypes.WithDefault<
CodegenTypes.Int32,
-1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const IS_TEST_ENV = isTestEnv();

// RN's Pressable default. Touchable's own default is 600ms
const DEFAULT_LONG_PRESS_DURATION = 500;
// Pressable has no native opacity/scale animation. The press-in duration only matters with
// `pressOutAfterAnimation`: it is how long a platform-deferred quick tap keeps `pressed` before
// Pressable has no native opacity/scale animation. The press-in duration only matters without
// `immediatePressOut`: it is how long a platform-deferred quick tap keeps `pressed` before
// onPressOut. 130 ms matches RN's minPressDuration.
const PRESSABLE_ANIMATION_DURATION = { in: 50, out: 100, tap: { in: 130 } };

Expand Down Expand Up @@ -88,7 +88,7 @@ const PressableWithTouchable = (props: PressableProps) => {
disabled,
accessible,
ref,
pressOutAfterAnimation = true,
immediatePressOut = false,
...rest
} = props;

Expand Down Expand Up @@ -319,7 +319,7 @@ const PressableWithTouchable = (props: PressableProps) => {
delayLongPress={resolvedDelayLongPress}
style={[pointerStyle, resolvedStyle]}
animationDuration={PRESSABLE_ANIMATION_DURATION}
pressOutAfterAnimation={pressOutAfterAnimation}
immediatePressOut={immediatePressOut}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
onPress={handlePress}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const StatefulPressable = (props: PressableProps) => {
ref,
// Only PressableWithTouchable holds onPressOut; keep the native button at its default here.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
pressOutAfterAnimation,
immediatePressOut,
...remainingProps
} = props;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const Touchable = (props: TouchableProps) => {
children,
disabled = false,
cancelOnLeave = true,
pressOutAfterAnimation = false,
immediatePressOut = true,
ref,
...rest
} = props;
Expand Down Expand Up @@ -226,7 +226,7 @@ export const Touchable = (props: TouchableProps) => {
activeUnderlayOpacity={activeUnderlayOpacity}
underlayColor={underlayColor}
longPressDuration={resolvedDelayLongPress}
pressOutAfterAnimation={pressOutAfterAnimation}
immediatePressOut={immediatePressOut}
hasLongPressHandler={onLongPress !== undefined}
onButtonPress={internalOnPress}
onButtonPressIn={internalOnPressIn}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ export type TouchableProps = Omit<
cancelOnLeave?: boolean | undefined;

/**
* When the platform deferred the press (e.g. inside a scrolling container), holds `onPressOut`
* until the press-in animation has finished, so a quick tap still shows its feedback.
* `onPress` is not delayed and may precede `onPressOut` on such taps. By default set to false.
* Set to `false` to hold `onPressOut` for a press the platform deferred (e.g. inside a scrolling
* container) until the press-in animation has finished, so a quick tap still shows its feedback.
* `onPress` is not delayed and may precede `onPressOut` on such taps. By default set to true.
*/
pressOutAfterAnimation?: boolean | undefined;
immediatePressOut?: boolean | undefined;
};
Loading