Skip to content

Commit

Permalink
refactor(modal): use portal close #79
Browse files Browse the repository at this point in the history
  • Loading branch information
BANG88 committed Dec 4, 2018
1 parent 5c1db35 commit 69e3cbb
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 136 deletions.
9 changes: 6 additions & 3 deletions components/action-sheet/AndroidContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// tslint:disable:jsx-no-multiline-js
import React from 'react';
import { ActionSheetIOSOptions, Text, TouchableHighlight, View } from 'react-native';
import Modal from 'rmc-dialog/lib/Modal';
import Modal from '../modal/ModalView';
import styles, { ActionSheetStyle, vars as variables } from './style/index';

export interface ActionSheetNativeProps {
Expand All @@ -12,7 +12,10 @@ export interface ActionSheetNativeProps {
styles?: ActionSheetStyle;
}

class ActionSheetAndroid extends React.PureComponent<ActionSheetNativeProps, any> {
class ActionSheetAndroid extends React.PureComponent<
ActionSheetNativeProps,
any
> {
constructor(props: ActionSheetNativeProps) {
super(props);
this.state = {
Expand All @@ -33,7 +36,7 @@ class ActionSheetAndroid extends React.PureComponent<ActionSheetNativeProps, any
this.setState({
visible: false,
});
}
};
render() {
const { config, onAnimationEnd } = this.props;
const {
Expand Down
25 changes: 8 additions & 17 deletions components/modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Dimensions, LayoutChangeEvent, Modal, StyleProp, StyleSheet, Text, TextStyle, TouchableHighlight, TouchableWithoutFeedback, View, ViewStyle } from 'react-native';
import RCModal from 'rmc-dialog/lib/Modal';
import { Dimensions, LayoutChangeEvent, StyleProp, StyleSheet, Text, TextStyle, TouchableHighlight, TouchableWithoutFeedback, View, ViewStyle } from 'react-native';
import { getComponentLocale } from '../_util/getLocale';
import zh_CN from './locale/zh_CN';
import RCModal from './ModalView';
import { ModalPropsType } from './PropsType';
import modalStyle, { IModalStyle } from './style/index';

Expand Down Expand Up @@ -53,11 +53,11 @@ class AntmModal extends React.Component<IModalNativeProps, any> {
style: [{ paddingBottom: e.nativeEvent.layout.height }, maxHeight],
});
}
}
};

saveRoot = (root: any) => {
this.root = root;
}
};

render() {
const {
Expand Down Expand Up @@ -219,23 +219,14 @@ class AntmModal extends React.Component<IModalNativeProps, any> {
</View>
);
}
if (
animType === 'slide-up' ||
animType === 'slide-down' ||
animType === 'slide'
) {
animType = 'slide';
if (animType === 'slide') {
animType = undefined;
}
return (
<View style={styles.container}>
<Modal
visible={visible}
animationType={animType}
onRequestClose={onClose}
hardwareAccelerated
>
<RCModal visible={visible} animationType={animType} onClose={onClose}>
<View style={style}>{children}</View>
</Modal>
</RCModal>
</View>
);
}
Expand Down
228 changes: 228 additions & 0 deletions components/modal/ModalView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import React from 'react';
import { Animated, Dimensions, Easing, StyleSheet, TouchableWithoutFeedback, View, ViewStyle } from 'react-native';
import { Portal } from '../portal';

const styles = StyleSheet.create({
wrap: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0)',
} as ViewStyle,
mask: {
backgroundColor: 'black',
opacity: 0.5,
} as ViewStyle,
content: {
backgroundColor: 'white',
} as ViewStyle,
absolute: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
},
});

const screen = Dimensions.get('window');

export interface IModalPropTypes {
wrapStyle?: ViewStyle;
maskStyle?: ViewStyle;
style?: {};
animationType: 'none' | 'fade' | 'slide-up' | 'slide-down';
animationDuration?: number;
visible: boolean;
maskClosable?: boolean;
animateAppear?: boolean;
onClose?: () => void;
onAnimationEnd?: (visible: boolean) => void;
}

