diff options
Diffstat (limited to 'packages/website/ts/components/header.tsx')
-rw-r--r-- | packages/website/ts/components/header.tsx | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/packages/website/ts/components/header.tsx b/packages/website/ts/components/header.tsx new file mode 100644 index 000000000..90e097070 --- /dev/null +++ b/packages/website/ts/components/header.tsx @@ -0,0 +1,252 @@ +import { Link } from '@0x/react-shared'; +import _ from 'lodash'; +import * as React from 'react'; +import MediaQuery from 'react-responsive'; +import styled, { css, withTheme } from 'styled-components'; + +import Headroom from 'react-headroom'; + +import { Button } from 'ts/components/button'; +import { DropdownDevelopers } from 'ts/components/dropdowns/dropdown_developers'; +import { DropdownProducts } from 'ts/components/dropdowns/dropdown_products'; +import { Hamburger } from 'ts/components/hamburger'; +import { Logo } from 'ts/components/logo'; +import { MobileNav } from 'ts/components/mobileNav'; +import { FlexWrap } from 'ts/components/newLayout'; +import { ThemeValuesInterface } from 'ts/components/siteWrap'; +import { WebsitePaths } from 'ts/types'; +import { constants } from 'ts/utils/constants'; + +interface HeaderProps { + location?: Location; + isNavToggled?: boolean; + toggleMobileNav?: () => void; + theme: ThemeValuesInterface; +} + +interface NavItemProps { + url?: string; + id?: string; + text?: string; + dropdownWidth?: number; + dropdownComponent?: React.FunctionComponent<any>; + shouldOpenInNewTab?: boolean; +} + +interface DropdownWrapInterface { + width?: number; +} + +const navItems: NavItemProps[] = [ + { + id: 'why', + url: WebsitePaths.Why, + text: 'Why 0x', + }, + { + id: 'products', + text: 'Products', + dropdownComponent: DropdownProducts, + dropdownWidth: 280, + }, + { + id: 'developers', + text: 'Developers', + dropdownComponent: DropdownDevelopers, + dropdownWidth: 480, + }, + { + id: 'about', + url: WebsitePaths.AboutMission, + text: 'About', + }, + { + id: 'blog', + url: constants.URL_BLOG, + shouldOpenInNewTab: true, + text: 'Blog', + }, +]; + +class HeaderBase extends React.Component<HeaderProps> { + public onUnpin = () => { + if (this.props.isNavToggled) { + this.props.toggleMobileNav(); + } + }; + + public render(): React.ReactNode { + const { isNavToggled, toggleMobileNav, theme } = this.props; + + return ( + <Headroom onUnpin={this.onUnpin} downTolerance={4} upTolerance={10}> + <StyledHeader isNavToggled={isNavToggled}> + <HeaderWrap> + <Link to={WebsitePaths.Home}> + <Logo /> + </Link> + + <NavLinks> + {_.map(navItems, (link, index) => <NavItem key={`navlink-${index}`} link={link} />)} + </NavLinks> + + <MediaQuery minWidth={990}> + <TradeButton bgColor={theme.headerButtonBg} color="#ffffff" href="/portal"> + Trade on 0x + </TradeButton> + </MediaQuery> + + <Hamburger isOpen={isNavToggled} onClick={toggleMobileNav} /> + <MobileNav isToggled={isNavToggled} toggleMobileNav={toggleMobileNav} /> + </HeaderWrap> + </StyledHeader> + </Headroom> + ); + } +} + +export const Header = withTheme(HeaderBase); + +const NavItem = (props: { link: NavItemProps; key: string }) => { + const { link } = props; + const Subnav = link.dropdownComponent; + const linkElement = _.isUndefined(link.url) ? ( + <StyledAnchor href="#">{link.text}</StyledAnchor> + ) : ( + <StyledNavLink to={link.url} shouldOpenInNewTab={link.shouldOpenInNewTab}> + {link.text} + </StyledNavLink> + ); + return ( + <LinkWrap> + {linkElement} + + {link.dropdownComponent && ( + <DropdownWrap width={link.dropdownWidth}> + <Subnav /> + </DropdownWrap> + )} + </LinkWrap> + ); +}; + +const StyledHeader = + styled.header < + HeaderProps > + ` + padding: 30px; + background-color: ${props => props.theme.bgColor}; +`; + +const LinkWrap = styled.li` + position: relative; + + a { + display: block; + } + + @media (min-width: 800px) { + &:hover > div { + display: block; + visibility: visible; + opacity: 1; + transform: translate3d(0, 0, 0); + transition: opacity 0.35s, transform 0.35s, visibility 0s; + } + } +`; + +const linkStyles = css` + color: ${props => props.theme.textColor}; + opacity: 0.5; + transition: opacity 0.35s; + padding: 15px 0; + margin: 0 30px; + + &:hover { + opacity: 1; + } +`; + +const StyledNavLink = styled(Link).attrs({ + activeStyle: { opacity: 1 }, +})` + ${linkStyles}; +`; + +const StyledAnchor = styled.a` + ${linkStyles}; + cursor: default; +`; + +const HeaderWrap = styled(FlexWrap)` + justify-content: space-between; + align-items: center; + + @media (max-width: 800px) { + padding-top: 0; + display: flex; + padding-bottom: 0; + } +`; + +const NavLinks = styled.ul` + display: flex; + align-items: center; + justify-content: space-between; + + @media (max-width: 800px) { + display: none; + } +`; + +const DropdownWrap = + styled.div < + DropdownWrapInterface > + ` + width: ${props => props.width || 280}px; + padding: 15px 0; + border: 1px solid transparent; + border-color: ${props => props.theme.dropdownBorderColor}; + background-color: ${props => props.theme.dropdownBg}; + color: ${props => props.theme.dropdownColor}; + position: absolute; + top: 100%; + left: calc(50% - ${props => (props.width || 280) / 2}px); + visibility: hidden; + opacity: 0; + transform: translate3d(0, -10px, 0); + transition: opacity 0.35s, transform 0.35s, visibility 0s 0.35s; + z-index: 20; + + &:after, &:before { + bottom: 100%; + left: 50%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + } + &:after { + border-color: rgba(255, 255, 255, 0); + border-bottom-color: ${props => props.theme.dropdownBg}; + border-width: 10px; + margin-left: -10px; + } + &:before { + border-color: rgba(255, 0, 0, 0); + border-bottom-color: ${props => props.theme.dropdownBorderColor}; + border-width: 11px; + margin-left: -11px; + } + + @media (max-width: 768px) { + display: none; + } +`; + +const TradeButton = styled(Button)` + padding: 14px 22px !important; +`; |