From 7a5348caa3e53a13bf4a62d30a7e97fb0ef9c64e Mon Sep 17 00:00:00 2001 From: QuentinHsu Date: Sat, 6 Jun 2026 18:22:29 +0800 Subject: [PATCH] feat(web): add shared dialog wrapper - introduce a reusable dialog component for consistent header, body, and footer layout. - support per-dialog sizing, trigger rendering, initial focus, and close button controls. - preserve base dialog open and close motion classes while allowing content-specific styling. --- web/default/src/components/dialog.tsx | 127 ++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 web/default/src/components/dialog.tsx diff --git a/web/default/src/components/dialog.tsx b/web/default/src/components/dialog.tsx new file mode 100644 index 00000000..1be98dc7 --- /dev/null +++ b/web/default/src/components/dialog.tsx @@ -0,0 +1,127 @@ +/* +Copyright (C) 2023-2026 QuantumNous + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +For commercial licensing, please contact support@quantumnous.com +*/ +import * as React from 'react' +import { cn } from '@/lib/utils' +import { + Dialog as DialogRoot, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog' + +type DialogProps = React.ComponentProps & { + title: React.ReactNode + description?: React.ReactNode + children: React.ReactNode + trigger?: React.ReactElement + footer?: React.ReactNode + contentHeight?: React.CSSProperties['height'] + contentClassName?: string + headerClassName?: string + titleClassName?: string + descriptionClassName?: string + bodyClassName?: string + footerClassName?: string + initialFocus?: boolean + showCloseButton?: boolean +} + +const dialogContentMotionClassName = + 'data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 duration-100' + +export function Dialog({ + title, + description, + children, + trigger, + footer, + contentHeight = 'min(58vh, 520px)', + contentClassName, + headerClassName, + titleClassName, + descriptionClassName, + bodyClassName, + footerClassName, + initialFocus, + showCloseButton, + ...dialogProps +}: DialogProps) { + return ( + + {trigger ? : null} + + + {title} + {description ? ( + + {description} + + ) : null} + + +
+
+ {children} +
+
+ + {footer ? ( + + {footer} + + ) : null} +
+
+ ) +}