Skip to content

Commit

Permalink
Merge pull request #26022 from allroundexperts/fix-25428
Browse files Browse the repository at this point in the history
  • Loading branch information
thienlnam authored Aug 30, 2023
2 parents 4bf9d2c + 85e7035 commit 4fee350
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 24 deletions.
47 changes: 47 additions & 0 deletions src/components/TabSelector/TabIcon.js
Original file line number Diff line number Diff line change
@@ -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 (
<View>
<Animated.View style={{opacity: inactiveOpacity}}>
<Icon
src={icon}
fill={themeColors.icon}
/>
</Animated.View>
<Animated.View style={[StyleSheet.absoluteFill, {opacity: activeOpacity}]}>
<Icon
src={icon}
fill={themeColors.iconMenu}
/>
</Animated.View>
</View>
);
}

TabIcon.propTypes = propTypes;
TabIcon.defaultProps = defaultProps;
TabIcon.displayName = 'TabIcon';

export default TabIcon;
40 changes: 40 additions & 0 deletions src/components/TabSelector/TabLabel.js
Original file line number Diff line number Diff line change
@@ -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 (
<View>
<Animated.View style={[{opacity: activeOpacity}]}>
<Text style={styles.tabText(true)}>{title}</Text>
</Animated.View>
<Animated.View style={[StyleSheet.absoluteFill, {opacity: inactiveOpacity}]}>
<Text style={styles.tabText(false)}>{title}</Text>
</Animated.View>
</View>
);
}

TabLabel.propTypes = propTypes;
TabLabel.defaultProps = defaultProps;
TabLabel.displayName = 'TabLabel';

export default TabLabel;
49 changes: 46 additions & 3 deletions src/components/TabSelector/TabSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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) => {
Expand All @@ -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 (
<View style={styles.tabSelector}>
{_.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({
Expand All @@ -73,11 +114,13 @@ function TabSelector({state, navigation, onTabPress}) {

return (
<TabSelectorItem
isSelected={isFocused}
key={route.name}
title={getTitle(route.name, translate)}
icon={getIcon(route.name)}
onPress={onPress}
activeOpacity={activeOpacity}
inactiveOpacity={inactiveOpacity}
backgroundColor={backgroundColor}
/>
);
})}
Expand Down
50 changes: 33 additions & 17 deletions src/components/TabSelector/TabSelectorItem.js
Original file line number Diff line number Diff line change
@@ -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 */
Expand All @@ -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 (
<PressableWithFeedback
accessibilityLabel={props.title}
style={[styles.tabSelectorButton(props.isSelected)]}
<AnimatedPressableWithFeedback
accessibilityLabel={title}
style={[styles.tabSelectorButton, {backgroundColor}]}
wrapperStyle={[styles.flex1]}
onPress={props.onPress}
onPress={onPress}
>
<Icon
src={props.icon}
fill={props.isSelected ? themeColors.iconMenu : themeColors.icon}
<TabIcon
icon={icon}
activeOpacity={activeOpacity}
inactiveOpacity={inactiveOpacity}
/>
<TabLabel
title={title}
activeOpacity={activeOpacity}
inactiveOpacity={inactiveOpacity}
/>
<Text style={styles.tabText(props.isSelected)}>{props.title}</Text>
</PressableWithFeedback>
</AnimatedPressableWithFeedback>
);
}

Expand Down
3 changes: 2 additions & 1 deletion src/pages/iou/MoneyRequestSelectorPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ function MoneyRequestSelectorPage(props) {
{(canUseScanReceipts || canUseDistanceRequests) && iouType === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST ? (
<OnyxTabNavigator
id={CONST.TAB.RECEIPT_TAB_ID}
tabBar={({state, navigation}) => (
tabBar={({state, navigation, position}) => (
<TabSelector
state={state}
navigation={navigation}
onTabPress={resetMoneyRequestInfo}
position={position}
/>
)}
>
Expand Down
5 changes: 2 additions & 3 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down

0 comments on commit 4fee350

Please sign in to comment.