mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-07-03 05:25:41 +05:30
Реализована анимация фильтрации языков
This commit is contained in:
parent
e2c8471ce2
commit
b76b98e3bf
|
@ -1,8 +1,9 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { TransitionMotion, spring, presets } from 'react-motion';
|
||||||
|
import { FormattedMessage as Message, intlShape } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormattedMessage as Message, intlShape } from 'react-intl';
|
|
||||||
|
|
||||||
import { requireLocaleFlag } from 'functions';
|
import { requireLocaleFlag } from 'functions';
|
||||||
import LANGS from 'i18n/index.json';
|
import LANGS from 'i18n/index.json';
|
||||||
|
@ -14,6 +15,7 @@ import styles from './languageSwitcher.scss';
|
||||||
import messages from './languageSwitcher.intl.json';
|
import messages from './languageSwitcher.intl.json';
|
||||||
|
|
||||||
const improveTranslationUrl = 'http://ely.by/erickskrauch/posts/174943';
|
const improveTranslationUrl = 'http://ely.by/erickskrauch/posts/174943';
|
||||||
|
const itemHeight = 51;
|
||||||
|
|
||||||
class LanguageSwitcher extends Component {
|
class LanguageSwitcher extends Component {
|
||||||
static displayName = 'LanguageSwitcher';
|
static displayName = 'LanguageSwitcher';
|
||||||
|
@ -22,6 +24,7 @@ class LanguageSwitcher extends Component {
|
||||||
onClose: PropTypes.func,
|
onClose: PropTypes.func,
|
||||||
userLang: PropTypes.string,
|
userLang: PropTypes.string,
|
||||||
changeLang: PropTypes.func,
|
changeLang: PropTypes.func,
|
||||||
|
langs: PropTypes.objectOf(PropTypes.object).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
|
@ -29,21 +32,18 @@ class LanguageSwitcher extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
items: [],
|
|
||||||
filter: '',
|
filter: '',
|
||||||
|
filteredLangs: this.props.langs,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
onClose() {}
|
langs: LANGS,
|
||||||
|
onClose() {},
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.setState({items: LANGS});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {userLang, onClose} = this.props;
|
const {userLang, onClose} = this.props;
|
||||||
const {items} = this.state;
|
const firstLocale = Object.keys(this.state.filteredLangs)[0] || null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.languageSwitcher}>
|
<div className={styles.languageSwitcher}>
|
||||||
|
@ -60,26 +60,40 @@ class LanguageSwitcher extends Component {
|
||||||
<input
|
<input
|
||||||
className={classNames(
|
className={classNames(
|
||||||
formStyles.lightTextField,
|
formStyles.lightTextField,
|
||||||
formStyles.greenTextField
|
formStyles.greenTextField,
|
||||||
)}
|
)}
|
||||||
placeholder={this.context.intl.formatMessage(messages.startTyping)}
|
placeholder={this.context.intl.formatMessage(messages.startTyping)}
|
||||||
onChange={this.onFilterUpdate()}
|
onChange={this.onFilterUpdate}
|
||||||
onKeyPress={this.onFilterKeyPress()}
|
onKeyPress={this.onFilterKeyPress()}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<span className={styles.searchIcon} />
|
<span className={styles.searchIcon} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.languagesList}>
|
<TransitionMotion
|
||||||
{Object.keys(items).map((locale) => (
|
defaultStyles={this.getItemsWithDefaultStyles()}
|
||||||
<li className={classNames(styles.languageItem, {
|
styles={this.getItemsWithStyles()}
|
||||||
[styles.activeLanguageItem]: locale === userLang
|
willLeave={this.willLeave}
|
||||||
})} onClick={this.onChangeLang(locale)} key={locale}
|
willEnter={this.willEnter}
|
||||||
>
|
>
|
||||||
{this.renderLanguageItem(locale, items[locale])}
|
{(items) => (
|
||||||
</li>
|
<div className={styles.languagesList}>
|
||||||
))}
|
{items.map(({key: locale, data: definition, style}) => (
|
||||||
</div>
|
<li
|
||||||
|
key={locale}
|
||||||
|
style={style}
|
||||||
|
className={classNames(styles.languageItem, {
|
||||||
|
[styles.activeLanguageItem]: locale === userLang,
|
||||||
|
[styles.firstLanguageItem]: locale === firstLocale,
|
||||||
|
})}
|
||||||
|
onClick={this.onChangeLang(locale)}
|
||||||
|
>
|
||||||
|
{this.renderLanguageItem(locale, definition)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TransitionMotion>
|
||||||
|
|
||||||
<div className={styles.improveTranslates}>
|
<div className={styles.improveTranslates}>
|
||||||
<div className={styles.improveTranslatesIcon} />
|
<div className={styles.improveTranslatesIcon} />
|
||||||
|
@ -93,7 +107,7 @@ class LanguageSwitcher extends Component {
|
||||||
<a href={improveTranslationUrl} target="_blank">
|
<a href={improveTranslationUrl} target="_blank">
|
||||||
<Message {...messages.improveTranslatesArticleLink} />
|
<Message {...messages.improveTranslatesArticleLink} />
|
||||||
</a>
|
</a>
|
||||||
)
|
),
|
||||||
}} />
|
}} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -122,7 +136,7 @@ class LanguageSwitcher extends Component {
|
||||||
return (
|
return (
|
||||||
<div className={styles.languageFlex}>
|
<div className={styles.languageFlex}>
|
||||||
<div className={styles.languageIco} style={{
|
<div className={styles.languageIco} style={{
|
||||||
backgroundImage: `url('${requireLocaleFlag(locale)}')`
|
backgroundImage: `url('${requireLocaleFlag(locale)}')`,
|
||||||
}} />
|
}} />
|
||||||
<div className={styles.languageCaptions}>
|
<div className={styles.languageCaptions}>
|
||||||
<div className={styles.languageName}>
|
<div className={styles.languageName}>
|
||||||
|
@ -149,28 +163,26 @@ class LanguageSwitcher extends Component {
|
||||||
setTimeout(this.props.onClose, 300);
|
setTimeout(this.props.onClose, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterUpdate() {
|
onFilterUpdate = (event) => {
|
||||||
return (event) => {
|
const filter = event.target.value.trim().toLowerCase();
|
||||||
const value = event.target.value.trim().toLowerCase();
|
const { langs } = this.props;
|
||||||
let items = LANGS;
|
const result = Object.keys(langs).reduce((previous, key) => {
|
||||||
if (value.length !== 0) {
|
if (langs[key].englishName.toLowerCase().search(filter) === -1
|
||||||
items = Object.keys(items).reduce((prev, next) => {
|
&& langs[key].name.toLowerCase().search(filter) === -1
|
||||||
if (items[next].englishName.toLowerCase().search(value) !== -1
|
) {
|
||||||
|| items[next].name.toLowerCase().search(value) !== -1
|
return previous;
|
||||||
) {
|
|
||||||
prev[next] = items[next];
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}, {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
previous[key] = langs[key];
|
||||||
items,
|
|
||||||
filter: value,
|
return previous;
|
||||||
});
|
}, {});
|
||||||
};
|
|
||||||
}
|
this.setState({
|
||||||
|
filter,
|
||||||
|
filteredLangs: result,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onFilterKeyPress() {
|
onFilterKeyPress() {
|
||||||
return (event) => {
|
return (event) => {
|
||||||
|
@ -178,7 +190,7 @@ class LanguageSwitcher extends Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const locales = Object.keys(this.state.items);
|
const locales = Object.keys(this.props.langs);
|
||||||
if (locales.length === 0) {
|
if (locales.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -186,13 +198,53 @@ class LanguageSwitcher extends Component {
|
||||||
this.changeLang(locales[0]);
|
this.changeLang(locales[0]);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getItemsWithDefaultStyles = () => Object.keys(this.props.langs).reduce((previous, key) => {
|
||||||
|
return [
|
||||||
|
...previous,
|
||||||
|
{
|
||||||
|
key,
|
||||||
|
data: this.props.langs[key],
|
||||||
|
style: {
|
||||||
|
height: itemHeight,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
getItemsWithStyles = () => Object.keys({...this.state.filteredLangs}).reduce((previous, key) => [
|
||||||
|
...previous,
|
||||||
|
{
|
||||||
|
key,
|
||||||
|
data: this.props.langs[key],
|
||||||
|
style: {
|
||||||
|
height: spring(itemHeight, presets.gentle),
|
||||||
|
opacity: spring(1, presets.gentle),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
], []);
|
||||||
|
|
||||||
|
willEnter() {
|
||||||
|
return {
|
||||||
|
height: 0,
|
||||||
|
opacity: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
willLeave() {
|
||||||
|
return {
|
||||||
|
height: spring(0),
|
||||||
|
opacity: spring(0),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { changeLang } from 'components/user/actions';
|
import { changeLang } from 'components/user/actions';
|
||||||
|
|
||||||
export default connect((state) => ({
|
export default connect((state) => ({
|
||||||
userLang: state.user.lang
|
userLang: state.user.lang,
|
||||||
}), {
|
}), {
|
||||||
changeLang
|
changeLang,
|
||||||
})(LanguageSwitcher);
|
})(LanguageSwitcher);
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
@import '~components/ui/fonts.scss';
|
@import '~components/ui/fonts.scss';
|
||||||
@import '~components/ui/popup/popup.scss';
|
@import '~components/ui/popup/popup.scss';
|
||||||
|
|
||||||
|
@mixin hideFooter {
|
||||||
|
@media (max-height: 455px) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.languageSwitcher {
|
.languageSwitcher {
|
||||||
composes: popupWrapper from 'components/ui/popup/popup.scss';
|
composes: popupWrapper from 'components/ui/popup/popup.scss';
|
||||||
|
|
||||||
|
@ -36,7 +42,8 @@
|
||||||
pointer-events: none; // Иконка чисто декоративная, так что клик должен проходить сквозь неё
|
pointer-events: none; // Иконка чисто декоративная, так что клик должен проходить сквозь неё
|
||||||
}
|
}
|
||||||
|
|
||||||
$languageListBorderStyle: 1px solid #eee;
|
$languageListBorderColor: #eee;
|
||||||
|
$languageListBorderStyle: 1px solid $languageListBorderColor;
|
||||||
|
|
||||||
.languagesList {
|
.languagesList {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
@ -44,28 +51,35 @@ $languageListBorderStyle: 1px solid #eee;
|
||||||
border-top: $languageListBorderStyle;
|
border-top: $languageListBorderStyle;
|
||||||
border-bottom: $languageListBorderStyle;
|
border-bottom: $languageListBorderStyle;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
@include hideFooter {
|
||||||
|
& {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.languageItem {
|
.languageItem {
|
||||||
padding: 10px;
|
|
||||||
border-top: $languageListBorderStyle;
|
|
||||||
font-family: $font-family-title;
|
font-family: $font-family-title;
|
||||||
transition: .25s;
|
transition: background-color .25s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $whiteButtonLight;
|
background-color: $whiteButtonLight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.languageFlex {
|
.languageFlex {
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
border-top: $languageListBorderStyle;
|
||||||
|
|
||||||
|
.firstLanguageItem & {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.languageIco {
|
.languageIco {
|
||||||
|
@ -140,7 +154,7 @@ $languageListBorderStyle: 1px solid #eee;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
@media screen and (max-height: 455px) {
|
@include hideFooter {
|
||||||
& {
|
& {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user