diff --git a/src/navigators/__tests__/__snapshots__/TabNavigator-test.js.snap b/src/navigators/__tests__/__snapshots__/TabNavigator-test.js.snap index 453a12fb..33e23bd7 100644 --- a/src/navigators/__tests__/__snapshots__/TabNavigator-test.js.snap +++ b/src/navigators/__tests__/__snapshots__/TabNavigator-test.js.snap @@ -105,68 +105,81 @@ exports[`TabNavigator renders successfully 1`] = ` "alignItems": "center", "backgroundColor": "rgba(0, 0, 0, 0)", "flex": 1, - "justifyContent": "flex-end", } } testID={undefined} > - + + + + + > + Welcome anonymous + - - Welcome anonymous - diff --git a/src/views/TabView/TabBarBottom.js b/src/views/TabView/TabBarBottom.js index b218d3c3..c261e60c 100644 --- a/src/views/TabView/TabBarBottom.js +++ b/src/views/TabView/TabBarBottom.js @@ -8,10 +8,12 @@ import { View, Platform, Keyboard, + Dimensions, } from 'react-native'; import TabBarIcon from './TabBarIcon'; import SafeAreaView from '../SafeAreaView'; import withOrientation from '../withOrientation'; +import type { Layout } from 'react-native-tab-view/src/TabViewTypeDefinitions'; import type { NavigationRoute, @@ -51,11 +53,16 @@ type Props = { tabStyle?: ViewStyleProp, showIcon?: boolean, isLandscape: boolean, + layout: Layout, + adaptive: boolean, }; const majorVersion = parseInt(Platform.Version, 10); const isIos = Platform.OS === 'ios'; -const useHorizontalTabs = majorVersion >= 11 && isIos; +const isIOS11 = majorVersion >= 11 && isIos; +const isTablet = + Dimensions.get('window').height / Dimensions.get('window').width < 1.6; +const defaultMaxTabBarItemWidth = 125; class TabBarBottom extends React.PureComponent { // See https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/UIKitUICatalog/UITabBar.html @@ -67,6 +74,7 @@ class TabBarBottom extends React.PureComponent { showLabel: true, showIcon: true, allowFontScaling: true, + adaptive: isIOS11, }; _renderLabel = (scene: TabScene) => { @@ -99,19 +107,18 @@ class TabBarBottom extends React.PureComponent { const tintColor = scene.focused ? activeTintColor : inactiveTintColor; const label = this.props.getLabel({ ...scene, tintColor }); - let marginLeft = 0; - if (isLandscape && showIcon && useHorizontalTabs) { - marginLeft = LABEL_LEFT_MARGIN; - } - let marginTop = 0; - if (!isLandscape && showIcon && useHorizontalTabs) { - marginTop = LABEL_TOP_MARGIN; - } if (typeof label === 'string') { return ( {label} @@ -147,7 +154,7 @@ class TabBarBottom extends React.PureComponent { inactiveTintColor={inactiveTintColor} renderIcon={renderIcon} scene={scene} - style={showLabel && useHorizontalTabs ? {} : styles.icon} + style={showLabel && this._shouldUseHorizontalTabs() ? {} : styles.icon} /> ); }; @@ -158,6 +165,65 @@ class TabBarBottom extends React.PureComponent { return testIDProps; }; + _tabItemMaxWidth() { + const { tabStyle, layout } = this.props; + let maxTabBarItemWidth; + + const flattenedTabStyle = StyleSheet.flatten(tabStyle); + + if (flattenedTabStyle) { + if (typeof flattenedTabStyle.width === 'number') { + maxTabBarItemWidth = flattenedTabStyle.width; + } else if ( + typeof flattenedTabStyle.width === 'string' && + flattenedTabStyle.endsWith('%') + ) { + const width = parseFloat(flattenedTabStyle.width); + if (Number.isFinite(width)) { + maxTabBarItemWidth = layout.width * (width / 100); + } + } else if (typeof flattenedTabStyle.maxWidth === 'number') { + maxTabBarItemWidth = flattenedTabStyle.maxWidth; + } else if ( + typeof flattenedTabStyle.maxWidth === 'string' && + flattenedTabStyle.endsWith('%') + ) { + const width = parseFloat(flattenedTabStyle.maxWidth); + if (Number.isFinite(width)) { + maxTabBarItemWidth = layout.width * (width / 100); + } + } + } + + if (!maxTabBarItemWidth) { + maxTabBarItemWidth = defaultMaxTabBarItemWidth; + } + + return maxTabBarItemWidth; + } + + _shouldUseHorizontalTabs() { + const { routes } = this.props.navigation.state; + const { isLandscape, layout, adaptive, tabStyle } = this.props; + + if (!adaptive) { + return false; + } + + let tabBarWidth = layout.width; + if (tabBarWidth === 0) { + return isTablet; + } + + const isHeightConstrained = layout.height < 500; + if (isHeightConstrained) { + return isLandscape; + } else { + const maxTabBarItemWidth = this._tabItemMaxWidth(); + return routes.length * maxTabBarItemWidth <= tabBarWidth; + } + } + render() { const { position, @@ -171,17 +237,20 @@ class TabBarBottom extends React.PureComponent { animateStyle, tabStyle, isLandscape, + layout, } = this.props; const { routes } = navigation.state; const previousScene = routes[navigation.state.index]; // Prepend '-1', so there are always at least 2 items in inputRange const inputRange = [-1, ...routes.map((x: *, i: number) => i)]; + const isHeightConstrained = + layout.height === 0 ? !isTablet : layout.height < 500; const tabBarStyle = [ styles.tabBar, - isLandscape && useHorizontalTabs - ? styles.tabBarLandscape - : styles.tabBarPortrait, + this._shouldUseHorizontalTabs() && isHeightConstrained + ? styles.tabBarCompact + : styles.tabBarRegular, style, ]; @@ -220,17 +289,19 @@ class TabBarBottom extends React.PureComponent { ? onPress({ previousScene, scene, jumpToIndex }) : jumpToIndex(index)} > - - {this._renderIcon(scene)} - {this._renderLabel(scene)} + + + {this._renderIcon(scene)} + {this._renderLabel(scene)} + ); @@ -241,8 +312,6 @@ class TabBarBottom extends React.PureComponent { } } -const LABEL_LEFT_MARGIN = 20; -const LABEL_TOP_MARGIN = 15; const styles = StyleSheet.create({ tabBar: { backgroundColor: '#F7F7F7', // Default background color in iOS 10 @@ -250,16 +319,15 @@ const styles = StyleSheet.create({ borderTopColor: 'rgba(0, 0, 0, .3)', flexDirection: 'row', }, - tabBarLandscape: { + tabBarCompact: { height: 29, }, - tabBarPortrait: { + tabBarRegular: { height: 49, }, tab: { flex: 1, alignItems: isIos ? 'center' : 'stretch', - justifyContent: 'flex-end', }, tabPortrait: { justifyContent: 'flex-end', @@ -274,9 +342,15 @@ const styles = StyleSheet.create({ }, label: { textAlign: 'center', + backgroundColor: 'transparent', + }, + labelBeneath: { fontSize: 10, marginBottom: 1.5, - backgroundColor: 'transparent', + }, + labelBeside: { + fontSize: 13, + marginLeft: 20, }, }); diff --git a/src/views/__tests__/__snapshots__/TabView-test.js.snap b/src/views/__tests__/__snapshots__/TabView-test.js.snap index 7f3877c3..9d4c1b7f 100644 --- a/src/views/__tests__/__snapshots__/TabView-test.js.snap +++ b/src/views/__tests__/__snapshots__/TabView-test.js.snap @@ -61,68 +61,81 @@ exports[`TabBarBottom renders successfully 1`] = ` "alignItems": "center", "backgroundColor": "rgba(0, 0, 0, 0)", "flex": 1, - "justifyContent": "flex-end", } } testID={undefined} > - + + + + + > + s1 + - - s1 -