<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>React Custom Modal Example & Tutorial</title>

    <link rel="stylesheet" href="_content/app.css" />
    <link rel="stylesheet" href="_content/modal.css" />

    <!-- transpile and run react code in browser with jspm -->
    <script src="https://jspm.io/system.js"></script>
    <script src="config.js"></script>
    <script type="text/javascript">
      System.import('./index.jsx');
    </script>
    
  </head>
  <body>
    <div id="app">Loading...</div>
    
    <!-- credits -->
    <div class="credits">
        <p>
            <a href="http://jasonwatmore.com/post/2018/01/23/react-custom-modal-window-dialog-box" target="_top">React - Custom Modal Window / Dialog Box</a>
        </p>
        <p>
            <a href="http://jasonwatmore.com" target="_top">JasonWatmore.com</a>
        </p>
    </div>
  </body>
</html>
import React from 'react';
import { render } from 'react-dom';
import { App } from './App';

render(
    <App />,
    document.getElementById('app')
);
export * from './App.jsx';
import React from 'react';
import { Router, Route } from 'react-router-dom';
import { Link } from 'react-router-dom';

import { createBrowserHistory } from 'history';
const history = createBrowserHistory();

import { HomePage } from '../HomePage';
import { TestPage } from '../TestPage';

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

        // this line is required to work on plunker because the app preview runs on a subfolder url
        history.push('/');
    }

    render() {
        return (
            <Router history={history}>
                <div>
                    <nav>
                        <Link to="/">Home</Link>
                        <Link to="/test-page">Test Page</Link>
                    </nav>
                    <div>
                        <Route exact path="/" component={HomePage} />
                        <Route path="/test-page" component={TestPage} />
                    </div>
                </div>
            </Router>
        );
    }
}

export { App }; 
/*global System */
'use strict';

System.config({
  transpiler: "babel",
  packages: {
    './': { defaultExtension: false },
    './_components': { main: 'index.js', defaultExtension: false },
    './App': { main: 'index.js', defaultExtension: false },
    './HomePage': { main: 'index.js', defaultExtension: false },
    './TestPage': { main: 'index.js', defaultExtension: false }
  },
  paths: {
    "*": "https://npm.jspm.io/*.js"
  }
});
export * from './JwModal.jsx';
import React from 'react';
import PropTypes from 'prop-types';

const propTypes = {
    id: PropTypes.string.isRequired
};

class JwModal extends React.Component {
    constructor(props) {
        super(props);

        this.state = { isOpen: false };

        this.handleClick = this.handleClick.bind(this);
    }

    componentDidMount() {
        // move element to bottom of page (just before </body>) so it can be displayed above everything else
        document.body.appendChild(this.element);

        // add this modal instance to the modal service so it's accessible from other components
        JwModal.modals.push(this);
    }

    componentWillUnmount() {
        // remove this modal instance from modal service
        JwModal.modals = JwModal.modals.filter(x => x.props.id !== this.props.id);
        this.element.remove();
    }

    handleClick(e) {
        // close modal on background click
        if (e.target.className === 'jw-modal') {
            JwModal.close(this.props.id)(e);
        }
    }
    
    render() {
        return (
            <div style={{display: + this.state.isOpen ? '' : 'none'}} onClick={this.handleClick} ref={el => this.element = el}>
                <div className="jw-modal">
                    <div className="jw-modal-body">
                        {this.props.children}
                    </div>
                </div>
                <div className="jw-modal-background"></div>
            </div>
        );
    }
}

JwModal.modals = [];

JwModal.open = (id) => (e) => {
    e.preventDefault();

    // open modal specified by id
    let modal = JwModal.modals.find(x => x.props.id === id);
    modal.setState({ isOpen: true });
    document.body.classList.add('jw-modal-open');
}

JwModal.close = (id) => (e) => {
    e.preventDefault();

    // close modal specified by id
    let modal = JwModal.modals.find(x => x.props.id === id);
    modal.setState({ isOpen: false });
    document.body.classList.remove('jw-modal-open');
}

JwModal.propTypes = propTypes;

export { JwModal };
/* MODAL STYLES
-------------------------------*/
.jw-modal {
    /* modal container fixed across whole screen */
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;

    /* z-index must be higher than .modal-background */
    z-index: 1000;
    
    /* enables scrolling for tall modals */
    overflow: auto;
}

.jw-modal-body {
    padding: 20px;
    background: #fff;

    /* margin exposes part of the modal background */
    margin: 40px;
}

.jw-modal-background {
    /* modal background fixed across whole screen */
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;

    /* semi-transparent black  */
    background-color: #000;
    opacity: 0.75;
    
    /* z-index must be below .modal and above everything else  */
    z-index: 900;
}

body.jw-modal-open {
    /* body overflow is hidden to hide main scrollbar when modal window is open */
    overflow: hidden;
}
/* STYLES FOR EXAMPLE APP
-------------------------------*/
body {
    font-family: roboto;
    padding: 20px;
}

nav {
    margin-bottom: 20px;
    padding-bottom: 20px;
    border-bottom: 1px solid #ddd;
}

nav a {
    margin-right: 8px;
}

h1 {
    font-weight: normal;
    margin-top: 0;
}

input[type="text"] {
    display:block;
    width: 100%;
    font-family: roboto;
}

.credits {
    margin-top: 30px;
    border-top: 1px solid #ddd;
    text-align: center;
}
import React from 'react';

import { JwModal } from '../_components';

class HomePage extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            bodyText: 'This text can be updated in modal 1'
        };

        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(e) {
        const { name, value } = e.target;
        this.setState({ [name]: value });
    }

    render() {
        const { bodyText } = this.state;
        return (
            <div>
                <h1>Home</h1>
                <p>{bodyText}</p>
                <button onClick={JwModal.open('custom-modal-1')}>Open Modal 1</button>
                <button onClick={JwModal.open('custom-modal-2')}>Open Modal 2</button>

                <JwModal id="custom-modal-1">
                    <h1>A Custom Modal!</h1>
                    <p>
                        Home page text: <input type="text" name="bodyText" value={bodyText} onChange={this.handleChange} />
                    </p>
                    <button onClick={JwModal.close('custom-modal-1')}>Close</button>
                </JwModal>

                <JwModal id="custom-modal-2">
                    <h1 style={{height: 1000}}>A Tall Custom Modal!</h1>
                    <button onClick={JwModal.close('custom-modal-2')}>Close</button>
                </JwModal>
            </div>
        );
    }
}

export { HomePage }; 
export * from './HomePage.jsx';
export * from './TestPage.jsx';
import React from 'react';

class TestPage extends React.Component {
    render() {
        return (
            <div>
                <h1>Test Page</h1>
                <p>This one doesn't have any modals...</p>
            </div>
        );
    }
}

export  { TestPage };