import * as React from 'react' import * as Dialog from '@radix-ui/react-dialog' import { X } from 'lucide-react' import type { SAILMarginSize } from '../../types/sail' import { mergeClasses } from '../../utils/classNames' import { marginAboveMap, marginBelowMap } from '../../utils/sailMaps' /** * Width options for dialog sizing */ export type DialogWidth = "NARROW" | "MEDIUM" | "MEDIUM_PLUS" | "WIDE" | "FIT" /** * Height options for dialog sizing */ export type DialogHeight = "AUTO" | "FIT" | "SHORT" | "MEDIUM" | "TALL" | "EXTRA_TALL" /** * Displays a modal dialog overlay with customizable content * Inspired by SAIL form field patterns (not an official SAIL component) * * This is a "new SAIL" component - not available in public SAIL but follows * the same conventions and patterns for consistency with other Sailwind components. */ export interface DialogFieldProps { /** Whether the dialog is open */ open?: boolean /** Callback when dialog open state changes */ onOpenChange?: (open: boolean) => void /** Element that triggers the dialog (usually a button) */ trigger?: React.ReactNode /** Dialog title text */ title?: string /** Dialog description text */ description?: string /** Main content of the dialog */ children: React.ReactNode /** Width of the dialog */ width?: DialogWidth /** Height of the dialog */ height?: DialogHeight /** Whether to show the close button */ showCloseButton?: boolean /** Whether clicking outside closes the dialog */ closeOnOutsideClick?: boolean /** Whether pressing escape closes the dialog */ closeOnEscape?: boolean /** Determines whether component is displayed */ showWhen?: boolean /** Space added above component */ marginAbove?: SAILMarginSize /** Space added below component */ marginBelow?: SAILMarginSize /** Callback when dialog is closed */ onClose?: () => void /** Additional Tailwind classes for prototype-specific styling (not part of SAIL API) */ className?: string } export const DialogField: React.FC = ({ open, onOpenChange, trigger, title, description, children, width = "MEDIUM", height = "AUTO", showCloseButton = true, closeOnOutsideClick = true, closeOnEscape = true, showWhen = true, marginAbove = "NONE", marginBelow = "STANDARD", onClose, className }) => { // Visibility control if (!showWhen) return null // Width mappings const widthMap: Record = { NARROW: 'w-[90vw] max-w-sm', // ~384px max MEDIUM: 'w-[90vw] max-w-md', // ~448px max MEDIUM_PLUS: 'w-[90vw] max-w-lg', // ~512px max WIDE: 'w-[90vw] max-w-2xl', // ~672px max FIT: 'w-[95vw]' // Full screen width (with small margin) } // Height mappings const heightMap: Record = { AUTO: 'h-auto', // Content-based height FIT: 'h-auto max-h-[85vh]', // Content-based with max SHORT: 'h-[300px]', // Fixed short height MEDIUM: 'h-[500px]', // Fixed medium height TALL: 'h-[700px]', // Fixed tall height EXTRA_TALL: 'h-[85vh]' // Very tall, viewport-based } // Container classes const sailClasses = [ marginAboveMap[marginAbove], marginBelowMap[marginBelow] ].filter(Boolean).join(' ') const containerClasses = mergeClasses(sailClasses, className) // Handle close events const handleOpenChange = (newOpen: boolean) => { if (onOpenChange) { onOpenChange(newOpen) } if (!newOpen && onClose) { onClose() } } const dialogContent = ( e.preventDefault()} onEscapeKeyDown={closeOnEscape ? undefined : (e) => e.preventDefault()} > {/* Header */} {(title || description || showCloseButton) && (
{title && ( {title} )} {description && ( {description} )}
{showCloseButton && ( )}
)} {/* Content */}
{children}
) // If no trigger provided, return controlled dialog if (!trigger) { return (
{dialogContent}
) } // Return dialog with trigger return (
{trigger} {dialogContent}
) }