Files
fotospiel-app/resources/js/components/dashboard-language-switcher.tsx

95 lines
3.9 KiB
TypeScript

import { useState } from 'react';
import { router, usePage } from '@inertiajs/react';
import { Check, Languages } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { type SharedData } from '@/types';
import { setLocale } from '@/routes';
export function DashboardLanguageSwitcher() {
const page = usePage<SharedData>();
const { locale, supportedLocales, translations } = page.props;
const locales = supportedLocales && supportedLocales.length > 0 ? supportedLocales : ['de', 'en'];
const activeLocale = locales.includes(locale as string) && typeof locale === 'string' ? locale : locales[0];
const dashboardTranslations =
translations && typeof translations.dashboard === 'object' && translations.dashboard !== null
? (translations.dashboard as Record<string, unknown>)
: null;
const languageSwitcherTranslations =
dashboardTranslations && typeof dashboardTranslations.language_switcher === 'object'
? (dashboardTranslations.language_switcher as Record<string, string | undefined>)
: {};
const languageCopy = languageSwitcherTranslations ?? {};
const label = languageCopy.label ?? 'Sprache';
const changeLabel = languageCopy.change ?? 'Sprache wechseln';
const [pendingLocale, setPendingLocale] = useState<string | null>(null);
const handleChange = (nextLocale: string) => {
if (nextLocale === activeLocale || pendingLocale === nextLocale || !locales.includes(nextLocale)) {
return;
}
setPendingLocale(nextLocale);
router.post(
setLocale().url,
{ locale: nextLocale },
{
preserveScroll: true,
preserveState: true,
onFinish: () => setPendingLocale(null),
},
);
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="flex items-center gap-2 rounded-full border-white/30 bg-white/80 px-3 text-xs font-semibold uppercase tracking-wide text-slate-900 backdrop-blur transition hover:bg-white dark:border-white/20 dark:bg-white/10 dark:text-white"
aria-label={label}
>
<Languages className="size-4" />
<span>{label}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="min-w-[9rem]">
<DropdownMenuLabel className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
{changeLabel}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{locales.map((code) => {
const isActive = code === activeLocale;
const isPending = code === pendingLocale;
return (
<DropdownMenuItem
key={code}
onSelect={(event) => {
event.preventDefault();
handleChange(code);
}}
disabled={isPending}
className="flex items-center justify-between gap-3"
>
<span className="text-sm font-medium uppercase tracking-wide">{code}</span>
{(isActive || isPending) && <Check className="size-4 text-pink-500" />}
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}