JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 51
  • Score
    100M100P100Q85516F
  • License MIT

React Modal Library with context

Package Exports

  • @reactleaf/react-modal

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@reactleaf/react-modal) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@reactleaf/react-modal

React modal with context and hooks

Concept

This library uses HoC to provide context and container, and provide hooks to open and close modal. Main Concept of this library is providing type-safe method to open specific modals on anywhere of your code.

More details are on below

Installation and Usage

npm install @reactleaf/react-modal
# or
yarn add @reactleaf/react-modal

At first, you should make a your own modal register. we are using dynamic import, to reduce initial loading size. Any modals registered will be loaded on when modal is called to open, not on initialize sequence.

const register = {
  Alert: () => import("./Alert"),
  Confirm: () => import("./Confirm"),
};

export default register;

Now provide this register to your app. This HoC will provide modalContext to your app, and also modal container, that modals will be rendered. So Simple!

import { withModal } from "@reactleaf/react-modal";
import register from "./modals/register";

function App() {
  ...
}

export default withModal(register, App);

useModal Hook

To open modal, you should use useModal hook. for type-safe of your code, do not use useModal directly.

Don't

If you are non-typescript user, it's okay to use this way.

import { useModal } from "@reactleaf/react-modal";

const { openModal } = useModal();
function openAlert() {
  // openModal cannot check types.
  openModal({ type: "Alert", props: { title: "Hello" } });
}

With this way, you can check type and props are properly provided.

// useModal.ts
import { createModalHook } from "@reactleaf/react-modal";
import register from "./register";

export const useModal = createModalHook<typeof register>();

openModal will check modal type

import { useModal } from './modals/useModal'

const { openModal } = useModal()
function openAlert() {
  openModal({ type: 'Confrim', props: { title: 'Hello', message: 'Wow' } })
              ^^^^
              type 'Confrim' is not assignable to type 'Alert' | 'Confirm'
}

openModal will check props type for the matching type

import { useModal } from './modals/useModal'

const { openModal } = useModal()
function openAlert() {
  openModal({ type: 'Alert', props: { title: 'Hello' } })
                             ^^^^^
                             property 'message' is missing
}

Preload modals

We use dynamic import to load modals when modal opens. It makes code-splitting easier, and initial bundle size smaller. But sometimes, you may need synchronous imports, for instantly open modals. It might for mounting animation, or modal has large dependencies to load on open. Then, you can preload modals before user click the button that opens modal. This calls import() from your register, to ensure openModal() runs synchronously.

import { createModalPreloader } from "@reactleaf/react-modal";
const preloadModal = createModalPreloader(register);

// when component mounted, load relative modals.
useEffect(() => {
  preloadModal("Alert", "Confirm");
}, []);

Props

withModal(register, App)

  • register - your modal register
  • App - your App
  • returns - Higher ordered App

createModalHook()

const useModal = createModalHook<typeof yourModalRegister>();

useModal()

  • retuns - { openModal, closeAll }

openModal(payload)

open selected typed modal with given props

function openModal(payload: {
  type: keyof Register;
  props: Props;
  overlayOptions?: OverlayOptions;
});
  • Props - Matching Props as type. if type === "Alert", props should be React.ComponentProps<Alert>
  • OverlayOptions
export interface OverlayOptions {
  dim?: boolean; // default: true
  closeDelay?: number; // default: 0, as ms. this will make modal close(unmount) delayed. Useful if you want to add closing animation.
  closeOnOverlayClick?: boolean; // default: true
  preventScroll?: boolean; // default: true, when modal is opened, body scroll is blocked.
}

closeAll()

close all opened modals

How to add opening / closing animation?

For animation, modal opening is delayed for a frame. You can add Overlay styles like this.

.modal-overlay {
  opacity: 0;
  transition: opacity 0.3s;
}
.modal-overlay.visible {
  opacity: 1;
}

and also in your custom modal, has visible props. See below to know more about visible props. Be sure that closeDelay option is properly set, if you want to animate on closing. Detailed implementation is at Slideup Example

.slideup {
  transition: transform 500ms;
  transform: translateY(100%);
}

.slideup.visible {
  transform: translateY(0);
}

How to close opened modal?

Modal can only closed by modal itself. see more on below

But there are 2 exceptions.

  • closeAll() - If you close All Modals, then every opened modals are closed.
  • closeOnOverlayClick: true - if user click outside of modal (may be darken with dim color), top modal is closed.

BasicModalProps

When modal is opened by openModal, 2 more props are injected to your modal.

  • close(): void
  • visible: boolean

So, When implementing modal, you can consider close props like this.

import { BasicModalProps } from "@reactleaf/react-modal";

interface Props extends BasicModalProps {
  title: string;
  message: string;
}
const Alert = ({
  title,
  message,
  visible,
  close, // injected by react-modal
}: Props) => {
  return (
    <div className={cx("alert", "modal", { visible })}>
      <p className="modal-title">{title}</p>
      <div className="modal-body">
        <p className="message">{message}</p>
      </div>
      <div className="modal-buttons">
        <button onClick={close}>Close</button>
      </div>
    </div>
  );
};

Working Examples

See more on Examples