2026-03-31 22:53:39 +05:00
|
|
|
|
---
|
|
|
|
|
|
export interface Props {
|
2026-04-02 20:38:33 +05:00
|
|
|
|
links?: Array<{ name: string; url: string }>;
|
|
|
|
|
|
class?: string;
|
2026-03-31 22:53:39 +05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const defaultLinks = [
|
2026-04-02 20:38:33 +05:00
|
|
|
|
{ name: 'Главная', url: '/' },
|
2026-03-31 22:53:39 +05:00
|
|
|
|
{ name: 'Услуги', url: '/services' },
|
|
|
|
|
|
{ name: 'Кейсы', url: '/cases' },
|
|
|
|
|
|
{ name: 'Блог', url: '/blog' },
|
|
|
|
|
|
{ name: 'Отзывы', url: '/reviews' },
|
|
|
|
|
|
{ name: 'Контакты', url: '/contacts' },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
2026-04-02 20:38:33 +05:00
|
|
|
|
const {
|
|
|
|
|
|
links = defaultLinks,
|
|
|
|
|
|
class: className = '',
|
2026-03-31 22:53:39 +05:00
|
|
|
|
}: Props = Astro.props;
|
2026-04-02 20:38:33 +05:00
|
|
|
|
|
|
|
|
|
|
// Определяем текущую страницу
|
|
|
|
|
|
const currentPath = Astro.url.pathname;
|
|
|
|
|
|
const isHomePage = currentPath === '/' || currentPath === '';
|
|
|
|
|
|
|
|
|
|
|
|
// Фильтруем ссылки: убираем "Главная" если мы на главной странице
|
|
|
|
|
|
const filteredLinks = isHomePage
|
|
|
|
|
|
? links.filter(link => link.url !== '/')
|
|
|
|
|
|
: links;
|
2026-03-31 22:53:39 +05:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
<nav class={className}>
|
|
|
|
|
|
<ul class="nav-list">
|
2026-04-02 20:38:33 +05:00
|
|
|
|
{filteredLinks.map((link, index) => {
|
|
|
|
|
|
const isActive = currentPath === link.url ||
|
|
|
|
|
|
(link.url !== '/' && currentPath.startsWith(link.url));
|
|
|
|
|
|
return (
|
|
|
|
|
|
<li class="nav-item" style={{ '--item-index': index } as any}>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href={link.url}
|
|
|
|
|
|
class={`nav-link ${isActive ? 'active' : ''}`}
|
|
|
|
|
|
data-text={link.name}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span class="nav-link-text">{link.name}</span>
|
|
|
|
|
|
<span class="nav-indicator"></span>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
)})}
|
2026-03-31 22:53:39 +05:00
|
|
|
|
</ul>
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
.nav-list {
|
|
|
|
|
|
display: flex;
|
2026-04-02 20:38:33 +05:00
|
|
|
|
gap: 0.5rem;
|
2026-03-31 22:53:39 +05:00
|
|
|
|
list-style: none;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
2026-04-02 20:38:33 +05:00
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-item {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
perspective: 500px;
|
|
|
|
|
|
/* Убираем анимацию появления */
|
2026-03-31 22:53:39 +05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-link {
|
2026-04-02 20:38:33 +05:00
|
|
|
|
position: relative;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 0.6rem 1.2rem;
|
|
|
|
|
|
/* Цвет текста как в логотипе (logo-name) */
|
|
|
|
|
|
color: #1e3050;
|
2026-03-31 22:53:39 +05:00
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
font-size: 0.95rem;
|
2026-04-02 20:38:33 +05:00
|
|
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
letter-spacing: 0.3px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Текст с анимацией */
|
|
|
|
|
|
.nav-link-text {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
transition: transform 0.2s cubic-bezier(0.34, 1.2, 0.64, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Индикатор под пунктом */
|
|
|
|
|
|
.nav-indicator {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: -4px;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
width: 0;
|
|
|
|
|
|
height: 3px;
|
|
|
|
|
|
background: linear-gradient(90deg, #eac26e, #d4af37, #eac26e);
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
|
opacity: 0;
|
2026-03-31 22:53:39 +05:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-02 20:38:33 +05:00
|
|
|
|
/* Hover эффекты */
|
2026-03-31 22:53:39 +05:00
|
|
|
|
.nav-link:hover {
|
2026-04-02 20:38:33 +05:00
|
|
|
|
/* При наведении цвет становится насыщеннее */
|
|
|
|
|
|
color: #0a1a2e;
|
|
|
|
|
|
background: rgba(30, 48, 80, 0.05);
|
|
|
|
|
|
transform: translateY(-2px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-link:hover .nav-link-text {
|
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-item:hover .nav-indicator {
|
|
|
|
|
|
width: 70%;
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
animation: indicatorPulse 0.6s ease-out;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Активное состояние - улучшенная видимость */
|
|
|
|
|
|
.nav-link.active {
|
|
|
|
|
|
/* Используем темно-синий цвет вместо золотого для лучшей читаемости */
|
2026-03-31 22:53:39 +05:00
|
|
|
|
color: #1e3050;
|
2026-04-02 20:38:33 +05:00
|
|
|
|
background: linear-gradient(135deg, rgba(234, 194, 110, 0.25), rgba(234, 194, 110, 0.12));
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
/* Добавляем небольшую тень для выделения */
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-link.active .nav-indicator {
|
|
|
|
|
|
width: 80%;
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
background: linear-gradient(90deg, #eac26e, #f0d68a, #eac26e);
|
|
|
|
|
|
height: 3px;
|
|
|
|
|
|
bottom: -4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Дополнительный эффект для активного пункта - полужирный */
|
|
|
|
|
|
.nav-link.active .nav-link-text {
|
|
|
|
|
|
font-weight: 700;
|
2026-03-31 22:53:39 +05:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-02 20:38:33 +05:00
|
|
|
|
/* Ripple эффект при клике */
|
|
|
|
|
|
.nav-link::before {
|
|
|
|
|
|
content: '';
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-link:active::before {
|
|
|
|
|
|
width: 200px;
|
|
|
|
|
|
height: 200px;
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Анимация индикатора */
|
|
|
|
|
|
@keyframes indicatorPulse {
|
|
|
|
|
|
0% {
|
|
|
|
|
|
width: 0;
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
50% {
|
|
|
|
|
|
width: 85%;
|
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
|
}
|
|
|
|
|
|
100% {
|
|
|
|
|
|
width: 70%;
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Дополнительный эффект свечения при наведении */
|
|
|
|
|
|
.nav-link::after {
|
|
|
|
|
|
content: '';
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: -100%;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
background: linear-gradient(90deg, transparent, rgba(234, 194, 110, 0.1), transparent);
|
|
|
|
|
|
transition: left 0.5s ease;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-link:hover::after {
|
|
|
|
|
|
left: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Адаптив */
|
2026-03-31 22:53:39 +05:00
|
|
|
|
@media (max-width: 992px) {
|
|
|
|
|
|
.nav-list {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
2026-04-02 20:38:33 +05:00
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
// Добавляем дополнительные эффекты для активной ссылки
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
|
// Находим все активные ссылки
|
|
|
|
|
|
const activeLinks = document.querySelectorAll('.nav-link.active');
|
|
|
|
|
|
|
|
|
|
|
|
activeLinks.forEach(link => {
|
|
|
|
|
|
// Добавляем дополнительный класс для стилизации
|
|
|
|
|
|
link.classList.add('active-animated');
|
|
|
|
|
|
|
|
|
|
|
|
// Создаем эффект пульсации для активного индикатора
|
|
|
|
|
|
const indicator = link.querySelector('.nav-indicator');
|
|
|
|
|
|
if (indicator) {
|
|
|
|
|
|
(indicator as HTMLElement).style.animation = 'indicatorPulse 1s ease-out infinite';
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Добавляем поддержку touch-устройств
|
|
|
|
|
|
const navLinks = document.querySelectorAll('.nav-link');
|
|
|
|
|
|
|
|
|
|
|
|
navLinks.forEach(link => {
|
|
|
|
|
|
link.addEventListener('touchstart', function(this: HTMLElement) {
|
|
|
|
|
|
// Убираем эффект наведения на touch-устройствах
|
|
|
|
|
|
this.classList.add('touch-active');
|
|
|
|
|
|
|
|
|
|
|
|
// Убираем класс через таймаут
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.classList.remove('touch-active');
|
|
|
|
|
|
}, 300);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|