Mobile user menu as bottom sheet instead of dropdown
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
On mobile (≤768px), user menu now slides up from bottom as a sheet: - Full name displayed in header with avatar - Larger touch targets (14px padding) - Dark overlay behind (tap to close) - Smooth slide-up animation (translateY) - Safe area inset for iPhone notch/home indicator - Handle bar at top (standard bottom sheet pattern) Desktop behavior unchanged (absolute dropdown). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b28d3c1879
commit
abd2a8a95c
@ -946,6 +946,73 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Mobile: bottom sheet */
|
||||
@media (max-width: 768px) {
|
||||
.user-menu-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.4);
|
||||
z-index: 9998;
|
||||
}
|
||||
.user-menu-overlay.show { display: block; }
|
||||
|
||||
.user-menu {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-width: 100%;
|
||||
border-radius: 16px 16px 0 0;
|
||||
border: none;
|
||||
box-shadow: 0 -4px 20px rgba(0,0,0,0.15);
|
||||
z-index: 9999;
|
||||
padding-bottom: env(safe-area-inset-bottom, 16px);
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.25s ease-out;
|
||||
}
|
||||
.user-menu.show {
|
||||
display: block;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.user-menu-handle {
|
||||
display: block;
|
||||
width: 36px;
|
||||
height: 4px;
|
||||
background: #d1d5db;
|
||||
border-radius: 2px;
|
||||
margin: 10px auto 4px;
|
||||
}
|
||||
.user-menu-mobile-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 8px 16px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.user-menu-mobile-header .user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.user-menu-mobile-name {
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.user-menu-item {
|
||||
padding: 14px 16px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 769px) {
|
||||
.user-menu-overlay { display: none !important; }
|
||||
.user-menu-handle { display: none; }
|
||||
.user-menu-mobile-header { display: none; }
|
||||
}
|
||||
|
||||
.user-menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -1552,7 +1619,13 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="user-menu-overlay" id="userMenuOverlay" onclick="closeUserMenu()"></div>
|
||||
<div class="user-menu" id="userMenu">
|
||||
<div class="user-menu-handle"></div>
|
||||
<div class="user-menu-mobile-header">
|
||||
<span class="user-avatar">{{ current_user.name[:1].upper() }}</span>
|
||||
<span class="user-menu-mobile-name">{{ current_user.name }}</span>
|
||||
</div>
|
||||
<a href="{{ url_for('dashboard') }}" class="user-menu-item">
|
||||
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
|
||||
@ -2002,23 +2075,36 @@
|
||||
event.stopPropagation();
|
||||
const userDropdown = document.querySelector('.user-dropdown');
|
||||
const userMenu = document.getElementById('userMenu');
|
||||
const overlay = document.getElementById('userMenuOverlay');
|
||||
|
||||
// Close notifications if open
|
||||
const notificationsMenu = document.getElementById('notificationsMenu');
|
||||
if (notificationsMenu) notificationsMenu.classList.remove('show');
|
||||
|
||||
userDropdown.classList.toggle('active');
|
||||
userMenu.classList.toggle('show');
|
||||
const isOpen = userMenu.classList.contains('show');
|
||||
if (isOpen) {
|
||||
closeUserMenu();
|
||||
} else {
|
||||
userDropdown.classList.add('active');
|
||||
userMenu.classList.add('show');
|
||||
if (overlay) overlay.classList.add('show');
|
||||
}
|
||||
}
|
||||
|
||||
// Close user menu when clicking outside
|
||||
document.addEventListener('click', function(event) {
|
||||
function closeUserMenu() {
|
||||
const userDropdown = document.querySelector('.user-dropdown');
|
||||
const userMenu = document.getElementById('userMenu');
|
||||
const overlay = document.getElementById('userMenuOverlay');
|
||||
if (userDropdown) userDropdown.classList.remove('active');
|
||||
if (userMenu) userMenu.classList.remove('show');
|
||||
if (overlay) overlay.classList.remove('show');
|
||||
}
|
||||
|
||||
if (userDropdown && userMenu && !userDropdown.contains(event.target)) {
|
||||
userDropdown.classList.remove('active');
|
||||
userMenu.classList.remove('show');
|
||||
// Close user menu when clicking outside (desktop)
|
||||
document.addEventListener('click', function(event) {
|
||||
const userDropdown = document.querySelector('.user-dropdown');
|
||||
if (userDropdown && !userDropdown.contains(event.target)) {
|
||||
closeUserMenu();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user