Sacha VAUDEY 796c7d1c21
Some checks failed
Build and Test / Classic Build (pull_request) Failing after 39s
Build and Test / Docker Build (pull_request) Has been skipped
optimize mobile navbar
2025-09-14 14:51:53 +02:00

161 lines
4.6 KiB
TypeScript

import React, { useEffect } from 'react';
import { Logo } from './navbar/Logo';
import { useTranslation } from '@/lib/hooks/useTranslation';
import { URLS } from '@/lib/config/constants';
import { cn, createNavClickHandler } from '@/lib/utils';
import { DiscordLogo } from '@/components/ui/DiscordLogo';
import type { Translation } from '@/types/i18n';
interface MobileMenuProps {
isOpen: boolean;
onClose: () => void;
translations: Translation['navigation'];
}
interface MobileNavItemProps {
title: string;
href: string;
isExternal?: boolean;
onClick?: () => void;
isDiscord?: boolean;
}
const MobileNavItem: React.FC<MobileNavItemProps> = ({
title,
href,
isExternal = false,
onClick,
isDiscord = false
}) => {
const handleClick = (e: React.MouseEvent) => {
if (onClick) {
e.preventDefault();
onClick();
}
};
return (
<a
href={href}
onClick={handleClick}
className={cn(
'flex items-center justify-between px-4 py-3 rounded-lg',
'text-white hover:bg-white/10 transition-colors duration-200',
'border-b border-white/10 last:border-b-0'
)}
target={isExternal ? '_blank' : undefined}
rel={isExternal ? 'noopener noreferrer' : undefined}
>
<div className="flex items-center space-x-3">
{isDiscord && <DiscordLogo size="sm" className="text-[#5865F2]" />}
<span className="font-medium">{title}</span>
</div>
{isExternal && (
<svg className="w-4 h-4 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
)}
</a>
);
};
export const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose, translations }) => {
const { t } = useTranslation();
// Gérer le scroll du body
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'unset';
}
return () => {
document.body.style.overflow = 'unset';
};
}, [isOpen]);
// Gestionnaire de navigation
const handleNavClick = createNavClickHandler(onClose);
return (
<div className={cn(
'md:hidden fixed inset-0 z-[100] transition-opacity duration-300',
isOpen ? 'visible opacity-100' : 'invisible opacity-0'
)}>
{/* Overlay simple */}
<div
className="absolute inset-0 bg-black/50 backdrop-blur-sm"
onClick={onClose}
/>
{/* Menu Panel épuré */}
<div className={cn(
'absolute top-0 right-0 h-full w-72 max-w-[85vw]',
'bg-blue-900/95 backdrop-blur-lg shadow-xl',
'border-l border-white/10',
'transition-transform duration-300 ease-out',
isOpen ? 'translate-x-0' : 'translate-x-full'
)}>
{/* Header simple */}
<div className="flex items-center justify-between p-4 border-b border-white/10">
<Logo scrolled={false} />
<button
className="p-2 rounded-lg hover:bg-white/10 transition-colors"
onClick={onClose}
aria-label="Fermer le menu"
>
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
{/* Navigation simple */}
<div className="p-4 space-y-2">
<MobileNavItem
title={translations.home}
href="#home"
onClick={() => handleNavClick('home')}
/>
<MobileNavItem
title={translations.services}
href="#services"
onClick={() => handleNavClick('services')}
/>
<MobileNavItem
title={translations.about}
href="#about"
onClick={() => handleNavClick('about')}
/>
<MobileNavItem
title={translations.contact}
href="mailto:contact@la-banquise.fr"
onClick={() => handleNavClick('contact')}
/>
{/* Séparateur simple */}
<div className="border-t border-white/10 my-4" />
<MobileNavItem
title="Discord"
href={URLS.social.discord}
isExternal={true}
isDiscord={true}
/>
<MobileNavItem
title="Se connecter"
href={URLS.services.auth}
isExternal={true}
/>
</div>
</div>
</div>
);
};