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

@vegatality/react-hook-modal

v0.1.25

Published

A simple modal hook for React

Downloads

3

Readme

A simple modal hook for React

This is a simple modal hook for React. It is a custom hook that allows you to create a modal with a simple API.

TOC

Installation

# npm
npm i @vegatality/react-hook-modal

# yarn
yarn add @vegatality/react-hook-modal

# pnpm
pnpm add @vegatality/react-hook-modal

Usage

1. Local State Layer

import { useModalList, type ModalComponent } from '@vegatality/react-hook-modal';

// const SomeModal = ({ closeModal, modalRef }: ModalComponentProps<{ name: string }>) => {
const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
  return (
    <div ref={modalRef}>
      <h1>Some Modal</h1>
      <p>{name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
};

const App = () => {
  const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();

  return (
    <div>
      <ModalComponentList />
      <button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: true } })}>
        Open Modal 1
      </button>
    </div>
  );
};

1-1. with Context API

import { useModalList, useModalContext, ModalProvider, type ModalComponent } from '@vegatality/react-hook-modal';

const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
  return (
    <div ref={modalRef}>
      <h1>Some Modal</h1>
      <p>{name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
};

const ChildComponent = () => {
  const { openModal } = useModalContext();

  return (
    <button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' } })}>
      Open Modal 1
    </button>
  );
};

const App = () => {
  const methods = useModalList();
  const { ModalComponentList, openModal } = methods;

  return (
    <div>
      <ModalComponentList />
      <ModalProvider {...methods}>
        <ChildComponent />
      </ModalProvider>
    </div>
  );
};

2. Global State Layer

import {
  useGlobalModalList,
  GlobalModalList,
  GlobalModalListProvider,
  type ModalComponentProps,
} from '@vegatality/react-hook-modal';

const SomeModal = ({ closeModal, modalRef, name }: ModalComponentProps<{ name: string }>) => {
  return (
    <div ref={modalRef}>
      <h1>Some Modal</h1>
      <p>{name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
};
const App = () => {
  const { openGlobalModal } = useGlobalModalList(); // useGlobalModalList hook uses useContext hook under the hood

  return (
    <button
      onClick={() => openGlobalModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' } })}
    >
      Open Modal 1
    </button>
  );
};

const Main = () => {
  return (
    <GlobalModalListProvider>
      <App />
      <GlobalModalList />
    </GlobalModalListProvider>
  );
};

3. Hierarchical Modal Key Management

You can manage the modal key hierarchically. If you pass an array as a modal key, it will be managed hierarchically.

const openModal1 = () => {
  openModal({
    modalKey: ['test'],
    ModalComponent: TestModal,
    options: { resistBackgroundClick: true },
  });
};

const openModal2 = () => {
  openModal({
    modalKey: ['test', 2],
    ModalComponent: TestModal,
    options: { resistBackgroundClick: [['test']] }, // This will resist background click until ['test'] modal is closed
  });
};

const openGlobalModal1 = () => {
  openGlobalModal({
    modalKey: ['test', 2, { id: 1 }],
    ModalComponent: TestModal,
    options: { resistBackgroundClick: false },
  });
};

4. Can open Multiple Modals

You can open multiple modals with openModal api from useModalList hook.

import { useModalList, type ModalComponent } from '@vegatality/react-hook-modal';

const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
  return (
    <div ref={modalRef}>
      <h1>Some Modal</h1>
      <p>{name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
};

const SomeModal2: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
  return (
    <div ref={modalRef}>
      <h1>Some Modal2</h1>
      <p>{name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
};

const App = () => {
  const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();

  return (
    <div>
      <ModalComponentList />
      <button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: true } })}>
        Open Modal 1
      </button>
      <button onClick={() => openModal({ modalKey: ['some2'], ModalComponent: SomeModal2, modalProps: { name: 'some2' }, options: { resistBackgroundClick: false, scrollable: false } })}>
        Open Modal 2
      </button>
    </div>
  );
};

5. Close Modal API

You can close the modal using the modal key that was passed when it was opened.

const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();

closeModal({ modalKey: ['some'] });

If you want to close all modals, you can use the destroy method.

useEffect(
  () => () => {
    destroy();
  },
  [],
);

5-1. Default Closing Modal Order with Options

If multiple modals are open, they will close in the order they were opened, starting with the most recently opened modal.

5-2. Handling Modals with Options

If the option value of the most recently opened modal is true or if there is a specified modalKey to resist, it will find and close the most recent modal with the specified option value set to false that was opened before it. For example, if the resistBackgroundClick option value of the most recently opened modal is true or if the modal with the specified modalKey is still open, it will find and close the earlier opened modal with the resistBackgroundClick value set to false when the background is clicked.

5-3. Resist other modals

You can prevent the modal from closing if the specified modal is open. Just specify the modalKey of the modal you want to resist in the options object when opening the modal.

import { useModalList, type ModalComponent } from '@vegatality/react-hook-modal';

const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
  return (
    <div ref={modalRef}>
      <h1>Some Modal</h1>
      <p>{name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
};

const SomeModal2: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
  return (
    <div ref={modalRef}>
      <h1>Some Modal2</h1>
      <p>{name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
};

const App = () => {
  const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();

  return (
    <div>
      <ModalComponentList />
      <button onClick={() => openModal({ modalKey: ['some', 1], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: true } })}>
        Open Modal 1
      </button>
      <button onClick={() => openModal({ modalKey: ['some2'], ModalComponent: SomeModal2, modalProps: { name: 'some2' }, options: { resistBackgroundClick: [['some', 1]] } })}> /* 👈 this will resist background click until ['some', 1] modal is closed */
        Open Modal 2
      </button>
    </div>
  );
};

5-4. Default Modal Options

You can set the default options for all modals. These default options are applied to all modals that are opened without specifying their own options. However, the options specified when opening each modal will override the default options.

import { useModalList, type ModalComponent } from '@vegatality/react-hook-modal';

const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
  return (
    <div ref={modalRef}>
      <h1>Some Modal</h1>
      <p>{name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
};

const App = () => {
  const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList({
    mode: {
      resistBackgroundClick: true, // set default option
    }
  });

  return (
    <div>
      <ModalComponentList />
      <button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: false } })}> /* 👈 this will override default option */
        Open Modal 1
      </button>
    </div>
  );
};

