import * as React from 'react'; import { PressableAndroidRippleConfig, StyleProp, Platform, ViewStyle, StyleSheet, Pressable, GestureResponderEvent, View, ColorValue, } from 'react-native'; import { Settings, SettingsContext } from '../../core/settings'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import { getTouchableRippleColors } from './utils'; const ANDROID_VERSION_LOLLIPOP = 21; const ANDROID_VERSION_PIE = 28; export type Props = React.ComponentProps & { borderless?: boolean; background?: PressableAndroidRippleConfig; centered?: boolean; disabled?: boolean; onPress?: (e: GestureResponderEvent) => void | null; onLongPress?: (e: GestureResponderEvent) => void; onPressIn?: (e: GestureResponderEvent) => void; onPressOut?: (e: GestureResponderEvent) => void; rippleColor?: ColorValue; underlayColor?: string; children: React.ReactNode; style?: StyleProp; theme?: ThemeProp; }; const TouchableRipple = ({ style, background, borderless = false, disabled: disabledProp, rippleColor, underlayColor, children, theme: themeOverrides, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); const { rippleEffectEnabled } = React.useContext(SettingsContext); const { onPress, onLongPress, onPressIn, onPressOut } = rest; const hasPassedTouchHandler = hasTouchHandler({ onPress, onLongPress, onPressIn, onPressOut, }); const disabled = disabledProp || !hasPassedTouchHandler; const { calculatedRippleColor, calculatedUnderlayColor } = getTouchableRippleColors({ theme, rippleColor, underlayColor, }); // A workaround for ripple on Android P is to use useForeground + overflow: 'hidden' // https://github.com/facebook/react-native/issues/6480 const useForeground = Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_PIE && borderless; if (TouchableRipple.supported) { const androidRipple = rippleEffectEnabled ? background ?? { color: calculatedRippleColor, borderless, foreground: useForeground, } : undefined; return ( {React.Children.only(children)} ); } return ( {({ pressed }) => ( <> {pressed && rippleEffectEnabled && ( )} {React.Children.only(children)} )} ); }; TouchableRipple.supported = Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP; const styles = StyleSheet.create({ overflowHidden: { overflow: 'hidden', }, underlay: { ...StyleSheet.absoluteFillObject, zIndex: 2, }, }); export default TouchableRipple;