react-native-1app
Version:
237 lines (194 loc) • 6.67 kB
JavaScript
import React, { Component } from "react";
import { Dimensions, Platform, StyleSheet } from "react-native";
import PropTypes from "prop-types";
/**
* DETECTION AND DIMENSIONS CODE FROM:
* https://github.com/react-community/react-native-safe-area-view
*/
const X_WIDTH = 375; // iPhone X
const X_HEIGHT = 812; // iPhone X
const PAD_WIDTH = 768; // iPad
const PAD_HEIGHT = 1024; // iPad
const { height: D_HEIGHT, width: D_WIDTH } = Dimensions.get("window");
const isIPhoneX = (() => {
if (Platform.OS === "web") return false;
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;
})();
const isOrientationLandscape = ({ width, height }) => width > height;
let _customStatusBarHeight = null;
const statusBarHeight = (isLandscape = false) => {
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;
};
/**
* Helper function to "append" extra padding in MessageComponent style
*/
export function styleWithInset(style, wrapperInset, hideStatusBar = false, prop = "padding") {
if (prop === "margin") {
return styleWithInsetMargin(style, wrapperInset, hideStatusBar);
}
const { width: viewWidth } = Dimensions.get("window");
let {
padding = 0,
paddingVertical = padding,
paddingHorizontal = padding,
paddingTop = paddingVertical,
paddingBottom = paddingVertical,
paddingLeft = paddingHorizontal,
paddingRight = paddingHorizontal,
...viewStyle
} = StyleSheet.flatten(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 {
...viewStyle,
paddingTop: !!wrapperInset.isIPhoneX || !hideStatusBar ? paddingTop + wrapperInset.insetTop : paddingTop,
paddingBottom: paddingBottom + wrapperInset.insetBottom,
paddingLeft: paddingLeft + wrapperInset.insetLeft,
paddingRight: paddingRight + wrapperInset.insetRight,
};
}
/**
* Helper function to "append" extra margin in MessageComponent style
*/
export function styleWithInsetMargin(style, wrapperInset, hideStatusBar = false) {
const { width: viewWidth } = Dimensions.get("window");
let {
margin = 0,
marginVertical = margin,
marginHorizontal = margin,
marginTop = marginVertical,
marginBottom = marginVertical,
marginLeft = marginHorizontal,
marginRight = marginHorizontal,
...viewStyle
} = StyleSheet.flatten(style || {});
if (typeof marginTop !== "number") {
marginTop = doubleFromPercentString(marginTop) * viewWidth;
}
if (typeof marginBottom !== "number") {
marginBottom = doubleFromPercentString(marginBottom) * viewWidth;
}
if (typeof marginLeft !== "number") {
marginLeft = doubleFromPercentString(marginLeft) * viewWidth;
}
if (typeof marginRight !== "number") {
marginRight = doubleFromPercentString(marginRight) * viewWidth;
}
return {
...viewStyle,
marginTop: !!wrapperInset.isIPhoneX || !hideStatusBar ? marginTop + wrapperInset.insetTop : marginTop,
marginBottom: marginBottom + wrapperInset.insetBottom,
marginLeft: marginLeft + wrapperInset.insetLeft,
marginRight: marginRight + wrapperInset.insetRight,
};
}
/**
* Utility component wrapper to handle orientation changes and extra padding controle for iOS (specially iPads and iPhone X)
*/
export default class FlashMessageWrapper extends Component {
static defaultProps = {
/**
* Default FlashMessage position is "top"
* Other options like "bottom" and "center" uses other extra padding configurations
*/
position: "top",
};
static propTypes = {
position: PropTypes.string,
children: PropTypes.func.isRequired,
};
static setStatusBarHeight = height => {
_customStatusBarHeight = height;
};
constructor() {
super();
this.handleOrientationChange = this.handleOrientationChange.bind(this);
const isLandscape = isOrientationLandscape(Dimensions.get("window"));
this.state = { isLandscape };
}
componentDidMount() {
Dimensions.addEventListener("change", this.handleOrientationChange);
}
componentWillUnmount() {
Dimensions.removeEventListener("change", this.handleOrientationChange);
}
handleOrientationChange({ window }) {
const isLandscape = isOrientationLandscape(window);
this.setState({ isLandscape });
}
render() {
const { position, children } = this.props;
const { isLandscape } = this.state;
const _statusBarHeight = statusBarHeight(isLandscape);
/**
* This wrapper will return data about extra inset padding, statusBarHeight and some device detection like iPhoneX and iPad
*/
const wrapper = {
isLandscape,
isIPhoneX: isIPhoneX,
isIPad: isIPad,
statusBarHeight: _statusBarHeight,
insetTop: position === "top" ? _statusBarHeight : 0,
insetLeft: (position === "top" || position === "bottom") && isLandscape ? (isIPhoneX ? 21 : 0) : 0,
insetRight: (position === "top" || position === "bottom") && isLandscape ? (isIPhoneX ? 21 : 0) : 0,
insetBottom: isIPhoneX && position === "bottom" ? (isLandscape ? 24 : 34) : 0,
};
return children(wrapper);
}
}
;