website-front/banquise-website/components/ui/ModernLanguageSwitcher.tsx
Sacha VAUDEY 30fd66f2c9
Some checks failed
Build / build-check (pull_request) Failing after 1m33s
Major UI update
2025-09-13 22:55:24 +02:00

115 lines
3.9 KiB
TypeScript

import React, { useState } from 'react';
import type { Language } from '@/types/i18n';
// Fonction utilitaire simple pour combiner les classes
const cn = (...classes: (string | undefined | null | false)[]): string => {
return classes.filter(Boolean).join(' ');
};
interface ModernLanguageSwitcherProps {
currentLanguage: Language;
onLanguageChange: (language: Language) => void;
availableLanguages: Language[];
}
export const ModernLanguageSwitcher: React.FC<ModernLanguageSwitcherProps> = ({
currentLanguage,
onLanguageChange,
availableLanguages
}) => {
const [isOpen, setIsOpen] = useState(false);
const languageConfig: Record<Language, { name: string; flag: string; nativeName: string }> = {
fr: { name: 'Français', flag: '🇫🇷', nativeName: 'FR' },
en: { name: 'English', flag: '🇬🇧', nativeName: 'EN' },
};
const currentConfig = languageConfig[currentLanguage];
return (
<div className="relative">
{/* Trigger Button */}
<button
onClick={() => setIsOpen(!isOpen)}
className={cn(
'flex items-center space-x-2 px-3 py-2 rounded-lg transition-all duration-200',
'bg-white/10 hover:bg-white/20 border border-white/20 hover:border-white/30',
'text-white text-sm font-medium',
'focus:outline-none focus:ring-2 focus:ring-blue-400/50',
'group'
)}
aria-expanded={isOpen}
aria-haspopup="listbox"
>
<span className="text-lg">{currentConfig.flag}</span>
<span className="hidden sm:inline">{currentConfig.nativeName}</span>
{/* Chevron Icon */}
<svg
className={cn(
'w-4 h-4 transition-transform duration-200',
isOpen ? 'rotate-180' : 'rotate-0'
)}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* Dropdown Menu */}
{isOpen && (
<>
{/* Backdrop */}
<div
className="fixed inset-0 z-10"
onClick={() => setIsOpen(false)}
/>
{/* Menu */}
<div className={cn(
'absolute right-0 top-full mt-2 z-20',
'bg-white/95 backdrop-blur-xl rounded-xl shadow-2xl border border-white/20',
'min-w-[140px] py-2',
'animate-slideUp'
)}>
{availableLanguages.map((lang) => {
const config = languageConfig[lang];
const isSelected = lang === currentLanguage;
return (
<button
key={lang}
onClick={() => {
onLanguageChange(lang);
setIsOpen(false);
}}
className={cn(
'w-full flex items-center space-x-3 px-4 py-2.5 text-sm transition-all duration-200',
'hover:bg-blue-600/10 focus:bg-blue-600/10',
'focus:outline-none',
isSelected
? 'text-blue-800 font-semibold bg-blue-600/10'
: 'text-gray-700 hover:text-blue-800'
)}
role="option"
aria-selected={isSelected}
>
<span className="text-lg">{config.flag}</span>
<span className="flex-1 text-left">{config.name}</span>
{isSelected && (
<svg className="w-4 h-4 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
)}
</button>
);
})}
</div>
</>
)}
</div>
);
};