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;
|
||||
},
|
||||
|
||||
sidebarOpen: window.innerWidth >= 1024,
|
||||
toggleSidebar() {
|
||||
this.sidebarOpen = !this.sidebarOpen;
|
||||
},
|
||||
|
||||
init() {
|
||||
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
|
||||
document.documentElement.setAttribute('data-theme', 'black');
|
||||
document.documentElement.classList.add('dark');
|
||||
|
||||
@@ -489,3 +489,12 @@
|
||||
.skeleton-table-row {
|
||||
@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 -->
|
||||
<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">
|
||||
<!-- 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
|
||||
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>
|
||||
@@ -103,77 +110,113 @@
|
||||
</div>
|
||||
|
||||
<!-- 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 -->
|
||||
<div class="w-64 bg-space-900 border-r border-space-border flex flex-col pt-6 pb-4">
|
||||
<div class="px-4 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest"
|
||||
x-text="$store.global.t('main')">Main</div>
|
||||
<nav class="flex flex-col gap-1">
|
||||
<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="{'active': $store.global.activeTab === 'dashboard'}"
|
||||
@click="$store.global.activeTab = 'dashboard'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('dashboard')">Dashboard</span>
|
||||
</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="{'active': $store.global.activeTab === 'models'}"
|
||||
@click="$store.global.activeTab = 'models'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('models')">Models</span>
|
||||
</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="{'active': $store.global.activeTab === 'accounts'}"
|
||||
@click="$store.global.activeTab = 'accounts'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('accounts')">Accounts</span>
|
||||
</button>
|
||||
</nav>
|
||||
<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"
|
||||
: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>
|
||||
|
||||
<div class="px-4 mt-8 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest"
|
||||
x-text="$store.global.t('system')">System</div>
|
||||
<nav class="flex flex-col gap-1">
|
||||
<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="{'active': $store.global.activeTab === 'logs'}" @click="$store.global.activeTab = 'logs'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('logs')">Logs</span>
|
||||
</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="{'active': $store.global.activeTab === 'settings'}"
|
||||
@click="$store.global.activeTab = 'settings'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('settings')">Settings</span>
|
||||
</button>
|
||||
</nav>
|
||||
<!-- 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>
|
||||
|
||||
<nav class="flex flex-col gap-1">
|
||||
<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="{'active': $store.global.activeTab === 'dashboard'}"
|
||||
@click="$store.global.activeTab = 'dashboard'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('dashboard')">Dashboard</span>
|
||||
</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="{'active': $store.global.activeTab === 'models'}"
|
||||
@click="$store.global.activeTab = 'models'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('models')">Models</span>
|
||||
</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="{'active': $store.global.activeTab === 'accounts'}"
|
||||
@click="$store.global.activeTab = 'accounts'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('accounts')">Accounts</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- Footer Info -->
|
||||
<div class="mt-auto px-6 text-[10px] text-gray-700 font-mono">
|
||||
<div class="flex justify-between">
|
||||
<span x-text="'V ' + $store.global.version">V 1.0.0</span>
|
||||
<a href="https://github.com/badri-s2001/antigravity-claude-proxy" target="_blank" rel="noopener noreferrer"
|
||||
class="hover:text-neon-purple transition-colors">GitHub</a>
|
||||
<div class="px-4 mt-8 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest"
|
||||
x-text="$store.global.t('system')">System</div>
|
||||
<nav class="flex flex-col gap-1">
|
||||
<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="{'active': $store.global.activeTab === 'logs'}" @click="$store.global.activeTab = 'logs'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('logs')">Logs</span>
|
||||
</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="{'active': $store.global.activeTab === 'settings'}"
|
||||
@click="$store.global.activeTab = 'settings'">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('settings')">Settings</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- Footer Info -->
|
||||
<div class="mt-auto px-6 text-[10px] text-gray-700 font-mono">
|
||||
<div class="flex justify-between">
|
||||
<span x-text="'V ' + $store.global.version">V 1.0.0</span>
|
||||
<a href="https://github.com/badri-s2001/antigravity-claude-proxy" target="_blank" rel="noopener noreferrer"
|
||||
class="hover:text-neon-purple transition-colors">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -260,7 +260,7 @@ window.DashboardCharts.updateCharts = function (component) {
|
||||
data: data,
|
||||
backgroundColor: colors,
|
||||
borderColor: getThemeColor("--color-space-950"),
|
||||
borderWidth: 2,
|
||||
borderWidth: 0,
|
||||
hoverOffset: 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 col-span-2 sm:col-span-1"></div>
|
||||
</div>
|
||||
|
||||
<!-- Skeleton Charts -->
|
||||
@@ -155,7 +155,7 @@
|
||||
|
||||
<!-- Global Quota Chart -->
|
||||
<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 -->
|
||||
<div class="h-14 w-14 lg:h-16 lg:w-16 relative flex-shrink-0">
|
||||
<canvas id="quotaChart"></canvas>
|
||||
|
||||
Reference in New Issue
Block a user