diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/css/layout.css | 39 | ||||
| -rw-r--r-- | web/css/player.css | 20 | ||||
| -rw-r--r-- | web/index.html | 7 | ||||
| -rw-r--r-- | web/js/app.js | 3 | ||||
| -rw-r--r-- | web/js/player.js | 23 |
5 files changed, 84 insertions, 8 deletions
diff --git a/web/css/layout.css b/web/css/layout.css index 8860f77..7dd70a4 100644 --- a/web/css/layout.css +++ b/web/css/layout.css @@ -257,6 +257,19 @@ body { /* Responsive */ @media (max-width: 860px) { + :root { + --header-height: 3rem; + --sidebar-width: 0; + } + .site-header { + gap: 0.35rem; + padding: 0 0.5rem; + } + .search-bar { + max-width: none; + min-width: 0; + flex: 1 1 auto; + } .sidebar { position: fixed; inset: var(--header-height) auto auto 0; @@ -269,11 +282,35 @@ body { } .sidebar.open { display: flex; } .page { padding-left: 0; } - .media-grid { grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); } + .player { inset: auto 0 0 0 !important; } + .toolbar { + flex-wrap: wrap; + gap: 0.35rem; + } + .toolbar .input-sm { + min-width: 4.5rem; + max-width: none; + flex: 1 1 4.5rem; + } + .site-header .logo { + font-size: 0.9rem; + } + #theme-toggle, + #user-name { + display: none; + } + #logout-btn { + padding: 0.3rem 0.5rem; + font-size: 0.75rem; + } + .media-grid { grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); } } @media (max-width: 520px) { .media-grid { grid-template-columns: repeat(2, 1fr); } + .toolbar .input-sm { + min-width: 3.5rem; + } } /* Utilities */ diff --git a/web/css/player.css b/web/css/player.css index 3221042..706667d 100644 --- a/web/css/player.css +++ b/web/css/player.css @@ -97,7 +97,25 @@ transform: translate(-50%, -50%) scale(1); } -/* Volume */ +/* Touch-friendly progress bar */ +.progress-track { + touch-action: none; +} + +@media (max-width: 520px) { + .player .controls { + gap: 0.35rem; + padding: 0.4rem 0.5rem; + } + .volume-slider { + width: 3.5rem; + } + .big-play .play-icon { + width: 2.8rem; + height: 2.8rem; + font-size: 1rem; + } +} .volume-control { display: flex; align-items: center; diff --git a/web/index.html b/web/index.html index 4b63f13..1fc583e 100644 --- a/web/index.html +++ b/web/index.html @@ -43,13 +43,16 @@ <option value="audio">Audio</option> </select> <button id="filter-favorites" class="btn btn-ghost btn-sm">❤ Favorites</button> + <div class="spacer"></div> + <button id="filter-toggle" class="btn btn-ghost btn-sm" title="More filters">Filters ▾</button> + <span id="result-count" class="text-muted text-xs"></span> + </div> + <div id="filter-advanced" class="toolbar hidden"> <input id="filter-tags" type="text" placeholder="Tags (comma)" aria-label="Filter by tags" class="input-sm"> <input id="filter-min-duration" type="number" min="0" placeholder="Min s" aria-label="Min duration seconds" class="input-sm"> <input id="filter-max-duration" type="number" min="0" placeholder="Max s" aria-label="Max duration seconds" class="input-sm"> <input id="filter-min-filesize" type="number" min="0" placeholder="Min MB" aria-label="Min file size MB" class="input-sm"> <input id="filter-max-filesize" type="number" min="0" placeholder="Max MB" aria-label="Max file size MB" class="input-sm"> - <div class="spacer"></div> - <span id="result-count" class="text-muted text-xs"></span> </div> <div id="media-grid" class="media-grid" tabindex="0" aria-label="Media grid"></div> </div> diff --git a/web/js/app.js b/web/js/app.js index ea1021c..e72b58e 100644 --- a/web/js/app.js +++ b/web/js/app.js @@ -117,6 +117,9 @@ async function initApp() { document.getElementById('filter-max-duration')?.addEventListener('change', (e) => { state.filters.maxDuration = e.target.value; loadMedia(); }); document.getElementById('filter-min-filesize')?.addEventListener('change', (e) => { state.filters.minFilesizeMB = e.target.value; loadMedia(); }); document.getElementById('filter-max-filesize')?.addEventListener('change', (e) => { state.filters.maxFilesizeMB = e.target.value; loadMedia(); }); + document.getElementById('filter-toggle')?.addEventListener('click', () => { + document.getElementById('filter-advanced')?.classList.toggle('hidden'); + }); // Menu toggle document.getElementById('menu-btn')?.addEventListener('click', () => { diff --git a/web/js/player.js b/web/js/player.js index ed3e8bf..b61be08 100644 --- a/web/js/player.js +++ b/web/js/player.js @@ -68,13 +68,28 @@ export function initPlayer() { }); let seeking = false; - e.track?.addEventListener('click', (ev) => { + const seekToFraction = (frac) => { const m = currentMediaElement(); if (!m || !m.duration) return; - const r = e.track.getBoundingClientRect(); - const frac = (ev.clientX - r.left) / r.width; m.currentTime = Math.max(0, Math.min(1, frac)) * m.duration; - }); + }; + const handlePointer = (ev) => { + const r = e.track.getBoundingClientRect(); + const clientX = ev.touches ? ev.touches[0].clientX : ev.clientX; + return (clientX - r.left) / r.width; + }; + e.track?.addEventListener('click', (ev) => seekToFraction(handlePointer(ev))); + e.track?.addEventListener('touchstart', (ev) => { + seeking = true; + ev.preventDefault(); + seekToFraction(handlePointer(ev)); + }, { passive: false }); + e.track?.addEventListener('touchmove', (ev) => { + if (!seeking) return; + ev.preventDefault(); + seekToFraction(handlePointer(ev)); + }, { passive: false }); + e.track?.addEventListener('touchend', () => { seeking = false; }); e.track?.addEventListener('keydown', (ev) => { const m = currentMediaElement(); if (!m || !m.duration) return; |
