Skip to content

Instantly share code, notes, and snippets.

@erick-otenyo
Forked from rmdort/react-modal-portal.js
Created December 19, 2018 10:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erick-otenyo/ee69bdd98a6f10c17bd6218bf5af0a7b to your computer and use it in GitHub Desktop.
Save erick-otenyo/ee69bdd98a6f10c17bd6218bf5af0a7b to your computer and use it in GitHub Desktop.
Simple react modal using portals
import React from 'react'
import PropTypes from 'prop-types'
import ReactDOM from 'react-dom'
import cx from 'classnames'
const TAB_KEY = 9
const ESC_KEY = 27
const FOCUSABLE_ELEMENTS = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA', 'A']
class ModalPortal extends React.Component {
constructor (props) {
super(props)
this.shouldClose = null
}
handleContentOnClick = () => {
this.shouldClose = false
}
handleContentOnMouseDown = () => {
this.shouldClose = false
}
handleContentOnMouseUp = () => {
this.shouldClose = false
}
handleOverlayOnClick = (event) => {
if (this.shouldClose === null) {
this.shouldClose = true
}
if (this.shouldClose) {
this.requestClose(event)
}
this.shouldClose = null
}
componentDidUpdate (prevProps, prevState) {
if (prevProps.isOpen !== this.props.isOpen && this.props.isOpen) {
this.focusContent()
}
}
focusContent = () => {
this.content && this.content.focus()
}
requestClose = (event) => {
this.props.onRequestClose && this.props.onRequestClose(event)
}
setContentRef = (ref) => {
this.content = ref
}
handleKeyDown = (event) => {
if (document.activeElement && FOCUSABLE_ELEMENTS.indexOf(document.activeElement.nodeName) !== -1) return
if (event.keyCode === ESC_KEY) {
event.stopPropagation()
this.requestClose(event)
}
}
render () {
const { isOpen, inline } = this.props
if (!isOpen) return null
const contentClass = cx('ola-modal-content', this.props.contentClassName)
const overlayClass = cx('ola-modal-overlay', {
'ola-modal-inline': inline
})
return (
<div
className={overlayClass}
aria-modal='true'
onClick={this.handleOverlayOnClick}
>
<div
className={contentClass}
onClick={this.handleContentOnClick}
ref={this.setContentRef}
onKeyDown={this.handleKeyDown}
onMouseDown={this.handleContentOnMouseDown}
onMouseUp={this.handleContentOnMouseUp}
tabIndex='-1'
>
<button className='ola-modal-close' onClick={this.requestClose}>
Close
</button>
{inline ? (
this.props.children
) : (
<div className='ola-modal-body'>{this.props.children}</div>
)}
</div>
</div>
)
}
}
class Portal extends React.Component {
constructor (props) {
super(props)
this.el = document.createElement('div')
this.el.className = props.className
}
static defaultProps = {
isOpen: false,
className: 'ola-modal-portal',
inline: false,
contentClassName: 'ola-modal-content-small'
}
componentDidMount () {
document.body.appendChild(this.el)
}
componentWillUnmount () {
document.body.removeChild(this.el)
}
render () {
return ReactDOM.createPortal(<ModalPortal {...this.props} />, this.el)
}
}
Portal.propTypes = {
children: PropTypes.node.isRequired,
onRequestClose: PropTypes.func,
contentClassName: PropTypes.string
}
export default Portal
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment