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

@reactleaf/modal

v1.1.6

Published

React Modal Library with context

Downloads

733

Readme

@reactleaf/modal

npm version

React modal with context and hooks

Concept

This library provides container, and hooks to open and close modal. Main Concept of this library is providing type-safe method to open any modals on anywhere of your code. Second object ismodal code will not be loaded until modal is opened: Reduce bundle size. This is important for faster web.

Installation and Usage

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

Modal Register

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 bundled on app/page code.

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

export default register;

But in some cases, you may need to pre-load some modals before they are opened. Then see below

Modal Context

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

import { ModalProvider } from "@reactleaf/modal";
import register from "./modals/register";

function App() {
  ...
  return <ModalProvider register={register}>{...}</ModalProvider>
}

useModal Hook

You cannot import useModal() directly. call createModalHook() to make the hook.

It is intended to inject type of register, to check modal type and props are properly provided.

// useModal.ts
import { createModalHook } from "@reactleaf/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.

// [WARN] preloadModal is not a hook.
import { createModalPreloader } from "@reactleaf/modal";
const preloadModal = createModalPreloader(register);

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

Props

  • register - your modal register
  • defaultOverlayOptions - Set default value of overlayOptions.

How to use defaultOverlayOptions:

type defaultOverlayOptions = Partial<OverlayOptions>;

// just use as default. default values are decribed on openModal()'s description.
return (
  <ModalProvider register={register}>
    <App />
  </ModalProvider>
);
// if you need some settings that applied to every modal, use 'default'
return (
  <ModalProvider
    register={register}
    defaultOverlayOptions={{ closeDelay: 300 }}
  >
    <App />
  </ModalProvider>
);

createModalHook()

const useModal = createModalHook<typeof yourModalRegister>();
const { openModal, closeModal, closeAll, openedModals } = useModal();

openModal(payload)

open selected typed modal with given props

interface OpenModalPayload {
  type: keyof Register;
  props?: Props;
  overlayOptions?: OverlayOptions;
  events?: ModalEvents;
}

function openModal(payload: OpenModalPayload);
  • Props - Matching Props as type. if type === "Alert", props should be React.ComponentProps<Alert>
  • OverlayOptions
export interface OverlayOptions {
  className?: string; // to distinguish each overlay element: make different animation per modal.
  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
  dim?: boolean; // default: true
  preventScroll?: boolean; // default: true, when modal is opened, body scroll is blocked.
}
  • ModalEvents
export interface ModalEvents {
  onOpen?(payload: { type; props; id }): void; // modal will be loaded asynchrounously. onOpen() is called when modal component is actually mounted.
  beforeClose?(): PromiseOr<void>; // It called right before modal close. to prevent close modal, throw Error in beforeClose(). If Promise is returned, modal will be opened until Promise is resolved.
  onClose?(): void; // a callback that is called right after modal is closed.
}
  • returns - It returns unique "ID" of opened modal. You can use it to close that specific modal.

closeModal({ id: string })

Close specifig modal. This action requires the "ID" that openModal() returns.

closeAll()

Close all opened modals

openedModals: OpenModalPayload[]

Returns opened modals. It is an array of the OpenModalPayload, so you can check any modal is opened, or some type of modal is opened or not.

OverlayOptions

There are three points that you could set overlay options.

  1. openModal({ type: "...", overlayOptions: HERE })
  • This options are applied ONLY to the modal opens by the call.
  1. When you exports Modal Components
  • This options are applied to ALL of these type modals.
// register
  ...
  'common/Alert': () => import('./Alert'),

// ./Alert.tsx
export const defaultOverlayOptions: OverlayOptions;

export default function Alert(props) {
  return ...
}
  1. When setting MoalProvider
  • This options are applied to ALL TYPE OF modals.
<ModalProvider register={register} defaultOverlayOptions={HERE}>
  <YourApp />
</ModalProvider>

The first one has higher priority, and last one has lower. These options are merged by Object.assign() way.

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. See Slideup Example.

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

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

How to close opened modal?

Recommended way to close modal is closing by modal itself. see more on below

But there are some other ways.

  • const id = openModal(); closeModal({ id });
  • closeAll()
  • closeOnOverlayClick: true - if user click outside of modal (may be darken with dim color), top modal is closed.

Can I open modals without hooks?

@reactleaf/modal recieves message, so you can use window.postMessage() to open modal. This is useful when you use third-party state control libraries, like redux-saga.

But be careful: postMessage is NOT GOOD for type checking, and cannot handle functions on props. If your modal has props like onConfirm, postMessage cannot handle the props.

postMessage only can receive openModal payload. CANNOT CLOSE modals.

window.postMessage({
  to: "@reactleaf/modal",
  payload: {
    type: "Example",
    props: { warning: "postMessage only can send SERIALIZABLE values." },
  },
});

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/modal";

interface Props extends BasicModalProps {
  title: string;
  message: string;
}
const Alert = ({
  title,
  message,
  visible, // injected by modal
  close, // injected by 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>
  );
};

Styling

There is default style file You can import this

import "@reactleaf/modal/style.css";

or make or override your own styles.

  • .modal-overlay - Each modal's overlay element.
  • .modal-overlay.dim - When you applied overlayOption: { dim: true }. This option is true by default.
  • .modal-overlay.visible - .visible is attatched after a frame after mount. This is for mounting animation.

Here is some basic overriding example you can use

.modal-overlay {
  opacity: 0;
  transition: opacity 0.3s;
}
.modal-overlay.dim {
  background-color: rgba(0, 0, 0, 0.5);
}
.modal-overlay.visible {
  opacity: 1;
}

Also, OverlayOptions has className parameter, to distinguish each overlay. You can add className to each modal, so every overlay element can have different animation or dim colors.

Working Examples

See more on Examples