npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

styled-modal

v7.1.4

Published

A Modal built with styled-components and Portals with Server-Side Rendering Support.

Downloads

1,602

Readme

styled-modal

GitHub Actions version code size dependencies devDependencies

A Modal built with styled-components and Portals with Server-Side Rendering Support.

Inspired by Render React portals on the server - Michal Zalecki.

Props

Below is a cheatcheet of all available props. All of them are optional.

| Prop | Type | description | |:--------------------|:----------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------| | afterClose | () => Promise | void | Async function ran after closing | | afterOpen | () => Promise | void | Async function ran after opening | | appId | string | Id of the main react root dom node. Will set as aria-hidden=true | | beforeClose | () => Promise | void | Async function ran before closing | | beforeOpen | () => Promise | void | Async function ran before opening | | children | any | Contents of the modal | | closeOnEsc | boolean | Whether the modal should close (call onClose) when pressing Esc | | closeOnOutsideClick | boolean | Whether the modal should close (call onClose) when clicking outside the modal | | containerComponent | any | A custom component for the container. By default the dark background. Its child is the overscrollComponent | | lockFocusWhenOpen | boolean | Whether focus should be locked inside the modal, via react-focus-lock | | lockScrollWhenOpen | boolean | Whether scrolling should be locked when the modal is open, via body-scroll-lock | | modalComponent | any | A custom component for the modal. By default a simple div. Its children are the <StyledModal>'s children | | onClose | () => Promise | void | Async function ran when closing. Typically for setting the prop open: false | | onOpen | () => Promise | void | Async function ran after opening. Typically for setting the prop open: true | | open | boolean | Whether the modal is open. Defaults to true | | overscrollComponent | any | A custom component for the overscoll. By default this allows the Modal to nicely scroll. Its child is the modalComponent | | scrollLockRef | React.RefObject<any> | A reference to the Component of which scrolling will be disabled. By default document.body. | | target | string | The id of the Portal's target dom node. By default modal. The node will be added to the end of document.body if not present |

About

This component is used for rendering React components inside a Modal. There are some basic styles included but you should supply your own styles. The component uses the native ReactDOM.createPortal introduced with React 16.2.0.

Yarn:

yarn add styled-modal

npm:

npm install styled-modal

Please see the included Storybook for examples:

[email protected]:iiroj/styled-modal.git
cd styled-modal
yarn
yarn start
open http://127.0.0.1:8080/

<Modal />

The main entrypoint of this package is the <Modal /> component. It's a stateful component with support for closing on outside click and ESC keypress. You can supply the open-state with a prop, as well as callback functions for opening and closing. There's an option for showing a built-in close button. You can also render it conditionally without any props, as it's open by default. You can further specify the id for portaling.

Finally, you can customize the UI by supplying your own styled components for the Container (backdrop) and Modal via the containerComponent and modalComponent props. Some default styles will be supplied via the styled-components theme prop.

Basic Props

import { render } from "react-dom";
import Modal from "styled-modal";

render(
  <Modal>
    <p>This text will open in a modal</p>
  </Modal>,
  document.getElementById("root")
);

Conditional Rendering

import Modal from 'styled-modal';

...

export default props => {
  const { showModal, handleOpen } = props;

  return (
   <div>
    <button onClick={handleOpen}>Show Modal!</button>
    {showModal && (
      <Modal>
        <p>This text will open in a modal</p>
      </Modal>
    )}
  )
}

Rendering Based on State

import Modal from 'styled-modal';

...

export default props => {
  const { showModal, openCallback, closeCallback } = props;

  return (
   <div>
    <button onClick={handleOpen}>Show Modal!</button>
    <Modal open={showModal} onOpen={openCallback} onClose={closeCallback}>
      <p>This text will open in a modal</p>
    </Modal>
  )
}

Lifecycle Methods

<Modal />supports async lifecycle methods beforeOpen, afterOpen, beforeClose and afterClose (() => Promise<void> | void), which run around the changing of open: boolean (the Modal opening). They are awaited so the modal will only open after beforeOpen resolves, and close after beforeClose resolves. Similarly, the beforeOpen cannot fire before afterClose is resolved, or beforeClose before afterOpen.

