Easier to dismiss popups: close button and swipe down
build-container / build (push) Successful in 3m3s
build-container / build (push) Successful in 3m3s
Sheets (including Now Playing) get a close button, an animated close and swipe-down on phones; lyrics and the visualizer close on swipe-down too. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
714680a94e
commit
36395eb7f0
5 files changed
+34
-7
No files matched your search
+27
-2
@@ -5,13 +5,38 @@ function openSheet(title, sub, bodyHtml) {
|
||||
const el = document.createElement('div');
|
||||
el.id = 'sheet';
|
||||
el.innerHTML = `<div class="scrim"></div><div class="sheet" role="dialog" aria-modal="true" aria-label="${esc(title)}">
|
||||
<div class="grip"></div><h3>${esc(title)}</h3>${sub ? `<p class="sub">${esc(sub)}</p>` : ''}${bodyHtml}</div>`;
|
||||
<button type="button" class="ibtn sheetx" aria-label="Close">${icon('x')}</button><div class="grip"></div><h3>${esc(title)}</h3>${sub ? `<p class="sub">${esc(sub)}</p>` : ''}${bodyHtml}</div>`;
|
||||
document.body.appendChild(el);
|
||||
el.querySelector('.scrim').onclick = closeSheet;
|
||||
el.querySelector('.scrim').onclick = dismissSheet; el.querySelector('.sheetx').onclick = dismissSheet;
|
||||
swipeToDismiss(el.querySelector('.sheet'), () => closeSheet(), { handle: '.grip, h3, .sheetx', scrim: el.querySelector('.scrim') });
|
||||
requestAnimationFrame(() => el.classList.add('on'));
|
||||
return el;
|
||||
}
|
||||
function closeSheet() { document.getElementById('sheet')?.remove(); }
|
||||
/** Close with the slide-down (the close button, a tap outside); closeSheet() is the instant one other code calls before opening something else. */
|
||||
function dismissSheet() { const s = document.getElementById('sheet'); if (!s || s._closing) return; s._closing = true; s.classList.remove('on'); setTimeout(() => { if (s.isConnected) s.remove(); }, 230); }
|
||||
/** Swipe down to dismiss: `el` follows the finger and `close` runs when it is let go far enough or fast enough. Touches starting in `handle` always count; elsewhere only when nothing under the finger is scrolled down, and never on sliders or text fields. On a phone-sized screen only, unless `always` (full-screen views). */
|
||||
function swipeToDismiss(el, close, { handle = '', scrim = null, always = false } = {}) {
|
||||
let y0 = 0, x0 = 0, t0 = 0, on = false, dead = true;
|
||||
const reset = () => { el.style.transition = 'transform .2s ease'; el.style.transform = ''; if (scrim) { scrim.style.transition = 'opacity .2s'; scrim.style.opacity = ''; } setTimeout(() => { el.style.transition = ''; if (scrim) scrim.style.transition = ''; }, 220); };
|
||||
el.addEventListener('touchstart', e => {
|
||||
on = false; dead = true; if (e.touches.length !== 1 || (!always && !matchMedia('(max-width: 767px)').matches)) return;
|
||||
const t = e.target; if (t.closest && t.closest('input, textarea, select, [contenteditable], [data-nodrag]')) return;
|
||||
if (!(handle && t.closest && t.closest(handle))) for (let n = t; n; n = n.parentElement) { if (n.scrollTop > 0) return; if (n === el) break; }
|
||||
y0 = e.touches[0].clientY; x0 = e.touches[0].clientX; t0 = Date.now(); dead = false;
|
||||
}, { passive: true });
|
||||
el.addEventListener('touchmove', e => {
|
||||
if (dead) return; const dy = e.touches[0].clientY - y0, dx = e.touches[0].clientX - x0;
|
||||
if (!on) { if (dy > 10 && dy > Math.abs(dx) * 1.5) { on = true; el.style.transition = 'none'; if (scrim) scrim.style.transition = 'none'; } else if (Math.abs(dx) > 10 || dy < -6) { dead = true; return; } else return; }
|
||||
if (e.cancelable) e.preventDefault();
|
||||
const d = Math.max(0, dy); el.style.transform = 'translateY(' + d + 'px)'; if (scrim) scrim.style.opacity = String(Math.max(0, 1 - d / 420));
|
||||
}, { passive: false });
|
||||
const end = e => {
|
||||
if (!on) return; on = false; dead = true; const dy = (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientY : y0) - y0, fast = dy / Math.max(1, Date.now() - t0) > 0.6;
|
||||
if (e.type !== 'touchcancel' && (dy > 110 || (fast && dy > 30))) { el.style.transition = 'transform .18s ease'; el.style.transform = 'translateY(105%)'; if (scrim) { scrim.style.transition = 'opacity .18s'; scrim.style.opacity = '0'; } setTimeout(close, 170); } else reset();
|
||||
};
|
||||
el.addEventListener('touchend', end); el.addEventListener('touchcancel', end);
|
||||
}
|
||||
addEventListener('keydown', e => { if (e.key === 'Escape') closeSheet(); });
|
||||
|
||||
function actionSheet(title, sub, actions) {
|
||||
|
||||
Reference in new issue
Block a user