@yncoder/element-react
Version:
Element UI for React
175 lines (155 loc) • 4.93 kB
JSX
/* @flow */
import * as React from 'react';
import ReactDOM from 'react-dom';
import { Component, View, Transition, PropTypes } from '../../libs';
import { cleanScrollBar } from '../table/utils';
type State = {
bodyOverflow: string,
}
export default class Dialog extends Component {
state: State;
static defaultProps = {
visible: false,
title: '',
size: 'small',
top: '15%',
modal: true,
lockScroll: true,
closeOnClickModal: true,
closeOnPressEscape: true,
showClose: true
}
constructor(props: Object) {
super(props);
this.wrap = React.createRef();
this.state = {
bodyOverflow: ''
}
}
componentWillReceiveProps(nextProps: Object): void {
const { bodyOverflow } = this.state;
const { lockScroll, modal } = this.props;
if (this.willOpen(this.props, nextProps)) {
cleanScrollBar();
if (lockScroll && document.body && document.body.style) {
if (!bodyOverflow) {
this.setState({
bodyOverflow: document.body.style.overflow
});
}
document.body.style.overflow = 'hidden';
}
}
if (this.willClose(this.props, nextProps) && lockScroll) {
if (modal && bodyOverflow !== 'hidden' && document.body && document.body.style) {
document.body.style.overflow = bodyOverflow;
}
}
}
componentDidUpdate(prevProps: Object): void {
if (this.willOpen(prevProps, this.props)) {
this.wrap.current.focus();
}
}
componentWillUnmount(): void {
const { lockScroll } = this.props;
if (lockScroll && document.body && document.body.style) {
document.body.style.removeProperty('overflow');
}
}
onKeyDown(e: SyntheticKeyboardEvent<any>): void {
const { closeOnPressEscape } = this.props;
if (closeOnPressEscape && e.keyCode === 27) {
this.close(e);
}
}
handleWrapperClick(e: SyntheticEvent<HTMLDivElement>): void {
const { closeOnClickModal } = this.props;
if (e.target instanceof HTMLDivElement) {
if (closeOnClickModal && e.target === e.currentTarget) {
this.close(e);
}
}
}
close(e: SyntheticEvent<HTMLDivElement> | SyntheticKeyboardEvent<HTMLDivElement>): void {
this.props.onCancel(e);
}
willOpen(prevProps: Object, nextProps: Object): boolean {
return (!prevProps.visible && nextProps.visible);
}
willClose(prevProps: Object, nextProps: Object): boolean {
return (prevProps.visible && !nextProps.visible);
}
render() {
const { visible, title, size, top, modal, customClass, showClose, children } = this.props;
const body = document.body;
if (!body) {
return null;
}
return ReactDOM.createPortal(
(
<React.Fragment>
{
modal && (
<View show={ visible }>
<div className="v-modal"></div>
</View>
)
}
<Transition name="dialog-fade">
<View show={ visible }>
<div
ref={this.wrap}
className={this.classNames('el-dialog__wrapper')}
onClick={ e => this.handleWrapperClick(e) }
onKeyDown={ e => this.onKeyDown(e) }
>
<div
ref="dialog"
style={this.style(size === 'full' ? {} : { 'top': top })}
className={ this.className("el-dialog", `el-dialog--${ size }`, customClass) }
>
<div className="el-dialog__header">
<span className="el-dialog__title">{ title }</span>
{
showClose && (
<button type="button" className="el-dialog__headerbtn">
<i className="el-dialog__close el-icon el-icon-close" onClick={ e => this.close(e) }></i>
</button>
)
}
</div>
{children}
</div>
</div>
</View>
</Transition>
</React.Fragment>
),
body
);
}
}
Dialog.propTypes = {
// 控制对话框是否可见
visible: PropTypes.bool.isRequired,
// 标题
title: PropTypes.string,
// 大小 (tiny/small/large/full)
size: PropTypes.string,
// top 值(仅在 size 不为 full 时有效)
top: PropTypes.string,
// 控制遮罩层展示
modal: PropTypes.bool,
// Dialog 的自定义类名
customClass: PropTypes.string,
// 是否在 Dialog 出现时将 body 滚动锁定
lockScroll: PropTypes.bool,
// 是否可以通过点击 modal 关闭 Dialog
closeOnClickModal: PropTypes.bool,
// 是否可以通过按下 ESC 关闭 Dialog
closeOnPressEscape: PropTypes.bool,
// 点击遮罩层或右上角叉或取消按钮的回调
onCancel: PropTypes.func.isRequired,
showClose: PropTypes.bool
};