The other methods are onOpen: () => Promise<void> | void, which is run between beforeOpen and afterOpen, and onClose: () => Promise<void> | void, similarly.

The onClose method is fired when clicking outside the modal or pressing the Esc key, if closeOnOutsideClick?: boolean = true or closeOnEsc?: boolean = true are enabled, respectively.

Custom styles

It is possible to supply custom modalComponent, containerComponent and overscrollComponent to customize the look of the Modal. These should be styled-components. The components will be provided default styles necessary for the Modal to function in the theme prop.

import React from 'react';
import StyledModal from 'styled-modal';
import styled from 'styled-components';

const ContainerComponent = React.forwardRef(({ children, className, open }, ref) =>
  open
    ? <div className={className} ref={ref}>{children}</div>
    : null
);

const Container = styled(ContainerComponent)`
  background-color: rgba(0, 0, 0, 0.4);
  ${props => props.theme.container};
`

const Overscroll = styled.div`
  ${props => props.theme.overscroll};
`

const Modal = styled.div`
  background-color: white;
  ${props => props.theme.modal};
`

...

export default () => (
  <StyledModal open={showModal} containerComponent={Container} modalComponent={Modal} overscrollComponent={Overscroll}>
    <p>This text will open in a modal</p>
  </StyledModal>
)

Animations

StyledModal supports animations since you can supply custom components. These can be wrapped in whatever, like react-motion-ui-pack's <Transition /> as seen in the Storybook's Animated example.

There's is also a special prop isClientSide: boolean available to all components. You can disable your animations based on this server-side, to not mess up initial styles client-side.

Refs

Since we need the DOM reference to the modalComponent for accessibility features (focus lock and closing by clicking outside), we supply (via React.createRef) a ref prop to it. Styled-components v4 and emotion v10 both support the new React.forwardRef, introduced in React v16.3, method of forwarding the ref. If you use a custom React component, you should use that as well.

If you want to disable scrolling of another element when the modal is open (like a custom component inside <body>), its ref can be supplied via the scrollLockRef prop.

<Portal />

This component handles the actual portaling and is used as the top-most wrapper component in <Modal />. You can also use <Portal /> directly to render things into some other DOM Element.

import { Portal } from "styled-modal";

const target = document.getElementById("Helsinki");

render(
  <Portal target={target}>
    <p>This text will be portaled to #Helsinki</p>
  </Portal>,
  document.getElementById("root")
);

Server-Side Rendering

On the server side we cannot use ReactDOM.createPortal. This is why the underlying <Portal /> component returns null and instead collects the portals' contents into an array with PortalCollector. We can then render this array seperately ourselves! To prevent rendering double client-side, we must also flush the portal target element before (client-side) rendering with flushPortals.

server

import Express from 'express';
import { renderToStaticMarkup } from 'react-dom/server';
import { ServerStyleSheet } from 'styled-components';
import Modal, { PortalCollector } from 'styled-modal';
import App from 'components/App';

...

const renderResponse = async (req, res) => {
  const initialState = await getInitialState(req);
  const sheet = new ServerStyleSheet();
  const collectedPortals = {}

  const appHtml = renderToStaticMarkup(
    sheet.collectStyles(
      <PortalCollector portals={collectedPortals}>
        <App>
          <Modal>
            <p>This text will be portaled to #modal</p>
          </Modal>
        </App>
      </PortalCollector>
    )
  );

  const portalHtml = renderToStaticMarkup(collectedPortals);
  const styles = sheet.getStyleTags();

  const response = renderHtmlResponse(initialState, appHtml, portalHtml, styles);
  res.send(response);
};

app.use(renderResponse);

client

import { render } from 'react-dom'
import Modal, { flushPortals } from 'styled-modal';
import App from 'components/App';

flushPortals();

render(
  <App>
    <Modal>
      <p>This text will be portaled to #modal</p>
    </Modal>
  </App>,
  document.getElementById('root');
);