Phase 3: Mobile hamburger navigation menu
- Added responsive hamburger menu button visible on mobile (sm:hidden) - Hidden desktop navigation on mobile (hidden sm:block) - Added mobile menu dropdown with all navigation links - Added mobile language switcher and theme toggle in menu - JavaScript toggle functionality for menu open/close - Auto-close menu when clicking navigation links - Updated theme toggle to work with both desktop and mobile toggles - Proper ARIA attributes for accessibility (aria-expanded, aria-label)
This commit is contained in:
parent
06da5bdf2f
commit
c7ad302334
1 changed files with 122 additions and 19 deletions
|
|
@ -291,8 +291,8 @@
|
||||||
<h1 class="text-xl font-bold">{{ _('Tablet Management System') }}</h1>
|
<h1 class="text-xl font-bold">{{ _('Tablet Management System') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Language Switcher -->
|
<!-- Desktop Controls: Language + Theme Toggle -->
|
||||||
<div class="flex items-center gap-2">
|
<div class="hidden sm:flex items-center gap-2">
|
||||||
<span class="text-sm opacity-80">{{ _('Language') }}:</span>
|
<span class="text-sm opacity-80">{{ _('Language') }}:</span>
|
||||||
{% for lang_code, lang_name in config['LANGUAGES'].items() %}
|
{% for lang_code, lang_name in config['LANGUAGES'].items() %}
|
||||||
<a
|
<a
|
||||||
|
|
@ -310,6 +310,69 @@
|
||||||
<span class="icon-sun" style="display:none;">☀️</span>
|
<span class="icon-sun" style="display:none;">☀️</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile Menu Button -->
|
||||||
|
<button id="mobile-menu-btn" class="sm:hidden text-white hover:text-cobalt-200 focus:outline-none"
|
||||||
|
aria-label="Toggle navigation menu" aria-expanded="false">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path id="menu-icon-hamburger" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||||
|
<path id="menu-icon-close" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile Navigation Menu (hidden by default) -->
|
||||||
|
<div id="mobile-menu" class="sm:hidden hidden">
|
||||||
|
<div class="px-4 py-3 space-y-2 bg-cobalt-700">
|
||||||
|
<!-- Mobile Language Switcher -->
|
||||||
|
<div class="flex items-center gap-2 pb-2 border-b border-cobalt-600 mb-2">
|
||||||
|
<span class="text-sm opacity-80">{{ _('Language') }}:</span>
|
||||||
|
{% for lang_code, lang_name in config['LANGUAGES'].items() %}
|
||||||
|
<a
|
||||||
|
href="{{ url_for('set_language', lang=lang_code) }}"
|
||||||
|
class="px-2 py-1 text-sm rounded hover:bg-white/20 transition-colors
|
||||||
|
{% if language == lang_code %}bg-white/30 font-medium{% endif %}"
|
||||||
|
>
|
||||||
|
{{ lang_name }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Mobile Theme Toggle -->
|
||||||
|
<a href="#" id="theme-toggle-mobile" class="text-white hover:text-cobalt-200 ml-2" title="{{ _('Toggle dark mode') }}">
|
||||||
|
<span class="icon-moon">🌙</span>
|
||||||
|
<span class="icon-sun" style="display:none;">☀️</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile Navigation Links -->
|
||||||
|
<a href="/" class="block px-3 py-2 text-white rounded hover:bg-cobalt-600 transition-colors">
|
||||||
|
{{ _('Home') }}
|
||||||
|
</a>
|
||||||
|
<a href="/add_tablet" class="block px-3 py-2 text-white rounded hover:bg-cobalt-600 transition-colors">
|
||||||
|
{{ _('Add Tablet') }}
|
||||||
|
</a>
|
||||||
|
<a href="/add_user" class="block px-3 py-2 text-white rounded hover:bg-cobalt-600 transition-colors">
|
||||||
|
{{ _('Add User') }}
|
||||||
|
</a>
|
||||||
|
<a href="/loan_tablet" class="block px-3 py-2 text-white rounded hover:bg-cobalt-600 transition-colors">
|
||||||
|
{{ _('Loan Tablet') }}
|
||||||
|
</a>
|
||||||
|
<a href="/history" class="block px-3 py-2 text-white rounded hover:bg-cobalt-600 transition-colors">
|
||||||
|
{{ _('History') }}
|
||||||
|
</a>
|
||||||
|
<a href="/user_loans" class="block px-3 py-2 text-white rounded hover:bg-cobalt-600 transition-colors">
|
||||||
|
{{ _('User Loans') }}
|
||||||
|
</a>
|
||||||
|
<a href="/students" class="block px-3 py-2 text-white rounded hover:bg-cobalt-600 transition-colors">
|
||||||
|
{{ _('Students') }}
|
||||||
|
</a>
|
||||||
|
<a href="/non_loanable_devices" class="block px-3 py-2 text-white rounded hover:bg-cobalt-600 transition-colors">
|
||||||
|
{{ _('Non-Loanable Devices') }}
|
||||||
|
</a>
|
||||||
|
<a href="/project_management" class="block px-3 py-2 text-white rounded hover:bg-cobalt-600 transition-colors">
|
||||||
|
{{ _('Project Management') }}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -346,8 +409,8 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<!-- Navigation -->
|
<!-- Navigation (Desktop only) -->
|
||||||
<nav class="bg-cobalt-600 rounded-lg shadow-md mb-6" aria-label="Main navigation">
|
<nav class="hidden sm:block bg-cobalt-600 rounded-lg shadow-md mb-6" aria-label="Main navigation">
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex flex-wrap justify-center sm:justify-start gap-1 sm:gap-2 py-3">
|
<div class="flex flex-wrap justify-center sm:justify-start gap-1 sm:gap-2 py-3">
|
||||||
<a href="/"
|
<a href="/"
|
||||||
|
|
@ -404,6 +467,33 @@
|
||||||
|
|
||||||
<!-- Theme Toggle Script -->
|
<!-- Theme Toggle Script -->
|
||||||
<script>
|
<script>
|
||||||
|
// Mobile menu toggle
|
||||||
|
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
|
||||||
|
const mobileMenu = document.getElementById('mobile-menu');
|
||||||
|
const menuIconHamburger = document.getElementById('menu-icon-hamburger');
|
||||||
|
const menuIconClose = document.getElementById('menu-icon-close');
|
||||||
|
|
||||||
|
if (mobileMenuBtn && mobileMenu) {
|
||||||
|
mobileMenuBtn.addEventListener('click', function() {
|
||||||
|
const isOpen = mobileMenu.classList.toggle('hidden');
|
||||||
|
mobileMenuBtn.setAttribute('aria-expanded', !isOpen);
|
||||||
|
|
||||||
|
// Toggle icons
|
||||||
|
menuIconHamburger.classList.toggle('hidden');
|
||||||
|
menuIconClose.classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close menu when clicking a link
|
||||||
|
mobileMenu.querySelectorAll('a').forEach(link => {
|
||||||
|
link.addEventListener('click', function() {
|
||||||
|
mobileMenu.classList.add('hidden');
|
||||||
|
mobileMenuBtn.setAttribute('aria-expanded', false);
|
||||||
|
menuIconHamburger.classList.remove('hidden');
|
||||||
|
menuIconClose.classList.add('hidden');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Check for saved theme preference or default to light mode
|
// Check for saved theme preference or default to light mode
|
||||||
const savedTheme = localStorage.getItem('theme');
|
const savedTheme = localStorage.getItem('theme');
|
||||||
if (savedTheme === 'dark') {
|
if (savedTheme === 'dark') {
|
||||||
|
|
@ -414,23 +504,36 @@
|
||||||
localStorage.removeItem('theme');
|
localStorage.removeItem('theme');
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('theme-toggle').addEventListener('click', function(e) {
|
// Theme toggle for desktop
|
||||||
e.preventDefault();
|
const themeToggle = document.getElementById('theme-toggle');
|
||||||
const isDark = document.body.classList.toggle('dark-mode');
|
if (themeToggle) {
|
||||||
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
themeToggle.addEventListener('click', function(e) {
|
||||||
updateToggleIcon(isDark);
|
e.preventDefault();
|
||||||
});
|
const isDark = document.body.classList.toggle('dark-mode');
|
||||||
|
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
||||||
|
updateToggleIcon(isDark);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme toggle for mobile
|
||||||
|
const themeToggleMobile = document.getElementById('theme-toggle-mobile');
|
||||||
|
if (themeToggleMobile) {
|
||||||
|
themeToggleMobile.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const isDark = document.body.classList.toggle('dark-mode');
|
||||||
|
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
||||||
|
updateToggleIcon(isDark);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateToggleIcon(isDark) {
|
function updateToggleIcon(isDark) {
|
||||||
const iconMoon = document.querySelector('.icon-moon');
|
// Update all theme toggle icons (desktop and mobile)
|
||||||
const iconSun = document.querySelector('.icon-sun');
|
document.querySelectorAll('.icon-moon').forEach(icon => {
|
||||||
if (isDark) {
|
icon.style.display = isDark ? 'none' : 'inline';
|
||||||
iconMoon.style.display = 'none';
|
});
|
||||||
iconSun.style.display = 'inline';
|
document.querySelectorAll('.icon-sun').forEach(icon => {
|
||||||
} else {
|
icon.style.display = isDark ? 'inline' : 'none';
|
||||||
iconMoon.style.display = 'inline';
|
});
|
||||||
iconSun.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue