Add navigation element animations

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2023-07-16 15:50:55 +03:00
parent 622d88cf40
commit 6e1f7bd81f
19 changed files with 450 additions and 277 deletions

View File

@@ -0,0 +1,101 @@
import React, { useEffect, useRef } from 'react';
import { BottomNavigation } from 'react-native-paper';
import { Animated, StyleSheet } from 'react-native';
import {
CommonActions,
NavigationHelpers,
ParamListBase,
TabNavigationState,
} from '@react-navigation/native';
import { EdgeInsets } from 'react-native-safe-area-context';
import { BottomTabNavigationEventMap } from '@react-navigation/bottom-tabs';
import { BottomTabDescriptorMap } from '@react-navigation/bottom-tabs/lib/typescript/src/types';
import { ROUTE } from '../types';
const hideableBottomNavigationBarStyles = StyleSheet.create({
bar: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
});
const HideableBottomNavigationBar = ({
navigation,
state,
descriptors,
insets,
visible = true,
routeCallback,
}: {
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>;
state: TabNavigationState<ParamListBase>;
descriptors: BottomTabDescriptorMap;
insets: EdgeInsets;
visible?: boolean;
routeCallback?: (route: ROUTE) => void;
}) => {
const visibleAnim = useRef(new Animated.Value(visible ? 0 : 1)).current;
useEffect(() => {
Animated.timing(visibleAnim, {
toValue: visible ? 0 : 1,
duration: visible ? 200 : 150,
useNativeDriver: true,
}).start();
}, [visible, visibleAnim]);
return (
<Animated.View
style={{
marginBottom: insets.bottom,
transform: [
{
translateY: visibleAnim.interpolate({
inputRange: [0, 1],
outputRange: [0, 80],
}),
},
],
...hideableBottomNavigationBarStyles.bar,
}}>
<BottomNavigation.Bar
navigationState={state}
safeAreaInsets={insets}
onTabPress={({ route, preventDefault }) => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (event.defaultPrevented) {
preventDefault();
} else {
navigation.dispatch({
...CommonActions.navigate(route.name, route.params),
target: state.key,
});
if (routeCallback) routeCallback(route.name as ROUTE);
}
}}
renderIcon={({ route, focused, color }) => {
const { options } = descriptors[route.key];
if (options.tabBarIcon) {
return options.tabBarIcon({
focused,
color,
size: 22,
});
}
}}
getLabelText={({ route }) => {
const { options } = descriptors[route.key];
return options.title ?? route.name;
}}
/>
</Animated.View>
);
};
export default HideableBottomNavigationBar;

View File

@@ -1,6 +1,5 @@
export { default as FloatingActionButton } from './floatingActionButton';
export { default as HideableBottomNavigationBar } from './hideableBottomNavigationBar';
export { default as LoadingView } from './loadingView';
export { default as RootScrollView } from './rootScrollView';
export { default as RootView } from './rootView';
export { default as TagChip } from './tagChip';
export { default as TagPreview } from './tagPreview';

View File

@@ -1,15 +1,21 @@
import React from 'react';
import { ActivityIndicator } from 'react-native';
import { ActivityIndicator, View } from 'react-native';
import { useTheme } from 'react-native-paper';
import { RootView } from '.';
import styles from '../styles';
const LoadingView = () => {
const { colors } = useTheme();
return (
<RootView centered>
<View
style={[
styles.centered,
styles.flex,
styles.fullSize,
{ backgroundColor: colors.background },
]}>
<ActivityIndicator size="large" color={colors.primary} />
</RootView>
</View>
);
};

View File

@@ -1,47 +0,0 @@
import React, { ReactNode } from 'react';
import { StyleProp, ScrollView, ViewStyle } from 'react-native';
import { useTheme } from 'react-native-paper';
import styles from '../styles';
import { useDimensions } from '../contexts';
const RootScrollView = ({
children,
style,
centered,
padded,
}: {
children: ReactNode;
style?: StyleProp<ViewStyle>;
centered?: boolean;
padded?: boolean;
}) => {
const { colors } = useTheme();
const { orientation } = useDimensions();
return (
<ScrollView
contentContainerStyle={[
padded &&
orientation == 'portrait' && [
styles.paddingHorizontal,
styles.paddingTop,
],
padded &&
orientation == 'landscape' && [
styles.paddingHorizontal,
styles.smallPaddingTop,
],
centered && [styles.centered, styles.flex],
styles.fullSize,
centered && [styles.centered, styles.flex],
styles.fullSize,
{ backgroundColor: colors.background },
style,
]}
nestedScrollEnabled>
{children}
</ScrollView>
);
};
export default RootScrollView;

View File

@@ -1,44 +0,0 @@
import React, { ReactNode } from 'react';
import { StyleProp, View, ViewStyle } from 'react-native';
import { useTheme } from 'react-native-paper';
import styles from '../styles';
import { useDimensions } from '../contexts';
const RootView = ({
children,
style,
centered,
padded,
}: {
children: ReactNode;
style?: StyleProp<ViewStyle>;
centered?: boolean;
padded?: boolean;
}) => {
const { colors } = useTheme();
const { orientation } = useDimensions();
return (
<View
style={[
padded &&
orientation == 'portrait' && [
styles.paddingHorizontal,
styles.paddingTop,
],
padded &&
orientation == 'landscape' && [
styles.paddingHorizontal,
styles.smallPaddingTop,
],
centered && [styles.centered, styles.flex],
styles.fullSize,
{ backgroundColor: colors.background },
style,
]}>
{children}
</View>
);
};
export default RootView;