11---
22import lessons from ' ../data/lessons.json' ;
33import { languages , useTranslations } from ' ../i18n/ui' ;
4+ import { ChevronLeft , ChevronRight } from ' @lucide/astro' ;
45
56interface Props {
67 activePath? : string ;
@@ -28,22 +29,21 @@ const categoryOrder = ["syntax", "data_structures", "classes", "advanced"];
2829
2930<aside class =" sidebar" >
3031 <button id =" sidebar-toggle" class =" sidebar-toggle-btn" aria-label =" Toggle Sidebar" >
31- <span >←</span >
32+ <span class =" arrow-left" ><ChevronLeft size ={ 16 } /></span >
33+ <span class =" arrow-right" ><ChevronRight size ={ 16 } /></span >
3234 </button >
3335
3436 <script is:inline >
3537 // Run this immediately to prevent layout shift/flash
3638 (function() {
37- const isCollapsed = localStorage.getItem('sidebar-collapsed') === 'true';
39+ const stored = localStorage.getItem('sidebar-collapsed');
40+ const isMobile = window.innerWidth < 900;
41+ const isCollapsed = stored === 'true' || (stored === null && isMobile);
3842 if (isCollapsed) {
3943 const container = document.querySelector('.app-container');
4044 if (container) {
4145 container.classList.add('sidebar-collapsed');
4246 }
43- const btn = document.getElementById('sidebar-toggle');
44- if (btn) {
45- btn.innerHTML = '<span>→</span>';
46- }
4747 }
4848 })();
4949 </script >
@@ -110,6 +110,7 @@ const categoryOrder = ["syntax", "data_structures", "classes", "advanced"];
110110 })}
111111 </nav >
112112</aside >
113+ <div class =" sidebar-backdrop" id =" sidebar-backdrop" ></div >
113114
114115<style >
115116 .sidebar {
@@ -127,7 +128,7 @@ const categoryOrder = ["syntax", "data_structures", "classes", "advanced"];
127128 .sidebar-toggle-btn {
128129 position: absolute;
129130 right: -15px;
130- top: 25px ;
131+ top: 15px ;
131132 width: 30px;
132133 height: 30px;
133134 border-radius: 50%;
@@ -146,6 +147,24 @@ const categoryOrder = ["syntax", "data_structures", "classes", "advanced"];
146147 font-weight: bold;
147148 }
148149
150+ .arrow-left, .arrow-right {
151+ display: flex;
152+ align-items: center;
153+ justify-content: center;
154+ }
155+
156+ .arrow-right {
157+ display: none;
158+ }
159+
160+ :global(.app-container.sidebar-collapsed) .arrow-left {
161+ display: none;
162+ }
163+
164+ :global(.app-container.sidebar-collapsed) .arrow-right {
165+ display: flex;
166+ }
167+
149168 .sidebar-toggle-btn:hover {
150169 background-color: var(--bg-tertiary);
151170 transform: scale(1.05);
@@ -327,6 +346,35 @@ const categoryOrder = ["syntax", "data_structures", "classes", "advanced"];
327346 .lesson-link.active .lesson-indicator {
328347 background-color: var(--forest-green);
329348 }
349+
350+ .sidebar-backdrop {
351+ display: none;
352+ }
353+
354+ @media (max-width: 900px) {
355+ .sidebar {
356+ position: fixed;
357+ left: 0;
358+ top: 0;
359+ bottom: 0;
360+ z-index: 1000;
361+ box-shadow: 5px 0 15px rgba(0, 0, 0, 0.1);
362+ }
363+
364+
365+
366+ :global(.app-container:not(.sidebar-collapsed)) .sidebar-backdrop {
367+ display: block;
368+ position: fixed;
369+ top: 0;
370+ left: 0;
371+ right: 0;
372+ bottom: 0;
373+ background-color: rgba(0, 0, 0, 0.4);
374+ backdrop-filter: blur(2px);
375+ z-index: 999;
376+ }
377+ }
330378</style >
331379
332380<script >
@@ -336,46 +384,52 @@ const categoryOrder = ["syntax", "data_structures", "classes", "advanced"];
336384 if (!searchInput || searchInput.dataset.searchInitialized) return;
337385 searchInput.dataset.searchInitialized = 'true';
338386
387+ let searchTimeout: number;
339388 searchInput.addEventListener('input', (e) => {
340- const query = (e.target as HTMLInputElement).value.toLowerCase().trim();
341- const items = document.querySelectorAll('.lesson-item');
342- const groups = document.querySelectorAll('.category-group');
343-
344- if (!query) {
345- // Reset visibility
346- items.forEach(el => el.classList.remove('hidden-search'));
347- groups.forEach(el => el.classList.remove('hidden-search'));
348- return;
349- }
350-
351- // Track which categories have visible matches
352- const categoryMatches = new Map();
389+ cancelAnimationFrame(searchTimeout);
390+ const target = e.target as HTMLInputElement;
353391
354- items.forEach(item => {
355- const name = item.getAttribute('data-lesson-name') || '' ;
356- const desc = item.getAttribute('data- lesson-desc') || '' ;
357- const id = item.getAttribute('data-lesson-id') || '' ;
392+ searchTimeout = requestAnimationFrame(() => {
393+ const query = target.value.toLowerCase().trim() ;
394+ const items = document.querySelectorAll('. lesson-item') ;
395+ const groups = document.querySelectorAll('.category-group') ;
358396
359- const isMatch = name.includes(query) || desc.includes(query) || id.includes(query);
397+ if (!query) {
398+ // Reset visibility
399+ items.forEach(el => el.classList.remove('hidden-search'));
400+ groups.forEach(el => el.classList.remove('hidden-search'));
401+ return;
402+ }
360403
361- const parentGroup = item.closest('.category-group');
362- if (parentGroup) {
363- if (isMatch) {
364- item.classList.remove('hidden-search');
365- categoryMatches.set(parentGroup, true);
404+ // Track which categories have visible matches
405+ const categoryMatches = new Map();
406+
407+ items.forEach(item => {
408+ const name = item.getAttribute('data-lesson-name') || '';
409+ const desc = item.getAttribute('data-lesson-desc') || '';
410+ const id = item.getAttribute('data-lesson-id') || '';
411+
412+ const isMatch = name.includes(query) || desc.includes(query) || id.includes(query);
413+
414+ const parentGroup = item.closest('.category-group');
415+ if (parentGroup) {
416+ if (isMatch) {
417+ item.classList.remove('hidden-search');
418+ categoryMatches.set(parentGroup, true);
419+ } else {
420+ item.classList.add('hidden-search');
421+ }
422+ }
423+ });
424+
425+ groups.forEach(group => {
426+ if (categoryMatches.has(group)) {
427+ group.classList.remove('hidden-search');
428+ (group as HTMLDetailsElement).open = true;
366429 } else {
367- item .classList.add('hidden-search');
430+ group .classList.add('hidden-search');
368431 }
369- }
370- });
371-
372- groups.forEach(group => {
373- if (categoryMatches.has(group)) {
374- group.classList.remove('hidden-search');
375- (group as HTMLDetailsElement).open = true;
376- } else {
377- group.classList.add('hidden-search');
378- }
432+ });
379433 });
380434 });
381435 }
@@ -405,27 +459,51 @@ const categoryOrder = ["syntax", "data_structures", "classes", "advanced"];
405459 function setupSidebarToggle() {
406460 const toggleBtn = document.getElementById('sidebar-toggle');
407461 const container = document.querySelector('.app-container');
462+ const backdrop = document.getElementById('sidebar-backdrop');
408463 if (!toggleBtn || !container || toggleBtn.dataset.toggleInitialized) return;
409464 toggleBtn.dataset.toggleInitialized = 'true';
410465
411466 // Load initial state
412- const isCollapsed = localStorage.getItem('sidebar-collapsed') === 'true';
467+ const stored = localStorage.getItem('sidebar-collapsed');
468+ const isMobile = window.innerWidth < 900;
469+ const isCollapsed = stored === 'true' || (stored === null && isMobile);
413470 if (isCollapsed) {
414471 container.classList.add('sidebar-collapsed');
415- toggleBtn.innerHTML = '<span>→</span>';
416472 } else {
417473 container.classList.remove('sidebar-collapsed');
418- toggleBtn.innerHTML = '<span>←</span>';
419474 }
420475
476+ const closeSidebar = () => {
477+ container.classList.add('sidebar-collapsed');
478+ localStorage.setItem('sidebar-collapsed', 'true');
479+ };
480+
421481 toggleBtn.addEventListener('click', () => {
422482 const collapsed = container.classList.toggle('sidebar-collapsed');
423483 localStorage.setItem('sidebar-collapsed', String(collapsed));
424- if (collapsed) {
425- toggleBtn.innerHTML = '<span>→</span>';
426- } else {
427- toggleBtn.innerHTML = '<span>←</span>';
484+ });
485+
486+ if (backdrop) {
487+ backdrop.addEventListener('click', closeSidebar);
488+ }
489+
490+ // Auto-collapse/expand on window resize transition
491+ let lastWidth = window.innerWidth;
492+ window.addEventListener('resize', () => {
493+ const currentWidth = window.innerWidth;
494+ const wasDesktop = lastWidth >= 900;
495+ const isMobile = currentWidth < 900;
496+
497+ if (wasDesktop && isMobile) {
498+ container.classList.add('sidebar-collapsed');
499+ } else if (!wasDesktop && !isMobile) {
500+ const stored = localStorage.getItem('sidebar-collapsed');
501+ const isCollapsed = stored === 'true';
502+ if (!isCollapsed) {
503+ container.classList.remove('sidebar-collapsed');
504+ }
428505 }
506+ lastWidth = currentWidth;
429507 });
430508 }
431509
0 commit comments