Skip to content

Instantly share code, notes, and snippets.

@alobato
Last active February 20, 2019 18:37
Show Gist options
  • Select an option

  • Save alobato/9f8e867a9fb1de4185c3baeab5c79faa to your computer and use it in GitHub Desktop.

Select an option

Save alobato/9f8e867a9fb1de4185c3baeab5c79faa to your computer and use it in GitHub Desktop.
import React, { useEffect } from 'react'
import Modal from '../components/StyledModalAlert'
const Alert = ({autoHideDuration = 3000, open, message, onClose}) => {
return (
<>
{open && (
<Modal
hasBackdrop={false}
onCloseCompleted={() => onClose()}
render={({onRequestClose}) => {
let timeout
useEffect(() => {
if (open) {
timeout = setTimeout(() => {
onRequestClose()
}, autoHideDuration)
return () => clearTimeout(timeout)
}
}, [open])
return (
<div onClick={() => onRequestClose()}>{message}</div>
)
}}
/>
)}
</>
)
}
export default Alert
import React, { forwardRef, useRef, useEffect } from 'react'
import { createPortal } from 'react-dom'
import useKeyPress from '../hooks/useKeypress'
import useLockBodyScroll from '../hooks/useLockBodyScroll'
const Portal = ({children}) => createPortal(children, document.getElementById('modal-root'))
const Backdrop = forwardRef(({onClick, zIndex = 1000, style}, ref) => (
<div ref={ref} style={{position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, backgroundColor: 'black', opacity: 0, zIndex: zIndex, outline: 'none', tabIndex: -1, ...style}} onClick={onClick} />
))
const Modal = ({render, className, onCloseCompleted = () => {}, zIndex = 1001, hasBackdrop = true, clickOutsideDisabled = false, backdropOpacity = 0.6, exitAnimation, enterAnimation, backdropStyle}) => {
if (hasBackdrop) useLockBodyScroll()
const modal = useRef()
const backdrop = useRef()
let initialOpacity = 0
const handleExit = () => {
if (exitAnimation) {
exitAnimation(modal, backdrop, backdropOpacity, onCloseCompleted)
} else {
onCloseCompleted()
}
}
useEffect(() => {
if (enterAnimation) {
enterAnimation(modal, backdrop, backdropOpacity)
} else {
modal.current.style.opacity = 1
modal.current.style.transform = 'translateY(0)'
backdrop.current.style.opacity = backdropOpacity
}
}, [])
const escPress = useKeyPress('Escape')
if (escPress) handleExit()
return (
<Portal>
<div className={className} ref={modal} tabIndex='-1' style={{opacity: initialOpacity, position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, zIndex: zIndex, overflow: 'hidden', pointerEvents: 'none', outline: 'none', display: 'flex', alignItems: 'center', justifyContent: 'center'}}>
<div>
{render({onRequestClose: handleExit})}
</div>
</div>
{hasBackdrop &&
<Backdrop
style={backdropStyle}
ref={backdrop}
onClick={() => {
if (clickOutsideDisabled) return false
handleExit()
}} />
}
</Portal>
)
}
export default Modal
import { Component } from 'react'
import PropTypes from 'prop-types'
import { createPortal } from 'react-dom'
const portalRoot = document.getElementById('portal-root')
export default class Portal extends Component {
static propTypes = {
children: PropTypes.node.isRequired
}
render() {
return createPortal(this.props.children, portalRoot)
}
}
import React from 'react'
import Modal from './Modal'
import styled from 'styled-components'
const enterAnimation = (modal, backdrop, backdropOpacity) => {
if (!modal.current) return
if ('animate' in modal.current) {
backdrop.current.animate([{opacity: 0}, {opacity: backdropOpacity}], {duration: 300, easing: 'ease'})
modal.current.animate([{opacity: 0, transform: 'translateY(-100px)'}, {opacity: 1, transform: 'translateY(0)'}], {duration: 300, easing: 'ease'})
.onfinish = () => {
if (modal.current) { modal.current.style.opacity = 1 }
if (backdrop.current) { backdrop.current.style.opacity = backdropOpacity }
}
} else {
modal.current.style.opacity = 1
modal.current.style.transform = 'translateY(0)'
backdrop.current.style.opacity = backdropOpacity
}
}
const exitAnimation = (modal, backdrop, backdropOpacity, callback) => {
if (!modal.current) return
if ('animate' in modal.current) {
backdrop.current.animate([{opacity: backdropOpacity}, {opacity: 0}], {duration: 300, easing: 'ease'})
modal.current.animate([{opacity: 1, transform: 'translateY(0)'}, {opacity: 0, transform: 'translateY(-100px)'}], {duration: 300, easing: 'ease'})
.onfinish = () => { callback() }
} else {
callback()
}
}
const StyledModal = styled(Modal)`
& > div {
position: relative;
background-color: white;
box-shadow: 0 7px 8px -4px rgba(0,0,0,0.2), 0 13px 19px 2px rgba(0,0,0,0.14), 0 5px 24px 4px rgba(0,0,0,0.12);
pointer-events: auto;
height: calc(100vh);
max-height: 295px;
margin: 0;
overflow: scroll;
width: 100%;
border-radius: 8px;
@media (min-width: 40em) {
margin: 32px auto;
width: auto;
/* height: min-content; */
height: calc(100vh - 64px);
max-height: 800px;
min-width: 36em;
}
}
`
const StyledModalWithAnimations = props => (
<StyledModal enterAnimation={enterAnimation} exitAnimation={exitAnimation} {...props} />
)
export default StyledModalWithAnimations
import React from 'react'
import Modal from './Modal'
import styled from 'styled-components'
const enterAnimation = (modal, backdrop, backdropOpacity) => {
if (!modal.current) return
if ('animate' in modal.current) {
modal.current.animate([{opacity: 0, transform: 'translateY(-100px)'}, {opacity: 1, transform: 'translateY(0)'}], {duration: 300, easing: 'ease'})
.onfinish = () => {
if (modal.current) { modal.current.style.opacity = 1 }
}
} else {
modal.current.style.opacity = 1
modal.current.style.transform = 'translateY(0)'
}
}
const exitAnimation = (modal, backdrop, backdropOpacity, callback) => {
if (!modal.current) return
if ('animate' in modal.current) {
modal.current.animate([{opacity: 1, transform: 'translateY(0)'}, {opacity: 0, transform: 'translateY(-100px)'}], {duration: 300, easing: 'ease'})
.onfinish = () => { callback() }
} else {
callback()
}
}
const StyledModal = styled(Modal)`
align-items: stretch !important;
& > div {
cursor: pointer;
margin-top: 8px;
pointer-events: auto !important;
height: 40px;
border-radius: 4px;
padding: 0 40px;
background-color: hsla(0, 0%, 0%, 0.8);
color: hsla(0, 0%, 90%, 1);
min-width: 100px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
`
const StyledModalWithAnimations = props => (
<StyledModal enterAnimation={enterAnimation} exitAnimation={exitAnimation} {...props} />
)
export default StyledModalWithAnimations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment