|
|
|
|
| import { useState, useRef, useEffect } from 'react'; |
| import { useI18n } from '@/lib/hooks/use-i18n'; |
| import { supportedLocales } from '@/lib/i18n'; |
| import { cn } from '@/lib/utils'; |
|
|
| interface LanguageSwitcherProps { |
| |
| onOpen?: () => void; |
| } |
|
|
| export function LanguageSwitcher({ onOpen }: LanguageSwitcherProps) { |
| const { locale, setLocale } = useI18n(); |
| const [open, setOpen] = useState(false); |
| const ref = useRef<HTMLDivElement>(null); |
|
|
| |
| useEffect(() => { |
| if (!open) return; |
| const handler = (e: MouseEvent) => { |
| if (ref.current && !ref.current.contains(e.target as Node)) { |
| setOpen(false); |
| } |
| }; |
| document.addEventListener('mousedown', handler); |
| return () => document.removeEventListener('mousedown', handler); |
| }, [open]); |
|
|
| return ( |
| <div className="relative" ref={ref}> |
| <button |
| onClick={() => { |
| const next = !open; |
| setOpen(next); |
| if (next) onOpen?.(); |
| }} |
| className="flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-bold text-gray-500 dark:text-gray-400 hover:bg-white dark:hover:bg-gray-700 hover:text-gray-800 dark:hover:text-gray-200 hover:shadow-sm transition-all" |
| > |
| {supportedLocales.find((l) => l.code === locale)?.shortLabel ?? locale} |
| </button> |
| {open && ( |
| <div className="absolute top-full mt-2 right-0 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg overflow-hidden z-50 min-w-[120px]"> |
| {supportedLocales.map((l) => ( |
| <button |
| key={l.code} |
| onClick={() => { |
| setLocale(l.code); |
| setOpen(false); |
| }} |
| className={cn( |
| 'w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors', |
| locale === l.code && |
| 'bg-purple-50 dark:bg-purple-900/20 text-purple-600 dark:text-purple-400', |
| )} |
| > |
| {l.label} |
| </button> |
| ))} |
| </div> |
| )} |
| </div> |
| ); |
| } |
|
|