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-native-community/masked-view": "0.1.6",
|
||||||
"@react-navigation/bottom-tabs": "^5.1.1",
|
"@react-navigation/bottom-tabs": "^5.1.1",
|
||||||
"@react-navigation/drawer": "^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/native": "^5.0.9",
|
||||||
"@react-navigation/stack": "^5.1.1",
|
"@react-navigation/stack": "^5.1.1",
|
||||||
"expo": "^37.0.0",
|
"expo": "^37.0.0",
|
||||||
|
|
|
@ -192,7 +192,7 @@ class SideBar extends React.Component<Props, State> {
|
||||||
try {
|
try {
|
||||||
const state = event.data.state.routes[0].state; // Get the Drawer's state if it exists
|
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.
|
// 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];
|
const routeName = state.routeNames[state.index];
|
||||||
if (this.state.activeRoute !== routeName)
|
if (this.state.activeRoute !== routeName)
|
||||||
this.setState({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 * as React from 'react';
|
||||||
import {createStackNavigator, TransitionPresets} from '@react-navigation/stack';
|
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 HomeScreen from '../screens/HomeScreen';
|
||||||
import PlanningScreen from '../screens/Planning/PlanningScreen';
|
import PlanningScreen from '../screens/Planning/PlanningScreen';
|
||||||
|
@ -11,9 +11,8 @@ import ProximoMainScreen from '../screens/Proximo/ProximoMainScreen';
|
||||||
import ProximoListScreen from "../screens/Proximo/ProximoListScreen";
|
import ProximoListScreen from "../screens/Proximo/ProximoListScreen";
|
||||||
import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen";
|
import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen";
|
||||||
import PlanexScreen from '../screens/Websites/PlanexScreen';
|
import PlanexScreen from '../screens/Websites/PlanexScreen';
|
||||||
import {MaterialCommunityIcons} from "@expo/vector-icons";
|
|
||||||
import AsyncStorageManager from "../managers/AsyncStorageManager";
|
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 i18n from "i18n-js";
|
||||||
import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen";
|
import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen";
|
||||||
import ScannerScreen from "../screens/ScannerScreen";
|
import ScannerScreen from "../screens/ScannerScreen";
|
||||||
|
@ -21,6 +20,7 @@ import MaterialHeaderButtons, {Item} from "../components/Custom/HeaderButton";
|
||||||
import FeedItemScreen from "../screens/FeedItemScreen";
|
import FeedItemScreen from "../screens/FeedItemScreen";
|
||||||
import {createCollapsibleStack} from "react-navigation-collapsible";
|
import {createCollapsibleStack} from "react-navigation-collapsible";
|
||||||
import GroupSelectionScreen from "../screens/GroupSelectionScreen";
|
import GroupSelectionScreen from "../screens/GroupSelectionScreen";
|
||||||
|
import CustomTabBar from "../components/Tabbar/CustomTabBar";
|
||||||
|
|
||||||
const TAB_ICONS = {
|
const TAB_ICONS = {
|
||||||
home: 'triangle',
|
home: 'triangle',
|
||||||
|
@ -306,7 +306,7 @@ function PlanexStackComponent() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Tab = createMaterialBottomTabNavigator();
|
const Tab = createBottomTabNavigator();
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
defaultRoute: string | null,
|
defaultRoute: string | null,
|
||||||
|
@ -329,16 +329,6 @@ class TabNavigator extends React.Component<Props> {
|
||||||
this.createHomeStackComponent = () => HomeStackComponent(props.defaultRoute, props.defaultData);
|
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() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Tab.Navigator
|
<Tab.Navigator
|
||||||
|
@ -349,14 +339,15 @@ class TabNavigator extends React.Component<Props> {
|
||||||
let icon = TAB_ICONS[route.name];
|
let icon = TAB_ICONS[route.name];
|
||||||
icon = focused ? icon : icon + ('-outline');
|
icon = focused ? icon : icon + ('-outline');
|
||||||
if (route.name !== "home")
|
if (route.name !== "home")
|
||||||
return <MaterialCommunityIcons name={icon} color={color} size={26}/>;
|
return icon;
|
||||||
else
|
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}
|
tabBar={props => <CustomTabBar {...props} />}
|
||||||
inactiveColor={this.props.theme.colors.tabIcon}
|
|
||||||
>
|
>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="proximo"
|
name="proximo"
|
||||||
|
|
Loading…
Reference in a new issue