Fixed tabbar design

This commit is contained in:
Mustafa Buyukcelebi 2019-10-04 00:22:42 +03:00
parent 403ac5fee2
commit a927c70f77
12 changed files with 268 additions and 265 deletions

View File

@ -71,7 +71,7 @@ class BottomTabBarView extends PureComponent {
} = this.props;
return (
<View
<SafeAreaView
style={{
position: 'absolute',
bottom: 0,
@ -81,13 +81,31 @@ class BottomTabBarView extends PureComponent {
}}
>
<TabBar
itemList={routes}
renderIcon={renderIcon}
onPress={this._jumpTo}
selectedIndex={0}
circleBackgroundColor="#357ce6"
backgroundColor="#f6f6f6"
onChange={index => this._jumpTo(routes[index])}
activeTintColor={activeTintColor}
inactiveTintColor={inactiveTintColor}
/>
</View>
>
{routes.map(route => (
<TabBar.Item
icon={renderIcon({
route,
focused: false,
tintColor: inactiveTintColor,
})}
selectedIcon={renderIcon({
route,
focused: true,
tintColor: activeTintColor,
})}
key={route}
disabled={route.routeName === ROUTES.TABBAR.POSTBUTTON}
/>
))}
</TabBar>
</SafeAreaView>
);
}
}

View File

