Improve tab components to match linter
This commit is contained in:
parent
0117b25cd8
commit
aa992d20b2
3 changed files with 417 additions and 350 deletions
|
@ -2,179 +2,216 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {withTheme} from 'react-native-paper';
|
import {withTheme} from 'react-native-paper';
|
||||||
import TabIcon from "./TabIcon";
|
import Animated from 'react-native-reanimated';
|
||||||
import TabHomeIcon from "./TabHomeIcon";
|
import {Collapsible} from 'react-navigation-collapsible';
|
||||||
import {Animated} from 'react-native';
|
import {StackNavigationProp} from '@react-navigation/stack';
|
||||||
import {Collapsible} from "react-navigation-collapsible";
|
import TabIcon from './TabIcon';
|
||||||
|
import TabHomeIcon from './TabHomeIcon';
|
||||||
|
import type {CustomTheme} from '../../managers/ThemeManager';
|
||||||
|
|
||||||
type Props = {
|
type RouteType = {
|
||||||
state: Object,
|
name: string,
|
||||||
descriptors: Object,
|
key: string,
|
||||||
navigation: Object,
|
params: {collapsible: Collapsible},
|
||||||
theme: Object,
|
state: {
|
||||||
collapsibleStack: Object,
|
index: number,
|
||||||
}
|
routes: Array<RouteType>,
|
||||||
|
},
|
||||||
type State = {
|
|
||||||
translateY: AnimatedValue,
|
|
||||||
barSynced: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
const TAB_ICONS = {
|
|
||||||
proxiwash: 'tshirt-crew',
|
|
||||||
services: 'account-circle',
|
|
||||||
planning: 'calendar-range',
|
|
||||||
planex: 'clock',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CustomTabBar extends React.Component<Props, State> {
|
type PropsType = {
|
||||||
|
state: {
|
||||||
|
index: number,
|
||||||
|
routes: Array<RouteType>,
|
||||||
|
},
|
||||||
|
descriptors: {
|
||||||
|
[key: string]: {
|
||||||
|
options: {
|
||||||
|
tabBarLabel: string,
|
||||||
|
title: string,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
navigation: StackNavigationProp,
|
||||||
|
theme: CustomTheme,
|
||||||
|
};
|
||||||
|
|
||||||
static TAB_BAR_HEIGHT = 48;
|
type StateType = {
|
||||||
|
// eslint-disable-next-line flowtype/no-weak-types
|
||||||
|
translateY: any,
|
||||||
|
};
|
||||||
|
|
||||||
state = {
|
const TAB_ICONS = {
|
||||||
translateY: new Animated.Value(0),
|
proxiwash: 'tshirt-crew',
|
||||||
}
|
services: 'account-circle',
|
||||||
|
planning: 'calendar-range',
|
||||||
|
planex: 'clock',
|
||||||
|
};
|
||||||
|
|
||||||
syncTabBar = (route, index) => {
|
class CustomTabBar extends React.Component<PropsType, StateType> {
|
||||||
const state = this.props.state;
|
static TAB_BAR_HEIGHT = 48;
|
||||||
const isFocused = state.index === index;
|
|
||||||
if (isFocused) {
|
constructor() {
|
||||||
const stackState = route.state;
|
super();
|
||||||
const stackRoute = stackState ? stackState.routes[stackState.index] : undefined;
|
this.state = {
|
||||||
const params: { collapsible: Collapsible } = stackRoute ? stackRoute.params : undefined;
|
translateY: new Animated.Value(0),
|
||||||
const collapsible = params ? params.collapsible : undefined;
|
|
||||||
if (collapsible) {
|
|
||||||
this.setState({
|
|
||||||
translateY: Animated.multiply(-1.5, collapsible.translateY), // Hide tab bar faster than header bar
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates to the given route if it is different from the current one
|
* Navigates to the given route if it is different from the current one
|
||||||
*
|
*
|
||||||
* @param route Destination route
|
* @param route Destination route
|
||||||
* @param currentIndex The current route index
|
* @param currentIndex The current route index
|
||||||
* @param destIndex The destination route index
|
* @param destIndex The destination route index
|
||||||
*/
|
*/
|
||||||
onItemPress(route: Object, currentIndex: number, destIndex: number) {
|
onItemPress(route: RouteType, currentIndex: number, destIndex: number) {
|
||||||
const event = this.props.navigation.emit({
|
const {navigation} = this.props;
|
||||||
type: 'tabPress',
|
const event = navigation.emit({
|
||||||
target: route.key,
|
type: 'tabPress',
|
||||||
canPreventDefault: true,
|
target: route.key,
|
||||||
|
canPreventDefault: true,
|
||||||
|
});
|
||||||
|
if (currentIndex !== destIndex && !event.defaultPrevented)
|
||||||
|
navigation.navigate(route.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates to tetris screen on home button long press
|
||||||
|
*
|
||||||
|
* @param route
|
||||||
|
*/
|
||||||
|
onItemLongPress(route: RouteType) {
|
||||||
|
const {navigation} = this.props;
|
||||||
|
const event = navigation.emit({
|
||||||
|
type: 'tabLongPress',
|
||||||
|
target: route.key,
|
||||||
|
canPreventDefault: true,
|
||||||
|
});
|
||||||
|
if (route.name === 'home' && !event.defaultPrevented)
|
||||||
|
navigation.navigate('game-start');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the active route and syncs the tab bar animation with the header bar
|
||||||
|
*/
|
||||||
|
onRouteChange = () => {
|
||||||
|
const {props} = this;
|
||||||
|
props.state.routes.map(this.syncTabBar);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an icon for the given route if it is not the home one as it uses a custom button
|
||||||
|
*
|
||||||
|
* @param route
|
||||||
|
* @param focused
|
||||||
|
* @returns {null}
|
||||||
|
*/
|
||||||
|
getTabBarIcon = (route: RouteType, focused: boolean): React.Node => {
|
||||||
|
let icon = TAB_ICONS[route.name];
|
||||||
|
icon = focused ? icon : `${icon}-outline`;
|
||||||
|
if (route.name !== 'home') return icon;
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a tab icon render.
|
||||||
|
* If the given route is focused, it syncs the tab bar and header bar animations together
|
||||||
|
*
|
||||||
|
* @param route The route for the icon
|
||||||
|
* @param index The index of the current route
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
getRenderIcon = (route: RouteType, index: number): React.Node => {
|
||||||
|
const {props} = this;
|
||||||
|
const {state} = props;
|
||||||
|
const {options} = props.descriptors[route.key];
|
||||||
|
let label;
|
||||||
|
if (options.tabBarLabel != null) label = options.tabBarLabel;
|
||||||
|
else if (options.title != null) label = options.title;
|
||||||
|
else label = route.name;
|
||||||
|
|
||||||
|
const onPress = () => {
|
||||||
|
this.onItemPress(route, state.index, index);
|
||||||
|
};
|
||||||
|
const onLongPress = () => {
|
||||||
|
this.onItemLongPress(route);
|
||||||
|
};
|
||||||
|
const isFocused = state.index === index;
|
||||||
|
|
||||||
|
const color = isFocused
|
||||||
|
? props.theme.colors.primary
|
||||||
|
: props.theme.colors.tabIcon;
|
||||||
|
if (route.name !== 'home') {
|
||||||
|
return (
|
||||||
|
<TabIcon
|
||||||
|
onPress={onPress}
|
||||||
|
onLongPress={onLongPress}
|
||||||
|
icon={this.getTabBarIcon(route, isFocused)}
|
||||||
|
color={color}
|
||||||
|
label={label}
|
||||||
|
focused={isFocused}
|
||||||
|
extraData={state.index > index}
|
||||||
|
key={route.key}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<TabHomeIcon
|
||||||
|
onPress={onPress}
|
||||||
|
onLongPress={onLongPress}
|
||||||
|
focused={isFocused}
|
||||||
|
key={route.key}
|
||||||
|
tabBarHeight={CustomTabBar.TAB_BAR_HEIGHT}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
getIcons(): React.Node {
|
||||||
|
const {props} = this;
|
||||||
|
return props.state.routes.map(this.getRenderIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
syncTabBar = (route: RouteType, index: number) => {
|
||||||
|
const {state} = this.props;
|
||||||
|
const isFocused = state.index === index;
|
||||||
|
if (isFocused) {
|
||||||
|
const stackState = route.state;
|
||||||
|
const stackRoute =
|
||||||
|
stackState != null ? stackState.routes[stackState.index] : null;
|
||||||
|
const params: {collapsible: Collapsible} | null =
|
||||||
|
stackRoute != null ? stackRoute.params : null;
|
||||||
|
const collapsible = params != null ? params.collapsible : null;
|
||||||
|
if (collapsible != null) {
|
||||||
|
this.setState({
|
||||||
|
translateY: Animated.multiply(-1.5, collapsible.translateY), // Hide tab bar faster than header bar
|
||||||
});
|
});
|
||||||
if (currentIndex !== destIndex && !event.defaultPrevented)
|
}
|
||||||
this.props.navigation.navigate(route.name);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
render(): React.Node {
|
||||||
* Navigates to tetris screen on home button long press
|
const {props, state} = this;
|
||||||
*
|
props.navigation.addListener('state', this.onRouteChange);
|
||||||
* @param route
|
const icons = this.getIcons();
|
||||||
*/
|
// $FlowFixMe
|
||||||
onItemLongPress(route: Object) {
|
return (
|
||||||
const event = this.props.navigation.emit({
|
<Animated.View
|
||||||
type: 'tabLongPress',
|
useNativeDriver
|
||||||
target: route.key,
|
style={{
|
||||||
canPreventDefault: true,
|
flexDirection: 'row',
|
||||||
});
|
height: CustomTabBar.TAB_BAR_HEIGHT,
|
||||||
if (route.name === "home" && !event.defaultPrevented)
|
width: '100%',
|
||||||
this.props.navigation.navigate('game-start');
|
position: 'absolute',
|
||||||
}
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
/**
|
backgroundColor: props.theme.colors.surface,
|
||||||
* Gets an icon for the given route if it is not the home one as it uses a custom button
|
transform: [{translateY: state.translateY}],
|
||||||
*
|
}}>
|
||||||
* @param route
|
{icons}
|
||||||
* @param focused
|
</Animated.View>
|
||||||
* @returns {null}
|
);
|
||||||
*/
|
}
|
||||||
tabBarIcon = (route, focused) => {
|
|
||||||
let icon = TAB_ICONS[route.name];
|
|
||||||
icon = focused ? icon : icon + ('-outline');
|
|
||||||
if (route.name !== "home")
|
|
||||||
return icon;
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the active route and syncs the tab bar animation with the header bar
|
|
||||||
*/
|
|
||||||
onRouteChange = () => {
|
|
||||||
this.props.state.routes.map(this.syncTabBar)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a tab icon render.
|
|
||||||
* If the given route is focused, it syncs the tab bar and header bar animations together
|
|
||||||
*
|
|
||||||
* @param route The route for the icon
|
|
||||||
* @param index The index of the current route
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
renderIcon = (route, index) => {
|
|
||||||
const state = this.props.state;
|
|
||||||
const {options} = this.props.descriptors[route.key];
|
|
||||||
const label =
|
|
||||||
options.tabBarLabel != null
|
|
||||||
? options.tabBarLabel
|
|
||||||
: options.title != null
|
|
||||||
? options.title
|
|
||||||
: route.name;
|
|
||||||
|
|
||||||
const onPress = () => this.onItemPress(route, state.index, index);
|
|
||||||
const onLongPress = () => this.onItemLongPress(route);
|
|
||||||
const isFocused = state.index === index;
|
|
||||||
|
|
||||||
const color = isFocused ? this.props.theme.colors.primary : this.props.theme.colors.tabIcon;
|
|
||||||
if (route.name !== "home") {
|
|
||||||
return <TabIcon
|
|
||||||
onPress={onPress}
|
|
||||||
onLongPress={onLongPress}
|
|
||||||
icon={this.tabBarIcon(route, isFocused)}
|
|
||||||
color={color}
|
|
||||||
label={label}
|
|
||||||
focused={isFocused}
|
|
||||||
extraData={state.index > index}
|
|
||||||
key={route.key}
|
|
||||||
/>
|
|
||||||
} else
|
|
||||||
return <TabHomeIcon
|
|
||||||
onPress={onPress}
|
|
||||||
onLongPress={onLongPress}
|
|
||||||
focused={isFocused}
|
|
||||||
key={route.key}
|
|
||||||
tabBarHeight={CustomTabBar.TAB_BAR_HEIGHT}
|
|
||||||
/>
|
|
||||||
};
|
|
||||||
|
|
||||||
getIcons() {
|
|
||||||
return this.props.state.routes.map(this.renderIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
this.props.navigation.addListener('state', this.onRouteChange);
|
|
||||||
const icons = this.getIcons();
|
|
||||||
return (
|
|
||||||
<Animated.View
|
|
||||||
useNativeDriver
|
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
height: CustomTabBar.TAB_BAR_HEIGHT,
|
|
||||||
width: '100%',
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
backgroundColor: this.props.theme.colors.surface,
|
|
||||||
transform: [{translateY: this.state.translateY}],
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{icons}
|
|
||||||
</Animated.View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(CustomTabBar);
|
export default withTheme(CustomTabBar);
|
||||||
|
|
|
@ -1,106 +1,133 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Image, Platform, View} from "react-native";
|
import {Image, Platform, View} from 'react-native';
|
||||||
import {FAB, TouchableRipple, withTheme} from 'react-native-paper';
|
import {FAB, TouchableRipple, withTheme} from 'react-native-paper';
|
||||||
import * as Animatable from "react-native-animatable";
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
import FOCUSED_ICON from '../../../assets/tab-icon.png';
|
||||||
|
import UNFOCUSED_ICON from '../../../assets/tab-icon-outline.png';
|
||||||
|
import type {CustomTheme} from '../../managers/ThemeManager';
|
||||||
|
|
||||||
type Props = {
|
type PropsType = {
|
||||||
focused: boolean,
|
focused: boolean,
|
||||||
onPress: Function,
|
onPress: () => void,
|
||||||
onLongPress: Function,
|
onLongPress: () => void,
|
||||||
theme: Object,
|
theme: CustomTheme,
|
||||||
tabBarHeight: number,
|
tabBarHeight: number,
|
||||||
}
|
};
|
||||||
|
|
||||||
const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
|
const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction layer for Agenda component, using custom configuration
|
* Abstraction layer for Agenda component, using custom configuration
|
||||||
*/
|
*/
|
||||||
class TabHomeIcon extends React.Component<Props> {
|
class TabHomeIcon extends React.Component<PropsType> {
|
||||||
|
constructor(props: PropsType) {
|
||||||
|
super(props);
|
||||||
|
Animatable.initializeRegistryWithDefinitions({
|
||||||
|
fabFocusIn: {
|
||||||
|
'0': {
|
||||||
|
scale: 1,
|
||||||
|
translateY: 0,
|
||||||
|
},
|
||||||
|
'0.9': {
|
||||||
|
scale: 1.2,
|
||||||
|
translateY: -9,
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
scale: 1.1,
|
||||||
|
translateY: -7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fabFocusOut: {
|
||||||
|
'0': {
|
||||||
|
scale: 1.1,
|
||||||
|
translateY: -6,
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
scale: 1,
|
||||||
|
translateY: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
focusedIcon = require('../../../assets/tab-icon.png');
|
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||||
unFocusedIcon = require('../../../assets/tab-icon-outline.png');
|
const {focused} = this.props;
|
||||||
|
return nextProps.focused !== focused;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
getIconRender = ({
|
||||||
super(props);
|
size,
|
||||||
Animatable.initializeRegistryWithDefinitions({
|
color,
|
||||||
fabFocusIn: {
|
}: {
|
||||||
"0": {
|
size: number,
|
||||||
scale: 1, translateY: 0
|
color: string,
|
||||||
},
|
}): React.Node => {
|
||||||
"0.9": {
|
const {focused} = this.props;
|
||||||
scale: 1.2, translateY: -9
|
if (focused)
|
||||||
},
|
return (
|
||||||
"1": {
|
<Image
|
||||||
scale: 1.1, translateY: -7
|
source={FOCUSED_ICON}
|
||||||
},
|
style={{
|
||||||
},
|
width: size,
|
||||||
fabFocusOut: {
|
height: size,
|
||||||
"0": {
|
tintColor: color,
|
||||||
scale: 1.1, translateY: -6
|
}}
|
||||||
},
|
/>
|
||||||
"1": {
|
);
|
||||||
scale: 1, translateY: 0
|
return (
|
||||||
},
|
<Image
|
||||||
}
|
source={UNFOCUSED_ICON}
|
||||||
});
|
style={{
|
||||||
}
|
width: size,
|
||||||
|
height: size,
|
||||||
iconRender = ({size, color}) =>
|
tintColor: color,
|
||||||
this.props.focused
|
}}
|
||||||
? <Image
|
/>
|
||||||
source={this.focusedIcon}
|
);
|
||||||
style={{width: size, height: size, tintColor: color}}
|
};
|
||||||
/>
|
|
||||||
: <Image
|
|
||||||
source={this.unFocusedIcon}
|
|
||||||
style={{width: size, height: size, tintColor: color}}
|
|
||||||
/>;
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: Props): boolean {
|
|
||||||
return (nextProps.focused !== this.props.focused);
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): React$Node {
|
|
||||||
const props = this.props;
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}>
|
|
||||||
<TouchableRipple
|
|
||||||
onPress={props.onPress}
|
|
||||||
onLongPress={props.onLongPress}
|
|
||||||
borderless={true}
|
|
||||||
rippleColor={Platform.OS === 'android' ? this.props.theme.colors.primary : 'transparent'}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
height: this.props.tabBarHeight + 30,
|
|
||||||
marginBottom: -15,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AnimatedFAB
|
|
||||||
duration={200}
|
|
||||||
easing={"ease-out"}
|
|
||||||
animation={props.focused ? "fabFocusIn" : "fabFocusOut"}
|
|
||||||
icon={this.iconRender}
|
|
||||||
style={{
|
|
||||||
marginTop: 15,
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto'
|
|
||||||
}}/>
|
|
||||||
</TouchableRipple>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
render(): React.Node {
|
||||||
|
const {props} = this;
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}>
|
||||||
|
<TouchableRipple
|
||||||
|
onPress={props.onPress}
|
||||||
|
onLongPress={props.onLongPress}
|
||||||
|
borderless
|
||||||
|
rippleColor={
|
||||||
|
Platform.OS === 'android'
|
||||||
|
? props.theme.colors.primary
|
||||||
|
: 'transparent'
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: props.tabBarHeight + 30,
|
||||||
|
marginBottom: -15,
|
||||||
|
}}>
|
||||||
|
<AnimatedFAB
|
||||||
|
duration={200}
|
||||||
|
easing="ease-out"
|
||||||
|
animation={props.focused ? 'fabFocusIn' : 'fabFocusOut'}
|
||||||
|
icon={this.getIconRender}
|
||||||
|
style={{
|
||||||
|
marginTop: 15,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TouchableRipple>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(TabHomeIcon);
|
export default withTheme(TabHomeIcon);
|
||||||
|
|
|
@ -1,114 +1,117 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View} from "react-native";
|
import {View} from 'react-native';
|
||||||
import {TouchableRipple, withTheme} from 'react-native-paper';
|
import {TouchableRipple, withTheme} from 'react-native-paper';
|
||||||
import type {MaterialCommunityIconsGlyphs} from "react-native-vector-icons/MaterialCommunityIcons";
|
import type {MaterialCommunityIconsGlyphs} from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import * as Animatable from "react-native-animatable";
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
import type {CustomTheme} from '../../managers/ThemeManager';
|
||||||
type Props = {
|
|
||||||
focused: boolean,
|
|
||||||
color: string,
|
|
||||||
label: string,
|
|
||||||
icon: MaterialCommunityIconsGlyphs,
|
|
||||||
onPress: Function,
|
|
||||||
onLongPress: Function,
|
|
||||||
theme: Object,
|
|
||||||
extraData: any,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
type PropsType = {
|
||||||
|
focused: boolean,
|
||||||
|
color: string,
|
||||||
|
label: string,
|
||||||
|
icon: MaterialCommunityIconsGlyphs,
|
||||||
|
onPress: () => void,
|
||||||
|
onLongPress: () => void,
|
||||||
|
theme: CustomTheme,
|
||||||
|
extraData: null | boolean | number | string,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction layer for Agenda component, using custom configuration
|
* Abstraction layer for Agenda component, using custom configuration
|
||||||
*/
|
*/
|
||||||
class TabIcon extends React.Component<Props> {
|
class TabIcon extends React.Component<PropsType> {
|
||||||
|
firstRender: boolean;
|
||||||
|
|
||||||
firstRender: boolean;
|
constructor(props: PropsType) {
|
||||||
|
super(props);
|
||||||
|
Animatable.initializeRegistryWithDefinitions({
|
||||||
|
focusIn: {
|
||||||
|
'0': {
|
||||||
|
scale: 1,
|
||||||
|
translateY: 0,
|
||||||
|
},
|
||||||
|
'0.9': {
|
||||||
|
scale: 1.3,
|
||||||
|
translateY: 7,
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
scale: 1.2,
|
||||||
|
translateY: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
focusOut: {
|
||||||
|
'0': {
|
||||||
|
scale: 1.2,
|
||||||
|
translateY: 6,
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
scale: 1,
|
||||||
|
translateY: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.firstRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
componentDidMount() {
|
||||||
super(props);
|
this.firstRender = false;
|
||||||
Animatable.initializeRegistryWithDefinitions({
|
}
|
||||||
focusIn: {
|
|
||||||
"0": {
|
|
||||||
scale: 1, translateY: 0
|
|
||||||
},
|
|
||||||
"0.9": {
|
|
||||||
scale: 1.3, translateY: 7
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
scale: 1.2, translateY: 6
|
|
||||||
},
|
|
||||||
},
|
|
||||||
focusOut: {
|
|
||||||
"0": {
|
|
||||||
scale: 1.2, translateY: 6
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
scale: 1, translateY: 0
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.firstRender = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||||
this.firstRender = false;
|
const {props} = this;
|
||||||
}
|
return (
|
||||||
|
nextProps.focused !== props.focused ||
|
||||||
|
nextProps.theme.dark !== props.theme.dark ||
|
||||||
|
nextProps.extraData !== props.extraData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: Props): boolean {
|
render(): React.Node {
|
||||||
return (nextProps.focused !== this.props.focused)
|
const {props} = this;
|
||||||
|| (nextProps.theme.dark !== this.props.theme.dark)
|
return (
|
||||||
|| (nextProps.extraData !== this.props.extraData);
|
<TouchableRipple
|
||||||
}
|
onPress={props.onPress}
|
||||||
|
onLongPress={props.onLongPress}
|
||||||
render(): React$Node {
|
borderless
|
||||||
const props = this.props;
|
rippleColor={props.theme.colors.primary}
|
||||||
return (
|
style={{
|
||||||
<TouchableRipple
|
flex: 1,
|
||||||
onPress={props.onPress}
|
justifyContent: 'center',
|
||||||
onLongPress={props.onLongPress}
|
}}>
|
||||||
borderless={true}
|
<View>
|
||||||
rippleColor={this.props.theme.colors.primary}
|
<Animatable.View
|
||||||
style={{
|
duration={200}
|
||||||
flex: 1,
|
easing="ease-out"
|
||||||
justifyContent: 'center',
|
animation={props.focused ? 'focusIn' : 'focusOut'}
|
||||||
}}
|
useNativeDriver>
|
||||||
>
|
<MaterialCommunityIcons
|
||||||
<View>
|
name={props.icon}
|
||||||
<Animatable.View
|
color={props.color}
|
||||||
duration={200}
|
size={26}
|
||||||
easing={"ease-out"}
|
style={{
|
||||||
animation={props.focused ? "focusIn" : "focusOut"}
|
marginLeft: 'auto',
|
||||||
useNativeDriver
|
marginRight: 'auto',
|
||||||
>
|
}}
|
||||||
<MaterialCommunityIcons
|
/>
|
||||||
name={props.icon}
|
</Animatable.View>
|
||||||
color={props.color}
|
<Animatable.Text
|
||||||
size={26}
|
animation={props.focused ? 'fadeOutDown' : 'fadeIn'}
|
||||||
style={{
|
useNativeDriver
|
||||||
marginLeft: 'auto',
|
style={{
|
||||||
marginRight: 'auto',
|
color: props.color,
|
||||||
}}
|
marginLeft: 'auto',
|
||||||
/>
|
marginRight: 'auto',
|
||||||
</Animatable.View>
|
fontSize: 10,
|
||||||
<Animatable.Text
|
}}>
|
||||||
animation={props.focused ? "fadeOutDown" : "fadeIn"}
|
{props.label}
|
||||||
useNativeDriver
|
</Animatable.Text>
|
||||||
|
</View>
|
||||||
style={{
|
</TouchableRipple>
|
||||||
color: props.color,
|
);
|
||||||
marginLeft: 'auto',
|
}
|
||||||
marginRight: 'auto',
|
|
||||||
fontSize: 10,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.label}
|
|
||||||
</Animatable.Text>
|
|
||||||
</View>
|
|
||||||
</TouchableRipple>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(TabIcon);
|
export default withTheme(TabIcon);
|
||||||
|
|
Loading…
Reference in a new issue