175 lines
4.9 KiB
TypeScript
175 lines
4.9 KiB
TypeScript
import React from 'react';
|
|
import { mergeClasses as cn } from '@/lib/styles/designSystem';
|
|
import type { Translation } from '@/types/i18n';
|
|
|
|
interface NavLinksProps {
|
|
translations: Translation['navigation'];
|
|
scrolled: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
interface NavLinkProps {
|
|
href: string;
|
|
children: React.ReactNode;
|
|
isActive?: boolean;
|
|
onClick?: () => void;
|
|
}
|
|
|
|
const NavLink: React.FC<NavLinkProps> = ({ href, children, isActive = false, onClick }) => {
|
|
return (
|
|
<a
|
|
href={href}
|
|
onClick={onClick}
|
|
className={cn(
|
|
'relative px-4 py-2 text-sm font-medium transition-all duration-300 rounded-lg group',
|
|
'hover:text-white focus:outline-none focus:ring-2 focus:ring-banquise-blue-light/50',
|
|
isActive
|
|
? 'text-white bg-white/20 shadow-lg'
|
|
: 'text-white/80 hover:bg-white/10'
|
|
)}
|
|
>
|
|
<span className="relative z-10">{children}</span>
|
|
|
|
{/* Hover effect */}
|
|
<div className={cn(
|
|
'absolute inset-0 rounded-lg bg-gradient-to-r from-banquise-blue-light/20 to-banquise-blue/20',
|
|
'opacity-0 group-hover:opacity-100 transition-all duration-300 scale-95 group-hover:scale-100'
|
|
)} />
|
|
|
|
{/* Active indicator */}
|
|
{isActive && (
|
|
<div className="absolute bottom-0 left-1/2 transform -translate-x-1/2 w-6 h-0.5 bg-banquise-blue-lightest rounded-full" />
|
|
)}
|
|
</a>
|
|
);
|
|
};
|
|
|
|
export const NavLinks: React.FC<NavLinksProps> = ({ translations, className }) => {
|
|
const [activeSection, setActiveSection] = React.useState<string>('home');
|
|
|
|
// Observer pour détecter la section active
|
|
React.useEffect(() => {
|
|
const handleScroll = () => {
|
|
const scrollPosition = window.scrollY;
|
|
const windowHeight = window.innerHeight;
|
|
|
|
// Si on est en haut de la page (moins de 100px du haut), on active "home"
|
|
if (scrollPosition < 100) {
|
|
setActiveSection('home');
|
|
return;
|
|
}
|
|
|
|
// Sinon, on utilise l'intersection observer logic
|
|
const sections = ['home', 'services', 'about'];
|
|
let currentSection = 'home';
|
|
|
|
sections.forEach((sectionId) => {
|
|
const element = document.getElementById(sectionId);
|
|
if (element) {
|
|
const rect = element.getBoundingClientRect();
|
|
const sectionTop = rect.top + scrollPosition;
|
|
|
|
// Si la section est visible dans le viewport
|
|
if (scrollPosition >= sectionTop - windowHeight / 3) {
|
|
currentSection = sectionId;
|
|
}
|
|
}
|
|
});
|
|
|
|
setActiveSection(currentSection);
|
|
};
|
|
|
|
// Écouter le scroll
|
|
window.addEventListener('scroll', handleScroll);
|
|
// Appeler une fois au chargement
|
|
handleScroll();
|
|
|
|
return () => {
|
|
window.removeEventListener('scroll', handleScroll);
|
|
};
|
|
}, []);
|
|
|
|
// Observer pour détecter la section active avec IntersectionObserver (fallback)
|
|
React.useEffect(() => {
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting && window.scrollY > 100) {
|
|
setActiveSection(entry.target.id);
|
|
}
|
|
});
|
|
},
|
|
{
|
|
threshold: 0.3,
|
|
rootMargin: '-100px 0px -100px 0px'
|
|
}
|
|
);
|
|
|
|
const sections = ['home', 'services', 'about'];
|
|
sections.forEach((id) => {
|
|
const element = document.getElementById(id);
|
|
if (element) observer.observe(element);
|
|
});
|
|
|
|
return () => observer.disconnect();
|
|
}, []);
|
|
|
|
const handleNavClick = (sectionId: string) => {
|
|
if (sectionId === 'home') {
|
|
// Scroll to top for home section
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth'
|
|
});
|
|
} else if (sectionId === 'contact') {
|
|
// Open email client for contact
|
|
window.location.href = 'mailto:contact@la-banquise.fr';
|
|
} else {
|
|
// Scroll to specific section
|
|
const element = document.getElementById(sectionId);
|
|
if (element) {
|
|
element.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
return (
|
|
<nav className={cn('hidden md:flex items-center space-x-1', className)}>
|
|
<NavLink
|
|
href="#home"
|
|
isActive={activeSection === 'home'}
|
|
onClick={() => handleNavClick('home')}
|
|
>
|
|
{translations.home}
|
|
</NavLink>
|
|
|
|
<NavLink
|
|
href="#services"
|
|
isActive={activeSection === 'services'}
|
|
onClick={() => handleNavClick('services')}
|
|
>
|
|
{translations.services}
|
|
</NavLink>
|
|
|
|
<NavLink
|
|
href="#about"
|
|
isActive={activeSection === 'about'}
|
|
onClick={() => handleNavClick('about')}
|
|
>
|
|
{translations.about}
|
|
</NavLink>
|
|
|
|
<NavLink
|
|
href="mailto:contact@la-banquise.fr"
|
|
isActive={false}
|
|
onClick={() => handleNavClick('contact')}
|
|
>
|
|
{translations.contact}
|
|
</NavLink>
|
|
</nav>
|
|
);
|
|
};
|