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 = '';
|
||||
const fi = $('listFilterInput');
|
||||
if (fi) fi.value = '';
|
||||
// On mobile, navigating (nav item / playlist / search) dismisses the drawer.
|
||||
if (typeof closeSidebar === 'function') closeSidebar();
|
||||
exitSelectMode();
|
||||
renderSidebar();
|
||||
renderSmartSidebar();
|
||||
@@ -1977,7 +1979,38 @@ function closeModal() { $('modal').classList.add('hidden'); }
|
||||
// ============================================================================
|
||||
// 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() {
|
||||
setupSidebarDrawer();
|
||||
els.searchForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
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 === '?') toggleShortcutHelp();
|
||||
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(); }
|
||||
}
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<body>
|
||||
<div class="app">
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar">
|
||||
<aside id="sidebar" class="sidebar">
|
||||
<div class="brand">
|
||||
<span class="brand-mark">▶</span> YT Player
|
||||
</div>
|
||||
@@ -58,9 +58,20 @@
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Sidebar backdrop (mobile drawer) -->
|
||||
<div id="sidebarBackdrop" class="sidebar-backdrop"></div>
|
||||
|
||||
<!-- Main -->
|
||||
<main class="main">
|
||||
<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">
|
||||
<input
|
||||
id="searchInput"
|
||||
|
||||
@@ -90,7 +90,8 @@ body::after {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 22px 14px 16px;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.brand {
|
||||
@@ -200,7 +201,7 @@ body::after {
|
||||
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 {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
color: var(--text-2); cursor: pointer; padding: 8px 10px;
|
||||
@@ -214,12 +215,45 @@ body::after {
|
||||
.main { display: flex; flex-direction: column; overflow: hidden; }
|
||||
|
||||
.topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 26px;
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
background: linear-gradient(180deg, rgba(16,16,19,0.7), transparent);
|
||||
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 {
|
||||
flex: 1;
|
||||
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); }
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* 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
|
||||
* ========================================================================== */
|
||||
|
||||
Reference in New Issue
Block a user