astro_avtourist/frontend/src/components/layout/header/MobileMenu.astro
2026-04-04 16:18:55 +05:00

497 lines
No EOL
14 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
import { NAV_LINKS, COMPANY } from '@constants';
---
<div class="mobile-menu-overlay" id="mobile-menu-overlay">
<div class="mobile-menu-container">
<div class="mobile-menu">
<!-- Шапка меню - убрана кнопка входа -->
<div class="mobile-menu-header">
<a href="/" class="mobile-logo">
<div class="logo-container">
<div class="logo-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" class="shield">
<path d="M24 4L6 10V22C6 34 24 44 24 44C24 44 42 34 42 22V10L24 4Z" fill="#eac26e" stroke="#eac26e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="24" cy="24" r="8" fill="#1e3050" opacity="0.9"/>
<circle cx="24" cy="24" r="3" fill="#eac26e"/>
<line x1="24" y1="16" x2="24" y2="20" stroke="#1e3050" stroke-width="2"/>
<line x1="24" y1="28" x2="24" y2="32" stroke="#1e3050" stroke-width="2"/>
<line x1="16" y1="24" x2="20" y2="24" stroke="#1e3050" stroke-width="2"/>
<line x1="28" y1="24" x2="32" y2="24" stroke="#1e3050" stroke-width="2"/>
</svg>
</div>
<div class="logo-text">
<span class="logo-name">Автоюрист</span>
<span class="logo-number">086</span>
</div>
</div>
</a>
<button class="mobile-menu-close" id="mobile-menu-close" aria-label="Закрыть меню">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<!-- Навигация -->
<nav class="mobile-nav">
<ul class="mobile-nav-list">
{NAV_LINKS.map((link, index) => {
const isActive = Astro.url.pathname === link.url;
return (
<li style={`--item-index: ${index}`} class="mobile-nav-item">
<a href={link.url} class={`mobile-nav-link ${isActive ? 'active' : ''}`}>
<span class="mobile-nav-text">
<span class="mobile-nav-number">{String(index + 1).padStart(2, '0')}</span>
<span class="mobile-nav-name">{link.name}</span>
</span>
<div class="mobile-nav-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</div>
</a>
<div class="mobile-nav-ripple"></div>
</li>
);
})}
</ul>
</nav>
<!-- Контакты -->
<div class="mobile-contacts">
<div class="mobile-contact-card">
<a href={`tel:${COMPANY.phone}`} class="mobile-phone">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="phone-icon">
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>
</svg>
<span>{COMPANY.phone}</span>
</a>
<span class="contact-label">Ежедневно 9:0021:00</span>
</div>
<!-- Кнопка входа ОСТАВЛЕНА ВНИЗУ -->
<a href="/login" class="mobile-login-btn">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path>
<polyline points="10 17 15 12 10 7"></polyline>
<line x1="15" y1="12" x2="3" y2="12"></line>
</svg>
<span>Вход в личный кабинет</span>
</a>
</div>
</div>
</div>
</div>
<style>
/* Оверлей */
.mobile-menu-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(3, 21, 41, 0.98);
backdrop-filter: blur(12px);
z-index: 2000;
opacity: 0;
visibility: hidden;
transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1),
visibility 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.mobile-menu-overlay.active {
opacity: 1;
visibility: visible;
}
.mobile-menu-container {
position: absolute;
top: 0;
right: 0;
width: 100%;
max-width: 450px;
height: 100%;
transform: translateX(100%);
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.mobile-menu-overlay.active .mobile-menu-container {
transform: translateX(0);
}
.mobile-menu {
position: relative;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0a2540 0%, #031529 100%);
display: flex;
flex-direction: column;
overflow-y: auto;
overflow-x: hidden;
box-shadow: -5px 0 30px rgba(0, 0, 0, 0.3);
}
.mobile-menu-header {
padding: 1.5rem 1.5rem 1rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
margin-bottom: 1rem;
}
.mobile-logo {
text-decoration: none;
transition: transform 0.3s ease;
}
.mobile-logo:hover {
transform: scale(1.05);
}
.logo-container {
display: flex;
align-items: center;
gap: 0.75rem;
}
.logo-icon {
width: 42px;
height: 42px;
}
.shield {
width: 100%;
height: 100%;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
}
.logo-text {
display: flex;
flex-direction: column;
line-height: 1.1;
}
.logo-name {
font-size: 1rem;
font-weight: 700;
color: #ffffff;
letter-spacing: -0.3px;
}
.logo-number {
font-size: 1.1rem;
font-weight: 900;
color: #eac26e;
letter-spacing: -0.5px;
}
.mobile-menu-close {
background: rgba(255, 255, 255, 0.1);
border: none;
color: #ffffff;
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.mobile-menu-close:hover {
background: rgba(234, 194, 110, 0.2);
transform: rotate(90deg);
}
.mobile-menu-close:active {
transform: rotate(90deg) scale(0.95);
}
.mobile-nav {
flex: 1;
padding: 0 1.5rem;
}
.mobile-nav-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.mobile-nav-item {
position: relative;
opacity: 0;
transform: translateX(30px);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
transition-delay: calc(0.05s * var(--item-index, 0));
}
.mobile-menu-overlay.active .mobile-nav-item {
opacity: 1;
transform: translateX(0);
}
.mobile-nav-link {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 1rem;
color: #ffffff;
text-decoration: none;
font-size: 1.1rem;
font-weight: 500;
border-radius: 12px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
z-index: 1;
}
.mobile-nav-text {
display: flex;
align-items: center;
gap: 1rem;
}
.mobile-nav-number {
font-size: 0.85rem;
color: #eac26e;
font-weight: 700;
opacity: 0.6;
transition: opacity 0.3s ease;
}
.mobile-nav-name {
transition: transform 0.3s ease;
}
.mobile-nav-icon {
opacity: 0;
transform: translateX(-10px);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
color: #eac26e;
}
.mobile-nav-link:hover {
background: rgba(234, 194, 110, 0.1);
transform: translateX(8px);
padding-left: 1.5rem;
}
.mobile-nav-link:hover .mobile-nav-number {
opacity: 1;
}
.mobile-nav-link:hover .mobile-nav-name {
transform: translateX(4px);
}
.mobile-nav-link:hover .mobile-nav-icon {
opacity: 1;
transform: translateX(0);
}
.mobile-nav-link.active {
background: linear-gradient(90deg, rgba(234, 194, 110, 0.15), transparent);
border-left: 3px solid #eac26e;
}
.mobile-nav-link.active .mobile-nav-number {
opacity: 1;
color: #eac26e;
}
.mobile-nav-ripple {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: radial-gradient(circle, rgba(234, 194, 110, 0.3), transparent);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
pointer-events: none;
}
.mobile-nav-link:active + .mobile-nav-ripple {
width: 200px;
height: 200px;
}
/* Контакты - кнопка входа ВНИЗУ */
.mobile-contacts {
padding: 1.5rem;
margin-top: auto;
border-top: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
flex-direction: column;
gap: 1rem;
}
.mobile-contact-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 1rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
transition: all 0.3s ease;
}
.mobile-contact-card:hover {
background: rgba(255, 255, 255, 0.08);
transform: translateY(-2px);
}
.mobile-phone {
display: flex;
align-items: center;
gap: 0.5rem;
color: #eac26e;
font-size: 1.1rem;
font-weight: 700;
text-decoration: none;
transition: all 0.3s ease;
}
.mobile-phone:hover {
color: #f0d68a;
}
.phone-icon {
flex-shrink: 0;
color: #eac26e;
}
.contact-label {
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.6);
}
/* Кнопка входа ВНИЗУ */
.mobile-login-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 1rem 1.5rem;
background: linear-gradient(135deg, #eac26e 0%, #d4af37 100%);
color: #1e3050;
border-radius: 12px;
text-decoration: none;
font-weight: 700;
font-size: 0.95rem;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(234, 194, 110, 0.3);
}
.mobile-login-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 25px rgba(234, 194, 110, 0.4);
}
.mobile-login-btn:active {
transform: translateY(0);
}
.mobile-menu::-webkit-scrollbar {
width: 4px;
}
.mobile-menu::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.mobile-menu::-webkit-scrollbar-thumb {
background: rgba(234, 194, 110, 0.5);
border-radius: 4px;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
.mobile-nav-link.active .mobile-nav-number {
animation: pulse 2s infinite;
}
</style>
// Внутри MobileMenu.astro
<script>
document.addEventListener('DOMContentLoaded', () => {
const overlay = document.getElementById('mobile-menu-overlay');
const closeBtn = document.getElementById('mobile-menu-close');
const mobileNavLinks = document.querySelectorAll('.mobile-nav-link');
// Находим кнопку бургера из Header, чтобы сбрасывать её состояние
const burgerBtn = document.getElementById('burger-btn');
function openMenu() {
overlay?.classList.add('active');
burgerBtn?.classList.add('active'); // Синхронизируем бургер
document.body.style.overflow = 'hidden';
}
function closeMenu() {
overlay?.classList.remove('active');
// ВАЖНО: Находим кнопку бургера и убираем у неё класс "active"
// чтобы крестик снова стал гамбургером
burgerBtn?.classList.remove('active');
document.body.style.overflow = '';
document.body.style.position = '';
document.body.style.width = '';
}
// Закрытие по кнопке "Крестик" внутри меню
closeBtn?.addEventListener('click', closeMenu);
// Закрытие при клике на темную область (оверлей)
overlay?.addEventListener('click', (e) => {
if (e.target === overlay) closeMenu();
});
// Закрытие по клавише Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeMenu();
});
// Закрытие при клике на пункты меню
mobileNavLinks?.forEach(link => {
link.addEventListener('click', () => {
setTimeout(closeMenu, 150);
});
});
// Логика свайпа для закрытия
let touchStartX = 0;
const menuContainer = document.querySelector('.mobile-menu-container');
menuContainer?.addEventListener('touchstart', (e) => {
touchStartX = (e as TouchEvent).changedTouches[0].screenX;
}, { passive: true });
menuContainer?.addEventListener('touchend', (e) => {
let touchEndX = (e as TouchEvent).changedTouches[0].screenX;
if (touchEndX - touchStartX < -50) closeMenu(); // Свайп влево
});
});
</script>