diff --git a/src/App.css b/src/App.css index 46717544..d5fec1b0 100644 --- a/src/App.css +++ b/src/App.css @@ -133,10 +133,6 @@ button:active { margin-top: 46px; /* This matches the height of #search */ } -body.overlay-open { - overflow: hidden; -} - #webamp { opacity: 0; transition: opacity 400ms ease-in-out; @@ -163,32 +159,6 @@ body.webamp-loaded #webamp { backface-visibility: hidden; } -.overlay { - z-index: 1000; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0); - transition: background-color 400ms ease-out; -} - -body.overlay-open .overlay { - background: rgba(0, 0, 0, 0.95); -} - -#close-modal { - color: #a7a394; - position: fixed; - top: 10px; - right: 10px; - padding: 0; - font-size: 50px; - line-height: 25px; - text-decoration: none; -} - .metadata { position: absolute; margin: 10px; diff --git a/src/Overlay.js b/src/Overlay.js index 3dc8e030..fce23034 100644 --- a/src/Overlay.js +++ b/src/Overlay.js @@ -1,97 +1,10 @@ -import React from "react"; -import ReactDOM from "react-dom"; import { connect } from "react-redux"; import * as ActionCreators from "./redux/actionCreators"; - -class Overlay extends React.Component { - constructor() { - super(); - this._node = document.createElement("div"); - this._node.classList.add("overlay"); - this._handleKeyDown = this._handleKeyDown.bind(this); - this._handleClick = this._handleClick.bind(this); - this._handleTouchMove = this._handleTouchMove.bind(this); - - window.document.body.appendChild(this._node); - } - - componentDidMount() { - window.document.addEventListener("keydown", this._handleKeyDown); - window.document.addEventListener("touchmove", this._handleTouchMove); - // TODO: This is technically a race condition, since we could unmount before this fires. - if (this.props.shouldAnimate) { - setTimeout(() => { - // This does not seem to work the first time. - // This should _not_ work on page load - document.body.classList.add("overlay-open"); - }, 0); - } else { - document.body.classList.add("overlay-open"); - } - } - - componentWillUnmount() { - window.document.body.removeChild(this._node); - window.document.removeEventListener("keydown", this._handleKeyDown); - window.document.removeEventListener("touchmove", this._handleTouchMove); - document.body.classList.remove("overlay-open"); - } - - _handleTouchMove(e) { - e.preventDefault(); - } - - _handleKeyDown(e) { - // Esc - if (e.keyCode === 27) { - this.props.closeModal(); - } - } - - _handleClick(e) { - if (e.target === e.currentTarget) { - this.props.closeModal(); - } - } - render() { - return ReactDOM.createPortal( -
e.stopPropagation()} - > - { - this.props.closeModal(); - e.preventDefault(); - }} - aria-label="Close Modal" - > - × - - {this.props.children} -
, - this._node - ); - } -} +import BaseOverlay from "./components/BaseOverlay"; const mapDispatchToProps = dispatch => ({ closeModal() { dispatch(ActionCreators.closeModal()); } }); -export default connect( - null, - mapDispatchToProps -)(Overlay); +export default connect(null, mapDispatchToProps)(BaseOverlay); diff --git a/src/components/BaseOverlay.js b/src/components/BaseOverlay.js new file mode 100644 index 00000000..6e9a023a --- /dev/null +++ b/src/components/BaseOverlay.js @@ -0,0 +1,115 @@ +import React, { + useEffect, + useCallback, + useLayoutEffect, + useState +} from "react"; +import ReactDOM from "react-dom"; + +function handleTouchMove(e) { + e.preventDefault(); +} + +function Overlay({ shouldAnimate, closeModal, children }) { + const [mounted, setMounted] = useState(); + + useLayoutEffect(() => { + const timeout = setTimeout(() => { + setMounted(true); + }); + const bodyOverflow = document.body.style.overflow; + document.body.style.overflow = "hidden"; + return () => { + document.body.style.overflow = bodyOverflow; + if (timeout != null) { + clearTimeout(timeout); + } + }; + }, []); + + const handleClick = useCallback( + e => { + if (e.target === e.currentTarget) { + closeModal(); + } + }, + [closeModal] + ); + + const handleKeyDown = useCallback( + e => { + // Esc + if (e.keyCode === 27) { + closeModal(); + } + }, + [closeModal] + ); + + useEffect(() => { + window.document.addEventListener("keydown", handleKeyDown); + window.document.addEventListener("touchmove", handleTouchMove); + return () => { + window.document.removeEventListener("keydown", handleKeyDown); + window.document.removeEventListener("touchmove", handleTouchMove); + }; + }, [handleKeyDown]); + + return ReactDOM.createPortal( +
+
+ { + closeModal(); + e.preventDefault(); + }} + aria-label="Close Modal" + style={{ + color: "#a7a394", + position: "fixed", + top: 10, + right: 10, + padding: 0, + fontSize: 50, + lineHeight: "25px", + textDecoration: "none" + }} + > + × + + {children} +
+
, + window.document.body + ); +} + +export default Overlay;