Skip to content

Commit

Permalink
Forward unused props in ReanimatedSwipeable and Pressable to thei…
Browse files Browse the repository at this point in the history
…r inner components (#3039)

## Description

Currently large portion of the props accepted by `ReanimatedSwipeable`
and `Pressable` components remain unused.
This issue mostly refers to all the `accessibility` props.

This PR adds the use of `spread` operator to `ReanimatedSwipeable` and
`Pressable` to forward all the remaining props to inner components,
which are not explicitly used otherwise.

Fixes: #3032

## Test plan
- turn on radio description
- open the nested `Pressable` example
- add `accessibilityLabel` prop to one of the `Pressables`
- navigate over the modified `Pressable` with your keyboard (use `Tab`
and `Shift-Tab` to navigate)
- hear as your label is being read out, which wouldn't occur without
this change
  • Loading branch information
latekvo authored Aug 20, 2024
1 parent b832692 commit 40b4e11
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 87 deletions.
109 changes: 62 additions & 47 deletions src/components/Pressable/Pressable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,29 @@ import { INT32_MAX } from '../../utils';
const DEFAULT_LONG_PRESS_DURATION = 500;

export default function Pressable(props: PressableProps) {
const [pressedState, setPressedState] = useState(
props.testOnly_pressed ?? false
);
const {
testOnly_pressed,
hitSlop,
pressRetentionOffset,
delayHoverIn,
onHoverIn,
delayHoverOut,
onHoverOut,
delayLongPress,
unstable_pressDelay,
onPress,
onPressIn,
onPressOut,
onLongPress,
style,
children,
android_disableSound,
android_ripple,
disabled,
...remainingProps
} = props;

const [pressedState, setPressedState] = useState(testOnly_pressed ?? false);

const pressableRef = useRef<View>(null);

Expand All @@ -40,18 +60,16 @@ export default function Pressable(props: PressableProps) {

const normalizedHitSlop: Insets = useMemo(
() =>
typeof props.hitSlop === 'number'
? numberAsInset(props.hitSlop)
: props.hitSlop ?? {},
[props.hitSlop]
typeof hitSlop === 'number' ? numberAsInset(hitSlop) : hitSlop ?? {},
[hitSlop]
);

const normalizedPressRetentionOffset: Insets = useMemo(
() =>
typeof props.pressRetentionOffset === 'number'
? numberAsInset(props.pressRetentionOffset)
: props.pressRetentionOffset ?? {},
[props.pressRetentionOffset]
typeof pressRetentionOffset === 'number'
? numberAsInset(pressRetentionOffset)
: pressRetentionOffset ?? {},
[pressRetentionOffset]
);

const hoverInTimeout = useRef<number | null>(null);
Expand All @@ -66,29 +84,29 @@ export default function Pressable(props: PressableProps) {
if (hoverOutTimeout.current) {
clearTimeout(hoverOutTimeout.current);
}
if (props.delayHoverIn) {
if (delayHoverIn) {
hoverInTimeout.current = setTimeout(
() => props.onHoverIn?.(gestureToPressableEvent(event)),
props.delayHoverIn
() => onHoverIn?.(gestureToPressableEvent(event)),
delayHoverIn
);
return;
}
props.onHoverIn?.(gestureToPressableEvent(event));
onHoverIn?.(gestureToPressableEvent(event));
})
.onFinalize((event) => {
if (hoverInTimeout.current) {
clearTimeout(hoverInTimeout.current);
}
if (props.delayHoverOut) {
if (delayHoverOut) {
hoverOutTimeout.current = setTimeout(
() => props.onHoverOut?.(gestureToPressableEvent(event)),
props.delayHoverOut
() => onHoverOut?.(gestureToPressableEvent(event)),
delayHoverOut
);
return;
}
props.onHoverOut?.(gestureToPressableEvent(event));
onHoverOut?.(gestureToPressableEvent(event));
}),
[props]
[delayHoverIn, delayHoverOut, onHoverIn, onHoverOut]
);

const pressDelayTimeoutRef = useRef<number | null>(null);
Expand All @@ -109,12 +127,12 @@ export default function Pressable(props: PressableProps) {

deferredEventPayload.current = null;

props.onPressIn?.(event);
onPressIn?.(event);
isPressCallbackEnabled.current = true;
pressDelayTimeoutRef.current = null;
setPressedState(true);
},
[props]
[onPressIn]
);

const pressOutHandler = useCallback(
Expand All @@ -127,7 +145,7 @@ export default function Pressable(props: PressableProps) {
return;
}

if (props.unstable_pressDelay && pressDelayTimeoutRef.current !== null) {
if (unstable_pressDelay && pressDelayTimeoutRef.current !== null) {
// When delay is preemptively finished by lifting touches,
// we want to immediately activate it's effects - pressInHandler,
// even though we are located at the pressOutHandler
Expand All @@ -136,14 +154,14 @@ export default function Pressable(props: PressableProps) {
}

if (deferredEventPayload.current) {
props.onPressIn?.(deferredEventPayload.current);
onPressIn?.(deferredEventPayload.current);
deferredEventPayload.current = null;
}

props.onPressOut?.(event);
onPressOut?.(event);

if (isPressCallbackEnabled.current) {
props.onPress?.(event);
onPress?.(event);
}

if (longPressTimeoutRef.current) {
Expand All @@ -156,7 +174,7 @@ export default function Pressable(props: PressableProps) {
isPressCallbackEnabled.current = true;
setPressedState(false);
},
[pressInHandler, props]
[onPress, onPressIn, onPressOut, pressInHandler, unstable_pressDelay]
);

const handlingOnTouchesDown = useRef<boolean>(false);
Expand All @@ -170,7 +188,7 @@ export default function Pressable(props: PressableProps) {
}

if (hasPassedBoundsChecks.current) {
props.onLongPress?.(gestureTouchToPressableEvent(event));
onLongPress?.(gestureTouchToPressableEvent(event));
isPressCallbackEnabled.current = false;
}

Expand All @@ -179,13 +197,13 @@ export default function Pressable(props: PressableProps) {
longPressTimeoutRef.current = null;
}
},
[props]
[onLongPress]
);

const longPressTimeoutRef = useRef<number | null>(null);
const longPressMinDuration =
(props.delayLongPress ?? DEFAULT_LONG_PRESS_DURATION) +
(props.unstable_pressDelay ?? 0);
(delayLongPress ?? DEFAULT_LONG_PRESS_DURATION) +
(unstable_pressDelay ?? 0);

const pressAndTouchGesture = useMemo(
() =>
Expand Down Expand Up @@ -225,10 +243,10 @@ export default function Pressable(props: PressableProps) {
);
}

if (props.unstable_pressDelay) {
if (unstable_pressDelay) {
pressDelayTimeoutRef.current = setTimeout(() => {
pressInHandler(gestureTouchToPressableEvent(event));
}, props.unstable_pressDelay);
}, unstable_pressDelay);
} else {
pressInHandler(gestureTouchToPressableEvent(event));
}
Expand Down Expand Up @@ -276,7 +294,7 @@ export default function Pressable(props: PressableProps) {
normalizedHitSlop,
pressInHandler,
pressOutHandler,
props.unstable_pressDelay,
unstable_pressDelay,
]
);

Expand Down Expand Up @@ -334,7 +352,7 @@ export default function Pressable(props: PressableProps) {
normalizedPressRetentionOffset
);

const isPressableEnabled = props.disabled !== true;
const isPressableEnabled = disabled !== true;

const gestures = [pressAndTouchGesture, hoverGesture, buttonGesture];

Expand All @@ -354,39 +372,36 @@ export default function Pressable(props: PressableProps) {

const gesture = Gesture.Simultaneous(...gestures);

const defaultRippleColor = props.android_ripple ? undefined : 'transparent';
const defaultRippleColor = android_ripple ? undefined : 'transparent';

// `cursor: 'pointer'` on `RNButton` crashes iOS
const pointerStyle: StyleProp<ViewStyle> =
Platform.OS === 'web' ? { cursor: 'pointer' } : {};

const styleProp =
typeof props.style === 'function'
? props.style({ pressed: pressedState })
: props.style;
typeof style === 'function' ? style({ pressed: pressedState }) : style;

const childrenProp =
typeof props.children === 'function'
? props.children({ pressed: pressedState })
: props.children;
typeof children === 'function'
? children({ pressed: pressedState })
: children;

const flattenedStyles = StyleSheet.flatten(styleProp ?? {});

const [innerStyles, outerStyles] = splitStyles(flattenedStyles);

return (
<View style={outerStyles}>
<View {...remainingProps} style={outerStyles}>
<GestureDetector gesture={gesture}>
<NativeButton
ref={pressableRef}
testID={props.testID}
hitSlop={appliedHitSlop}
enabled={isPressableEnabled}
touchSoundDisabled={props.android_disableSound ?? undefined}
touchSoundDisabled={android_disableSound ?? undefined}
rippleColor={processColor(
props.android_ripple?.color ?? defaultRippleColor
android_ripple?.color ?? defaultRippleColor
)}
rippleRadius={props.android_ripple?.radius ?? undefined}
rippleRadius={android_ripple?.radius ?? undefined}
style={[
{ width: '100%', height: '100%' },
pointerStyle,
Expand Down
Loading

0 comments on commit 40b4e11

Please sign in to comment.