Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: use hook with modal manager #453

Merged
merged 1 commit into from Nov 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 11 additions & 8 deletions react-responsive-modal/src/index.tsx
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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();
}
Expand All @@ -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 (
Expand All @@ -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;
}

Expand Down
40 changes: 26 additions & 14 deletions 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<any>; blockScroll: boolean }[] = [];

/**
* Handle the order of the modals.
* Inspired by the material-ui implementation.
*/
export default {
export const modalManager = {
/**
* Return the modals array
*/
Expand All @@ -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<any>, 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<any>) => {
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<any>) =>
!!modals.length && modals[modals.length - 1].element === modal,
};

export function useModalManager(
ref: Ref<any>,
open: boolean,
blockScroll: boolean
) {
useEffect(() => {
if (open) {
modalManager.add(ref, blockScroll);
}
return () => {
modalManager.remove(ref);
};
}, [open, ref]);
}
8 changes: 1 addition & 7 deletions 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';

Expand All @@ -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();
};