diff options
-rw-r--r-- | packages/website/package.json | 1 | ||||
-rw-r--r-- | packages/website/ts/@next/components/blockIconLink.tsx | 36 | ||||
-rw-r--r-- | packages/website/ts/@next/components/button.tsx | 4 | ||||
-rw-r--r-- | packages/website/ts/@next/components/modals/input.tsx | 65 | ||||
-rw-r--r-- | packages/website/ts/@next/components/modals/modal_contact.tsx | 198 | ||||
-rw-r--r-- | packages/website/ts/@next/components/sections/landing/cta.tsx | 8 | ||||
-rw-r--r-- | packages/website/ts/@next/pages/landing.tsx | 38 | ||||
-rw-r--r-- | yarn.lock | 76 |
8 files changed, 402 insertions, 24 deletions
diff --git a/packages/website/package.json b/packages/website/package.json index 629cbffc2..fc98a1a1b 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -32,6 +32,7 @@ "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", "@0x/web3-wrapper": "^3.1.6", + "@reach/dialog": "^0.1.2", "@types/react-lazyload": "^2.3.1", "@types/react-loadable": "^5.4.2", "@types/react-syntax-highlighter": "^0.0.8", diff --git a/packages/website/ts/@next/components/blockIconLink.tsx b/packages/website/ts/@next/components/blockIconLink.tsx index cc3e88280..42b260731 100644 --- a/packages/website/ts/@next/components/blockIconLink.tsx +++ b/packages/website/ts/@next/components/blockIconLink.tsx @@ -1,14 +1,15 @@ import * as React from 'react'; import styled from 'styled-components'; -import {Link} from 'ts/@next/components/button'; +import {Button, Link} from 'ts/@next/components/button'; import {Icon} from 'ts/@next/components/icon'; interface Props { icon: string; title: string; linkLabel: string; - linkUrl: string; + linkUrl?: string; + onClick?: () => void; } export const BlockIconLink = (props: Props) => ( @@ -24,14 +25,29 @@ export const BlockIconLink = (props: Props) => ( {props.title} </Title> - <Link - isWithArrow={true} - isTransparent={true} - isNoBorder={true} - href={props.linkUrl} - > - {props.linkLabel} - </Link> + {props.linkUrl && + <Link + isWithArrow={true} + isTransparent={true} + isNoBorder={true} + href={props.linkUrl} + onClick={props.onClick} + > + {props.linkLabel} + </Link> + } + + {props.onClick && + <Button + isWithArrow={true} + isTransparent={true} + isNoBorder={true} + href={props.linkUrl} + onClick={props.onClick} + > + {props.linkLabel} + </Button> + } </div> </Wrap> ); diff --git a/packages/website/ts/@next/components/button.tsx b/packages/website/ts/@next/components/button.tsx index 3f14b59bd..5d44f1ce5 100644 --- a/packages/website/ts/@next/components/button.tsx +++ b/packages/website/ts/@next/components/button.tsx @@ -16,6 +16,8 @@ interface ButtonInterface { isWithArrow?: boolean; isAccentColor?: boolean; hasIcon?: boolean | string; + isInline?: boolean; + type?: string; href?: string; onClick?: () => any; theme?: { @@ -57,7 +59,7 @@ export const Button = styled.button<ButtonInterface>` } `; -export const Link = (props: ButtonInterface) => { +export const Link: React.ReactNode = (props: ButtonInterface) => { const { children, href, diff --git a/packages/website/ts/@next/components/modals/input.tsx b/packages/website/ts/@next/components/modals/input.tsx new file mode 100644 index 000000000..eee2d4102 --- /dev/null +++ b/packages/website/ts/@next/components/modals/input.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import styled from 'styled-components'; + +import { colors } from 'ts/style/colors'; + +export enum InputWidth { + Half, + Full, +} + +interface InputProps { + name: string; + width: InputWidth; + label: string; + type?: string; +} + +interface LabelProps { + string: boolean; +} + +export const Input = React.forwardRef((props: InputProps, ref) => { + const { name, label, type } = props; + const id = `input-${name}`; + + return ( + <InputWrapper {...props}> + <Label htmlFor={id}>{label}</Label> + <StyledInput ref={ref} id={id} placeholder={label} {...props} /> + </InputWrapper> + ); +}); + +Input.defaultProps = { + width: InputWidth.Full, +}; + +const StyledInput = styled.input` + appearance: none; + background-color: #fff; + border: 1px solid #D5D5D5; + color: #000; + font-size: 1.294117647rem; + padding: 16px 15px; + outline: none; + width: 100%; + + &::placeholder { + color: #9D9D9D; + } +`; + +const InputWrapper = styled.div<InputProps>` + position: relative; + flex-grow: ${props => props.width === InputWidth.Full && 1}; + width: ${props => props.width === InputWidth.Half && `calc(50% - 15px)`}; +`; + +const Label = styled.label` + color: #000; + font-size: 1.111111111rem; + line-height: 1.4em; + margin-bottom: 10px; + display: inline-block; +`; diff --git a/packages/website/ts/@next/components/modals/modal_contact.tsx b/packages/website/ts/@next/components/modals/modal_contact.tsx new file mode 100644 index 000000000..cd6335103 --- /dev/null +++ b/packages/website/ts/@next/components/modals/modal_contact.tsx @@ -0,0 +1,198 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import {Link as RouterLink} from 'react-router-dom'; +import styled, {withTheme} from 'styled-components'; + +import { colors } from 'ts/style/colors'; + +import { + Dialog, + DialogOverlay, + DialogContent + } from "@reach/dialog"; +import "@reach/dialog/styles.css"; + +import {Button} from 'ts/@next/components/button'; +import {Column, Wrap, WrapGrid} from 'ts/@next/components/layout'; +import { Icon } from 'ts/@next/components/icon'; +import {Input, InputWidth} from 'ts/@next/components/modals/input'; +import {Heading, Paragraph} from 'ts/@next/components/text'; +import {GlobalStyle} from 'ts/@next/constants/globalStyle'; + +interface Props { + theme?: GlobalStyle; + isOpen?: boolean; + onDismiss?: () => void; +} + +interface FormProps { + isSuccessful?: boolean; + isSubmitting?: boolean; +} + +export class ModalContact extends React.Component<Props> { + public state = { + isSubmitting: false, + isSuccessful: false, + }; + public constructor(props: Props) { + super(props); + } + public render(): React.ReactNode { + const {isOpen, onDismiss} = this.props; + const {isSuccessful} = this.state; + + return ( + <> + <DialogOverlay + style={{ background: 'rgba(0, 0, 0, 0.75)' }} + isOpen={isOpen} + > + <StyledDialogContent> + <Form onSubmit={this._onSubmit.bind(this)} isSuccessful={isSuccessful}> + <Heading color={colors.textDarkPrimary} size={34} asElement="h2">Contact the 0x Core Team</Heading> + <Paragraph isMuted={true} color={colors.textDarkPrimary}>If you're considering building on 0x, we're happy to answer your questions. Fill out the form so we can connect you with the right person to help you get started.</Paragraph> + <InputRow> + <Input + name="name" + label="Your name" + type="text" + width={InputWidth.Half} + /> + <Input + name="email" + label="Your email" + type="email" + width={InputWidth.Half} + /> + </InputRow> + <InputRow> + <Input + name="companyOrProject" + label="Name of your project / company" + type="text" + /> + </InputRow> + <InputRow> + <Input + name="link" + label="Do you have any documentation or a website?" + type="text" + /> + </InputRow> + <InputRow> + <Input + name="comments" + label="Anything else?" + type="text" + /> + </InputRow> + <InputRow> + <Button + color="#5C5C5C" + isNoBorder={true} + isTransparent={true} + type="button" + onClick={this.props.onDismiss} + > + Back + </Button> + <Button>Submit</Button> + </InputRow> + </Form> + <Confirmation isSuccessful={isSuccessful}> + <Icon name="checkmark" size="large" /> + <Heading color={colors.textDarkPrimary} size={34} asElement="h2">Thanks for contacting us.</Heading> + <Paragraph isMuted={true} color={colors.textDarkPrimary}>We'll get back to you soon. If you need quick support in the meantime, reach out to the 0x team on Discord.</Paragraph> + <Button onClick={this.props.onDismiss}>Done</Button> + </Confirmation> + </StyledDialogContent> + </DialogOverlay> + </> + ); + } + private async _onSubmit(e): void { + e.preventDefault(); + + // const email = this.emailInput.current.value; + const email = 'fred@sjelfull.no'; + + this.setState({ ...this.state, isSubmitting: true }); + + try { + const response = await fetch('/email', { + method: 'post', + headers: { + 'content-type': 'application/json; charset=utf-8', + }, + body: JSON.stringify({ email }), + }); + const json = await response.json(); + + console.log(response.json()); + } catch (e) { + console.log(e); + } + + this.setState({ ...this.state, isSuccessful: true }); + } + private async _onDone(e): void { + e.preventDefault(); + + this.props.onDismiss(); + } +}; + +const StyledWrap = styled(Wrap)` + padding-top: 20px; + margin-top: 30px; + position: relative; + + &:before { + content: ''; + width: 100%; + height: 1px; + background-color: ${props => props.theme.dropdownColor}; + opacity: 0.15; + position: absolute; + top: 0; + left:0; + } +`; + +const InputRow = styled.div` + display: flex; + justify-content: space-between; + margin-bottom: 30px; + width: 100%; + flex: 0 0 auto; +`; + +const StyledDialogContent = styled(DialogContent)` + position: relative; + max-width: 800px; + background-color: #F6F6F6 !important; + padding: 60px 60px !important; +`; + +const Form = styled.form<FormProps>` + position: relative; + transition: opacity 0.30s ease-in-out, visibility 0.30s ease-in-out; + + opacity: ${props => props.isSuccessful && `0`}; + visibility: ${props => props.isSuccessful && `hidden`}; +`; + +const Confirmation = styled.div<FormProps>` + position: absolute; + top: 50%; + text-align: center; + width: 100%; + left: 0; + transition: opacity 0.30s ease-in-out, visibility 0.30s ease-in-out; + transition-delay: 0.40s; + padding: 60px 60px; + transform: translateY(-50%); + opacity: ${props => props.isSuccessful ? `1` : `0`}; + visibility: ${props => props.isSuccessful ? 'visible' : `hidden`}; +`; diff --git a/packages/website/ts/@next/components/sections/landing/cta.tsx b/packages/website/ts/@next/components/sections/landing/cta.tsx index 4c06982e4..b90b4070e 100644 --- a/packages/website/ts/@next/components/sections/landing/cta.tsx +++ b/packages/website/ts/@next/components/sections/landing/cta.tsx @@ -8,7 +8,11 @@ import {Column, Section} from 'ts/@next/components/newLayout'; import {BlockIconLink} from 'ts/@next/components/blockIconLink'; -export const SectionLandingCta = () => ( +interface Props { + onContactClick?: () => void; +} + +export const SectionLandingCta = (props: Props) => ( <Section isPadded={false} isFullWidth={true} @@ -25,7 +29,7 @@ export const SectionLandingCta = () => ( icon="coin" title="Wat help from the 0x team?" linkLabel="Get in Touch" - linkUrl="#" + onClick={props.onContactClick} /> </Section> ); diff --git a/packages/website/ts/@next/pages/landing.tsx b/packages/website/ts/@next/pages/landing.tsx index fab5e62b6..5ead1b6b5 100644 --- a/packages/website/ts/@next/pages/landing.tsx +++ b/packages/website/ts/@next/pages/landing.tsx @@ -7,9 +7,7 @@ import {SectionLandingClients} from 'ts/@next/components/sections/landing/client import {SectionLandingCta} from 'ts/@next/components/sections/landing/cta'; import {SectionLandingHero} from 'ts/@next/components/sections/landing/hero'; -import {Button} from 'ts/@next/components/button'; -import {Hero} from 'ts/@next/components/hero'; -import {LandingAnimation} from 'ts/@next/components/heroImage'; +import { ModalContact } from 'ts/@next/components/modals/modal_contact'; import LogoOutlined from 'ts/@next/icons/illustrations/logo-outlined.svg'; @@ -21,11 +19,29 @@ interface Props { }; } -export const NextLanding: React.StatelessComponent<{}> = (props: Props) => ( - <SiteWrap theme="dark"> - <SectionLandingHero /> - <SectionLandingAbout /> - <SectionLandingClients /> - <SectionLandingCta /> - </SiteWrap> -); +export class NextLanding extends React.Component<Props> { + public state = { + isContactModalOpen: false, + }; + public render(): React.ReactNode { + return ( + <SiteWrap theme="dark"> + <SectionLandingHero /> + <SectionLandingAbout /> + <SectionLandingClients /> + <SectionLandingCta onContactClick={this._onOpenContactModal.bind(this)} /> + <ModalContact isOpen={this.state.isContactModalOpen} onDismiss={this._onDismissContactModal.bind(this)} /> + </SiteWrap> + ); + } + + private _onOpenContactModal(e): void { + e.preventDefault(); + + this.setState({ isContactModalOpen: true }); + } + + private _onDismissContactModal(): void { + this.setState({ isContactModalOpen: false }); + } +} @@ -1203,6 +1203,33 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@reach/component-component@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@reach/component-component/-/component-component-0.1.1.tgz#62ea2ec290da32f5e3a9872fb51f9a3ae4370cc4" + integrity sha512-4HxyyOLo/nDg0LlgYvzgqpTGaxqKmrZB8/y5wWhtyvGzgKAb1DJzSnYRaFsgGGgkQAQmhwPXKZ0H/rOAkxXJ8Q== + +"@reach/dialog@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@reach/dialog/-/dialog-0.1.2.tgz#46a3639c01928e125110fd5ccb753281172e844d" + integrity sha512-aaBS2a4ZNKGpqsjCdZukYhjf1x75sZ1lQrYg9mJq21Nfz9kk0Yx6zn4LR7ZYxgqkGj5/CQOSciGuUPF+Z8k9nA== + dependencies: + "@reach/component-component" "^0.1.1" + "@reach/portal" "^0.1.1" + "@reach/utils" "^0.1.2" + focus-trap "^3.0.0" + +"@reach/portal@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@reach/portal/-/portal-0.1.1.tgz#94f3f9b999c5a0dfb819309912ec23e36807e70b" + integrity sha512-AWsOH2TLfetlUzw3wbpy6n8trnuFI7N/HB2jYjrBRP5IbG/PmgrVQiL/HFP/OsCbbOo0rK7AsKnG7qyOf0r4Tg== + dependencies: + "@reach/component-component" "^0.1.1" + +"@reach/utils@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.1.2.tgz#72f547b5c9b0401a56de303d9e508abf6d3fa56a" + integrity sha512-HpDR5BeCApr3HufQtpg0P5b9sl0S66zikZSMtC/2jZBvrDzjVaT9O8PbKTQqW0PdkMNrnBkX1UtlrrM1TN3/Og== + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -1236,6 +1263,11 @@ version "0.4.1" resolved "https://registry.yarnpkg.com/@types/accounting/-/accounting-0.4.1.tgz#865d9f5694fd7c438fba34eb4bc82eec6f34cdd5" +"@types/anymatch@*": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.0.tgz#d1d55958d1fccc5527d4aba29fc9c4b942f563ff" + integrity sha512-7WcbyctkE8GTzogDb0ulRAEw7v8oIS54ft9mQTU7PfM0hp5e+8kpa+HeQ7IQrFbKtJXBKcZ4bh+Em9dTw5L6AQ== + "@types/axios@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@types/axios/-/axios-0.14.0.tgz#ec2300fbe7d7dddd7eb9d3abf87999964cafce46" @@ -1597,6 +1629,14 @@ dependencies: "@types/react" "*" +"@types/react-loadable@^5.4.2": + version "5.4.2" + resolved "https://registry.yarnpkg.com/@types/react-loadable/-/react-loadable-5.4.2.tgz#087ead218ee0494c44e41fbdec7477b0ea4b7c86" + integrity sha512-F9hv1ErL1zRch8u6VNuxqXSvSr/WdlfpLhU1UcipHRWg1NXZ8pq2Dh7Uqkp+11ex9wocu/F26Edw4Hn318k6cA== + dependencies: + "@types/react" "*" + "@types/webpack" "*" + "@types/react-redux@^4.4.37": version "4.4.47" resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-4.4.47.tgz#12af1677116e08d413fe2620d0a85560c8a0536e" @@ -1740,10 +1780,22 @@ "@types/react" "*" csstype "^2.2.0" +"@types/tapable@*": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" + integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ== + "@types/tmp@^0.0.33": version "0.0.33" resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" +"@types/uglify-js@*": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082" + integrity sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ== + dependencies: + source-map "^0.6.1" + "@types/uuid@^3.4.2", "@types/uuid@^3.4.3": version "3.4.3" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.3.tgz#121ace265f5569ce40f4f6d0ff78a338c732a754" @@ -1760,6 +1812,17 @@ dependencies: "@types/ethereum-protocol" "*" +"@types/webpack@*": + version "4.4.21" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.4.21.tgz#1a80de6d3e465f35067dd2f4533bf6e04c2e7187" + integrity sha512-QJfA6GeLSlnx8yyrEQ7fNLYj1MYKzqHlo89skOwnKG4nblpwAyXe9Gcm/eTz/BpX0vBEtiehrSv9b/W9TMkhKg== + dependencies: + "@types/anymatch" "*" + "@types/node" "*" + "@types/tapable" "*" + "@types/uglify-js" "*" + source-map "^0.6.0" + "@types/websocket@^0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-0.0.39.tgz#aa971e24f9c1455fe2a57ee3e69c7d395016b12a" @@ -6995,6 +7058,14 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: inherits "^2.0.1" readable-stream "^2.0.4" +focus-trap@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-3.0.0.tgz#4d2ee044ae66bf7eb6ebc6c93bd7a1039481d7dc" + integrity sha512-jTFblf0tLWbleGjj2JZsAKbgtZTdL1uC48L8FcmSDl4c2vDoU4NycN1kgV5vJhuq1mxNFkw7uWZ1JAGlINWvyw== + dependencies: + tabbable "^3.1.0" + xtend "^4.0.1" + follow-redirects@^1.3.0: version "1.5.8" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.8.tgz#1dbfe13e45ad969f813e86c00e5296f525c885a1" @@ -15452,6 +15523,11 @@ symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" +tabbable@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-3.1.1.tgz#db7512f28a9a1ed16e4275bd190131be9d5ad8e9" + integrity sha512-583MHIOwictf7+zbxqO/L5fBqMN6Li4SJ1XTKQA9WzHRA7c2BB+D+Ny7Y6kGqU2u+rHK59+oRzrBvMU53aZz+A== + table@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" |