WIP mobile header + developer dropdown
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import { ALink, Link } from '@0x/react-shared';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { colors } from 'ts/style/colors';
|
||||
import { Container } from 'ts/components/ui/container';
|
||||
import { DropDown } from 'ts/components/ui/drop_down';
|
||||
import { Heading, Paragraph } from 'ts/@next/components/text';
|
||||
import { Deco, Key, WebsitePaths } from 'ts/types';
|
||||
import { constants } from 'ts/utils/constants';
|
||||
import { Translate } from 'ts/utils/translate';
|
||||
|
||||
const gettingStartedKeyToLinkInfo1: ALink[] = [
|
||||
{
|
||||
title: Key.BuildARelayer,
|
||||
to: `${WebsitePaths.Wiki}#Build-A-Relayer`,
|
||||
},
|
||||
{
|
||||
title: Key.OrderBasics,
|
||||
to: `${WebsitePaths.Wiki}#Create,-Validate,-Fill-Order`,
|
||||
},
|
||||
];
|
||||
const gettingStartedKeyToLinkInfo2: ALink[] = [
|
||||
{
|
||||
title: Key.DevelopOnEthereum,
|
||||
to: `${WebsitePaths.Wiki}#Ethereum-Development`,
|
||||
},
|
||||
{
|
||||
title: Key.UseNetworkedLiquidity,
|
||||
to: `${WebsitePaths.Wiki}#Find,-Submit,-Fill-Order-From-Relayer`,
|
||||
},
|
||||
];
|
||||
const popularDocsToLinkInfos: ALink[] = [
|
||||
{
|
||||
title: Key.ZeroExJs,
|
||||
to: WebsitePaths.ZeroExJs,
|
||||
},
|
||||
{
|
||||
title: Key.Connect,
|
||||
to: WebsitePaths.Connect,
|
||||
},
|
||||
{
|
||||
title: Key.SmartContract,
|
||||
to: WebsitePaths.SmartContracts,
|
||||
},
|
||||
];
|
||||
const usefulLinksToLinkInfo: ALink[] = [
|
||||
{
|
||||
title: Key.Wiki,
|
||||
to: WebsitePaths.Wiki,
|
||||
},
|
||||
{
|
||||
title: Key.Github,
|
||||
to: constants.URL_GITHUB_ORG,
|
||||
shouldOpenInNewTab: true,
|
||||
},
|
||||
{
|
||||
title: Key.Whitepaper,
|
||||
to: WebsitePaths.Whitepaper,
|
||||
shouldOpenInNewTab: true,
|
||||
},
|
||||
];
|
||||
|
||||
interface DevelopersDropDownProps {
|
||||
location: Location;
|
||||
}
|
||||
|
||||
interface DevelopersDropDownState {}
|
||||
|
||||
export class DevelopersDropDown extends React.Component<DevelopersDropDownProps, DevelopersDropDownState> {
|
||||
public render(): React.ReactNode {
|
||||
const activeNode = (
|
||||
<Paragraph isNoMargin={true}>
|
||||
Developers
|
||||
</Paragraph>
|
||||
);
|
||||
return (
|
||||
<DropDown
|
||||
activeNode={activeNode}
|
||||
popoverContent={this._renderDropdownMenu()}
|
||||
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
|
||||
targetOrigin={{ horizontal: 'left', vertical: 'top' }}
|
||||
popoverStyle={{ borderRadius: 0, width: 420, height: 377, marginTop: 0 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
private _renderDropdownMenu(): React.ReactNode {
|
||||
const sectionPadding = '26px';
|
||||
const dropdownMenu = (
|
||||
<Container>
|
||||
<Container className="flex" padding={sectionPadding}>
|
||||
<Container paddingRight="45px">
|
||||
{this._renderLinkSection(gettingStartedKeyToLinkInfo1, 'Getting started')}
|
||||
</Container>
|
||||
<Container>{this._renderLinkSection(gettingStartedKeyToLinkInfo2)}</Container>
|
||||
</Container>
|
||||
<Container width="100%" height="1px" backgroundColor={colors.grey300} />
|
||||
<Container className="flex" padding={sectionPadding}>
|
||||
<Container paddingRight="62px">
|
||||
<Container>{this._renderLinkSection(popularDocsToLinkInfos, 'Popular docs')}</Container>
|
||||
</Container>
|
||||
<Container>
|
||||
<Container>{this._renderLinkSection(usefulLinksToLinkInfo, 'Useful links')}</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
<Link to={WebsitePaths.Docs} fontColor={colors.brandLight}>
|
||||
<Container
|
||||
padding="0.9rem"
|
||||
backgroundColor={colors.white}
|
||||
borderBottomLeftRadius={4}
|
||||
borderBottomRightRadius={4}
|
||||
>
|
||||
<Paragraph color={colors.brandLight} isCentered={true} isNoMargin={true}>
|
||||
View all documentation
|
||||
</Paragraph>
|
||||
</Container>
|
||||
</Link>
|
||||
</Container>
|
||||
);
|
||||
return dropdownMenu;
|
||||
}
|
||||
private _renderLinkSection(links: ALink[], title: string = ''): React.ReactNode {
|
||||
const numLinks = links.length;
|
||||
let i = 0;
|
||||
const renderLinks = _.map(links, (link: ALink) => {
|
||||
const isWikiLink = _.startsWith(link.to, WebsitePaths.Wiki) && _.includes(link.to, '#');
|
||||
const isOnWiki = this.props.location.pathname === WebsitePaths.Wiki;
|
||||
let to = link.to;
|
||||
if (isWikiLink && isOnWiki) {
|
||||
to = `${link.to.split('#')[1]}`;
|
||||
}
|
||||
i++;
|
||||
const isLast = i === numLinks;
|
||||
// const linkText = this.props.translate.get(link.title as Key, Deco.Cap);
|
||||
const linkText = link.title;
|
||||
return (
|
||||
<Container className={`pr1 pt1 ${!isLast && 'pb1'}`} key={`dev-dropdown-link-${link.title}`}>
|
||||
<Link to={to} shouldOpenInNewTab={!!link.shouldOpenInNewTab}>
|
||||
<Paragraph size="small" color={colors.brandDark} isNoMargin={true}>
|
||||
{linkText}
|
||||
</Paragraph>
|
||||
</Link>
|
||||
</Container>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Container>
|
||||
<Container height="33px">
|
||||
{!_.isEmpty(title) && (
|
||||
<Heading asElement="h3" size="small">
|
||||
{title}
|
||||
</Heading>
|
||||
)}
|
||||
</Container>
|
||||
{renderLinks}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
68
packages/website/ts/@next/components/hamburger.tsx
Normal file
68
packages/website/ts/@next/components/hamburger.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export const Hamburger: React.FunctionComponent<Props> = (props: Props) => {
|
||||
return (
|
||||
<StyledHamburger isOpen={props.isOpen} onClick={props.onClick}>
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
</StyledHamburger>
|
||||
);
|
||||
};
|
||||
|
||||
const StyledHamburger = styled.button<Props>`
|
||||
background: none;
|
||||
border: 0;
|
||||
width: 22px;
|
||||
height: 16px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
margin-bottom: 5px;
|
||||
transform-origin: 4px 0px;
|
||||
transition: transform 0.5s cubic-bezier(0.77,0.2,0.05,1.0),
|
||||
background-color 0.5s cubic-bezier(0.77,0.2,0.05,1.0),
|
||||
opacity 0.55s ease;
|
||||
|
||||
&:first-child {
|
||||
//transform-origin: 0% 0%;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
//transform-origin: 0% 100%;
|
||||
}
|
||||
|
||||
${props => props.isOpen && `
|
||||
opacity: 1;
|
||||
transform: rotate(45deg) translate(0, 1px);
|
||||
background-color: #fff;
|
||||
|
||||
&:nth-child(2) {
|
||||
opacity: 0;
|
||||
transform: rotate(0deg) scale(0.2, 0.2);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
transform: rotate(-45deg) translate(1px, -4px);
|
||||
}
|
||||
`}
|
||||
}
|
||||
`;
|
||||
@@ -3,48 +3,206 @@ import * as React from 'react';
|
||||
import { Link as ReactRouterLink } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { colors } from 'ts/style/colors';
|
||||
|
||||
import { Button, ButtonWrap, Link } from 'ts/@next/components/button';
|
||||
import { DevelopersDropDown } from 'ts/@next/components/dropdowns/developers_drop_down';
|
||||
import { Hamburger } from 'ts/@next/components/hamburger';
|
||||
import { Section, Wrap } from 'ts/@next/components/layout';
|
||||
import { Logo } from 'ts/@next/components/logo';
|
||||
import { Paragraph } from 'ts/@next/components/text';
|
||||
|
||||
interface HeaderProps {
|
||||
isOpen: boolean;
|
||||
location?: Location;
|
||||
}
|
||||
|
||||
const links = [
|
||||
{ url: '/next/why', text: 'Why 0x' },
|
||||
{ url: '/next/0x-instant', text: 'Products' },
|
||||
{ url: '#', text: 'Developers' },
|
||||
{ url: '/next/about/mission', text: 'About' },
|
||||
{ url: '#', text: 'Blog' },
|
||||
interface HeaderState {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
interface HeaderState {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
interface NavItem {
|
||||
url?: string;
|
||||
id?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
const mobileProductLinks = [
|
||||
{ url: '/next/0x-instant', text: '0x Instant' },
|
||||
{ url: '/next/launch-kit', text: '0x Launch Kit' },
|
||||
];
|
||||
|
||||
export const Header: React.StatelessComponent<HeaderProps> = ({}) => (
|
||||
<StyledHeader>
|
||||
<HeaderWrap>
|
||||
<ReactRouterLink to="/next">
|
||||
<Logo/>
|
||||
</ReactRouterLink>
|
||||
const navItems: NavItem[] = [
|
||||
{ id: 'why', url: '/next/why', text: 'Why 0x' },
|
||||
{ id: 'products', url: '/next/0x-instant', text: 'Products' },
|
||||
{ id: 'developers', url: '#', text: 'Developers' },
|
||||
{ id: 'about', url: '/next/about/mission', text: 'About' },
|
||||
{ id: 'blog', url: '#', text: 'Blog' },
|
||||
];
|
||||
|
||||
<ButtonWrap>
|
||||
{_.map(links, (link, index) => (
|
||||
<Link
|
||||
key={`hb-${index}`}
|
||||
href={link.url}
|
||||
isTransparent={true}
|
||||
isNoBorder={true}
|
||||
>
|
||||
{link.text}
|
||||
</Link>
|
||||
))}
|
||||
</ButtonWrap>
|
||||
export class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
constructor(props: HeaderProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpen: false,
|
||||
};
|
||||
}
|
||||
public render(): React.ReactNode {
|
||||
return (
|
||||
<StyledHeader isOpen={this.state.isOpen}>
|
||||
<HeaderWrap>
|
||||
<ReactRouterLink to="/next">
|
||||
<Logo/>
|
||||
</ReactRouterLink>
|
||||
<Hamburger isOpen={this.state.isOpen} onClick={this._onMenuButtonClick.bind(this)}/>
|
||||
<Nav>
|
||||
<MobileProductLinksWrap>
|
||||
<Paragraph isNoMargin={true} isMuted={0.5} size="small">Products</Paragraph>
|
||||
{_.map(mobileProductLinks, (link, index) => (
|
||||
<StyledLink
|
||||
key={`productlink-${index}`}
|
||||
href={link.url}
|
||||
isTransparent={true}
|
||||
isNoBorder={true}
|
||||
>
|
||||
{link.text}
|
||||
</StyledLink>
|
||||
))}
|
||||
</MobileProductLinksWrap>
|
||||
<StyledButtonWrap>
|
||||
{_.map(navItems, (link, index) => this._getNavItem(link, index))}
|
||||
</StyledButtonWrap>
|
||||
</Nav>
|
||||
<TradeButton href="#">Trade on 0x</TradeButton>
|
||||
</HeaderWrap>
|
||||
</StyledHeader>
|
||||
);
|
||||
}
|
||||
private _onMenuButtonClick(): void {
|
||||
this.setState({
|
||||
isOpen: !this.state.isOpen,
|
||||
});
|
||||
}
|
||||
private _getNavItem(link: NavItem, index: number): React.ReactNode {
|
||||
if (link.id === 'developers') {
|
||||
return (
|
||||
<DevelopersDropDown
|
||||
location={window.location}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
<Button href="#">Trade on 0x</Button>
|
||||
</HeaderWrap>
|
||||
</StyledHeader>
|
||||
);
|
||||
return (
|
||||
<StyledLink
|
||||
key={`header-nav-item-${index}`}
|
||||
href={link.url}
|
||||
isTransparent={true}
|
||||
isNoBorder={true}
|
||||
>
|
||||
{link.text}
|
||||
</StyledLink>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const StyledHeader = styled(Section.withComponent('header'))<HeaderProps>`
|
||||
@media (max-width: 768px) {
|
||||
overflow: hidden;
|
||||
min-height: ${props => props.isOpen ? '385px' : '70px'};
|
||||
position: relative;
|
||||
transition: min-height 0.25s ease-in-out;
|
||||
:root & {
|
||||
padding: 20px 20px 0 !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledHeader = Section.withComponent('header');
|
||||
const HeaderWrap = styled(Wrap)`
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
padding-top: 0;
|
||||
display: flex;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButtonWrap = styled(ButtonWrap)`
|
||||
display: flex;
|
||||
@media (max-width: 768px) {
|
||||
background-color: #022924;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 20px 20px;
|
||||
|
||||
a {
|
||||
text-align: left;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
button + button,
|
||||
a + a,
|
||||
a + button,
|
||||
button + a {
|
||||
margin-left: 0;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const MobileProductLinksWrap = styled(StyledButtonWrap)`
|
||||
display: none;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
display: block;
|
||||
background-color: transparent;
|
||||
flex-direction: column;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
width: 50%;
|
||||
text-align: left;
|
||||
@media (max-width: 768px) {
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
width: auto;
|
||||
text-align: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const Nav = styled.div`
|
||||
@media (max-width: 768px) {
|
||||
background-color: ${colors.brandDark};
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-top: 65px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
width: auto;
|
||||
text-align: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const TradeButton = styled(Button)`
|
||||
@media (max-width: 768px) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user