Skip to content

Instantly share code, notes, and snippets.

@vagusX
Last active December 26, 2017 02:40
Show Gist options
  • Save vagusX/56a71ebff8aaa0428efcbf9ad2d11029 to your computer and use it in GitHub Desktop.
Save vagusX/56a71ebff8aaa0428efcbf9ad2d11029 to your computer and use it in GitHub Desktop.
write a modal in react 15
import * as React from 'react'
import Overlay from './Overlay'
const styles = require('./styles.scss')
const Modal = props => {
const { show, onCancel, children } = props
return (
<Overlay show={show} onCancel={onCancel}>
<div className={styles.content} onClick={e => e.stopPropagation()}>
{ React.cloneElement(children, {onCancel}) }
</div>
</Overlay>
)
}
export default Modal
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { CSSTransitionGroup } from 'react-transition-group'
import * as PropTypes from 'prop-types'
const styles = require('./styles.scss')
interface Props {
show: boolean,
onCancel: Function,
closable?: boolean,
hasMask?: boolean
}
export default class Overlay extends React.PureComponent<Props, {}> {
static defaultProps = {
closable: true,
hasMask: true,
}
static propTypes = {
show: PropTypes.bool,
onCancel: PropTypes.func,
closable: PropTypes.bool,
hasMask: PropTypes.bool,
}
container?: HTMLElement
componentDidMount() {
this.getContainer()
this.renderIntoContainer(this.props)
}
componentWillReceiveProps(newProps) {
this.renderIntoContainer(newProps)
}
componentDidUpdate(prevProps) {
if (this.props.closable) {
// add event listener to close side panel
// use `touchstart` instead of `mousedown`
if (this.props.show) {
window.addEventListener('touchstart', this.handleClose, false)
}
if (!this.props.show) {
window.removeEventListener('touchstart', this.handleClose, false)
}
} else {
window.removeEventListener('touchstart', this.handleClose, false)
}
}
componentWillUnmount() {
this.removeContainer()
}
renderContent = props => {
const { show, onCancel, children, hasMask } = props
return (
<div>
{
hasMask
? <div className={styles.mask} data-disabled={!show} onClick={onCancel}></div>
: null
}
<CSSTransitionGroup
transitionName="flip"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}>
{show && children}
</CSSTransitionGroup>
</div>
)
}
render() {
return null
}
renderIntoContainer = props => {
ReactDOM.unstable_renderSubtreeIntoContainer(this, this.renderContent(props), this.container)
}
handleClose = e => {
// close modal when click outside
if (e && this.container && this.container.contains(e.target)) return
this.props.show && this.props.onCancel()
}
getContainer() {
if (!this.container) {
this.container = document.createElement('div')
this.container.className = 'overlay'
document.body.appendChild(this.container)
}
return this.container
}
removeContainer() {
ReactDOM.unmountComponentAtNode(this.getContainer())
document.body.removeChild(this.container)
this.container = null
}
}
@import "../../shared/styles/variables.scss";
.mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow: hidden;
background-color: rgba(#000, .3);
-webkit-overflow-scrolling: touch;
outline: 0;
box-shadow: -3px 0 3px rgba(0,0,0,.1);
z-index: 1000;
&[data-disabled] {
display: none;
}
}
.content {
position: fixed;
left: 0;
bottom: 0;
background-color: #fff;
width: 100%;
z-index: 1000;
}
:global {
// effect slide from Right
.flip-enter {
transform: translate3d(0, 100%, 0);
}
.flip-enter-active {
transform: none;
transition: transform 200ms ease;
}
.flip-leave {
transform: none;
}
.flip-leave-active {
transform: translate3d(0, 100%, 0);
transition: transform 200ms ease;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment