reactjs / react-modal

Accessible modal dialog component for React
http://reactcommunity.org/react-modal
MIT License
7.35k stars 809 forks source link

How to render something outside ReactModal__Content but within ReactModal__Overlay #792

Open Albert-Gao opened 4 years ago

Albert-Gao commented 4 years ago

I am using 3.11.1

Our UI requirements, is the close button of a modal is not inside the modal, but at the top-right corner of the screen.

I tried position: absolute and fixed, works on everything but some mobile Safari, it just won't show it at all, the only way is to lift the button outside the ReactModal__Content div

Anyway to render something outside the ReactModal__Content

llorentegerman commented 4 years ago

you can try this:

<ReactModal isOpen>
    <span style={{ position: 'fixed', right: 1, top: 1 }}>X</span>
    <div>your content</div>
</ReactModal>
Albert-Gao commented 4 years ago

Thanks, tried, but some iOS safari will hate this, I think Apple honors HTML structure more than position: fixed, as long as it is inside, it won't render it outside.

llorentegerman commented 4 years ago

you are right, but try with overflow: 'none' for content styles

<ReactModal isOpen
    style={{
        content: {
            overflow: 'none'
        }
    }}
>
    <span style={{ position: 'fixed', right: 1, top: 1 }}>X</span>
    <div>your content</div>
</ReactModal>

this worked for me

OliverJAsh commented 4 years ago

I would really like a way to do this without having to resort to fixed positioning. I would like to use non-fixed position for the elements inside my overlay:

Albert-Gao commented 4 years ago

Actually, tried fixed, as I mentioned when posted, but some version of safari does not like it, absolute is better but still has its incompatibility with some devices, will just use absolute for now.

A fully control of the content would be better. Otherwise, it will have this constraint of having everything inside a box. But in my context, all the control buttons, back button, close button, they are outside the modal box, look like they are on the overlay.

fforres commented 4 years ago

You can always use display: contents (At least for newer-ish browsers) to essentially "omit" ReactModal__Content from the rendering (Big quotes around "omit" btw) https://caniuse.com/#feat=css-display-contents

const customStyles = {
  content: {
    display: "contents",
    borderWidth: 0,
    padding: 0,
    margin: 0
  }
};

(.....)

const MyModal = () => (
    <Modal style={customStyles}>
        <TheActualContentComponent />
    </Modal>
)
fo-fo commented 4 years ago

You can work around this by grabbing a ref to the overlay and rendering your content there with a portal. Incomplete example:

const MyModal: React.FC = () => {
  const [overlayDiv, setOverlayDiv] = useState<HTMLDivElement | null>(null);

  return (
    <ReactModal overlayRef={setOverlayDiv}>
      {overlayDiv &&
        ReactDOM.createPortal(
          <button onClick={/* handle the close button click */} />,
          overlayDiv
        )}
      {/* modal content goes here */}
    </ReactModal>
  );
};

Note that this will likely not play together perfectly with react-modal's tab handling.

timngyn commented 2 years ago

Sorry for bumping an old thread, but in case anyone was trying to figure it out: I think you can do this now using the custom overlayElement prop.

An example of it is in the tests: https://github.com/reactjs/react-modal/blob/827796d48e7d4c74b4362cf90955e162082ee46d/specs/Modal.spec.js#L309-L321

// Custom overlay element to hold the close button outside of the actual modal
  const OverlayElement = (
    props: JSX.IntrinsicAttributes &
      ClassAttributes<HTMLDivElement> &
      HTMLAttributes<HTMLDivElement>,
    contentElement:
      | string
      | number
      | boolean
      | ReactElement<any, string | JSXElementConstructor<any>>
      | ReactFragment
      | ReactPortal
      | null
      | undefined
  ) => {
    return (
      <div
        {...props}
      >
          <CloseIcon 
            onClick={closeModal}
            position="absolute"
            right="20px"
            top="20px" />
        {contentElement}
      </div>
    );
  };

const MyModal = () => <Modal overlayElement={OverlayElement}>this is my modal!</Modal>