From 07fb5ab0178092ceda4cb93959089317e9a4c130 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Sun, 30 Nov 2025 01:44:20 +0200 Subject: [PATCH] client/dialogs: add support for custom title bar buttons --- .../src/stylesheets/theme-next/dialogs.css | 8 ++++++-- apps/client/src/widgets/react/Modal.tsx | 20 ++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/client/src/stylesheets/theme-next/dialogs.css b/apps/client/src/stylesheets/theme-next/dialogs.css index ed617991c..768dc5530 100644 --- a/apps/client/src/stylesheets/theme-next/dialogs.css +++ b/apps/client/src/stylesheets/theme-next/dialogs.css @@ -25,6 +25,7 @@ .modal .modal-header .btn-close, .modal .modal-header .help-button, +.modal .modal-header .custom-title-bar-button, #toast-container .toast .toast-header .btn-close { display: flex; justify-content: center; @@ -55,15 +56,17 @@ font-family: boxicons; } -.modal .modal-header .help-button { +.modal .modal-header .help-button, +.modal .modal-header .custom-title-bar-button { margin-inline-end: 0; - font-size: calc(var(--modal-control-button-size) * .75); + font-size: calc(var(--modal-control-button-size) * .65); font-family: unset; font-weight: bold; } .modal .modal-header .btn-close:hover, .modal .modal-header .help-button:hover, +.modal .modal-header .custom-title-bar-button:hover, #toast-container .toast .toast-header .btn-close:hover { background: var(--modal-control-button-hover-background); color: var(--modal-control-button-hover-color); @@ -71,6 +74,7 @@ .modal .modal-header .btn-close:active, .modal .modal-header .help-button:active, +.modal .modal-header .custom-title-bar-button:active, #toast-container .toast .toast-header .btn-close:active { transform: scale(.85); } diff --git a/apps/client/src/widgets/react/Modal.tsx b/apps/client/src/widgets/react/Modal.tsx index 9c1e4a230..d28244ac0 100644 --- a/apps/client/src/widgets/react/Modal.tsx +++ b/apps/client/src/widgets/react/Modal.tsx @@ -1,3 +1,4 @@ +import clsx from "clsx"; import { useEffect, useRef, useMemo } from "preact/hooks"; import { t } from "../../services/i18n"; import { ComponentChildren } from "preact"; @@ -7,9 +8,16 @@ import { Modal as BootstrapModal } from "bootstrap"; import { memo } from "preact/compat"; import { useSyncedRef } from "./hooks"; +interface CustomTitleBarButton { + title: string; + iconClassName: string; + onClick: () => void; +} + interface ModalProps { className: string; title: string | ComponentChildren; + customTitleBarButtons?: (CustomTitleBarButton | null)[]; size: "xl" | "lg" | "md" | "sm"; children: ComponentChildren; /** @@ -72,7 +80,7 @@ interface ModalProps { noFocus?: boolean; } -export default function Modal({ children, className, size, title, header, footer, footerStyle, footerAlignment, onShown, onSubmit, helpPageId, minWidth, maxWidth, zIndex, scrollable, onHidden: onHidden, modalRef: externalModalRef, formRef, bodyStyle, show, stackable, keepInDom, noFocus }: ModalProps) { +export default function Modal({ children, className, size, title, customTitleBarButtons: titleBarButtons, header, footer, footerStyle, footerAlignment, onShown, onSubmit, helpPageId, minWidth, maxWidth, zIndex, scrollable, onHidden: onHidden, modalRef: externalModalRef, formRef, bodyStyle, show, stackable, keepInDom, noFocus }: ModalProps) { const modalRef = useSyncedRef(externalModalRef); const modalInstanceRef = useRef(); const elementToFocus = useRef(); @@ -148,7 +156,17 @@ export default function Modal({ children, className, size, title, header, footer {helpPageId && ( )} + + {titleBarButtons?.filter((b) => b !== null).map((titleBarButton) => ( + + ))} + + {onSubmit ? (