Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
satya164 committed Sep 12, 2022
1 parent 62fdb21 commit 39258a5
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 64 deletions.
13 changes: 12 additions & 1 deletion example/src/Screens/BottomTabs.tsx
Expand Up @@ -12,7 +12,12 @@ import {
import type { StackScreenProps } from '@react-navigation/stack';
import { BlurView } from 'expo-blur';
import * as React from 'react';
import { ScrollView, StatusBar, StyleSheet } from 'react-native';
import {
ScrollView,
StatusBar,
StyleSheet,
useWindowDimensions,
} from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';

import Albums from '../Shared/Albums';
Expand Down Expand Up @@ -67,9 +72,15 @@ export default function BottomTabsScreen({
});
}, [navigation, routeName]);

const dimensions = useWindowDimensions();

const isLargeScreen = dimensions.width >= 1024;

return (
<BottomTabs.Navigator
screenOptions={{
tabBarPosition: isLargeScreen ? 'left' : 'bottom',
tabBarLabelPosition: 'below-icon',
headerLeft: (props) => (
<HeaderBackButton {...props} onPress={navigation.goBack} />
),
Expand Down
17 changes: 3 additions & 14 deletions example/src/index.tsx
Expand Up @@ -25,15 +25,14 @@ import { createURL } from 'expo-linking';
import * as SplashScreen from 'expo-splash-screen';
import * as React from 'react';
import {
Dimensions,
I18nManager,
Linking,
LogBox,
Platform,
ScaledSize,
ScrollView,
StatusBar,
Text,
useWindowDimensions,
} from 'react-native';
import {
DarkTheme as PaperDarkTheme,
Expand Down Expand Up @@ -118,18 +117,7 @@ export default function App() {
};
}, [theme.colors, theme.dark]);

const [dimensions, setDimensions] = React.useState(Dimensions.get('window'));

React.useEffect(() => {
const onDimensionsChange = ({ window }: { window: ScaledSize }) => {
setDimensions(window);
};

Dimensions.addEventListener('change', onDimensionsChange);

return () => Dimensions.removeEventListener('change', onDimensionsChange);
}, []);

const dimensions = useWindowDimensions();
const navigationRef = useNavigationContainerRef();

useReduxDevToolsExtension(navigationRef);
Expand Down Expand Up @@ -167,6 +155,7 @@ export default function App() {
// iOS (bare): xcrun simctl openurl booted rne://127.0.0.1:19000/--/simple-stack
// The first segment of the link is the the scheme + host (returned by `Linking.makeUrl`)
prefixes: [createURL('/')],
enabled: false,
config: {
initialRouteName: 'Home',
screens: SCREEN_NAMES.reduce<PathConfigMap<RootStackParamList>>(
Expand Down
5 changes: 5 additions & 0 deletions packages/bottom-tabs/src/types.tsx
Expand Up @@ -89,6 +89,11 @@ export type BottomTabNavigationOptions = HeaderOptions & {
*/
title?: string;

/**
* Position of the tab bar on the screen.
*/
tabBarPosition?: 'top' | 'bottom' | 'left' | 'right';

/**
* Title string of a tab displayed in the tab bar
* or a function that given { focused: boolean, color: string, position: 'below-icon' | 'beside-icon' } returns a React.Node to display in tab bar.
Expand Down
55 changes: 42 additions & 13 deletions packages/bottom-tabs/src/views/BottomTabBar.tsx
Expand Up @@ -140,6 +140,7 @@ export default function BottomTabBar({
const focusedOptions = focusedDescriptor.options;

const {
tabBarPosition,
tabBarShowLabel,
tabBarHideOnKeyboard = false,
tabBarVisibilityAnimationConfig,
Expand Down Expand Up @@ -252,16 +253,12 @@ export default function BottomTabBar({
});

const tabBarBackgroundElement = tabBarBackground?.();
const tabBarOnSides = tabBarPosition === 'left' || tabBarPosition === 'right';

return (
<Animated.View
style={[
styles.tabBar,
{
backgroundColor:
tabBarBackgroundElement != null ? 'transparent' : colors.card,
borderTopColor: colors.border,
},
{
transform: [
{
Expand All @@ -277,12 +274,35 @@ export default function BottomTabBar({
// Absolutely position the tab bar so that the content is below it
// This is needed to avoid gap at bottom when the tab bar is hidden
position: isTabBarHidden ? 'absolute' : (null as any),
backgroundColor:
tabBarBackgroundElement != null ? 'transparent' : colors.card,
borderColor: colors.border,
height: tabBarOnSides ? (null as any) : tabBarHeight,
},
{
height: tabBarHeight,
paddingBottom,
paddingHorizontal: Math.max(insets.left, insets.right),
},
tabBarOnSides
? [
{
paddingTop: insets.top,
paddingBottom: insets.bottom,
},
tabBarPosition === 'right'
? {
paddingRight: insets.right,
borderLeftWidth: StyleSheet.hairlineWidth,
}
: {
paddingLeft: insets.left,
borderRightWidth: StyleSheet.hairlineWidth,
},
]
: [
{
paddingHorizontal: Math.max(insets.left, insets.right),
},
tabBarPosition === 'top'
? { borderBottomWidth: StyleSheet.hairlineWidth }
: { borderTopWidth: StyleSheet.hairlineWidth, paddingBottom },
],
tabBarStyle,
]}
pointerEvents={isTabBarHidden ? 'none' : 'auto'}
Expand All @@ -291,7 +311,13 @@ export default function BottomTabBar({
<View pointerEvents="none" style={StyleSheet.absoluteFill}>
{tabBarBackgroundElement}
</View>
<View accessibilityRole="tablist" style={styles.content}>
<View
accessibilityRole="tablist"
style={[
styles.content,
{ flexDirection: tabBarOnSides ? 'column' : 'row' },
]}
>
{routes.map((route, index) => {
const focused = index === state.index;
const { options } = descriptors[route.key];
Expand Down Expand Up @@ -365,7 +391,7 @@ export default function BottomTabBar({
showLabel={tabBarShowLabel}
labelStyle={options.tabBarLabelStyle}
iconStyle={options.tabBarIconStyle}
style={options.tabBarItemStyle}
style={[styles.tabItem, options.tabBarItemStyle]}
/>
</NavigationRouteContext.Provider>
</NavigationContext.Provider>
Expand All @@ -381,9 +407,12 @@ const styles = StyleSheet.create({
left: 0,
right: 0,
bottom: 0,
borderTopWidth: StyleSheet.hairlineWidth,
elevation: 8,
},
tabItem: {
flex: 1,
alignItems: 'center',
},
content: {
flex: 1,
flexDirection: 'row',
Expand Down
8 changes: 2 additions & 6 deletions packages/bottom-tabs/src/views/BottomTabItem.tsx
Expand Up @@ -269,7 +269,6 @@ export default function BottomTabBarItem({
// @ts-expect-error: keep for compatibility with older React Native versions
accessibilityStates: focused ? ['selected'] : [],
style: [
styles.tab,
{ backgroundColor },
horizontal ? styles.tabLandscape : styles.tabPortrait,
style,
Expand All @@ -284,10 +283,6 @@ export default function BottomTabBarItem({
}

const styles = StyleSheet.create({
tab: {
flex: 1,
alignItems: 'center',
},
tabPortrait: {
justifyContent: 'flex-end',
flexDirection: 'column',
Expand All @@ -301,11 +296,12 @@ const styles = StyleSheet.create({
backgroundColor: 'transparent',
},
labelBeneath: {
marginTop: 5,
fontSize: 10,
},
labelBeside: {
fontSize: 13,
marginLeft: 20,
marginLeft: 5,
marginTop: 3,
},
button: {
Expand Down
38 changes: 26 additions & 12 deletions packages/bottom-tabs/src/views/BottomTabView.tsx
Expand Up @@ -45,6 +45,8 @@ export default function BottomTabView(props: Props) {
} = props;

const focusedRouteKey = state.routes[state.index].key;
const focusedOptions = descriptors[focusedRouteKey].options;

const [loaded, setLoaded] = React.useState([focusedRouteKey]);

if (!loaded.includes(focusedRouteKey)) {
Expand All @@ -62,12 +64,13 @@ export default function BottomTabView(props: Props) {
...SafeAreaProviderCompat.initialMetrics.insets,
...props.safeAreaInsets,
},
style: descriptors[state.routes[state.index].key].options.tabBarStyle,
style: focusedOptions.tabBarStyle,
})
);

const renderTabBar = () => {
return (
const tabBarPosition = focusedOptions.tabBarPosition ?? 'bottom';
const tabBarElement = (
<BottomTabBarHeightCallbackContext.Provider value={setTabBarHeight}>
<SafeAreaInsetsContext.Consumer>
{(insets) =>
tabBar({
Expand All @@ -83,19 +86,29 @@ export default function BottomTabView(props: Props) {
})
}
</SafeAreaInsetsContext.Consumer>
);
};
</BottomTabBarHeightCallbackContext.Provider>
);

const { routes } = state;
const flexDirectionForPosition = {
left: 'row-reverse',
right: 'row',
top: 'column-reverse',
bottom: 'column',
} as const;

return (
<SafeAreaProviderCompat>
<SafeAreaProviderCompat
style={[
styles.container,
{ flexDirection: flexDirectionForPosition[tabBarPosition] },
]}
>
<MaybeScreenContainer
enabled={detachInactiveScreens}
hasTwoStates
style={styles.container}
style={styles.screens}
>
{routes.map((route, index) => {
{state.routes.map((route, index) => {
const descriptor = descriptors[route.key];
const { lazy = true, unmountOnBlur } = descriptor.options;
const isFocused = state.index === index;
Expand Down Expand Up @@ -152,16 +165,17 @@ export default function BottomTabView(props: Props) {
);
})}
</MaybeScreenContainer>
<BottomTabBarHeightCallbackContext.Provider value={setTabBarHeight}>
{renderTabBar()}
</BottomTabBarHeightCallbackContext.Provider>
{tabBarElement}
</SafeAreaProviderCompat>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
},
screens: {
flex: 1,
overflow: 'hidden',
},
});
32 changes: 14 additions & 18 deletions packages/bottom-tabs/src/views/TabBarIcon.tsx
Expand Up @@ -27,6 +27,8 @@ type Props = {
style: StyleProp<ViewStyle>;
};

const ICON_SIZE = 25;

export default function TabBarIcon({
route: _,
horizontal,
Expand All @@ -39,25 +41,21 @@ export default function TabBarIcon({
renderIcon,
style,
}: Props) {
const size = 25;

// We render the icon twice at the same position on top of each other:
// active and inactive one, so we can fade between them.
return (
<View
style={[horizontal ? styles.iconHorizontal : styles.iconVertical, style]}
>
<View style={[styles.iconContainer, style]}>
<View style={[styles.icon, { opacity: activeOpacity }]}>
{renderIcon({
focused: true,
size,
size: ICON_SIZE,
color: activeTintColor,
})}
</View>
<View style={[styles.icon, { opacity: inactiveOpacity }]}>
{renderIcon({
focused: false,
size,
size: ICON_SIZE,
color: inactiveTintColor,
})}
</View>
Expand All @@ -68,7 +66,7 @@ export default function TabBarIcon({
horizontal ? styles.badgeHorizontal : styles.badgeVertical,
badgeStyle,
]}
size={(size * 3) / 4}
size={(ICON_SIZE * 3) / 4}
>
{badge}
</Badge>
Expand All @@ -88,23 +86,21 @@ const styles = StyleSheet.create({
height: '100%',
width: '100%',
// Workaround for react-native >= 0.54 layout bug
minWidth: 25,
minWidth: ICON_SIZE,
},
iconVertical: {
flex: 1,
},
iconHorizontal: {
height: '100%',
marginTop: 3,
iconContainer: {
width: ICON_SIZE,
aspectRatio: 1,
},
badge: {
position: 'absolute',
left: 3,
},
badgeVertical: {
top: 3,
top: -2,
left: 16,
},
badgeHorizontal: {
top: 7,
top: -8,
left: 16,
},
});

0 comments on commit 39258a5

Please sign in to comment.