feat(ui): add responsive sidebar with mobile toggle and overlay
Implement responsive sidebar functionality that auto-opens on desktop (≥1024px) and auto-closes on mobile, with a toggle button for mobile users. Added overlay for mobile sidebar dismissal and CSS for collapsed state on desktop. Minor adjustments to dashboard chart borders and grid layouts.
This commit is contained in:
@@ -57,9 +57,33 @@ document.addEventListener('alpine:init', () => {
|
|||||||
return Alpine.store('data')?.loading || false;
|
return Alpine.store('data')?.loading || false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sidebarOpen: window.innerWidth >= 1024,
|
||||||
|
toggleSidebar() {
|
||||||
|
this.sidebarOpen = !this.sidebarOpen;
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
console.log('App controller initialized');
|
console.log('App controller initialized');
|
||||||
|
|
||||||
|
// Handle responsive sidebar transitions
|
||||||
|
let lastWidth = window.innerWidth;
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
const currentWidth = window.innerWidth;
|
||||||
|
const lgBreakpoint = 1024;
|
||||||
|
|
||||||
|
// Desktop -> Mobile: Auto-close sidebar to prevent overlay blocking screen
|
||||||
|
if (lastWidth >= lgBreakpoint && currentWidth < lgBreakpoint) {
|
||||||
|
this.sidebarOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mobile -> Desktop: Auto-open sidebar (restore standard desktop layout)
|
||||||
|
if (lastWidth < lgBreakpoint && currentWidth >= lgBreakpoint) {
|
||||||
|
this.sidebarOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastWidth = currentWidth;
|
||||||
|
});
|
||||||
|
|
||||||
// Theme setup
|
// Theme setup
|
||||||
document.documentElement.setAttribute('data-theme', 'black');
|
document.documentElement.setAttribute('data-theme', 'black');
|
||||||
document.documentElement.classList.add('dark');
|
document.documentElement.classList.add('dark');
|
||||||
|
|||||||
@@ -489,3 +489,12 @@
|
|||||||
.skeleton-table-row {
|
.skeleton-table-row {
|
||||||
@apply skeleton h-12 w-full mb-2;
|
@apply skeleton h-12 w-full mb-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Desktop Sidebar Collapsed State */
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.sidebar-collapsed {
|
||||||
|
width: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
public/css/style.css
generated
2
public/css/style.css
generated
File diff suppressed because one or more lines are too long
@@ -62,8 +62,15 @@
|
|||||||
|
|
||||||
<!-- Navbar -->
|
<!-- Navbar -->
|
||||||
<div
|
<div
|
||||||
class="h-14 border-b border-space-border flex items-center px-6 justify-between bg-space-900/50 backdrop-blur-md z-50">
|
class="h-14 border-b border-space-border flex items-center px-4 lg:px-6 justify-between bg-space-900/50 backdrop-blur-md z-50 relative">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
|
<!-- Mobile Menu Button -->
|
||||||
|
<button @click="toggleSidebar()" class="text-gray-400 hover:text-white focus:outline-none p-1 transition-colors">
|
||||||
|
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="w-8 h-8 rounded bg-gradient-to-br from-neon-purple to-blue-600 flex items-center justify-center text-white font-bold shadow-[0_0_15px_rgba(168,85,247,0.4)]">
|
class="w-8 h-8 rounded bg-gradient-to-br from-neon-purple to-blue-600 flex items-center justify-center text-white font-bold shadow-[0_0_15px_rgba(168,85,247,0.4)]">
|
||||||
AG</div>
|
AG</div>
|
||||||
@@ -103,12 +110,47 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Layout -->
|
<!-- Layout -->
|
||||||
<div class="flex h-[calc(100vh-56px)]">
|
<div class="flex h-[calc(100vh-56px)] relative">
|
||||||
|
|
||||||
|
<!-- Mobile Sidebar Overlay -->
|
||||||
|
<div x-show="sidebarOpen"
|
||||||
|
x-transition:enter="transition-opacity ease-linear duration-300"
|
||||||
|
x-transition:enter-start="opacity-0"
|
||||||
|
x-transition:enter-end="opacity-100"
|
||||||
|
x-transition:leave="transition-opacity ease-linear duration-300"
|
||||||
|
x-transition:leave-start="opacity-100"
|
||||||
|
x-transition:leave-end="opacity-0"
|
||||||
|
@click="sidebarOpen = false"
|
||||||
|
class="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
||||||
|
style="display: none;"></div>
|
||||||
|
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<div class="w-64 bg-space-900 border-r border-space-border flex flex-col pt-6 pb-4">
|
<div class="fixed top-14 bottom-0 left-0 z-40 bg-space-900 border-r border-space-border transition-all duration-300 shadow-2xl overflow-hidden lg:static lg:h-auto lg:shadow-none lg:flex-shrink-0"
|
||||||
<div class="px-4 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest"
|
:class="{
|
||||||
|
'translate-x-0': sidebarOpen,
|
||||||
|
'-translate-x-full': !sidebarOpen,
|
||||||
|
'w-64': true,
|
||||||
|
'lg:translate-x-0': true,
|
||||||
|
'lg:w-64': sidebarOpen,
|
||||||
|
'sidebar-collapsed': !sidebarOpen
|
||||||
|
}">
|
||||||
|
|
||||||
|
<!-- Inner Sidebar Content (Fixed Width to prevent squashing) -->
|
||||||
|
<div class="w-64 flex flex-col h-full pt-6 pb-4 flex-shrink-0">
|
||||||
|
<!-- Mobile Menu Header -->
|
||||||
|
<div class="flex items-center justify-between px-4 mb-6 lg:hidden">
|
||||||
|
<span class="text-sm font-bold text-white">Menu</span>
|
||||||
|
<button @click="sidebarOpen = false" class="text-gray-400 hover:text-white">
|
||||||
|
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Desktop Header (Main) -->
|
||||||
|
<div class="px-4 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest hidden lg:block"
|
||||||
x-text="$store.global.t('main')">Main</div>
|
x-text="$store.global.t('main')">Main</div>
|
||||||
|
|
||||||
<nav class="flex flex-col gap-1">
|
<nav class="flex flex-col gap-1">
|
||||||
<button
|
<button
|
||||||
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
|
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
|
||||||
@@ -177,6 +219,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<div class="flex-1 overflow-auto bg-space-950 relative custom-scrollbar">
|
<div class="flex-1 overflow-auto bg-space-950 relative custom-scrollbar">
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ window.DashboardCharts.updateCharts = function (component) {
|
|||||||
data: data,
|
data: data,
|
||||||
backgroundColor: colors,
|
backgroundColor: colors,
|
||||||
borderColor: getThemeColor("--color-space-950"),
|
borderColor: getThemeColor("--color-space-950"),
|
||||||
borderWidth: 2,
|
borderWidth: 0,
|
||||||
hoverOffset: 0,
|
hoverOffset: 0,
|
||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
<div class="skeleton-stat-card"></div>
|
<div class="skeleton-stat-card"></div>
|
||||||
<div class="skeleton-stat-card"></div>
|
<div class="skeleton-stat-card"></div>
|
||||||
<div class="skeleton-stat-card"></div>
|
<div class="skeleton-stat-card"></div>
|
||||||
<div class="skeleton-stat-card"></div>
|
<div class="skeleton-stat-card col-span-2 sm:col-span-1"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Skeleton Charts -->
|
<!-- Skeleton Charts -->
|
||||||
@@ -155,7 +155,7 @@
|
|||||||
|
|
||||||
<!-- Global Quota Chart -->
|
<!-- Global Quota Chart -->
|
||||||
<div
|
<div
|
||||||
class="stat bg-space-900/40 border border-space-border/30 rounded-xl p-6 h-full flex items-center justify-between gap-3 overflow-hidden relative group hover:border-space-border/60 transition-colors">
|
class="stat bg-space-900/40 border border-space-border/30 rounded-xl p-6 h-full flex items-center justify-between gap-3 overflow-hidden relative group hover:border-space-border/60 transition-colors col-span-2 sm:col-span-1">
|
||||||
<!-- Chart Container -->
|
<!-- Chart Container -->
|
||||||
<div class="h-14 w-14 lg:h-16 lg:w-16 relative flex-shrink-0">
|
<div class="h-14 w-14 lg:h-16 lg:w-16 relative flex-shrink-0">
|
||||||
<canvas id="quotaChart"></canvas>
|
<canvas id="quotaChart"></canvas>
|
||||||
|
|||||||
Reference in New Issue
Block a user