export default class RCModal extends React.Component<IModalPropTypes, any> {
static defaultProps = {
wrapStyle: styles.wrap,
maskStyle: styles.mask,
animationType: 'slide-up',
animateAppear: false,
animationDuration: 300,
visible: false,
maskClosable: true,
onClose() {},
onAnimationEnd(_visible: boolean) {},
} as IModalPropTypes;

animMask: any;
animDialog: any;
constructor(props: IModalPropTypes) {
super(props);
const { visible } = props;
this.state = {
position: new Animated.Value(this.getPosition(visible)),
scale: new Animated.Value(this.getScale(visible)),
opacity: new Animated.Value(this.getOpacity(visible)),
modalVisible: visible,
};
}
componentWillReceiveProps(nextProps: IModalPropTypes) {
if (this.shouldComponentUpdate(nextProps, null)) {
this.setState({
modalVisible: true,
});
}
}
shouldComponentUpdate(nextProps: IModalPropTypes, nextState: any) {
if (this.props.visible || this.props.visible !== nextProps.visible) {
return true;
}
if (nextState) {
if (nextState.modalVisible !== this.state.modalVisible) {
return true;
}
}
return false;
}
componentDidMount() {
if (this.props.animateAppear && this.props.animationType !== 'none') {
this.componentDidUpdate({} as IModalPropTypes);
}
}
componentDidUpdate(prevProps: IModalPropTypes) {
const { props } = this;
if (prevProps.visible !== props.visible) {
this.animateDialog(props.visible);
}
}
animateMask = (visible: boolean) => {
this.stopMaskAnim();
this.state.opacity.setValue(this.getOpacity(!visible));
this.animMask = Animated.timing(this.state.opacity, {
toValue: this.getOpacity(visible),
duration: this.props.animationDuration,
useNativeDriver: true,
});
this.animMask.start(() => {
this.animMask = null;
});
};
stopMaskAnim = () => {
if (this.animMask) {
this.animMask.stop();
this.animMask = null;
}
};
stopDialogAnim = () => {
if (this.animDialog) {
this.animDialog.stop();
this.animDialog = null;
}
};
animateDialog = (visible: boolean) => {
this.stopDialogAnim();
this.animateMask(visible);

let { animationType, animationDuration } = this.props;
animationDuration = animationDuration!;
if (animationType !== 'none') {
if (animationType === 'slide-up' || animationType === 'slide-down') {
this.state.position.setValue(this.getPosition(!visible));
this.animDialog = Animated.timing(this.state.position, {
toValue: this.getPosition(visible),
duration: animationDuration,
easing: (visible ? Easing.elastic(0.8) : undefined) as any,
useNativeDriver: true,
});
} else if (animationType === 'fade') {
this.animDialog = Animated.parallel([
Animated.timing(this.state.opacity, {
toValue: this.getOpacity(visible),
duration: animationDuration,
easing: (visible ? Easing.elastic(0.8) : undefined) as any,
useNativeDriver: true,
}),
Animated.spring(this.state.scale, {
toValue: this.getScale(visible),
useNativeDriver: true,
}),
]);
}

this.animDialog.start(() => {
this.animDialog = null;
if (!visible) {
this.setState({
modalVisible: false,
});
}
if (this.props.onAnimationEnd) {
this.props.onAnimationEnd(visible);
}
});
} else {
if (!visible) {
this.setState({
modalVisible: false,
});
}
}
};
close = () => {
this.animateDialog(false);
};
onMaskClose = () => {
if (this.props.maskClosable && this.props.onClose) {
this.props.onClose();
}
};
getPosition = (visible: boolean) => {
if (visible) {
return 0;
}
return this.props.animationType === 'slide-down'
? -screen.height
: screen.height;
};
getScale = (visible: boolean) => {
return visible ? 1 : 1.05;
};
getOpacity = (visible: boolean) => {
return visible ? 1 : 0;
};
render() {
const { props } = this;
if (!this.state.modalVisible) {
return null as any;
}
const animationStyleMap = {
none: {},
'slide-up': { transform: [{ translateY: this.state.position }] },
'slide-down': { transform: [{ translateY: this.state.position }] },
fade: {
transform: [{ scale: this.state.scale }],
opacity: this.state.opacity,
},
};

return (
<Portal>
<View style={[styles.wrap, props.wrapStyle]}>
<TouchableWithoutFeedback onPress={this.onMaskClose}>
<Animated.View
style={[styles.absolute, { opacity: this.state.opacity }]}
>
<View style={[styles.absolute, props.maskStyle]} />
</Animated.View>
</TouchableWithoutFeedback>
<Animated.View
style={[
styles.content,
props.style,
animationStyleMap[props.animationType],
]}
>
{this.props.children}
</Animated.View>
</View>
</Portal>
);
}
}
Loading

0 comments on commit 69e3cbb

Please sign in to comment.