import { Dialog, DialogActions, DialogContent, DialogContentProps, DialogTitle, DialogTitleProps, DialogProps, IconButton, Stack, SxProps, Theme, } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import React, { ReactNode } from "react"; /** * ResponsiveDialog - A reusable dialog component that can switch between normal and expanded modes * * Features: * - Normal mode: Dialog with customizable max-width (default 685px) * - Expanded mode: Full height dialog (excluding app header) with centered content container * - Fully customizable with sx props for Dialog, DialogTitle, and DialogContent * - Preserves app header visibility in expanded mode * * @example * ```tsx * Save} * contentSx={{ padding: 3 }} * > * * * ``` */ interface ResponsiveDialogProps extends Omit { /** Whether the dialog is open */ open: boolean; /** Callback fired when the dialog should be closed */ onClose: () => void; /** Dialog title - can be string or custom ReactNode */ title: string | ReactNode; /** Dialog content - form fields, text, etc. */ children: ReactNode; /** Optional actions rendered in DialogActions (buttons, etc.) */ actions?: ReactNode; /** Toggle between normal and expanded (fullscreen) mode */ isExpanded?: boolean; /** Callback when expand/collapse button is clicked (required if using isExpanded) */ onExpandToggle?: () => void; /** Max width in normal mode (default: "685px") */ normalMaxWidth?: string; /** Max width of content container in expanded mode (default: "990px") */ expandedContentMaxWidth?: string; /** Height of app header to preserve visibility (default: "90px") */ headerHeight?: string; /** Spacing between children in normal mode (default: 2 = 16px) */ normalSpacing?: number; /** Spacing between children in expanded mode (default: 3 = 24px) */ expandedSpacing?: number; /** Custom styles for DialogContent - merged with base styles */ contentSx?: SxProps; /** Custom styles for DialogTitle */ titleSx?: SxProps; /** Additional props for DialogContent (excluding sx) */ dialogContentProps?: Omit; /** Additional props for DialogTitle (excluding sx) */ dialogTitleProps?: Omit; /** Whether to display dividers between title/content/actions */ dividers?: boolean; } function ResponsiveDialog({ open, onClose, title, children, actions, isExpanded = false, onExpandToggle, normalMaxWidth = "685px", expandedContentMaxWidth = "990px", headerHeight = "90px", normalSpacing = 2, expandedSpacing = 3, contentSx, titleSx, dialogContentProps, dialogTitleProps, dividers = false, sx, ...otherDialogProps }: ResponsiveDialogProps) { const baseSx: SxProps = { "& .MuiBackdrop-root": { opacity: isExpanded ? "0 !important" : undefined, transition: isExpanded ? "none !important" : undefined, }, "& .MuiDialog-paper": { maxWidth: isExpanded ? "100%" : normalMaxWidth, width: "100%", // height: isExpanded ? `calc(100vh - ${headerHeight})` : "90vh", margin: isExpanded ? `${headerHeight} 0 0 0` : "32px", maxHeight: isExpanded ? `calc(100vh - ${headerHeight})` : "790px", boxShadow: isExpanded ? "none !important" : undefined, transition: isExpanded ? "none !important" : undefined, }, "& .MuiDialogActions-root .MuiBox-root": { maxWidth: isExpanded ? expandedContentMaxWidth : undefined, margin: isExpanded ? "0 auto" : undefined, padding: isExpanded ? "0 12px" : undefined, width: isExpanded ? "100%" : undefined, justifyContent: isExpanded ? "flex-end" : undefined, }, }; const baseContentSx: SxProps = { width: "100%", padding: isExpanded ? "16px" : undefined, }; const contentWrapperSx: SxProps = { maxWidth: isExpanded ? expandedContentMaxWidth : "100%", margin: isExpanded ? "0 auto" : "0", width: "100%", }; const currentSpacing = isExpanded ? expandedSpacing : normalSpacing; return ( {isExpanded && onExpandToggle ? ( ) : ( title )} {isExpanded ? ( {children} ) : ( {children} )} {actions && {actions}} ); } export default ResponsiveDialog;