Add collapsible sidebar menu functionality

Features:
- Sidebar can now be collapsed to save screen space (260px → 60px)
- Collapse button integrated directly into sidebar header
- Smooth animations for expand/collapse transitions
- State persistence using localStorage (remembers user preference)
- Responsive design - collapse feature disabled on mobile devices
- Visual feedback with hover effects and rotation animation
- Icons remain visible when collapsed for easy navigation
- Seamless integration with existing mobile sidebar toggle

User Experience:
- Click the "‹" button in sidebar to collapse
- Click "›" button to expand back to full width
- User preference is saved and restored on page reload
- Main content area automatically adjusts width
- Smooth 0.3s transition animations for professional feel
- Tooltips show current state (Collapse/Expand sidebar)

Technical Implementation:
- CSS transitions for smooth animations
- JavaScript event handling with localStorage persistence
- Responsive CSS media queries for mobile compatibility
- Graceful degradation on smaller screens
- Clean separation of desktop vs mobile behavior

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
kris 2025-08-28 20:37:57 +00:00
parent 31a02a69ac
commit 8f141beef5
3 changed files with 135 additions and 6 deletions

View File

@ -9,6 +9,7 @@
<body> <body>
<div class="app-container"> <div class="app-container">
<nav class="sidebar"> <nav class="sidebar">
<button class="sidebar-collapse-btn" id="sidebar-collapse-btn" title="Collapse sidebar"></button>
<div class="sidebar-header"> <div class="sidebar-header">
<h2>ETF Tracker</h2> <h2>ETF Tracker</h2>
</div> </div>

View File

@ -12,6 +12,7 @@ class ETFTradeTracker {
this.bindEvents(); this.bindEvents();
this.bindNavigation(); this.bindNavigation();
this.bindAuthEvents(); this.bindAuthEvents();
this.bindSidebarToggle();
this.setDefaultDateTime(); this.setDefaultDateTime();
// Check if user is logged in // Check if user is logged in
@ -73,6 +74,49 @@ class ETFTradeTracker {
}); });
} }
bindSidebarToggle() {
const sidebarCollapseBtn = document.getElementById('sidebar-collapse-btn');
const sidebar = document.querySelector('.sidebar');
const mainContent = document.querySelector('.main-content');
// Load saved sidebar state from localStorage
const savedState = localStorage.getItem('sidebarCollapsed');
if (savedState === 'true') {
sidebar.classList.add('collapsed');
mainContent.classList.add('sidebar-collapsed');
sidebarCollapseBtn.setAttribute('title', 'Expand sidebar');
sidebarCollapseBtn.innerHTML = '';
}
sidebarCollapseBtn.addEventListener('click', () => {
const isCollapsed = sidebar.classList.contains('collapsed');
if (isCollapsed) {
// Expand sidebar
sidebar.classList.remove('collapsed');
mainContent.classList.remove('sidebar-collapsed');
sidebarCollapseBtn.setAttribute('title', 'Collapse sidebar');
sidebarCollapseBtn.innerHTML = '';
localStorage.setItem('sidebarCollapsed', 'false');
} else {
// Collapse sidebar
sidebar.classList.add('collapsed');
mainContent.classList.add('sidebar-collapsed');
sidebarCollapseBtn.setAttribute('title', 'Expand sidebar');
sidebarCollapseBtn.innerHTML = '';
localStorage.setItem('sidebarCollapsed', 'true');
}
});
// Handle mobile sidebar toggle (existing functionality)
const sidebarToggle = document.querySelector('.sidebar-toggle');
if (sidebarToggle) {
sidebarToggle.addEventListener('click', () => {
sidebar.classList.toggle('open');
});
}
}
showPage(pageId) { showPage(pageId) {
const pages = document.querySelectorAll('.page'); const pages = document.querySelectorAll('.page');
const pageTitle = document.getElementById('page-title'); const pageTitle = document.getElementById('page-title');

View File

@ -28,10 +28,50 @@ body {
position: fixed; position: fixed;
height: 100vh; height: 100vh;
overflow-y: auto; overflow-y: auto;
transition: transform 0.3s ease; transition: all 0.3s ease;
z-index: 1000; z-index: 1000;
} }
.sidebar.collapsed {
width: 60px;
}
.sidebar.collapsed .menu-text,
.sidebar.collapsed .sidebar-header h2,
.sidebar.collapsed .user-info span,
.sidebar.collapsed .logout-btn {
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
}
.menu-text,
.sidebar-header h2,
.user-info span,
.logout-btn {
transition: opacity 0.2s ease, visibility 0.2s ease;
}
.sidebar.collapsed .menu-icon {
margin-right: 0;
text-align: center;
width: 100%;
}
.sidebar.collapsed .menu-item {
justify-content: center;
padding: 15px 0;
}
.sidebar.collapsed .sidebar-header {
padding: 20px 10px;
}
.sidebar.collapsed .user-info {
padding: 15px 10px;
text-align: center;
}
.sidebar-header { .sidebar-header {
padding: 30px 20px; padding: 30px 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1); border-bottom: 1px solid rgba(255, 255, 255, 0.1);
@ -125,6 +165,11 @@ body {
margin-left: 260px; margin-left: 260px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
transition: margin-left 0.3s ease;
}
.main-content.sidebar-collapsed {
margin-left: 60px;
} }
.top-header { .top-header {
@ -140,18 +185,45 @@ body {
} }
.sidebar-toggle { .sidebar-toggle {
display: none; display: block;
background: none; background: none;
border: none; border: none;
font-size: 1.5rem; font-size: 1.5rem;
cursor: pointer; cursor: pointer;
padding: 5px; padding: 8px;
border-radius: 4px; border-radius: 6px;
transition: background-color 0.2s ease; transition: all 0.2s ease;
color: #666;
} }
.sidebar-toggle:hover { .sidebar-toggle:hover {
background-color: #f0f0f0; background: #f1f3f4;
color: #333;
}
.sidebar-collapse-btn {
position: absolute;
top: 20px;
right: 10px;
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
font-size: 1.2rem;
cursor: pointer;
padding: 8px;
border-radius: 4px;
transition: all 0.2s ease;
opacity: 0.8;
}
.sidebar-collapse-btn:hover {
background: rgba(255, 255, 255, 0.3);
opacity: 1;
}
.sidebar.collapsed .sidebar-collapse-btn {
right: 15px;
transform: rotate(180deg);
} }
#page-title { #page-title {
@ -187,6 +259,10 @@ body {
width: 280px; width: 280px;
} }
.sidebar.collapsed {
width: 280px; /* Keep full width on mobile when collapsed */
}
.sidebar.open { .sidebar.open {
transform: translateX(0); transform: translateX(0);
} }
@ -195,10 +271,18 @@ body {
margin-left: 0; margin-left: 0;
} }
.main-content.sidebar-collapsed {
margin-left: 0; /* No change on mobile */
}
.sidebar-toggle { .sidebar-toggle {
display: block; display: block;
} }
.sidebar-collapse-btn {
display: none; /* Hide collapse button on mobile */
}
.content-area { .content-area {
padding: 20px; padding: 20px;
} }