headlessui modal factory in typescript

To use the HeadlessUI Modal component in TypeScript, you can create a reusable factory function that returns a configured instance of the Modal component. Here's how you can do it:

index.ts
import { Fragment, useState } from "react";
import { Dialog, Transition } from "@headlessui/react";

type ModalProps = {
  title: string;
  isOpen: boolean;
  onClose: () => void;
};

type ModalComponent = ({ children }: { children: React.ReactNode }) => JSX.Element;

export const createModal: (args: ModalProps) => ModalComponent = ({
  title,
  isOpen,
  onClose,
}) => {
  const [isModalOpen, setIsModalOpen] = useState(isOpen);

  const handleClose = (): void => {
    setIsModalOpen(false);
    onClose();
  };

  return ({ children }: { children: React.ReactNode }): JSX.Element => (
    <Transition appear show={isModalOpen} as={Fragment}>
      <Dialog
        as="div"
        className="fixed inset-0 z-10 overflow-y-auto"
        onClose={handleClose}
      >
        <Dialog.Overlay className="fixed inset-0 bg-black opacity-30" />

        <div className="min-h-screen px-4 text-center">
          <Transition.Child
            as={Fragment}
            enter="ease-out duration-300"
            enterFrom="opacity-0"
            enterTo="opacity-100"
            leave="ease-in duration-200"
            leaveFrom="opacity-100"
            leaveTo="opacity-0"
          >
            <Dialog.Title as="h2" className="font-bold text-lg my-4">
              {title}
            </Dialog.Title>
          </Transition.Child>

          <Transition.Child
            as={Fragment}
            enter="ease-out duration-300"
            enterFrom="opacity-0"
            enterTo="opacity-100"
            leave="ease-in duration-200"
            leaveFrom="opacity-100"
            leaveTo="opacity-0"
          >
            <div className="p-4">{children}</div>
          </Transition.Child>
        </div>
      </Dialog>
    </Transition>
  );
};
1760 chars
64 lines

This factory function takes in the modal props including the title, isOpen, and onClose handlers, and returns a component that can receive children to be displayed in the modal content.

You can use this factory function like this:

index.ts
import { useState } from "react";
import { createModal } from "./createModal";

const MyComponent = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);

  const handleOpen = (): void => {
    setIsModalOpen(true);
  };

  const handleClose = (): void => {
    setIsModalOpen(false);
  };

  const Modal = createModal({
    title: "My Modal Title",
    isOpen: isModalOpen,
    onClose: handleClose,
  });

  return (
    <>
      <button onClick={handleOpen}>Open Modal</button>
      <Modal>
        <p>Modal Content Here</p>
      </Modal>
    </>
  );
};
571 chars
30 lines

In this example, MyComponent creates a new instance of the Modal component using the createModal factory function. When the Open Modal button is clicked, the onOpen handler is called, which sets the isModalOpen state to true. This causes the Modal component to become visible and display the contents passed in through its children.

With this method, you can create multiple instances of the Modal component with different titles and content throughout your application.

gistlibby LogSnag