From bcf60cdcc509c13cd19714ccc8f0d8022b223b62 Mon Sep 17 00:00:00 2001 From: Julien Scholz <8947616+pikaju@users.noreply.github.com> Date: Tue, 14 Feb 2023 21:10:59 +0100 Subject: [PATCH 1/6] Add keepMounted option --- react-responsive-modal/src/index.tsx | 12 +++++++++--- react-responsive-modal/styles.css | 15 +++++++++++++++ website/src/docs/index.mdx | 1 + 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/react-responsive-modal/src/index.tsx b/react-responsive-modal/src/index.tsx index 2427009d..9f8ed427 100644 --- a/react-responsive-modal/src/index.tsx +++ b/react-responsive-modal/src/index.tsx @@ -158,6 +158,11 @@ export interface ModalProps { * Callback fired when the Modal has exited and the animation is finished. */ onAnimationEnd?: () => void; + /** + * Keeps the Modal mounted even when it is hidden. This is useful for keeping the DOM state + * inside the Modal as well as for SEO purposes. + */ + keepMounted: boolean; children?: React.ReactNode; } @@ -187,6 +192,7 @@ export const Modal = React.forwardRef( onEscKeyDown, onOverlayClick, onAnimationEnd, + keepMounted = false, children, reserveScrollBarGap, }: ModalProps, @@ -260,7 +266,7 @@ export const Modal = React.forwardRef( useEffect(() => { // If the open prop is changing, we need to open the modal // This is also called on the first render if the open prop is true when the modal is created - if (open && !showPortal) { + if ((open || keepMounted) && !showPortal) { setShowPortal(true); handleOpen(); } @@ -292,7 +298,7 @@ export const Modal = React.forwardRef( }; const handleAnimationEnd = () => { - if (!open) { + if (!open && !keepMounted) { setShowPortal(false); } @@ -324,6 +330,7 @@ export const Modal = React.forwardRef( animation: `${overlayAnimation} ${animationDuration}ms`, ...styles?.overlay, }} + onClick={handleClickOverlay} />
void` | | Callback fired when the escape key is pressed. | | **onOverlayClick\*** | `(event: React.MouseEvent) => void` | | Callback fired when the overlay is clicked. | | **onAnimationEnd\*** | `() => void` | | Callback fired when the Modal has exited and the animation is finished. | +| **keepMounted** | `boolean` | false | Keeps the Modal mounted to the DOM tree even when it is hidden. This is useful for keeping DOM state inside the Modal as well as for SEO purposes. | ## License From c9a1da9d23435c13c29bf1725a31c284b303b4c9 Mon Sep 17 00:00:00 2001 From: Julien Scholz <8947616+pikaju@users.noreply.github.com> Date: Tue, 14 Feb 2023 21:51:36 +0100 Subject: [PATCH 2/6] Fix keepMounted being required --- react-responsive-modal/src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react-responsive-modal/src/index.tsx b/react-responsive-modal/src/index.tsx index 9f8ed427..48e2d74f 100644 --- a/react-responsive-modal/src/index.tsx +++ b/react-responsive-modal/src/index.tsx @@ -162,7 +162,7 @@ export interface ModalProps { * Keeps the Modal mounted even when it is hidden. This is useful for keeping the DOM state * inside the Modal as well as for SEO purposes. */ - keepMounted: boolean; + keepMounted?: boolean; children?: React.ReactNode; } From 53d933b38c40e590ded54f9ab8be3e98df2b9c65 Mon Sep 17 00:00:00 2001 From: Julien Scholz <8947616+pikaju@users.noreply.github.com> Date: Tue, 14 Feb 2023 22:44:37 +0100 Subject: [PATCH 3/6] Fix bugs --- react-responsive-modal/src/index.tsx | 18 +++++++++++++++--- react-responsive-modal/styles.css | 7 ++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/react-responsive-modal/src/index.tsx b/react-responsive-modal/src/index.tsx index 48e2d74f..9ff10152 100644 --- a/react-responsive-modal/src/index.tsx +++ b/react-responsive-modal/src/index.tsx @@ -212,6 +212,9 @@ export const Modal = React.forwardRef( // it will match the server rendered content const [showPortal, setShowPortal] = useState(false); + // Used to hide the modal when keepMounted is true + const [display, setDisplay] = useState(false); + // Hook used to manage multiple modals opened at the same time useModalManager(refModal, open); @@ -270,6 +273,9 @@ export const Modal = React.forwardRef( setShowPortal(true); handleOpen(); } + if (open && !display) { + setDisplay(true); + } }, [open]); const handleClickOverlay = ( @@ -298,8 +304,11 @@ export const Modal = React.forwardRef( }; const handleAnimationEnd = () => { - if (!open && !keepMounted) { - setShowPortal(false); + if (!open) { + setDisplay(false); + if (!keepMounted) { + setShowPortal(false); + } } onAnimationEnd?.(); @@ -319,7 +328,10 @@ export const Modal = React.forwardRef( ? ReactDom.createPortal(
Date: Sat, 4 Mar 2023 22:33:55 +0100 Subject: [PATCH 4/6] Fix modal not being scrollable --- react-responsive-modal/src/index.tsx | 4 ++-- react-responsive-modal/styles.css | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/react-responsive-modal/src/index.tsx b/react-responsive-modal/src/index.tsx index 9ff10152..a914c362 100644 --- a/react-responsive-modal/src/index.tsx +++ b/react-responsive-modal/src/index.tsx @@ -330,7 +330,7 @@ export const Modal = React.forwardRef( className={cx(classes.root, classNames?.root)} style={{ ...styles?.root, - ...(display ? {} : { display: 'none' }), + ...(display ? {} : { display: 'none', pointerEvents: 'none' }), }} data-testid="root" > @@ -342,7 +342,6 @@ export const Modal = React.forwardRef( animation: `${overlayAnimation} ${animationDuration}ms`, ...styles?.overlay, }} - onClick={handleClickOverlay} />
Date: Thu, 16 Mar 2023 17:36:44 +0100 Subject: [PATCH 5/6] Add example for keepMounted option --- website/src/components/ExampleRendered.tsx | 2 ++ website/src/docs/index.mdx | 21 ++++++++++++++ website/src/examples/KeepMounted.tsx | 32 ++++++++++++++++++++++ website/src/pages/index.tsx | 8 ++++++ 4 files changed, 63 insertions(+) create mode 100644 website/src/examples/KeepMounted.tsx diff --git a/website/src/components/ExampleRendered.tsx b/website/src/components/ExampleRendered.tsx index 1f6086a3..3510ef69 100644 --- a/website/src/components/ExampleRendered.tsx +++ b/website/src/components/ExampleRendered.tsx @@ -3,6 +3,7 @@ import ExampleMultiple from '../examples/Multiple'; import LongContent from '../examples/LongContent'; import FocusTrapped from '../examples/FocusTrapped'; import FocusTrappedInitialFocus from '../examples/FocusTrappedInitialFocus'; +import KeepMounted from '../examples/KeepMounted'; import CustomCssStyle from '../examples/CustomCssStyle'; import CustomAnimation from '../examples/CustomAnimation'; import CustomCloseIcon from '../examples/CustomCloseIcon'; @@ -14,6 +15,7 @@ const examples: Record JSX.Element> = { longContent: LongContent, focusTrapped: FocusTrapped, focusTrappedInitialFocus: FocusTrappedInitialFocus, + keepMounted: KeepMounted, customCssStyle: CustomCssStyle, customAnimation: CustomAnimation, customCloseIcon: CustomCloseIcon, diff --git a/website/src/docs/index.mdx b/website/src/docs/index.mdx index 11165223..bc960168 100644 --- a/website/src/docs/index.mdx +++ b/website/src/docs/index.mdx @@ -8,6 +8,7 @@ A simple responsive and accessible react modal. - Multiple modals. - Accessible modals. - Easily customizable via props. +- Optionally keep hidden modals mounted to the DOM - Typescript support - [Small bundle size](https://bundlephobia.com/result?p=react-responsive-modal) @@ -103,6 +104,26 @@ You can also set to trap focus within the modal, but decide where to put focus w ``` +### Keeping a hidden modal mounted + +By setting the `keepMounted` property to `true`, you can specify that the modal should always be mounted to the DOM, even when hidden it is hidden. +Instead of removing the DOM nodes, the modal is hidden using `display: none`. + +```js + + ... + +``` + +This is useful when you want the state of the DOM nodes (e.g. the text inside an `` element) to stay alive, as well as for search engine optimization (Google can see the content of the hidden modal). + +Press the button below and watch the element tree in your browser. The modal is a `
` at the very bottom of the ``, and will not disappear even when the modal is closed! + + + ### Custom styling with css Customising the Modal style via css is really easy. For example if you add the following css to your app you will get the following result: diff --git a/website/src/examples/KeepMounted.tsx b/website/src/examples/KeepMounted.tsx new file mode 100644 index 00000000..2555eef5 --- /dev/null +++ b/website/src/examples/KeepMounted.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Modal } from 'react-responsive-modal'; + +const App = () => { + const [open, setOpen] = React.useState(false); + + return ( + <> + + + setOpen(false)} keepMounted> +
+ This modal will stay mounted even when closed. +
+
+ +
+
+ + ); +}; + +export default App; diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx index e8e4cf41..760bc1de 100644 --- a/website/src/pages/index.tsx +++ b/website/src/pages/index.tsx @@ -46,6 +46,14 @@ const IndexPage = () => ( Focus Trapped modal +
  • + + Keeping a hidden modal mounted + +
  • Date: Thu, 16 Mar 2023 18:38:39 +0100 Subject: [PATCH 6/6] Improve how modals are centered --- react-responsive-modal/styles.css | 16 +++++++--------- website/src/examples/KeepMounted.tsx | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/react-responsive-modal/styles.css b/react-responsive-modal/styles.css index 922a9fab..e82792c2 100644 --- a/react-responsive-modal/styles.css +++ b/react-responsive-modal/styles.css @@ -22,23 +22,21 @@ outline: 0; overflow-x: hidden; overflow-y: auto; - text-align: center; + display: flex; + flex-direction: column; + align-items: center; } /* Used to trick the browser to center the modal content properly */ -.react-responsive-modal-containerCenter:after { - width: 0; - height: 100%; +.react-responsive-modal-containerCenter::before, +.react-responsive-modal-containerCenter::after { + flex: 1 1 0; content: ''; - display: inline-block; - vertical-align: middle; } .react-responsive-modal-modal { + flex: 0 0 auto; max-width: 800px; - display: inline-block; - text-align: left; - vertical-align: middle; background: #ffffff; box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.25); margin: 1.2rem; diff --git a/website/src/examples/KeepMounted.tsx b/website/src/examples/KeepMounted.tsx index 2555eef5..d5e0fcd4 100644 --- a/website/src/examples/KeepMounted.tsx +++ b/website/src/examples/KeepMounted.tsx @@ -10,7 +10,7 @@ const App = () => { Open modal - setOpen(false)} keepMounted> + setOpen(false)} keepMounted center>
    This modal will stay mounted even when closed.