From 374ad4ddc402e25eb5dbf99522a8d8845e52cd18 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Tue, 7 Jul 2026 22:28:33 +0200 Subject: [PATCH] fix(ui): only close a modal when the backdrop click starts on the backdrop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pressing the mouse inside a dialog (e.g. to select text in an input), dragging out, and releasing on the backdrop wrongly dismissed it: the click event fires on the common ancestor of mousedown+mouseup (the backdrop), so the outside-click handler ran. Track whether the press started on the backdrop and only close when it both starts and ends there. Fixes all shared-Modal dialogs at once (downloads editor/share, confirm, Plex collections + add-to-playlist, …). --- frontend/src/components/Modal.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Modal.tsx b/frontend/src/components/Modal.tsx index 16699ba..f07d22f 100644 --- a/frontend/src/components/Modal.tsx +++ b/frontend/src/components/Modal.tsx @@ -23,6 +23,11 @@ export default function Modal({ // Browser/mouse Back closes the topmost modal instead of navigating away. useBackToClose(onClose); + // Backdrop-click close must only fire when the press STARTED on the backdrop too. Otherwise a + // drag that begins inside the dialog (e.g. selecting text in an input) and is released out on + // the backdrop wrongly dismisses — the click event bubbles to the common ancestor (the backdrop). + const pressedOnBackdrop = useRef(false); + // Keep a stable handler across renders so the stack id is assigned once per mount. const onCloseRef = useRef(onClose); onCloseRef.current = onClose; @@ -48,13 +53,17 @@ export default function Modal({ return createPortal(
{ + pressedOnBackdrop.current = e.target === e.currentTarget; + }} + onClick={(e) => { + if (pressedOnBackdrop.current && e.target === e.currentTarget) onClose(); + }} role="dialog" aria-modal="true" >
e.stopPropagation()} >

{title}