Open cyruscuenca opened 2 years ago
Have you tried using parentSelector property from the docs?
I have it set to my root. I'll experiment with attaching it to the navigation bar by ID
I appended my modal to the profile image div in an attempt to position the modal relative to the image. However, the modal seems to behave as normal and create a fixed position overlay with a modal over it.
Basically, I'm trying to position my modal like a dropdown.
any updates?
Yes, I just had to set it to the correct element. Sorry for keeping this open!
Hey, I've a similar requirement in my project. Hoe did you solve this issue?
I'm at a similar position. I need to have the modal appear where my mouse clicked on an image. I've set the parentSelector property to the parent of my image, but it is still responding with the default document.body functionality.
parentSelector={() => document.querySelector('.modalParent')}
@cyruscuenca @C0DE-RUNNER @kaichii @exaucae Have you looked in the resultant style for the components involved?
below is my working code. one can provide a tiny wrapper library to ease everyone else life. I can do that in the coming weeks
import React, { ReactNode, RefObject, useCallback, useEffect, useState } from 'react';
import ReactModal from 'react-modal';
import { css } from '@emotion/css';
import { brandColors, spaces } from '../style';
import { devicePoints } from '../breakpoints';
export type MenuProps = {
children: ReactNode;
open: boolean;
parentRef: RefObject<Element>;
contentStyle: string;
anchorOrigin: 'bottom-left' | 'bottom-right';
onClose: () => void;
};
export const Menu = ({ children, parentRef, open, onClose, anchorOrigin, contentStyle }: MenuProps) => {
const [contentRef, setContentRef] = useState(null);
const handleClickOutsideContent = useCallback(
(event: MouseEvent) => {
const eventTarget = event.target as HTMLElement;
const isClosable =
open &&
!parentRef?.current?.contains(eventTarget) &&
!contentRef?.contains(eventTarget) &&
!eventTarget.closest('.ReactModal__Overlay');
if (isClosable) onClose();
},
[contentRef, open, parentRef, onClose]
);
const getRightPosition = useCallback(() => {
if (parentRef?.current && contentRef) {
const compStyles = window.getComputedStyle(parentRef.current);
const parentPaddingRight = compStyles.getPropertyValue('padding-right').replace('px', '');
const menuStyle = window.getComputedStyle(contentRef);
const menuWidth = menuStyle.getPropertyValue('width').replace('px', '');
switch (anchorOrigin) {
case 'bottom-left':
return Number(parentRef?.current?.clientWidth) - Number(parentPaddingRight);
default:
return Number(menuWidth) - Number(parentPaddingRight);
}
}
return 0;
}, [anchorOrigin, contentRef, parentRef]);
useEffect(() => {
document.addEventListener('click', handleClickOutsideContent);
return () => {
document.removeEventListener('click', handleClickOutsideContent);
};
}, [handleClickOutsideContent]);
return (
<ReactModal
overlayClassName={css`
position: fixed;
width: 100vw;
top: 0;
left: unset;
right: 0;
bottom: 0;
background-color: unset;
@media (min-width: ${devicePoints.tablet}) {
position: absolute;
width: auto;
right: ${getRightPosition()}px;
top: unset;
}
`}
className={css`
position: static;
inset: auto;
width: 100vw;
height: 100vh;
border-radius: 0;
padding: 0;
margin: 0;
border: 0;
outline: none;
z-index: auto;
overflow: auto;
box-shadow: 0 4px 12px 0 ${brandColors.neutrals.semiGrey};
background-color: ${brandColors.neutrals.white};
@media (min-width: ${devicePoints.tablet}) {
position: inherit;
z-index: 2;
margin-top: ${spaces.s16};
}
${contentStyle};
`}
portalClassName={css`
position: relative;
`}
isOpen={open}
onRequestClose={onClose}
parentSelector={() => parentRef.current}
contentRef={(node) => setContentRef(node)}
ariaHideApp={false}
>
{children}
</ReactModal>
);
};
Example Usage:
export const BottomRight = () => {
const [open, setOpen] = useState(false);
const parentRef = useRef(null);
const handleClose = () => {
setOpen(false);
};
const handleOpen = () => {
setOpen(true);
};
return (
<>
<button type="button" ref={parentRef} onClick={handleOpen}>
Open bottom right Menu
</button>
<Menu
anchorOrigin="bottom-right"
onClose={handleClose}
open={open}
parentRef={parentRef}
contentStyle={`
@media (min-width: ${devicePoints.tablet}) {
background-color: green;
height: 250px;
width: 200px;
}
`}
>
This is the bottom right menu content
</Menu>
</>
);
};
Thanks, @exaucae. can you explain this use case?
sure @diasbruno ,
I needed a menu component that could be positioned at the right or left of its parent element, like MUI react menu. But a constraint on our component library made us exclude MUI usage. Another alternative was react-menu but I faced an awkward configuration issue. Since we already used react-modal for our Dialogs, I went with it for our Menus.
Hmmm...I have seen this before. Using react-modal in this case is not the best option. Your solution looks great, I'd recommend extract this code to its own library or maybe instead of using react-modal behind the curtains, you can thing something more lightweight.
Hmmm...I have seen this before. Using react-modal in this case is not the best option.
Indeed, came as last resort :)
Your solution looks great, I'd recommend extract this code to its own library or maybe instead of using react-modal behind the curtains, you can thing something more lightweight.
sure thing. Let's see what I can do in coming weeks
Summary:
Steps to reproduce:
ReactModal.setAppElement('#root');
What I want
I want to position this modal relative to it's parent component. This modal is for a user menu popup, and it needs to appear below a fixed navbar. The position of the user image is not static. The site is responsive, so the dropdown must be relative to that parent.
Link to example of issue:
https://www.google.com has a similar modal popup when you sign in with an account.
Here is an example image: https://imgur.com/KIykuAc
Any help would be apprecieted.