diff --git a/react-responsive-modal/src/index.tsx b/react-responsive-modal/src/index.tsx index a84ba231..b8b03f50 100644 --- a/react-responsive-modal/src/index.tsx +++ b/react-responsive-modal/src/index.tsx @@ -3,7 +3,7 @@ import ReactDom from 'react-dom'; import cx from 'classnames'; import CloseIcon from './CloseIcon'; import { FocusTrap } from './FocusTrap'; -import modalManager from './modalManager'; +import { modalManager, useModalManager } from './modalManager'; import { isBrowser, blockNoScroll, unblockNoScroll } from './utils'; const classes = { @@ -172,8 +172,10 @@ export const Modal = ({ // it will match the server rendered content const [showPortal, setShowPortal] = useState(false); + // Hook used to manage multiple modals opened at the same time + useModalManager(refModal, open, blockScroll); + const handleOpen = () => { - modalManager.add(refContainer.current!, blockScroll); if (blockScroll) { blockNoScroll(); } @@ -188,8 +190,12 @@ export const Modal = ({ }; const handleClose = () => { - modalManager.remove(refContainer.current!); - if (blockScroll) { + // Restore the scroll only if there is no modal on the screen + // We filter the modals that are not affecting the scroll + if ( + blockScroll && + modalManager.modals().filter((modal) => modal.blockScroll).length === 0 + ) { unblockNoScroll(); } if ( @@ -204,10 +210,7 @@ export const Modal = ({ const handleKeydown = (event: KeyboardEvent) => { // Only the last modal need to be escaped when pressing the esc key - if ( - event.keyCode !== 27 || - !modalManager.isTopModal(refContainer.current!) - ) { + if (event.keyCode !== 27 || !modalManager.isTopModal(refModal)) { return; } diff --git a/react-responsive-modal/src/modalManager.ts b/react-responsive-modal/src/modalManager.ts index 372aeea2..6c02b92d 100644 --- a/react-responsive-modal/src/modalManager.ts +++ b/react-responsive-modal/src/modalManager.ts @@ -1,10 +1,12 @@ -const modals: { element: HTMLDivElement; blockScroll: boolean }[] = []; +import { Ref, useEffect } from 'react'; + +let modals: { element: Ref; blockScroll: boolean }[] = []; /** * Handle the order of the modals. * Inspired by the material-ui implementation. */ -export default { +export const modalManager = { /** * Return the modals array */ @@ -13,25 +15,35 @@ export default { /** * Register a new modal */ - add: (newModal: HTMLDivElement, blockScroll: boolean) => { - if (modals.findIndex((modal) => modal.element === newModal) === -1) { - modals.push({ element: newModal, blockScroll }); - } + add: (newModal: Ref, blockScroll: boolean) => { + modals.push({ element: newModal, blockScroll }); }, /** * Remove a modal */ - remove: (oldModal: HTMLDivElement) => { - const index = modals.findIndex((modal) => modal.element === oldModal); - if (index !== -1) { - modals.splice(index, 1); - } + remove: (oldModal: Ref) => { + modals = modals.filter((modal) => modal.element !== oldModal); }, /** - * Check if the modal is the first one on the screen + * When multiple modals are rendered will return true if current modal is the last one */ - isTopModal: (modal: HTMLDivElement) => - !!modals.length && modals[modals.length - 1]?.element === modal, + isTopModal: (modal: Ref) => + !!modals.length && modals[modals.length - 1].element === modal, }; + +export function useModalManager( + ref: Ref, + open: boolean, + blockScroll: boolean +) { + useEffect(() => { + if (open) { + modalManager.add(ref, blockScroll); + } + return () => { + modalManager.remove(ref); + }; + }, [open, ref]); +} diff --git a/react-responsive-modal/src/utils.ts b/react-responsive-modal/src/utils.ts index 355284b2..d52f4143 100644 --- a/react-responsive-modal/src/utils.ts +++ b/react-responsive-modal/src/utils.ts @@ -1,5 +1,4 @@ import noScroll from 'no-scroll'; -import modalManager from './modalManager'; export const isBrowser = typeof window !== 'undefined'; @@ -8,10 +7,5 @@ export const blockNoScroll = () => { }; export const unblockNoScroll = () => { - // Restore the scroll only if there is no modal on the screen - // We filter the modals that are not affecting the scroll - const modals = modalManager.modals().filter((modal) => modal.blockScroll); - if (modals.length === 0) { - noScroll.off(); - } + noScroll.off(); };