diff options
6 files changed, 194 insertions, 42 deletions
diff --git a/packages/website/ts/@next/components/animatedChatIcon.tsx b/packages/website/ts/@next/components/animatedChatIcon.tsx new file mode 100644 index 000000000..ea97daf48 --- /dev/null +++ b/packages/website/ts/@next/components/animatedChatIcon.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import styled, { keyframes } from 'styled-components'; + +export const AnimatedChatIcon = () => ( + <svg width="150" height="150" viewBox="0 0 150 150" fill="none" xmlns="http://www.w3.org/2000/svg"> + <circle cx="75" cy="75" r="73" stroke="#00AE99" stroke-width="3"/> + <path d="M76 37H137.5" stroke="#00AE99" stroke-width="3"/> + <path d="M37 73.5L37 12M113 137.5L113 75" stroke="#00AE99" stroke-width="3"/> + <path d="M13 113H71.5" stroke="#00AE99" stroke-width="3"/> + <path d="M49.087 47.5264L92.574 4.03932" stroke="#00AE99" stroke-width="3"/> + <path d="M47.3192 100.913L3.8321 57.4259M146.314 92.4277L102.12 48.2335" stroke="#00AE99" stroke-width="3"/> + <path d="M58.2793 145.814L101.766 102.327" stroke="#00AE99" stroke-width="3"/> + <Bubble> + <path vector-effect="non-scaling-stroke" d="M113 75C113 85.3064 108.897 94.6546 102.235 101.5C98.4048 105.436 71 132.5 71 132.5V112.792C51.8933 110.793 37 94.6359 37 75C37 54.0132 54.0132 37 75 37C95.9868 37 113 54.0132 113 75Z" stroke="#00AE99" strokeWidth="3"/> + <Dot delay={0} vector-effect="non-scaling-stroke" cx="75" cy="75" r="4" stroke="#00AE99" strokeWidth="3"/> + <Dot delay={5.6} vector-effect="non-scaling-stroke" cx="91" cy="75" r="4" stroke="#00AE99" strokeWidth="3"/> + <Dot delay={-5.8} vector-effect="non-scaling-stroke" cx="59" cy="75" r="4" stroke="#00AE99" strokeWidth="3"/> + </Bubble> + </svg> +); + +const scale = keyframes` + 0% { transform: scale(1.2) } + 20% { transform: scale(1) } + 80% { transform: scale(1) } + 100% { transform: scale(1.2) } +`; + +const fadeInOut = keyframes` + 0%, 50%, 54%, 100% { + transform: initial; + } + + 25% { + transform: translateY(-5px); + } +`; + +const Bubble = styled.g` + animation: ${scale} 5s infinite cubic-bezier(0.175, 0.885, 0.32, 1.275); + transform-origin: 50% 50%; + + path, + circle { + fill: ${props => props.theme.lightBgColor}; + } +`; + +const Dot = styled.circle<{ delay: number }>` + animation: ${fadeInOut} 5s ${props => `${props.delay}s`} infinite; +`; diff --git a/packages/website/ts/@next/components/animatedCompassIcon.tsx b/packages/website/ts/@next/components/animatedCompassIcon.tsx new file mode 100644 index 000000000..aa0cfd099 --- /dev/null +++ b/packages/website/ts/@next/components/animatedCompassIcon.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import styled, { keyframes } from 'styled-components'; + +export const AnimatedCompassIcon = () => ( + <svg width="150" height="150" viewBox="0 0 150 150" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g> + <circle cx="75" cy="75" r="73" stroke="#00AE99" stroke-width="3"/> + <circle cx="75" cy="75" r="58" stroke="#00AE99" stroke-width="3"/> + <Needle d="M62.9792 62.9792L36.6447 113.355L87.0208 87.0208M62.9792 62.9792L113.355 36.6447L87.0208 87.0208M62.9792 62.9792L87.0208 87.0208" stroke="#00AE99" strokeWidth="3"/> + + <Dial> + <path d="M75 2V17M75 133V148" stroke="#00AE99" stroke-width="3"/> + <path d="M2 75L17 75M133 75L148 75" stroke="#00AE99" stroke-width="3"/> + <path d="M11.7801 38.5L24.7705 46M125.229 104L138.22 111.5" stroke="#00AE99" stroke-width="3"/> + <path d="M38.5001 11.7801L46.0001 24.7705M104 125.229L111.5 138.22" stroke="#00AE99" stroke-width="3"/> + <path d="M111.5 11.7801L104 24.7705M46 125.229L38.5 138.22" stroke="#00AE99" stroke-width="3"/> + <path d="M138.22 38.5L125.229 46M24.7705 104L11.7801 111.5" stroke="#00AE99" stroke-width="3"/> + </Dial> + </g> + </svg> +); + +const point = keyframes` + 0% { transform: rotate(0deg) } + 20% { transform: rotate(10deg) } + 30% { transform: rotate(30deg) } + 60% { transform: rotate(-20deg) } + 80% { transform: rotate(-20deg) } + 100% { transform: rotate(0deg) } +`; + +const rotate = keyframes` + 0% { transform: rotate(0deg) } + 20% { transform: rotate(-10deg) } + 30% { transform: rotate(-30deg) } + 60% { transform: rotate(20deg) } + 80% { transform: rotate(20deg) } + 100% { transform: rotate(0deg) } +`; + +const Needle = styled.path` + animation: ${point} 5s infinite; + transform-origin: 50% 50%; +`; + +const Dial = styled.g` + animation: ${rotate} 5s infinite; + transform-origin: 50% 50%; +`; diff --git a/packages/website/ts/@next/components/blockIconLink.tsx b/packages/website/ts/@next/components/blockIconLink.tsx index 0e97aed1a..648b5a485 100644 --- a/packages/website/ts/@next/components/blockIconLink.tsx +++ b/packages/website/ts/@next/components/blockIconLink.tsx @@ -1,41 +1,72 @@ import * as React from 'react'; +import {withRouter} from 'react-router-dom'; import styled from 'styled-components'; import {Button} from 'ts/@next/components/button'; import {Icon} from 'ts/@next/components/icon'; interface Props { - icon: string; + icon?: string; + iconComponent?: React.ReactNode; title: string; linkLabel: string; linkUrl?: string; linkAction?: () => void; } -export const BlockIconLink = (props: Props) => ( - <Wrap> - <div> - <Icon - name={props.icon} - size="large" - margin={[0, 0, 'default', 0]} - /> - - <Title> - {props.title} - </Title> - - <Button - isWithArrow={true} - isTransparent={true} - href={props.linkUrl} - onClick={props.linkAction} - > - {props.linkLabel} - </Button> - </div> - </Wrap> -); +class BaseComponent extends React.PureComponent<Props> { + public onClick = (): void => { + const { + linkAction, + linkUrl, + } = this.props; + + if (linkAction) { + linkAction(); + } else { + this.props.history.push(linkUrl); + } + } + + public render(): React.ReactNode { + const { + icon, + iconComponent, + linkUrl, + linkAction, + title, + linkLabel, + } = this.props; + + return ( + <Wrap onClick={this.onClick}> + <div> + <Icon + name={icon} + component={iconComponent} + size="large" + margin={[0, 0, 'default', 0]} + /> + + <Title> + {title} + </Title> + + <Button + isWithArrow={true} + isTransparent={true} + href={linkUrl} + onClick={linkAction} + > + {linkLabel} + </Button> + </div> + </Wrap> + ); + } +} + +export const BlockIconLink = withRouter(BaseComponent); const Wrap = styled.div` width: calc(50% - 15px); @@ -46,6 +77,12 @@ const Wrap = styled.div` align-items: center; text-align: center; background-color: ${props => props.theme.lightBgColor}; + cursor: pointer; + + a, + button { + pointer-events: none; + } @media (max-width: 900px) { width: 100%; diff --git a/packages/website/ts/@next/components/icon.tsx b/packages/website/ts/@next/components/icon.tsx index 84d9f4cb4..e3ad83c94 100644 --- a/packages/website/ts/@next/components/icon.tsx +++ b/packages/website/ts/@next/components/icon.tsx @@ -5,20 +5,33 @@ import {getCSSPadding, PaddingInterface} from 'ts/@next/constants/utilities'; interface IconProps extends PaddingInterface { name: string; + component?: React.ReactNode; size?: 'small' | 'medium' | 'large' | 'hero' | number; } export const Icon: React.FunctionComponent<IconProps> = (props: IconProps) => { - const IconSVG = Loadable({ - loader: () => import(/* webpackChunkName: "icon" */`ts/@next/icons/illustrations/${props.name}.svg`), - loading: () => 'Loading', - }); - - return ( - <StyledIcon {...props}> - <IconSVG /> - </StyledIcon> - ); + if (props.name && !props.component) { + const IconSVG = Loadable({ + loader: () => import(/* webpackChunkName: "icon" */`ts/@next/icons/illustrations/${props.name}.svg`), + loading: () => 'Loading', + }); + + return ( + <StyledIcon {...props}> + <IconSVG /> + </StyledIcon> + ); + } + + if (props.component) { + return ( + <StyledIcon {...props}> + {props.component} + </StyledIcon> + ); + } + + return null; }; export const InlineIconWrap = styled.div<IconProps>` diff --git a/packages/website/ts/@next/components/sections/landing/cta.tsx b/packages/website/ts/@next/components/sections/landing/cta.tsx index 49fc48c1c..1a959a6e9 100644 --- a/packages/website/ts/@next/components/sections/landing/cta.tsx +++ b/packages/website/ts/@next/components/sections/landing/cta.tsx @@ -3,6 +3,9 @@ import * as React from 'react'; import {BlockIconLink} from 'ts/@next/components/blockIconLink'; import {Section} from 'ts/@next/components/newLayout'; +import {AnimatedChatIcon} from 'ts/@next/components/animatedChatIcon'; +import {AnimatedCompassIcon} from 'ts/@next/components/animatedCompassIcon'; + interface Props { onContactClick?: () => void; } @@ -16,13 +19,13 @@ export const SectionLandingCta = (props: Props) => ( flexBreakpoint="900px" > <BlockIconLink - icon="getStarted" + iconComponent={<AnimatedCompassIcon />} title="Ready to build on 0x?" linkLabel="Get Started" linkUrl="https://0xproject.com/docs" /> <BlockIconLink - icon="getInTouch" + iconComponent={<AnimatedChatIcon />} title="Want help from the 0x team?" linkLabel="Get in Touch" linkAction={props.onContactClick} diff --git a/packages/website/ts/@next/pages/landing.tsx b/packages/website/ts/@next/pages/landing.tsx index aa3e829b5..6788e08be 100644 --- a/packages/website/ts/@next/pages/landing.tsx +++ b/packages/website/ts/@next/pages/landing.tsx @@ -29,18 +29,17 @@ export class NextLanding extends React.Component<Props> { <SectionLandingHero /> <SectionLandingAbout /> <SectionLandingClients /> - <SectionLandingCta onContactClick={this._onOpenContactModal.bind(this)} /> - <ModalContact isOpen={this.state.isContactModalOpen} onDismiss={this._onDismissContactModal.bind(this)} /> + <SectionLandingCta onContactClick={this._onOpenContactModal} /> + <ModalContact isOpen={this.state.isContactModalOpen} onDismiss={this._onDismissContactModal} /> </SiteWrap> ); } - private _onOpenContactModal(e: Event): void { - e.preventDefault(); + private _onOpenContactModal = (): void => { this.setState({ isContactModalOpen: true }); } - private _onDismissContactModal(): void { + private _onDismissContactModal = (): void => { this.setState({ isContactModalOpen: false }); } } |