Pithos: Cancel closes dialogs again, and Escape works after a menu
Cancel did nothing in the confirm dialogs (delete, switch account) and in Add another account: the dialog helper read a Cancel result of false as "keep the dialog open", a meaning meant only for actions like Copy link. A fixed result now always closes; only an action that returns false keeps the dialog. Picking an item from a ⋯ menu also left that menu's Escape listener behind, so Escape no longer closed any dialog opened from it.
This commit is contained in:
parent
068de477df
commit
f59dced33a
1 changed files with 13 additions and 7 deletions
20
ui/app.js
20
ui/app.js
|
|
@ -91,12 +91,15 @@ function modal(title, body, buttons = []) {
|
||||||
const onKey = (e) => { if (e.key === 'Escape') close(undefined); };
|
const onKey = (e) => { if (e.key === 'Escape') close(undefined); };
|
||||||
const foot = h('div', { class: 'foot' }, buttons.map((b) => h('button', {
|
const foot = h('div', { class: 'foot' }, buttons.map((b) => h('button', {
|
||||||
class: `btn ${b.kind || ''}`, type: b.submit ? 'submit' : 'button',
|
class: `btn ${b.kind || ''}`, type: b.submit ? 'submit' : 'button',
|
||||||
onclick: b.submit ? null : async () => { const v = b.value ? await b.value() : b.result; if (v !== false) close(v); },
|
// A fixed result (Cancel: false/null) always closes; only a value()
|
||||||
|
// action that returns false keeps the dialog open (e.g. Copy link).
|
||||||
|
onclick: b.submit ? null : async () => { if (!b.value) return close(b.result); const v = await b.value(); if (v !== false) close(v); },
|
||||||
}, b.label)));
|
}, b.label)));
|
||||||
const form = h('form', { class: 'modal', onsubmit: async (e) => {
|
const form = h('form', { class: 'modal', onsubmit: async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const b = buttons.find((x) => x.submit);
|
const b = buttons.find((x) => x.submit);
|
||||||
const v = b.value ? await b.value() : b.result;
|
if (!b.value) return close(b.result);
|
||||||
|
const v = await b.value();
|
||||||
if (v !== false) close(v);
|
if (v !== false) close(v);
|
||||||
} }, h('h2', {}, title), body, foot);
|
} }, h('h2', {}, title), body, foot);
|
||||||
const root = h('div', { class: 'modal-backdrop', onmousedown: (e) => { if (e.target === root) close(undefined); } }, form);
|
const root = h('div', { class: 'modal-backdrop', onmousedown: (e) => { if (e.target === root) close(undefined); } }, form);
|
||||||
|
|
@ -1162,16 +1165,19 @@ function itemMenu(items) {
|
||||||
btn.addEventListener('click', (e) => {
|
btn.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
document.querySelectorAll('.menu').forEach((m) => m.remove());
|
document.querySelectorAll('.menu').forEach((m) => m.remove());
|
||||||
const menu = h('div', { class: 'menu', role: 'menu' }, items.filter(Boolean).map(([label, run, kind]) =>
|
// Picking an item must also drop the listeners, or a stale Escape handler
|
||||||
h('button', { type: 'button', role: 'menuitem', class: kind || '', onclick: (ev) => { ev.stopPropagation(); menu.remove(); run(); } }, label)));
|
// swallows Escape for every dialog opened afterwards.
|
||||||
|
let menu;
|
||||||
|
function done() { menu?.remove(); document.removeEventListener('mousedown', away, true); document.removeEventListener('keydown', esc, true); }
|
||||||
|
menu = h('div', { class: 'menu', role: 'menu' }, items.filter(Boolean).map(([label, run, kind]) =>
|
||||||
|
h('button', { type: 'button', role: 'menuitem', class: kind || '', onclick: (ev) => { ev.stopPropagation(); done(); run(); } }, label)));
|
||||||
document.body.append(menu);
|
document.body.append(menu);
|
||||||
const r = btn.getBoundingClientRect();
|
const r = btn.getBoundingClientRect();
|
||||||
const w = menu.offsetWidth, ht = menu.offsetHeight;
|
const w = menu.offsetWidth, ht = menu.offsetHeight;
|
||||||
menu.style.left = Math.max(8, Math.min(r.right - w, innerWidth - w - 8)) + 'px';
|
menu.style.left = Math.max(8, Math.min(r.right - w, innerWidth - w - 8)) + 'px';
|
||||||
menu.style.top = (r.bottom + 4 + ht > innerHeight - 8 ? Math.max(8, r.top - ht - 4) : r.bottom + 4) + 'px';
|
menu.style.top = (r.bottom + 4 + ht > innerHeight - 8 ? Math.max(8, r.top - ht - 4) : r.bottom + 4) + 'px';
|
||||||
const done = () => { menu.remove(); document.removeEventListener('mousedown', away, true); document.removeEventListener('keydown', esc, true); };
|
function away(ev) { if (!menu.contains(ev.target)) done(); }
|
||||||
const away = (ev) => { if (!menu.contains(ev.target)) done(); };
|
function esc(ev) { if (ev.key === 'Escape') { ev.stopPropagation(); done(); btn.focus(); } }
|
||||||
const esc = (ev) => { if (ev.key === 'Escape') { ev.stopPropagation(); done(); btn.focus(); } };
|
|
||||||
setTimeout(() => { document.addEventListener('mousedown', away, true); document.addEventListener('keydown', esc, true); }, 0);
|
setTimeout(() => { document.addEventListener('mousedown', away, true); document.addEventListener('keydown', esc, true); }, 0);
|
||||||
menu.querySelector('button')?.focus();
|
menu.querySelector('button')?.focus();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue