feat(#156): language selector component (#24)

keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 6 years ago committed by GitHub
parent 1c7586e7a4
commit 568e50ea1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      package.json
  2. BIN
      public/images/flags-sprite.png
  3. 17
      public/images/worldIcon.svg
  4. 42
      src/features/App/AuthenticatedApp.tsx
  5. 2
      src/features/App/UnauthenticatedApp.tsx
  6. 12
      src/features/LanguageSelect/config.tsx
  7. 30
      src/features/LanguageSelect/flags.scss
  8. 68
      src/features/LanguageSelect/index.tsx
  9. 76
      src/features/LanguageSelect/styled.tsx
  10. 3
      src/features/Login/index.tsx

@ -16,6 +16,7 @@
"@reach/combobox": "^0.10.4",
"history": "^4.10.1",
"lodash": "^4.17.15",
"node-sass": "^4.14.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router": "^5.2.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

@ -0,0 +1,17 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none"
xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d)">
<path d="M19 8.82C18.91 4.13 15.22 0.3 10.54 0.0200005V0.0100002L9.82001 0V0.0100002C5.13001 0.1 1.29999 3.79 1.01999 8.47H1.00998L1 9.19H1.00998C1.09998 13.88 4.79 17.71 9.47 17.99V18L10.19 18.01V18C14.88 17.91 18.71 14.22 18.99 9.54H19V8.82ZM14.69 8.73C14.69 7.22 14.49 5.81 14.15 4.57C14.99 4.35 15.76 4.06 16.43 3.71C17.58 5.11 18.29 6.88 18.33 8.81L14.69 8.73ZM12.25 0.98C13.68 1.38 14.95 2.16 15.96 3.19C15.36 3.49 14.69 3.74 13.95 3.93C13.52 2.69 12.94 1.67 12.25 0.98ZM13.29 4.08C12.41 4.26 11.45 4.36 10.46 4.36L10.53 0.72C11.66 0.97 12.66 2.24 13.29 4.08ZM9.81 0.71L9.73999 4.35C8.74999 4.31 7.79999 4.17 6.92999 3.95C7.62999 2.14 8.67 0.91 9.81 0.71ZM6.26999 3.77C5.53999 3.55 4.87001 3.27 4.29001 2.95C5.34001 1.96 6.64 1.24 8.09 0.89C7.37 1.56 6.74999 2.55 6.26999 3.77ZM9.72998 5.02L9.64999 8.63L6.00998 8.56C6.06998 7.12 6.32 5.76 6.69 4.6C7.63 4.83 8.64998 4.98 9.72998 5.02ZM9.63998 9.35L9.57001 12.96C8.50001 12.96 7.47 13.07 6.53 13.27C6.2 12.09 6.00998 10.72 6.00998 9.28L9.63998 9.35ZM9.54999 13.64L9.47998 17.28C8.34998 17.03 7.36 15.76 6.72 13.92C7.61 13.74 8.55999 13.64 9.54999 13.64ZM7.75998 17.02C6.32998 16.62 5.05999 15.84 4.04999 14.81C4.64999 14.51 5.32 14.26 6.06 14.07C6.49 15.31 7.06998 16.33 7.75998 17.02ZM10.2 17.29L10.27 13.65C11.26 13.69 12.21 13.83 13.08 14.05C12.38 15.86 11.34 17.09 10.2 17.29ZM13.74 14.23C14.47 14.45 15.14 14.73 15.72 15.05C14.67 16.04 13.37 16.76 11.92 17.11C12.64 16.44 13.26 15.45 13.74 14.23ZM10.28 12.98L10.35 9.36L13.99 9.43C13.93 10.87 13.68 12.23 13.31 13.4C12.38 13.17 11.36 13.02 10.28 12.98ZM10.37 8.65L10.44 5.04C11.51 5.04 12.54 4.93 13.48 4.74C13.81 5.92 14 7.29 14 8.73L10.37 8.65ZM3.79999 3.46C4.44999 3.84 5.21001 4.16 6.04001 4.42C5.65001 5.64 5.38999 7.05 5.32999 8.55L1.69 8.48C1.82 6.55 2.59999 4.8 3.79999 3.46ZM5.32001 9.27C5.32001 10.78 5.51999 12.19 5.85999 13.43C5.01999 13.65 4.24999 13.94 3.57999 14.3C2.42999 12.9 1.71999 11.13 1.67999 9.2L5.32001 9.27ZM16.21 14.54C15.55 14.16 14.8 13.84 13.97 13.58C14.36 12.36 14.62 10.95 14.68 9.44L18.32 9.51C18.19 11.45 17.41 13.2 16.21 14.54Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d" x="0" y="0" width="20" height="20.01" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="0.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -1,4 +1,4 @@
import React from 'react'
import React, { Fragment } from 'react'
import {
Route,
Redirect,
@ -12,25 +12,29 @@ import { TeamPage } from 'features/TeamPage'
import { MatchPage } from 'features/MatchPage'
import { PlayerPage } from 'features/PlayerPage'
import { TournamentPage } from 'features/TournamentPage'
import { LanguageSelect } from 'features/LanguageSelect'
export const AuthenticatedApp = () => (
<Switch>
<Route exact path={PAGES.home}>
<HomePage />
</Route>
<Route path={`${PAGES.tournament}/:pageId`}>
<TournamentPage />
</Route>
<Route path={`${PAGES.team}/:pageId`}>
<TeamPage />
</Route>
<Route path={`${PAGES.player}/:pageId`}>
<PlayerPage />
</Route>
<Route path={`${PAGES.match}/:pageId`}>
<MatchPage />
</Route>
<Fragment>
<LanguageSelect />
<Switch>
<Route exact path={PAGES.home}>
<HomePage />
</Route>
<Route path={`${PAGES.tournament}/:pageId`}>
<TournamentPage />
</Route>
<Route path={`${PAGES.team}/:pageId`}>
<TeamPage />
</Route>
<Route path={`${PAGES.player}/:pageId`}>
<PlayerPage />
</Route>
<Route path={`${PAGES.match}/:pageId`}>
<MatchPage />
</Route>
<Redirect to={PAGES.home} />
</Switch>
<Redirect to={PAGES.home} />
</Switch>
</Fragment>
)

@ -10,12 +10,14 @@ import { publicLexics } from 'config/lexics/public'
import { Login } from 'features/Login'
import { Register } from 'features/Register'
import { LanguageSelect } from 'features/LanguageSelect'
import { useLexicsConfig } from 'features/LexicsStore'
export const UnauthenticatedApp = () => {
useLexicsConfig(publicLexics)
return (
<Fragment>
<LanguageSelect />
<Switch>
<Route path={PAGES.login}>
<Login />

@ -0,0 +1,12 @@
export const langsList = [
{
className: 'ru',
locale: 'ru',
title: 'Русский',
},
{
className: 'gb',
locale: 'en',
title: 'English',
},
]

@ -0,0 +1,30 @@
@mixin flag-position($col, $row, $offsetX, $offsetY) {
background-position: $col*(-1)*$offsetX $row*(-1)*$offsetY;
}
@mixin flags-list($width, $height, $bgSize) {
background-size: $bgSize;
height: $height;
width: $width;
&.flag-icon-ru { @include flag-position(8, 9, $width, $height); } /* Russia */
&.flag-icon-gb { @include flag-position(4, 12, $width, $height); } /* United Kingdom */
}
/*
* PNG images
*/
.flag-icon {
background-image: url('/images/flags-sprite.png');
background-position: 1000px 1000px;
background-repeat: no-repeat;
display: inline-block;
position: relative;
&.flag-icon-24x16 {
@include flags-list(24px, 16px, 360px);
}
&.flag-icon-15x10 {
@include flags-list(15px, 10px, 225px);
}
}

@ -0,0 +1,68 @@
import React from 'react'
import map from 'lodash/map'
import { useLexicsStore } from 'features/LexicsStore'
import { OutsideClick } from 'features/OutsideClick'
import { useToggle } from 'hooks'
import { langsList } from './config'
import {
Wrapper,
WorldIcon,
LangsList,
LangsItem,
FlagIcon,
} from './styled'
import './flags.scss'
export const LanguageSelect = () => {
const { changeLang } = useLexicsStore()
const {
close,
isOpen,
open,
} = useToggle()
const handleLangChange = (locale: string) => () => {
changeLang(locale)
close()
}
return (
<OutsideClick onClick={close}>
<Wrapper>
<WorldIcon onClick={open} active={isOpen} />
{isOpen && (
<LangsList>
{
map(
langsList,
({
className,
locale,
title,
}) => (
<LangsItem
key={locale}
title={title}
onClick={handleLangChange(locale)}
>
<FlagIcon
className={`
flag-icon
flag-icon-24x16
flag-icon-${className}
`}
/>
</LangsItem>
),
)
}
</LangsList>
)}
</Wrapper>
</OutsideClick>
)
}

@ -0,0 +1,76 @@
import styled from 'styled-components/macro'
export const Wrapper = styled.div`
position: absolute;
right: 31px;
top: 31px;
`
type WorldIconProps = {
active?: boolean,
}
export const WorldIcon = styled.span<WorldIconProps>`
display: block;
width: 18px;
height: 18px;
background-image: url(/images/worldIcon.svg);
background-position-x: -1px;
cursor: pointer;
:hover {
opacity: 1;
}
${({ active }) => (
active
? 'opacity: 1;'
: 'opacity: 0.7;'
)}
`
export const LangsList = styled.ul`
display: flex;
flex-wrap: wrap;
width: 96px;
position: absolute;
background-color: #666666;
box-shadow: 0 2px 5px rgba(0,0,0,.5);
border-radius: 2px;
right: -16px;
top: 36px;
:before {
content: '';
width: 12px;
height: 12px;
transform: translateY(-50%) rotate(45deg);
position: absolute;
right: 19px;
background-color: #666666;
z-index: 0;
}
`
export const LangsItem = styled.li`
text-align: center;
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
z-index: 1;
border-radius: 2px;
:hover {
background-color: #999999;
cursor: pointer;
}
`
export const FlagIcon = styled.span`
display: inline-block;
width: 28px;
height: 16px;
transition: .3s;
`

@ -2,6 +2,7 @@ import React from 'react'
import { PAGES } from 'config'
import { T9n } from 'features/T9n'
import { Logo } from 'features/Logo'
import { Input, ButtonSolid } from 'features/Common'
import { formIds } from 'features/Register/components/RegistrationStep/config'
@ -24,7 +25,7 @@ export const Login = () => {
<CenterBlock>
<Logo />
<Form onSubmit={handleSubmit}>
<BlockTitle>Авторизация</BlockTitle>
<BlockTitle><T9n t='authorization' /></BlockTitle>
<Input
required

Loading…
Cancel
Save