diff options
author | Kadinsky <kandinsky454@protonmail.ch> | 2018-10-18 19:20:50 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-18 19:20:50 +0800 |
commit | 325af82217e994fdde8e904b93a76e6e3461f85a (patch) | |
tree | c51543ab9038ce0a3d42e4f0b7f6e257a2368c08 /packages/website/ts | |
parent | ea65fa4f6560264b8aee6b4b1c9272a0efa82b12 (diff) | |
parent | 47dc384ea36fecbcf01eb9b3f20936331b43a0c6 (diff) | |
download | dexon-sol-tools-325af82217e994fdde8e904b93a76e6e3461f85a.tar dexon-sol-tools-325af82217e994fdde8e904b93a76e6e3461f85a.tar.gz dexon-sol-tools-325af82217e994fdde8e904b93a76e6e3461f85a.tar.bz2 dexon-sol-tools-325af82217e994fdde8e904b93a76e6e3461f85a.tar.lz dexon-sol-tools-325af82217e994fdde8e904b93a76e6e3461f85a.tar.xz dexon-sol-tools-325af82217e994fdde8e904b93a76e6e3461f85a.tar.zst dexon-sol-tools-325af82217e994fdde8e904b93a76e6e3461f85a.zip |
Merge pull request #1154 from 0xProject/dev-section-redesign
[BASE_BRANCH] Developer section redesign
Diffstat (limited to 'packages/website/ts')
48 files changed, 1630 insertions, 650 deletions
diff --git a/packages/website/ts/components/documentation/docs_logo.tsx b/packages/website/ts/components/documentation/docs_logo.tsx new file mode 100644 index 000000000..b727fea36 --- /dev/null +++ b/packages/website/ts/components/documentation/docs_logo.tsx @@ -0,0 +1,20 @@ +import { Link } from '@0xproject/react-shared'; +import * as React from 'react'; +import { WebsitePaths } from 'ts/types'; + +export interface DocsLogoProps { + height: number; + containerStyle?: React.CSSProperties; +} + +export const DocsLogo: React.StatelessComponent<DocsLogoProps> = props => { + return ( + <Link to={WebsitePaths.Docs}> + <img src="/images/docs_logo.svg" height={props.height} /> + </Link> + ); +}; + +DocsLogo.defaultProps = { + containerStyle: {}, +}; diff --git a/packages/website/ts/components/documentation/docs_top_bar.tsx b/packages/website/ts/components/documentation/docs_top_bar.tsx new file mode 100644 index 000000000..9a9b9a616 --- /dev/null +++ b/packages/website/ts/components/documentation/docs_top_bar.tsx @@ -0,0 +1,108 @@ +import { ALink, colors, Link } from '@0xproject/react-shared'; +import * as _ from 'lodash'; +import Drawer from 'material-ui/Drawer'; +import * as React from 'react'; +import { DocsLogo } from 'ts/components/documentation/docs_logo'; +import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; +import { Deco, Key, ScreenWidths } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; + +export interface DocsTopBarProps { + location: Location; + screenWidth: ScreenWidths; + translate: Translate; + sidebar?: React.ReactNode; +} + +interface DocsTopBarState { + isDrawerOpen: boolean; +} + +export class DocsTopBar extends React.Component<DocsTopBarProps, DocsTopBarState> { + constructor(props: DocsTopBarProps) { + super(props); + this.state = { + isDrawerOpen: false, + }; + } + public componentWillReceiveProps(nextProps: DocsTopBarProps): void { + if (nextProps.location.pathname !== this.props.location.pathname) { + this.setState({ + isDrawerOpen: false, + }); + } + } + public render(): React.ReactNode { + return ( + <Container height={80}> + <Container + className="flex items-center lg-pt3 md-pt3 sm-pt1 lg-mt1 md-mt1 sm-mt0 lg-justify-end md-justify-end sm-justify-start" + width="100%" + > + <Container className="sm-hide xs-hide"> + <Container className="flex items-center justify-between right" width="300px"> + {this._renderMenuItems(constants.DEVELOPER_TOPBAR_LINKS)} + </Container> + </Container> + <Container className="lg-hide md-hide"> + <Container paddingTop="6px"> + <DocsLogo height={30} /> + </Container> + </Container> + <Container className="md-hide lg-hide absolute" right="18px" top="12px"> + <i + className="zmdi zmdi-menu" + style={{ + color: colors.grey700, + fontSize: 30, + cursor: 'pointer', + }} + onClick={this._onMenuButtonClick.bind(this)} + /> + </Container> + </Container> + <Container width={'100%'} height={'1px'} backgroundColor={colors.grey300} marginTop={'13px'} /> + {this.props.screenWidth === ScreenWidths.Sm && this._renderDrawer()} + </Container> + ); + } + private _renderMenuItems(menuItemLinks: ALink[]): React.ReactNode { + const menuItems = _.map(menuItemLinks, menuItemInfo => { + return ( + <Link + key={`menu-item-${menuItemInfo.title}`} + to={menuItemInfo.to} + shouldOpenInNewTab={menuItemInfo.shouldOpenInNewTab} + > + <Container className="flex items-center" paddingLeft="4px"> + <Text fontSize="16px" fontColor={colors.lightLinkBlue} fontWeight="bold"> + {this.props.translate.get(menuItemInfo.title as Key, Deco.Cap)} + </Text> + </Container> + </Link> + ); + }); + return menuItems; + } + private _renderDrawer(): React.ReactNode { + return ( + <Drawer + open={this.state.isDrawerOpen} + docked={false} + openSecondary={true} + onRequestChange={this._onMenuButtonClick.bind(this)} + > + <Container className="clearfix pl1 pt2" onClick={this._onMenuButtonClick.bind(this)}> + {this.props.sidebar} + </Container> + </Drawer> + ); + } + private _onMenuButtonClick(): void { + this.setState({ + isDrawerOpen: !this.state.isDrawerOpen, + }); + } +} diff --git a/packages/website/ts/components/documentation/overview_content.tsx b/packages/website/ts/components/documentation/overview_content.tsx new file mode 100644 index 000000000..6999e039a --- /dev/null +++ b/packages/website/ts/components/documentation/overview_content.tsx @@ -0,0 +1,134 @@ +import { colors, Link, MarkdownLinkBlock, utils as sharedUtils } from '@0xproject/react-shared'; +import { ObjectMap } from '@0xproject/types'; +import * as _ from 'lodash'; +import * as React from 'react'; +import * as ReactMarkdown from 'react-markdown'; +import { Element as ScrollElement } from 'react-scroll'; +import { TutorialButton } from 'ts/components/documentation/tutorial_button'; +import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; +import { Deco, Key, Package, TutorialInfo } from 'ts/types'; +import { Translate } from 'ts/utils/translate'; + +export interface OverviewContentProps { + translate: Translate; + tutorials: TutorialInfo[]; + categoryToPackages: ObjectMap<Package[]>; +} + +export interface OverviewContentState {} + +export class OverviewContent extends React.Component<OverviewContentProps, OverviewContentState> { + public render(): React.ReactNode { + return ( + <Container> + {this._renderSectionTitle(this.props.translate.get(Key.StartBuildOn0x, Deco.Cap))} + <Container paddingTop="12px"> + {this._renderSectionDescription(this.props.translate.get(Key.StartBuildOn0xDescription, Deco.Cap))} + <Container marginTop="36px"> + {_.map(this.props.tutorials, tutorialInfo => ( + <ScrollElement + name={sharedUtils.getIdFromName( + this.props.translate.get(tutorialInfo.link.title as Key, Deco.Cap), + )} + key={`tutorial-${tutorialInfo.link.title}`} + > + <TutorialButton translate={this.props.translate} tutorialInfo={tutorialInfo} /> + </ScrollElement> + ))} + </Container> + </Container> + <Container marginTop="32px" paddingBottom="100px"> + {this._renderSectionTitle(this.props.translate.get(Key.LibrariesAndTools, Deco.CapWords))} + <Container paddingTop="12px"> + {this._renderSectionDescription( + this.props.translate.get(Key.LibrariesAndToolsDescription, Deco.Cap), + )} + <Container marginTop="36px"> + {_.map(this.props.categoryToPackages, (pkgs, category) => + this._renderPackageCategory(category, pkgs), + )} + </Container> + </Container> + </Container> + </Container> + ); + } + private _renderPackageCategory(category: string, pkgs: Package[]): React.ReactNode { + return ( + <Container key={`category-${category}`}> + <Text fontSize="18px">{category}</Text> + <Container>{_.map(pkgs, pkg => this._renderPackage(pkg))}</Container> + </Container> + ); + } + private _renderPackage(pkg: Package): React.ReactNode { + const id = sharedUtils.getIdFromName(pkg.link.title); + return ( + <ScrollElement name={id} key={`package-${pkg.link.title}`}> + <Container className="pb2"> + <Container width="100%" height="1px" backgroundColor={colors.grey300} marginTop="11px" /> + <Container className="clearfix mt2 pt1"> + <Container className="md-col lg-col md-col-4 lg-col-4"> + <Link + to={pkg.link.to} + fontColor={colors.lightLinkBlue} + shouldOpenInNewTab={!!pkg.link.shouldOpenInNewTab} + > + <Text Tag="div" fontColor={colors.lightLinkBlue} fontWeight="bold"> + {pkg.link.title} + </Text> + </Link> + </Container> + <Container className="md-col lg-col md-col-6 lg-col-6 sm-py2"> + <Text fontColor={colors.grey700}> + <ReactMarkdown + source={pkg.description} + renderers={{ + link: MarkdownLinkBlock, + paragraph: 'span', + }} + /> + </Text> + </Container> + <Container className="md-col lg-col md-col-2 lg-col-2 sm-pb2 relative"> + <Container position="absolute" right="0px"> + <Link + to={pkg.link.to} + fontColor={colors.lightLinkBlue} + shouldOpenInNewTab={!!pkg.link.shouldOpenInNewTab} + > + <Container className="flex"> + <Container>{this.props.translate.get(Key.More, Deco.Cap)}</Container> + <Container paddingTop="1px" paddingLeft="6px"> + <i + className="zmdi zmdi-chevron-right bold" + style={{ fontSize: 18, color: colors.lightLinkBlue }} + /> + </Container> + </Container> + </Link> + </Container> + </Container> + </Container> + </Container> + </ScrollElement> + ); + } + private _renderSectionTitle(text: string): React.ReactNode { + return ( + <Container paddingTop="30px"> + <Text fontColor={colors.projectsGrey} fontSize="30px" fontWeight="bold"> + {text} + </Text> + </Container> + ); + } + private _renderSectionDescription(text: string): React.ReactNode { + return ( + <Text fontColor={colors.linkSectionGrey} fontSize="16px" fontFamily="Roboto Mono"> + {text} + </Text> + ); + } +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/documentation/sidebar_header.tsx b/packages/website/ts/components/documentation/sidebar_header.tsx new file mode 100644 index 000000000..ec0ada8bd --- /dev/null +++ b/packages/website/ts/components/documentation/sidebar_header.tsx @@ -0,0 +1,57 @@ +import { colors } from '@0xproject/react-shared'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { VersionDropDown } from 'ts/components/documentation/version_drop_down'; +import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; +import { ScreenWidths } from 'ts/types'; + +export interface SidebarHeaderProps { + screenWidth: ScreenWidths; + title: string; + docsVersion?: string; + availableDocVersions?: string[]; + onVersionSelected?: () => void; +} + +export const SidebarHeader: React.StatelessComponent<SidebarHeaderProps> = ({ + screenWidth, + title, + docsVersion, + availableDocVersions, + onVersionSelected, +}) => { + return ( + <Container> + <Container className="flex justify-bottom"> + <Container className="left pl1" width="150px"> + <Text + fontColor={colors.lightLinkBlue} + fontSize={screenWidth === ScreenWidths.Sm ? '20px' : '22px'} + fontWeight="bold" + > + {title} + </Text> + </Container> + {!_.isUndefined(docsVersion) && + !_.isUndefined(availableDocVersions) && + !_.isUndefined(onVersionSelected) && ( + <div className="right" style={{ alignSelf: 'flex-end' }}> + <VersionDropDown + selectedVersion={docsVersion} + versions={availableDocVersions} + onVersionSelected={onVersionSelected} + /> + </div> + )} + </Container> + <Container + width={'100%'} + height={'1px'} + backgroundColor={colors.grey300} + marginTop="20px" + marginBottom="27px" + /> + </Container> + ); +}; diff --git a/packages/website/ts/components/documentation/tutorial_button.tsx b/packages/website/ts/components/documentation/tutorial_button.tsx new file mode 100644 index 000000000..dc00bf743 --- /dev/null +++ b/packages/website/ts/components/documentation/tutorial_button.tsx @@ -0,0 +1,59 @@ +import { colors, Link } from '@0xproject/react-shared'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; +import { Deco, Key, TutorialInfo } from 'ts/types'; +import { Translate } from 'ts/utils/translate'; + +import { styled } from 'ts/style/theme'; + +export interface TutorialButtonProps { + className?: string; + translate: Translate; + tutorialInfo: TutorialInfo; +} + +const PlainTutorialButton: React.StatelessComponent<TutorialButtonProps> = ({ translate, tutorialInfo, className }) => ( + <Container className={className}> + <Link to={tutorialInfo.link.to} shouldOpenInNewTab={tutorialInfo.link.shouldOpenInNewTab}> + <div className="flex relative"> + <div className="col col-1 flex items-center sm-pr3"> + <img src={tutorialInfo.iconUrl} height={40} /> + </div> + <div className="lg-pl2 md-pl2 sm-pl3 col col-10"> + <Text Tag="div" fontSize="18" fontColor={colors.lightLinkBlue} fontWeight="bold"> + {translate.get(tutorialInfo.link.title as Key, Deco.Cap)} + </Text> + <Text Tag="div" fontColor={colors.grey750} fontSize="16"> + {translate.get(tutorialInfo.description as Key, Deco.Cap)} + </Text> + </div> + <div className="col col-1 flex items-center justify-end"> + <div className="right"> + <i + className="zmdi zmdi-chevron-right bold" + style={{ fontSize: 26, color: colors.lightLinkBlue }} + /> + </div> + </div> + </div> + </Link> + </Container> +); + +export const TutorialButton = styled(PlainTutorialButton)` + border-radius: 4px; + border: 1px solid ${colors.grey325}; + background-color: ${colors.white}; + &:hover { + border: 1px solid ${colors.lightLinkBlue}; + background-color: ${colors.lightestBlue}; + } + padding: 20px; + margin-bottom: 15px; +`; + +TutorialButton.defaultProps = {}; + +TutorialButton.displayName = 'TutorialButton'; diff --git a/packages/website/ts/components/documentation/version_drop_down.tsx b/packages/website/ts/components/documentation/version_drop_down.tsx new file mode 100644 index 000000000..ec1c99f7b --- /dev/null +++ b/packages/website/ts/components/documentation/version_drop_down.tsx @@ -0,0 +1,80 @@ +import { colors } from '@0xproject/react-shared'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { Button } from 'ts/components/ui/button'; +import { Container } from 'ts/components/ui/container'; +import { DropDown, DropdownMouseEvent } from 'ts/components/ui/drop_down'; +import { Text } from 'ts/components/ui/text'; +import { styled } from 'ts/style/theme'; + +interface ActiveNodeProps { + className?: string; + selectedVersion: string; +} + +const PlainActiveNode: React.StatelessComponent<ActiveNodeProps> = ({ className, selectedVersion }) => ( + <Container className={className}> + <Container className="flex justify-center"> + <Text fontColor={colors.grey700} fontSize="12px"> + v {selectedVersion} + </Text> + <Container paddingLeft="6px"> + <i className="zmdi zmdi-chevron-down" style={{ fontSize: 17, color: 'rgba(153, 153, 153, 0.8)' }} /> + </Container> + </Container> + </Container> +); + +const ActiveNode = styled(PlainActiveNode)` + cursor: pointer; + border: 2px solid ${colors.beigeWhite}; + border-radius: 4px; + padding: 4px 6px 4px 8px; +`; + +interface VersionDropDownProps { + selectedVersion: string; + versions: string[]; + onVersionSelected: (semver: string) => void; +} + +interface VersionDropDownState {} + +export class VersionDropDown extends React.Component<VersionDropDownProps, VersionDropDownState> { + public render(): React.ReactNode { + const activeNode = <ActiveNode selectedVersion={this.props.selectedVersion} />; + return ( + <DropDown + activateEvent={DropdownMouseEvent.Click} + activeNode={activeNode} + popoverContent={this._renderDropdownMenu()} + anchorOrigin={{ horizontal: 'middle', vertical: 'bottom' }} + targetOrigin={{ horizontal: 'middle', vertical: 'top' }} + popoverStyle={{ borderRadius: 4 }} + /> + ); + } + private _renderDropdownMenu(): React.ReactNode { + const items = _.map(this.props.versions, version => { + const isSelected = version === this.props.selectedVersion; + return ( + <Container key={`dropdown-items-${version}`}> + <Button + borderRadius="0px" + padding="0.8em 0em" + width="100%" + isDisabled={isSelected} + onClick={this._onClick.bind(this, version)} + > + v {version} + </Button> + </Container> + ); + }); + const dropdownMenu = <Container width="88px">{items}</Container>; + return dropdownMenu; + } + private _onClick(selectedVersion: string): void { + this.props.onVersionSelected(selectedVersion); + } +} diff --git a/packages/website/ts/components/dropdowns/developers_drop_down.tsx b/packages/website/ts/components/dropdowns/developers_drop_down.tsx new file mode 100644 index 000000000..1db1e89b8 --- /dev/null +++ b/packages/website/ts/components/dropdowns/developers_drop_down.tsx @@ -0,0 +1,164 @@ +import { ALink, colors, Link } from '@0xproject/react-shared'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { Container } from 'ts/components/ui/container'; +import { DropDown } from 'ts/components/ui/drop_down'; +import { Text } from 'ts/components/ui/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.UseSharedLiquidity, + 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; + translate: Translate; + menuItemStyles: React.CSSProperties; + menuIconStyle: React.CSSProperties; +} + +interface DevelopersDropDownState {} + +export class DevelopersDropDown extends React.Component<DevelopersDropDownProps, DevelopersDropDownState> { + public render(): React.ReactNode { + const activeNode = ( + <Container className="flex relative" paddingRight="10"> + <Text fontColor={this.props.menuIconStyle.color}> + {this.props.translate.get(Key.Developers, Deco.Cap)} + </Text> + </Container> + ); + return ( + <DropDown + activeNode={activeNode} + popoverContent={this._renderDropdownMenu()} + anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }} + targetOrigin={{ horizontal: 'left', vertical: 'top' }} + style={this.props.menuItemStyles} + popoverStyle={{ borderRadius: 4, width: 397, height: 373, marginTop: 0 }} + /> + ); + } + private _renderDropdownMenu(): React.ReactNode { + const dropdownMenu = ( + <Container> + <Container padding="1.75rem"> + {this._renderTitle('Getting started')} + <Container className="flex"> + <Container className="pr4 mr2"> + {this._renderLinkSection(gettingStartedKeyToLinkInfo1)} + </Container> + <Container>{this._renderLinkSection(gettingStartedKeyToLinkInfo2)}</Container> + </Container> + </Container> + <Container width="100%" height="1px" backgroundColor={colors.grey300} /> + <Container className="flex" padding="1.75rem"> + <Container className="pr4 mr2"> + <Container>{this._renderTitle('Popular docs')}</Container> + <Container>{this._renderLinkSection(popularDocsToLinkInfos)}</Container> + </Container> + <Container> + <Container>{this._renderTitle('Useful links')}</Container> + <Container>{this._renderLinkSection(usefulLinksToLinkInfo)}</Container> + </Container> + </Container> + <Link to={WebsitePaths.Docs} fontColor={colors.lightBlueA700}> + <Container + padding="0.9rem" + backgroundColor={colors.lightBgGrey} + borderBottomLeftRadius={4} + borderBottomRightRadius={4} + > + <Text fontColor={colors.lightBlueA700} fontWeight="bold" fontSize="14px" textAlign="center"> + {this.props.translate.get(Key.ViewAllDocumentation, Deco.Upper)} + </Text> + </Container> + </Link> + </Container> + ); + return dropdownMenu; + } + private _renderTitle(title: string): React.ReactNode { + return ( + <Container paddingBottom="12px"> + <Text letterSpacing={1} fontColor={colors.linkSectionGrey} fontSize="14px" fontWeight={600}> + {title.toUpperCase()} + </Text> + </Container> + ); + } + private _renderLinkSection(links: ALink[]): 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); + return ( + <Container className={`pr1 pt1 ${!isLast && 'pb1'}`} key={`dev-dropdown-link-${link.title}`}> + <Link to={to} shouldOpenInNewTab={!!link.shouldOpenInNewTab}> + <Text fontFamily="Roboto, Roboto Mono" fontColor={colors.lightBlueA700}> + {linkText} + </Text> + </Link> + </Container> + ); + }); + return <Container>{renderLinks}</Container>; + } +} diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index 3c3155349..c1bbadfde 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -1,5 +1,5 @@ import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; -import { colors } from '@0xproject/react-shared'; +import { colors, Link } from '@0xproject/react-shared'; import { BigNumber, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as accounting from 'accounting'; @@ -8,7 +8,6 @@ import { Card, CardHeader, CardText } from 'material-ui/Card'; import Divider from 'material-ui/Divider'; import RaisedButton from 'material-ui/RaisedButton'; import * as React from 'react'; -import { Link } from 'react-router-dom'; import { Blockchain } from 'ts/blockchain'; import { TrackTokenConfirmationDialog } from 'ts/components/dialogs/track_token_confirmation_dialog'; import { FillOrderJSON } from 'ts/components/fill_order_json'; @@ -324,7 +323,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { return ( <div> Order successfully filled. See the trade details in your{' '} - <Link to={`${WebsitePaths.Portal}/trades`} style={{ color: colors.white }}> + <Link to={`${WebsitePaths.Portal}/trades`} fontColor={colors.white}> trade history </Link> </div> diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx index 6dcb5a0e9..db4f57100 100644 --- a/packages/website/ts/components/footer.tsx +++ b/packages/website/ts/components/footer.tsx @@ -1,31 +1,17 @@ -import { colors } from '@0xproject/react-shared'; +import { ALink, colors, Link } from '@0xproject/react-shared'; +import { ObjectMap } from '@0xproject/types'; import * as _ from 'lodash'; import DropDownMenu from 'material-ui/DropDownMenu'; import MenuItem from 'material-ui/MenuItem'; import * as React from 'react'; -import { Link } from 'react-router-dom'; + import { Dispatcher } from 'ts/redux/dispatcher'; import { Deco, Key, Language, WebsitePaths } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; -interface MenuItemsBySection { - [sectionName: string]: FooterMenuItem[]; -} - -interface FooterMenuItem { - title: string; - path?: string; - isExternal?: boolean; -} - const ICON_DIMENSION = 16; -const linkStyle = { - color: colors.white, - cursor: 'pointer', -}; - const languageToMenuTitle = { [Language.English]: 'English', [Language.Russian]: 'Русский', @@ -51,76 +37,74 @@ export class Footer extends React.Component<FooterProps, FooterState> { }; } public render(): React.ReactNode { - const menuItemsBySection: MenuItemsBySection = { + const sectionNameToLinks: ObjectMap<ALink[]> = { [Key.Documentation]: [ { + title: 'Developer Home', + to: WebsitePaths.Docs, + }, + { title: '0x.js', - path: WebsitePaths.ZeroExJs, + to: WebsitePaths.ZeroExJs, }, { title: this.props.translate.get(Key.SmartContracts, Deco.Cap), - path: WebsitePaths.SmartContracts, + to: WebsitePaths.SmartContracts, }, { title: this.props.translate.get(Key.Connect, Deco.Cap), - path: WebsitePaths.Connect, + to: WebsitePaths.Connect, }, { title: this.props.translate.get(Key.Whitepaper, Deco.Cap), - path: WebsitePaths.Whitepaper, - isExternal: true, + to: WebsitePaths.Whitepaper, + shouldOpenInNewTab: true, }, { title: this.props.translate.get(Key.Wiki, Deco.Cap), - path: WebsitePaths.Wiki, - }, - { - title: this.props.translate.get(Key.Faq, Deco.Cap), - path: WebsitePaths.FAQ, + to: WebsitePaths.Wiki, }, ], [Key.Community]: [ { title: this.props.translate.get(Key.RocketChat, Deco.Cap), - isExternal: true, - path: constants.URL_ZEROEX_CHAT, + to: constants.URL_ZEROEX_CHAT, + shouldOpenInNewTab: true, }, { title: this.props.translate.get(Key.Blog, Deco.Cap), - isExternal: true, - path: constants.URL_BLOG, + to: constants.URL_BLOG, + shouldOpenInNewTab: true, }, { title: 'Twitter', - isExternal: true, - path: constants.URL_TWITTER, + to: constants.URL_TWITTER, + shouldOpenInNewTab: true, }, { title: 'Reddit', - isExternal: true, - path: constants.URL_REDDIT, + to: constants.URL_REDDIT, + shouldOpenInNewTab: true, }, { title: this.props.translate.get(Key.Forum, Deco.Cap), - isExternal: true, - path: constants.URL_DISCOURSE_FORUM, + to: constants.URL_DISCOURSE_FORUM, + shouldOpenInNewTab: true, }, ], [Key.Organization]: [ { title: this.props.translate.get(Key.About, Deco.Cap), - isExternal: false, - path: WebsitePaths.About, + to: WebsitePaths.About, }, { title: this.props.translate.get(Key.Careers, Deco.Cap), - isExternal: false, - path: WebsitePaths.Careers, + to: WebsitePaths.Careers, }, { title: this.props.translate.get(Key.Contact, Deco.Cap), - isExternal: true, - path: 'mailto:team@0xproject.com', + to: 'mailto:team@0xproject.com', + shouldOpenInNewTab: true, }, ], }; @@ -160,19 +144,19 @@ export class Footer extends React.Component<FooterProps, FooterState> { <div className="col lg-col-4 md-col-4 col-12"> <div className="lg-right md-right sm-center"> {this._renderHeader(Key.Documentation)} - {_.map(menuItemsBySection[Key.Documentation], this._renderMenuItem.bind(this))} + {_.map(sectionNameToLinks[Key.Documentation], this._renderMenuItem.bind(this))} </div> </div> <div className="col lg-col-4 md-col-4 col-12 lg-pr2 md-pr2"> <div className="lg-right md-right sm-center"> {this._renderHeader(Key.Community)} - {_.map(menuItemsBySection[Key.Community], this._renderMenuItem.bind(this))} + {_.map(sectionNameToLinks[Key.Community], this._renderMenuItem.bind(this))} </div> </div> <div className="col lg-col-4 md-col-4 col-12"> <div className="lg-right md-right sm-center"> {this._renderHeader(Key.Organization)} - {_.map(menuItemsBySection[Key.Organization], this._renderMenuItem.bind(this))} + {_.map(sectionNameToLinks[Key.Organization], this._renderMenuItem.bind(this))} </div> </div> </div> @@ -187,7 +171,7 @@ export class Footer extends React.Component<FooterProps, FooterState> { </div> ); } - private _renderMenuItem(item: FooterMenuItem): React.ReactNode { + private _renderMenuItem(link: ALink): React.ReactNode { const titleToIcon: { [title: string]: string } = { [this.props.translate.get(Key.RocketChat, Deco.Cap)]: 'rocketchat.png', [this.props.translate.get(Key.Blog, Deco.Cap)]: 'medium.png', @@ -195,30 +179,26 @@ export class Footer extends React.Component<FooterProps, FooterState> { Reddit: 'reddit.png', [this.props.translate.get(Key.Forum, Deco.Cap)]: 'discourse.png', }; - const iconIfExists = titleToIcon[item.title]; + const iconIfExists = titleToIcon[link.title]; return ( - <div key={item.title} className="sm-center" style={{ fontSize: 13, paddingTop: 25 }}> - {item.isExternal ? ( - <a className="text-decoration-none" style={linkStyle} target="_blank" href={item.path}> + <div key={link.title} className="sm-center" style={{ fontSize: 13, paddingTop: 25 }}> + <Link + to={link.to} + shouldOpenInNewTab={link.shouldOpenInNewTab} + fontColor={colors.white} + className="text-decoration-none" + > + <div> {!_.isUndefined(iconIfExists) ? ( <div className="inline-block"> <div className="pr1 table-cell">{this._renderIcon(iconIfExists)}</div> - <div className="table-cell">{item.title}</div> + <div className="table-cell">{link.title}</div> </div> ) : ( - item.title + link.title )} - </a> - ) : ( - <Link to={item.path} style={linkStyle} className="text-decoration-none"> - <div> - {!_.isUndefined(iconIfExists) && ( - <div className="pr1">{this._renderIcon(iconIfExists)}</div> - )} - {item.title} - </div> - </Link> - )} + </div> + </Link> </div> ); } diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx index db093fb68..134d1cc76 100644 --- a/packages/website/ts/components/inputs/token_amount_input.tsx +++ b/packages/website/ts/components/inputs/token_amount_input.tsx @@ -1,9 +1,8 @@ -import { colors } from '@0xproject/react-shared'; +import { colors, Link } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import * as React from 'react'; -import { Link } from 'react-router-dom'; import { Blockchain } from 'ts/blockchain'; import { BalanceBoundedInput } from 'ts/components/inputs/balance_bounded_input'; import { Token, ValidatedBigNumberCallback, WebsitePaths } from 'ts/types'; @@ -112,7 +111,8 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok Insufficient allowance.{' '} <Link to={`${WebsitePaths.Portal}/account`} - style={{ cursor: 'pointer', color: colors.darkestGrey }} + textDecoration="underline" + fontColor={colors.darkestGrey} > Set allowance </Link> diff --git a/packages/website/ts/components/portal/back_button.tsx b/packages/website/ts/components/portal/back_button.tsx index ca35abc2b..64a332e07 100644 --- a/packages/website/ts/components/portal/back_button.tsx +++ b/packages/website/ts/components/portal/back_button.tsx @@ -1,7 +1,5 @@ -import { Styles } from '@0xproject/react-shared'; +import { Link, Styles } from '@0xproject/react-shared'; import * as React from 'react'; -import { Link } from 'react-router-dom'; - import { Island } from 'ts/components/ui/island'; import { colors } from 'ts/style/colors'; @@ -27,7 +25,7 @@ const styles: Styles = { export const BackButton = (props: BackButtonProps) => { return ( <div style={{ height: 65, paddingTop: 25 }}> - <Link to={props.to} style={{ textDecoration: 'none' }}> + <Link to={props.to}> <Island className="flex right" style={styles.backButton}> <div style={{ marginLeft: 12 }}> <i style={styles.backButtonIcon} className={`zmdi zmdi-arrow-left`} /> diff --git a/packages/website/ts/components/portal/drawer_menu.tsx b/packages/website/ts/components/portal/drawer_menu.tsx index a6707e86c..3a8c69a70 100644 --- a/packages/website/ts/components/portal/drawer_menu.tsx +++ b/packages/website/ts/components/portal/drawer_menu.tsx @@ -39,7 +39,7 @@ export interface DrawerMenuProps { } export const DrawerMenu = (props: DrawerMenuProps) => { const relayerItemEntry = { - to: `${WebsitePaths.Portal}`, + to: WebsitePaths.Portal, labelText: 'Relayer ecosystem', iconName: 'zmdi-portable-wifi', }; diff --git a/packages/website/ts/components/portal/menu.tsx b/packages/website/ts/components/portal/menu.tsx index 39dab77f5..a3352529c 100644 --- a/packages/website/ts/components/portal/menu.tsx +++ b/packages/website/ts/components/portal/menu.tsx @@ -1,7 +1,7 @@ import { Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; -import { MenuItem } from 'ts/components/ui/menu_item'; +import { CustomMenuItem } from 'ts/components/ui/custom_menu_item'; import { colors } from 'ts/style/colors'; import { WebsitePaths } from 'ts/types'; @@ -67,14 +67,14 @@ export const Menu: React.StatelessComponent<MenuProps> = (props: MenuProps) => { {_.map(props.menuItemEntries, entry => { const isSelected = entry.to === props.selectedPath; return ( - <MenuItem key={entry.to} to={entry.to}> + <CustomMenuItem key={entry.to} to={entry.to}> <MenuItemLabel title={entry.labelText} iconName={entry.iconName} selected={isSelected} theme={props.theme} /> - </MenuItem> + </CustomMenuItem> ); })} </div> diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 1470239d6..c58dc26bd 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -1,9 +1,9 @@ -import { colors } from '@0xproject/react-shared'; +import { colors, Link } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import * as React from 'react'; import * as DocumentTitle from 'react-document-title'; -import { Link, Route, RouteComponentProps, Switch } from 'react-router-dom'; +import { Route, RouteComponentProps, Switch } from 'react-router-dom'; import { Blockchain } from 'ts/blockchain'; import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; @@ -317,7 +317,7 @@ export class Portal extends React.Component<PortalProps, PortalState> { }; return ( <Section - header={<BackButton to={`${WebsitePaths.Portal}`} labelText="back to Relayers" />} + header={<BackButton to={WebsitePaths.Portal} labelText="back to Relayers" />} body={<Menu selectedPath={routeComponentProps.location.pathname} theme={menuTheme} />} /> ); @@ -389,9 +389,7 @@ export class Portal extends React.Component<PortalProps, PortalState> { </Container> ); return !shouldStartOnboarding ? ( - <Link to={{ pathname: `${WebsitePaths.Portal}/account` }} style={{ textDecoration: 'none' }}> - {startOnboarding} - </Link> + <Link to={`${WebsitePaths.Portal}/account`}>{startOnboarding}</Link> ) : ( startOnboarding ); diff --git a/packages/website/ts/components/sidebar_header.tsx b/packages/website/ts/components/sidebar_header.tsx deleted file mode 100644 index a14ab54f5..000000000 --- a/packages/website/ts/components/sidebar_header.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { colors } from '@0xproject/react-shared'; -import * as React from 'react'; - -interface SidebarHeaderProps { - title: string; - iconUrl: string; -} - -interface SidebarHeaderState {} - -export class SidebarHeader extends React.Component<SidebarHeaderProps, SidebarHeaderState> { - public render(): React.ReactNode { - return ( - <div className="pt2 md-px1 sm-px2" style={{ color: colors.black, paddingBottom: 18 }}> - <div className="flex" style={{ fontSize: 25 }}> - <div style={{ fontWeight: 'bold', fontFamily: 'Roboto Mono' }}>0x</div> - <div className="pl2" style={{ lineHeight: 1.4, fontWeight: 300 }}> - docs - </div> - </div> - <div className="pl1" style={{ color: colors.grey350, paddingBottom: 9, paddingLeft: 10, height: 17 }}> - | - </div> - <div className="flex"> - <div> - <img src={this.props.iconUrl} width="22" /> - </div> - <div className="pl1" style={{ fontWeight: 600, fontSize: 20, lineHeight: 1.2 }}> - {this.props.title} - </div> - </div> - </div> - ); - } -} diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index fe2c1fcf9..7e7247c25 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -1,23 +1,16 @@ -import { DocsInfo, DocsMenu } from '@0xproject/react-docs'; -import { - colors, - constants as sharedConstants, - MenuSubsectionsBySection, - NestedSidebarMenu, - Styles, -} from '@0xproject/react-shared'; +import { DocsInfo } from '@0xproject/react-docs'; +import { ALink, colors, Link, Styles } from '@0xproject/react-shared'; +import { ObjectMap } from '@0xproject/types'; import * as _ from 'lodash'; import Drawer from 'material-ui/Drawer'; -import Menu from 'material-ui/Menu'; import MenuItem from 'material-ui/MenuItem'; import * as React from 'react'; -import { Link } from 'react-router-dom'; import { Blockchain } from 'ts/blockchain'; +import { DevelopersDropDown } from 'ts/components/dropdowns/developers_drop_down'; import { DrawerMenu } from 'ts/components/portal/drawer_menu'; import { ProviderDisplay } from 'ts/components/top_bar/provider_display'; import { TopBarMenuItem } from 'ts/components/top_bar/top_bar_menu_item'; import { Container } from 'ts/components/ui/container'; -import { DropDown } from 'ts/components/ui/drop_down'; import { Dispatcher } from 'ts/redux/dispatcher'; import { Deco, Key, ProviderType, WebsitePaths } from 'ts/types'; import { constants } from 'ts/utils/constants'; @@ -41,8 +34,7 @@ export interface TopBarProps { translate: Translate; docsVersion?: string; availableDocVersions?: string[]; - menu?: DocsMenu; - menuSubsectionsBySection?: MenuSubsectionsBySection; + sectionNameToLinks?: ObjectMap<ALink[]>; displayType?: TopBarDisplayType; docsInfo?: DocsInfo; style?: React.CSSProperties; @@ -80,21 +72,6 @@ const styles: Styles = { }, }; -const DOC_WEBSITE_PATHS_TO_KEY = { - [WebsitePaths.SolCov]: Key.SolCov, - [WebsitePaths.SmartContracts]: Key.SmartContracts, - [WebsitePaths.Web3Wrapper]: Key.Web3Wrapper, - [WebsitePaths.SolCompiler]: Key.SolCompiler, - [WebsitePaths.JSONSchemas]: Key.JsonSchemas, - [WebsitePaths.Subproviders]: Key.Subproviders, - [WebsitePaths.ContractWrappers]: Key.ContractWrappers, - [WebsitePaths.Connect]: Key.Connect, - [WebsitePaths.ZeroExJs]: Key.ZeroExJs, - [WebsitePaths.OrderUtils]: Key.OrderUtils, - [WebsitePaths.OrderWatcher]: Key.OrderWatcher, - [WebsitePaths.AssetBuyer]: Key.AssetBuyer, -}; - const DEFAULT_HEIGHT = 68; const EXPANDED_HEIGHT = 75; @@ -130,117 +107,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { ? 'flex mx-auto items-center max-width-4' : 'flex mx-auto items-center'; const height = isExpandedDisplayType ? EXPANDED_HEIGHT : DEFAULT_HEIGHT; - const developerSectionMenuItems = [ - <Link key="subMenuItem-zeroEx" to={WebsitePaths.ZeroExJs} className="text-decoration-none"> - <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="0x.js" /> - </Link>, - <Link key="subMenuItem-smartContracts" to={WebsitePaths.SmartContracts} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.SmartContracts, Deco.CapWords)} - /> - </Link>, - <Link key="subMenuItem-0xconnect" to={WebsitePaths.Connect} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.Connect, Deco.CapWords)} - /> - </Link>, - <a - key="subMenuItem-standard-relayer-api" - target="_blank" - className="text-decoration-none" - href={constants.URL_STANDARD_RELAYER_API_GITHUB} - > - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.StandardRelayerApi, Deco.CapWords)} - /> - </a>, - <Link key="subMenuItem-jsonSchema" to={WebsitePaths.JSONSchemas} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.JsonSchemas, Deco.CapWords)} - /> - </Link>, - <Link key="subMenuItem-subproviders" to={WebsitePaths.Subproviders} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.Subproviders, Deco.CapWords)} - /> - </Link>, - <Link key="subMenuItem-web3Wrapper" to={WebsitePaths.Web3Wrapper} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.Web3Wrapper, Deco.CapWords)} - /> - </Link>, - <Link - key="subMenuItem-contractWrappers" - to={WebsitePaths.ContractWrappers} - className="text-decoration-none" - > - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.ContractWrappers, Deco.CapWords)} - /> - </Link>, - <Link key="subMenuItem-orderUtils" to={WebsitePaths.OrderUtils} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.OrderUtils, Deco.CapWords)} - /> - </Link>, - <Link key="subMenuItem-orderWatcher" to={WebsitePaths.OrderWatcher} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.OrderWatcher, Deco.CapWords)} - /> - </Link>, - <Link key="subMenuItem-sol-compiler" to={WebsitePaths.SolCompiler} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.SolCompiler, Deco.CapWords)} - /> - </Link>, - <Link key="subMenuItem-sol-cov" to={WebsitePaths.SolCov} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.SolCov, Deco.CapWords)} - /> - </Link>, - <Link key="subMenuItem-ethereum-types" to={WebsitePaths.EthereumTypes} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.EthereumTypes, Deco.CapWords)} - /> - </Link>, - <Link key="subMenuItem-asset-buyer" to={WebsitePaths.AssetBuyer} className="text-decoration-none"> - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.AssetBuyer, Deco.CapWords)} - /> - </Link>, - <a - key="subMenuItem-whitePaper" - target="_blank" - className="text-decoration-none" - href={`${WebsitePaths.Whitepaper}`} - > - <MenuItem - style={{ fontSize: styles.menuItem.fontSize }} - primaryText={this.props.translate.get(Key.Whitepaper, Deco.CapWords)} - /> - </a>, - <a - key="subMenuItem-github" - target="_blank" - className="text-decoration-none" - href={constants.URL_GITHUB_ORG} - > - <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="GitHub" /> - </a>, - ]; const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {}; const fullWidthClasses = isExpandedDisplayType ? 'pr4' : ''; const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png'; @@ -252,15 +118,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { color: isNightVersion ? 'white' : 'black', cursor: 'pointer', }; - const activeNode = ( - <div className="flex relative" style={{ color: menuIconStyle.color }}> - <div style={{ paddingRight: 10 }}>{this.props.translate.get(Key.Developers, Deco.Cap)}</div> - <div className="absolute" style={{ paddingLeft: 3, right: 3, top: -2 }}> - <i className="zmdi zmdi-caret-right" style={{ fontSize: 22 }} /> - </div> - </div> - ); - const popoverContent = <Menu style={{ color: colors.darkGrey }}>{developerSectionMenuItems}</Menu>; return ( <div style={{ ...styles.topBar, ...bottomBorderStyle, ...this.props.style, ...{ height } }} @@ -273,56 +130,45 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { paddingLeft={this.props.paddingLeft} paddingRight={this.props.paddingRight} > - <Link to={`${WebsitePaths.Home}`} className="text-decoration-none"> + <Link to={WebsitePaths.Home}> <img src={logoUrl} height="30" /> </Link> <div className="flex-auto" /> {!this._isViewingPortal() && ( <div className={menuClasses}> <div className="flex items-center justify-between"> - <DropDown - activeNode={activeNode} - popoverContent={popoverContent} - anchorOrigin={{ horizontal: 'middle', vertical: 'bottom' }} - targetOrigin={{ horizontal: 'middle', vertical: 'top' }} - style={styles.menuItem} - /> - <TopBarMenuItem - title={this.props.translate.get(Key.Wiki, Deco.Cap)} - path={`${WebsitePaths.Wiki}`} - style={styles.menuItem} - isNightVersion={isNightVersion} - isExternal={false} + <DevelopersDropDown + location={this.props.location} + menuItemStyles={{ ...styles.menuItem, paddingBottom: 12, paddingTop: 12 }} + translate={this.props.translate} + menuIconStyle={menuIconStyle} /> <TopBarMenuItem title={this.props.translate.get(Key.Blog, Deco.Cap)} path={constants.URL_BLOG} style={styles.menuItem} isNightVersion={isNightVersion} - isExternal={true} + shouldOpenInNewTab={true} /> <TopBarMenuItem title={this.props.translate.get(Key.About, Deco.Cap)} - path={`${WebsitePaths.About}`} + path={WebsitePaths.About} style={styles.menuItem} isNightVersion={isNightVersion} - isExternal={false} /> <TopBarMenuItem title={this.props.translate.get(Key.Careers, Deco.Cap)} - path={`${WebsitePaths.Careers}`} + path={WebsitePaths.Careers} style={styles.menuItem} isNightVersion={isNightVersion} - isExternal={false} /> <TopBarMenuItem title={this.props.translate.get(Key.TradeCallToAction, Deco.Cap)} - path={`${WebsitePaths.Portal}`} + path={WebsitePaths.Portal} isPrimary={true} style={styles.menuItem} className={`${isExpandedDisplayType && 'md-hide'}`} isNightVersion={isNightVersion} - isExternal={false} /> </div> </div> @@ -379,54 +225,35 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { onRequestChange={this._onMenuButtonClick.bind(this)} > <div className="clearfix"> - {this._renderDocsMenu()} - {this._renderWiki()} <div className="pl1 py1 mt3" style={{ backgroundColor: colors.lightGrey }}> {this.props.translate.get(Key.Website, Deco.Cap)} </div> - <Link to={WebsitePaths.Home} className="text-decoration-none"> + <Link to={WebsitePaths.Home}> <MenuItem className="py2">{this.props.translate.get(Key.Home, Deco.Cap)}</MenuItem> </Link> - <Link to={`${WebsitePaths.Wiki}`} className="text-decoration-none"> - <MenuItem className="py2">{this.props.translate.get(Key.Wiki, Deco.Cap)}</MenuItem> + <Link to={WebsitePaths.Docs}> + <MenuItem className="py2">{this.props.translate.get(Key.Docs, Deco.Cap)}</MenuItem> </Link> - {_.map(DOC_WEBSITE_PATHS_TO_KEY, (key, websitePath) => { - if (!this._doesUrlInclude(websitePath)) { - return ( - <Link - key={`drawer-menu-item-${websitePath}`} - to={websitePath} - className="text-decoration-none" - > - <MenuItem className="py2"> - {this.props.translate.get(key, Deco.Cap)}{' '} - {this.props.translate.get(Key.Docs, Deco.Cap)} - </MenuItem> - </Link> - ); - } - return null; - })} {!this._isViewingPortal() && ( - <Link to={`${WebsitePaths.Portal}`} className="text-decoration-none"> + <Link to={WebsitePaths.Portal}> <MenuItem className="py2"> {this.props.translate.get(Key.PortalDApp, Deco.CapWords)} </MenuItem> </Link> )} - <a className="text-decoration-none" target="_blank" href={`${WebsitePaths.Whitepaper}`}> + <Link to={WebsitePaths.Whitepaper} shouldOpenInNewTab={true}> <MenuItem className="py2">{this.props.translate.get(Key.Whitepaper, Deco.Cap)}</MenuItem> - </a> - <Link to={`${WebsitePaths.About}`} className="text-decoration-none"> + </Link> + <Link to={WebsitePaths.About}> <MenuItem className="py2">{this.props.translate.get(Key.About, Deco.Cap)}</MenuItem> </Link> - <Link to={`${WebsitePaths.Careers}`} className="text-decoration-none"> + <Link to={WebsitePaths.Careers}> <MenuItem className="py2">{this.props.translate.get(Key.Careers, Deco.Cap)}</MenuItem> </Link> - <a className="text-decoration-none" target="_blank" href={constants.URL_BLOG}> + <Link to={constants.URL_BLOG} shouldOpenInNewTab={true}> <MenuItem className="py2">{this.props.translate.get(Key.Blog, Deco.Cap)}</MenuItem> - </a> - <Link to={`${WebsitePaths.FAQ}`} className="text-decoration-none"> + </Link> + <Link to={WebsitePaths.FAQ}> <MenuItem className="py2" onClick={this._onMenuButtonClick.bind(this)}> {this.props.translate.get(Key.Faq, Deco.Cap)} </MenuItem> @@ -435,49 +262,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { </Drawer> ); } - private _renderDocsMenu(): React.ReactNode { - const isViewingDocsPage = _.some(DOC_WEBSITE_PATHS_TO_KEY, (_key, websitePath) => { - return this._doesUrlInclude(websitePath); - }); - // HACK: We need to make sure the SCROLL_CONTAINER is loaded before rendering the Sidebar - // because the sidebar renders `react-scroll` links which depend on the scroll container already - // being rendered. - const documentationContainer = document.getElementById(sharedConstants.SCROLL_CONTAINER_ID); - if (!isViewingDocsPage || _.isUndefined(this.props.menu) || _.isNull(documentationContainer)) { - return undefined; - } - return ( - <div className="lg-hide md-hide"> - <NestedSidebarMenu - topLevelMenu={this.props.menu} - menuSubsectionsBySection={this.props.menuSubsectionsBySection} - sidebarHeader={this.props.sidebarHeader} - shouldDisplaySectionHeaders={false} - onMenuItemClick={this._onMenuButtonClick.bind(this)} - selectedVersion={this.props.docsVersion} - versions={this.props.availableDocVersions} - onVersionSelected={this.props.onVersionSelected} - /> - </div> - ); - } - private _renderWiki(): React.ReactNode { - if (!this._isViewingWiki()) { - return undefined; - } - - return ( - <div className="lg-hide md-hide"> - <NestedSidebarMenu - topLevelMenu={this.props.menuSubsectionsBySection} - menuSubsectionsBySection={this.props.menuSubsectionsBySection} - sidebarHeader={this.props.sidebarHeader} - shouldDisplaySectionHeaders={false} - onMenuItemClick={this._onMenuButtonClick.bind(this)} - /> - </div> - ); - } private _onMenuButtonClick(): void { this.setState({ isDrawerOpen: !this.state.isDrawerOpen, @@ -486,28 +270,10 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { private _isViewingPortal(): boolean { return _.includes(this.props.location.pathname, WebsitePaths.Portal); } - private _isViewingDocs(): boolean { - return _.includes(this.props.location.pathname, WebsitePaths.Docs); - } private _isViewingFAQ(): boolean { return _.includes(this.props.location.pathname, WebsitePaths.FAQ); } - private _doesUrlInclude(aPath: string): boolean { - return _.includes(this.props.location.pathname, aPath); - } - private _isViewingWiki(): boolean { - return _.includes(this.props.location.pathname, WebsitePaths.Wiki); - } private _shouldDisplayBottomBar(): boolean { - const isViewingDocsPage = _.some(DOC_WEBSITE_PATHS_TO_KEY, (_key, websitePath) => { - return this._doesUrlInclude(websitePath); - }); - return ( - isViewingDocsPage || - this._isViewingWiki() || - this._isViewingFAQ() || - this._isViewingDocs() || - this._isViewingPortal() - ); + return this._isViewingFAQ() || this._isViewingPortal(); } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/top_bar/top_bar_menu_item.tsx b/packages/website/ts/components/top_bar/top_bar_menu_item.tsx index 25fab2868..eff5ba66d 100644 --- a/packages/website/ts/components/top_bar/top_bar_menu_item.tsx +++ b/packages/website/ts/components/top_bar/top_bar_menu_item.tsx @@ -1,7 +1,6 @@ -import { colors } from '@0xproject/react-shared'; +import { colors, Link } from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; -import { Link } from 'react-router-dom'; import { CallToAction } from 'ts/components/ui/button'; @@ -13,7 +12,7 @@ interface TopBarMenuItemProps { title: string; path?: string; isPrimary?: boolean; - isExternal: boolean; + shouldOpenInNewTab?: boolean; style?: React.CSSProperties; className?: string; isNightVersion?: boolean; @@ -26,6 +25,7 @@ export class TopBarMenuItem extends React.Component<TopBarMenuItemProps, TopBarM isPrimary: false, style: DEFAULT_STYLE, className: '', + shouldOpenInNewTab: false, isNightVersion: false, }; public render(): React.ReactNode { @@ -38,20 +38,9 @@ export class TopBarMenuItem extends React.Component<TopBarMenuItemProps, TopBarM ); return ( <div className={`center ${this.props.className}`} style={{ ...this.props.style, color: menuItemColor }}> - {this.props.isExternal ? ( - <a - className="text-decoration-none" - style={{ color: linkColor }} - target="_blank" - href={this.props.path} - > - {itemContent} - </a> - ) : ( - <Link to={this.props.path} className="text-decoration-none" style={{ color: linkColor }}> - {itemContent} - </Link> - )} + <Link to={this.props.path} shouldOpenInNewTab={this.props.shouldOpenInNewTab} fontColor={linkColor}> + {itemContent} + </Link> </div> ); } diff --git a/packages/website/ts/components/ui/button.tsx b/packages/website/ts/components/ui/button.tsx index 75ba7bcff..2a6a1f477 100644 --- a/packages/website/ts/components/ui/button.tsx +++ b/packages/website/ts/components/ui/button.tsx @@ -10,11 +10,13 @@ export interface ButtonProps { fontFamily?: string; backgroundColor?: string; borderColor?: string; + borderRadius?: string; width?: string; padding?: string; type?: string; isDisabled?: boolean; onClick?: (event: React.MouseEvent<HTMLElement>) => void; + textAlign?: string; } const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, isDisabled, onClick, type, className }) => ( @@ -29,11 +31,12 @@ export const Button = styled(PlainButton)` color: ${props => props.fontColor}; transition: background-color, opacity 0.5s ease; padding: ${props => props.padding}; - border-radius: 6px; + border-radius: ${props => props.borderRadius}; font-weight: 500; outline: none; font-family: ${props => props.fontFamily}; width: ${props => props.width}; + text-align: ${props => props.textAlign}; background-color: ${props => props.backgroundColor}; border: ${props => (props.borderColor ? `1px solid ${props.borderColor}` : 'none')}; &:hover { @@ -52,11 +55,13 @@ export const Button = styled(PlainButton)` Button.defaultProps = { fontSize: '12px', + borderRadius: '6px', backgroundColor: colors.white, width: 'auto', fontFamily: 'Roboto', isDisabled: false, padding: '0.8em 2.2em', + textAlign: 'center', }; Button.displayName = 'Button'; diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx index f2ae68b70..ece077563 100644 --- a/packages/website/ts/components/ui/container.tsx +++ b/packages/website/ts/components/ui/container.tsx @@ -15,7 +15,11 @@ export interface ContainerProps { paddingRight?: StringOrNum; paddingLeft?: StringOrNum; backgroundColor?: string; + background?: string; borderRadius?: StringOrNum; + borderBottomLeftRadius?: StringOrNum; + borderBottomRightRadius?: StringOrNum; + borderBottom?: StringOrNum; maxWidth?: StringOrNum; maxHeight?: StringOrNum; width?: StringOrNum; diff --git a/packages/website/ts/components/ui/menu_item.tsx b/packages/website/ts/components/ui/custom_menu_item.tsx index 0cb4b387c..c25da6be6 100644 --- a/packages/website/ts/components/ui/menu_item.tsx +++ b/packages/website/ts/components/ui/custom_menu_item.tsx @@ -1,24 +1,23 @@ +import { Link } from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; -import { Link } from 'react-router-dom'; -interface MenuItemProps { +interface CustomMenuItemProps { to: string; - style?: React.CSSProperties; onClick?: () => void; className?: string; } -interface MenuItemState { +interface CustomMenuItemState { isHovering: boolean; } -export class MenuItem extends React.Component<MenuItemProps, MenuItemState> { - public static defaultProps: Partial<MenuItemProps> = { +export class CustomMenuItem extends React.Component<CustomMenuItemProps, CustomMenuItemState> { + public static defaultProps: Partial<CustomMenuItemProps> = { onClick: _.noop.bind(_), className: '', }; - public constructor(props: MenuItemProps) { + public constructor(props: CustomMenuItemProps) { super(props); this.state = { isHovering: false, @@ -30,7 +29,7 @@ export class MenuItem extends React.Component<MenuItemProps, MenuItemState> { opacity: this.state.isHovering ? 0.5 : 1, }; return ( - <Link to={this.props.to} style={{ textDecoration: 'none', ...this.props.style }}> + <Link to={this.props.to}> <div onClick={this.props.onClick.bind(this)} className={`mx-auto ${this.props.className}`} diff --git a/packages/website/ts/components/ui/drop_down.tsx b/packages/website/ts/components/ui/drop_down.tsx index 4d5caef08..4138b3fe5 100644 --- a/packages/website/ts/components/ui/drop_down.tsx +++ b/packages/website/ts/components/ui/drop_down.tsx @@ -1,3 +1,4 @@ +import * as _ from 'lodash'; import Popover from 'material-ui/Popover'; import * as React from 'react'; import { MaterialUIPosition } from 'ts/types'; @@ -21,6 +22,7 @@ export interface DropDownProps { zDepth?: number; activateEvent?: DropdownMouseEvent; closeEvent?: DropdownMouseEvent; + popoverStyle?: React.CSSProperties; } interface DropDownState { @@ -34,6 +36,7 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> { zDepth: 1, activateEvent: DropdownMouseEvent.Hover, closeEvent: DropdownMouseEvent.Hover, + popoverStyle: {}, }; private _isHovering: boolean; private _popoverCloseCheckIntervalId: number; @@ -73,10 +76,15 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> { anchorEl={this.state.anchorEl} anchorOrigin={this.props.anchorOrigin} targetOrigin={this.props.targetOrigin} - onRequestClose={this._closePopover.bind(this)} + onRequestClose={ + this.props.closeEvent === DropdownMouseEvent.Click + ? this._closePopover.bind(this) + : _.noop.bind(_) + } useLayerForClickAway={this.props.closeEvent === DropdownMouseEvent.Click} animated={false} zDepth={this.props.zDepth} + style={this.props.popoverStyle} > <div onMouseEnter={this._onHover.bind(this)} @@ -92,7 +100,7 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> { private _onActiveNodeClick(event: React.FormEvent<HTMLInputElement>): void { if (this.props.activateEvent === DropdownMouseEvent.Click) { this.setState({ - isDropDownOpen: true, + isDropDownOpen: !this.state.isDropDownOpen, anchorEl: event.currentTarget, }); } diff --git a/packages/website/ts/components/ui/simple_menu.tsx b/packages/website/ts/components/ui/simple_menu.tsx index 8a9349c6d..bdaf0701e 100644 --- a/packages/website/ts/components/ui/simple_menu.tsx +++ b/packages/website/ts/components/ui/simple_menu.tsx @@ -1,7 +1,7 @@ +import { Link } from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; import * as CopyToClipboard from 'react-copy-to-clipboard'; -import { Link } from 'react-router-dom'; import { Container } from 'ts/components/ui/container'; import { Text } from 'ts/components/ui/text'; @@ -72,7 +72,7 @@ export const GoToAccountManagementSimpleMenuItem: React.StatelessComponent< GoToAccountManagementSimpleMenuItemProps > = ({ onClick }) => { return ( - <Link to={`${WebsitePaths.Portal}/account`} style={{ textDecoration: 'none' }}> + <Link to={`${WebsitePaths.Portal}/account`}> <SimpleMenuItem displayText="Manage Account..." onClick={onClick} /> </Link> ); diff --git a/packages/website/ts/components/ui/text.tsx b/packages/website/ts/components/ui/text.tsx index cd8f290e3..4415962b1 100644 --- a/packages/website/ts/components/ui/text.tsx +++ b/packages/website/ts/components/ui/text.tsx @@ -19,7 +19,9 @@ export interface TextProps { textDecorationLine?: string; onClick?: (event: React.MouseEvent<HTMLElement>) => void; hoverColor?: string; + letterSpacing?: string | number; noWrap?: boolean; + textAlign?: string; display?: string; } @@ -34,6 +36,8 @@ export const Text = styled(PlainText)` font-style: ${props => props.fontStyle}; font-weight: ${props => props.fontWeight}; font-size: ${props => props.fontSize}; + text-align: ${props => props.textAlign}; + letter-spacing: ${props => props.letterSpacing}; text-decoration-line: ${props => props.textDecorationLine}; ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')}; ${props => (props.center ? 'text-align: center' : '')}; diff --git a/packages/website/ts/containers/asset_buyer_documentation.ts b/packages/website/ts/containers/asset_buyer_documentation.ts index f794625de..13a597ea3 100644 --- a/packages/website/ts/containers/asset_buyer_documentation.ts +++ b/packages/website/ts/containers/asset_buyer_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -47,6 +47,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -58,6 +59,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/connect_documentation.ts b/packages/website/ts/containers/connect_documentation.ts index a728abe2c..7c86357c5 100644 --- a/packages/website/ts/containers/connect_documentation.ts +++ b/packages/website/ts/containers/connect_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -26,8 +26,7 @@ const docsInfoConfig: DocsInfoConfig = { displayName: '0x Connect', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [markdownSections.introduction], - install: [markdownSections.installation], + 'getting-started': [markdownSections.introduction, markdownSections.installation], }, sectionNameToMarkdownByVersion: { '0.0.1': { @@ -48,6 +47,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -59,6 +59,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/contract_wrappers_documentation.ts b/packages/website/ts/containers/contract_wrappers_documentation.ts index 1e1735846..f7e5f17eb 100644 --- a/packages/website/ts/containers/contract_wrappers_documentation.ts +++ b/packages/website/ts/containers/contract_wrappers_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -25,8 +25,7 @@ const docsInfoConfig: DocsInfoConfig = { displayName: 'Contract Wrappers', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [markdownSections.introduction], - install: [markdownSections.installation], + 'getting-started': [markdownSections.introduction, markdownSections.installation], }, sectionNameToMarkdownByVersion: { '0.0.1': { @@ -43,6 +42,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -54,6 +54,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, docsInfo, translate: state.translate, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/docs_home.ts b/packages/website/ts/containers/docs_home.ts new file mode 100644 index 000000000..e0ca439a6 --- /dev/null +++ b/packages/website/ts/containers/docs_home.ts @@ -0,0 +1,31 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { DocsHome as DocsHomeComponent, DocsHomeProps } from 'ts/pages/documentation/docs_home'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { State } from 'ts/redux/reducer'; +import { ScreenWidths } from 'ts/types'; +import { Translate } from 'ts/utils/translate'; + +interface ConnectedState { + translate: Translate; + screenWidth: ScreenWidths; +} + +interface ConnectedDispatch { + dispatcher: Dispatcher; +} + +const mapStateToProps = (state: State, _ownProps: DocsHomeProps): ConnectedState => ({ + translate: state.translate, + screenWidth: state.screenWidth, +}); + +const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ + dispatcher: new Dispatcher(dispatch), +}); + +export const DocsHome: React.ComponentClass<DocsHomeProps> = connect(mapStateToProps, mapDispatchToProps)( + DocsHomeComponent, +); diff --git a/packages/website/ts/containers/ethereum_types_documentation.ts b/packages/website/ts/containers/ethereum_types_documentation.ts index 2b6d6e64d..3cab885b4 100644 --- a/packages/website/ts/containers/ethereum_types_documentation.ts +++ b/packages/website/ts/containers/ethereum_types_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -26,9 +26,7 @@ const docsInfoConfig: DocsInfoConfig = { displayName: 'Ethereum Types', packageUrl: 'https://github.com/0xProject/0x-monorepo/packages/ethereum-types', markdownMenu: { - introduction: [markdownSections.introduction], - install: [markdownSections.installation], - types: [markdownSections.types], + 'getting-started': [markdownSections.introduction, markdownSections.installation], }, sectionNameToMarkdownByVersion: { '0.0.1': { @@ -45,6 +43,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -56,6 +55,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/json_schemas_documentation.ts b/packages/website/ts/containers/json_schemas_documentation.ts index f32a50ea9..4b3200203 100644 --- a/packages/website/ts/containers/json_schemas_documentation.ts +++ b/packages/website/ts/containers/json_schemas_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -20,7 +20,6 @@ const markdownSections = { introduction: 'introduction', installation: 'installation', usage: 'usage', - schemaValidator: 'schemaValidator', schemas: 'schemas', }; @@ -31,10 +30,7 @@ const docsInfoConfig: DocsInfoConfig = { displayName: 'JSON Schemas', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [markdownSections.introduction], - install: [markdownSections.installation], - usage: [markdownSections.usage], - schemaValidator: [markdownSections.schemaValidator], + 'getting-started': [markdownSections.introduction, markdownSections.installation, markdownSections.usage], schemas: [markdownSections.schemas], }, sectionNameToMarkdownByVersion: { @@ -60,6 +56,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -71,6 +68,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/order_utils_documentation.ts b/packages/website/ts/containers/order_utils_documentation.ts index b54c30a1e..77204b480 100644 --- a/packages/website/ts/containers/order_utils_documentation.ts +++ b/packages/website/ts/containers/order_utils_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -25,8 +25,7 @@ const docsInfoConfig: DocsInfoConfig = { displayName: 'Order utils', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [markdownSections.introduction], - install: [markdownSections.installation], + 'getting-started': [markdownSections.introduction, markdownSections.installation], }, sectionNameToMarkdownByVersion: { '0.0.1': { @@ -43,6 +42,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -54,6 +54,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/order_watcher_documentation.ts b/packages/website/ts/containers/order_watcher_documentation.ts index 59a018847..1b0c1ec91 100644 --- a/packages/website/ts/containers/order_watcher_documentation.ts +++ b/packages/website/ts/containers/order_watcher_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -25,8 +25,7 @@ const docsInfoConfig: DocsInfoConfig = { displayName: 'OrderWatcher', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [markdownSections.introduction], - install: [markdownSections.installation], + 'getting-started': [markdownSections.introduction, markdownSections.installation], }, sectionNameToMarkdownByVersion: { '0.0.1': { @@ -43,6 +42,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -54,6 +54,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/smart_contracts_documentation.ts b/packages/website/ts/containers/smart_contracts_documentation.ts index 05b2a50c3..0989a7d0e 100644 --- a/packages/website/ts/containers/smart_contracts_documentation.ts +++ b/packages/website/ts/containers/smart_contracts_documentation.ts @@ -6,7 +6,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages, SmartContractDocSections as Sections } from 'ts/types'; +import { DocPackages, ScreenWidths, SmartContractDocSections as Sections } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -97,6 +97,7 @@ interface ConnectedState { docsVersion: string; availableDocVersions: string[]; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -108,6 +109,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState docsVersion: state.docsVersion, availableDocVersions: state.availableDocVersions, translate: state.translate, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/sol_compiler_documentation.ts b/packages/website/ts/containers/sol_compiler_documentation.ts index 20f26ed1d..b84974d69 100644 --- a/packages/website/ts/containers/sol_compiler_documentation.ts +++ b/packages/website/ts/containers/sol_compiler_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -27,9 +27,7 @@ const docsInfoConfig: DocsInfoConfig = { displayName: 'Solidity Compiler', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [markdownSections.introduction], - install: [markdownSections.installation], - usage: [markdownSections.usage], + 'getting-started': [markdownSections.introduction, markdownSections.installation, markdownSections.usage], }, sectionNameToMarkdownByVersion: { '0.0.1': { @@ -47,6 +45,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -58,6 +57,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/sol_cov_documentation.ts b/packages/website/ts/containers/sol_cov_documentation.ts index 27efd641e..f9c428f8e 100644 --- a/packages/website/ts/containers/sol_cov_documentation.ts +++ b/packages/website/ts/containers/sol_cov_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -27,9 +27,7 @@ const docsInfoConfig: DocsInfoConfig = { displayName: 'Sol-cov', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [markdownSections.introduction], - install: [markdownSections.installation], - usage: [markdownSections.usage], + 'getting-started': [markdownSections.introduction, markdownSections.installation, markdownSections.usage], }, sectionNameToMarkdownByVersion: { '0.0.1': { @@ -47,6 +45,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -58,6 +57,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/subproviders_documentation.ts b/packages/website/ts/containers/subproviders_documentation.ts index 28b2e9508..1588c7d46 100644 --- a/packages/website/ts/containers/subproviders_documentation.ts +++ b/packages/website/ts/containers/subproviders_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -27,9 +27,7 @@ const docsInfoConfig: DocsInfoConfig = { displayName: 'Subproviders', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [docSections.introduction], - install: [docSections.installation], - ['ledger-node-hid-issue']: [docSections.ledgerNodeHid], + 'getting-started': [docSections.introduction, docSections.installation, docSections.ledgerNodeHid], }, sectionNameToMarkdownByVersion: { '0.0.1': { @@ -47,6 +45,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -58,6 +57,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/web3_wrapper_documentation.ts b/packages/website/ts/containers/web3_wrapper_documentation.ts index dc9d23304..0154c05b9 100644 --- a/packages/website/ts/containers/web3_wrapper_documentation.ts +++ b/packages/website/ts/containers/web3_wrapper_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -13,7 +13,7 @@ const IntroMarkdownV1 = require('md/docs/web3_wrapper/introduction'); const InstallationMarkdownV1 = require('md/docs/web3_wrapper/installation'); /* tslint:enable:no-var-requires */ -const docSections = { +const markdownSections = { introduction: 'introduction', installation: 'installation', }; @@ -25,16 +25,15 @@ const docsInfoConfig: DocsInfoConfig = { displayName: 'Web3Wrapper', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [docSections.introduction], - install: [docSections.installation], + 'getting-started': [markdownSections.introduction, markdownSections.installation], }, sectionNameToMarkdownByVersion: { '0.0.1': { - [docSections.introduction]: IntroMarkdownV1, - [docSections.installation]: InstallationMarkdownV1, + [markdownSections.introduction]: IntroMarkdownV1, + [markdownSections.installation]: InstallationMarkdownV1, }, }, - markdownSections: docSections, + markdownSections, }; const docsInfo = new DocsInfo(docsInfoConfig); @@ -43,6 +42,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -54,6 +54,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, translate: state.translate, docsInfo, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/wiki.ts b/packages/website/ts/containers/wiki.ts index 357f8bbf4..069b0ae54 100644 --- a/packages/website/ts/containers/wiki.ts +++ b/packages/website/ts/containers/wiki.ts @@ -4,10 +4,12 @@ import { Dispatch } from 'redux'; import { Wiki as WikiComponent, WikiProps } from 'ts/pages/wiki/wiki'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; +import { ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; interface ConnectedState { translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -16,6 +18,7 @@ interface ConnectedDispatch { const mapStateToProps = (state: State, _ownProps: WikiProps): ConnectedState => ({ translate: state.translate, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/containers/zero_ex_js_documentation.ts b/packages/website/ts/containers/zero_ex_js_documentation.ts index 2a3afd86c..459d8390a 100644 --- a/packages/website/ts/containers/zero_ex_js_documentation.ts +++ b/packages/website/ts/containers/zero_ex_js_documentation.ts @@ -5,7 +5,7 @@ import { Dispatch } from 'redux'; import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -39,9 +39,13 @@ const docsInfoConfig: DocsInfoConfig = { displayName: '0x.js', packageUrl: 'https://github.com/0xProject/0x-monorepo', markdownMenu: { - introduction: [markdownSections.introduction], - install: [markdownSections.installation], - topics: [markdownSections.async, markdownSections.errors, markdownSections.versioning], + 'getting-started': [ + markdownSections.introduction, + markdownSections.installation, + markdownSections.async, + markdownSections.errors, + markdownSections.versioning, + ], }, sectionNameToMarkdownByVersion: { '0.0.1': { @@ -68,6 +72,7 @@ interface ConnectedState { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface ConnectedDispatch { @@ -79,6 +84,7 @@ const mapStateToProps = (state: State, _ownProps: DocPageProps): ConnectedState availableDocVersions: state.availableDocVersions, docsInfo, translate: state.translate, + screenWidth: state.screenWidth, }); const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index bb218eac1..21157e427 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -5,6 +5,7 @@ import { Provider } from 'react-redux'; import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-dom'; import { MetaTags } from 'ts/components/meta_tags'; import { About } from 'ts/containers/about'; +import { DocsHome } from 'ts/containers/docs_home'; import { FAQ } from 'ts/containers/faq'; import { Jobs } from 'ts/containers/jobs'; import { Landing } from 'ts/containers/landing'; @@ -140,6 +141,7 @@ render( path={`${WebsitePaths.AssetBuyer}/:version?`} component={LazyAssetBuyerDocumentation} /> + <Route path={WebsitePaths.Docs} component={DocsHome as any} /> {/* Legacy endpoints */} <Route path={`${WebsiteLegacyPaths.ZeroExJs}/:version?`} diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index 3ede56a71..4d346327b 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -1,8 +1,7 @@ -import { colors, Styles } from '@0xproject/react-shared'; +import { colors, Link, Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; import * as DocumentTitle from 'react-document-title'; -import { Link } from 'react-router-dom'; import { Footer } from 'ts/components/footer'; import { TopBar } from 'ts/components/top_bar/top_bar'; import { Profile } from 'ts/pages/about/profile'; @@ -389,7 +388,7 @@ export class About extends React.Component<AboutProps, AboutState> { }} > We are seeking outstanding candidates to{' '} - <Link to={WebsitePaths.Careers} style={{ color: 'black' }}> + <Link to={WebsitePaths.Careers} textDecoration="underline" fontColor="black"> join our team </Link> . We value passion, diversity and unique perspectives. diff --git a/packages/website/ts/pages/documentation/developers_page.tsx b/packages/website/ts/pages/documentation/developers_page.tsx new file mode 100644 index 000000000..e35218a70 --- /dev/null +++ b/packages/website/ts/pages/documentation/developers_page.tsx @@ -0,0 +1,182 @@ +import { colors, constants as sharedConstants } from '@0xproject/react-shared'; +import * as _ from 'lodash'; +import * as React from 'react'; +import DocumentTitle = require('react-document-title'); +import { DocsLogo } from 'ts/components/documentation/docs_logo'; +import { DocsTopBar } from 'ts/components/documentation/docs_top_bar'; +import { Container } from 'ts/components/ui/container'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { ScreenWidths } from 'ts/types'; +import { Translate } from 'ts/utils/translate'; +import { utils } from 'ts/utils/utils'; + +const THROTTLE_TIMEOUT = 100; +const TOP_BAR_HEIGHT = 80; +const SCROLLER_WIDTH = 4; + +export interface DevelopersPageProps { + location: Location; + translate: Translate; + screenWidth: ScreenWidths; + dispatcher: Dispatcher; + mainContent: React.ReactNode; + sidebar: React.ReactNode; +} + +export interface DevelopersPageState { + isHoveringSidebar: boolean; + isHoveringMainContent: boolean; + isSidebarScrolling: boolean; +} + +export class DevelopersPage extends React.Component<DevelopersPageProps, DevelopersPageState> { + private readonly _throttledScreenWidthUpdate: () => void; + private readonly _throttledSidebarScrolling: () => void; + private _sidebarScrollClearingInterval: number; + constructor(props: DevelopersPageProps) { + super(props); + this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); + this._throttledSidebarScrolling = _.throttle(this._onSidebarScroll.bind(this), THROTTLE_TIMEOUT); + this.state = { + isHoveringSidebar: false, + isHoveringMainContent: false, + isSidebarScrolling: false, + }; + } + public componentDidMount(): void { + window.addEventListener('resize', this._throttledScreenWidthUpdate); + window.scrollTo(0, 0); + this._sidebarScrollClearingInterval = window.setInterval(() => { + this.setState({ + isSidebarScrolling: false, + }); + }, 1000); + } + public componentWillUnmount(): void { + window.removeEventListener('resize', this._throttledScreenWidthUpdate); + window.clearInterval(this._sidebarScrollClearingInterval); + } + public render(): React.ReactNode { + const scrollableContainerStyles: React.CSSProperties = { + position: 'absolute', + top: 80, + left: 0, + bottom: 0, + right: 0, + overflowX: 'hidden', + overflowY: 'scroll', + minHeight: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, + WebkitOverflowScrolling: 'touch', + }; + const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; + const mainContentPadding = isSmallScreen ? 20 : 50; + const sidebarPadding = 22; + return ( + <Container + className="flex items-center overflow-hidden" + width="100%" + background={`linear-gradient(to right, ${colors.grey100} 0%, ${colors.grey100} 50%, ${ + colors.white + } 50%, ${colors.white} 100%)`} + > + <DocumentTitle title="0x Docs DevelopersPage" /> + <Container className="flex mx-auto" height="100vh"> + <Container + className="sm-hide xs-hide relative" + width={234} + paddingLeft={22} + paddingRight={22} + paddingTop={0} + backgroundColor={colors.grey100} + > + <Container + borderBottom={this.state.isSidebarScrolling ? `1px solid ${colors.grey300}` : 'none'} + > + <Container paddingTop="30px" paddingLeft="10px" paddingBottom="8px"> + <DocsLogo height={36} /> + </Container> + </Container> + <div + style={{ + ...scrollableContainerStyles, + paddingTop: 27, + overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden', + }} + onMouseEnter={this._onSidebarHover.bind(this, true)} + onMouseLeave={this._onSidebarHover.bind(this, false)} + onWheel={this._throttledSidebarScrolling} + > + <div + style={{ + paddingBottom: 100, + paddingLeft: sidebarPadding, + paddingRight: this.state.isHoveringSidebar + ? sidebarPadding - SCROLLER_WIDTH + : sidebarPadding, + }} + > + {this.props.screenWidth !== ScreenWidths.Sm && this.props.sidebar} + </div> + </div> + </Container> + <Container + className="relative" + width={isSmallScreen ? '100vw' : 786} + paddingBottom="100px" + backgroundColor={colors.white} + > + <Container paddingLeft={mainContentPadding} paddingRight={mainContentPadding}> + <DocsTopBar + location={this.props.location} + screenWidth={this.props.screenWidth} + translate={this.props.translate} + sidebar={this.props.sidebar} + /> + </Container> + <div + id={sharedConstants.SCROLL_CONTAINER_ID} + className="absolute" + style={{ + ...scrollableContainerStyles, + paddingTop: 0, + paddingLeft: mainContentPadding, + paddingRight: this.state.isHoveringMainContent + ? mainContentPadding - SCROLLER_WIDTH + : mainContentPadding, + overflow: this.state.isHoveringMainContent ? 'auto' : 'hidden', + }} + onMouseEnter={this._onMainContentHover.bind(this, true)} + onMouseOver={this._onMainContentHover.bind(this, true)} + onMouseLeave={this._onMainContentHover.bind(this, false)} + > + {this.props.mainContent} + </div> + </Container> + </Container> + </Container> + ); + } + private _onSidebarHover(isHovering: boolean, _event: React.FormEvent<HTMLInputElement>): void { + if (isHovering !== this.state.isHoveringSidebar) { + this.setState({ + isHoveringSidebar: isHovering, + }); + } + } + private _onMainContentHover(isHovering: boolean, _event: React.FormEvent<HTMLInputElement>): void { + if (isHovering !== this.state.isHoveringMainContent) { + this.setState({ + isHoveringMainContent: isHovering, + }); + } + } + private _onSidebarScroll(_event: React.FormEvent<HTMLInputElement>): void { + this.setState({ + isSidebarScrolling: true, + }); + } + private _updateScreenWidth(): void { + const newScreenWidth = utils.getScreenWidth(); + this.props.dispatcher.updateScreenWidth(newScreenWidth); + } +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/pages/documentation/doc_page.tsx b/packages/website/ts/pages/documentation/doc_page.tsx index 135581fc9..40c728c24 100644 --- a/packages/website/ts/pages/documentation/doc_page.tsx +++ b/packages/website/ts/pages/documentation/doc_page.tsx @@ -1,35 +1,30 @@ import { DocAgnosticFormat, + DocReference, DocsInfo, - Documentation, GeneratedDocJson, SupportedDocJson, TypeDocUtils, } from '@0xproject/react-docs'; +import { NestedSidebarMenu } from '@0xproject/react-shared'; import findVersions = require('find-versions'); import * as _ from 'lodash'; +import CircularProgress from 'material-ui/CircularProgress'; import * as React from 'react'; -import DocumentTitle = require('react-document-title'); import semverSort = require('semver-sort'); -import { SidebarHeader } from 'ts/components/sidebar_header'; -import { TopBar } from 'ts/components/top_bar/top_bar'; +import { SidebarHeader } from 'ts/components/documentation/sidebar_header'; +import { Container } from 'ts/components/ui/container'; +import { DevelopersPage } from 'ts/pages/documentation/developers_page'; import { Dispatcher } from 'ts/redux/dispatcher'; -import { DocPackages } from 'ts/types'; +import { DocPackages, ScreenWidths } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { docUtils } from 'ts/utils/doc_utils'; import { Translate } from 'ts/utils/translate'; import { utils } from 'ts/utils/utils'; const isDevelopmentOrStaging = utils.isDevelopment() || utils.isStaging(); -const DEFAULT_ICON = 'docs.png'; const ZERO_EX_JS_VERSION_MISSING_TOPLEVEL_PATH = '0.32.4'; -const idToIcon: { [id: string]: string } = { - [DocPackages.ZeroExJs]: 'zeroExJs.png', - [DocPackages.Connect]: 'connect.png', - [DocPackages.SmartContracts]: 'contracts.png', -}; - const docIdToSubpackageName: { [id: string]: string } = { [DocPackages.ZeroExJs]: '0x.js', [DocPackages.Connect]: 'connect', @@ -53,6 +48,7 @@ export interface DocPageProps { availableDocVersions: string[]; docsInfo: DocsInfo; translate: Translate; + screenWidth: ScreenWidths; } interface DocPageState { @@ -80,38 +76,58 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> { this._isUnmounted = true; } public render(): React.ReactNode { - const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat) - ? {} - : this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat); const sourceUrl = this._getSourceUrl(); - const iconFileName = idToIcon[this.props.docsInfo.id] || DEFAULT_ICON; - const iconUrl = `/images/doc_icons/${iconFileName}`; + const sectionNameToLinks = _.isUndefined(this.state.docAgnosticFormat) + ? {} + : this.props.docsInfo.getSectionNameToLinks(this.state.docAgnosticFormat); + const mainContent = _.isUndefined(this.state.docAgnosticFormat) ? ( + <div className="flex justify-center">{this._renderLoading()}</div> + ) : ( + <DocReference + selectedVersion={this.props.docsVersion} + availableVersions={this.props.availableDocVersions} + docsInfo={this.props.docsInfo} + docAgnosticFormat={this.state.docAgnosticFormat} + sourceUrl={sourceUrl} + /> + ); + const sidebar = _.isUndefined(this.state.docAgnosticFormat) ? ( + <div /> + ) : ( + <NestedSidebarMenu sidebarHeader={this._renderSidebarHeader()} sectionNameToLinks={sectionNameToLinks} /> + ); + return ( + <DevelopersPage + sidebar={sidebar} + mainContent={mainContent} + location={this.props.location} + screenWidth={this.props.screenWidth} + translate={this.props.translate} + dispatcher={this.props.dispatcher} + /> + ); + } + private _renderSidebarHeader(): React.ReactNode { + return ( + <SidebarHeader + screenWidth={this.props.screenWidth} + title={this.props.docsInfo.displayName} + docsVersion={this.props.docsVersion} + availableDocVersions={this.props.availableDocVersions} + onVersionSelected={this._onVersionSelected.bind(this)} + /> + ); + } + private _renderLoading(): React.ReactNode { return ( - <div> - <DocumentTitle title={`${this.props.docsInfo.displayName} Documentation`} /> - <TopBar - blockchainIsLoaded={false} - location={this.props.location} - docsVersion={this.props.docsVersion} - availableDocVersions={this.props.availableDocVersions} - menu={this.props.docsInfo.menu} - menuSubsectionsBySection={menuSubsectionsBySection} - docsInfo={this.props.docsInfo} - translate={this.props.translate} - onVersionSelected={this._onVersionSelected.bind(this)} - sidebarHeader={<SidebarHeader title={this.props.docsInfo.displayName} iconUrl={iconUrl} />} - /> - <Documentation - selectedVersion={this.props.docsVersion} - availableVersions={this.props.availableDocVersions} - docsInfo={this.props.docsInfo} - docAgnosticFormat={this.state.docAgnosticFormat} - sidebarHeader={<SidebarHeader title={this.props.docsInfo.displayName} iconUrl={iconUrl} />} - sourceUrl={sourceUrl} - topBarHeight={60} - onVersionSelected={this._onVersionSelected.bind(this)} - /> - </div> + <Container className="pt4"> + <Container className="center pb2"> + <CircularProgress size={40} thickness={5} /> + </Container> + <Container className="center pt2" paddingBottom="11px"> + Loading documentation... + </Container> + </Container> ); } private async _fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise<void> { @@ -146,10 +162,10 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> { // documenting solidity. docAgnosticFormat = versionDocObj as DocAgnosticFormat; // HACK: need to modify docsInfo like convertToDocAgnosticFormat() would do - this.props.docsInfo.menu.Contracts = []; + this.props.docsInfo.markdownMenu.Contracts = []; _.each(docAgnosticFormat, (_docObj, sectionName) => { this.props.docsInfo.sections[sectionName] = sectionName; - this.props.docsInfo.menu.Contracts.push(sectionName); + this.props.docsInfo.markdownMenu.Contracts.push(sectionName); }); } diff --git a/packages/website/ts/pages/documentation/docs_home.tsx b/packages/website/ts/pages/documentation/docs_home.tsx new file mode 100644 index 000000000..f0af78fc1 --- /dev/null +++ b/packages/website/ts/pages/documentation/docs_home.tsx @@ -0,0 +1,384 @@ +import { ALink, colors, Link, NestedSidebarMenu } from '@0xproject/react-shared'; +import { ObjectMap } from '@0xproject/types'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { OverviewContent } from 'ts/components/documentation/overview_content'; +import { Button } from 'ts/components/ui/button'; +import { DevelopersPage } from 'ts/pages/documentation/developers_page'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { Categories, Deco, Key, Package, ScreenWidths, TutorialInfo, WebsitePaths } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; + +const TUTORIALS: TutorialInfo[] = [ + { + iconUrl: '/images/developers/tutorials/develop_on_ethereum.svg', + description: Key.DevelopOnEthereumDescription, + link: { + title: Key.DevelopOnEthereum, + to: `${WebsitePaths.Wiki}#Ethereum-Development`, + }, + }, + { + iconUrl: '/images/developers/tutorials/build_a_relayer.svg', + description: Key.BuildARelayerDescription, + link: { + title: Key.BuildARelayer, + to: `${WebsitePaths.Wiki}#Build-A-Relayer`, + }, + }, + { + iconUrl: '/images/developers/tutorials/0x_order_basics.svg', + description: Key.OrderBasicsDescription, + link: { + title: Key.OrderBasics, + to: `${WebsitePaths.Wiki}#Create,-Validate,-Fill-Order`, + }, + }, + { + iconUrl: '/images/developers/tutorials/use_shared_liquidity.svg', + description: Key.UseSharedLiquidityDescription, + link: { + title: Key.UseSharedLiquidity, + to: `${WebsitePaths.Wiki}#Find,-Submit,-Fill-Order-From-Relayer`, + }, + }, +]; + +const CATEGORY_TO_PACKAGES: ObjectMap<Package[]> = { + [Categories.ZeroExProtocol]: [ + { + description: + 'A library for interacting with the 0x protocol. It is a high level package which combines a number of smaller specific-purpose packages such as [order-utils](https://0xproject.com/docs/order-utils) and [contract-wrappers](https://0xproject.com/docs/contract-wrappers).', + link: { + title: '0x.js', + to: WebsitePaths.ZeroExJs, + }, + }, + { + description: + 'A Typescript starter project that will walk you through the basics of how to interact with 0x Protocol and trade of an SRA relayer', + link: { + title: '0x starter project', + to: 'https://github.com/0xProject/0x-starter-project', + shouldOpenInNewTab: true, + }, + }, + { + description: + 'An http & websocket client for interacting with relayers that have implemented the [Standard Relayer API](https://github.com/0xProject/standard-relayer-api)', + link: { + title: '@0xproject/connect', + to: WebsitePaths.Connect, + }, + }, + { + description: + 'Typescript/Javascript wrappers of the 0x protocol Ethereum smart contracts. Use this library to call methods on the 0x smart contracts, subscribe to contract events and to fetch information stored in contracts.', + link: { + title: '@0xproject/contract-wrappers', + to: WebsitePaths.ContractWrappers, + }, + }, + { + description: + 'A collection of 0x-related JSON-schemas (incl. SRA request/response schemas, 0x order message format schema, etc...)', + link: { + title: '@0xproject/json-schemas', + to: WebsitePaths.JSONSchemas, + }, + }, + { + description: + 'A set of utils for working with 0x orders. It includes utilities for creating, signing, validating 0x orders, encoding/decoding assetData and much more.', + link: { + title: '@0xproject/order-utils', + to: WebsitePaths.OrderUtils, + }, + }, + { + description: + "A daemon that watches a set of 0x orders and emits events when an order's fillability has changed. Can be used by a relayer to prune their orderbook or by a trader to keep their view of the market up-to-date.", + link: { + title: '@0xproject/order-watcher', + to: WebsitePaths.OrderWatcher, + }, + }, + { + description: + 'Contains the Standard Relayer API OpenAPI Spec. The package distributes both a javascript object version and a json version.', + link: { + title: '@0xproject/sra-spec', + to: 'https://github.com/0xProject/0x-monorepo/tree/development/packages/sra-spec', + shouldOpenInNewTab: true, + }, + }, + { + description: + 'Convenience package for buying assets represented on the Ethereum blockchain using 0x. In its simplest form, the package helps in the usage of the [0x forwarder contract](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md), which allows users to execute [Wrapped Ether](https://weth.io/) based 0x orders without having to set allowances, wrap Ether or own ZRX, meaning they can buy tokens with Ether alone. Given some liquidity (0x signed orders), it helps estimate the Ether cost of buying a certain asset (giving a range) and then buying that asset.', + link: { + title: '@0xproject/asset-buyer', + to: WebsitePaths.AssetBuyer, + }, + }, + ], + [Categories.Ethereum]: [ + { + description: + "This package allows you to generate TypeScript contract wrappers from ABI files. It's heavily inspired by Geth abigen but takes a different approach. You can write your custom handlebars templates which will allow you to seamlessly integrate the generated code into your existing codebase with existing conventions.", + link: { + title: 'abi-gen', + to: 'https://github.com/0xProject/0x-monorepo/tree/development/packages/abi-gen', + shouldOpenInNewTab: true, + }, + }, + { + description: + 'A collection of Typescript types that are useful when working on an Ethereum-based project (e.g RawLog, Transaction, TxData, SolidityTypes, etc...).', + link: { + title: 'ethereum-types', + to: WebsitePaths.EthereumTypes, + }, + }, + { + description: + 'A wrapper around [solc-js](https://github.com/ethereum/solc-js) that adds smart re-compilation, ability to compile an entire project, Solidity version specific compilation, standard input description support and much more.', + link: { + title: '@0xproject/sol-compiler', + to: WebsitePaths.SolCompiler, + }, + }, + { + description: + 'A Solidity code coverage tool. Sol-cov uses transaction traces to figure out which lines of your code has been covered by your tests.', + link: { + title: '@0xproject/sol-cov', + to: WebsitePaths.SolCov, + }, + }, + { + description: + 'A collection of subproviders to use with [web3-provider-engine](https://www.npmjs.com/package/web3-provider-engine) (e.g subproviders for interfacing with Ledger hardware wallet, Mnemonic wallet, private key wallet, etc...)', + link: { + title: '@0xproject/subproviders', + to: WebsitePaths.Subproviders, + }, + }, + { + description: + 'A raw Ethereum JSON RPC client to simplify interfacing with Ethereum nodes. Also includes some convenience functions for awaiting transactions to be mined, converting between token units, etc...', + link: { + title: '@0xproject/web3-wrapper', + to: WebsitePaths.Web3Wrapper, + }, + }, + ], + [Categories.CommunityMaintained]: [ + { + description: + 'Node.js worker originally built for 0x Tracker which extracts 0x fill events from the Ethereum blockchain and persists them to MongoDB. Support for both V1 and V2 of the 0x protocol is included with events tagged against the protocol version they belong to.', + link: { + title: '0x Event Extractor', + to: 'https://github.com/0xTracker/0x-event-extractor', + shouldOpenInNewTab: true, + }, + }, + { + description: + 'Node.js worker built for 0x Tracker which performs various ETL tasks related to the 0x protocol trading data and other information used on 0x Tracker.', + link: { + title: '0x Tracker Worker', + to: 'https://github.com/0xTracker/0x-tracker-worker', + shouldOpenInNewTab: true, + }, + }, + { + description: + "ERCdex's Javascript SDK for trading on their relayer, as well as other Aquaduct partner relayers", + link: { + title: 'Aquaduct', + to: 'https://www.npmjs.com/package/aqueduct', + shouldOpenInNewTab: true, + }, + }, + { + description: + 'SDKs for automation using Aqueduct & ERC dEX. Aqueduct Server is a lightweight, portable and secure server that runs locally on any workstation. The server exposes a small number of foundational endpoints that enable working with the decentralized Aqueduct liquidity pool from any context or programming language.', + link: { + title: 'Aquaduct Server SDK', + to: 'https://github.com/ERCdEX/aqueduct-server-sdk', + shouldOpenInNewTab: true, + }, + }, + { + description: 'A node.js SDK for trading on the DDEX relayer', + link: { + to: 'https://www.npmjs.com/package/ddex-api', + title: 'DDEX Node.js SDK', + shouldOpenInNewTab: true, + }, + }, + { + description: "The ERC dEX Trade Widget let's any website provide token liquidity to it's users", + link: { + to: 'https://github.com/ERCdEX/widget', + title: 'ERCdex Widget', + shouldOpenInNewTab: true, + }, + }, + { + description: "ERCdex's Java SDK for trading on their relayer, as well as other Aquaduct partner relayers", + link: { + to: 'https://github.com/ERCdEX/java', + title: 'ERCdex Java SDK', + shouldOpenInNewTab: true, + }, + }, + { + description: "ERCdex's Python SDK for trading on their relayer, as well as other Aquaduct partner relayers", + link: { + to: 'https://github.com/ERCdEX/python', + title: 'ERCdex Python SDK', + shouldOpenInNewTab: true, + }, + }, + { + description: + 'A set of command-line tools for creating command-line scripts for interacting with the Ethereum blockchain in general, and 0x in particular', + link: { + title: 'Massive', + to: 'https://github.com/NoteGio/massive', + shouldOpenInNewTab: true, + }, + }, + { + description: 'An open-source API-only Relayer written in Go', + link: { + to: 'https://github.com/NoteGio/openrelay', + title: 'OpenRelay', + shouldOpenInNewTab: true, + }, + }, + { + description: + 'A JavaScript Library for Interacting with OpenRelay.xyz and other 0x Standard Relayer API Implementations', + link: { + title: 'OpenRelay.js', + to: 'https://github.com/NoteGio/openrelay.js', + shouldOpenInNewTab: true, + }, + }, + { + description: + 'The Radar Relay SDK is a software development kit that simplifies the interactions with Radar Relay’s APIs', + link: { + title: 'Radar SDK', + to: 'https://github.com/RadarRelay/sdk', + shouldOpenInNewTab: true, + }, + }, + { + description: + 'The Ocean provides a simple REST API, WebSockets API, and JavaScript library to help you integrate decentralized trading into your existing trading strategy.', + link: { + title: 'The Ocean Javascript SDK', + to: 'https://github.com/TheOceanTrade/theoceanx-javascript', + shouldOpenInNewTab: true, + }, + }, + { + description: "Tokenlon SDK provides APIs for developers to trade of imToken's relayer", + link: { + to: 'https://www.npmjs.com/package/tokenlon-sdk', + title: 'Tokenlon Javascript SDK', + shouldOpenInNewTab: true, + }, + }, + { + description: 'A small library that implements the 0x order assetData encoding/decoding in Java', + link: { + to: 'https://github.com/wildnothing/asset-data-decoder', + title: 'AssetData decoder library in Java', + shouldOpenInNewTab: true, + }, + }, + ], +}; + +export interface DocsHomeProps { + location: Location; + translate: Translate; + screenWidth: ScreenWidths; + tutorials: TutorialInfo[]; + categoryToPackages: ObjectMap<Package[]>; + dispatcher: Dispatcher; +} + +export interface DocsHomeState {} + +export class DocsHome extends React.Component<DocsHomeProps, DocsHomeState> { + public render(): React.ReactNode { + const sectionNameToLinks: ObjectMap<ALink[]> = { + 'Starter guides': _.map(TUTORIALS, tutorialInfo => { + return { + ...tutorialInfo.link, + title: this.props.translate.get(tutorialInfo.link.title as Key, Deco.Cap), + }; + }), + [Categories.ZeroExProtocol]: _.map(CATEGORY_TO_PACKAGES[Categories.ZeroExProtocol], pkg => pkg.link), + [Categories.Ethereum]: _.map(CATEGORY_TO_PACKAGES[Categories.Ethereum], pkg => pkg.link), + [Categories.CommunityMaintained]: _.map( + CATEGORY_TO_PACKAGES[Categories.CommunityMaintained], + pkg => pkg.link, + ), + }; + const mainContent = ( + <OverviewContent + translate={this.props.translate} + tutorials={TUTORIALS} + categoryToPackages={CATEGORY_TO_PACKAGES} + /> + ); + const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; + const sidebar = ( + <NestedSidebarMenu + sidebarHeader={isSmallScreen ? this._renderSidebarHeader() : undefined} + sectionNameToLinks={sectionNameToLinks} + shouldReformatMenuItemNames={false} + /> + ); + return ( + <DevelopersPage + mainContent={mainContent} + sidebar={sidebar} + location={this.props.location} + screenWidth={this.props.screenWidth} + translate={this.props.translate} + dispatcher={this.props.dispatcher} + /> + ); + } + private _renderSidebarHeader(): React.ReactNode { + const menuItems = _.map(constants.DEVELOPER_TOPBAR_LINKS, menuItemInfo => { + return ( + <Link + key={`menu-item-${menuItemInfo.title}`} + to={menuItemInfo.to} + shouldOpenInNewTab={menuItemInfo.shouldOpenInNewTab} + > + <Button + borderRadius="4px" + padding="0.4em 6px" + width="100%" + fontColor={colors.grey800} + fontSize="14px" + textAlign="left" + > + {this.props.translate.get(menuItemInfo.title as Key, Deco.Cap)} + </Button> + </Link> + ); + }); + return menuItems; + } +} diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index 388e83d51..f7a6fe0ce 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -1,8 +1,7 @@ -import { colors } from '@0xproject/react-shared'; +import { colors, Link } from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; import DocumentTitle = require('react-document-title'); -import { Link } from 'react-router-dom'; import { Footer } from 'ts/components/footer'; import { SubscribeForm } from 'ts/components/forms/subscribe_form'; import { TopBar } from 'ts/components/top_bar/top_bar'; @@ -213,14 +212,14 @@ export class Landing extends React.Component<LandingProps, LandingState> { className={`pt3 flex clearfix sm-mx-auto ${isSmallScreen ? 'justify-center' : ''}`} > <Container paddingRight="20px"> - <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none"> + <Link to={WebsitePaths.Docs}> <CallToAction type="light"> {this.props.translate.get(Key.BuildCallToAction, Deco.Cap)} </CallToAction> </Link> </Container> <div> - <Link to={WebsitePaths.Portal} className="text-decoration-none"> + <Link to={WebsitePaths.Portal}> <CallToAction> {this.props.translate.get(Key.TradeCallToAction, Deco.Cap)} </CallToAction> @@ -318,11 +317,7 @@ export class Landing extends React.Component<LandingProps, LandingState> { }} > {this.props.translate.get(Key.FullListPrompt)}{' '} - <Link - to={WebsitePaths.Portal} - className="text-decoration-none underline" - style={{ color: colors.landingLinkGrey }} - > + <Link to={WebsitePaths.Portal} textDecoration="underline" fontColor={colors.landingLinkGrey}> {this.props.translate.get(Key.FullListLink)} </Link> </div> @@ -603,7 +598,7 @@ export class Landing extends React.Component<LandingProps, LandingState> { {this.props.translate.get(Key.FinalCallToAction, Deco.Cap)} </div> <div className="sm-center sm-pt2 lg-table-cell md-table-cell"> - <Link to={WebsitePaths.ZeroExJs} className="text-decoration-none"> + <Link to={WebsitePaths.Docs}> <CallToAction fontSize="15px"> {this.props.translate.get(Key.BuildCallToAction, Deco.Cap)} </CallToAction> diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx index 55f532b11..76d52c9bd 100644 --- a/packages/website/ts/pages/wiki/wiki.tsx +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -1,27 +1,28 @@ import { + ALink, colors, constants as sharedConstants, HeaderSizes, + Link, MarkdownSection, NestedSidebarMenu, - Styles, utils as sharedUtils, } from '@0xproject/react-shared'; +import { ObjectMap } from '@0xproject/types'; import * as _ from 'lodash'; import CircularProgress from 'material-ui/CircularProgress'; -import RaisedButton from 'material-ui/RaisedButton'; import * as React from 'react'; -import DocumentTitle = require('react-document-title'); -import { SidebarHeader } from 'ts/components/sidebar_header'; -import { TopBar } from 'ts/components/top_bar/top_bar'; +import { SidebarHeader } from 'ts/components/documentation/sidebar_header'; +import { Button } from 'ts/components/ui/button'; +import { Container } from 'ts/components/ui/container'; +import { DevelopersPage } from 'ts/pages/documentation/developers_page'; import { Dispatcher } from 'ts/redux/dispatcher'; -import { Article, ArticlesBySection } from 'ts/types'; +import { Article, ArticlesBySection, Deco, Key, ScreenWidths } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; import { utils } from 'ts/utils/utils'; -const TOP_BAR_HEIGHT = 60; const WIKI_NOT_READY_BACKOUT_TIMEOUT_MS = 5000; export interface WikiProps { @@ -29,6 +30,7 @@ export interface WikiProps { location: Location; dispatcher: Dispatcher; translate: Translate; + screenWidth: ScreenWidths; } interface WikiState { @@ -36,24 +38,6 @@ interface WikiState { isHoveringSidebar: boolean; } -const styles: Styles = { - mainContainers: { - position: 'absolute', - top: 1, - left: 0, - bottom: 0, - right: 0, - overflow: 'hidden', - height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, - WebkitOverflowScrolling: 'touch', - }, - menuContainer: { - borderColor: colors.grey300, - maxWidth: 330, - backgroundColor: colors.gray40, - }, -}; - export class Wiki extends React.Component<WikiProps, WikiState> { private _wikiBackoffTimeoutId: number; private _isUnmounted: boolean; @@ -78,89 +62,73 @@ export class Wiki extends React.Component<WikiProps, WikiState> { window.removeEventListener('hashchange', this._onHashChanged.bind(this), false); } public render(): React.ReactNode { - const menuSubsectionsBySection = _.isUndefined(this.state.articlesBySection) + const sectionNameToLinks = _.isUndefined(this.state.articlesBySection) ? {} - : this._getMenuSubsectionsBySection(this.state.articlesBySection); - const mainContainersStyle: React.CSSProperties = { - ...styles.mainContainers, - overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden', - }; - const sidebarHeader = <SidebarHeader title="Wiki" iconUrl="/images/doc_icons/wiki.png" />; - return ( - <div> - <DocumentTitle title="0x Protocol Wiki" /> - <TopBar - blockchainIsLoaded={false} - location={this.props.location} - menuSubsectionsBySection={menuSubsectionsBySection} - translate={this.props.translate} - sidebarHeader={sidebarHeader} - /> - {_.isUndefined(this.state.articlesBySection) ? ( - <div className="col col-12" style={mainContainersStyle}> - <div - className="relative sm-px2 sm-pt2 sm-m1" - style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }} - > - <div className="center pb2"> - <CircularProgress size={40} thickness={5} /> - </div> - <div className="center pt2" style={{ paddingBottom: 11 }}> - Loading wiki... - </div> - </div> - </div> - ) : ( - <div style={{ width: '100%', height: '100%', backgroundColor: colors.gray40 }}> - <div - className="mx-auto max-width-4 flex" - style={{ color: colors.grey800, height: `calc(100vh - ${TOP_BAR_HEIGHT}px)` }} - > - <div - className="relative lg-pl0 md-pl1 sm-hide xs-hide" - style={{ height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, width: '36%' }} - > - <div - className="absolute" - style={{ - ...styles.menuContainer, - ...mainContainersStyle, - height: 'calc(100vh - 76px)', - }} - onMouseEnter={this._onSidebarHover.bind(this)} - onMouseLeave={this._onSidebarHoverOff.bind(this)} - > - <NestedSidebarMenu - topLevelMenu={menuSubsectionsBySection} - menuSubsectionsBySection={menuSubsectionsBySection} - sidebarHeader={sidebarHeader} - /> - </div> - </div> - <div - className="relative" - style={{ - width: '100%', - height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, - backgroundColor: 'white', - }} - > - <div - id={sharedConstants.SCROLL_CONTAINER_ID} - style={{ ...mainContainersStyle, overflow: 'auto' }} - className="absolute" - > - <div id={sharedConstants.SCROLL_TOP_ID} /> - <div id="wiki" style={{ paddingRight: 2 }}> - {this._renderWikiArticles()} - </div> - </div> - </div> - </div> - </div> - )} + : this._getSectionNameToLinks(this.state.articlesBySection); + + const mainContent = _.isUndefined(this.state.articlesBySection) ? ( + <div className="flex justify-center">{this._renderLoading()}</div> + ) : ( + <div id="wiki" style={{ paddingRight: 2 }}> + {this._renderWikiArticles()} </div> ); + const sidebar = _.isUndefined(this.state.articlesBySection) ? ( + <div /> + ) : ( + <NestedSidebarMenu sidebarHeader={this._renderSidebarHeader()} sectionNameToLinks={sectionNameToLinks} /> + ); + return ( + <DevelopersPage + sidebar={sidebar} + mainContent={mainContent} + location={this.props.location} + screenWidth={this.props.screenWidth} + translate={this.props.translate} + dispatcher={this.props.dispatcher} + /> + ); + } + private _renderSidebarHeader(): React.ReactNode { + const menuItems = _.map(constants.DEVELOPER_TOPBAR_LINKS, menuItemInfo => { + return ( + <Link + key={`menu-item-${menuItemInfo.title}`} + to={menuItemInfo.to} + shouldOpenInNewTab={menuItemInfo.shouldOpenInNewTab} + > + <Button + borderRadius="4px" + padding="0.4em 6px" + width="100%" + fontColor={colors.grey800} + fontSize="14px" + textAlign="left" + > + {this.props.translate.get(menuItemInfo.title as Key, Deco.Cap)} + </Button> + </Link> + ); + }); + const wikiTitle = this.props.translate.get(Key.Wiki, Deco.Cap); + return ( + <Container> + <SidebarHeader screenWidth={this.props.screenWidth} title={wikiTitle} /> + {menuItems} + </Container> + ); + } + private _renderLoading(): React.ReactNode { + return ( + <Container className="pt4"> + <Container className="center pb2"> + <CircularProgress size={40} thickness={5} /> + </Container> + <Container className="center pt2" paddingBottom="11px"> + Loading wiki... + </Container> + </Container> + ); } private _renderWikiArticles(): React.ReactNode { const sectionNames = _.keys(this.state.articlesBySection); @@ -179,22 +147,10 @@ export class Wiki extends React.Component<WikiProps, WikiState> { headerSize={HeaderSizes.H2} githubLink={githubLink} /> - <div className="clearfix mb3 mt2 p3 mx-auto lg-flex md-flex sm-pb4" style={{ maxWidth: 390 }}> - <div className="sm-col sm-col-12 sm-center" style={{ opacity: 0.4, lineHeight: 2.5 }}> - See a way to improve this article? - </div> - <div className="sm-col sm-col-12 lg-col-7 md-col-7 sm-center sm-pt2"> - <RaisedButton href={githubLink} target="_blank" label="Edit on Github" /> - </div> - </div> </div> ); }); - return ( - <div key={`section-${sectionName}`} className="py2 md-px1 sm-px0"> - {renderedArticles} - </div> - ); + return <div key={`section-${sectionName}`}>{renderedArticles}</div>; } private async _fetchArticlesBySectionAsync(): Promise<void> { try { @@ -223,25 +179,20 @@ export class Wiki extends React.Component<WikiProps, WikiState> { } } } - private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection): { [section: string]: string[] } { + private _getSectionNameToLinks(articlesBySection: ArticlesBySection): ObjectMap<ALink[]> { const sectionNames = _.keys(articlesBySection); - const menuSubsectionsBySection: { [section: string]: string[] } = {}; + const sectionNameToLinks: ObjectMap<ALink[]> = {}; for (const sectionName of sectionNames) { const articles = articlesBySection[sectionName]; - const articleNames = _.map(articles, article => article.title); - menuSubsectionsBySection[sectionName] = articleNames; + const articleLinks = _.map(articles, article => { + return { + to: sharedUtils.getIdFromName(article.title), + title: article.title, + }; + }); + sectionNameToLinks[sectionName] = articleLinks; } - return menuSubsectionsBySection; - } - private _onSidebarHover(_event: React.FormEvent<HTMLInputElement>): void { - this.setState({ - isHoveringSidebar: true, - }); - } - private _onSidebarHoverOff(): void { - this.setState({ - isHoveringSidebar: false, - }); + return sectionNameToLinks; } private _onHashChanged(_event: any): void { const hash = window.location.hash.slice(1); diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 7e140c951..84be4d3b0 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -1,3 +1,4 @@ +import { ALink } from '@0xproject/react-shared'; import { ObjectMap, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Provider } from 'ethereum-types'; @@ -461,6 +462,23 @@ export enum Key { RocketChat = 'ROCKETCHAT', TradeCallToAction = 'TRADE_CALL_TO_ACTION', OurMissionAndValues = 'OUR_MISSION_AND_VALUES', + BuildARelayer = 'BUILD_A_RELAYER', + BuildARelayerDescription = 'BUILD_A_RELAYER_DESCRIPTION', + DevelopOnEthereum = 'DEVELOP_ON_ETHEREUM', + DevelopOnEthereumDescription = 'DEVELOP_ON_ETHEREUM_DESCRIPTION', + OrderBasics = 'ORDER_BASICS', + OrderBasicsDescription = 'ORDER_BASICS_DESCRIPTION', + UseSharedLiquidity = 'USE_SHARED_LIQUIDITY', + UseSharedLiquidityDescription = 'USE_SHARED_LIQUIDITY_DESCRIPTION', + ViewAllDocumentation = 'VIEW_ALL_DOCUMENTATION', + Sandbox = 'SANDBOX', + Github = 'GITHUB', + LiveChat = 'LIVE_CHAT', + LibrariesAndTools = 'LIBRARIES_AND_TOOLS', + LibrariesAndToolsDescription = 'LIBRARIES_AND_TOOLS_DESCRIPTION', + More = 'MORE', + StartBuildOn0x = 'START_BUILDING_ON_0X', + StartBuildOn0xDescription = 'START_BUILDING_ON_0X_DESCRIPTION', } export enum SmartContractDocSections { @@ -603,4 +621,21 @@ export interface InjectedWeb3 { getNetwork(cd: (err: Error, networkId: string) => void): void; }; } + +export interface TutorialInfo { + iconUrl: string; + description: string; + link: ALink; +} + +export enum Categories { + ZeroExProtocol = '0x Protocol', + Ethereum = 'Ethereum', + CommunityMaintained = 'Community Maintained', +} + +export interface Package { + description: string; + link: ALink; +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index 18a4d8100..e76d40bd3 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -1,4 +1,9 @@ +import { ALink } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; +import { Key, WebsitePaths } from 'ts/types'; + +const URL_FORUM = 'https://forum.0xproject.com'; +const URL_ZEROEX_CHAT = 'https://chat.0xproject.com'; export const constants = { DECIMAL_PLACES_ETH: 18, @@ -74,6 +79,7 @@ export const constants = { URL_TESTNET_FAUCET: 'https://faucet.0xproject.com', URL_GITHUB_ORG: 'https://github.com/0xProject', URL_GITHUB_WIKI: 'https://github.com/0xProject/wiki', + URL_FORUM, URL_METAMASK_CHROME_STORE: 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn', URL_METAMASK_FIREFOX_STORE: 'https://addons.mozilla.org/en-US/firefox/addon/ether-metamask/', URL_COINBASE_WALLET_IOS_APP_STORE: 'https://itunes.apple.com/us/app/coinbase-wallet/id1278383455?mt=8', @@ -84,10 +90,11 @@ export const constants = { URL_PARITY_CHROME_STORE: 'https://chrome.google.com/webstore/detail/parity-ethereum-integrati/himekenlppkgeaoeddcliojfddemadig', URL_REDDIT: 'https://reddit.com/r/0xproject', + URL_SANDBOX: 'https://codesandbox.io/s/1qmjyp7p5j', URL_STANDARD_RELAYER_API_GITHUB: 'https://github.com/0xProject/standard-relayer-api/blob/master/README.md', URL_TWITTER: 'https://twitter.com/0xproject', URL_WETH_IO: 'https://weth.io/', - URL_ZEROEX_CHAT: 'https://chat.0xproject.com', + URL_ZEROEX_CHAT, URL_WEB3_DOCS: 'https://github.com/ethereum/wiki/wiki/JavaScript-API', URL_WEB3_DECODED_LOG_ENTRY_EVENT: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L123', @@ -95,4 +102,24 @@ export const constants = { URL_WEB3_PROVIDER_DOCS: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L150', URL_BIGNUMBERJS_GITHUB: 'http://mikemcl.github.io/bignumber.js', URL_MISSION_AND_VALUES_BLOG_POST: 'https://blog.0xproject.com/the-0x-mission-and-values-181a58706f9f', + DEVELOPER_TOPBAR_LINKS: [ + { + title: Key.Home, + to: WebsitePaths.Home, + }, + { + title: Key.Wiki, + to: WebsitePaths.Wiki, + }, + { + title: Key.Forum, + to: URL_FORUM, + shouldOpenInNewTab: true, + }, + { + title: Key.LiveChat, + to: URL_ZEROEX_CHAT, + shouldOpenInNewTab: true, + }, + ] as ALink[], }; diff --git a/packages/website/ts/utils/translate.ts b/packages/website/ts/utils/translate.ts index 1ee1a59c5..5595e6e0f 100644 --- a/packages/website/ts/utils/translate.ts +++ b/packages/website/ts/utils/translate.ts @@ -80,7 +80,12 @@ export class Translate { case Deco.CapWords: const words = text.split(' '); - const capitalizedWords = _.map(words, w => this._capitalize(w)); + const capitalizedWords = _.map(words, (w: string, i: number) => { + if (w.length === 1) { + return w; + } + return this._capitalize(w); + }); text = capitalizedWords.join(' '); break; |