feat: implement core application structure, UI components, internationalization, and database seeding.
This commit is contained in:
67
src/components/LanguageSwitcher.tsx
Normal file
67
src/components/LanguageSwitcher.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useLanguage } from '@/providers/LanguageProvider';
|
||||
import { SupportedLanguage } from '@/lib/i18n/translations';
|
||||
import { Globe, ChevronDown } from 'lucide-react';
|
||||
|
||||
const languages: { code: SupportedLanguage; label: string; flag: string }[] = [
|
||||
{ code: 'fr', label: 'Français', flag: 'https://flagcdn.com/fr.svg' },
|
||||
{ code: 'en', label: 'English', flag: 'https://flagcdn.com/gb.svg' },
|
||||
{ code: 'es', label: 'Español', flag: 'https://flagcdn.com/es.svg' },
|
||||
{ code: 'de', label: 'Deutsch', flag: 'https://flagcdn.com/de.svg' },
|
||||
];
|
||||
|
||||
export const LanguageSwitcher: React.FC = () => {
|
||||
const { language, setLanguage } = useLanguage();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const currentLang = languages.find(l => l.code === language) || languages[0];
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="relative" ref={dropdownRef}>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium hover:bg-slate-100 dark:hover:bg-slate-800 text-slate-600 dark:text-slate-300 transition-colors"
|
||||
title="Changer de langue"
|
||||
>
|
||||
<img src={currentLang.flag} alt={currentLang.label} className="w-5 h-3.5 object-cover rounded-[2px] shadow-sm" />
|
||||
<span className="hidden sm:inline font-bold">{currentLang.code.toUpperCase()}</span>
|
||||
<ChevronDown size={14} className={`transition-transform text-slate-400 ${isOpen ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute right-0 mt-2 w-40 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl shadow-lg z-50 overflow-hidden">
|
||||
<div className="py-1">
|
||||
{languages.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
onClick={() => {
|
||||
setLanguage(lang.code);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
className={`w-full text-left px-4 py-2 text-sm flex items-center gap-3 hover:bg-slate-50 dark:hover:bg-slate-700 transition-colors
|
||||
${language === lang.code ? 'bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 font-medium' : 'text-slate-700 dark:text-slate-300'}
|
||||
`}
|
||||
>
|
||||
<img src={lang.flag} alt={lang.label} className="w-5 h-3.5 object-cover rounded-[2px] shadow-sm" />
|
||||
{lang.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user