aboutsummaryrefslogtreecommitdiffstats
path: root/packages/instant/src/components
diff options
context:
space:
mode:
authorFrancesco Agosti <francesco.agosti93@gmail.com>2018-11-14 09:31:38 +0800
committerGitHub <noreply@github.com>2018-11-14 09:31:38 +0800
commit4fc457b78b30e761164eac26fe5f1ebcddd11f7d (patch)
tree05a0d01029815d36370f8548d365289f555e8be4 /packages/instant/src/components
parente02dc13805349a770506350c69e9061f596b48b2 (diff)
parent2f6b1273aaf621beebcbc70af4bb6c5f4a8217a3 (diff)
downloaddexon-0x-contracts-4fc457b78b30e761164eac26fe5f1ebcddd11f7d.tar.gz
dexon-0x-contracts-4fc457b78b30e761164eac26fe5f1ebcddd11f7d.tar.zst
dexon-0x-contracts-4fc457b78b30e761164eac26fe5f1ebcddd11f7d.zip
Merge pull request #1242 from 0xProject/feature/instant/metamask-connect-flow
[instant] Install/Unlock MetaMask, connect PaymentDropdown to redux state
Diffstat (limited to 'packages/instant/src/components')
-rw-r--r--packages/instant/src/components/animations/slide_animation.tsx2
-rw-r--r--packages/instant/src/components/buy_button.tsx1
-rw-r--r--packages/instant/src/components/buy_order_progress.tsx2
-rw-r--r--packages/instant/src/components/buy_order_state_buttons.tsx2
-rw-r--r--packages/instant/src/components/erc20_token_selector.tsx7
-rw-r--r--packages/instant/src/components/install_wallet_panel_content.tsx32
-rw-r--r--packages/instant/src/components/meta_mask_logo.tsx78
-rw-r--r--packages/instant/src/components/payment_method.tsx139
-rw-r--r--packages/instant/src/components/placing_order_button.tsx4
-rw-r--r--packages/instant/src/components/scaling_input.tsx2
-rw-r--r--packages/instant/src/components/secondary_button.tsx2
-rw-r--r--packages/instant/src/components/sliding_error.tsx3
-rw-r--r--packages/instant/src/components/sliding_panel.tsx22
-rw-r--r--packages/instant/src/components/standard_panel_content.tsx60
-rw-r--r--packages/instant/src/components/standard_sliding_panel.tsx29
-rw-r--r--packages/instant/src/components/timed_progress_bar.tsx46
-rw-r--r--packages/instant/src/components/ui/button.tsx33
-rw-r--r--packages/instant/src/components/ui/icon.tsx6
-rw-r--r--packages/instant/src/components/ui/text.tsx9
-rw-r--r--packages/instant/src/components/zero_ex_instant.tsx3
-rw-r--r--packages/instant/src/components/zero_ex_instant_container.tsx15
-rw-r--r--packages/instant/src/components/zero_ex_instant_overlay.tsx2
-rw-r--r--packages/instant/src/components/zero_ex_instant_provider.tsx7
23 files changed, 416 insertions, 90 deletions
diff --git a/packages/instant/src/components/animations/slide_animation.tsx b/packages/instant/src/components/animations/slide_animation.tsx
index 9adb1c674..5992bcba7 100644
--- a/packages/instant/src/components/animations/slide_animation.tsx
+++ b/packages/instant/src/components/animations/slide_animation.tsx
@@ -1,10 +1,10 @@
import * as React from 'react';
import { OptionallyScreenSpecific } from '../../style/media';
+import { SlideAnimationState } from '../../types';
import { PositionAnimation, PositionAnimationSettings } from './position_animation';
-export type SlideAnimationState = 'slidIn' | 'slidOut' | 'none';
export interface SlideAnimationProps {
animationState: SlideAnimationState;
slideInSettings: OptionallyScreenSpecific<PositionAnimationSettings>;
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx
index 877ab275c..8b6121e43 100644
--- a/packages/instant/src/components/buy_button.tsx
+++ b/packages/instant/src/components/buy_button.tsx
@@ -43,7 +43,6 @@ export class BuyButton extends React.Component<BuyButtonProps> {
onClick={this._handleClick}
isDisabled={shouldDisableButton}
fontColor={ColorOption.white}
- fontSize="20px"
>
Buy
</Button>
diff --git a/packages/instant/src/components/buy_order_progress.tsx b/packages/instant/src/components/buy_order_progress.tsx
index bc7319423..6568de91b 100644
--- a/packages/instant/src/components/buy_order_progress.tsx
+++ b/packages/instant/src/components/buy_order_progress.tsx
@@ -12,7 +12,6 @@ export interface BuyOrderProgressProps {
export const BuyOrderProgress: React.StatelessComponent<BuyOrderProgressProps> = props => {
const { buyOrderState } = props;
-
if (
buyOrderState.processState === OrderProcessState.Processing ||
buyOrderState.processState === OrderProcessState.Success ||
@@ -30,6 +29,5 @@ export const BuyOrderProgress: React.StatelessComponent<BuyOrderProgressProps> =
</Container>
);
}
-
return null;
};
diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx
index 6041bf4f5..e563bec73 100644
--- a/packages/instant/src/components/buy_order_state_buttons.tsx
+++ b/packages/instant/src/components/buy_order_state_buttons.tsx
@@ -35,7 +35,7 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP
if (props.buyOrderProcessingState === OrderProcessState.Failure) {
return (
<Flex justify="space-between">
- <Button width="48%" onClick={props.onRetry} fontColor={ColorOption.white} fontSize="16px">
+ <Button width="48%" onClick={props.onRetry} fontColor={ColorOption.white}>
Back
</Button>
<SecondaryButton width="48%" onClick={props.onViewTransaction}>
diff --git a/packages/instant/src/components/erc20_token_selector.tsx b/packages/instant/src/components/erc20_token_selector.tsx
index 3503ff31a..d4a77c278 100644
--- a/packages/instant/src/components/erc20_token_selector.tsx
+++ b/packages/instant/src/components/erc20_token_selector.tsx
@@ -29,13 +29,18 @@ export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps>
const { tokens, onTokenSelect } = this.props;
return (
<Container height="100%">
+ <Container marginBottom="10px">
+ <Text fontColor={ColorOption.darkGrey} fontSize="18px" fontWeight="600" lineHeight="22px">
+ Select Token
+ </Text>
+ </Container>
<SearchInput
placeholder="Search tokens..."
width="100%"
value={this.state.searchQuery}
onChange={this._handleSearchInputChange}
/>
- <Container overflow="scroll" height="calc(100% - 80px)" marginTop="10px">
+ <Container overflow="scroll" height="calc(100% - 90px)" marginTop="10px">
{_.map(tokens, token => {
if (!this._isTokenQueryMatch(token)) {
return null;
diff --git a/packages/instant/src/components/install_wallet_panel_content.tsx b/packages/instant/src/components/install_wallet_panel_content.tsx
new file mode 100644
index 000000000..546874212
--- /dev/null
+++ b/packages/instant/src/components/install_wallet_panel_content.tsx
@@ -0,0 +1,32 @@
+import * as React from 'react';
+
+import { META_MASK_CHROME_STORE_URL, META_MASK_SITE_URL } from '../constants';
+import { ColorOption } from '../style/theme';
+
+import { MetaMaskLogo } from './meta_mask_logo';
+import { StandardPanelContent } from './standard_panel_content';
+import { Button } from './ui/button';
+
+export interface InstallWalletPanelContentProps {}
+
+export const InstallWalletPanelContent: React.StatelessComponent<InstallWalletPanelContentProps> = () => (
+ <StandardPanelContent
+ image={<MetaMaskLogo width={85} height={80} />}
+ title="Install MetaMask"
+ description="Please install the MetaMask wallet extension from the Chrome Store."
+ moreInfoSettings={{
+ href: META_MASK_SITE_URL,
+ text: 'What is MetaMask?',
+ }}
+ action={
+ <Button
+ href={META_MASK_CHROME_STORE_URL}
+ width="100%"
+ fontColor={ColorOption.white}
+ backgroundColor={ColorOption.darkOrange}
+ >
+ Get Chrome Extension
+ </Button>
+ }
+ />
+);
diff --git a/packages/instant/src/components/meta_mask_logo.tsx b/packages/instant/src/components/meta_mask_logo.tsx
new file mode 100644
index 000000000..d1ad10c23
--- /dev/null
+++ b/packages/instant/src/components/meta_mask_logo.tsx
@@ -0,0 +1,78 @@
+import * as React from 'react';
+
+export interface MetaMaskLogoProps {
+ width?: number;
+ height?: number;
+}
+
+export const MetaMaskLogo: React.StatelessComponent<MetaMaskLogoProps> = ({ width, height }) => (
+ <svg width={width} height={height} viewBox="0 0 85 80" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M80.578 0L47.7107 24.8648L53.542 10.2702L80.578 0Z" fill="#E2761B" />
+ <path d="M4.24075 0L37.1081 25.4053L31.2768 10.2702L4.24075 0Z" fill="#E4761B" />
+ <path d="M68.9152 57.8379L59.9032 71.8919L78.9874 77.2973L84.2886 58.3785L68.9152 57.8379Z" fill="#E4761B" />
+ <path d="M0.53006 58.3785L5.83124 77.2973L24.9155 71.8919L15.9035 57.8379L0.53006 58.3785Z" fill="#E4761B" />
+ <path d="M23.8552 34.5941L18.554 42.7022L37.1082 43.7833L36.5781 23.2428L23.8552 34.5941Z" fill="#E4761B" />
+ <path d="M60.9635 34.5941L47.7106 23.2428V43.7833L66.2647 42.7022L60.9635 34.5941Z" fill="#E4761B" />
+ <path d="M24.9156 71.8914L36.0481 66.4861L26.5059 58.378L24.9156 71.8914Z" fill="#E4761B" />
+ <path d="M48.7709 66.4861L59.9034 71.8914L58.313 58.378L48.7709 66.4861Z" fill="#E4761B" />
+ <path d="M59.9034 71.8919L48.7709 66.4865L49.301 73.5135V76.7567L59.9034 71.8919Z" fill="#D7C1B3" />
+ <path d="M24.9157 71.892L35.518 76.7568V73.5136L36.0482 66.4866L24.9157 71.892Z" fill="#D7C1B3" />
+ <path d="M35.5179 53.5138L25.9758 50.8111L32.8673 47.5678L35.5179 53.5138Z" fill="#233447" />
+ <path d="M49.3009 53.5138L51.9515 47.5678L58.843 50.8111L49.3009 53.5138Z" fill="#233447" />
+ <path d="M24.9155 71.892L26.5059 57.838L15.9035 58.3785L24.9155 71.892Z" fill="#CD6116" />
+ <path d="M58.313 57.838L59.9034 71.892L68.9154 58.3785L58.313 57.838Z" fill="#CD6116" />
+ <path
+ d="M66.2648 42.7025L47.7106 43.7836L49.301 53.5132L51.9516 47.5673L58.8431 50.8106L66.2648 42.7025Z"
+ fill="#CD6116"
+ />
+ <path
+ d="M25.9758 50.8106L32.8673 47.5673L35.5179 53.5132L37.1083 43.7836L18.5541 42.7025L25.9758 50.8106Z"
+ fill="#CD6116"
+ />
+ <path d="M18.5541 42.7024L26.5059 58.378L25.9758 50.8105L18.5541 42.7024Z" fill="#E4751F" />
+ <path d="M58.8431 50.8106L58.313 58.3781L66.2647 42.7025L58.8431 50.8106Z" fill="#E4751F" />
+ <path d="M37.1083 43.7838L35.518 53.5135L37.6384 65.4053L38.1686 49.7297L37.1083 43.7838Z" fill="#E4751F" />
+ <path d="M47.7105 43.7838L46.6503 49.7297L47.1804 65.4053L49.3009 53.5135L47.7105 43.7838Z" fill="#E4751F" />
+ <path
+ d="M49.301 53.5134L47.1805 65.4052L48.7709 66.4863L58.313 58.3782L58.8431 50.8107L49.301 53.5134Z"
+ fill="#F6851B"
+ />
+ <path
+ d="M25.9758 50.8107L26.5059 58.3782L36.048 66.4863L37.6384 65.4052L35.5179 53.5134L25.9758 50.8107Z"
+ fill="#F6851B"
+ />
+ <path
+ d="M49.3011 76.7568V73.5135L48.771 72.973H36.0482L35.518 73.5135V76.7568L24.9157 71.8919L28.6265 75.1351L36.0482 80H48.771L56.1927 75.1351L59.9035 71.8919L49.3011 76.7568Z"
+ fill="#C0AD9E"
+ />
+ <path
+ d="M48.771 66.486L47.1806 65.405H37.6385L36.0482 66.486L35.518 73.513L36.0482 72.9725H48.771L49.3011 73.513L48.771 66.486Z"
+ fill="#161616"
+ />
+ <path
+ d="M82.1685 26.4864L84.8191 12.9729L80.5781 0L48.771 24.3242L60.9637 34.5945L78.4576 39.9998L82.1685 35.6755L80.5781 34.0539L83.2287 31.8918L81.1082 30.2702L83.7588 28.108L82.1685 26.4864Z"
+ fill="#763D16"
+ />
+ <path
+ d="M0 12.9729L2.65059 26.4864L1.06024 28.108L3.71083 30.2702L1.59036 31.8918L4.24095 34.0539L2.65059 35.6755L6.36142 39.9998L23.8553 34.5945L36.0481 24.3242L4.24095 0L0 12.9729Z"
+ fill="#763D16"
+ />
+ <path
+ d="M78.4575 39.9993L60.9636 34.5939L66.2648 42.702L58.313 58.3776H68.9154H84.2888L78.4575 39.9993Z"
+ fill="#F6851B"
+ />
+ <path
+ d="M23.8554 34.5939L6.36147 39.9993L0.530167 58.3776H15.9036H26.506L18.5542 42.702L23.8554 34.5939Z"
+ fill="#F6851B"
+ />
+ <path
+ d="M47.7106 43.7833L48.7709 24.3239L53.5419 10.2699H31.2769L36.048 24.3239L37.1083 43.7833L37.6384 49.7292V65.4048H47.1805V49.7292L47.7106 43.7833Z"
+ fill="#F6851B"
+ />
+ </svg>
+);
+
+MetaMaskLogo.defaultProps = {
+ width: 85,
+ height: 80,
+};
diff --git a/packages/instant/src/components/payment_method.tsx b/packages/instant/src/components/payment_method.tsx
index 8c0b47d72..49ec22164 100644
--- a/packages/instant/src/components/payment_method.tsx
+++ b/packages/instant/src/components/payment_method.tsx
@@ -1,45 +1,132 @@
-import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import * as React from 'react';
import { ColorOption } from '../style/theme';
-import { Network } from '../types';
+import { Account, AccountState, Network } from '../types';
+import { MetaMaskLogo } from './meta_mask_logo';
import { PaymentMethodDropdown } from './payment_method_dropdown';
import { Circle } from './ui/circle';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
+import { Icon } from './ui/icon';
import { Text } from './ui/text';
-export interface PaymentMethodProps {}
+export interface PaymentMethodProps {
+ account: Account;
+ network: Network;
+ onInstallWalletClick: () => void;
+ onUnlockWalletClick: () => void;
+}
-export const PaymentMethod: React.StatelessComponent<PaymentMethodProps> = () => (
- <Container padding="20px" width="100%">
- <Container marginBottom="10px">
- <Flex justify="space-between">
- <Text
- letterSpacing="1px"
- fontColor={ColorOption.primaryColor}
- fontWeight={600}
- textTransform="uppercase"
- fontSize="14px"
- >
- Payment Method
- </Text>
- <Flex>
- <Circle color={ColorOption.green} diameter={8} />
+export class PaymentMethod extends React.Component<PaymentMethodProps> {
+ public render(): React.ReactNode {
+ return (
+ <Container padding="20px" width="100%">
+ <Container marginBottom="12px">
+ <Flex justify="space-between">
+ <Text
+ letterSpacing="1px"
+ fontColor={ColorOption.primaryColor}
+ fontWeight={600}
+ textTransform="uppercase"
+ fontSize="14px"
+ >
+ {this._renderTitleText()}
+ </Text>
+ <Flex>{this._renderTitleLabel()}</Flex>
+ </Flex>
+ </Container>
+ {this._renderMainContent()}
+ </Container>
+ );
+ }
+ private readonly _renderTitleText = (): string => {
+ const { account } = this.props;
+ switch (account.state) {
+ case AccountState.Loading:
+ return 'loading...';
+ case AccountState.Locked:
+ case AccountState.None:
+ return 'connect your wallet';
+ case AccountState.Ready:
+ return 'payment method';
+ }
+ };
+ private readonly _renderTitleLabel = (): React.ReactNode => {
+ const { account } = this.props;
+ if (account.state === AccountState.Ready || account.state === AccountState.Locked) {
+ const circleColor: ColorOption = account.state === AccountState.Ready ? ColorOption.green : ColorOption.red;
+ return (
+ <React.Fragment>
+ <Circle diameter={8} color={circleColor} />
<Container marginLeft="3px">
<Text fontColor={ColorOption.darkGrey} fontSize="12px">
MetaMask
</Text>
</Container>
- </Flex>
- </Flex>
- </Container>
- <PaymentMethodDropdown
- accountAddress="0xa1b2c3d4e5f6g7h8j9k10"
- accountEthBalanceInWei={new BigNumber(10500000000000000000)}
- network={Network.Mainnet}
- />
+ </React.Fragment>
+ );
+ }
+ return null;
+ };
+ private readonly _renderMainContent = (): React.ReactNode => {
+ const { account, network } = this.props;
+ switch (account.state) {
+ case AccountState.Loading:
+ // Just take up the same amount of space as the other states.
+ return <Container height="52px" />;
+ case AccountState.Locked:
+ return (
+ <WalletPrompt
+ onClick={this.props.onUnlockWalletClick}
+ image={<Icon width={13} icon="lock" color={ColorOption.black} />}
+ >
+ Please Unlock MetaMask
+ </WalletPrompt>
+ );
+ case AccountState.None:
+ return (
+ <WalletPrompt
+ onClick={this.props.onInstallWalletClick}
+ image={<MetaMaskLogo width={19} height={18} />}
+ >
+ Install MetaMask
+ </WalletPrompt>
+ );
+ case AccountState.Ready:
+ return (
+ <PaymentMethodDropdown
+ accountAddress={account.address}
+ accountEthBalanceInWei={account.ethBalanceInWei}
+ network={network}
+ />
+ );
+ }
+ };
+}
+
+interface WalletPromptProps {
+ image: React.ReactNode;
+ onClick?: () => void;
+}
+
+const WalletPrompt: React.StatelessComponent<WalletPromptProps> = ({ onClick, image, children }) => (
+ <Container
+ padding="14.5px"
+ border={`1px solid ${ColorOption.darkOrange}`}
+ backgroundColor={ColorOption.lightOrange}
+ width="100%"
+ borderRadius="4px"
+ onClick={onClick}
+ cursor={onClick ? 'pointer' : undefined}
+ boxShadowOnHover={!!onClick}
+ >
+ <Flex>
+ <Container marginRight="10px">{image}</Container>
+ <Text fontSize="16px" fontColor={ColorOption.darkOrange}>
+ {children}
+ </Text>
+ </Flex>
</Container>
);
diff --git a/packages/instant/src/components/placing_order_button.tsx b/packages/instant/src/components/placing_order_button.tsx
index d774d7d27..2516b90b1 100644
--- a/packages/instant/src/components/placing_order_button.tsx
+++ b/packages/instant/src/components/placing_order_button.tsx
@@ -7,9 +7,9 @@ import { Container } from './ui/container';
import { Spinner } from './ui/spinner';
export const PlacingOrderButton: React.StatelessComponent<{}> = props => (
- <Button isDisabled={true} width="100%" fontColor={ColorOption.white} fontSize="20px">
+ <Button isDisabled={true} width="100%" fontColor={ColorOption.white}>
<Container display="inline-block" position="relative" top="3px" marginRight="8px">
- <Spinner widthPx={20} heightPx={20} />
+ <Spinner widthPx={16} heightPx={16} />
</Container>
Placing Order&hellip;
</Button>
diff --git a/packages/instant/src/components/scaling_input.tsx b/packages/instant/src/components/scaling_input.tsx
index 1abadb78b..e1599a316 100644
--- a/packages/instant/src/components/scaling_input.tsx
+++ b/packages/instant/src/components/scaling_input.tsx
@@ -156,8 +156,6 @@ export class ScalingInput extends React.Component<ScalingInputProps, ScalingInpu
return `${width}px`;
}
return `${textLengthThreshold}ch`;
- default:
- return '1ch';
}
};
private readonly _calculateFontSize = (phase: ScalingInputPhase): number => {
diff --git a/packages/instant/src/components/secondary_button.tsx b/packages/instant/src/components/secondary_button.tsx
index df0539606..705390e28 100644
--- a/packages/instant/src/components/secondary_button.tsx
+++ b/packages/instant/src/components/secondary_button.tsx
@@ -15,8 +15,6 @@ export const SecondaryButton: React.StatelessComponent<SecondaryButtonProps> = p
borderColor={ColorOption.lightGrey}
width={props.width}
onClick={props.onClick}
- fontColor={ColorOption.primaryColor}
- fontSize="16px"
{...buttonProps}
>
{props.children}
diff --git a/packages/instant/src/components/sliding_error.tsx b/packages/instant/src/components/sliding_error.tsx
index a8d4e391c..b59e2a905 100644
--- a/packages/instant/src/components/sliding_error.tsx
+++ b/packages/instant/src/components/sliding_error.tsx
@@ -3,9 +3,10 @@ import * as React from 'react';
import { ScreenSpecification } from '../style/media';
import { ColorOption } from '../style/theme';
import { zIndex } from '../style/z_index';
+import { SlideAnimationState } from '../types';
import { PositionAnimationSettings } from './animations/position_animation';
-import { SlideAnimation, SlideAnimationState } from './animations/slide_animation';
+import { SlideAnimation } from './animations/slide_animation';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
diff --git a/packages/instant/src/components/sliding_panel.tsx b/packages/instant/src/components/sliding_panel.tsx
index 9d16f9560..7f9037049 100644
--- a/packages/instant/src/components/sliding_panel.tsx
+++ b/packages/instant/src/components/sliding_panel.tsx
@@ -2,35 +2,25 @@ import * as React from 'react';
import { ColorOption } from '../style/theme';
import { zIndex } from '../style/z_index';
+import { SlideAnimationState } from '../types';
import { PositionAnimationSettings } from './animations/position_animation';
-import { SlideAnimation, SlideAnimationState } from './animations/slide_animation';
+import { SlideAnimation } from './animations/slide_animation';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
import { Icon } from './ui/icon';
-import { Text } from './ui/text';
export interface PanelProps {
- title?: string;
onClose?: () => void;
}
-export const Panel: React.StatelessComponent<PanelProps> = ({ title, children, onClose }) => (
+export const Panel: React.StatelessComponent<PanelProps> = ({ children, onClose }) => (
<Container backgroundColor={ColorOption.white} width="100%" height="100%" zIndex={zIndex.panel} padding="20px">
- <Flex justify="space-between">
- {title && (
- <Container marginTop="3px">
- <Text fontColor={ColorOption.darkGrey} fontSize="18px" fontWeight="600" lineHeight="22px">
- {title}
- </Text>
- </Container>
- )}
- <Container position="relative" bottom="7px">
- <Icon width={12} color={ColorOption.lightGrey} icon="closeX" onClick={onClose} />
- </Container>
+ <Flex justify="flex-end">
+ <Icon padding="5px" width={12} color={ColorOption.lightGrey} icon="closeX" onClick={onClose} />
</Flex>
- <Container marginTop="10px" height="100%">
+ <Container position="relative" top="-10px" height="100%">
{children}
</Container>
</Container>
diff --git a/packages/instant/src/components/standard_panel_content.tsx b/packages/instant/src/components/standard_panel_content.tsx
new file mode 100644
index 000000000..89e4da70c
--- /dev/null
+++ b/packages/instant/src/components/standard_panel_content.tsx
@@ -0,0 +1,60 @@
+import * as React from 'react';
+
+import { ColorOption } from '../style/theme';
+
+import { Container } from './ui/container';
+import { Flex } from './ui/flex';
+import { Text } from './ui/text';
+
+export interface MoreInfoSettings {
+ text: string;
+ href: string;
+}
+
+export interface StandardPanelContentProps {
+ image: React.ReactNode;
+ title: string;
+ description: string;
+ moreInfoSettings?: MoreInfoSettings;
+ action: React.ReactNode;
+}
+
+const SPACING_BETWEEN_PX = '20px';
+
+export const StandardPanelContent: React.StatelessComponent<StandardPanelContentProps> = ({
+ image,
+ title,
+ description,
+ moreInfoSettings,
+ action,
+}) => (
+ <Container height="100%">
+ <Flex direction="column" height="calc(100% - 58px)">
+ <Container marginBottom={SPACING_BETWEEN_PX}>{image}</Container>
+ <Container marginBottom={SPACING_BETWEEN_PX}>
+ <Text fontSize="20px" fontWeight={700} fontColor={ColorOption.black}>
+ {title}
+ </Text>
+ </Container>
+ <Container marginBottom={SPACING_BETWEEN_PX}>
+ <Text fontSize="14px" fontColor={ColorOption.grey} center={true}>
+ {description}
+ </Text>
+ </Container>
+ <Container marginBottom={SPACING_BETWEEN_PX}>
+ {moreInfoSettings && (
+ <Text
+ center={true}
+ fontSize="13px"
+ textDecorationLine="underline"
+ fontColor={ColorOption.lightGrey}
+ href={moreInfoSettings.href}
+ >
+ {moreInfoSettings.text}
+ </Text>
+ )}
+ </Container>
+ </Flex>
+ <Container>{action}</Container>
+ </Container>
+);
diff --git a/packages/instant/src/components/standard_sliding_panel.tsx b/packages/instant/src/components/standard_sliding_panel.tsx
new file mode 100644
index 000000000..f587ff79a
--- /dev/null
+++ b/packages/instant/src/components/standard_sliding_panel.tsx
@@ -0,0 +1,29 @@
+import * as React from 'react';
+
+import { SlideAnimationState, StandardSlidingPanelContent, StandardSlidingPanelSettings } from '../types';
+
+import { InstallWalletPanelContent } from './install_wallet_panel_content';
+import { SlidingPanel } from './sliding_panel';
+
+export interface StandardSlidingPanelProps extends StandardSlidingPanelSettings {
+ onClose: () => void;
+}
+
+export class StandardSlidingPanel extends React.Component<StandardSlidingPanelProps> {
+ public render(): React.ReactNode {
+ const { animationState, content, onClose } = this.props;
+ return (
+ <SlidingPanel animationState={animationState} onClose={onClose}>
+ {this._getNodeForContent(content)}
+ </SlidingPanel>
+ );
+ }
+ private readonly _getNodeForContent = (content: StandardSlidingPanelContent): React.ReactNode => {
+ switch (content) {
+ case StandardSlidingPanelContent.InstallWallet:
+ return <InstallWalletPanelContent />;
+ case StandardSlidingPanelContent.None:
+ return null;
+ }
+ };
+}
diff --git a/packages/instant/src/components/timed_progress_bar.tsx b/packages/instant/src/components/timed_progress_bar.tsx
index 59aaa33a1..8465b9cd0 100644
--- a/packages/instant/src/components/timed_progress_bar.tsx
+++ b/packages/instant/src/components/timed_progress_bar.tsx
@@ -2,7 +2,7 @@ import * as _ from 'lodash';
import * as React from 'react';
import { PROGRESS_FINISH_ANIMATION_TIME_MS, PROGRESS_STALL_AT_WIDTH } from '../constants';
-import { ColorOption, keyframes, styled } from '../style/theme';
+import { ColorOption, css, keyframes, styled } from '../style/theme';
import { Container } from './ui/container';
@@ -20,15 +20,11 @@ export class TimedProgressBar extends React.Component<TimedProgressBarProps, {}>
private readonly _barRef = React.createRef<HTMLDivElement>();
public render(): React.ReactNode {
- const timedProgressProps = this._calculateTimedProgressProps();
- return (
- <Container width="100%" backgroundColor={ColorOption.lightGrey} borderRadius="6px">
- <TimedProgress {...timedProgressProps} ref={this._barRef as any} />
- </Container>
- );
+ const widthAnimationSettings = this._calculateWidthAnimationSettings();
+ return <ProgressBar animationSettings={widthAnimationSettings} ref={this._barRef} />;
}
- private _calculateTimedProgressProps(): TimedProgressProps {
+ private _calculateWidthAnimationSettings(): WidthAnimationSettings {
if (this.props.hasEnded) {
if (!this._barRef.current) {
throw new Error('ended but no reference');
@@ -60,21 +56,45 @@ const expandingWidthKeyframes = (fromWidth: string, toWidth: string) => {
`;
};
-interface TimedProgressProps {
+export interface WidthAnimationSettings {
timeMs: number;
fromWidth: string;
toWidth: string;
}
-export const TimedProgress =
+interface ProgressProps {
+ width?: string;
+ animationSettings?: WidthAnimationSettings;
+}
+
+export const Progress =
styled.div <
- TimedProgressProps >
+ ProgressProps >
`
&& {
background-color: ${props => props.theme[ColorOption.primaryColor]};
border-radius: 6px;
height: 6px;
- animation: ${props => expandingWidthKeyframes(props.fromWidth, props.toWidth)}
- ${props => props.timeMs}ms linear 1 forwards;
+ ${props => (props.width ? `width: ${props.width};` : '')}
+ ${props =>
+ props.animationSettings
+ ? css`
+ animation: ${expandingWidthKeyframes(
+ props.animationSettings.fromWidth,
+ props.animationSettings.toWidth,
+ )}
+ ${props.animationSettings.timeMs}ms linear 1 forwards;
+ `
+ : ''}
}
`;
+
+export interface ProgressBarProps extends ProgressProps {}
+
+export const ProgressBar: React.ComponentType<ProgressBarProps & React.ClassAttributes<{}>> = React.forwardRef(
+ (props, ref) => (
+ <Container width="100%" backgroundColor={ColorOption.lightGrey} borderRadius="6px">
+ <Progress {...props} ref={ref as any} />
+ </Container>
+ ),
+);
diff --git a/packages/instant/src/components/ui/button.tsx b/packages/instant/src/components/ui/button.tsx
index b90221bf4..e77b1b5d1 100644
--- a/packages/instant/src/components/ui/button.tsx
+++ b/packages/instant/src/components/ui/button.tsx
@@ -2,6 +2,9 @@ import { darken, saturate } from 'polished';
import * as React from 'react';
import { ColorOption, styled } from '../../style/theme';
+import { util } from '../../util/util';
+
+export type ButtonOnClickHandler = (event: React.MouseEvent<HTMLElement>) => void;
export interface ButtonProps {
backgroundColor?: ColorOption;
@@ -12,15 +15,26 @@ export interface ButtonProps {
padding?: string;
type?: string;
isDisabled?: boolean;
- onClick?: (event: React.MouseEvent<HTMLElement>) => void;
+ href?: string;
+ onClick?: ButtonOnClickHandler;
className?: string;
}
-const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, isDisabled, onClick, type, className }) => (
- <button type={type} className={className} onClick={isDisabled ? undefined : onClick} disabled={isDisabled}>
- {children}
- </button>
-);
+const PlainButton: React.StatelessComponent<ButtonProps> = ({
+ children,
+ isDisabled,
+ onClick,
+ href,
+ type,
+ className,
+}) => {
+ const computedOnClick = isDisabled ? undefined : href ? util.createOpenUrlInNewWindow(href) : onClick;
+ return (
+ <button type={type} className={className} onClick={computedOnClick} disabled={isDisabled}>
+ {children}
+ </button>
+ );
+};
const darkenOnHoverAmount = 0.1;
const darkenOnActiveAmount = 0.2;
@@ -31,7 +45,7 @@ export const Button = styled(PlainButton)`
box-sizing: border-box;
font-size: ${props => props.fontSize};
font-family: 'Inter UI', sans-serif;
- font-weight: 600;
+ font-weight: 500;
color: ${props => props.fontColor && props.theme[props.fontColor]};
cursor: ${props => (props.isDisabled ? 'default' : 'pointer')};
transition: background-color, opacity 0.5s ease;
@@ -64,11 +78,10 @@ export const Button = styled(PlainButton)`
Button.defaultProps = {
backgroundColor: ColorOption.primaryColor,
- borderColor: ColorOption.primaryColor,
width: 'auto',
isDisabled: false,
- padding: '.6em 1.2em',
- fontSize: '15px',
+ padding: '.82em 1.2em',
+ fontSize: '16px',
};
Button.displayName = 'Button';
diff --git a/packages/instant/src/components/ui/icon.tsx b/packages/instant/src/components/ui/icon.tsx
index a88fa87dd..811142b5b 100644
--- a/packages/instant/src/components/ui/icon.tsx
+++ b/packages/instant/src/components/ui/icon.tsx
@@ -20,6 +20,7 @@ interface IconInfoMapping {
success: IconInfo;
chevron: IconInfo;
search: IconInfo;
+ lock: IconInfo;
}
const ICONS: IconInfoMapping = {
closeX: {
@@ -58,6 +59,11 @@ const ICONS: IconInfoMapping = {
path:
'M8.39404 5.19727C8.39404 6.96289 6.96265 8.39453 5.19702 8.39453C3.4314 8.39453 2 6.96289 2 5.19727C2 3.43164 3.4314 2 5.19702 2C6.96265 2 8.39404 3.43164 8.39404 5.19727ZM8.09668 9.51074C7.26855 10.0684 6.27075 10.3945 5.19702 10.3945C2.3269 10.3945 0 8.06738 0 5.19727C0 2.32715 2.3269 0 5.19702 0C8.06738 0 10.394 2.32715 10.394 5.19727C10.394 6.27051 10.0686 7.26855 9.51074 8.09668L13.6997 12.2861L12.2854 13.7002L8.09668 9.51074Z',
},
+ lock: {
+ viewBox: '0 0 13 16',
+ path:
+ 'M6.47619 0C3.79509 0 1.60489 2.21216 1.60489 4.92014V6.33135C0.717479 6.33135 0 7.05602 0 7.95232V14.379C0 15.2753 0.717479 16 1.60489 16H11.3475C12.2349 16 12.9524 15.2753 12.9524 14.379V7.95232C12.9524 7.05602 12.2349 6.33135 11.3475 6.33135V4.92014C11.3475 2.21216 9.1573 0 6.47619 0ZM9.6482 6.33135H3.30418V4.92014C3.30418 3.16567 4.72026 1.71633 6.47619 1.71633C8.23213 1.71633 9.6482 3.16567 9.6482 4.92014V6.33135Z',
+ },
};
export interface IconProps {
diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx
index 4fe429d25..fd14cc4d1 100644
--- a/packages/instant/src/components/ui/text.tsx
+++ b/packages/instant/src/components/ui/text.tsx
@@ -2,6 +2,7 @@ import { darken } from 'polished';
import * as React from 'react';
import { ColorOption, styled } from '../../style/theme';
+import { util } from '../../util/util';
export interface TextProps {
fontColor?: ColorOption;
@@ -20,10 +21,16 @@ export interface TextProps {
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
noWrap?: boolean;
display?: string;
+ href?: string;
}
+export const Text: React.StatelessComponent<TextProps> = ({ href, onClick, ...rest }) => {
+ const computedOnClick = href ? util.createOpenUrlInNewWindow(href) : onClick;
+ return <StyledText {...rest} onClick={computedOnClick} />;
+};
+
const darkenOnHoverAmount = 0.3;
-export const Text =
+export const StyledText =
styled.div <
TextProps >
`
diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx
index b945f9908..2267b4dbf 100644
--- a/packages/instant/src/components/zero_ex_instant.tsx
+++ b/packages/instant/src/components/zero_ex_instant.tsx
@@ -1,8 +1,9 @@
import * as React from 'react';
+import { ZeroExInstantContainer } from '../components/zero_ex_instant_container';
+
import { INJECTED_DIV_CLASS } from '../constants';
-import { ZeroExInstantContainer } from './zero_ex_instant_container';
import { ZeroExInstantProvider, ZeroExInstantProviderProps } from './zero_ex_instant_provider';
export type ZeroExInstantProps = ZeroExInstantProviderProps;
diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx
index 5748e064e..c0a197590 100644
--- a/packages/instant/src/components/zero_ex_instant_container.tsx
+++ b/packages/instant/src/components/zero_ex_instant_container.tsx
@@ -1,26 +1,29 @@
import * as React from 'react';
import { AvailableERC20TokenSelector } from '../containers/available_erc20_token_selector';
+import { ConnectedBuyOrderProgressOrPaymentMethod } from '../containers/connected_buy_order_progress_or_payment_method';
+import { CurrentStandardSlidingPanel } from '../containers/current_standard_sliding_panel';
import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order_details';
import { LatestError } from '../containers/latest_error';
-import { SelectedAssetBuyOrderProgress } from '../containers/selected_asset_buy_order_progress';
import { SelectedAssetBuyOrderStateButtons } from '../containers/selected_asset_buy_order_state_buttons';
import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading';
import { ColorOption } from '../style/theme';
import { zIndex } from '../style/z_index';
+import { OrderProcessState, SlideAnimationState } from '../types';
-import { SlideAnimationState } from './animations/slide_animation';
import { CSSReset } from './css_reset';
import { SlidingPanel } from './sliding_panel';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
-export interface ZeroExInstantContainerProps {}
+export interface ZeroExInstantContainerProps {
+ orderProcessState: OrderProcessState;
+}
export interface ZeroExInstantContainerState {
tokenSelectionPanelAnimationState: SlideAnimationState;
}
-export class ZeroExInstantContainer extends React.Component<ZeroExInstantContainerProps, ZeroExInstantContainerState> {
+export class ZeroExInstantContainer extends React.Component<{}, ZeroExInstantContainerState> {
public state = {
tokenSelectionPanelAnimationState: 'none' as SlideAnimationState,
};
@@ -47,19 +50,19 @@ export class ZeroExInstantContainer extends React.Component<ZeroExInstantContain
>
<Flex direction="column" justify="flex-start" height="100%">
<SelectedAssetInstantHeading onSelectAssetClick={this._handleSymbolClick} />
- <SelectedAssetBuyOrderProgress />
+ <ConnectedBuyOrderProgressOrPaymentMethod />
<LatestBuyQuoteOrderDetails />
<Container padding="20px" width="100%">
<SelectedAssetBuyOrderStateButtons />
</Container>
</Flex>
<SlidingPanel
- title="Select Token"
animationState={this.state.tokenSelectionPanelAnimationState}
onClose={this._handlePanelClose}
>
<AvailableERC20TokenSelector onTokenSelect={this._handlePanelClose} />
</SlidingPanel>
+ <CurrentStandardSlidingPanel />
</Container>
</Container>
</React.Fragment>
diff --git a/packages/instant/src/components/zero_ex_instant_overlay.tsx b/packages/instant/src/components/zero_ex_instant_overlay.tsx
index 10438ab7a..2856ea3e3 100644
--- a/packages/instant/src/components/zero_ex_instant_overlay.tsx
+++ b/packages/instant/src/components/zero_ex_instant_overlay.tsx
@@ -1,12 +1,12 @@
import * as React from 'react';
+import { ZeroExInstantContainer } from '../components/zero_ex_instant_container';
import { ColorOption } from '../style/theme';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
import { Icon } from './ui/icon';
import { Overlay } from './ui/overlay';
-import { ZeroExInstantContainer } from './zero_ex_instant_container';
import { ZeroExInstantProvider, ZeroExInstantProviderProps } from './zero_ex_instant_provider';
export interface ZeroExInstantOverlayProps extends ZeroExInstantProviderProps {
diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx
index e64579518..18e71edb6 100644
--- a/packages/instant/src/components/zero_ex_instant_provider.tsx
+++ b/packages/instant/src/components/zero_ex_instant_provider.tsx
@@ -91,12 +91,13 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
}
public componentDidMount(): void {
const state = this._store.getState();
+ const dispatch = this._store.dispatch;
// tslint:disable-next-line:no-floating-promises
- asyncData.fetchEthPriceAndDispatchToStore(this._store);
+ asyncData.fetchEthPriceAndDispatchToStore(dispatch);
// fetch available assets if none are specified
if (_.isUndefined(state.availableAssets)) {
// tslint:disable-next-line:no-floating-promises
- asyncData.fetchAvailableAssetDatasAndDispatchToStore(this._store);
+ asyncData.fetchAvailableAssetDatasAndDispatchToStore(state, dispatch);
}
if (state.providerState.account.state !== AccountState.None) {
this._accountUpdateHeartbeat = generateAccountHeartbeater({
@@ -112,7 +113,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
});
this._buyQuoteHeartbeat.start(BUY_QUOTE_UPDATE_INTERVAL_TIME_MS);
// tslint:disable-next-line:no-floating-promises
- asyncData.fetchCurrentBuyQuoteAndDispatchToStore({ store: this._store, shouldSetPending: true });
+ asyncData.fetchCurrentBuyQuoteAndDispatchToStore(state, dispatch, true);
// warm up the gas price estimator cache just in case we can't
// grab the gas price estimate when submitting the transaction
// tslint:disable-next-line:no-floating-promises