feat: side bar playlist and other items not present on some screensizes. make completely responsive
Task #40 completed by ClaudeQueue ClaudeQueue
This commit is contained in:
@@ -1741,6 +1741,8 @@ function render() {
|
|||||||
listFilter = '';
|
listFilter = '';
|
||||||
const fi = $('listFilterInput');
|
const fi = $('listFilterInput');
|
||||||
if (fi) fi.value = '';
|
if (fi) fi.value = '';
|
||||||
|
// On mobile, navigating (nav item / playlist / search) dismisses the drawer.
|
||||||
|
if (typeof closeSidebar === 'function') closeSidebar();
|
||||||
exitSelectMode();
|
exitSelectMode();
|
||||||
renderSidebar();
|
renderSidebar();
|
||||||
renderSmartSidebar();
|
renderSmartSidebar();
|
||||||
@@ -1977,7 +1979,38 @@ function closeModal() { $('modal').classList.add('hidden'); }
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Events
|
// Events
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
// ============================================================================
|
||||||
|
// Responsive sidebar drawer (mobile)
|
||||||
|
// ============================================================================
|
||||||
|
function openSidebar() {
|
||||||
|
const app = document.querySelector('.app');
|
||||||
|
if (!app) return;
|
||||||
|
app.classList.add('sidebar-open');
|
||||||
|
const t = $('sidebarToggle');
|
||||||
|
if (t) t.setAttribute('aria-expanded', 'true');
|
||||||
|
}
|
||||||
|
function closeSidebar() {
|
||||||
|
const app = document.querySelector('.app');
|
||||||
|
if (!app) return;
|
||||||
|
app.classList.remove('sidebar-open');
|
||||||
|
const t = $('sidebarToggle');
|
||||||
|
if (t) t.setAttribute('aria-expanded', 'false');
|
||||||
|
}
|
||||||
|
function setupSidebarDrawer() {
|
||||||
|
const toggle = $('sidebarToggle');
|
||||||
|
const backdrop = $('sidebarBackdrop');
|
||||||
|
if (toggle) {
|
||||||
|
toggle.addEventListener('click', () => {
|
||||||
|
const app = document.querySelector('.app');
|
||||||
|
if (app && app.classList.contains('sidebar-open')) closeSidebar();
|
||||||
|
else openSidebar();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (backdrop) backdrop.addEventListener('click', closeSidebar);
|
||||||
|
}
|
||||||
|
|
||||||
function wireUI() {
|
function wireUI() {
|
||||||
|
setupSidebarDrawer();
|
||||||
els.searchForm.addEventListener('submit', async (e) => {
|
els.searchForm.addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const q = els.searchInput.value.trim();
|
const q = els.searchInput.value.trim();
|
||||||
@@ -2233,7 +2266,9 @@ document.querySelectorAll('.chip').forEach((c) => {
|
|||||||
else if (e.key === 'b') { if (current) setAbB(); }
|
else if (e.key === 'b') { if (current) setAbB(); }
|
||||||
else if (e.key === '?') toggleShortcutHelp();
|
else if (e.key === '?') toggleShortcutHelp();
|
||||||
else if (e.key === 'Escape') {
|
else if (e.key === 'Escape') {
|
||||||
if (!$('shortcutHelp').classList.contains('hidden')) $('shortcutHelp').classList.add('hidden');
|
const app = document.querySelector('.app');
|
||||||
|
if (app && app.classList.contains('sidebar-open')) closeSidebar();
|
||||||
|
else if (!$('shortcutHelp').classList.contains('hidden')) $('shortcutHelp').classList.add('hidden');
|
||||||
else if (selectMode) { exitSelectMode(); renderList(); }
|
else if (selectMode) { exitSelectMode(); renderList(); }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="app">
|
<div class="app">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<aside class="sidebar">
|
<aside id="sidebar" class="sidebar">
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
<span class="brand-mark">▶</span> YT Player
|
<span class="brand-mark">▶</span> YT Player
|
||||||
</div>
|
</div>
|
||||||
@@ -58,9 +58,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
<!-- Sidebar backdrop (mobile drawer) -->
|
||||||
|
<div id="sidebarBackdrop" class="sidebar-backdrop"></div>
|
||||||
|
|
||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
|
<button
|
||||||
|
id="sidebarToggle"
|
||||||
|
class="sidebar-toggle"
|
||||||
|
type="button"
|
||||||
|
aria-label="Toggle sidebar"
|
||||||
|
aria-controls="sidebar"
|
||||||
|
aria-expanded="false"
|
||||||
|
>☰</button>
|
||||||
<form id="searchForm" class="search-form">
|
<form id="searchForm" class="search-form">
|
||||||
<input
|
<input
|
||||||
id="searchInput"
|
id="searchInput"
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ body::after {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 22px 14px 16px;
|
padding: 22px 14px 16px;
|
||||||
overflow: hidden;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand {
|
.brand {
|
||||||
@@ -200,7 +201,7 @@ body::after {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-footer { border-top: 1px solid var(--line-soft); padding-top: 12px; margin-top: 10px; }
|
.sidebar-footer { border-top: 1px solid var(--line-soft); padding-top: 12px; margin-top: auto; flex-shrink: 0; }
|
||||||
.switch {
|
.switch {
|
||||||
display: flex; align-items: center; gap: 10px;
|
display: flex; align-items: center; gap: 10px;
|
||||||
color: var(--text-2); cursor: pointer; padding: 8px 10px;
|
color: var(--text-2); cursor: pointer; padding: 8px 10px;
|
||||||
@@ -214,12 +215,45 @@ body::after {
|
|||||||
.main { display: flex; flex-direction: column; overflow: hidden; }
|
.main { display: flex; flex-direction: column; overflow: hidden; }
|
||||||
|
|
||||||
.topbar {
|
.topbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
padding: 16px 26px;
|
padding: 16px 26px;
|
||||||
border-bottom: 1px solid var(--line-soft);
|
border-bottom: 1px solid var(--line-soft);
|
||||||
background: linear-gradient(180deg, rgba(16,16,19,0.7), transparent);
|
background: linear-gradient(180deg, rgba(16,16,19,0.7), transparent);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
}
|
}
|
||||||
.search-form { display: flex; gap: 10px; max-width: 760px; }
|
.search-form { flex: 1; display: flex; gap: 10px; max-width: 760px; }
|
||||||
|
|
||||||
|
/* Hamburger toggle — hidden on desktop, revealed in the mobile breakpoint */
|
||||||
|
.sidebar-toggle {
|
||||||
|
display: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--bg-2);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 11px;
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.18s, border-color 0.18s;
|
||||||
|
}
|
||||||
|
.sidebar-toggle:hover { background: var(--bg-3); border-color: var(--accent); }
|
||||||
|
|
||||||
|
/* Backdrop behind the off-canvas drawer — only shown when the drawer is open */
|
||||||
|
.sidebar-backdrop {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 90;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.25s var(--ease);
|
||||||
|
}
|
||||||
#searchInput {
|
#searchInput {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: var(--bg-2);
|
background: var(--bg-2);
|
||||||
@@ -1179,6 +1213,44 @@ input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.25); }
|
|||||||
.list-pane { width: auto; border-left: none; border-top: 1px solid var(--line-soft); }
|
.list-pane { width: auto; border-left: none; border-top: 1px solid var(--line-soft); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Responsive sidebar — off-canvas drawer on small screens
|
||||||
|
* Below 860px the fixed 264px grid column is dropped; the sidebar becomes a
|
||||||
|
* slide-in drawer toggled by the hamburger button so the playlist and every
|
||||||
|
* other sidebar item stay reachable from 320px up to tablet width.
|
||||||
|
* ========================================================================== */
|
||||||
|
@media (max-width: 860px) {
|
||||||
|
.app { grid-template-columns: 1fr; }
|
||||||
|
|
||||||
|
.sidebar-toggle { display: inline-flex; }
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: min(86vw, 300px);
|
||||||
|
max-width: 300px;
|
||||||
|
z-index: 100;
|
||||||
|
border-right: 1px solid var(--line-soft);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
transform: translateX(-100%);
|
||||||
|
transition: transform 0.28s var(--ease);
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app.sidebar-open .sidebar { transform: translateX(0); }
|
||||||
|
|
||||||
|
.app.sidebar-open .sidebar-backdrop { display: block; opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Very narrow screens (e.g. 320px) — keep the topbar usable and the drawer
|
||||||
|
from ever exceeding the viewport. */
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
.topbar { padding: 12px 14px; }
|
||||||
|
.sidebar { width: min(88vw, 280px); }
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Loop / repeat active state
|
* Loop / repeat active state
|
||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
|
|||||||
Reference in New Issue
Block a user