111 lines
3.9 KiB
TypeScript
111 lines
3.9 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { mergeClasses as cn } from '../../styles/designSystem';
|
|
import type { Language } from '../../types/i18n';
|
|
|
|
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-banquise-blue-light/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-banquise-blue/10 focus:bg-banquise-blue/10',
|
|
'focus:outline-none',
|
|
isSelected
|
|
? 'text-banquise-blue-dark font-semibold bg-banquise-blue/10'
|
|
: 'text-gray-700 hover:text-banquise-blue-dark'
|
|
)}
|
|
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-banquise-blue" 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>
|
|
);
|
|
};
|