diff --git a/src/components/TabSelector/TabIcon.js b/src/components/TabSelector/TabIcon.js
new file mode 100644
index 000000000000..dae8d6057d3f
--- /dev/null
+++ b/src/components/TabSelector/TabIcon.js
@@ -0,0 +1,47 @@
+import {StyleSheet, View, Animated} from 'react-native';
+import React from 'react';
+import PropTypes from 'prop-types';
+import Icon from '../Icon';
+import themeColors from '../../styles/themes/default';
+
+const propTypes = {
+ /** Icon to display on tab */
+ icon: PropTypes.func,
+
+ /** Animated opacity value while the label is inactive state */
+ inactiveOpacity: PropTypes.number,
+
+ /** Animated opacity value while the label is in active state */
+ activeOpacity: PropTypes.number,
+};
+
+const defaultProps = {
+ icon: '',
+ inactiveOpacity: 1,
+ activeOpacity: 0,
+};
+
+function TabIcon({icon, activeOpacity, inactiveOpacity}) {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
+TabIcon.propTypes = propTypes;
+TabIcon.defaultProps = defaultProps;
+TabIcon.displayName = 'TabIcon';
+
+export default TabIcon;
diff --git a/src/components/TabSelector/TabLabel.js b/src/components/TabSelector/TabLabel.js
new file mode 100644
index 000000000000..890286b2ee1d
--- /dev/null
+++ b/src/components/TabSelector/TabLabel.js
@@ -0,0 +1,40 @@
+import {StyleSheet, View, Text, Animated} from 'react-native';
+import React from 'react';
+import PropTypes from 'prop-types';
+import styles from '../../styles/styles';
+
+const propTypes = {
+ /** Title of the tab */
+ title: PropTypes.string,
+
+ /** Animated opacity value while the label is inactive state */
+ inactiveOpacity: PropTypes.number,
+
+ /** Animated opacity value while the label is in active state */
+ activeOpacity: PropTypes.number,
+};
+
+const defaultProps = {
+ title: '',
+ inactiveOpacity: 1,
+ activeOpacity: 0,
+};
+
+function TabLabel({title, activeOpacity, inactiveOpacity}) {
+ return (
+
+
+ {title}
+
+
+ {title}
+
+
+ );
+}
+
+TabLabel.propTypes = propTypes;
+TabLabel.defaultProps = defaultProps;
+TabLabel.displayName = 'TabLabel';
+
+export default TabLabel;
diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js
index 5383253c417b..d50744682027 100644
--- a/src/components/TabSelector/TabSelector.js
+++ b/src/components/TabSelector/TabSelector.js
@@ -7,6 +7,7 @@ import TabSelectorItem from './TabSelectorItem';
import CONST from '../../CONST';
import useLocalize from '../../hooks/useLocalize';
import styles from '../../styles/styles';
+import themeColors from '../../styles/themes/default';
const propTypes = {
/* Navigation state provided by React Navigation */
@@ -21,10 +22,18 @@ const propTypes = {
/* Callback fired when tab is pressed */
onTabPress: PropTypes.func,
+
+ /* AnimatedValue for the position of the screen while swiping */
+ position: PropTypes.shape({
+ interpolate: PropTypes.func.isRequired,
+ }),
};
const defaultProps = {
onTabPress: () => {},
+ position: {
+ interpolate: () => {},
+ },
};
const getIcon = (route) => {
@@ -49,12 +58,44 @@ const getTitle = (route, translate) => {
}
};
-function TabSelector({state, navigation, onTabPress}) {
+const getOpacity = (position, routesLength, tabIndex, active) => {
+ const activeValue = active ? 1 : 0;
+ const inactiveValue = active ? 0 : 1;
+
+ if (routesLength > 1) {
+ const inputRange = Array.from({length: routesLength}, (v, i) => i);
+
+ return position.interpolate({
+ inputRange,
+ outputRange: _.map(inputRange, (i) => (i === tabIndex ? activeValue : inactiveValue)),
+ });
+ }
+ return activeValue;
+};
+
+const getBackgroundColor = (position, routesLength, tabIndex) => {
+ if (routesLength > 1) {
+ const inputRange = Array.from({length: routesLength}, (v, i) => i);
+
+ return position.interpolate({
+ inputRange,
+ outputRange: _.map(inputRange, (i) => (i === tabIndex ? themeColors.midtone : themeColors.appBG)),
+ });
+ }
+ return themeColors.midtone;
+};
+
+function TabSelector({state, navigation, onTabPress, position}) {
const {translate} = useLocalize();
+
return (
{_.map(state.routes, (route, index) => {
- const isFocused = state.index === index;
+ const activeOpacity = getOpacity(position, state.routes.length, index, true);
+ const inactiveOpacity = getOpacity(position, state.routes.length, index, false);
+ const backgroundColor = getBackgroundColor(position, state.routes.length, index);
+
+ const isFocused = index === state.index;
const onPress = () => {
const event = navigation.emit({
@@ -73,11 +114,13 @@ function TabSelector({state, navigation, onTabPress}) {
return (
);
})}
diff --git a/src/components/TabSelector/TabSelectorItem.js b/src/components/TabSelector/TabSelectorItem.js
index cea59bc2ee65..6f96c5ef5d0a 100644
--- a/src/components/TabSelector/TabSelectorItem.js
+++ b/src/components/TabSelector/TabSelectorItem.js
@@ -1,10 +1,10 @@
-import {Text} from 'react-native';
+import {Animated} from 'react-native';
import React from 'react';
import PropTypes from 'prop-types';
-import Icon from '../Icon';
-import themeColors from '../../styles/themes/default';
import styles from '../../styles/styles';
import PressableWithFeedback from '../Pressable/PressableWithFeedback';
+import TabIcon from './TabIcon';
+import TabLabel from './TabLabel';
const propTypes = {
/** Function to call when onPress */
@@ -13,34 +13,50 @@ const propTypes = {
/** Icon to display on tab */
icon: PropTypes.func,
- /** True if tab is the selected item */
- isSelected: PropTypes.bool,
-
/** Title of the tab */
title: PropTypes.string,
+
+ /** Animated background color value for the tab button */
+ // eslint-disable-next-line
+ backgroundColor: PropTypes.any,
+
+ /** Animated opacity value while the label is inactive state */
+ inactiveOpacity: PropTypes.number,
+
+ /** Animated opacity value while the label is in active state */
+ activeOpacity: PropTypes.number,
};
const defaultProps = {
onPress: () => {},
icon: () => {},
- isSelected: false,
title: '',
+ backgroundColor: '',
+ inactiveOpacity: 1,
+ activeOpacity: 0,
};
-function TabSelectorItem(props) {
+const AnimatedPressableWithFeedback = Animated.createAnimatedComponent(PressableWithFeedback);
+
+function TabSelectorItem({icon, title, onPress, backgroundColor, activeOpacity, inactiveOpacity}) {
return (
-
-
+
- {props.title}
-
+
);
}
diff --git a/src/pages/iou/MoneyRequestSelectorPage.js b/src/pages/iou/MoneyRequestSelectorPage.js
index 922db11ee702..03ea45b204fa 100644
--- a/src/pages/iou/MoneyRequestSelectorPage.js
+++ b/src/pages/iou/MoneyRequestSelectorPage.js
@@ -75,11 +75,12 @@ function MoneyRequestSelectorPage(props) {
{(canUseScanReceipts || canUseDistanceRequests) && iouType === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST ? (
(
+ tabBar={({state, navigation, position}) => (
)}
>
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 6b8799e2065e..d4fc1073bc46 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -3673,15 +3673,14 @@ const styles = {
height: 450,
},
- tabSelectorButton: (isSelected) => ({
+ tabSelectorButton: {
height: 40,
padding: 12,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
borderRadius: variables.buttonBorderRadius,
- backgroundColor: isSelected ? themeColors.midtone : themeColors.appBG,
- }),
+ },
tabSelector: {
flexDirection: 'row',