import { Button } from "@repo/shadcn-ui/components/button"; import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@repo/shadcn-ui/components/dialog"; import type { ComponentProps, ReactNode } from "react"; import { createContext, useContext } from "react"; import { useDialogHelpLayout } from "../hooks/use-dialog-help-layout"; import { DialogHelpContext, DialogHelpLabelsContext } from "../lib/dialog-help-context"; import { XIcon } from "../lib/icons"; type FormDialogLayout = "content" | "stable" | "picker"; const FormLayoutContext = createContext("content"); const FormCancelContext = createContext(undefined); export function FormDialog({ open, onOpenChange, title, cancelLabel, children, wide = false, size, formActions = false, layout = "content", finalFocus, initialFocus, }: { open: boolean; onOpenChange: (open: boolean) => void; title: ReactNode; cancelLabel: string; children: ReactNode; wide?: boolean; size?: "small" | "medium" | "large"; formActions?: boolean; layout?: FormDialogLayout; finalFocus?: ComponentProps["finalFocus"]; initialFocus?: "title" | ComponentProps["initialFocus"]; }) { const help = useDialogHelpLayout(open, onOpenChange); const { popupRef, triggerRef, setPopupRef, actionsHost, setActionsHost, contentHost, setContentHost, setPresentation, helpOpen, helpMode, helpView, } = help; const labels = useContext(DialogHelpLabelsContext); const width = size ?? (wide ? "large" : "small"); const widths = { small: "sm:max-w-xl", medium: "sm:max-w-3xl", large: "sm:w-[90vw] sm:max-w-[1200px]", }; // La altura de los diálogos dinámicos depende de la ventana, nunca de sus resultados o pasos. const height = layout === "content" ? "" : "h-[calc(100dvh-2rem)] sm:h-[min(85dvh,900px)]"; return ( popupRef.current?.querySelector('[data-slot="dialog-title"]') ?? true : (initialFocus ?? (layout === "picker" ? () => popupRef.current?.querySelector("input[data-quick-search]") ?? true : undefined)) } ref={setPopupRef} showCloseButton={false} >
{title}
} >
{children}
{!(formActions || helpView) && ( }> {cancelLabel} )}
); } export function FormActions({ children, leadingActions, cancelDisabled = false, }: { children: ReactNode; leadingActions?: ReactNode; cancelDisabled?: boolean; }) { const cancelLabel = useContext(FormCancelContext); const layout = useContext(FormLayoutContext); return ( button]:flex-1 sm:[&>button]:flex-none" : "py-4"}`} > {leadingActions && (
{leadingActions}
)} {cancelLabel && ( }> {cancelLabel} )} {children}
); } /** El cuerpo reserva espacio simétrico para que el scroll no recorte el anillo de foco. */ export function FormBody({ children, contentLayout = "stack", }: { children: ReactNode; contentLayout?: "stack" | "fill"; }) { const layout = useContext(FormLayoutContext); const fills = layout === "picker" || contentLayout === "fill"; return (
p]:shrink-0" : `overflow-y-auto pt-1 pb-5 ${fills ? "flex flex-col gap-5 [&>*]:shrink-0" : "space-y-5"}`} ${layout === "content" ? "" : "[scrollbar-gutter:stable]"}`} data-slot="form-body" > {children}
); }