<Modal />
Add dialogs to your web application for lightboxes, user notifications, or completely custom content.Component
1import React, { Fragment, useEffect, useLayoutEffect, useState } from "react";2import ReactDOM from "react-dom";3import "./Styles/_modal.scss";45export default function Modal({ children, ...props }) {6 const { show, callback } = props;7 useLockedBody(show);89 return ReactDOM.createPortal(10 <Fragment>11 {show && <div className="Backdrop" onClick={callback} />}12 <div className={`Modal ${show ? "active" : ""}`}>13 <div className="Modal__Close" onClick={callback}>14 Close15 </div>16 <div className="Modal__Content">{children}</div>17 </div>18 </Fragment>,19 document.getElementById("portals")20 );21}2223Modal.defaultProps = {24 show: false,25 callback: () => { },26};2728export { ModalBody } from "./Components/ModalBody";29export { ModalHeader } from "./Components/ModalHeader";30export { ModalFooter } from "./Components/ModalFooter";3132// Hooks For Adding Functionalities.33const useLockedBody = (initialLocked = false) => {34 const [locked, setLocked] = useState(initialLocked);3536 // Do the side effect before render37 useLayoutEffect(() => {38 if (!locked) {39 return;40 }4142 // Save initial body style43 const originalOverflow = document.body.style.overflow;44 const originalPaddingRight = document.body.style.paddingRight;4546 // Lock body scroll47 document.body.style.overflow = "hidden";4849 // Get the scrollBar width50 const root = document.getElementById("root"); // or root51 const scrollBarWidth = root ? root.offsetWidth - root.scrollWidth : 0;5253 // Avoid width reflow54 if (scrollBarWidth) {55 document.body.style.paddingRight = `${scrollBarWidth}px`;56 }5758 return () => {59 document.body.style.overflow = originalOverflow;6061 if (scrollBarWidth) {62 document.body.style.paddingRight = originalPaddingRight;63 }64 };65 }, [locked]);6667 // Update state if initialValue changes68 useEffect(() => {69 if (locked !== initialLocked) {70 setLocked(initialLocked);71 }72 // eslint-disable-next-line react-hooks/exhaustive-deps73 }, [initialLocked]);7475 return [locked, setLocked];76};
Styles
1$properties: (2 breakpoints: (3 small: 40em,4 ),5 modal: (6 height: 80vh,7 maxHeight: 85vh,8 ),9 backdrop: (10 bgclr: hsla(0, 0%, 96%, 0.502),11 ),12);1314.Modal {15 position: fixed;16 display: none;17 background-color: white;18 z-index: 600;19 top: 50%;20 left: 50%;21 -webkit-transform: translate(-50%, -50%);22 -ms-transform: translate(-50%, -50%);23 transform: translate(-50%, -50%);24 width: 350px;25 -webkit-transition: all 0.3s ease-out;26 -o-transition: all 0.3s ease-out;27 transition: all 0.3s ease-out;28 padding-top: 1.5rem;29 -webkit-box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,30 rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;31 box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,32 rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;33 height: map-get($properties, modal, height);34 max-height: map-get($properties, modal, maxHeight);3536 &.active {37 display: block;38 }3940 &__Content {41 display: -ms-grid;42 display: grid;43 -ms-grid-rows: auto;44 grid-template-rows: auto;45 padding: 0 0.5rem;46 height: 100%;47 overflow: scroll;48 }4950 &-Header {51 }5253 &-Body {54 }5556 &-Footer {57 }5859 &__Close {60 position: absolute;61 top: 0;62 right: 5px;63 cursor: none;64 color: red;6566 @media only screen and (min-width: map-get($properties, breakpoints, small)) {67 cursor: pointer;68 }69 }7071 @media only screen and (min-width: map-get($properties, breakpoints, small)) {72 width: auto;73 }74}7576.Backdrop {77 position: fixed;78 top: 0;79 left: 0;80 bottom: 0;81 right: 0;82 z-index: 500;83 background-color: map-get($properties, backdrop, bgclr);84}
Usage
1