mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-11 09:20:54 +08:00
SafeAreaView padding from style prop (#2889)
* SafeAreaView now adds padding from style object. If height is specified, inset padding is added to height. * Header now only accepts headerStyle prop, backgroundColor works as expected. * TabBarBottom now only accepts style prop, backgroundColor works as expected. Fixes top inset bug. * Update snapshot. * Add clarifying comment. * Support padding with percentage.
This commit is contained in:
@@ -135,10 +135,6 @@ Style object for the header
|
||||
|
||||
Style object for the title component
|
||||
|
||||
### `headerBackgroundColor`
|
||||
|
||||
Color string that overrides default background color.
|
||||
|
||||
#### `headerBackTitleStyle`
|
||||
|
||||
Style object for the back title
|
||||
|
||||
@@ -106,7 +106,6 @@ Several options get passed to the underlying router to modify navigation logic:
|
||||
- `inactiveBackgroundColor` - Background color of the inactive tab.
|
||||
- `showLabel` - Whether to show label for tab, default is true.
|
||||
- `style` - Style object for the tab bar.
|
||||
- `backgroundColor` - Color string that overrides default backgroundColor.
|
||||
- `labelStyle` - Style object for the tab label.
|
||||
- `tabStyle` - Style object for the tab.
|
||||
- `allowFontScaling` - Whether label font should scale to respect Text Size accessibility settings, default is true.
|
||||
|
||||
@@ -360,7 +360,6 @@ export type NavigationStackScreenOptions = {|
|
||||
headerPressColorAndroid?: string,
|
||||
headerRight?: React.Node,
|
||||
headerStyle?: ViewStyleProp,
|
||||
headerBackgroundColor?: string,
|
||||
gesturesEnabled?: boolean,
|
||||
gestureResponseDistance?: { vertical?: number, horizontal?: number },
|
||||
|};
|
||||
|
||||
@@ -94,15 +94,12 @@ exports[`DrawerNavigator renders successfully 1`] = `
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Array [
|
||||
undefined,
|
||||
Object {
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
@@ -142,17 +139,13 @@ exports[`DrawerNavigator renders successfully 1`] = `
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(0, 0, 0, .04)",
|
||||
},
|
||||
Object {
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"backgroundColor": "rgba(0, 0, 0, .04)",
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
|
||||
@@ -75,77 +75,65 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"borderBottomColor": "rgba(0, 0, 0, .3)",
|
||||
"borderBottomWidth": 0.5,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
},
|
||||
],
|
||||
cardStyle={undefined}
|
||||
collapsable={undefined}
|
||||
getScreenDetails={[Function]}
|
||||
headerMode={undefined}
|
||||
index={0}
|
||||
layout={
|
||||
Object {
|
||||
"height": 0,
|
||||
"initHeight": 0,
|
||||
"initWidth": 0,
|
||||
"isMeasured": false,
|
||||
"width": 0,
|
||||
}
|
||||
}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"dispatch": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "Init-id-0-1",
|
||||
"routeName": "Home",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
router={
|
||||
Object {
|
||||
"getActionForPathAndParams": [Function],
|
||||
"getComponentForRouteName": [Function],
|
||||
"getComponentForState": [Function],
|
||||
"getPathAndParamsForState": [Function],
|
||||
"getScreenConfig": [Function],
|
||||
"getScreenOptions": [Function],
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
transitionConfig={undefined}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
"borderBottomColor": "rgba(0, 0, 0, .3)",
|
||||
"borderBottomWidth": 0.5,
|
||||
"height": 64,
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
cardStyle={undefined}
|
||||
collapsable={undefined}
|
||||
getScreenDetails={[Function]}
|
||||
headerMode={undefined}
|
||||
index={0}
|
||||
layout={
|
||||
Object {
|
||||
"height": 0,
|
||||
"initHeight": 0,
|
||||
"initWidth": 0,
|
||||
"isMeasured": false,
|
||||
"width": 0,
|
||||
}
|
||||
}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"dispatch": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "Init-id-0-1",
|
||||
"routeName": "Home",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
router={
|
||||
Object {
|
||||
"getActionForPathAndParams": [Function],
|
||||
"getComponentForRouteName": [Function],
|
||||
"getComponentForState": [Function],
|
||||
"getPathAndParamsForState": [Function],
|
||||
"getScreenConfig": [Function],
|
||||
"getScreenOptions": [Function],
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
style={
|
||||
Object {
|
||||
"height": 44,
|
||||
}
|
||||
}
|
||||
transitionConfig={undefined}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
@@ -315,77 +303,65 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"borderBottomColor": "rgba(0, 0, 0, .3)",
|
||||
"borderBottomWidth": 0.5,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
},
|
||||
],
|
||||
cardStyle={undefined}
|
||||
collapsable={undefined}
|
||||
getScreenDetails={[Function]}
|
||||
headerMode={undefined}
|
||||
index={0}
|
||||
layout={
|
||||
Object {
|
||||
"height": 0,
|
||||
"initHeight": 0,
|
||||
"initWidth": 0,
|
||||
"isMeasured": false,
|
||||
"width": 0,
|
||||
}
|
||||
}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"dispatch": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "Init-id-0-0",
|
||||
"routeName": "Home",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
router={
|
||||
Object {
|
||||
"getActionForPathAndParams": [Function],
|
||||
"getComponentForRouteName": [Function],
|
||||
"getComponentForState": [Function],
|
||||
"getPathAndParamsForState": [Function],
|
||||
"getScreenConfig": [Function],
|
||||
"getScreenOptions": [Function],
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
transitionConfig={undefined}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
"borderBottomColor": "rgba(0, 0, 0, .3)",
|
||||
"borderBottomWidth": 0.5,
|
||||
"height": 64,
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
cardStyle={undefined}
|
||||
collapsable={undefined}
|
||||
getScreenDetails={[Function]}
|
||||
headerMode={undefined}
|
||||
index={0}
|
||||
layout={
|
||||
Object {
|
||||
"height": 0,
|
||||
"initHeight": 0,
|
||||
"initWidth": 0,
|
||||
"isMeasured": false,
|
||||
"width": 0,
|
||||
}
|
||||
}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"dispatch": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "Init-id-0-0",
|
||||
"routeName": "Home",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
router={
|
||||
Object {
|
||||
"getActionForPathAndParams": [Function],
|
||||
"getComponentForRouteName": [Function],
|
||||
"getComponentForState": [Function],
|
||||
"getPathAndParamsForState": [Function],
|
||||
"getScreenConfig": [Function],
|
||||
"getScreenOptions": [Function],
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
style={
|
||||
Object {
|
||||
"height": 44,
|
||||
}
|
||||
}
|
||||
transitionConfig={undefined}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
|
||||
@@ -65,33 +65,21 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"borderTopColor": "rgba(0, 0, 0, .3)",
|
||||
"borderTopWidth": 0.5,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
},
|
||||
],
|
||||
collapsable={undefined}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
"borderTopColor": "rgba(0, 0, 0, .3)",
|
||||
"borderTopWidth": 0.5,
|
||||
"flexDirection": "row",
|
||||
"height": 49,
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"height": 49,
|
||||
"paddingTop": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
@@ -302,12 +302,10 @@ class Header extends React.PureComponent<Props, State> {
|
||||
} = this.props;
|
||||
|
||||
const { options } = this.props.getScreenDetails(scene);
|
||||
const {
|
||||
headerStyle,
|
||||
headerBackgroundColor = Platform.OS === 'ios' ? '#F7F7F7' : '#FFF',
|
||||
} = options;
|
||||
const { headerStyle } = options;
|
||||
const appBarHeight = Platform.OS === 'ios' ? (isLandscape ? 32 : 44) : 56;
|
||||
const containerStyles = [
|
||||
styles.container,
|
||||
{
|
||||
height: appBarHeight,
|
||||
},
|
||||
@@ -315,14 +313,14 @@ class Header extends React.PureComponent<Props, State> {
|
||||
];
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={[styles.container, { backgroundColor: headerBackgroundColor }]}
|
||||
forceInset={{ top: 'always', bottom: 'never' }}
|
||||
>
|
||||
<Animated.View {...rest} style={containerStyles}>
|
||||
<Animated.View {...rest}>
|
||||
<SafeAreaView
|
||||
style={containerStyles}
|
||||
forceInset={{ top: 'always', bottom: 'never' }}
|
||||
>
|
||||
<View style={styles.appBar}>{appBar}</View>
|
||||
</Animated.View>
|
||||
</SafeAreaView>
|
||||
</SafeAreaView>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -347,6 +345,7 @@ if (Platform.OS === 'ios') {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: Platform.OS === 'ios' ? '#F7F7F7' : '#FFF',
|
||||
...platformContainerStyles,
|
||||
},
|
||||
appBar: {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
NativeModules,
|
||||
Platform,
|
||||
SafeAreaView,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import withOrientation from './withOrientation';
|
||||
@@ -62,6 +63,18 @@ const statusBarHeight = isLandscape => {
|
||||
return isLandscape ? 0 : 20;
|
||||
};
|
||||
|
||||
const doubleFromPercentString = percent => {
|
||||
if (!percent.includes('%')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const dbl = parseFloat(percent) / 100;
|
||||
|
||||
if (isNaN(dbl)) return 0;
|
||||
|
||||
return dbl;
|
||||
};
|
||||
|
||||
class SafeView extends Component {
|
||||
state = {
|
||||
touchesTop: true,
|
||||
@@ -69,6 +82,8 @@ class SafeView extends Component {
|
||||
touchesLeft: true,
|
||||
touchesRight: true,
|
||||
orientation: null,
|
||||
viewWidth: 0,
|
||||
viewHeight: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@@ -88,17 +103,13 @@ class SafeView extends Component {
|
||||
return <View style={style}>{this.props.children}</View>;
|
||||
}
|
||||
|
||||
if (!forceInset && minor >= 50) {
|
||||
return <SafeAreaView style={style}>{this.props.children}</SafeAreaView>;
|
||||
}
|
||||
|
||||
const safeAreaStyle = this._getSafeAreaStyle();
|
||||
|
||||
return (
|
||||
<View
|
||||
ref={c => (this.view = c)}
|
||||
onLayout={this._onLayout}
|
||||
style={[style, safeAreaStyle]}
|
||||
style={safeAreaStyle}
|
||||
>
|
||||
{this.props.children}
|
||||
</View>
|
||||
@@ -145,6 +156,8 @@ class SafeView extends Component {
|
||||
touchesLeft,
|
||||
touchesRight,
|
||||
orientation: newOrientation,
|
||||
viewWidth: winWidth,
|
||||
viewHeight: winHeight,
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -153,7 +166,16 @@ class SafeView extends Component {
|
||||
const { touchesTop, touchesBottom, touchesLeft, touchesRight } = this.state;
|
||||
const { forceInset, isLandscape } = this.props;
|
||||
|
||||
const {
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
paddingLeft,
|
||||
paddingRight,
|
||||
viewStyle,
|
||||
} = this._getViewStyles();
|
||||
|
||||
const style = {
|
||||
...viewStyle,
|
||||
paddingTop: touchesTop ? this._getInset('top') : 0,
|
||||
paddingBottom: touchesBottom ? this._getInset('bottom') : 0,
|
||||
paddingLeft: touchesLeft ? this._getInset('left') : 0,
|
||||
@@ -195,9 +217,64 @@ class SafeView extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
// new height/width should only include padding from insets
|
||||
// height/width should not be affected by padding from style obj
|
||||
if (style.height && typeof style.height === 'number') {
|
||||
style.height += style.paddingTop + style.paddingBottom;
|
||||
}
|
||||
|
||||
if (style.width && typeof style.width === 'number') {
|
||||
style.width += style.paddingLeft + style.paddingRight;
|
||||
}
|
||||
|
||||
style.paddingTop += paddingTop;
|
||||
style.paddingBottom += paddingBottom;
|
||||
style.paddingLeft += paddingLeft;
|
||||
style.paddingRight += paddingRight;
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
_getViewStyles = () => {
|
||||
const { viewWidth } = this.state;
|
||||
// get padding values from style to add back in after insets are determined
|
||||
// default precedence: padding[Side] -> vertical | horizontal -> padding -> 0
|
||||
let {
|
||||
padding = 0,
|
||||
paddingVertical = padding,
|
||||
paddingHorizontal = padding,
|
||||
paddingTop = paddingVertical,
|
||||
paddingBottom = paddingVertical,
|
||||
paddingLeft = paddingHorizontal,
|
||||
paddingRight = paddingHorizontal,
|
||||
...viewStyle
|
||||
} = StyleSheet.flatten(this.props.style || {});
|
||||
|
||||
if (typeof paddingTop !== 'number') {
|
||||
paddingTop = doubleFromPercentString(paddingTop) * viewWidth;
|
||||
}
|
||||
|
||||
if (typeof paddingBottom !== 'number') {
|
||||
paddingBottom = doubleFromPercentString(paddingBottom) * viewWidth;
|
||||
}
|
||||
|
||||
if (typeof paddingLeft !== 'number') {
|
||||
paddingLeft = doubleFromPercentString(paddingLeft) * viewWidth;
|
||||
}
|
||||
|
||||
if (typeof paddingRight !== 'number') {
|
||||
paddingRight = doubleFromPercentString(paddingRight) * viewWidth;
|
||||
}
|
||||
|
||||
return {
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
paddingLeft,
|
||||
paddingRight,
|
||||
viewStyle,
|
||||
};
|
||||
};
|
||||
|
||||
_getInset = key => {
|
||||
const { isLandscape } = this.props;
|
||||
switch (key) {
|
||||
|
||||
@@ -40,7 +40,6 @@ type Props = {
|
||||
getTestIDProps: (scene: TabScene) => (scene: TabScene) => any,
|
||||
renderIcon: (scene: TabScene) => React.Node,
|
||||
style?: ViewStyleProp,
|
||||
backgroundColor?: string,
|
||||
labelStyle?: TextStyleProp,
|
||||
tabStyle?: ViewStyleProp,
|
||||
showIcon?: boolean,
|
||||
@@ -165,7 +164,6 @@ class TabBarBottom extends React.PureComponent<Props> {
|
||||
inactiveBackgroundColor,
|
||||
style,
|
||||
tabStyle,
|
||||
backgroundColor = '#F7F7F7', // Default background color in iOS 10
|
||||
isLandscape,
|
||||
} = this.props;
|
||||
const { routes } = navigation.state;
|
||||
@@ -181,11 +179,11 @@ class TabBarBottom extends React.PureComponent<Props> {
|
||||
];
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={[styles.tabBarContainer, { backgroundColor }]}
|
||||
forceInset={{ bottom: 'always' }}
|
||||
>
|
||||
<Animated.View style={tabBarStyle}>
|
||||
<Animated.View>
|
||||
<SafeAreaView
|
||||
style={tabBarStyle}
|
||||
forceInset={{ bottom: 'always', top: 'never' }}
|
||||
>
|
||||
{routes.map((route: NavigationRoute, index: number) => {
|
||||
const focused = index === navigation.state.index;
|
||||
const scene = { route, index, focused };
|
||||
@@ -228,8 +226,8 @@ class TabBarBottom extends React.PureComponent<Props> {
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
})}
|
||||
</Animated.View>
|
||||
</SafeAreaView>
|
||||
</SafeAreaView>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -237,11 +235,10 @@ class TabBarBottom extends React.PureComponent<Props> {
|
||||
const LABEL_LEFT_MARGIN = 20;
|
||||
const LABEL_TOP_MARGIN = 15;
|
||||
const styles = StyleSheet.create({
|
||||
tabBarContainer: {
|
||||
tabBar: {
|
||||
backgroundColor: '#F7F7F7', // Default background color in iOS 10
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: 'rgba(0, 0, 0, .3)',
|
||||
},
|
||||
tabBar: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
tabBarLandscape: {
|
||||
|
||||
@@ -21,33 +21,21 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"borderTopColor": "rgba(0, 0, 0, .3)",
|
||||
"borderTopWidth": 0.5,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
},
|
||||
],
|
||||
collapsable={undefined}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
"borderTopColor": "rgba(0, 0, 0, .3)",
|
||||
"borderTopWidth": 0.5,
|
||||
"flexDirection": "row",
|
||||
"height": 49,
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"height": 49,
|
||||
"paddingTop": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user