From 3659533102f3babe41a666555ddd382fe52f5663 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Thu, 28 May 2026 16:58:29 +0800 Subject: [PATCH] fix: responsive layout - sidebar collapse, mobile adaptation, prevent text jumping - Fix text jumping on right side when screen narrows (min-width:0 on flex item) - Fix sidebar toggle button not working (retry icon init, proper state sync) - Mobile adaptation: sidebar as overlay with slide animation - Auto-collapse sidebar on mobile viewport (<=768px) - Click outside sidebar to close on mobile - Responsive padding for stat cards and main content - Hide brand title on very small screens (<=480px) - Smooth transitions for sidebar collapse/expand --- wwwroot/shell_theme.css | 87 +++++++++++++++++++++++++++++++++++------ wwwroot/shell_theme.js | 76 ++++++++++++++++++++++++++++++----- 2 files changed, 141 insertions(+), 22 deletions(-) diff --git a/wwwroot/shell_theme.css b/wwwroot/shell_theme.css index 9215966..6d5cdd9 100644 --- a/wwwroot/shell_theme.css +++ b/wwwroot/shell_theme.css @@ -128,7 +128,9 @@ body { overflow-y: auto; overflow-x: hidden; flex-shrink: 0; - transition: width 0.25s ease; + transition: width 0.3s ease, transform 0.3s ease; + position: relative; + z-index: 50; } .sage-sidebar.collapsed { @@ -141,12 +143,48 @@ body { overflow: hidden; } +/* Mobile: sidebar as overlay */ +@media (max-width: 768px) { + .sage-sidebar { + position: fixed; + left: 0; + top: var(--sage-topbar-height); + height: calc(100vh - var(--sage-topbar-height)); + transform: translateX(0); + box-shadow: var(--sage-shadow-lg); + } + + .sage-sidebar.collapsed { + transform: translateX(-100%); + width: var(--sage-sidebar-width); /* Keep full width when hidden on mobile */ + } + + .sage-main { + width: 100%; + } +} + .sage-main { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 24px; background-color: var(--sage-bg-primary); + min-width: 0; /* Prevent flex item from overflowing */ + transition: margin-left 0.3s ease; +} + +/* Responsive padding for mobile */ +@media (max-width: 768px) { + .sage-main { + padding: 16px; + } +} + +@media (max-width: 480px) { + .sage-main { + padding: 12px; + } } /* ===== Stat Cards ===== */ @@ -719,18 +757,43 @@ body { color: var(--sage-text-secondary); } -/* ===== Responsive ===== */ +/* ===== Responsive - Topbar ===== */ @media (max-width: 768px) { - .sage-sidebar { - position: fixed; - left: 0; - top: var(--sage-topbar-height); - bottom: 0; - z-index: 200; - transform: translateX(-100%); - transition: transform 0.25s ease; + .sage-topbar { + padding: 0 8px; + gap: 6px; } - .sage-sidebar.mobile-open { - transform: translateX(0); + .sage-brand-title { + font-size: 16px; + } +} + +@media (max-width: 480px) { + .sage-brand-title { + display: none; + } +} + +/* ===== Responsive - Stat Cards ===== */ +@media (max-width: 768px) { + .stat-card { + padding: 14px; + } + .stat-card .stat-value { + font-size: 22px; + } + .stat-card .stat-label { + font-size: 12px; + } + .stat-card .stat-icon { + width: 32px; + height: 32px; + margin-bottom: 8px; + } + .quick-link { + padding: 14px; + } + .section-title { + font-size: 16px; } } diff --git a/wwwroot/shell_theme.js b/wwwroot/shell_theme.js index 2b86ddb..89f8de3 100644 --- a/wwwroot/shell_theme.js +++ b/wwwroot/shell_theme.js @@ -42,12 +42,61 @@ function initSidebar() { var collapsed = false; try { collapsed = localStorage.getItem(SIDEBAR_KEY) === 'true'; } catch(e) {} + + // Auto-collapse on mobile + if (isMobile()) { + collapsed = true; + } + var sidebar = document.getElementById('sage_sidebar'); if (sidebar && collapsed) { sidebar.classList.add('collapsed'); } } + // Handle window resize - auto-collapse on mobile + function handleResize() { + var sidebar = document.getElementById('sage_sidebar'); + if (!sidebar) return; + + if (isMobile() && !sidebar.classList.contains('collapsed')) { + sidebar.classList.add('collapsed'); + try { localStorage.setItem(SIDEBAR_KEY, 'true'); } catch(e) {} + updateSidebarIcon(true); + } + } + + // Check if we're on mobile viewport + function isMobile() { + return window.innerWidth <= 768; + } + + // Close sidebar when clicking outside on mobile + function setupMobileOverlay() { + document.addEventListener('click', function(e) { + if (!isMobile()) return; + var sidebar = document.getElementById('sage_sidebar'); + var toggleBtn = document.getElementById('sidebar_toggle_btn'); + if (!sidebar || sidebar.classList.contains('collapsed')) return; + // If click is outside sidebar and toggle button, close sidebar + if (!sidebar.contains(e.target) && (!toggleBtn || !toggleBtn.contains(e.target))) { + sidebar.classList.add('collapsed'); + try { localStorage.setItem(SIDEBAR_KEY, 'true'); } catch(ex) {} + updateSidebarIcon(true); + } + }); + } + + function updateSidebarIcon(isCollapsed) { + var btn = document.getElementById('sidebar_toggle_btn'); + if (!btn) return; + if (isCollapsed) { + btn.innerHTML = ''; + } else { + btn.innerHTML = ''; + } + } + // Toggle sidebar collapse function toggleSidebar() { var sidebar = document.getElementById('sage_sidebar'); @@ -55,16 +104,7 @@ sidebar.classList.toggle('collapsed'); var isCollapsed = sidebar.classList.contains('collapsed'); try { localStorage.setItem(SIDEBAR_KEY, isCollapsed); } catch(e) {} - - // Update toggle icon - var btn = document.getElementById('sidebar_toggle_btn'); - if (btn) { - if (isCollapsed) { - btn.innerHTML = ''; - } else { - btn.innerHTML = ''; - } - } + updateSidebarIcon(isCollapsed); } // Initialize SPA Router @@ -115,11 +155,15 @@ initTheme(); initSidebar(); initRouter(); + setupMobileOverlay(); + window.addEventListener('resize', handleResize); }); } else { initTheme(); initSidebar(); initRouter(); + setupMobileOverlay(); + window.addEventListener('resize', handleResize); } // Bricks widgets render asynchronously after DOMContentLoaded. @@ -134,6 +178,18 @@ } })(); + // Retry sidebar icon init until the button element exists + (function retrySidebarIcon() { + var sidebar = document.getElementById('sage_sidebar'); + var btn = document.getElementById('sidebar_toggle_btn'); + if (btn && sidebar) { + var isCollapsed = sidebar.classList.contains('collapsed'); + updateSidebarIcon(isCollapsed); + } else { + setTimeout(retrySidebarIcon, 200); + } + })(); + // Expose global functions for bricks bind access window.sageToggleTheme = toggleTheme; window.sageToggleSidebar = toggleSidebar;