Home Reference Source Repository

src/Dialog.js

/**
 * @file melon/Dialog
 * @author cxtom<[email protected]>
 */

import React, {Component, PropTypes} from 'react';
import ReactDOM from 'react-dom';
import Mask from './Mask';
import dom from './common/util/dom';
import DialogWindow from './dialog/DialogWindow';

import {create} from 'melon-core/classname/cxBuilder';

import {Motion, spring} from 'react-motion';

const cx = create('Dialog');

export default class Dialog extends Component {

    constructor(props) {

        super(props);

        this.originalHTMLBodySize = {};

        this.state = {
            open: props.open
        };

        this.onShow = this.onShow.bind(this);
        this.onHide = this.onHide.bind(this);
        this.onMaskClick = this.onMaskClick.bind(this);

    }

    componentDidMount() {
        if (this.state.open) {
            this.positionDialog();
        }
    }

    componentWillUpdate(nextProps, nextState) {
        if (nextState.open) {
            this.positionDialog();
        }
    }

    componentWillReceiveProps({open}) {

        if (open === this.state.open) {
            return;
        }

        this.setState({open}, open ? this.onShow : this.onHide);

    }

    positionDialog() {
        let dialogWindow = ReactDOM.findDOMNode(this.dialogWindow);
        let marginTop = -dialogWindow.offsetHeight / 2;

        let windowHeight = dom.getClientHeight();

        marginTop = dialogWindow.offsetHeight > windowHeight
                        ? (-windowHeight / 2 + 16)
                        : marginTop;
        dialogWindow.style.marginLeft = -dialogWindow.offsetWidth / 2 + 'px';
        dialogWindow.style.marginTop = marginTop + 'px';
    }

    onMaskClick(e) {
        if (this.props.maskClickClose) {
            this.setState({open: false}, this.onHide);
        }
        else {
            e.stopPropagation();
        }
    }

    onShow() {
        const onShow = this.props.onShow;
        if (onShow) {
            onShow();
        }
    }

    onHide() {
        const onHide = this.props.onHide;
        if (onHide) {
            onHide();
        }
    }

    renderTitle() {

        const title = this.props.title;

        return title
            ? <h1 className={cx().part('title').build()}>{title}</h1>
            : null;

    }

    renderAction() {

        const actions = this.props.actions;

        return actions
            ? (
                <div ref="dialogActions" className={cx().part('actions').build()}>
                    {actions}
                </div>
            )
            : null;

    }

    render() {

        const {props, state} = this;

        const {
            children,
            width,
            ...others
        } = props;

        const open = state.open;

        const title = this.renderTitle();

        const body = (
            <div className={cx().part('body').build()}>
                {children}
            </div>
        );

        const footer = this.renderAction();

        const windowPartClassName = cx()
            .part('window')
            .addVariants(width === 'adaptive' ? 'adaptive' : undefined)
            .build();

        return (
            <div {...others} className={cx(props).addStates({open}).build()}>
                <Motion style={{y: spring(open ? 0 : -80)}}>
                    {({y}) =>
                        <DialogWindow
                            top={Math.round(y)}
                            ref={c => {
                                this.dialogWindow = c;
                            }}
                            width={width}
                            title={title}
                            footer={footer}
                            className={windowPartClassName}>
                            {body}
                        </DialogWindow>
                    }
                </Motion>
                <Mask
                    show={open}
                    lockScrollingOnShow={true}
                    onClick={this.onMaskClick} />
            </div>
        );

    }

}

Dialog.propTypes = {
    actions: PropTypes.node,
    maskClickClose: PropTypes.bool,
    open: PropTypes.bool,
    onHide: PropTypes.func,
    onShow: PropTypes.func,
    title: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.element
    ]),
    width: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number
    ])
};

Dialog.defaultProps = {
    maskClickClose: true,
    open: false
};