2020-04-23 13:20:41 +07:00
|
|
|
import React, { MouseEvent, useCallback, useState, useRef, useContext } from 'react';
|
|
|
|
import LanguageIcon from '@material-ui/icons/Language';
|
|
|
|
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
|
|
|
import i18next, { TFunction } from 'i18next';
|
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import { withStyles } from '@material-ui/core/styles';
|
|
|
|
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
|
|
|
|
import Grow from '@material-ui/core/Grow';
|
|
|
|
import Popper from '@material-ui/core/Popper';
|
|
|
|
import MenuList from '@material-ui/core/MenuList';
|
|
|
|
import styled from '@emotion/styled';
|
|
|
|
|
|
|
|
import { Language } from '../../../i18n/config';
|
|
|
|
import ThemeContext from '../../design-tokens/ThemeContext';
|
|
|
|
import Paper from '../../muiComponents/Paper';
|
|
|
|
import MenuItem from '../../muiComponents/MenuItem';
|
|
|
|
import Button from '../../muiComponents/Button';
|
|
|
|
import Tooltip from '../../muiComponents/Tooltip';
|
|
|
|
import Divider from '../../muiComponents/Divider';
|
|
|
|
import Box from '../../muiComponents/Box';
|
|
|
|
import { Theme } from '../../design-tokens/theme';
|
|
|
|
import Link from '../Link';
|
|
|
|
import Icon from '../Icon';
|
|
|
|
|
|
|
|
const VERDACCIO_UI_GITHUB_REPOSITORY = 'https://github.com/verdaccio/ui';
|
|
|
|
|
2020-04-28 02:36:40 +07:00
|
|
|
const getTranslatedCurrentLanguageDetails = (
|
|
|
|
t: TFunction,
|
|
|
|
currentLanguage: string
|
|
|
|
): { translation: string; icon: React.ComponentProps<typeof Icon>['name'] } => {
|
|
|
|
switch (currentLanguage) {
|
|
|
|
case 'fr-FR':
|
|
|
|
return {
|
|
|
|
translation: t('lng.french'),
|
|
|
|
icon: 'france',
|
|
|
|
};
|
|
|
|
case 'pt-BR':
|
|
|
|
return {
|
|
|
|
translation: t('lng.portuguese'),
|
|
|
|
icon: 'brazil',
|
|
|
|
};
|
|
|
|
case 'de-DE':
|
|
|
|
return {
|
|
|
|
translation: t('lng.german'),
|
|
|
|
icon: 'germany',
|
|
|
|
};
|
|
|
|
case 'es-ES':
|
|
|
|
return {
|
|
|
|
translation: t('lng.spanish'),
|
|
|
|
icon: 'spain',
|
|
|
|
};
|
|
|
|
case 'zh-CN':
|
|
|
|
return {
|
|
|
|
translation: t('lng.chinese'),
|
|
|
|
icon: 'china',
|
|
|
|
};
|
|
|
|
case 'ja-JP':
|
|
|
|
return {
|
|
|
|
translation: t('lng.japanese'),
|
|
|
|
icon: 'japan',
|
|
|
|
};
|
2020-05-03 22:45:17 +07:00
|
|
|
case 'uk-UA':
|
|
|
|
return {
|
|
|
|
translation: t('lng.ukraine'),
|
|
|
|
icon: 'ukraine',
|
|
|
|
};
|
2020-04-28 02:36:40 +07:00
|
|
|
default:
|
|
|
|
return {
|
|
|
|
translation: t('lng.english'),
|
|
|
|
icon: 'usa',
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
2020-04-23 13:20:41 +07:00
|
|
|
|
|
|
|
const LanguageSwitch = () => {
|
|
|
|
const themeContext = useContext(ThemeContext);
|
|
|
|
const { t } = useTranslation();
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
const anchorRef = useRef<HTMLButtonElement>(null);
|
|
|
|
|
|
|
|
if (!themeContext) {
|
|
|
|
throw Error(t('theme-context-not-correct-used'));
|
|
|
|
}
|
|
|
|
|
|
|
|
const languages = (i18next.options.resources ? Object.keys(i18next.options.resources) : []) as Array<Language>;
|
2020-04-28 02:36:40 +07:00
|
|
|
const currentLanguage = themeContext.language;
|
2020-04-23 13:20:41 +07:00
|
|
|
|
2020-04-28 02:36:40 +07:00
|
|
|
const { translation: userLanguage } = getTranslatedCurrentLanguageDetails(t, currentLanguage);
|
2020-04-23 13:20:41 +07:00
|
|
|
|
|
|
|
const handleToggle = useCallback(() => {
|
|
|
|
setOpen(prevOpen => !prevOpen);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const handleClose = useCallback(
|
|
|
|
(event: MouseEvent<HTMLLIElement | Document | HTMLAnchorElement>) => {
|
|
|
|
if (anchorRef.current) {
|
|
|
|
if (anchorRef.current.contains(event.currentTarget)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setOpen(false);
|
|
|
|
},
|
|
|
|
[setOpen]
|
|
|
|
);
|
|
|
|
|
|
|
|
const handleSwitchLanguage = useCallback(
|
|
|
|
(language: Language) => (event: MouseEvent<HTMLLIElement>) => {
|
|
|
|
themeContext.setLanguage(language);
|
|
|
|
handleClose(event);
|
|
|
|
},
|
|
|
|
[handleClose, themeContext]
|
|
|
|
);
|
|
|
|
|
|
|
|
// return focus to the button when we transitioned from !open -> open
|
|
|
|
const prevOpen = React.useRef(open);
|
|
|
|
React.useEffect(() => {
|
|
|
|
if (prevOpen.current === true && open === false) {
|
|
|
|
if (anchorRef.current) {
|
|
|
|
anchorRef.current.focus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
prevOpen.current = open;
|
|
|
|
}, [open]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Wrapper>
|
|
|
|
<Tooltip enterDelay={300} title={t('change-language')}>
|
|
|
|
<SwitchButton color="inherit" onClick={handleToggle} ref={anchorRef}>
|
|
|
|
<LanguageIcon />
|
|
|
|
<UserLanguage>{userLanguage}</UserLanguage>
|
|
|
|
<ExpandMoreIcon fontSize="small" />
|
|
|
|
</SwitchButton>
|
|
|
|
</Tooltip>
|
|
|
|
<Popper anchorEl={anchorRef.current} disablePortal={true} open={open} role={undefined} transition={true}>
|
|
|
|
{({ TransitionProps }) => (
|
|
|
|
<Grow {...TransitionProps}>
|
|
|
|
<StyledPaper>
|
|
|
|
<ClickAwayListener onClickAway={handleClose}>
|
|
|
|
<MenuList>
|
|
|
|
{languages
|
|
|
|
.filter(language => language !== currentLanguage)
|
|
|
|
.map(language => {
|
2020-04-28 02:36:40 +07:00
|
|
|
const { icon, translation } = getTranslatedCurrentLanguageDetails(t, language);
|
2020-04-23 13:20:41 +07:00
|
|
|
return (
|
|
|
|
<StyledMenuItem
|
|
|
|
key={language}
|
|
|
|
onClick={handleSwitchLanguage(language)}
|
|
|
|
selected={userLanguage === translation}>
|
|
|
|
<Icon name={icon} size="md" />
|
|
|
|
{translation}
|
|
|
|
</StyledMenuItem>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
<Box my={1}>
|
|
|
|
<Divider />
|
|
|
|
</Box>
|
|
|
|
<MenuItem button={true}>
|
|
|
|
<StyledLink external={true} onClick={handleClose} to={VERDACCIO_UI_GITHUB_REPOSITORY}>
|
|
|
|
{`${t('help-to-translate')}`}
|
|
|
|
</StyledLink>
|
|
|
|
</MenuItem>
|
|
|
|
</MenuList>
|
|
|
|
</ClickAwayListener>
|
|
|
|
</StyledPaper>
|
|
|
|
</Grow>
|
|
|
|
)}
|
|
|
|
</Popper>
|
|
|
|
</Wrapper>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default LanguageSwitch;
|
|
|
|
|
|
|
|
const Wrapper = styled('div')<{ theme?: Theme }>(({ theme }) => ({
|
|
|
|
display: 'none',
|
|
|
|
[`@media screen and (min-width: ${theme && theme.breakPoints.medium}px)`]: {
|
|
|
|
display: 'inline-block',
|
|
|
|
},
|
|
|
|
}));
|
|
|
|
|
|
|
|
const UserLanguage = styled('span')<{ theme?: Theme }>(({ theme }) => ({
|
|
|
|
display: 'none',
|
|
|
|
[`@media screen and (min-width: ${theme && theme.breakPoints.medium}px)`]: {
|
|
|
|
display: 'inline-block',
|
|
|
|
},
|
|
|
|
}));
|
|
|
|
|
|
|
|
const SwitchButton = withStyles((theme: Theme) => ({
|
|
|
|
label: {
|
|
|
|
display: 'grid',
|
|
|
|
gridGap: theme?.spacing(1),
|
|
|
|
gridTemplateColumns: '24px 20px',
|
|
|
|
[`@media screen and (min-width: ${theme && theme.breakPoints.medium}px)`]: {
|
|
|
|
gridTemplateColumns: '24px 1fr 20px',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}))(Button);
|
|
|
|
|
|
|
|
const StyledMenuItem = withStyles((theme: Theme) => ({
|
|
|
|
root: {
|
|
|
|
display: 'grid',
|
|
|
|
cursor: 'pointer',
|
|
|
|
gridGap: theme?.spacing(0.5),
|
|
|
|
gridTemplateColumns: '20px 1fr',
|
|
|
|
alignItems: 'center',
|
|
|
|
'&:first-child': {
|
|
|
|
borderTopLeftRadius: 4,
|
|
|
|
borderTopRightRadius: 4,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}))(MenuItem);
|
|
|
|
|
|
|
|
const StyledLink = styled(Link)<{ theme?: Theme }>(({ theme }) => ({
|
|
|
|
color: theme?.palette.type === 'dark' ? theme?.palette.white : theme?.palette.text.primary,
|
|
|
|
textDecoration: 'none',
|
|
|
|
}));
|
|
|
|
|
|
|
|
const StyledPaper = styled(Paper)<{ theme?: Theme }>(({ theme }) => ({
|
|
|
|
backgroundColor: theme?.palette.type === 'dark' ? theme?.palette.cyanBlue : theme?.palette.white,
|
|
|
|
}));
|