ZeroToShip

Docs/Componenti

// componente

Modal

Modal controllato con Headless UI: backdrop scuro, pannello centrato, animazioni di entrata/uscita, chiusura con click fuori, tasto Esc o la X in alto a destra. Client component. La visibilità la gestisci tu dall'esterno con una coppia useState.

File

components/Modal.tsx

Uso base

app/dashboard/page.tsx
tsx
"use client";

import { useState } from "react";
import Modal from "@/components/Modal";

export default function Page() {
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <>
      <button className="btn-brutal" onClick={() => setIsModalOpen(true)}>
        Apri il modal
      </button>

      <Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} />
    </>
  );
}

Props

  • isModalOpen: boolean — obbligatoria. Passa a Transition show: true monta e anima il modal.
  • setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>> — obbligatoria. È il setter dello useState del genitore. Viene chiamato con false sia dalla X sia da onClose del Dialog (click fuori o Esc).

Proprio perché riceve una funzione come prop, il genitore deve essere un client component: non puoi passare un setter da un server component.

Personalizzare

Titolo (“I'm a modal”) e contenuto (“And here is my content”) sono hard-coded. Il primo cambio sensato è renderli props:

components/Modal.tsx
tsx
interface ModalProps {
  isModalOpen: boolean;
  setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
  title?: string;
  children?: React.ReactNode;
}

const Modal = ({ isModalOpen, setIsModalOpen, title, children }: ModalProps) => {
  // ...
  <Dialog.Title as="h2" className="font-semibold">
    {title}
  </Dialog.Title>
  // ...
  <section>{children}</section>
};
  • Larghezza: max-w-3xl sul Dialog.Panel — scendi a max-w-md per una conferma, sali per un form lungo.
  • Stile: il pannello usa ancora rounded-xl bg-base-100 shadow-xl di DaisyUI. Per il look del resto del sito sostituiscilo con card-brutal e background: var(--cream).
  • Chiusura forzata: se il modal contiene un form che non deve essere chiuso per sbaglio, passa onClose={() => {}} al Dialog e lascia solo la X.
  • Backdrop: il Transition.Child con bg-neutral-focus bg-opacity-50. Cambia colore e opacità lì.

Nel repo attuale nessuna pagina lo importa: è pronto da agganciare quando ti serve.