import { useState } from 'react'; import { Languages } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { useTranslation } from '@/contexts/language'; import { LANGUAGE_OPTIONS } from '@/lib/languageOptions'; import { cn } from '@/lib/utils'; const VISIBLE_LANGUAGE_OPTIONS = LANGUAGE_OPTIONS.filter((language) => !language.hidden); interface LanguageToggleProps { /** Show only the flag icon (compact mode) */ compact?: boolean; /** * Render the languages as an inline list instead of a dropdown. Use this * INSIDE a Radix Sheet/Dialog: a portaled DropdownMenu inherits the dialog's * `pointer-events: none` on and its items become unclickable. */ inline?: boolean; /** Additional class names */ className?: string; /** Called after a locale is chosen (e.g. to close a sheet sub-view). */ onSelect?: () => void; /** Variant style */ variant?: 'default' | 'outline' | 'ghost'; } /** * Flags are an emoji or a local SVG. Catalonia, the Basque Country, Galicia and * England have no Unicode emoji, so they ship as real SVG files and render as * an image instead of a text badge. */ function Flag({ flag, className }: { flag: string; className?: string }) { if (flag.startsWith('/')) { return ( ); } return ( {flag} ); } /** * Language toggle dropdown for switching between the available locales */ export function LanguageToggle({ compact = false, inline = false, className, variant = 'ghost', onSelect }: LanguageToggleProps) { const { language, setLanguage, t } = useTranslation(); const [query, setQuery] = useState(''); // Auto-focusing the search on a phone pops the keyboard over the list before // the user can see the languages. Keep it for mouse/pen, not touch. const autoFocusSearch = typeof window !== 'undefined' && !!window.matchMedia?.('(pointer: fine)').matches; const safeQuery = query.trim().toLowerCase(); const filteredLanguages = safeQuery ? VISIBLE_LANGUAGE_OPTIONS.filter( (l) => l.name.toLowerCase().includes(safeQuery) || l.nativeName.toLowerCase().includes(safeQuery) || l.code.toLowerCase().includes(safeQuery) ) : VISIBLE_LANGUAGE_OPTIONS; const currentLanguage = LANGUAGE_OPTIONS.find((l) => l.code === language) || LANGUAGE_OPTIONS[0]; if (inline) { return (
setQuery(e.target.value)} placeholder={t('nav.searchLanguages')} className="h-9 w-full rounded-lg border border-border bg-background px-3 text-sm outline-none focus:border-primary" />
{filteredLanguages.map((lang) => ( ))} {filteredLanguages.length === 0 && (
{t('nav.noLanguages')}
)}
); } return ( { if (!open) setQuery(''); }}>
e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()} > setQuery(e.target.value)} placeholder={t('nav.searchLanguages')} className="h-8 w-full rounded-sm bg-transparent px-2 text-sm outline-none placeholder:text-muted-foreground" />
{filteredLanguages.map((lang) => ( { setLanguage(lang.code); onSelect?.(); }} className={cn( 'flex min-h-11 items-center gap-3 cursor-pointer', language === lang.code && 'bg-primary/10 text-primary' )} >
{lang.nativeName} {lang.nativeName !== lang.name && ( {lang.name} )}
{language === lang.code && ( ✓ )}
))} {filteredLanguages.length === 0 && (
{t('nav.noLanguages')}
)}
); } /** * Simple language selector for settings page */ export function LanguageSelector() { const { language, setLanguage, t } = useTranslation(); return (
{VISIBLE_LANGUAGE_OPTIONS.map((lang) => ( ))}
); }