forked from vergnet/application-amicale
Created custom tab bar
This commit is contained in:
parent
26cf3cf2c3
commit
0385e8ad76
6 changed files with 282 additions and 21 deletions
|
@ -24,7 +24,6 @@
|
|||
"@react-native-community/masked-view": "0.1.6",
|
||||
"@react-navigation/bottom-tabs": "^5.1.1",
|
||||
"@react-navigation/drawer": "^5.1.1",
|
||||
"@react-navigation/material-bottom-tabs": "^5.1.1",
|
||||
"@react-navigation/native": "^5.0.9",
|
||||
"@react-navigation/stack": "^5.1.1",
|
||||
"expo": "^37.0.0",
|
||||
|
|
|
@ -192,7 +192,7 @@ class SideBar extends React.Component<Props, State> {
|
|||
try {
|
||||
const state = event.data.state.routes[0].state; // Get the Drawer's state if it exists
|
||||
// Get the current route name. This will only show Drawer routes.
|
||||
// Tab routes will be shown as 'Main'
|
||||
// Tabbar routes will be shown as 'Main'
|
||||
const routeName = state.routeNames[state.index];
|
||||
if (this.state.activeRoute !== routeName)
|
||||
this.setState({activeRoute: routeName});
|
||||
|
|
81
src/components/Tabbar/CustomTabBar.js
Normal file
81
src/components/Tabbar/CustomTabBar.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
import * as React from 'react';
|
||||
import {View} from "react-native";
|
||||
import {withTheme} from 'react-native-paper';
|
||||
import TabIcon from "./TabIcon";
|
||||
import TabHomeIcon from "./TabHomeIcon";
|
||||
|
||||
type Props = {
|
||||
state: Object,
|
||||
descriptors: Object,
|
||||
navigation: Object,
|
||||
theme: Object,
|
||||
}
|
||||
|
||||
const TAB_BAR_HEIGHT = 48;
|
||||
|
||||
/**
|
||||
* Abstraction layer for Agenda component, using custom configuration
|
||||
*/
|
||||
class CustomTabBar extends React.Component<Props> {
|
||||
|
||||
render() {
|
||||
const state = this.props.state;
|
||||
const descriptors = this.props.descriptors;
|
||||
const navigation = this.props.navigation;
|
||||
return (
|
||||
<View style={{
|
||||
flexDirection: 'row',
|
||||
height: TAB_BAR_HEIGHT,
|
||||
}}>
|
||||
{state.routes.map((route, index) => {
|
||||
const {options} = descriptors[route.key];
|
||||
const label =
|
||||
options.tabBarLabel !== undefined
|
||||
? options.tabBarLabel
|
||||
: options.title !== undefined
|
||||
? options.title
|
||||
: route.name;
|
||||
|
||||
const isFocused = state.index === index;
|
||||
|
||||
const onPress = () => {
|
||||
const event = navigation.emit({
|
||||
type: 'tabPress',
|
||||
target: route.key,
|
||||
canPreventDefault: true,
|
||||
});
|
||||
|
||||
if (!isFocused && !event.defaultPrevented) {
|
||||
navigation.navigate(route.name);
|
||||
}
|
||||
};
|
||||
|
||||
const onLongPress = () => {
|
||||
navigation.emit({
|
||||
type: 'tabLongPress',
|
||||
target: route.key,
|
||||
});
|
||||
};
|
||||
|
||||
const color = isFocused ? options.activeColor : options.inactiveColor;
|
||||
const iconData = {focused: isFocused, color: color};
|
||||
if (route.name !== "home") {
|
||||
return <TabIcon
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
icon={options.tabBarIcon(iconData)}
|
||||
color={color}
|
||||
label={label}
|
||||
focused={isFocused}/>
|
||||
} else
|
||||
return <TabHomeIcon
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
focused={isFocused}/>
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(CustomTabBar);
|
79
src/components/Tabbar/TabHomeIcon.js
Normal file
79
src/components/Tabbar/TabHomeIcon.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {View} from "react-native";
|
||||
import {FAB, withTheme} from 'react-native-paper';
|
||||
import * as Animatable from "react-native-animatable";
|
||||
|
||||
type Props = {
|
||||
focused: boolean,
|
||||
onPress: Function,
|
||||
onLongPress: Function,
|
||||
theme: Object,
|
||||
}
|
||||
|
||||
const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
|
||||
|
||||
/**
|
||||
* Abstraction layer for Agenda component, using custom configuration
|
||||
*/
|
||||
class TabHomeIcon extends React.Component<Props> {
|
||||
|
||||
focusedIcon = require('../../../assets/tab-icon.png');
|
||||
unFocusedIcon = require('../../../assets/tab-icon-outline.png');
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
Animatable.initializeRegistryWithDefinitions({
|
||||
fabFocusIn: {
|
||||
"0": {
|
||||
scale: 1, translateY: 0
|
||||
},
|
||||
"0.9": {
|
||||
scale: 1.4, translateY: -6
|
||||
},
|
||||
"1": {
|
||||
scale: 1.3, translateY: -5
|
||||
},
|
||||
},
|
||||
fabFocusOut: {
|
||||
"0": {
|
||||
scale: 1.3, translateY: -5
|
||||
},
|
||||
"1": {
|
||||
scale: 1, translateY: 0
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: Props): boolean {
|
||||
return (nextProps.focused !== this.props.focused);
|
||||
}
|
||||
|
||||
render(): React$Node {
|
||||
const props = this.props;
|
||||
return (
|
||||
<View style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<AnimatedFAB
|
||||
duration={200}
|
||||
easing={"ease-out"}
|
||||
animation={props.focused ? "fabFocusIn" : "fabFocusOut"}
|
||||
useNativeDriver
|
||||
onPress={props.onPress}
|
||||
onLongPress={props.onLongPress}
|
||||
icon={this.focusedIcon}
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto'
|
||||
}}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withTheme(TabHomeIcon);
|
111
src/components/Tabbar/TabIcon.js
Normal file
111
src/components/Tabbar/TabIcon.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {View} from "react-native";
|
||||
import {TouchableRipple, withTheme} from 'react-native-paper';
|
||||
import {MaterialCommunityIcons} from "@expo/vector-icons";
|
||||
import * as Animatable from "react-native-animatable";
|
||||
|
||||
type Props = {
|
||||
focused: boolean,
|
||||
color: string,
|
||||
label: string,
|
||||
icon: string,
|
||||
onPress: Function,
|
||||
onLongPress: Function,
|
||||
theme: Object,
|
||||
}
|
||||
|
||||
const AnimatedIcon = Animatable.createAnimatableComponent(MaterialCommunityIcons);
|
||||
|
||||
|
||||
/**
|
||||
* Abstraction layer for Agenda component, using custom configuration
|
||||
*/
|
||||
class TabIcon extends React.Component<Props> {
|
||||
|
||||
firstRender: boolean;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
Animatable.initializeRegistryWithDefinitions({
|
||||
focusIn: {
|
||||
"0": {
|
||||
scale: 1, translateY: 0
|
||||
},
|
||||
"0.9": {
|
||||
scale: 1.6, translateY: 6
|
||||
},
|
||||
"1": {
|
||||
scale: 1.5, translateY: 5
|
||||
},
|
||||
},
|
||||
focusOut: {
|
||||
"0": {
|
||||
scale: 1.5, translateY: 5
|
||||
},
|
||||
"1": {
|
||||
scale: 1, translateY: 0
|
||||
},
|
||||
}
|
||||
});
|
||||
this.firstRender = true;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.firstRender = false;
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: Props): boolean {
|
||||
return (nextProps.focused !== this.props.focused)
|
||||
|| (nextProps.theme.dark !== this.props.theme.dark);
|
||||
}
|
||||
|
||||
render(): React$Node {
|
||||
const props = this.props;
|
||||
return (
|
||||
<TouchableRipple
|
||||
onPress={props.onPress}
|
||||
onLongPress={props.onLongPress}
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItem: 'center',
|
||||
}}
|
||||
>
|
||||
<View>
|
||||
<Animatable.View
|
||||
duration={200}
|
||||
easing={"ease-out"}
|
||||
animation={props.focused ? "focusIn" : "focusOut"}
|
||||
useNativeDriver
|
||||
>
|
||||
<AnimatedIcon
|
||||
name={props.icon}
|
||||
color={props.color}
|
||||
size={26}
|
||||
style={{
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
/>
|
||||
</Animatable.View>
|
||||
<Animatable.Text
|
||||
animation={props.focused ? "fadeOutDown" : "fadeIn"}
|
||||
useNativeDriver
|
||||
|
||||
style={{
|
||||
color: props.color,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
>
|
||||
{props.label}
|
||||
</Animatable.Text>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(TabIcon);
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import {createStackNavigator, TransitionPresets} from '@react-navigation/stack';
|
||||
import {createMaterialBottomTabNavigator} from "@react-navigation/material-bottom-tabs";
|
||||
import {createBottomTabNavigator} from "@react-navigation/bottom-tabs";
|
||||
|
||||
import HomeScreen from '../screens/HomeScreen';
|
||||
import PlanningScreen from '../screens/Planning/PlanningScreen';
|
||||
|
@ -11,9 +11,8 @@ import ProximoMainScreen from '../screens/Proximo/ProximoMainScreen';
|
|||
import ProximoListScreen from "../screens/Proximo/ProximoListScreen";
|
||||
import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen";
|
||||
import PlanexScreen from '../screens/Websites/PlanexScreen';
|
||||
import {MaterialCommunityIcons} from "@expo/vector-icons";
|
||||
import AsyncStorageManager from "../managers/AsyncStorageManager";
|
||||
import {FAB, useTheme, withTheme} from 'react-native-paper';
|
||||
import {useTheme, withTheme} from 'react-native-paper';
|
||||
import i18n from "i18n-js";
|
||||
import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen";
|
||||
import ScannerScreen from "../screens/ScannerScreen";
|
||||
|
@ -21,6 +20,7 @@ import MaterialHeaderButtons, {Item} from "../components/Custom/HeaderButton";
|
|||
import FeedItemScreen from "../screens/FeedItemScreen";
|
||||
import {createCollapsibleStack} from "react-navigation-collapsible";
|
||||
import GroupSelectionScreen from "../screens/GroupSelectionScreen";
|
||||
import CustomTabBar from "../components/Tabbar/CustomTabBar";
|
||||
|
||||
const TAB_ICONS = {
|
||||
home: 'triangle',
|
||||
|
@ -306,7 +306,7 @@ function PlanexStackComponent() {
|
|||
);
|
||||
}
|
||||
|
||||
const Tab = createMaterialBottomTabNavigator();
|
||||
const Tab = createBottomTabNavigator();
|
||||
|
||||
type Props = {
|
||||
defaultRoute: string | null,
|
||||
|
@ -329,16 +329,6 @@ class TabNavigator extends React.Component<Props> {
|
|||
this.createHomeStackComponent = () => HomeStackComponent(props.defaultRoute, props.defaultData);
|
||||
}
|
||||
|
||||
getHomeButton(focused: boolean) {
|
||||
let icon = focused ? require('../../assets/tab-icon.png') : require('../../assets/tab-icon-outline.png')
|
||||
return (
|
||||
<FAB
|
||||
icon={icon}
|
||||
small
|
||||
style={{position: 'absolute', top: '-25%'}}/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Tab.Navigator
|
||||
|
@ -349,14 +339,15 @@ class TabNavigator extends React.Component<Props> {
|
|||
let icon = TAB_ICONS[route.name];
|
||||
icon = focused ? icon : icon + ('-outline');
|
||||
if (route.name !== "home")
|
||||
return <MaterialCommunityIcons name={icon} color={color} size={26}/>;
|
||||
return icon;
|
||||
else
|
||||
return this.getHomeButton(focused);
|
||||
return null;
|
||||
},
|
||||
tabBarLabel: route.name !== 'home' ? undefined : ''
|
||||
tabBarLabel: route.name !== 'home' ? undefined : '',
|
||||
activeColor: this.props.theme.colors.primary,
|
||||
inactiveColor: this.props.theme.colors.tabIcon
|
||||
})}
|
||||
activeColor={this.props.theme.colors.primary}
|
||||
inactiveColor={this.props.theme.colors.tabIcon}
|
||||
tabBar={props => <CustomTabBar {...props} />}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="proximo"
|
||||
|
|
Loading…
Reference in a new issue