mirror of
https://github.com/status-im/react-navigation.git
synced 2025-02-23 16:48:22 +00:00
Sync and switch SafeAreaView with standalone (#3452)
* add react-native-safe-area-view npm package * remove local SafeAreaView, import from package in views * update to latest react-native-safe-area-view * update snapshots
This commit is contained in:
parent
7385c244b7
commit
cf36f22e68
@ -33,6 +33,7 @@
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react-native-drawer-layout-polyfill": "^1.3.2",
|
||||
"react-native-safe-area-view": "^0.6.0",
|
||||
"react-native-tab-view": "^0.0.74"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Dimensions, Platform, ScrollView } from 'react-native';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import createNavigator from './createNavigator';
|
||||
import createNavigationContainer from '../createNavigationContainer';
|
||||
@ -7,7 +8,6 @@ import TabRouter from '../routers/TabRouter';
|
||||
import DrawerScreen from '../views/Drawer/DrawerScreen';
|
||||
import DrawerView from '../views/Drawer/DrawerView';
|
||||
import DrawerItems from '../views/Drawer/DrawerNavigatorItems';
|
||||
import SafeAreaView from '../views/SafeAreaView';
|
||||
|
||||
// A stack navigators props are the intersection between
|
||||
// the base navigator props (navgiation, screenProps, etc)
|
||||
|
@ -140,6 +140,7 @@ exports[`DrawerNavigator renders successfully 1`] = `
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"paddingBottom": 0,
|
||||
@ -187,6 +188,7 @@ exports[`DrawerNavigator renders successfully 1`] = `
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(0, 0, 0, .04)",
|
||||
|
@ -133,6 +133,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
@ -371,6 +372,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
|
@ -71,6 +71,7 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
|
2
src/react-navigation.js
vendored
2
src/react-navigation.js
vendored
@ -51,7 +51,7 @@ module.exports = {
|
||||
return require('./views/CardStack/Card').default;
|
||||
},
|
||||
get SafeAreaView() {
|
||||
return require('./views/SafeAreaView').default;
|
||||
return require('react-native-safe-area-view').default;
|
||||
},
|
||||
|
||||
// Header
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { View, Text, Platform, StyleSheet } from 'react-native';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import SafeAreaView from '../SafeAreaView';
|
||||
import TouchableItem from '../TouchableItem';
|
||||
|
||||
/**
|
||||
|
@ -1,12 +1,11 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import withCachedChildNavigation from '../../withCachedChildNavigation';
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import invariant from '../../utils/invariant';
|
||||
|
||||
import SafeAreaView from '../SafeAreaView';
|
||||
|
||||
/**
|
||||
* Component that renders the sidebar screen of the drawer.
|
||||
*/
|
||||
|
@ -8,11 +8,11 @@ import {
|
||||
View,
|
||||
ViewPropTypes,
|
||||
} from 'react-native';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import HeaderTitle from './HeaderTitle';
|
||||
import HeaderBackButton from './HeaderBackButton';
|
||||
import HeaderStyleInterpolator from './HeaderStyleInterpolator';
|
||||
import SafeAreaView from '../SafeAreaView';
|
||||
import withOrientation from '../withOrientation';
|
||||
|
||||
const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56;
|
||||
|
@ -1,316 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
DeviceInfo,
|
||||
Dimensions,
|
||||
InteractionManager,
|
||||
NativeModules,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Animated,
|
||||
} from 'react-native';
|
||||
import withOrientation from './withOrientation';
|
||||
|
||||
// See https://mydevice.io/devices/ for device dimensions
|
||||
const X_WIDTH = 375;
|
||||
const X_HEIGHT = 812;
|
||||
const PAD_WIDTH = 768;
|
||||
const PAD_HEIGHT = 1024;
|
||||
|
||||
const { height: D_HEIGHT, width: D_WIDTH } = Dimensions.get('window');
|
||||
|
||||
const { PlatformConstants = {} } = NativeModules;
|
||||
const { minor = 0 } = PlatformConstants.reactNativeVersion || {};
|
||||
|
||||
const isIPhoneX = (() => {
|
||||
if (Platform.OS === 'web') return false;
|
||||
|
||||
if (minor >= 50) {
|
||||
return DeviceInfo.isIPhoneX_deprecated;
|
||||
}
|
||||
|
||||
return (
|
||||
Platform.OS === 'ios' &&
|
||||
((D_HEIGHT === X_HEIGHT && D_WIDTH === X_WIDTH) ||
|
||||
(D_HEIGHT === X_WIDTH && D_WIDTH === X_HEIGHT))
|
||||
);
|
||||
})();
|
||||
|
||||
const isIPad = (() => {
|
||||
if (Platform.OS !== 'ios' || isIPhoneX) return false;
|
||||
|
||||
// if portrait and width is smaller than iPad width
|
||||
if (D_HEIGHT > D_WIDTH && D_WIDTH < PAD_WIDTH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if landscape and height is smaller that iPad height
|
||||
if (D_WIDTH > D_HEIGHT && D_HEIGHT < PAD_WIDTH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})();
|
||||
|
||||
let _customStatusBarHeight = null;
|
||||
const statusBarHeight = isLandscape => {
|
||||
if (_customStatusBarHeight !== null) {
|
||||
return _customStatusBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a temporary workaround because we don't have a way to detect
|
||||
* if the status bar is translucent or opaque. If opaque, we don't need to
|
||||
* factor in the height here; if translucent (content renders under it) then
|
||||
* we do.
|
||||
*/
|
||||
if (Platform.OS === 'android') {
|
||||
if (global.Expo) {
|
||||
return global.Expo.Constants.statusBarHeight;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (isIPhoneX) {
|
||||
return isLandscape ? 0 : 44;
|
||||
}
|
||||
|
||||
if (isIPad) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
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 {
|
||||
static setStatusBarHeight = height => {
|
||||
_customStatusBarHeight = height;
|
||||
};
|
||||
|
||||
state = {
|
||||
touchesTop: true,
|
||||
touchesBottom: true,
|
||||
touchesLeft: true,
|
||||
touchesRight: true,
|
||||
orientation: null,
|
||||
viewWidth: 0,
|
||||
viewHeight: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this._onLayout();
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps() {
|
||||
this._onLayout();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { forceInset = false, isLandscape, children, style } = this.props;
|
||||
|
||||
const safeAreaStyle = this._getSafeAreaStyle();
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
ref={c => (this.view = c)}
|
||||
onLayout={this._onLayout}
|
||||
style={safeAreaStyle}
|
||||
>
|
||||
{this.props.children}
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
_onLayout = () => {
|
||||
if (!this.view) return;
|
||||
|
||||
const { isLandscape } = this.props;
|
||||
const { orientation } = this.state;
|
||||
const newOrientation = isLandscape ? 'landscape' : 'portrait';
|
||||
if (orientation && orientation === newOrientation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const WIDTH = isLandscape ? X_HEIGHT : X_WIDTH;
|
||||
const HEIGHT = isLandscape ? X_WIDTH : X_HEIGHT;
|
||||
|
||||
this.view._component.measureInWindow((winX, winY, winWidth, winHeight) => {
|
||||
let realY = winY;
|
||||
let realX = winX;
|
||||
|
||||
if (realY >= HEIGHT) {
|
||||
realY = realY % HEIGHT;
|
||||
} else if (realY < 0) {
|
||||
realY = realY % HEIGHT + HEIGHT;
|
||||
}
|
||||
|
||||
if (realX >= WIDTH) {
|
||||
realX = realX % WIDTH;
|
||||
} else if (realX < 0) {
|
||||
realX = realX % WIDTH + WIDTH;
|
||||
}
|
||||
|
||||
const touchesTop = realY === 0;
|
||||
const touchesBottom = realY + winHeight >= HEIGHT;
|
||||
const touchesLeft = realX === 0;
|
||||
const touchesRight = realX + winWidth >= WIDTH;
|
||||
|
||||
this.setState({
|
||||
touchesTop,
|
||||
touchesBottom,
|
||||
touchesLeft,
|
||||
touchesRight,
|
||||
orientation: newOrientation,
|
||||
viewWidth: winWidth,
|
||||
viewHeight: winHeight,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_getSafeAreaStyle = () => {
|
||||
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,
|
||||
paddingRight: touchesRight ? this._getInset('right') : 0,
|
||||
};
|
||||
|
||||
if (forceInset) {
|
||||
Object.keys(forceInset).forEach(key => {
|
||||
let inset = forceInset[key];
|
||||
|
||||
if (inset === 'always') {
|
||||
inset = this._getInset(key);
|
||||
}
|
||||
|
||||
if (inset === 'never') {
|
||||
inset = 0;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case 'horizontal': {
|
||||
style.paddingLeft = inset;
|
||||
style.paddingRight = inset;
|
||||
break;
|
||||
}
|
||||
case 'vertical': {
|
||||
style.paddingTop = inset;
|
||||
style.paddingBottom = inset;
|
||||
break;
|
||||
}
|
||||
case 'left':
|
||||
case 'right':
|
||||
case 'top':
|
||||
case 'bottom': {
|
||||
const padding = `padding${key[0].toUpperCase()}${key.slice(1)}`;
|
||||
style[padding] = inset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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 = Math.max(style.paddingTop, paddingTop);
|
||||
style.paddingBottom = Math.max(style.paddingBottom, paddingBottom);
|
||||
style.paddingLeft = Math.max(style.paddingLeft, paddingLeft);
|
||||
style.paddingRight = Math.max(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) {
|
||||
case 'horizontal':
|
||||
case 'right':
|
||||
case 'left': {
|
||||
return isLandscape ? (isIPhoneX ? 44 : 0) : 0;
|
||||
}
|
||||
case 'vertical':
|
||||
case 'top': {
|
||||
return statusBarHeight(isLandscape);
|
||||
}
|
||||
case 'bottom': {
|
||||
return isIPhoneX ? (isLandscape ? 24 : 34) : 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default withOrientation(SafeView);
|
@ -7,8 +7,9 @@ import {
|
||||
Platform,
|
||||
Keyboard,
|
||||
} from 'react-native';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import TabBarIcon from './TabBarIcon';
|
||||
import SafeAreaView from '../SafeAreaView';
|
||||
import withOrientation from '../withOrientation';
|
||||
|
||||
const majorVersion = parseInt(Platform.Version, 10);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet, Platform } from 'react-native';
|
||||
import { TabViewAnimated, TabViewPagerPan } from 'react-native-tab-view';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import SceneView from '../SceneView';
|
||||
import withCachedChildNavigation from '../../withCachedChildNavigation';
|
||||
import SafeAreaView from '../SafeAreaView';
|
||||
|
||||
class TabView extends React.PureComponent {
|
||||
static defaultProps = {
|
||||
|
@ -27,6 +27,7 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
|
@ -2512,7 +2512,7 @@ hoek@4.x.x:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
|
||||
|
||||
hoist-non-react-statics@^2.2.0:
|
||||
hoist-non-react-statics@^2.2.0, hoist-non-react-statics@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0"
|
||||
|
||||
@ -4416,6 +4416,12 @@ react-native-drawer-layout@1.3.2:
|
||||
dependencies:
|
||||
react-native-dismiss-keyboard "1.0.0"
|
||||
|
||||
react-native-safe-area-view@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.6.0.tgz#ce01eb27905a77780219537e0f53fe9c783a8b3d"
|
||||
dependencies:
|
||||
hoist-non-react-statics "^2.3.1"
|
||||
|
||||
react-native-tab-view@^0.0.74:
|
||||
version "0.0.74"
|
||||
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.74.tgz#62c0c882d9232b461ce181d440d683b4f99d1bd8"
|
||||
|
Loading…
x
Reference in New Issue
Block a user