fix(browse,classifier): backdrop dismiss no longer fires on a drag out of an input

Selecting text in a dialog input by click-dragging and releasing the mouse
outside the dialog closed it: the browser fires a `click` whose target is the
backdrop (mousedown was inside, mouseup outside), and the dismiss handler keyed
solely on `e.target === backdrop`.

Guard every backdrop click-to-close with a mousedown flag — close only when the
press ALSO started on the backdrop (a genuine backdrop click), not a drag that
began inside the dialog. Applied to the browse New file/folder party picker (the
reported case) and the other browse create dialogs (create/accept-transmittal,
stage), plus the classifier dialogs that share the pattern (copy chooser,
dir-picker, and the paste/match modal — whose textarea is a prime drag target).
The conflict/history dialogs already used mousedown and were unaffected.

Build + browse/classify/classifier suites green (80).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-06-15 10:36:07 -05:00
parent 2989e6e847
commit c718334d25
7 changed files with 35 additions and 9 deletions

View file

@ -173,8 +173,12 @@
box.querySelector('#acc-cancel').addEventListener('click', function () {
close(); reject(new Error('cancelled'));
});
// Close on a genuine backdrop click only — not when a drag that began
// inside the dialog (selecting text in an input) ends out here.
var pressedBackdrop = false;
overlay.addEventListener('mousedown', function (e) { pressedBackdrop = (e.target === overlay); });
overlay.addEventListener('click', function (e) {
if (e.target === overlay) { close(); reject(new Error('cancelled')); }
if (e.target === overlay && pressedBackdrop) { close(); reject(new Error('cancelled')); }
});
document.addEventListener('keydown', onKeydown);

View file

@ -82,8 +82,12 @@
box.querySelector('#ct-cancel').addEventListener('click', function () {
close(); reject(new Error('cancelled'));
});
// Close on a genuine backdrop click only — not when a drag that began
// inside the dialog (selecting text in an input) ends out here.
var pressedBackdrop = false;
overlay.addEventListener('mousedown', function (e) { pressedBackdrop = (e.target === overlay); });
overlay.addEventListener('click', function (e) {
if (e.target === overlay) { close(); reject(new Error('cancelled')); }
if (e.target === overlay && pressedBackdrop) { close(); reject(new Error('cancelled')); }
});
document.addEventListener('keydown', onKeydown);
submit.addEventListener('click', function () {

View file

@ -848,7 +848,11 @@
function close() { if (overlay.parentNode) overlay.parentNode.removeChild(overlay); }
function cancel() { close(); resolve(null); }
box.querySelector('#pp-cancel').addEventListener('click', cancel);
overlay.addEventListener('click', function (e) { if (e.target === overlay) cancel(); });
// Close on a genuine backdrop click only — NOT when a drag that began
// inside the dialog (e.g. selecting text in an input) ends out here.
var pressedBackdrop = false;
overlay.addEventListener('mousedown', function (e) { pressedBackdrop = (e.target === overlay); });
overlay.addEventListener('click', function (e) { if (e.target === overlay && pressedBackdrop) cancel(); });
box.querySelector('#pp-submit').addEventListener('click', function () {
var sel = box.querySelector('input[name="pp-party"]:checked');
if (!sel) { statusError('Pick a party.'); return; }

View file

@ -195,8 +195,10 @@
box.querySelector('#stage-cancel').addEventListener('click', function () {
close(); reject(new Error('cancelled'));
});
var pressedBackdrop = false;
overlay.addEventListener('mousedown', function (e) { pressedBackdrop = (e.target === overlay); });
overlay.addEventListener('click', function (e) {
if (e.target === overlay) { close(); reject(new Error('cancelled')); }
if (e.target === overlay && pressedBackdrop) { close(); reject(new Error('cancelled')); }
});
box.querySelector('#stage-submit').addEventListener('click', function () {
var sel = box.querySelector('input[name="stage-target"]:checked');
@ -246,8 +248,10 @@
box.querySelector('#unstage-cancel').addEventListener('click', function () {
close(); reject(new Error('cancelled'));
});
var pressedBackdrop = false;
overlay.addEventListener('mousedown', function (e) { pressedBackdrop = (e.target === overlay); });
overlay.addEventListener('click', function (e) {
if (e.target === overlay) { close(); reject(new Error('cancelled')); }
if (e.target === overlay && pressedBackdrop) { close(); reject(new Error('cancelled')); }
});
box.querySelector('#unstage-submit').addEventListener('click', function () {
var target = input.value.trim();

View file

@ -326,7 +326,9 @@
row.appendChild(go); row.appendChild(cancel);
box.appendChild(h); box.appendChild(p); box.appendChild(sel); box.appendChild(row);
back.appendChild(box);
back.addEventListener('click', function (e) { if (e.target === back) finish(null); });
var pressedBackdrop = false;
back.addEventListener('mousedown', function (e) { pressedBackdrop = (e.target === back); });
back.addEventListener('click', function (e) { if (e.target === back && pressedBackdrop) finish(null); });
document.addEventListener('keydown', onKey);
document.body.appendChild(back);
});
@ -359,7 +361,9 @@
row.appendChild(btn('Cancel', 'btn-secondary', null));
box.appendChild(h); box.appendChild(p); box.appendChild(row);
back.appendChild(box);
back.addEventListener('click', function (e) { if (e.target === back) finish(null); });
var pressedBackdrop = false;
back.addEventListener('mousedown', function (e) { pressedBackdrop = (e.target === back); });
back.addEventListener('click', function (e) { if (e.target === back && pressedBackdrop) finish(null); });
document.addEventListener('keydown', onKey);
document.body.appendChild(back);
});

View file

@ -62,7 +62,9 @@
btns.appendChild(go); btns.appendChild(cancel);
box.appendChild(h); box.appendChild(p); box.appendChild(treeWrap); box.appendChild(btns);
back.appendChild(box);
back.addEventListener('click', function (e) { if (e.target === back) finish([]); });
var pressedBackdrop = false;
back.addEventListener('mousedown', function (e) { pressedBackdrop = (e.target === back); });
back.addEventListener('click', function (e) { if (e.target === back && pressedBackdrop) finish([]); });
document.addEventListener('keydown', onKey);
document.body.appendChild(back);

View file

@ -726,7 +726,11 @@
var body = el('div', 'scratch-modal__body'); box.appendChild(body);
var foot = el('div', 'copy-choice__btns'); box.appendChild(foot);
back.appendChild(box);
back.addEventListener('click', function (e) { if (e.target === back) close(); });
// Close on a genuine backdrop click only — not when a drag that began in
// the paste textarea (selecting text) ends out on the backdrop.
var pressedBackdrop = false;
back.addEventListener('mousedown', function (e) { pressedBackdrop = (e.target === back); });
back.addEventListener('click', function (e) { if (e.target === back && pressedBackdrop) close(); });
document.addEventListener('keydown', onKey);
document.body.appendChild(back);
return { body: body, foot: foot, close: close };