Movies & TV: arrows and drag to slide rows (no scrollbar needed), Trailer button (Plex extras, else a YouTube search)
build-container / build (push) Successful in 11m18s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-23 10:30:24 -04:00
1 parent d598501393
commit 7198efaccd
5 files changed
+46 -2

No files matched your search

+23
View File
@@ -0,0 +1,23 @@
'use strict';
// Sideways rows (Continue watching, Collections, Recently added...): arrow buttons at the ends, and click-and-drag with a mouse, so nobody has to
// reach for the scrollbar. Touch and trackpads scroll them natively. Any .vrow that shows up on the page gets these.
(function () {
const enhance = row => {
if (row.dataset.sl) return; row.dataset.sl = '1';
const wrap = document.createElement('div'); wrap.className = 'vslide'; row.parentNode.insertBefore(wrap, row); wrap.appendChild(row);
const mk = (cls, label, d) => { const b = document.createElement('button'); b.type = 'button'; b.className = 'vsl ' + cls; b.setAttribute('aria-label', label); b.innerHTML = d < 0 ? '‹' : '›'; b.onclick = () => row.scrollBy({ left: d * Math.max(200, row.clientWidth * .85), behavior: 'smooth' }); wrap.appendChild(b); return b; };
const l = mk('l', 'Scroll left', -1), r = mk('r', 'Scroll right', 1);
const sync = () => { l.hidden = row.scrollLeft < 8; r.hidden = row.scrollLeft + row.clientWidth >= row.scrollWidth - 8; };
row.addEventListener('scroll', sync, { passive: true }); addEventListener('resize', sync); new ResizeObserver(sync).observe(row); sync(); setTimeout(sync, 400); setTimeout(sync, 1500); // (posters arrive after the row is drawn)
let down = null, moved = false;
row.addEventListener('pointerdown', e => { if (e.pointerType !== 'mouse' || e.button !== 0) return; down = { x: e.clientX, left: row.scrollLeft }; moved = false; });
addEventListener('pointermove', e => { if (!down || !row.isConnected) return; const dx = e.clientX - down.x; if (Math.abs(dx) > 5) { moved = true; row.classList.add('drag'); } if (moved) row.scrollLeft = down.left - dx; });
addEventListener('pointerup', () => { if (down) { down = null; setTimeout(() => row.classList.remove('drag'), 0); } });
row.addEventListener('click', e => { if (moved) { e.preventDefault(); e.stopPropagation(); moved = false; } }, true); // (a drag is not a tap on a poster)
row.addEventListener('dragstart', e => e.preventDefault());
};
let queued = 0;
const scan = () => { queued = 0; document.querySelectorAll('.vrow:not([data-sl])').forEach(enhance); };
new MutationObserver(() => { if (!queued) queued = requestAnimationFrame(scan); }).observe(document.getElementById('view') || document.body, { childList: true, subtree: true });
scan();
})();