feat: language selector (#484)
* feat: language selector * refactor: move user general page * feat: language selector * refactor: move user general page * feat: add language combobox in user general
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.flagIcon {
|
||||
border-radius: 4px;
|
||||
}
|
||||
94
apps/nextjs/src/components/language/language-combobox.tsx
Normal file
94
apps/nextjs/src/components/language/language-combobox.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { Combobox, Group, InputBase, Text, useCombobox } from "@mantine/core";
|
||||
import { IconCheck } from "@tabler/icons-react";
|
||||
|
||||
import type { SupportedLanguage } from "@homarr/translation";
|
||||
import { localeAttributes, supportedLanguages } from "@homarr/translation";
|
||||
import { useChangeLocale, useCurrentLocale } from "@homarr/translation/client";
|
||||
|
||||
import classes from "./language-combobox.module.css";
|
||||
|
||||
export const LanguageCombobox = () => {
|
||||
const combobox = useCombobox({
|
||||
onDropdownClose: () => combobox.resetSelectedOption(),
|
||||
});
|
||||
const currentLocale = useCurrentLocale();
|
||||
const changeLocale = useChangeLocale();
|
||||
|
||||
const handleOnOptionSubmit = React.useCallback(
|
||||
(value: string) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
changeLocale(value as SupportedLanguage);
|
||||
combobox.closeDropdown();
|
||||
},
|
||||
[changeLocale, combobox],
|
||||
);
|
||||
|
||||
const handleOnClick = React.useCallback(() => {
|
||||
combobox.toggleDropdown();
|
||||
}, [combobox]);
|
||||
|
||||
return (
|
||||
<Combobox store={combobox} onOptionSubmit={handleOnOptionSubmit}>
|
||||
<Combobox.Target>
|
||||
<InputBase
|
||||
component="button"
|
||||
type="button"
|
||||
pointer
|
||||
rightSection={<Combobox.Chevron />}
|
||||
rightSectionPointerEvents="none"
|
||||
onClick={handleOnClick}
|
||||
variant="filled"
|
||||
>
|
||||
<OptionItem currentLocale={currentLocale} localeKey={currentLocale} />
|
||||
</InputBase>
|
||||
</Combobox.Target>
|
||||
<Combobox.Dropdown>
|
||||
<Combobox.Options>
|
||||
{supportedLanguages.map((languageKey) => (
|
||||
<Combobox.Option value={languageKey} key={languageKey}>
|
||||
<OptionItem
|
||||
currentLocale={currentLocale}
|
||||
localeKey={languageKey}
|
||||
showCheck
|
||||
/>
|
||||
</Combobox.Option>
|
||||
))}
|
||||
</Combobox.Options>
|
||||
</Combobox.Dropdown>
|
||||
</Combobox>
|
||||
);
|
||||
};
|
||||
|
||||
const OptionItem = ({
|
||||
currentLocale,
|
||||
localeKey,
|
||||
showCheck,
|
||||
}: {
|
||||
currentLocale: SupportedLanguage;
|
||||
localeKey: SupportedLanguage;
|
||||
showCheck?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<Group wrap="nowrap" justify="space-between">
|
||||
<Group wrap="nowrap">
|
||||
<span
|
||||
className={`fi fi-${localeAttributes[localeKey].flagIcon} ${classes.flagIcon}`}
|
||||
></span>
|
||||
<Group wrap="nowrap" gap="xs">
|
||||
<Text>{localeAttributes[localeKey].name}</Text>
|
||||
<Text size="xs" c="dimmed" inherit>
|
||||
({localeAttributes[localeKey].translatedName})
|
||||
</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
{showCheck && localeKey === currentLocale && (
|
||||
<IconCheck color="currentColor" size={16} />
|
||||
)}
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
@@ -27,6 +27,10 @@ import { signOut, useSession } from "@homarr/auth/client";
|
||||
import { createModal, useModalAction } from "@homarr/modals";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import "flag-icons/css/flag-icons.min.css";
|
||||
|
||||
import { LanguageCombobox } from "./language/language-combobox";
|
||||
|
||||
interface UserAvatarMenuProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
@@ -57,7 +61,7 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => {
|
||||
}, [openModal, router]);
|
||||
|
||||
return (
|
||||
<Menu width={200} withArrow withinPortal>
|
||||
<Menu width={300} withArrow withinPortal>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
onClick={toggleColorScheme}
|
||||
@@ -72,23 +76,32 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => {
|
||||
>
|
||||
{t("navigateDefaultBoard")}
|
||||
</Menu.Item>
|
||||
{Boolean(session.data) && (
|
||||
<Menu.Item
|
||||
component={Link}
|
||||
href={`/manage/users/${session.data?.user.id}`}
|
||||
leftSection={<IconSettings size="1rem" />}
|
||||
>
|
||||
{t("preferences")}
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item
|
||||
component={Link}
|
||||
href="/manage"
|
||||
leftSection={<IconTool size="1rem" />}
|
||||
>
|
||||
{t("management")}
|
||||
<Menu.Divider />
|
||||
|
||||
<Menu.Item p={0} closeMenuOnClick={false}>
|
||||
<LanguageCombobox />
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
{Boolean(session.data) && (
|
||||
<>
|
||||
<Menu.Item
|
||||
component={Link}
|
||||
href={`/manage/users/${session.data?.user.id}`}
|
||||
leftSection={<IconSettings size="1rem" />}
|
||||
>
|
||||
{t("preferences")}
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item
|
||||
component={Link}
|
||||
href="/manage"
|
||||
leftSection={<IconTool size="1rem" />}
|
||||
>
|
||||
{t("management")}
|
||||
</Menu.Item>
|
||||
</>
|
||||
)}
|
||||
<Menu.Divider />
|
||||
{session.status === "authenticated" ? (
|
||||
<Menu.Item
|
||||
onClick={handleSignout}
|
||||
|
||||
Reference in New Issue
Block a user