@ -1,176 +1,183 @@
/* eslint-disable */
import { StyleSheet, View, TouchableHighlight, Animated, Image } from 'react-native';
import {
StyleSheet,
View,
TouchableHighlight,
Animated,
Dimensions,
SafeAreaView,
} from 'react-native';
import React, { Component } from 'react';
import Svg, { Circle, Path } from 'react-native-svg';
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
const AnimatedPath = Animated.createAnimatedComponent(Path);
const deviceWidth = Dimensions.get('window').width;
export default class TabBar extends Component {
constructor(props) {
super(props);
const { selectedIndex, children } = props;
let value;
switch (children.length) {
case 2:
value = 480;
break;
case 3:
value = 240;
break;
case 4:
value = 160;
break;
case 5:
value = 120;
break;
default:
break;
}
this.state = {
selectedIndex: 2,
circleRadius: new Animated.Value(331),
pathD: new Animated.Value(240),
pathX: 240,
selectedIndex: selectedIndex,
circleRadius: new Animated.Value(91 + selectedIndex * value),
pathD: new Animated.Value(selectedIndex * value),
pathX: selectedIndex * value,
showIcon: true,
animateConstant: value,
};
this.state.circleRadius.addListener(circleRadius => {
this._myCircle.setNativeProps({ cx: parseInt(circleRadius.value) });
this._myCircle.setNativeProps({ cx: parseInt(circleRadius.value, 10) });
});
this.state.pathD.addListener(a => {
console.log(this.state.pathD);
this.setState({
pathX: parseInt(a.value),
pathX: parseInt(a.value, 10),
});
});
}
_renderButtonContent = (i, selectedIndex, showIcon, route) => {
const { renderIcon, inactiveTintColor, activeTintColor } = this.props;
if (selectedIndex == i) {
if (showIcon) {
return (
<View style={styles.circle}>
{renderIcon({
route,
focused: true,
tintColor: activeTintColor,
})}
</View>
);
_onPress = (i, disabled) => {
const { onChange } = this.props;
if (!disabled) {
this._move(i);
if (onChange) {
onChange(i);
}
return <View />;
}
return (
<View>
{renderIcon({
route,
focused: false,
tintColor: inactiveTintColor,
})}
</View>
);
};
_onPress = (i, route) => {
const { onPress } = this.props;
this.update(i);
onPress(route);
};
_move = index => {
const { animateConstant, pathD, circleRadius } = this.state;
render() {
const { children, itemList } = this.props;
const { selectedIndex, showIcon, pathX } = this.state;
return (
<View style={[styles.container, this.props.style]}>
<View style={[styles.content]}>
<View style={styles.subContent}>
{itemList.map((route, i) => (
<TouchableHighlight
key={i}
underlayColor={'transparent'}
style={styles.navItem}
onPress={() => (selectedIndex !== i ? this._onPress(i, route) : null)}
>
{this._renderButtonContent(i, selectedIndex, showIcon, route)}
</TouchableHighlight>
))}
</View>
<Svg
version="1.1"
id="bottom-bar"
x="0px"
y="0px"
width="100%"
height="100"
viewBox="0 0 661 136"
space="preserve"
>
<AnimatedPath
fill="#f0f0f0"
d={`M${31 + pathX}.454074,80.6628108 C${42 + pathX}.339255,102.895752 ${64 +
pathX}.692432,118.142857 ${90 + pathX}.5,118.142857 C${116 +
pathX}.658561,118.142857 ${139 + pathX}.26813,102.478199 ${149 +
pathX}.983671,79.7518248 C${154 + pathX}.222383,70.7620241 ${170 +
pathX}.571658,50 ${197 + pathX}.357095,50 C${247 +
pathX}.055518,50 561.603153,50 661,50 L661,156 L0,156 L0,50 C99.6668047,50 ${-66 +
pathX}.416908,50 ${-16 + pathX}.250311,50 C${11 + pathX}.065333,50 ${26 +
pathX}.941653,71.4462087 ${31 + pathX}.454074,80.6628108 Z`}
/>
<AnimatedCircle
ref={ref => (this._myCircle = ref)}
fill="#f0f0f0"
cx="331"
cy="50.5"
r="50"
/>
</Svg>
</View>
</View>
);
}
update(index) {
const value = 120;
let that = this;
that.setState({
this.setState({
selectedIndex: index,
showIcon: false,
});
Animated.spring(that.state.pathD, {
toValue: 0 + index * value,
duration: 10,
friction: 10,
}).start();
setTimeout(function() {
that.setState({
showIcon: true,
});
}, 100);
Animated.spring(that.state.circleRadius, {
toValue: 91 + index * value,
friction: 10,
Animated.timing(pathD, {
toValue: 0 + index * animateConstant,
duration: 450,
}).start(() => this.setState({ showIcon: true }));
Animated.timing(circleRadius, {
toValue: 91 + index * animateConstant,
}).start();
};
render() {
const { children, backgroundColor, circleBackgroundColor, style } = this.props;
const { selectedIndex, showIcon, pathX, circleRadius } = this.state;
return (
<View style={[styles.container, style]}>
<View style={styles.subContent}>
{children.map((route, i) => {
let element = React.cloneElement(route, {
selected: selectedIndex === i,
onPress: this._onPress,
key: i,
index: i,
showIcon,
});
return element;
})}
</View>
<Svg
version="1.1"
id="bottom-bar"
x="0px"
y="0px"
width="100%"
height="100"
viewBox="0 0 661 136"
space="preserve"
>
<AnimatedPath
fill={backgroundColor}
d={`M${31 + pathX}.454074,80.6628108 C${42 + pathX}.339255,102.895752 ${64 +
pathX}.692432,118.142857 ${90 + pathX}.5,118.142857 C${116 +
pathX}.658561,118.142857 ${139 + pathX}.26813,102.478199 ${149 +
pathX}.983671,79.7518248 C${154 + pathX}.222383,70.7620241 ${170 +
pathX}.571658,50 ${197 + pathX}.357095,50 C${247 +
pathX}.055518,50 561.603153,50 661,50 L661,156 L0,156 L0,50 C99.6668047,50 ${-66 +
pathX}.416908,50 ${-16 + pathX}.250311,50 C${11 + pathX}.065333,50 ${26 +
pathX}.941653,71.4462087 ${31 + pathX}.454074,80.6628108 Z`}
/>
<AnimatedCircle
ref={ref => (this._myCircle = ref)}
fill={circleBackgroundColor}
cx={circleRadius}
cy="50.5"
r="50"
/>
</Svg>
</View>
);
}
}
const TabBarItem = ({ icon, selectedIcon, index, selected, onPress, showIcon, disabled }) => {
if (selected) {
if (showIcon) {
return (
<TouchableHighlight underlayColor={'transparent'} style={styles.navItem}>
<View style={styles.circle}>{selectedIcon || icon}</View>
</TouchableHighlight>
);
}
return <View style={styles.navItem} />;
}
return (
<TouchableHighlight
underlayColor={'transparent'}
style={styles.navItem}
onPress={() => onPress(index, disabled)}
>
{icon}
</TouchableHighlight>
);
};
TabBar.Item = TabBarItem;
const styles = StyleSheet.create({
container: {
flex: 1,
overflow: 'hidden',
},
content: {
borderColor: 'red',
borderWidth: 1,
},
subContent: {
flexDirection: 'row',
marginLeft: 19,
marginRight: 19,
marginBottom: 10,
zIndex: 1,
position: 'absolute',
bottom: 5,
bottom: 0,
marginHorizontal: 19,
justifyContent: 'space-between',
},
navItem: {
flex: 1,
paddingTop: 6,
paddingBottom: 6,
alignItems: 'center',
zIndex: 0,
},
navImage: {
width: 45,
height: 45,
padding: 20,
width: (deviceWidth - 38) / 5,
},
circle: {
bottom: 23,
bottom: 25,
},
});

View File

@ -9,16 +9,5 @@ export default EStyleSheet.create({
},
postButton: {
flex: 1,
bottom: 25,
backgroundColor: '#357ce6',
alignItems: 'center',
justifyContent: 'center',
shadowColor: '$shadowColor',
shadowOffset: { height: 2 },
shadowOpacity: 0.5,
elevation: 3,
width: 60,
height: 60,
borderRadius: 60 / 2,
},
});

View File

@ -1,29 +1,22 @@
import React from 'react';
import { TouchableOpacity, View } from 'react-native';
import { TouchableOpacity } from 'react-native';
import { withNavigation } from 'react-navigation';
import { Icon } from '../icon';
// Constant
import { default as ROUTES } from '../../constants/routeNames';
// Styles
import styles from './postButtonStyles';
const PostButtonView = ({ navigation }) => (
<View style={styles.postButtonWrapper}>
<TouchableOpacity
onPress={() =>
navigation.navigate({
routeName: ROUTES.SCREENS.EDITOR,
})
}
activeOpacity={1}
>
<View style={styles.postButton}>
<Icon name="pencil" size={24} iconType="MaterialCommunityIcons" color="#F8F8F8" />
</View>
</TouchableOpacity>
</View>
<TouchableOpacity
onPress={() =>
navigation.navigate({
routeName: ROUTES.SCREENS.EDITOR,
})
}
activeOpacity={1}
>
<Icon iconType="MaterialCommunityIcons" name="pencil" color="#c1c5c7" size={26} />
</TouchableOpacity>
);
export default withNavigation(PostButtonView);

View File

@ -3,7 +3,7 @@ import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flex: 1,
backgroundColor: '$primaryGrayBackground',
backgroundColor: '$primaryLightBackground',
},
content: {
backgroundColor: '$primaryGrayBackground',

View File

@ -1,5 +1,5 @@
import React, { PureComponent, Fragment } from 'react';
import { View, ScrollView } from 'react-native';
import { View, ScrollView, SafeAreaView } from 'react-native';
import { injectIntl } from 'react-intl';
import get from 'lodash/get';
import ScrollableTabView from 'react-native-scrollable-tab-view';
@ -99,7 +99,7 @@ class ProfileView extends PureComponent {
isReverse={!isOwnProfile}
handleOnBackPress={handleOnBackPress}
/>
<View style={styles.container}>
<SafeAreaView style={styles.container}>
{!isReady ? (
<ProfileSummaryPlaceHolder />
) : (
@ -229,7 +229,7 @@ class ProfileView extends PureComponent {
)}
</View>
</ScrollableTabView>
</View>
</SafeAreaView>
</Fragment>
);
}

View File

@ -37,22 +37,14 @@ const BaseNavigator = createBottomTabNavigator(
[ROUTES.TABBAR.POSTBUTTON]: {
screen: () => null,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon iconType="MaterialCommunityIcons" name="pencil" color={tintColor} size={26} />
),
tabBarIcon: ({ tintColor }) => <PostButton />,
},
},
[ROUTES.TABBAR.POINTS]: {
screen: Points,
navigationOptions: () => ({
tabBarIcon: ({ tintColor }) => (
<Icon
iconType="MaterialCommunityIcons"
style={{ padding: 15 }}
name="gift-outline"
color={tintColor}
size={26}
/>
<Icon iconType="MaterialCommunityIcons" name="gift-outline" color={tintColor} size={26} />
),
}),
},
@ -69,7 +61,7 @@ const BaseNavigator = createBottomTabNavigator(
tabBarComponent: props => <BottomTabBar {...props} />,
tabBarOptions: {
showLabel: false,
activeTintColor: '#357ce6',
activeTintColor: '#f6f6f6',
inactiveTintColor: '#c1c5c7',
style: {},
tabStyle: {},

View File

@ -1,5 +1,5 @@
import React, { PureComponent, Fragment } from 'react';
import { View } from 'react-native';
import { View, SafeAreaView } from 'react-native';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import { injectIntl } from 'react-intl';
@ -24,7 +24,7 @@ class HomeScreen extends PureComponent {
return (
<Fragment>
<Header />
<View style={styles.container}>
<SafeAreaView style={styles.container}>
<ScrollableTabView
style={globalStyles.tabView}
activeTab={!isLoggedIn ? 1 : 0}
@ -64,7 +64,7 @@ class HomeScreen extends PureComponent {
/>
</View>
</ScrollableTabView>
</View>
</SafeAreaView>
</Fragment>
);
}

View File

@ -5,7 +5,7 @@ export default EStyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
flex: 1,
backgroundColor: '$primaryBackgroundColor',
backgroundColor: '$primaryLightBackground',
},
buttonContainer: {
width: '50%',

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
import { View } from 'react-native';
import React, { PureComponent, Fragment } from 'react';
import { View, SafeAreaView } from 'react-native';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import { injectIntl } from 'react-intl';
@ -30,49 +30,51 @@ class NotificationScreen extends PureComponent {
} = this.props;
return (
<View style={styles.container}>
<Fragment>
<Header />
<ScrollableTabView
style={globalStyles.tabView}
renderTabBar={() => (
<TabBar style={styles.tabbar} tabUnderlineDefaultWidth={100} tabUnderlineScaleX={2} />
)}
>
<View
tabLabel={intl.formatMessage({
id: 'notification.notification',
})}
style={styles.tabbarItem}
>
{isLoggedIn ? (
<Notification
getActivities={getActivities}
notifications={notifications}
navigateToNotificationRoute={navigateToNotificationRoute}
readAllNotification={readAllNotification}
isNotificationRefreshing={isNotificationRefreshing}
changeSelectedFilter={changeSelectedFilter}
/>
) : (
<NoPost
isButtonText
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
handleOnButtonPress={handleLoginPress}
/>
<SafeAreaView style={styles.container}>
<ScrollableTabView
style={globalStyles.tabView}
renderTabBar={() => (
<TabBar style={styles.tabbar} tabUnderlineDefaultWidth={100} tabUnderlineScaleX={2} />
)}
</View>
<View
tabLabel={intl.formatMessage({
id: 'notification.leaderboard',
})}
style={styles.tabbarItem}
>
<LeaderBoard />
</View>
</ScrollableTabView>
</View>
<View
tabLabel={intl.formatMessage({
id: 'notification.notification',
})}
style={styles.tabbarItem}
>
{isLoggedIn ? (
<Notification
getActivities={getActivities}
notifications={notifications}
navigateToNotificationRoute={navigateToNotificationRoute}
readAllNotification={readAllNotification}
isNotificationRefreshing={isNotificationRefreshing}
changeSelectedFilter={changeSelectedFilter}
/>
) : (
<NoPost
isButtonText
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
handleOnButtonPress={handleLoginPress}
/>
)}
</View>
<View
tabLabel={intl.formatMessage({
id: 'notification.leaderboard',
})}
style={styles.tabbarItem}
>
<LeaderBoard />
</View>
</ScrollableTabView>
</SafeAreaView>
</Fragment>
);
}
}

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
import React, { PureComponent, Fragment } from 'react';
import { injectIntl } from 'react-intl';
import { View } from 'react-native';
import { View, SafeAreaView } from 'react-native';
// Containers
import { PointsContainer } from '../../../containers';
@ -30,45 +30,47 @@ class PointsScreen extends PureComponent {
const { intl, isLoggedIn, handleLoginPress } = this.props;
return (
<View style={styles.container}>
<Fragment>
<Header />
{isLoggedIn ? (
<PointsContainer>
{({
handleOnDropdownSelected,
claimPoints,
fetchUserActivity,
isClaiming,
isDarkTheme,
isLoading,
refreshing,
userActivities,
userPoints,
}) => (
<Points
claimPoints={claimPoints}
fetchUserActivity={fetchUserActivity}
isClaiming={isClaiming}
isDarkTheme={isDarkTheme}
isLoading={isLoading}
refreshing={refreshing}
userActivities={userActivities}
userPoints={userPoints}
handleOnDropdownSelected={handleOnDropdownSelected}
/>
)}
</PointsContainer>
) : (
<NoPost
style={styles.noPostContainer}
isButtonText
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
handleOnButtonPress={handleLoginPress}
/>
)}
</View>
<SafeAreaView style={styles.container}>
{isLoggedIn ? (
<PointsContainer>
{({
handleOnDropdownSelected,
claimPoints,
fetchUserActivity,
isClaiming,
isDarkTheme,
isLoading,
refreshing,
userActivities,
userPoints,
}) => (
<Points
claimPoints={claimPoints}
fetchUserActivity={fetchUserActivity}
isClaiming={isClaiming}
isDarkTheme={isDarkTheme}
isLoading={isLoading}
refreshing={refreshing}
userActivities={userActivities}
userPoints={userPoints}
handleOnDropdownSelected={handleOnDropdownSelected}
/>
)}
</PointsContainer>
) : (
<NoPost
style={styles.noPostContainer}
isButtonText
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
handleOnButtonPress={handleLoginPress}
/>
)}
</SafeAreaView>
</Fragment>
);
}
}

View File

@ -3,7 +3,7 @@ import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flex: 1,
backgroundColor: '$primaryBackgroundColor',
backgroundColor: '$primaryLightBackground',
},
image: {
width: 193,