weex-nuke
Version:
基于 Rax 、Weex 的高性能组件体系 ~~
246 lines (236 loc) • 6.74 kB
JSX
/** @jsx createElement */
import { createElement, findDOMNode, PropTypes, Component } from 'rax';
import { isWeb } from 'nuke-env';
import Mask from 'nuke-mask';
import Touchable from 'nuke-touchable';
import Transition from 'nuke-transition';
import Dimensions from 'nuke-dimensions';
import { connectStyle } from 'nuke-theme-provider';
import stylesProvider from './styles';
const deviceInfo = isWeb ? Dimensions.get('window') : Dimensions.get('screen');
class Slip extends Component {
constructor(props) {
super(props);
this.onVisibleChanged = this.onVisibleChanged.bind(this);
this.transition = this.transition.bind(this);
this.show = this.show.bind(this);
this.hide = this.hide.bind(this);
this.onMaskShow = this.onMaskShow.bind(this);
this.onMaskHide = this.onMaskHide.bind(this);
this.onMaskPress = this.onMaskPress.bind(this);
this.onPanePress = this.onPanePress.bind(this);
this.state = {
visible: props.defaultVisible || false,
defaultVisible: props.defaultVisible || false,
scrollPosition: 0,
};
}
onVisibleChanged(e) {
this.setState({
visible: e.visible,
});
}
onMaskHide() {
this.setState({
visible: false,
});
const { onHide } = this.props;
onHide && onHide();
}
onPanePress(e) {
if (isWeb) {
e.stopPropagation();
}
}
onMaskShow() {
const styles = this.calcTransitionStyle(false);
const box = findDOMNode(this.refs.slipPane);
setTimeout(() => {
this.transition(box, styles, () => {
this.setState({ visible: true });
const { onShow } = this.props;
onShow && onShow();
});
}, 10);
}
onMaskPress() {
const { maskClosable } = this.props;
if (!maskClosable) {
return;
}
this.hide();
}
getWidthAndHeight() {
const { direction } = this.props;
let { width, height } = this.props;
if (!height && (direction === 'left' || direction === 'right')) {
height = parseInt(deviceInfo.height, 10);
}
if (!width && (direction === 'top' || direction === 'bottom')) {
width = 750;
}
return {
width,
height,
};
}
calcTransitionStyle(contentDialogVisualState) {
const visible = typeof contentDialogVisualState === 'undefined' ? this.state.visible : contentDialogVisualState;
const { direction } = this.props;
const { width, height } = this.getWidthAndHeight();
let transitionStyle = {};
switch (direction) {
case 'left':
transitionStyle = {
transform: !visible ? `translateX(${width})` : 'translateX(0)',
webkitTransform: !visible ? `translateX(${width})` : 'translateX(0)',
};
break;
case 'right':
transitionStyle = {
transform: !visible ? `translateX(${-width})` : 'translateX(0)',
webkitTransform: !visible ? `translateX(${-width})` : 'translateX(0)',
};
// right
break;
case 'top':
transitionStyle = {
transform: !visible ? `translateY(${height})` : 'translateX(0)',
webkitTransform: !visible ? `translateY(${height})` : 'translateX(0)',
};
break;
default:
transitionStyle = {
transform: !visible ? `translateY(${-height})` : 'translateX(0)',
webkitTransform: !visible ? `translateY(${-height})` : 'translateX(0)',
};
break;
}
return transitionStyle;
}
calcInitStyle() {
const { direction } = this.props;
const { width, height } = this.getWidthAndHeight();
let positionObj = {
position: 'absolute',
width: `${width}rem`,
height: `${height}rem`,
};
switch (direction) {
case 'left':
positionObj = {
...positionObj,
left: `${-width}rem`,
top: 0,
};
break;
case 'right':
positionObj = {
...positionObj,
right: `${-width}rem`,
};
// right
break;
case 'top':
positionObj = {
...positionObj,
top: `${-height}rem`,
};
break;
default:
positionObj = {
...positionObj,
bottom: `${-height}rem`,
};
break;
}
return positionObj;
}
transition(box, styles, cb) {
const { effect, duration } = this.props;
Transition(
box,
styles,
{
timingFunction: effect,
delay: 0,
duration,
},
function () {
cb && cb.call(this);
}
);
}
show() {
// first show mask,
// then in mask onShow callback, show dialogContent;
this.refs.BaseSlipMask.show();
if (isWeb) {
this.setState({ scrollPosition: window.scrollY });
document.body.style.position = 'fixed';
document.body.style.overflow = 'hidden';
}
}
hide() {
// first hide dialogContent, then hideMask
const styles = this.calcTransitionStyle(true);
const box = findDOMNode(this.refs.slipPane);
setTimeout(() => {
this.transition(box, styles, () => {
this.setState({ visible: !this.state.visible });
this.refs.BaseSlipMask.hide();
});
}, 10);
if (isWeb) {
document.body.style.position = 'initial';
document.body.style.overflow = 'initial';
window.scrollTo(0, this.state.scrollPosition);
}
}
render() {
const { children, contentStyle } = this.props;
const styles = this.props.themeStyle;
return (
<Mask
defaultVisible={this.state.defaultVisible}
animate={false}
maskClosable={false}
ref="BaseSlipMask"
onVisibleChanged={this.onVisibleChanged}
onShow={this.onMaskShow}
onHide={this.onMaskHide}
style={styles.mask}
>
<Touchable style={styles['fullscreen-body']} onPress={this.onMaskPress}>
<Touchable
ref="slipPane"
style={[styles.pane, this.calcInitStyle(), contentStyle]}
onPress={this.onPanePress}
>
{children}
</Touchable>
</Touchable>
</Mask>
);
}
}
Slip.propTypes = {
direction: PropTypes.oneOf(['left', 'right', 'top', 'bottom']),
maskClosable: PropTypes.boolean,
effect: PropTypes.oneOf(['ease-in', 'ease-in-out', 'ease-out', 'linear', 'cubic-bezier']),
width: PropTypes.number,
height: PropTypes.number,
contentStyle: PropTypes.any,
duration: PropTypes.number,
};
Slip.defaultProps = {
direction: 'bottom',
maskClosable: false,
effect: 'ease-in-out',
duration: 200,
width: 750,
contentStyle: {},
};
Slip.displayName = 'Slip';
const StyledSlip = connectStyle(stylesProvider)(Slip);
export default StyledSlip;