96 lines
3.9 KiB
TypeScript
96 lines
3.9 KiB
TypeScript
'use client';
|
|
|
|
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
|
|
import { ChevronDownIcon } from '@heroicons/react/20/solid';
|
|
import { useTranslation } from '../i18n/useTranslation';
|
|
import { SUPPORTED_LANGUAGES, LANGUAGE_NAMES } from '../i18n/config';
|
|
|
|
interface LanguageSwitcherProps {
|
|
variant?: 'light' | 'dark';
|
|
}
|
|
|
|
// Flag Icons mit Emoji (viel sauberer als selbst gezeichnete CSS-Flaggen)
|
|
const FlagIcon = ({ countryCode, className = "size-5" }: { countryCode: string; className?: string }) => {
|
|
const flags = {
|
|
'de': '🇩🇪',
|
|
'en': '🇬🇧'
|
|
};
|
|
|
|
return (
|
|
<span className={`${className} flex items-center justify-center text-base`}>
|
|
{flags[countryCode as keyof typeof flags] || '🏳️'}
|
|
</span>
|
|
);
|
|
};
|
|
|
|
export default function LanguageSwitcher({ variant = 'light' }: LanguageSwitcherProps) {
|
|
const { language, setLanguage } = useTranslation();
|
|
|
|
const getButtonStyles = () => {
|
|
if (variant === 'dark') {
|
|
return 'inline-flex w-full justify-center gap-x-1.5 rounded-md bg-white/10 px-3 py-2 text-sm font-semibold text-white inset-ring-1 inset-ring-white/5 hover:bg-white/20';
|
|
}
|
|
return 'inline-flex w-full justify-center gap-x-1.5 rounded-md bg-gray-100 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-gray-300 hover:bg-gray-200';
|
|
};
|
|
|
|
const getMenuStyles = () => {
|
|
if (variant === 'dark') {
|
|
return 'absolute right-0 z-10 mt-2 w-48 origin-top-right divide-y divide-white/10 rounded-md bg-gray-800 outline-1 -outline-offset-1 outline-white/10 transition data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in';
|
|
}
|
|
return 'absolute right-0 z-10 mt-2 w-48 origin-top-right divide-y divide-gray-100 rounded-md bg-white outline-1 -outline-offset-1 outline-gray-200 transition data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in';
|
|
};
|
|
|
|
const getItemStyles = (isActive: boolean) => {
|
|
if (variant === 'dark') {
|
|
return `group flex items-center px-4 py-2 text-sm ${
|
|
isActive
|
|
? 'bg-[#8D6B1D] text-white'
|
|
: 'text-gray-300 data-focus:bg-white/5 data-focus:text-white data-focus:outline-hidden'
|
|
}`;
|
|
}
|
|
return `group flex items-center px-4 py-2 text-sm ${
|
|
isActive
|
|
? 'bg-[#8D6B1D] text-white'
|
|
: 'text-gray-700 data-focus:bg-gray-100 data-focus:text-gray-900 data-focus:outline-hidden'
|
|
}`;
|
|
};
|
|
|
|
return (
|
|
<Menu as="div" className="relative inline-block">
|
|
<MenuButton className={getButtonStyles()}>
|
|
<FlagIcon countryCode={language} className="size-4" />
|
|
{LANGUAGE_NAMES[language]}
|
|
<ChevronDownIcon aria-hidden="true" className="-mr-1 size-5 text-gray-500" />
|
|
</MenuButton>
|
|
|
|
<MenuItems
|
|
transition
|
|
className={getMenuStyles()}
|
|
>
|
|
<div className="py-1">
|
|
{SUPPORTED_LANGUAGES.map((lang) => (
|
|
<MenuItem key={lang}>
|
|
<button
|
|
onClick={() => setLanguage(lang)}
|
|
className={getItemStyles(language === lang)}
|
|
>
|
|
<FlagIcon
|
|
countryCode={lang}
|
|
className={`mr-3 size-5 ${
|
|
variant === 'dark'
|
|
? (language === lang ? 'opacity-100' : 'opacity-70 group-data-focus:opacity-100')
|
|
: (language === lang ? 'opacity-100' : 'opacity-80 group-data-focus:opacity-100')
|
|
}`}
|
|
/>
|
|
<span className="flex-1 text-left">{LANGUAGE_NAMES[lang]}</span>
|
|
{language === lang && (
|
|
<span className="ml-2 text-xs font-bold">✓</span>
|
|
)}
|
|
</button>
|
|
</MenuItem>
|
|
))}
|
|
</div>
|
|
</MenuItems>
|
|
</Menu>
|
|
);
|
|
} |