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).
*/