/** * Dialog — WealthX DS overrides (shadcn / \@base-ui dialog) * * Changes from shadcn default: * - DialogOverlay: `bg-black/50` → `bg-foreground/50` — DS foreground token (matches Figma) * - DialogContent: `rounded-lg` removed — sharp corners per Figma * - DialogContent: `shadow-lg` removed — flat panels per Figma * - DialogContent: `max-h-[calc(100dvh-2rem)]` + `overflow-y-auto` — tall dialogs scroll * inside themselves instead of pushing the footer off-screen * - DialogHeader: `text-center` removed — always left-aligned per DS * - DialogFooter: `border-t border-border pt-4` — separator above footer * - Close button: no radius, `hover:bg-foreground/5`, `focus:ring-border` */ import { type ReactElement } from "react"; import * as React from "react"; import { XIcon } from "lucide-react"; import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"; import { cn } from "@/lib/utils"; import { asChildProps } from "@/lib/as-child"; import { useThemeVars } from "@/lib/theme-provider"; import { buttonVariants } from "@/components/ui/button"; export type DialogProps = React.ComponentProps; function Dialog({ ...props }: DialogProps): ReactElement { return ; } export type DialogTriggerProps = React.ComponentProps< typeof DialogPrimitive.Trigger > & { /** shadcn-compatible alias for Base UI's `render`: replaces the trigger with its single child. */ asChild?: boolean; }; function DialogTrigger({ asChild, children, ...props }: DialogTriggerProps): ReactElement { return ( ); } export type DialogPortalProps = React.ComponentProps< typeof DialogPrimitive.Portal >; function DialogPortal({ ...props }: DialogPortalProps): ReactElement { return ; } export type DialogCloseProps = React.ComponentProps< typeof DialogPrimitive.Close >; function DialogClose({ ...props }: DialogCloseProps): ReactElement { return ; } export type DialogOverlayProps = React.ComponentProps< typeof DialogPrimitive.Backdrop >; function DialogOverlay({ className, ...props }: DialogOverlayProps): ReactElement { return ( ); } /** * Maps the `size` prop to an exact CSS max-width value. * Applied via inline style — bypasses Tailwind v4 class scanning entirely. */ const DIALOG_MAX_WIDTHS: Record = { sm: "24rem", md: "28rem", lg: "32rem", xl: "36rem", "2xl": "42rem", "3xl": "48rem", "4xl": "56rem", full: "100%", }; /** * Default minimum width for size="auto" dialogs. * Prevents the dialog from collapsing too narrow when hugging content. * Override per-instance via the `minWidth` prop. */ const DIALOG_AUTO_MIN_WIDTH = "20rem"; // 320px export type DialogContentProps = React.ComponentProps< typeof DialogPrimitive.Popup > & { showCloseButton?: boolean; /** * Optional container element to render the Dialog portal into. * Pass a ref'd element to scope the overlay inside a parent (e.g. a Sheet drawer) * instead of appending to document.body. */ container?: HTMLElement | null; /** Vertical alignment of the dialog. "top" (default) anchors to top-4; "center" vertically centers. */ align?: "center" | "top"; /** * Width behaviour of the dialog panel. * - Fixed sizes ("sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "full"): * dialog fills width up to that maximum (applied via inline style — reliable in Tailwind v4). * - "auto": dialog shrinks to fit its content (hug-content). Has a built-in minimum * width of `DIALOG_AUTO_MIN_WIDTH` (20rem / 320px) to avoid collapsing too narrow. * Defaults to "lg". */ size?: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "full" | "auto"; /** * Override the minimum width for `size="auto"` dialogs. * Accepts any valid CSS length value (e.g. "24rem", "400px"). * Ignored when using a fixed size. */ minWidth?: string; }; function DialogContent({ className, children, showCloseButton = true, style, container, align = "top", size = "lg", minWidth, ...props }: DialogContentProps): ReactElement { const themeVars = useThemeVars(); const isAuto = size === "auto"; const sizeStyle = isAuto ? { minWidth: minWidth ?? DIALOG_AUTO_MIN_WIDTH } : { maxWidth: `min(${DIALOG_MAX_WIDTHS[size]}, calc(100% - 2rem))` }; return ( {children} {showCloseButton ? ( Close ) : null} ); } export type DialogHeaderProps = React.ComponentProps<"div">; function DialogHeader({ className, ...props }: DialogHeaderProps): ReactElement { return (
); } export type DialogFooterProps = React.ComponentProps<"div"> & { showCloseButton?: boolean; }; function DialogFooter({ className, showCloseButton = false, children, ...props }: DialogFooterProps): ReactElement { return (
{children} {showCloseButton ? ( Close ) : null}
); } export type DialogTitleProps = React.ComponentProps< typeof DialogPrimitive.Title >; function DialogTitle({ className, ...props }: DialogTitleProps): ReactElement { return ( ); } export type DialogDescriptionProps = React.ComponentProps< typeof DialogPrimitive.Description >; function DialogDescription({ className, ...props }: DialogDescriptionProps): ReactElement { return ( ); } export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, };