UNPKG

@axeptio/design-system

Version:
273 lines (242 loc) 6.92 kB
import React, { useState } from 'react'; import styled from 'styled-components'; import { RoughNotation } from 'react-rough-notation'; import { Flipper } from 'react-flip-toolkit'; import PropTypes from 'prop-types'; import Icon from '../../Icon'; import DropdownContainer from './dropDownContainer'; import generateNavBar from './dropDown'; const menuColors = [ '#fac6a1', '#a9ddd7', '#afc2e7', '#f7daa6', '#f9a6a2', '#f8cfb6', '#fac6a1', '#a9ddd7', '#afc2e7', '#f7daa6', '#f9a6a2', '#f8cfb6' ]; const NavItemIconDropDown = styled.span` width: 24px; display: flex; justify-content: center; align-items: center; transition: transform 0.3s ease-in-out; ${props => props.show && ` transform: rotate(180deg); `} `; const NavItemButtonContent = styled.span` display: flex; flex-direction: row; flex-wrap: no-wrap; height: 100%; -webkit-box-pack: center; justify-content: center; -webkit-box-align: center; align-items: center; `; const NavItemButtonContainer = styled.span` display: flex; height: 100%; -webkit-box-pack: center; justify-content: center; -webkit-box-align: center; align-items: center; `; const NavItemButton = styled.div` font-weight: 400; display: flex; align-items: center; background: transparent; border: 0; font-weight: 400; font-family: inherit; font-size: 17px; padding: 0 20px; height: 100%; color: ${props => props.theme.colors.secondary}; justify-content: center; white-space: nowrap; transition: opacity 250ms; cursor: pointer; position: relative; z-index: 2; &:focus { outline: none; color: ${props => props.theme.colors.grey.v500}; } &:visited { color: ${props => props.theme.colors.secondary}; } &:active { outline: none; color: ${props => props.theme.colors.secondary}; } span { display: flex; height: 100%; justify-content: center; align-items: center; } &:link { text-decoration: none; } &:visited { text-decoration: none; } &:hover { text-decoration: none; } &:active { text-decoration: none; } `; const NavItem = styled.li` position: relative; margin: 0px; font-family: inherit; `; const Main = styled.div` display: none; font-family: ${props => props.theme.fonts.text}; @media (min-width: ${props => props.theme.breakpoints.xlarge}px) { display: flex; justify-content: center; } `; const Root = styled.ul` display: flex; -webkit-box-pack: center; justify-content: center; list-style: none; margin: 0px; padding: 0px; font-family: inherit; `; const DropdownSlot = styled.div` position: absolute; left: 50%; transform: translateX(-50%); perspective: 1500px; z-index: 10; `; const Menu = ({ menu, test, id }) => { let navbar = generateNavBar(menu, test); let [hover, setHover] = useState(null); const [activeIndices, setActiveIndices] = React.useState([]); const [animatingOut, setAnimatingOut] = React.useState(false); const [animatingOutTimeout, setAnimatingOutTimeout] = React.useState(null); const resetDropdownState = i => { setActiveIndices(typeof i === 'number' ? [i] : []); setAnimatingOut(false); setAnimatingOutTimeout(null); }; const onMouseEnter = i => { setHover(i); if (animatingOutTimeout) { clearTimeout(animatingOutTimeout); resetDropdownState(i); return; } if (activeIndices[activeIndices.length - 1] === i) { return; } // If there is no dropdown for the item if (!navbar[i]?.dropdown) { onMouseLeave(i); return; } setActiveIndices(prevState => prevState.concat(i)); setAnimatingOut(false); }; const onMouseLeave = i => { setHover(i ? i : null); setAnimatingOut(true); const timer = setTimeout(resetDropdownState, duration); setAnimatingOutTimeout(timer); }; let CurrentDropdown; let PrevDropdown; let direction; const currentIndex = activeIndices[activeIndices.length - 1]; const prevIndex = activeIndices.length > 1 && activeIndices[activeIndices.length - 2]; // current if (typeof currentIndex === 'number') { CurrentDropdown = navbar[currentIndex].dropdown; } // previous if (typeof prevIndex === 'number') { PrevDropdown = navbar[prevIndex].dropdown; direction = currentIndex > prevIndex ? 'right' : 'left'; } const showDropdown = !!CurrentDropdown; const duration = 300; return ( <Main id={id ? id : 'main_menu'}> <Flipper flipKey={currentIndex} spring={duration === 300 ? 'noWobble' : { stiffness: 10, damping: 10 }}> <Root onMouseLeave={onMouseLeave}> {menu.map((elem, index) => { const roughNotationProps = { show: index === hover && (elem?.noRoughAnimation ? !elem?.noRoughAnimation : true), type: 'underline', padding: elem?.submenu ? -25 : 5, iterations: 1, strokeWidth: 3, color: menuColors[index], animationDuration: 300 }; return ( <NavItem index={index} key={index}> <NavItemButton id={elem.name} onMouseEnter={() => { onMouseEnter(index); }} href={elem?.submenu?.length > 0 ? '' : elem?.link} as={elem?.submenu?.length > 0 ? 'button' : 'a'} > <NavItemButtonContainer> <RoughNotation {...roughNotationProps}> <NavItemButtonContent> {elem.name} {elem?.submenu ? ( <NavItemIconDropDown show={index === hover && (elem?.noAnimatedIcon ? !elem?.noAnimatedIcon : true)} name={test ? elem.icon : 'ArrowDown'} > <Icon iconSize={24} iconWidth={15} name={test ? elem.icon : 'ArrowDown'} /> </NavItemIconDropDown> ) : null} </NavItemButtonContent> </RoughNotation> </NavItemButtonContainer> </NavItemButton> {currentIndex === index && showDropdown ? ( <DropdownSlot> <DropdownContainer direction={direction} animatingOut={animatingOut} duration={duration}> <CurrentDropdown /> {PrevDropdown && <PrevDropdown />} </DropdownContainer> </DropdownSlot> ) : null} </NavItem> ); })} </Root> </Flipper> </Main> ); }; Menu.propTypes = { menu: PropTypes.array.isRequired, test: PropTypes.bool, id: PropTypes.string }; export default Menu;