@axeptio/design-system
Version:
Design System for Axeptio
307 lines (266 loc) • 7.1 kB
JSX
/* eslint-disable indent */
import { useContext, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import styled, { ThemeContext } from 'styled-components';
import { hrefResolver } from '../../../helpers';
import Icon from '../../Icon';
const Root = styled.div`
position: relative;
display: flex;
`;
const Label = styled.span`
display: none;
margin-left: 8px;
position: relative;
font-weight: 700;
&::after {
content: '';
position: absolute;
left: 0;
bottom: -2px;
width: 0;
height: 1.5px;
background-color: ${props => props.theme.colors.secondary};
transition: width 0.3s;
}
${props =>
props.darkTheme &&
`
color: #ffffff;
&::after {
background-color: #ffffff;
}
`};
@media (min-width: ${props => props.theme.breakpoints.small}px) {
display: inline;
}
`;
const LabelWrapper = styled.button`
display: flex;
align-items: center;
justify-content: center;
background: none;
cursor: pointer;
padding: 20px 0;
border-radius: 2px;
min-width: clamp(38px, 10vw, 44px);
&:hover {
span:not(.svg-icon)::after {
width: 100%;
}
}
`;
const Dropdown = styled.div`
z-index: 1;
position: fixed;
left: 0;
right: 0;
bottom: 0;
transition:
opacity 0.25s ease,
transform 0.3s;
${props => !props.isOpen && 'height: 0;'};
@media (min-width: ${props => props.theme.breakpoints.large}px) {
position: absolute;
right: auto;
bottom: auto;
top: 100%;
transform-origin: 0 0;
${props =>
`
left: calc(-${props.rightSpace}px - 10px);
`}
${props =>
!props.isOpen &&
`
opacity: 0;
transform: rotateX(-15deg);
visibility: collapse;
`};
}
`;
const LanguageLabel = styled.li`
padding: 4px 20px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
color: ${props => props.theme.colors.grey.v400};
display: inline-block;
@media (min-width: ${props => props.theme.breakpoints.large}px) {
display: none;
}
`;
const DropdownMenu = styled.div`
position: relative;
min-width: 200px;
min-height: min(250px, 100vh);
max-height: 70vh;
background: #ffffff;
box-shadow:
0 5.5px 2.2px rgba(0, 0, 0, 0.028),
0 18.3px 13.9px rgba(0, 0, 0, 0.042),
0 82px 80px rgba(0, 0, 0, 0.07);
border-radius: 0;
overflow-y: auto;
transform: translateY(100%);
transition: all 0.25s ease 0s;
> ul {
display: block;
list-style: none;
padding: 0;
margin: 20px 4px;
}
${props =>
props.isOpen &&
`
transform: translateY(0);
`};
@media (min-width: ${props => props.theme.breakpoints.large}px) {
min-height: auto;
border-radius: 8px;
transform: none;
> ul {
margin: 6px 0;
}
&:hover li {
a {
opacity: 0.4;
}
&:hover {
a {
opacity: 1;
}
.svg-icon {
opacity: 1;
transform: translateX(0);
}
}
}
}
`;
const LanguageRow = styled.li`
> span,
a {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 10px;
padding: 10px 30px;
font-size: 16px;
font-weight: 700;
text-decoration: none;
> span {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 10px;
&:first-child {
flex: 1;
}
}
@media (min-width: ${props => props.theme.breakpoints.large}px) {
> span {
column-gap: 35px;
}
}
}
a {
color: ${props => props.theme.colors.secondary};
transition: all 0.25s cubic-bezier(0.5, 0, 0, 1);
.svg-icon {
opacity: 0;
transform: translateX(-10px);
transition: all 0.25s cubic-bezier(0.5, 0, 0, 1);
}
}
> span {
color: ${props => props.theme.colors.primaryPalette.v500};
}
`;
const Overlay = styled.div`
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.2);
transition: all 0.25s ease 0s;
opacity: 0;
pointer-events: none;
${props =>
props.isOpen &&
`
opacity: 1;
pointer-events: auto;
`};
@media (min-width: ${props => props.theme.breakpoints.large}px) {
display: none;
}
`;
const LanguageDropdown = ({ darkTheme, currentLang, languages, altLangs }) => {
const dropdownRef = useRef();
const [dropdownOpen, setDropdownOpen] = useState(false);
const themeContext = useContext(ThemeContext);
const [dropdownRightSpace, setDropdownRightSpace] = useState(0);
const toggleDropdown = newState => {
if (newState) {
updateDropdownPos();
}
setDropdownOpen(newState);
};
const updateDropdownPos = () => {
const parentBounds = dropdownRef.current.parentElement.getBoundingClientRect();
const bounds = dropdownRef.current.getBoundingClientRect();
const overflow = Math.ceil(parentBounds.x + bounds.width - document.body.clientWidth);
setDropdownRightSpace(Math.max(overflow, 0));
};
useEffect(() => {
updateDropdownPos();
}, [dropdownRef]);
return (
<Root
onMouseLeave={() => toggleDropdown(false)}
onBlur={e => {
if (e.relatedTarget && !e.currentTarget.contains(e.relatedTarget)) {
toggleDropdown(false);
}
}}
>
<LabelWrapper onMouseEnter={() => toggleDropdown(true)} onFocus={() => toggleDropdown(true)}>
<Icon name="Planet" iconSize={16} strokeColor={darkTheme ? '#ffffff' : '#212121'} />
<Label darkTheme={darkTheme} role="lang">
{currentLang.toUpperCase()}
</Label>
</LabelWrapper>
<Dropdown ref={dropdownRef} isOpen={dropdownOpen} rightSpace={dropdownRightSpace}>
<DropdownMenu isOpen={dropdownOpen}>
<ul>
<LanguageLabel>{languages[currentLang].heading}</LanguageLabel>
{Object.values(languages).map(language => (
<LanguageRow key={language.locale}>
{language.locale === currentLang ? (
<span onClick={() => toggleDropdown(!dropdownOpen)}>
<span>{language.label}</span>
<Icon noStroke name="Check" iconSize={20} fillColor={themeContext.colors.primaryPalette.v500} />
</span>
) : (
<a href={hrefResolver(altLangs.find(x => x && x.lang == language.locale))} tabIndex={dropdownOpen ? 0 : -1}>
<span onClick={() => toggleDropdown(!dropdownOpen)}>
{language.label}
<Icon name="ArrowNext" iconSize={20} strokeColor={themeContext.colors.secondary} />
</span>
</a>
)}
</LanguageRow>
))}
</ul>
</DropdownMenu>
</Dropdown>
<Overlay isOpen={dropdownOpen} onClick={() => toggleDropdown(false)} />
</Root>
);
};
LanguageDropdown.propTypes = {
darkTheme: PropTypes.bool,
currentLang: PropTypes.string,
languages: PropTypes.array,
altLangs: PropTypes.array
};
export default LanguageDropdown;