diff --git a/src/components/BottomNavigation.tsx b/src/components/BottomNavigation.tsx index b79e160ad5..1c5b421b42 100644 --- a/src/components/BottomNavigation.tsx +++ b/src/components/BottomNavigation.tsx @@ -5,6 +5,7 @@ import { View, Animated, TouchableWithoutFeedback, + TouchableWithoutFeedbackProps, StyleSheet, StyleProp, Platform, @@ -44,6 +45,14 @@ type TabPressEvent = { preventDefault(): void; }; +type TouchableProps = TouchableWithoutFeedbackProps & { + key: string; + children: React.ReactNode; + borderless?: boolean; + centered?: boolean; + rippleColor?: string; +}; + type Props = { /** * Whether the shifting style is used, the active tab appears wider and the inactive tabs won't have a label. @@ -147,6 +156,11 @@ type Props = { focused: boolean; color: string; }) => React.ReactNode; + /** + * Callback which returns a React element to be used as the touchable for the tab item. + * Renders a `TouchableRipple` on Android and `TouchableWithoutFeedback` with `View` on iOS. + */ + renderTouchable?: (props: TouchableProps) => React.ReactNode; /** * Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component. */ @@ -263,11 +277,16 @@ const MAX_TAB_WIDTH = 168; const BAR_HEIGHT = 56; const FAR_FAR_AWAY = 9999; -// @ts-ignore const Touchable = TouchableRipple.supported ? TouchableRipple - : // eslint-disable-next-line @typescript-eslint/no-unused-vars - ({ style, children, borderless, centered, rippleColor, ...rest }: any) => ( + : ({ + style, + children, + borderless: _0, + centered: _1, + rippleColor: _2, + ...rest + }: TouchableProps) => ( {children} @@ -594,6 +613,7 @@ class BottomNavigation extends React.Component { renderScene, renderIcon, renderLabel, + renderTouchable = (props: TouchableProps) => , getLabelText = ({ route }: { route: Route }) => route.title, getBadge = ({ route }: { route: Route }) => route.badge, getColor = ({ route }: { route: Route }) => route.color, @@ -820,23 +840,22 @@ class BottomNavigation extends React.Component { const badge = getBadge({ route }); - return ( - this.handleTabPress(index)} - testID={getTestID({ route })} - accessibilityLabel={getAccessibilityLabel({ route })} - accessibilityTraits={ - focused ? ['button', 'selected'] : 'button' - } - accessibilityComponentType="button" - accessibilityRole="button" - accessibilityStates={['selected']} - style={styles.item} - > + return renderTouchable({ + key: route.key, + borderless: true, + centered: true, + rippleColor: touchColor, + onPress: () => this.handleTabPress(index), + testID: getTestID({ route }), + accessibilityLabel: getAccessibilityLabel({ route }), + accessibilityTraits: focused + ? ['button', 'selected'] + : 'button', + accessibilityComponentType: 'button', + accessibilityRole: 'button', + accessibilityStates: ['selected'], + style: styles.item, + children: ( { )} - - ); + ), + }); })} diff --git a/src/components/TouchableRipple/index.tsx b/src/components/TouchableRipple/index.tsx index 149f826ada..21a0163cf6 100644 --- a/src/components/TouchableRipple/index.tsx +++ b/src/components/TouchableRipple/index.tsx @@ -5,6 +5,7 @@ import { ViewStyle, StyleSheet, StyleProp, + GestureResponderEvent, } from 'react-native'; import color from 'color'; import { withTheme } from '../../core/theming'; @@ -31,11 +32,11 @@ type Props = React.ComponentPropsWithRef & { /** * Function to execute on press. If not set, will cause the touchable to be disabled. */ - onPress?: () => void | null; + onPress?: (e: GestureResponderEvent) => void; /** * Function to execute on long press. */ - onLongPress?: () => void; + onLongPress?: (e: GestureResponderEvent) => void; /** * Color of the ripple effect (Android >= 5.0 and Web). */