Add language selector
This commit is contained in:
parent
844a63d085
commit
8f5ddf1f72
|
@ -18,8 +18,11 @@
|
|||
"build": "webpack --mode production --progress"
|
||||
},
|
||||
"dependencies": {
|
||||
"i18next": "^21.5.2",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-i18next": "^11.14.2",
|
||||
"react-is": "^17.0.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"styled-components": "^5.2.1"
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="40" height="20" viewBox="0 0 44 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M42 2L22 22L2 2" stroke="white" stroke-width="5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 163 B |
|
@ -1,26 +1,29 @@
|
|||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { Colors } from '../constants'
|
||||
import arrow from '../assets/arrow.svg'
|
||||
import { Colors } from '../../constants/styles'
|
||||
import arrow from '../../assets/arrow.svg'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export function Navigation() {
|
||||
const { t, i18n } = useTranslation()
|
||||
|
||||
return (
|
||||
<Nav>
|
||||
<NavLinks>
|
||||
<NavItem path="example" link="THE PANCULTURISTS Party" sublink="Copy, Read Print" subpath="example/read" />
|
||||
<NavItem path="example" link="OUR MANIFESTO" />
|
||||
<NavItem path="example" link="COMMON POLICIES" />
|
||||
<NavItem path="example" link="BECOME A MEMBER" sublink="REGULAR OR INDIVIDUAL" subpath="example/types" />
|
||||
<NavItem path="example" link="CONTRIBUTE" sublink="SEE ALL OPEN BOUNTIES" subpath="example/bounties" />
|
||||
<NavItem path="example" link="THE PEOPLE’S FREE PRESS" sublink="Copy, Read Print" subpath="example/read" />
|
||||
<NavItem path="example" link="GENERAL ASSEMBLIES" />
|
||||
<NavItem path="example" link="WIKIPEDIA" />
|
||||
<NavItem path="example" link="THE BOARD " />
|
||||
<NavItem path="example" link="WHAT IS THE NETWORK STATE?" />
|
||||
<NavItem path="example" link="DISCUSS FORUM" />
|
||||
<NavItem path="example" link="THE PEOPLE’S SHOP" sublink="Copy, Read Print" subpath="example/read" />
|
||||
<NavItem path="example" link="CALENDAR & EVENTS" />
|
||||
<NavItem path="example" link={t('party')} sublink={t('nav_sub')} subpath="example/read" />
|
||||
<NavItem path="example" link={t('manifesto')} />
|
||||
<NavItem path="example" link={t('policies')} />
|
||||
<NavItem path="example" link={t('membership')} sublink={t('membership_types')} subpath="example/types" />
|
||||
<NavItem path="example" link={t('contribute')} sublink={t('contribute_bounties')} subpath="example/bounties" />
|
||||
<NavItem path="example" link={t('press')} sublink={t('nav_sub')} subpath="example/read" />
|
||||
<NavItem path="example" link={t('assamblies')} />
|
||||
<NavItem path="example" link={t('wiki')} />
|
||||
<NavItem path="example" link={t('board')} />
|
||||
<NavItem path="example" link={t('network')} />
|
||||
<NavItem path="example" link={t('forum')} />
|
||||
<NavItem path="example" link={t('shop')} sublink={t('nav_sub')} subpath="example/read" />
|
||||
<NavItem path="example" link={t('calendar')} />
|
||||
</NavLinks>
|
||||
</Nav>
|
||||
)
|
|
@ -0,0 +1,35 @@
|
|||
import React from 'react'
|
||||
import { Select } from './Select'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
import { Colors } from '../../constants/styles'
|
||||
|
||||
const languages = [
|
||||
{ name: 'EN', code: 'en' },
|
||||
{ name: 'NL', code: 'nl' },
|
||||
]
|
||||
|
||||
export const LanguageSelector = () => {
|
||||
const { t, i18n } = useTranslation()
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={t('language_name')}
|
||||
list={languages.map(({ name, code }) => (
|
||||
<Button key={code} onClick={() => i18n.changeLanguage(code)}>
|
||||
{name}
|
||||
</Button>
|
||||
))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const Button = styled.button`
|
||||
outline: none;
|
||||
color: ${Colors.White};
|
||||
font-size: calc(24px + (36 - 24) * ((100vw - 320px) / (1440 - 320)));
|
||||
|
||||
&:hover {
|
||||
color: ${Colors.Gray};
|
||||
}
|
||||
`
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { Colors } from '../../constants/styles'
|
||||
|
@ -6,22 +7,25 @@ import { DiscordIcon } from '../icons/DiscordIcon'
|
|||
import { GithubIcon } from '../icons/GithubIcon'
|
||||
import { TwitterIcon } from '../icons/TwitterIcon'
|
||||
import { MenuContent } from './Content'
|
||||
import { LanguageSelector } from './LanguageSelector'
|
||||
|
||||
export function Menu() {
|
||||
const { t, i18n } = useTranslation()
|
||||
|
||||
return (
|
||||
<NavigationMenu>
|
||||
<TopNavigation>
|
||||
<TopLink to="/">Home</TopLink>
|
||||
<TopLink to="/support">Support us</TopLink>
|
||||
<Dropdown></Dropdown>
|
||||
<TopLink to="/">{t('home')}</TopLink>
|
||||
<TopLink to="/support">{t('support')}</TopLink>
|
||||
<LanguageSelector />
|
||||
</TopNavigation>
|
||||
<Navigation>
|
||||
<NavLinks>
|
||||
<StyledLink to="/">WHAT’s ON</StyledLink>
|
||||
<StyledLink to="/">NEWS & PRESS</StyledLink>
|
||||
<StyledLink to="/">JOIN US</StyledLink>
|
||||
<StyledLink to="/">BECOME A MEMBER</StyledLink>
|
||||
<StyledLink to="/">CONTRIBUTE</StyledLink>
|
||||
<StyledLink to="/">{t('whats_on')}</StyledLink>
|
||||
<StyledLink to="/">{t('news')}</StyledLink>
|
||||
<StyledLink to="/">{t('join')}</StyledLink>
|
||||
<StyledLink to="/">{t('membership')}</StyledLink>
|
||||
<StyledLink to="/">{t('contribute')}</StyledLink>
|
||||
</NavLinks>
|
||||
</Navigation>
|
||||
<SocialLinks>
|
||||
|
@ -39,14 +43,6 @@ export function Menu() {
|
|||
)
|
||||
}
|
||||
|
||||
export function Dropdown() {
|
||||
return (
|
||||
<MenuBlock>
|
||||
<MenuList></MenuList>
|
||||
</MenuBlock>
|
||||
)
|
||||
}
|
||||
|
||||
const NavigationMenu = styled(MenuContent)`
|
||||
background-color: ${Colors.Black};
|
||||
position: absolute;
|
||||
|
@ -59,6 +55,7 @@ const NavigationMenu = styled(MenuContent)`
|
|||
`
|
||||
const TopNavigation = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
color: ${Colors.White};
|
||||
padding: 24px;
|
||||
`
|
||||
|
@ -107,14 +104,6 @@ const StyledLink = styled(Link)`
|
|||
}
|
||||
`
|
||||
|
||||
const MenuBlock = styled.div`
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const MenuList = styled.ul`
|
||||
list-style: none;
|
||||
`
|
||||
|
||||
const SocialLinks = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import React, { ReactNode, useRef } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { Colors } from '../../constants/styles'
|
||||
import arrowDown from '../../assets/arrow-down.svg'
|
||||
import { useClickOutside } from '../../hooks/useClickOutside'
|
||||
import { useToggler } from '../../hooks/useToggler'
|
||||
|
||||
export interface SelectProps {
|
||||
list: ReactNode[]
|
||||
value: any
|
||||
}
|
||||
|
||||
export const Select = ({ value, list }: SelectProps) => {
|
||||
const { visible, toggle } = useToggler()
|
||||
const ref = useRef(null)
|
||||
useClickOutside(ref, () => toggle(false))
|
||||
|
||||
return (
|
||||
<SelectWrapper ref={ref}>
|
||||
{visible && (
|
||||
<SelectContent>
|
||||
{list.map((item, index) => (
|
||||
<SelectItem key={index}>{item}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
)}
|
||||
<SelectButton onClick={() => toggle()} className={visible ? 'open' : ''}>
|
||||
{value}
|
||||
</SelectButton>
|
||||
</SelectWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
const SelectWrapper = styled.div`
|
||||
position: relative;
|
||||
width: 120px;
|
||||
color: ${Colors.White};
|
||||
`
|
||||
|
||||
const SelectButton = styled.button`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background: ${Colors.Black};
|
||||
color: ${Colors.White};
|
||||
font-size: calc(24px + (36 - 24) * ((100vw - 320px) / (1440 - 320)));
|
||||
text-decoration: underline;
|
||||
padding-right: 50px;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
transform: translateY(-50%);
|
||||
background: url(${arrowDown}) center no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
&.open::after {
|
||||
transform: translateY(-50%) rotate(180deg);
|
||||
}
|
||||
`
|
||||
|
||||
const SelectContent = styled.ul`
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
bottom: -12px;
|
||||
width: 100%;
|
||||
transform: translateY(100%);
|
||||
overflow-y: auto;
|
||||
background: ${Colors.Black};
|
||||
color: ${Colors.White};
|
||||
z-index: 1;
|
||||
`
|
||||
|
||||
const SelectItem = styled.li`
|
||||
& + & {
|
||||
margin-top: 20px;
|
||||
}
|
||||
`
|
|
@ -0,0 +1,17 @@
|
|||
import { useEffect, RefObject } from 'react'
|
||||
|
||||
export const useClickOutside = (ref: RefObject<HTMLDivElement>, callback: () => void, deps?: any[]) => {
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(e.target as HTMLInputElement)) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('mousedown', handleClick)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClick)
|
||||
}
|
||||
}, deps)
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import { useState } from 'react'
|
||||
|
||||
export const useToggler = () => {
|
||||
const [visible, setVisibility] = useState(false)
|
||||
const toggle = (value?: boolean) => {
|
||||
if (value === undefined) {
|
||||
setVisibility((visible) => !visible)
|
||||
} else {
|
||||
setVisibility(() => value)
|
||||
}
|
||||
}
|
||||
|
||||
return { visible, toggle }
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import i18n from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||
import en from './translations/en.json'
|
||||
import nl from './translations/nl.json'
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: en,
|
||||
},
|
||||
nl: {
|
||||
translation: nl,
|
||||
},
|
||||
}
|
||||
|
||||
i18n
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
debug: true,
|
||||
lng: 'en',
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
resources,
|
||||
})
|
||||
|
||||
export default i18n
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
import ReactDOM from 'react-dom'
|
||||
import { App } from './App'
|
||||
import { Providers } from './providers'
|
||||
import './i18n'
|
||||
|
||||
ReactDOM.render(
|
||||
<Providers>
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import styled from 'styled-components'
|
||||
import { Page, MenuContent } from '../components'
|
||||
import { Logo } from '../components/top/Logo'
|
||||
import { Navigation } from '../components/Navigation'
|
||||
import { Navigation } from '../components/home/Navigation'
|
||||
|
||||
export function Home() {
|
||||
return (
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"language_name": "EN",
|
||||
"home": "Home",
|
||||
"support": "Support us",
|
||||
"whats_on": "WHAT’s ON",
|
||||
"news": "NEWS & PRESS",
|
||||
"join": "JOIN US",
|
||||
"membership":"BECOME A MEMBER",
|
||||
"membership_types":"REGULAR OR INDIVIDUAL",
|
||||
"contribute": "CONTRIBUTE",
|
||||
"contribute_bounties": "SEE ALL OPEN BOUNTIES",
|
||||
"nav_sub":"Copy, Read, Print",
|
||||
"party": "THE PANCULTURISTS Party",
|
||||
"manifesto": "OUR MANIFESTO",
|
||||
"policies": "COMMON POLICIES",
|
||||
"press": "THE PEOPLE’S FREE PRESS",
|
||||
"assamblies": "GENERAL ASSEMBLIES",
|
||||
"wiki": "WIKIPEDIA",
|
||||
"board": "THE BOARD",
|
||||
"network": "WHAT IS THE NETWORK STATE?",
|
||||
"forum": "DISCUSS FORUM",
|
||||
"shop": "THE PEOPLE’S SHOP",
|
||||
"calendar": "CALENDAR & EVENTS"
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"language_name": "NL",
|
||||
"home": "Huis",
|
||||
"support": "Steun ons",
|
||||
"whats_on": "Wat is er op",
|
||||
"news": "NIEUWS & PERS",
|
||||
"join": "DOE MET ONS MEE",
|
||||
"membership":"LID WORDEN",
|
||||
"membership_types":"REGULAR OR INDIVIDUAL",
|
||||
"contribute": "BIJDRAGE",
|
||||
"contribute_bounties": "ZIE ALLE OPEN BUNTIES",
|
||||
"nav_sub":"Kopiëren, Lezen, Afdrukken",
|
||||
"party": "DE PANCULTURISTEN Partij",
|
||||
"manifesto": "ONS MANIFEST",
|
||||
"policies": "GEMEENSCHAPPELIJK BELEID",
|
||||
"press": "DE GRATIS PERS VAN DE MENSEN",
|
||||
"assamblies": "ALGEMENE VERGADERING",
|
||||
"wiki": "WIKIPEDIA",
|
||||
"board": "HET BORD",
|
||||
"network": "WAT IS DE NETWERKSTAAT?",
|
||||
"forum": "BESPREEK FORUM",
|
||||
"shop": "DE MENSEN WINKEL",
|
||||
"calendar": "KALENDER & EVENEMENTEN"
|
||||
}
|
41
yarn.lock
41
yarn.lock
|
@ -89,6 +89,13 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.12.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6":
|
||||
version "7.16.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
|
||||
integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.12.7":
|
||||
version "7.12.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc"
|
||||
|
@ -2977,6 +2984,13 @@ html-minifier-terser@^5.0.1:
|
|||
relateurl "^0.2.7"
|
||||
terser "^4.6.3"
|
||||
|
||||
html-parse-stringify@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
|
||||
integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
|
||||
dependencies:
|
||||
void-elements "3.1.0"
|
||||
|
||||
html-webpack-plugin@^4.5.0:
|
||||
version "4.5.1"
|
||||
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.1.tgz#40aaf1b5cb78f2f23a83333999625c20929cda65"
|
||||
|
@ -3084,6 +3098,20 @@ human-signals@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
|
||||
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
|
||||
|
||||
i18next-browser-languagedetector@^6.1.2:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.2.tgz#68565a28b929cbc98ab6a56826ef2faf0e927ff8"
|
||||
integrity sha512-YDzIGHhMRvr7M+c8B3EQUKyiMBhfqox4o1qkFvt4QXuu5V2cxf74+NCr+VEkUuU0y+RwcupA238eeolW1Yn80g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.14.6"
|
||||
|
||||
i18next@^21.5.2:
|
||||
version "21.5.2"
|
||||
resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.5.2.tgz#ababe6fb0a769360035ebf06a36751e0b810cdfa"
|
||||
integrity sha512-Iuztr2+7CPCh5SYQV0utw2HXMx1za18xfznrw/PmgX+98oIpm84bhIM7VUPODjLycwIZ299oP7sEVQ9oCgmzfg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.0"
|
||||
|
||||
iconv-lite@0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
|
@ -4731,6 +4759,14 @@ react-dom@^17.0.1:
|
|||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.1"
|
||||
|
||||
react-i18next@^11.14.2:
|
||||
version "11.14.2"
|
||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.14.2.tgz#2ff28f6a0ddf06eaf79435ed6c70c441d709cf34"
|
||||
integrity sha512-fmDhwNA0zDmSEL3BBT5qwNMvxrKu25oXDDAZyHprfB0AHZmWXfBmRLf8MX8i1iBd2I2C2vsA2D9wxYBIwzooEQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.14.5"
|
||||
html-parse-stringify "^3.0.1"
|
||||
|
||||
"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1:
|
||||
version "17.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
||||
|
@ -6022,6 +6058,11 @@ vm-browserify@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
|
||||
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
|
||||
|
||||
void-elements@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
||||
integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
|
||||
|
||||
w3c-hr-time@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
|
||||
|
|
Loading…
Reference in New Issue