tuya-panel-kit
Version:
a functional component library for developing tuya device panels!
233 lines (216 loc) • 5.98 kB
JavaScript
/* eslint-disable react/no-array-index-key */
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components/native';
import {
Modal,
TouchableOpacity,
Animated,
StyleSheet,
ViewPropTypes,
View,
Platform,
KeyboardAvoidingView,
} from 'react-native';
import { CoreUtils, RatioUtils } from '../../utils';
const { get } = CoreUtils;
const ALIGN = {
top: 'flex-start',
center: 'center',
bottom: 'flex-end',
};
const { isIphoneX } = RatioUtils;
const ios = Platform.OS === 'ios';
/* istanbul ignore next */
const StyledMask = styled(TouchableOpacity)`
background-color: ${props => get(props, 'theme.global.mask', 'rgba(0, 0, 0, 0.7)')};
`;
class TYModal extends React.Component {
static propTypes = {
/**
* modal出现的动画效果
*/
animationType: PropTypes.oneOf(['fade', 'none']),
/**
* modal默认出现的位置
*/
alignContainer: PropTypes.oneOf(['top', 'center', 'bottom']),
/**
* 点击遮罩回调
*/
onMaskPress: PropTypes.func,
/**
* modal弹出内容的样式
*/
modalChildStyle: ViewPropTypes.style,
/**
* 遮罩层样式
*/
maskStyle: ViewPropTypes.style,
/**
* 是否有遮罩层
*/
mask: PropTypes.bool,
/**
* 是否只显示最后一个弹出的 modal
*/
onlyLastModalVisible: PropTypes.bool,
/**
* 是否弹出键盘自适应
*/
useKeyboardView: PropTypes.bool,
/**
* Modal 组件销毁回调事件,一般用于在弹窗销毁后跳转新的 native 页面
* @platform iOS
*/
onDismiss: PropTypes.func,
};
static defaultProps = {
animationType: 'fade',
alignContainer: 'bottom',
onMaskPress: () => {},
maskStyle: null,
mask: true,
modalChildStyle: null,
onlyLastModalVisible: true,
useKeyboardView: false,
};
constructor(props) {
super(props);
this._childRef = {};
this.state = {
fade: new Animated.Value(0),
};
}
componentDidMount() {
Animated.timing(this.state.fade, {
duration: 200,
toValue: 1,
}).start();
}
_handleMaskPress = () => {
const { onMaskPress, children } = this.props;
const childMotionType = get(this, `_childRef.${children.length - 1}.props.motionType`, 'none');
const childMaskPress = get(this, `_childRef.${children.length - 1}._handleMaskPress`);
if (childMotionType !== 'none' && typeof childMaskPress === 'function') {
/* istanbul ignore next */
childMaskPress();
} else {
typeof onMaskPress === 'function' && onMaskPress();
}
};
renderNoMaskModal = () => {
const { onlyLastModalVisible, activeIdx, children, animationType, alignContainer } = this.props;
const maskContainer = [
styles.maskContainer,
alignContainer && { justifyContent: ALIGN[alignContainer] },
{ opacity: animationType === 'fade' ? this.state.fade : 1 },
];
return children.map((child, idx) => {
const childStyle = [
maskContainer,
onlyLastModalVisible && { display: idx === activeIdx ? 'flex' : 'none' },
];
return (
<Animated.View key={idx} style={childStyle} pointerEvents="box-none">
{child}
</Animated.View>
);
});
};
renderMaskModal = () => {
const {
visible,
onDismiss,
onlyLastModalVisible,
activeIdx,
children,
animationType,
onMaskPress,
alignContainer,
maskStyle,
modalChildStyle,
useKeyboardView,
...props
} = this.props;
const maskContainer = [
styles.maskContainer,
alignContainer && { justifyContent: ALIGN[alignContainer] },
maskStyle,
];
return (
<Modal
animationType={animationType}
onRequestClose={this._handleMaskPress}
visible={onlyLastModalVisible ? visible : true}
onDismiss={onDismiss}
{...props}
transparent={true}
>
{children.map((child, idx) => {
const childStyle = [
maskContainer,
onlyLastModalVisible && { display: idx === activeIdx ? 'flex' : 'none' },
{
position: Platform.OS === 'android' && idx !== activeIdx ? 'relative' : 'absolute',
},
];
let modalChild = child;
if (!Array.isArray(child)) {
modalChild = React.cloneElement(child, {
onMaskPress: get(child, 'props.onMaskPress', onMaskPress),
ref: /* istanbul ignore next */ ref => {
this._childRef[idx] = ref;
},
});
}
return (
<StyledMask
key={idx}
style={childStyle}
onPress={this._handleMaskPress}
activeOpacity={1}
>
{ios && useKeyboardView ? (
<KeyboardAvoidingView
behavior="padding"
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingTop: isIphoneX ? 20 : 0,
}}
>
<View onStartShouldSetResponder={() => true} style={modalChildStyle}>
{modalChild}
</View>
</KeyboardAvoidingView>
) : (
<View onStartShouldSetResponder={() => true} style={modalChildStyle}>
{modalChild}
</View>
)}
</StyledMask>
);
})}
</Modal>
);
};
render() {
const { mask } = this.props;
if (!mask) {
return this.renderNoMaskModal();
}
return this.renderMaskModal();
}
}
const styles = StyleSheet.create({
maskContainer: {
bottom: 0,
left: 0,
position: 'absolute',
right: 0,
top: 0,
},
});
export default TYModal;