import { Box, Dialog, DialogActions, DialogContent, DialogContentProps, DialogProps, DialogTitle, DialogTitleProps, IconButton, Stack, SxProps, Theme, } from "@linagora/twake-mui"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import CloseIcon from "@mui/icons-material/Close"; import OpenInFullIcon from "@mui/icons-material/OpenInFull"; 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 570px) * - 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< DialogProps, "maxWidth" | "title" > { /** 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: "570px") */ 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: 2 = 16px) */ 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; /** Whether to show header action icons (expand/close) in normal mode (default: true) */ showHeaderActions?: boolean; /** Whether to add border-top to DialogActions */ actionsBorderTop?: boolean; /** Justify content alignment for DialogActions */ actionsJustifyContent?: | "flex-start" | "center" | "flex-end" | "space-between"; } function ResponsiveDialog({ open, onClose, title, children, actions, isExpanded = false, onExpandToggle, normalMaxWidth = "570px", expandedContentMaxWidth = "990px", headerHeight = "70px", normalSpacing = 2, expandedSpacing = 2, contentSx, titleSx, dialogContentProps, dialogTitleProps, dividers = false, showHeaderActions = true, actionsBorderTop = false, actionsJustifyContent = "flex-end", sx, ...otherDialogProps }: ResponsiveDialogProps) { const baseSx: SxProps = { "& .MuiBackdrop-root": { backgroundColor: "rgba(0, 0, 0, 0.1)", opacity: isExpanded ? "0 !important" : undefined, transition: isExpanded ? "none !important" : undefined, pointerEvents: isExpanded ? "none" : undefined, }, "& .MuiDialog-paper": { maxWidth: isExpanded ? "100%" : normalMaxWidth, width: "100%", height: isExpanded ? `calc(100vh - ${headerHeight})` : undefined, margin: isExpanded ? `${headerHeight} 0 0 0` : "32px", boxShadow: isExpanded ? "none !important" : undefined, transition: isExpanded ? "none !important" : undefined, zIndex: isExpanded ? 1200 : 1300, }, "& .MuiDialogActions-root .MuiBox-root": { maxWidth: isExpanded ? expandedContentMaxWidth : undefined, margin: isExpanded ? "0 auto" : undefined, padding: "0", width: isExpanded ? "100%" : undefined, justifyContent: isExpanded ? "flex-end" : undefined, }, }; const baseContentSx: SxProps = { width: "100%", }; const contentWrapperSx: SxProps = { maxWidth: isExpanded ? expandedContentMaxWidth : "100%", margin: isExpanded ? "0 auto" : "0", width: "100%", }; React.useEffect(() => { if (isExpanded) { document.body.classList.add("fullscreen-view"); } else { document.body.classList.remove("fullscreen-view"); } }, [isExpanded]); const handleClose = ( event: {}, reason: "backdropClick" | "escapeKeyDown" ) => { if (isExpanded && reason === "backdropClick") { return; } onClose(); }; const currentSpacing = isExpanded ? expandedSpacing : normalSpacing; return ( {isExpanded && onExpandToggle ? ( ) : showHeaderActions ? ( {title} {onExpandToggle && ( )} ) : ( title )} {isExpanded ? ( {children} ) : ( {children} )} {actions && ( `1px solid ${theme.palette.divider}` : undefined, justifyContent: actionsJustifyContent, }} > {actions} )} ); } export default ResponsiveDialog;