mirror of
https://github.com/theoludwig/theoludwig.git
synced 2025-05-29 22:37:44 +02:00
feat: improve header to set locale + remove setup page
This commit is contained in:
@ -1,38 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
|
||||
export const BrandLogo: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<Link href='/'>
|
||||
<a className='Header__brand-link'>
|
||||
<Image
|
||||
width={65}
|
||||
height={65}
|
||||
src='/images/divlo_icon_small.png'
|
||||
alt="Divlo's Logo"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.Header__brand-link {
|
||||
display: inline-block;
|
||||
padding-top: 0.3125rem;
|
||||
padding-bottom: 0.3125rem;
|
||||
margin-right: 1rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: inherit;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@media (min-width: 993px) {
|
||||
.Header__brand-link {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
type HamburgerIconComponent = React.FC<{
|
||||
isActive: boolean
|
||||
handleToggleNavbar: () => void
|
||||
}>
|
||||
|
||||
export const HamburgerIcon: HamburgerIconComponent = props => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
onClick={props.handleToggleNavbar}
|
||||
className={classNames('Header__hamburger', {
|
||||
'Header__hamburger-active': props.isActive
|
||||
})}
|
||||
>
|
||||
<span />
|
||||
</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.Header__hamburger {
|
||||
display: none;
|
||||
width: 56px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 0.25rem;
|
||||
position: relative;
|
||||
}
|
||||
.Header__hamburger > span,
|
||||
.Header__hamburger > span::before,
|
||||
.Header__hamburger > span::after {
|
||||
position: absolute;
|
||||
width: 22px;
|
||||
height: 1.3px;
|
||||
background-color: rgba(255, 255, 255);
|
||||
}
|
||||
.Header__hamburger > span {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
}
|
||||
.Header__hamburger > span::before,
|
||||
.Header__hamburger > span::after {
|
||||
content: '';
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.Header__hamburger > span::before {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
.Header__hamburger > span::after {
|
||||
transform: translateY(8px);
|
||||
}
|
||||
.Header__hamburger-active span {
|
||||
background-color: transparent;
|
||||
}
|
||||
.Header__hamburger-active > span::before {
|
||||
transform: translateY(0px) rotateZ(45deg);
|
||||
}
|
||||
.Header__hamburger-active > span::after {
|
||||
transform: translateY(0px) rotateZ(-45deg);
|
||||
}
|
||||
/* Hamburger icon on Mobile */
|
||||
@media (max-width: 992px) {
|
||||
.Header__hamburger {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
16
components/Header/Language/Arrow.tsx
Normal file
16
components/Header/Language/Arrow.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
export const Arrow: React.FC = () => {
|
||||
return (
|
||||
<svg
|
||||
width='12'
|
||||
height='8'
|
||||
viewBox='0 0 12 8'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
d='M9.8024 0.292969L5.61855 4.58597L1.43469 0.292969L0.0566406 1.70697L5.61855 7.41397L11.1805 1.70697L9.8024 0.292969Z'
|
||||
fill='#fff'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
31
components/Header/Language/LanguageFlag.tsx
Normal file
31
components/Header/Language/LanguageFlag.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import Image from 'next/image'
|
||||
|
||||
export interface LanguageFlagProps {
|
||||
language: string
|
||||
}
|
||||
|
||||
export const LanguageFlag: React.FC<LanguageFlagProps> = (props) => {
|
||||
const { language } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
width={35}
|
||||
height={35}
|
||||
src={`/images/languages/${language}.svg`}
|
||||
alt={language}
|
||||
/>
|
||||
<p className='language-title'>{language.toUpperCase()}</p>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.language-title {
|
||||
margin: 0 8px 0 10px;
|
||||
font-size: 16px;
|
||||
font-family: 'Arial', 'sans-serif';
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
105
components/Header/Language/index.tsx
Normal file
105
components/Header/Language/index.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
import setLanguage from 'next-translate/setLanguage'
|
||||
|
||||
import { Arrow } from './Arrow'
|
||||
import { LanguageFlag } from './LanguageFlag'
|
||||
import { locales } from 'i18n.json'
|
||||
|
||||
export const Language: React.FC = () => {
|
||||
const { lang: currentLanguage } = useTranslation()
|
||||
const [hiddenMenu, setHiddenMenu] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (!hiddenMenu) {
|
||||
window.document.addEventListener('click', handleHiddenMenu)
|
||||
} else {
|
||||
window.document.removeEventListener('click', handleHiddenMenu)
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.document.removeEventListener('click', handleHiddenMenu)
|
||||
}
|
||||
}, [hiddenMenu])
|
||||
|
||||
const handleLanguage = async (language: string): Promise<void> => {
|
||||
await setLanguage(language)
|
||||
handleHiddenMenu()
|
||||
}
|
||||
|
||||
const handleHiddenMenu = (): void => {
|
||||
setHiddenMenu(!hiddenMenu)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='language-menu'>
|
||||
<div className='selected-language' onClick={handleHiddenMenu}>
|
||||
<LanguageFlag language={currentLanguage} />
|
||||
<Arrow />
|
||||
</div>
|
||||
{!hiddenMenu && (
|
||||
<ul>
|
||||
{locales.map((language, index) => {
|
||||
if (language === currentLanguage) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<li
|
||||
key={index}
|
||||
onClick={async () => await handleLanguage(language)}
|
||||
>
|
||||
<LanguageFlag language={language} />
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.language-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.selected-language {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 15px;
|
||||
}
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
width: 100px;
|
||||
padding: 10px;
|
||||
margin: 10px 15px 0 0px;
|
||||
border-radius: 15%;
|
||||
padding: 0;
|
||||
box-shadow: 0px 1px 10px var(--color-shadow);
|
||||
background-color: var(--color-background-primary);
|
||||
z-index: 10;
|
||||
}
|
||||
ul > li {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
}
|
||||
ul > li:hover {
|
||||
background-color: rgba(79, 84, 92, 0.16);
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import classNames from 'classnames'
|
||||
|
||||
type NavigationLinkComponent = React.FC<{ path: string }>
|
||||
|
||||
export const NavigationLink: NavigationLinkComponent = props => {
|
||||
const { pathname } = useRouter()
|
||||
const isCurrentPage = pathname === props.path
|
||||
|
||||
return (
|
||||
<>
|
||||
<li className='navbar-item'>
|
||||
<Link href={props.path}>
|
||||
<a
|
||||
className={classNames('navbar-link', {
|
||||
'navbar-link-active': isCurrentPage
|
||||
})}
|
||||
>
|
||||
{props.children}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.navbar-link {
|
||||
display: block;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
.navbar-link:hover {
|
||||
text-decoration: none;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
.navbar-link,
|
||||
.navbar-link-active {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.navbar-link-active,
|
||||
.navbar-link-active:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
.navbar-item {
|
||||
list-style: none;
|
||||
}
|
||||
.navbar-link {
|
||||
font-size: 16px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
import classNames from 'classnames'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
import { NavigationLink } from './NavigationLink'
|
||||
|
||||
type NavigationComponent = React.FC<{ isActive: boolean }>
|
||||
|
||||
export const Navigation: NavigationComponent = props => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className='Header__navbar'>
|
||||
<ul
|
||||
className={classNames('navbar__list', {
|
||||
'navbar__list-active': props.isActive
|
||||
})}
|
||||
>
|
||||
<NavigationLink path='/'>{t('common:home')}</NavigationLink>
|
||||
<NavigationLink path='/setup'>Setup</NavigationLink>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
@media (min-width: 992px) {
|
||||
.Header__navbar {
|
||||
display: flex;
|
||||
flex-basis: auto;
|
||||
}
|
||||
}
|
||||
.Header__navbar {
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
}
|
||||
.navbar__list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-left: auto;
|
||||
}
|
||||
.navbar__list.navbar__list-active {
|
||||
margin: 0 !important;
|
||||
display: flex;
|
||||
}
|
||||
@media (max-width: 992px) {
|
||||
.navbar__list {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,65 +1,84 @@
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
|
||||
import { HamburgerIcon } from './HamburgerIcon'
|
||||
import { BrandLogo } from './BrandLogo'
|
||||
import { Navigation } from './Navigation'
|
||||
import { Language } from './Language'
|
||||
|
||||
export const Header: React.FC = () => {
|
||||
const [isActive, setIsActive] = useState(false)
|
||||
|
||||
const handleToggleNavbar = (): void => {
|
||||
setIsActive(!isActive)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className='Header'>
|
||||
<header className='header'>
|
||||
<div className='container'>
|
||||
<BrandLogo />
|
||||
<HamburgerIcon
|
||||
isActive={isActive}
|
||||
handleToggleNavbar={handleToggleNavbar}
|
||||
/>
|
||||
<Navigation isActive={isActive} />
|
||||
<nav className='navbar navbar-fixed-top'>
|
||||
<Link href='/'>
|
||||
<a className='navbar__brand-link'>
|
||||
<div className='navbar__brand'>
|
||||
<Image
|
||||
width={60}
|
||||
height={60}
|
||||
src='/images/divlo_icon_small.png'
|
||||
alt='Divlo'
|
||||
/>
|
||||
<strong className='navbar__brand-title'>Divlo</strong>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className='navbar__buttons'>
|
||||
<Language />
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.Header {
|
||||
.header {
|
||||
background-color: var(--color-background);
|
||||
border-bottom: var(--border-header-footer);
|
||||
padding: 0.5rem 1rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
|
||||
height: var(--header-height);
|
||||
}
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.navbar {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 1rem;
|
||||
|
||||
border-bottom: var(--border-header-footer);
|
||||
background-color: var(--color-background);
|
||||
align-items: center;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.Header {
|
||||
display: flex;
|
||||
flex-basis: auto;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.navbar-fixed-top {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 200;
|
||||
}
|
||||
.Header > .container {
|
||||
.navbar__brand-link {
|
||||
color: var(--color-text-1);
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
}
|
||||
.navbar__brand {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.Header > .container {
|
||||
flex-wrap: nowrap;
|
||||
.navbar__brand-title {
|
||||
font-weight: 600;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.navbar__buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
@media (max-width: 320px) {
|
||||
.navbar__brand-title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`}
|
||||
|
Reference in New Issue
Block a user