fix(ui): only close a modal when the backdrop click starts on the backdrop
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, …).
This commit is contained in:
parent
33fad8911b
commit
374ad4ddc4
1 changed files with 11 additions and 2 deletions
|
|
@ -23,6 +23,11 @@ export default function Modal({
|
||||||
// Browser/mouse Back closes the topmost modal instead of navigating away.
|
// Browser/mouse Back closes the topmost modal instead of navigating away.
|
||||||
useBackToClose(onClose);
|
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.
|
// Keep a stable handler across renders so the stack id is assigned once per mount.
|
||||||
const onCloseRef = useRef(onClose);
|
const onCloseRef = useRef(onClose);
|
||||||
onCloseRef.current = onClose;
|
onCloseRef.current = onClose;
|
||||||
|
|
@ -48,13 +53,17 @@ export default function Modal({
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6 bg-black/70 backdrop-blur-sm"
|
className="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6 bg-black/70 backdrop-blur-sm"
|
||||||
onClick={onClose}
|
onMouseDown={(e) => {
|
||||||
|
pressedOnBackdrop.current = e.target === e.currentTarget;
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (pressedOnBackdrop.current && e.target === e.currentTarget) onClose();
|
||||||
|
}}
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`glass relative w-full ${maxWidth} max-h-full overflow-y-auto rounded-2xl shadow-2xl`}
|
className={`glass relative w-full ${maxWidth} max-h-full overflow-y-auto rounded-2xl shadow-2xl`}
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between gap-3 p-5 pb-3">
|
<div className="flex items-start justify-between gap-3 p-5 pb-3">
|
||||||
<h2 className="text-lg font-semibold leading-snug">{title}</h2>
|
<h2 className="text-lg font-semibold leading-snug">{title}</h2>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue