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