5-5. Close Modal with Hierarchical Modal Key

You can close the modal with a hierarchical modal key. If you want to close a modal with an exactly matched modal key, you must set the exact option to true.

const killTestModals = () => {
  closeModal({ modalKey: ['test'] }); // This will close modals with key like ['test'], ['test', 1], ['test', 2], ['test', 2, { once: true } ], and etc.
};

const killTestModalExactly = () => {
  closeModal({ modalKey: ['test'], exact: true }); // This will only close modal with key ['test']
};

6. Pass modalRef Prop

modalRef is a reference to the modal element. You have to pass the modalRef to the element ref. It determine the valid area of a modal.

import { ModalComponent } from '@vegatality/react-hook-modal';

const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
  return (
    <div className='modal_boundary' ref={modalRef}>
      <h1>Some Modal</h1>
      <p>{name}</p>
      <button onClick={closeModal}>Close</button>
    </div>
  );
};

If you don't pass the modalRef, the modal will not close even if you set resistBackgroundClick to false (default). This is because, without modalRef, we can't determine the valid boundary of the modal. However, the modal will close properly when the ESC key is pressed, regardless of whether the modalRef is passed.

7. useToggleModal Hook

You can use the useToggleModal hook to toggle the modal instead of using useModalList hook. It is a simple hook that returns the isModalOpen, modalRef, and toggleModal function that only controls a single modal.

import { useToggleModal } from '@vegatality/react-hook-modal';

function App() {
  const { isModalOpen, modalRef, toggleModal } = useToggleModal({
    initialValue: false,
    openModalOptions: { resistBackgroundClick: true },
  });
  return (
    <div>
      <button onClick={toggleModal}>Toggle Modal</button>
      {isModalOpen && (
        <div ref={modalRef}>
          <h1>Modal</h1>
          <button onClick={toggleModal}>Close Modal</button>
        </div>
      )}
    </div>
  );
}

Types

Providing types for the modal component. You can use them as shown below.

import type { ModalComponent, ModalComponentProps } from '@vegatality/react-hook-modal';

export const TestModal = ({ name, closeModal, modalRef }: ModalComponentProps<{ name: string }>) => {
  return (
    <div ref={modalRef}>
      <h1>Test Modal</h1>
      <p>{name}</p>
      <button type='button' onClick={closeModal}>
        Close Modal
      </button>
    </div>
  );
};

export const TestModal2: ModalComponent<{ name: string }> = ({ name, closeModal, modalRef }) => {
  return (
    <div ref={modalRef}>
      <h1>Test Modal</h1>
      <p>{name}</p>
      <button type='button' onClick={closeModal}>
        Close Modal
      </button>
    </div>
  );
};

However, you don't have to use the provided modal component types(e.g. ModalComponent, ModalComponentProps). You can use your own defined types.

import { ModalRef } from '@vegatality/react-hook-modal';

interface SomeModalProps {
  name: string;
  modalRef: ModalRef;
}

const SomeModal = ({ name, modalRef }: SomeModalProps) => {
  return (
    <div ref={modalRef}>
      <h1>Test Modal</h1>
      <p>{name}</p>
    </div>
  );
};

const App = () => {
  const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();

  return (
    <div>
      <ModalComponentList />
      <button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: true } })}>
        Open Modal 1
      </button>
      <button onClick={() => openModal({ modalKey: ['some2'], ModalComponent: SomeModal2, modalProps: { name: 'some2' }, options: { resistBackgroundClick: false, scrollable: false } })}>
        Open Modal 2
      </button>
      <button onClick={() => closeModal({ modalKey: ['some2'] })}>Close Modal 2</button>
    </div>
  );
};