晴明的博客园 GitHub      CodePen      CodeWars     

[react] react-modal 源码

看看思路

react-modal

//Modal

    function getParentElement(parentSelector) {
        return parentSelector();
    }

    componentDidMount() {
        if (!canUseDOM) return;//判断是否能取到真实dom

        const isReact16 = ReactDOM.createPortal !== undefined;
        if (!isReact16) {
            this.node = document.createElement("div");
        }
        this.node.className = this.props.portalClassName;

        //parentSelector为产生父容器的方法
        const parent = getParentElement(this.props.parentSelector);
        parent.appendChild(this.node);

        !isReact16 && this.renderPortal(this.props);
    }

    componentWillReceiveProps(newProps) {
        if (!canUseDOM) return;
        const { isOpen } = newProps;
        // Stop unnecessary renders if modal is remaining closed
        if (!this.props.isOpen && !isOpen) return;

        const currentParent = getParentElement(this.props.parentSelector);
        const newParent = getParentElement(newProps.parentSelector);

        if (newParent !== currentParent) {
        currentParent.removeChild(this.node);
        newParent.appendChild(this.node);
        }

        !isReact16 && this.renderPortal(newProps);
    }

    componentWillUpdate(newProps) {
        if (!canUseDOM) return;
        if (newProps.portalClassName !== this.props.portalClassName) {
        this.node.className = newProps.portalClassName;
        }
    }

    componentWillUnmount() {
        if (!canUseDOM || !this.node || !this.portal) return;

        const state = this.portal.state;
        const now = Date.now();
        const closesAt =
        state.isOpen &&
        this.props.closeTimeoutMS &&
        (state.closesAt || now + this.props.closeTimeoutMS);

        if (closesAt) {
        if (!state.beforeClose) {
            this.portal.closeWithTimeout();
        }

        setTimeout(this.removePortal, closesAt - now);
        } else {
        this.removePortal();
        }
    }

    removePortal = () => {
        !isReact16 && ReactDOM.unmountComponentAtNode(this.node);
        const parent = getParentElement(this.props.parentSelector);
        parent.removeChild(this.node);
    };

    portalRef = ref => {
        this.portal = ref;
    };

    renderPortal = props => {
        const portal = createPortal(
        this,
        <ModalPortal defaultStyles={Modal.defaultStyles} {...props} />,
        this.node
        );
        this.portalRef(portal);
    };

    render() {
        if (!canUseDOM || !isReact16) {
        return null;
        }

        if (!this.node && isReact16) {
        this.node = document.createElement("div");
        }

        return createPortal(
        <ModalPortal
            ref={this.portalRef}
            defaultStyles={Modal.defaultStyles}
            {...this.props}
        />,
        this.node
        );
    }

//ModalPortal

  render() {
    const { className, overlayClassName, defaultStyles } = this.props;
    const contentStyles = className ? {} : defaultStyles.content;
    const overlayStyles = overlayClassName ? {} : defaultStyles.overlay;

    return this.shouldBeClosed() ? null : (
      <div
        ref={this.setOverlayRef}
        className={this.buildClassName("overlay", overlayClassName)}
        style={{ ...overlayStyles, ...this.props.style.overlay }}
        onClick={this.handleOverlayOnClick}
        onMouseDown={this.handleOverlayOnMouseDown}
        onMouseUp={this.handleOverlayOnMouseUp}
      >
        <div
          ref={this.setContentRef}
          style={{ ...contentStyles, ...this.props.style.content }}
          className={this.buildClassName("content", className)}
          tabIndex="-1"
          onKeyDown={this.handleKeyDown}
          onMouseDown={this.handleContentOnMouseDown}
          onMouseUp={this.handleContentOnMouseUp}
          onClick={this.handleContentOnClick}
          role={this.props.role}
          aria-label={this.props.contentLabel}
          {...this.ariaAttributes(this.props.aria || {})}
        >
          {this.props.children}
        </div>
      </div>
    );
  }

//example1

    function getParent() {
        return document.querySelector('#root');
    }

    <Modal
    ...
    parentSelector={getParent}
    ...
    >
    <p>Modal Content.</p>
    </Modal>


    <Modal
    ...
    className={{
        base: 'myClass',
        afterOpen: 'myClass_after-open',
        beforeClose: 'myClass_before-close'
    }}
    overlayClassName={{
        base: 'myOverlayClass',
        afterOpen: 'myOverlayClass_after-open',
        beforeClose: 'myOverlayClass_before-close'
    }}
    ...
    >

//example2

import React from 'react';
import ReactDOM from 'react-dom';
import Modal from 'react-modal';

const customStyles = {
  content : {
    top                   : '50%',
    left                  : '50%',
    right                 : 'auto',
    bottom                : 'auto',
    marginRight           : '-50%',
    transform             : 'translate(-50%, -50%)'
  }
};

class App extends React.Component {
  constructor() {
    super();

    this.state = {
      modalIsOpen: false
    };

    this.openModal = this.openModal.bind(this);
    this.afterOpenModal = this.afterOpenModal.bind(this);
    this.closeModal = this.closeModal.bind(this);
  }

  openModal() {
    this.setState({modalIsOpen: true});
  }

  afterOpenModal() {
    // references are now sync'd and can be accessed.
    this.subtitle.style.color = '#f00';
  }

  closeModal() {
    this.setState({modalIsOpen: false});
  }

  render() {
    return (
      <div>
        <button onClick={this.openModal}>Open Modal</button>
        <Modal
          isOpen={this.state.modalIsOpen}
          onAfterOpen={this.afterOpenModal}
          onRequestClose={this.closeModal}
          style={customStyles}
          contentLabel="Example Modal"
        >

          <h2 ref={subtitle => this.subtitle = subtitle}>Hello</h2>
          <button onClick={this.closeModal}>close</button>
          <div>I am a modal</div>
          <form>
            <input />
            <button>tab navigation</button>
            <button>stays</button>
            <button>inside</button>
            <button>the modal</button>
          </form>
        </Modal>
      </div>
    );
  }
}

ReactDOM.render(<App />, appElement);
posted @ 2017-10-19 19:56  晴明桑  阅读(358)  评论(0编辑  收藏  举报