2020-01-18 02:07:52 +05:30
|
|
|
import React, { MouseEventHandler } from 'react';
|
|
|
|
import {
|
2020-05-24 04:38:24 +05:30
|
|
|
TransitionMotion,
|
|
|
|
spring,
|
|
|
|
presets,
|
|
|
|
TransitionStyle,
|
|
|
|
TransitionPlainStyle,
|
|
|
|
PlainStyle,
|
|
|
|
Style,
|
2020-01-18 02:07:52 +05:30
|
|
|
} from 'react-motion';
|
2017-12-31 03:08:54 +05:30
|
|
|
import { FormattedMessage as Message } from 'react-intl';
|
2019-12-08 01:13:08 +05:30
|
|
|
import clsx from 'clsx';
|
2019-12-07 16:58:52 +05:30
|
|
|
|
2017-12-31 03:08:54 +05:30
|
|
|
import LocaleItem from './LocaleItem';
|
2019-12-07 16:58:52 +05:30
|
|
|
import { LocalesMap } from './LanguageSwitcher';
|
2017-12-31 03:08:54 +05:30
|
|
|
import styles from './languageSwitcher.scss';
|
|
|
|
|
|
|
|
import thatFuckingPumpkin from './images/that_fucking_pumpkin.svg';
|
|
|
|
import mayTheForceBeWithYou from './images/may_the_force_be_with_you.svg';
|
|
|
|
import biteMyShinyMetalAss from './images/bite_my_shiny_metal_ass.svg';
|
|
|
|
import iTookAnArrowInMyKnee from './images/i_took_an_arrow_in_my_knee.svg';
|
|
|
|
|
2020-01-18 02:07:52 +05:30
|
|
|
interface EmptyCaption {
|
2020-05-24 04:38:24 +05:30
|
|
|
src: string;
|
|
|
|
caption: string;
|
2020-01-18 02:07:52 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
const emptyCaptions: ReadonlyArray<EmptyCaption> = [
|
2020-05-24 04:38:24 +05:30
|
|
|
{
|
|
|
|
// Homestuck
|
|
|
|
src: thatFuckingPumpkin,
|
|
|
|
caption: 'That fucking pumpkin',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// Star Wars
|
|
|
|
src: mayTheForceBeWithYou,
|
|
|
|
caption: 'May The Force Be With You',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// Futurama
|
|
|
|
src: biteMyShinyMetalAss,
|
|
|
|
caption: 'Bite my shiny metal ass',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// The Elder Scrolls V: Skyrim
|
|
|
|
src: iTookAnArrowInMyKnee,
|
|
|
|
caption: 'I took an arrow in my knee',
|
|
|
|
},
|
2020-01-18 02:07:52 +05:30
|
|
|
];
|
|
|
|
|
2017-12-31 03:08:54 +05:30
|
|
|
const itemHeight = 51;
|
|
|
|
|
|
|
|
export default class LanguageList extends React.Component<{
|
2020-05-24 04:38:24 +05:30
|
|
|
selectedLocale: string;
|
|
|
|
langs: LocalesMap;
|
|
|
|
onChangeLang: (lang: string) => void;
|
2017-12-31 03:08:54 +05:30
|
|
|
}> {
|
2020-05-24 04:38:24 +05:30
|
|
|
emptyListStateInner: HTMLDivElement | null;
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { selectedLocale, langs } = this.props;
|
|
|
|
const isListEmpty = Object.keys(langs).length === 0;
|
|
|
|
const firstLocale = Object.keys(langs)[0] || null;
|
|
|
|
const emptyCaption = this.getEmptyCaption();
|
|
|
|
|
|
|
|
return (
|
|
|
|
<TransitionMotion
|
|
|
|
defaultStyles={this.getItemsWithDefaultStyles()}
|
|
|
|
styles={this.getItemsWithStyles()}
|
|
|
|
willLeave={this.willLeave}
|
|
|
|
willEnter={this.willEnter}
|
2017-12-31 03:08:54 +05:30
|
|
|
>
|
2020-05-24 04:38:24 +05:30
|
|
|
{(items) => (
|
|
|
|
<div className={styles.languagesList} data-testid="language-list">
|
|
|
|
<div
|
|
|
|
className={clsx(styles.emptyLanguagesListWrapper, {
|
|
|
|
[styles.emptyLanguagesListVisible]: isListEmpty,
|
|
|
|
})}
|
|
|
|
style={{
|
|
|
|
height:
|
|
|
|
isListEmpty && this.emptyListStateInner ? this.emptyListStateInner.clientHeight : 0,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<div
|
|
|
|
ref={(elem: HTMLDivElement | null) => (this.emptyListStateInner = elem)}
|
|
|
|
className={styles.emptyLanguagesList}
|
|
|
|
>
|
|
|
|
<img
|
|
|
|
src={emptyCaption.src}
|
|
|
|
alt={emptyCaption.caption}
|
|
|
|
className={styles.emptyLanguagesListCaption}
|
|
|
|
/>
|
|
|
|
<div className={styles.emptyLanguagesListSubtitle}>
|
2020-06-04 22:11:27 +05:30
|
|
|
<Message
|
|
|
|
key="weDoNotSupportThisLang"
|
|
|
|
defaultMessage="Sorry, we do not support this language"
|
|
|
|
/>
|
2020-05-24 04:38:24 +05:30
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{items.map(({ key: locale, data: definition, style }) => (
|
|
|
|
<div
|
|
|
|
key={locale}
|
|
|
|
style={style}
|
|
|
|
className={clsx(styles.languageItem, {
|
|
|
|
[styles.activeLanguageItem]: locale === selectedLocale,
|
|
|
|
[styles.firstLanguageItem]: locale === firstLocale,
|
|
|
|
})}
|
|
|
|
onClick={this.onChangeLang(locale)}
|
|
|
|
>
|
|
|
|
<LocaleItem locale={definition} />
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</TransitionMotion>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
getEmptyCaption(): EmptyCaption {
|
|
|
|
return emptyCaptions[Math.floor(Math.random() * emptyCaptions.length)];
|
|
|
|
}
|
|
|
|
|
|
|
|
onChangeLang(lang: string): MouseEventHandler<HTMLDivElement> {
|
|
|
|
return (event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
this.props.onChangeLang(lang);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
getItemsWithDefaultStyles = (): Array<TransitionPlainStyle> => {
|
|
|
|
return Object.keys({ ...this.props.langs }).reduce(
|
|
|
|
(previous, key) => [
|
|
|
|
...previous,
|
|
|
|
{
|
|
|
|
key,
|
|
|
|
data: this.props.langs[key],
|
|
|
|
style: {
|
|
|
|
height: itemHeight,
|
|
|
|
opacity: 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[] as Array<TransitionPlainStyle>,
|
|
|
|
);
|
2019-11-27 14:33:32 +05:30
|
|
|
};
|
|
|
|
|
2020-05-24 04:38:24 +05:30
|
|
|
getItemsWithStyles = (): Array<TransitionStyle> => {
|
|
|
|
return Object.keys({ ...this.props.langs }).reduce(
|
|
|
|
(previous, key) => [
|
|
|
|
...previous,
|
|
|
|
{
|
|
|
|
key,
|
|
|
|
data: this.props.langs[key],
|
|
|
|
style: {
|
|
|
|
height: spring(itemHeight, presets.gentle),
|
|
|
|
opacity: spring(1, presets.gentle),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[] as Array<TransitionStyle>,
|
|
|
|
);
|
2019-11-27 14:33:32 +05:30
|
|
|
};
|
2020-05-24 04:38:24 +05:30
|
|
|
|
|
|
|
willEnter(): PlainStyle {
|
|
|
|
return {
|
|
|
|
height: 0,
|
|
|
|
opacity: 1,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
willLeave(): Style {
|
|
|
|
return {
|
|
|
|
height: spring(0),
|
|
|
|
opacity: spring(0),
|
|
|
|
};
|
|
|
|
}
|
2017-12-31 03:08:54 +05:30
|
|
|
}
|