aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrancesco Agosti <francesco.agosti93@gmail.com>2018-11-02 04:57:29 +0800
committerGitHub <noreply@github.com>2018-11-02 04:57:29 +0800
commit0955feb0234bc90b7dcf5ad3a308570c9fa5d490 (patch)
tree623c55dddb6326cf8998619da25a82c48b87f876
parent44a34ee541f2c350b65005a1430072ed90fbc790 (diff)
parent935e5da78e14f78685ad9270c19f473fa193d542 (diff)
downloaddexon-0x-contracts-0955feb0234bc90b7dcf5ad3a308570c9fa5d490.tar
dexon-0x-contracts-0955feb0234bc90b7dcf5ad3a308570c9fa5d490.tar.gz
dexon-0x-contracts-0955feb0234bc90b7dcf5ad3a308570c9fa5d490.tar.bz2
dexon-0x-contracts-0955feb0234bc90b7dcf5ad3a308570c9fa5d490.tar.lz
dexon-0x-contracts-0955feb0234bc90b7dcf5ad3a308570c9fa5d490.tar.xz
dexon-0x-contracts-0955feb0234bc90b7dcf5ad3a308570c9fa5d490.tar.zst
dexon-0x-contracts-0955feb0234bc90b7dcf5ad3a308570c9fa5d490.zip
Merge pull request #1201 from 0xProject/feature/instant/basic-token-modal
[instant] Add `Select Token` mode, animation abstractions, and basic token selection panel
-rw-r--r--packages/instant/src/components/animations/position_animation.tsx80
-rw-r--r--packages/instant/src/components/animations/slide_animation.tsx23
-rw-r--r--packages/instant/src/components/animations/slide_animations.tsx58
-rw-r--r--packages/instant/src/components/buy_order_state_buttons.tsx11
-rw-r--r--packages/instant/src/components/erc20_asset_amount_input.tsx70
-rw-r--r--packages/instant/src/components/instant_heading.tsx28
-rw-r--r--packages/instant/src/components/placing_order_button.tsx5
-rw-r--r--packages/instant/src/components/secondary_button.tsx3
-rw-r--r--packages/instant/src/components/sliding_error.tsx32
-rw-r--r--packages/instant/src/components/sliding_panel.tsx59
-rw-r--r--packages/instant/src/components/ui/container.tsx4
-rw-r--r--packages/instant/src/components/ui/flex.tsx4
-rw-r--r--packages/instant/src/components/ui/icon.tsx28
-rw-r--r--packages/instant/src/components/ui/index.ts10
-rw-r--r--packages/instant/src/components/zero_ex_instant_container.tsx73
-rw-r--r--packages/instant/src/containers/latest_error.tsx9
-rw-r--r--packages/instant/src/containers/selected_asset_instant_heading.ts6
-rw-r--r--packages/instant/src/containers/selected_erc20_asset_amount_input.ts1
-rw-r--r--packages/instant/src/style/z_index.ts5
19 files changed, 367 insertions, 142 deletions
diff --git a/packages/instant/src/components/animations/position_animation.tsx b/packages/instant/src/components/animations/position_animation.tsx
new file mode 100644
index 000000000..4bb21befb
--- /dev/null
+++ b/packages/instant/src/components/animations/position_animation.tsx
@@ -0,0 +1,80 @@
+import { Keyframes } from 'styled-components';
+
+import { css, keyframes, styled } from '../../style/theme';
+
+export interface TransitionInfo {
+ from: string;
+ to: string;
+}
+
+const generateTransitionInfoCss = (
+ key: keyof TransitionInfo,
+ top?: TransitionInfo,
+ bottom?: TransitionInfo,
+ left?: TransitionInfo,
+ right?: TransitionInfo,
+): string => {
+ const topStringIfExists = top ? `top: ${top[key]};` : '';
+ const bottomStringIfExists = bottom ? `bottom: ${bottom[key]};` : '';
+ const leftStringIfExists = left ? `left: ${left[key]};` : '';
+ const rightStringIfExists = right ? `right: ${right[key]};` : '';
+ return `
+ ${topStringIfExists}
+ ${bottomStringIfExists}
+ ${leftStringIfExists}
+ ${rightStringIfExists}
+ `;
+};
+
+const slideKeyframeGenerator = (
+ position: string,
+ top?: TransitionInfo,
+ bottom?: TransitionInfo,
+ left?: TransitionInfo,
+ right?: TransitionInfo,
+) => keyframes`
+ from {
+ position: ${position};
+ ${generateTransitionInfoCss('from', top, bottom, left, right)}
+ }
+
+ to {
+ position: ${position};
+ ${generateTransitionInfoCss('to', top, bottom, left, right)}
+ }
+`;
+
+export interface PositionAnimationSettings {
+ top?: TransitionInfo;
+ bottom?: TransitionInfo;
+ left?: TransitionInfo;
+ right?: TransitionInfo;
+ timingFunction: string;
+ duration?: string;
+}
+
+export interface PositionAnimationProps extends PositionAnimationSettings {
+ position: string;
+}
+
+export const PositionAnimation =
+ styled.div <
+ PositionAnimationProps >
+ `
+ animation-name: ${props =>
+ css`
+ ${slideKeyframeGenerator(props.position, props.top, props.bottom, props.left, props.right)};
+ `};
+ animation-duration: ${props => props.duration || '0.3s'};
+ animation-timing-function: ${props => props.timingFunction};
+ animation-delay: 0s;
+ animation-iteration-count: 1;
+ animation-fill-mode: forwards;
+ position: ${props => props.position};
+ height: 100%;
+ width: 100%;
+`;
+
+PositionAnimation.defaultProps = {
+ position: 'relative',
+};
diff --git a/packages/instant/src/components/animations/slide_animation.tsx b/packages/instant/src/components/animations/slide_animation.tsx
new file mode 100644
index 000000000..66a314c7f
--- /dev/null
+++ b/packages/instant/src/components/animations/slide_animation.tsx
@@ -0,0 +1,23 @@
+import * as React from 'react';
+
+import { PositionAnimation, PositionAnimationSettings } from './position_animation';
+
+export type SlideAnimationState = 'slidIn' | 'slidOut' | 'none';
+export interface SlideAnimationProps {
+ position: string;
+ animationState: SlideAnimationState;
+ slideInSettings: PositionAnimationSettings;
+ slideOutSettings: PositionAnimationSettings;
+}
+
+export const SlideAnimation: React.StatelessComponent<SlideAnimationProps> = props => {
+ if (props.animationState === 'none') {
+ return <React.Fragment>{props.children}</React.Fragment>;
+ }
+ const propsToUse = props.animationState === 'slidIn' ? props.slideInSettings : props.slideOutSettings;
+ return (
+ <PositionAnimation position={props.position} {...propsToUse}>
+ {props.children}
+ </PositionAnimation>
+ );
+};
diff --git a/packages/instant/src/components/animations/slide_animations.tsx b/packages/instant/src/components/animations/slide_animations.tsx
deleted file mode 100644
index 84280372b..000000000
--- a/packages/instant/src/components/animations/slide_animations.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import * as React from 'react';
-import { Keyframes } from 'styled-components';
-
-import { css, keyframes, styled } from '../../style/theme';
-
-const slideKeyframeGenerator = (fromY: string, toY: string) => keyframes`
- from {
- position: relative;
- top: ${fromY};
- }
-
- to {
- position: relative;
- top: ${toY};
- }
-`;
-
-export interface SlideAnimationProps {
- keyframes: Keyframes;
- animationType: string;
- animationDirection?: string;
-}
-
-export const SlideAnimation =
- styled.div <
- SlideAnimationProps >
- `
- animation-name: ${props =>
- css`
- ${props.keyframes};
- `};
- animation-duration: 0.3s;
- animation-timing-function: ${props => props.animationType};
- animation-delay: 0s;
- animation-iteration-count: 1;
- animation-fill-mode: ${props => props.animationDirection || 'none'};
- position: relative;
-`;
-
-export interface SlideAnimationComponentProps {
- downY: string;
-}
-
-export const SlideUpAnimation: React.StatelessComponent<SlideAnimationComponentProps> = props => (
- <SlideAnimation animationType="ease-in" keyframes={slideKeyframeGenerator(props.downY, '0px')}>
- {props.children}
- </SlideAnimation>
-);
-
-export const SlideDownAnimation: React.StatelessComponent<SlideAnimationComponentProps> = props => (
- <SlideAnimation
- animationDirection="forwards"
- animationType="cubic-bezier(0.25, 0.1, 0.25, 1)"
- keyframes={slideKeyframeGenerator('0px', props.downY)}
- >
- {props.children}
- </SlideAnimation>
-);
diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx
index d01e9ff57..51e06a284 100644
--- a/packages/instant/src/components/buy_order_state_buttons.tsx
+++ b/packages/instant/src/components/buy_order_state_buttons.tsx
@@ -1,16 +1,13 @@
import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
import * as React from 'react';
-import { BuyButton } from '../components/buy_button';
-import { SecondaryButton } from '../components/secondary_button';
-import { Flex } from '../components/ui/flex';
-
-import { PlacingOrderButton } from '../components/placing_order_button';
import { ColorOption } from '../style/theme';
import { OrderProcessState, ZeroExInstantError } from '../types';
-import { Button } from './ui/button';
-import { Text } from './ui/text';
+import { BuyButton } from './buy_button';
+import { PlacingOrderButton } from './placing_order_button';
+import { SecondaryButton } from './secondary_button';
+import { Button, Flex, Text } from './ui';
export interface BuyOrderStateButtonProps {
buyQuote?: BuyQuote;
diff --git a/packages/instant/src/components/erc20_asset_amount_input.tsx b/packages/instant/src/components/erc20_asset_amount_input.tsx
index 5abb34c2f..b1fec6405 100644
--- a/packages/instant/src/components/erc20_asset_amount_input.tsx
+++ b/packages/instant/src/components/erc20_asset_amount_input.tsx
@@ -8,13 +8,14 @@ import { BigNumberInput } from '../util/big_number_input';
import { util } from '../util/util';
import { ScalingAmountInput } from './scaling_amount_input';
-import { Container, Text } from './ui';
+import { Container, Flex, Icon, Text } from './ui';
// Asset amounts only apply to ERC20 assets
export interface ERC20AssetAmountInputProps {
asset?: ERC20Asset;
value?: BigNumberInput;
onChange: (value?: BigNumberInput, asset?: ERC20Asset) => void;
+ onSelectAssetClick?: (asset?: ERC20Asset) => void;
startingFontSizePx: number;
fontColor?: ColorOption;
isDisabled: boolean;
@@ -36,10 +37,18 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput
};
}
public render(): React.ReactNode {
- const { asset, onChange, ...rest } = this.props;
- const amountBorderBottom = this.props.isDisabled ? '' : `1px solid ${transparentWhite}`;
+ const { asset } = this.props;
return (
<Container whiteSpace="nowrap">
+ {_.isUndefined(asset) ? this._renderBackupContent() : this._renderContentForAsset(asset)}
+ </Container>
+ );
+ }
+ private readonly _renderContentForAsset = (asset: ERC20Asset): React.ReactNode => {
+ const { onChange, ...rest } = this.props;
+ const amountBorderBottom = this.props.isDisabled ? '' : `1px solid ${transparentWhite}`;
+ return (
+ <React.Fragment>
<Container borderBottom={amountBorderBottom} display="inline-block">
<ScalingAmountInput
{...rest}
@@ -49,18 +58,50 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput
onFontSizeChange={this._handleFontSizeChange}
/>
</Container>
- <Container display="inline-flex" marginLeft="10px" title={assetUtils.bestNameForAsset(asset)}>
- <Text
- fontSize={`${this.state.currentFontSizePx}px`}
- fontColor={ColorOption.white}
- textTransform="uppercase"
- >
- {assetUtils.formattedSymbolForAsset(asset)}
- </Text>
+ <Container
+ cursor="pointer"
+ display="inline-block"
+ marginLeft="8px"
+ title={assetUtils.bestNameForAsset(asset, undefined)}
+ >
+ <Flex inline={true}>
+ <Text
+ fontSize={`${this.state.currentFontSizePx}px`}
+ fontColor={ColorOption.white}
+ textTransform="uppercase"
+ onClick={this._handleSymbolClick}
+ >
+ {assetUtils.formattedSymbolForAsset(asset)}
+ </Text>
+ {this._renderChevronIcon()}
+ </Flex>
</Container>
+ </React.Fragment>
+ );
+ };
+ private readonly _renderBackupContent = (): React.ReactNode => {
+ return (
+ <Flex>
+ <Text
+ fontSize="30px"
+ fontColor={ColorOption.white}
+ opacity={0.7}
+ fontWeight="500"
+ onClick={this._handleSymbolClick}
+ >
+ Select Token
+ </Text>
+ {this._renderChevronIcon()}
+ </Flex>
+ );
+ };
+ private readonly _renderChevronIcon = (): React.ReactNode => {
+ return (
+ <Container marginLeft="5px" onClick={this._handleSymbolClick}>
+ <Icon icon="chevron" width={12} />
</Container>
);
- }
+ };
private readonly _handleChange = (value?: BigNumberInput): void => {
this.props.onChange(value, this.props.asset);
};
@@ -69,6 +110,11 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput
currentFontSizePx: fontSizePx,
});
};
+ private readonly _handleSymbolClick = () => {
+ if (this.props.onSelectAssetClick) {
+ this.props.onSelectAssetClick(this.props.asset);
+ }
+ };
// For assets with symbols of different length,
// start scaling the input at different character lengths
private readonly _textLengthThresholdForAsset = (asset?: ERC20Asset): number => {
diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx
index 1ef276ff3..19c08db70 100644
--- a/packages/instant/src/components/instant_heading.tsx
+++ b/packages/instant/src/components/instant_heading.tsx
@@ -4,13 +4,11 @@ import * as React from 'react';
import { SelectedERC20AssetAmountInput } from '../containers/selected_erc20_asset_amount_input';
import { ColorOption } from '../style/theme';
-import { AsyncProcessState, OrderProcessState, OrderState } from '../types';
+import { AsyncProcessState, ERC20Asset, OrderProcessState, OrderState } from '../types';
import { format } from '../util/format';
import { AmountPlaceholder } from './amount_placeholder';
-import { Container, Flex, Text } from './ui';
-import { Icon } from './ui/icon';
-import { Spinner } from './ui/spinner';
+import { Container, Flex, Icon, Spinner, Text } from './ui';
export interface InstantHeadingProps {
selectedAssetAmount?: BigNumber;
@@ -18,12 +16,13 @@ export interface InstantHeadingProps {
ethUsdPrice?: BigNumber;
quoteRequestState: AsyncProcessState;
buyOrderState: OrderState;
+ onSelectAssetClick?: (asset?: ERC20Asset) => void;
}
const PLACEHOLDER_COLOR = ColorOption.white;
const ICON_WIDTH = 34;
const ICON_HEIGHT = 34;
-const ICON_COLOR = ColorOption.white;
+const ICON_COLOR = 'white';
export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
public render(): React.ReactNode {
@@ -49,7 +48,10 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
</Container>
<Flex direction="row" justify="space-between">
<Flex height="60px">
- <SelectedERC20AssetAmountInput startingFontSizePx={38} />
+ <SelectedERC20AssetAmountInput
+ startingFontSizePx={38}
+ onSelectAssetClick={this.props.onSelectAssetClick}
+ />
</Flex>
<Flex direction="column" justify="space-between">
{iconOrAmounts}
@@ -62,8 +64,8 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
private _renderAmountsSection(): React.ReactNode {
return (
<Container>
- <Container marginBottom="5px">{this._placeholderOrAmount(this._ethAmount)}</Container>
- <Container opacity={0.7}>{this._placeholderOrAmount(this._dollarAmount)}</Container>
+ <Container marginBottom="5px">{this._renderPlaceholderOrAmount(this._renderEthAmount)}</Container>
+ <Container opacity={0.7}>{this._renderPlaceholderOrAmount(this._renderDollarAmount)}</Container>
</Container>
);
}
@@ -72,11 +74,11 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
const processState = this.props.buyOrderState.processState;
if (processState === OrderProcessState.FAILURE) {
- return <Icon icon={'failed'} width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
+ return <Icon icon="failed" width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
} else if (processState === OrderProcessState.PROCESSING) {
return <Spinner widthPx={ICON_HEIGHT} heightPx={ICON_HEIGHT} />;
} else if (processState === OrderProcessState.SUCCESS) {
- return <Icon icon={'success'} width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
+ return <Icon icon="success" width={ICON_WIDTH} height={ICON_HEIGHT} color={ICON_COLOR} />;
}
return undefined;
}
@@ -94,7 +96,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
return 'I want to buy';
}
- private _placeholderOrAmount(amountFunction: () => React.ReactNode): React.ReactNode {
+ private _renderPlaceholderOrAmount(amountFunction: () => React.ReactNode): React.ReactNode {
if (this.props.quoteRequestState === AsyncProcessState.PENDING) {
return <AmountPlaceholder isPulsating={true} color={PLACEHOLDER_COLOR} />;
}
@@ -104,7 +106,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
return amountFunction();
}
- private readonly _ethAmount = (): React.ReactNode => {
+ private readonly _renderEthAmount = (): React.ReactNode => {
return (
<Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}>
{format.ethBaseAmount(
@@ -116,7 +118,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
);
};
- private readonly _dollarAmount = (): React.ReactNode => {
+ private readonly _renderDollarAmount = (): React.ReactNode => {
return (
<Text fontSize="16px" fontColor={ColorOption.white}>
{format.ethBaseAmountInUsd(
diff --git a/packages/instant/src/components/placing_order_button.tsx b/packages/instant/src/components/placing_order_button.tsx
index 4232e6c22..e5a6371e6 100644
--- a/packages/instant/src/components/placing_order_button.tsx
+++ b/packages/instant/src/components/placing_order_button.tsx
@@ -2,10 +2,7 @@ import * as React from 'react';
import { ColorOption } from '../style/theme';
-import { Button } from './ui/button';
-import { Container } from './ui/container';
-import { Spinner } from './ui/spinner';
-import { Text } from './ui/text';
+import { Button, Container, Spinner, Text } from './ui';
export const PlacingOrderButton: React.StatelessComponent<{}> = props => (
<Button isDisabled={true} width="100%">
diff --git a/packages/instant/src/components/secondary_button.tsx b/packages/instant/src/components/secondary_button.tsx
index 583058b5b..ca698c77a 100644
--- a/packages/instant/src/components/secondary_button.tsx
+++ b/packages/instant/src/components/secondary_button.tsx
@@ -3,8 +3,7 @@ import * as React from 'react';
import { ColorOption } from '../style/theme';
-import { Button, ButtonProps } from './ui/button';
-import { Text } from './ui/text';
+import { Button, ButtonProps, Text } from './ui';
export interface SecondaryButtonProps extends ButtonProps {}
diff --git a/packages/instant/src/components/sliding_error.tsx b/packages/instant/src/components/sliding_error.tsx
index cc9abb7dd..17643fd7d 100644
--- a/packages/instant/src/components/sliding_error.tsx
+++ b/packages/instant/src/components/sliding_error.tsx
@@ -2,7 +2,8 @@ import * as React from 'react';
import { ColorOption } from '../style/theme';
-import { SlideDownAnimation, SlideUpAnimation } from './animations/slide_animations';
+import { PositionAnimationSettings } from './animations/position_animation';
+import { SlideAnimation, SlideAnimationState } from './animations/slide_animation';
import { Container, Flex, Text } from './ui';
@@ -31,16 +32,33 @@ export const Error: React.StatelessComponent<ErrorProps> = props => (
</Container>
);
-export type SlidingDirection = 'up' | 'down';
export interface SlidingErrorProps extends ErrorProps {
- direction: SlidingDirection;
+ animationState: SlideAnimationState;
}
export const SlidingError: React.StatelessComponent<SlidingErrorProps> = props => {
- const AnimationComponent = props.direction === 'up' ? SlideUpAnimation : SlideDownAnimation;
-
+ const slideAmount = '120px';
+ const slideUpSettings: PositionAnimationSettings = {
+ timingFunction: 'ease-in',
+ top: {
+ from: slideAmount,
+ to: '0px',
+ },
+ };
+ const slideDownSettings: PositionAnimationSettings = {
+ timingFunction: 'cubic-bezier(0.25, 0.1, 0.25, 1)',
+ top: {
+ from: '0px',
+ to: slideAmount,
+ },
+ };
return (
- <AnimationComponent downY="120px">
+ <SlideAnimation
+ position="relative"
+ slideInSettings={slideUpSettings}
+ slideOutSettings={slideDownSettings}
+ animationState={props.animationState}
+ >
<Error icon={props.icon} message={props.message} />
- </AnimationComponent>
+ </SlideAnimation>
);
};
diff --git a/packages/instant/src/components/sliding_panel.tsx b/packages/instant/src/components/sliding_panel.tsx
new file mode 100644
index 000000000..9219ad1f1
--- /dev/null
+++ b/packages/instant/src/components/sliding_panel.tsx
@@ -0,0 +1,59 @@
+import * as React from 'react';
+
+import { ColorOption } from '../style/theme';
+import { zIndex } from '../style/z_index';
+
+import { PositionAnimationSettings } from './animations/position_animation';
+import { SlideAnimation, SlideAnimationState } from './animations/slide_animation';
+import { Button, Container, Text } from './ui';
+
+export interface PanelProps {
+ onClose?: () => void;
+}
+
+export const Panel: React.StatelessComponent<PanelProps> = ({ children, onClose }) => (
+ <Container backgroundColor={ColorOption.white} width="100%" height="100%" zIndex={zIndex.panel}>
+ <Button onClick={onClose}>
+ <Text fontColor={ColorOption.white}>Close </Text>
+ </Button>
+ {children}
+ </Container>
+);
+
+export interface SlidingPanelProps extends PanelProps {
+ animationState: SlideAnimationState;
+}
+
+export const SlidingPanel: React.StatelessComponent<SlidingPanelProps> = props => {
+ if (props.animationState === 'none') {
+ return null;
+ }
+ const { animationState, ...rest } = props;
+ const slideAmount = '100%';
+ const slideUpSettings: PositionAnimationSettings = {
+ duration: '0.3s',
+ timingFunction: 'ease-in-out',
+ top: {
+ from: slideAmount,
+ to: '0px',
+ },
+ };
+ const slideDownSettings: PositionAnimationSettings = {
+ duration: '0.3s',
+ timingFunction: 'ease-out',
+ top: {
+ from: '0px',
+ to: slideAmount,
+ },
+ };
+ return (
+ <SlideAnimation
+ position="absolute"
+ slideInSettings={slideUpSettings}
+ slideOutSettings={slideDownSettings}
+ animationState={animationState}
+ >
+ <Panel {...rest} />
+ </SlideAnimation>
+ );
+};
diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx
index 76b570de7..7b8642761 100644
--- a/packages/instant/src/components/ui/container.tsx
+++ b/packages/instant/src/components/ui/container.tsx
@@ -28,6 +28,8 @@ export interface ContainerProps {
zIndex?: number;
whiteSpace?: string;
opacity?: number;
+ cursor?: string;
+ overflow?: string;
}
export const Container =
@@ -57,6 +59,8 @@ export const Container =
${props => cssRuleIfExists(props, 'z-index')}
${props => cssRuleIfExists(props, 'white-space')}
${props => cssRuleIfExists(props, 'opacity')}
+ ${props => cssRuleIfExists(props, 'cursor')}
+ ${props => cssRuleIfExists(props, 'overflow')}
${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')};
background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')};
diff --git a/packages/instant/src/components/ui/flex.tsx b/packages/instant/src/components/ui/flex.tsx
index 5fa3fc95b..29c6511bb 100644
--- a/packages/instant/src/components/ui/flex.tsx
+++ b/packages/instant/src/components/ui/flex.tsx
@@ -9,14 +9,14 @@ export interface FlexProps {
width?: string;
height?: string;
backgroundColor?: ColorOption;
- className?: string;
+ inline?: boolean;
}
export const Flex =
styled.div <
FlexProps >
`
- display: flex;
+ display: ${props => (props.inline ? 'inline-flex' : 'flex')};
flex-direction: ${props => props.direction};
flex-wrap: ${props => props.flexWrap};
justify-content: ${props => props.justify};
diff --git a/packages/instant/src/components/ui/icon.tsx b/packages/instant/src/components/ui/icon.tsx
index 7373c3acd..61b382760 100644
--- a/packages/instant/src/components/ui/icon.tsx
+++ b/packages/instant/src/components/ui/icon.tsx
@@ -1,17 +1,21 @@
import * as React from 'react';
-import { ColorOption } from '../../style/theme';
-
type svgRule = 'evenodd' | 'nonzero' | 'inherit';
interface IconInfo {
viewBox: string;
+ path: string;
fillRule?: svgRule;
clipRule?: svgRule;
- path: string;
+ stroke?: string;
+ strokeOpacity?: number;
+ strokeWidth?: number;
+ strokeLinecap?: 'butt' | 'round' | 'square' | 'inherit';
+ strokeLinejoin?: 'miter' | 'round' | 'bevel' | 'inherit';
}
interface IconInfoMapping {
failed: IconInfo;
success: IconInfo;
+ chevron: IconInfo;
}
const ICONS: IconInfoMapping = {
failed: {
@@ -28,12 +32,21 @@ const ICONS: IconInfoMapping = {
path:
'M17 34C26.3887 34 34 26.3888 34 17C34 7.61121 26.3887 0 17 0C7.61133 0 0 7.61121 0 17C0 26.3888 7.61133 34 17 34ZM25.7539 13.0977C26.2969 12.4718 26.2295 11.5244 25.6035 10.9817C24.9775 10.439 24.0303 10.5063 23.4878 11.1323L15.731 20.0771L12.3936 16.7438C11.8071 16.1583 10.8574 16.1589 10.272 16.7451C9.68652 17.3313 9.6875 18.281 10.2734 18.8665L14.75 23.3373L15.8887 24.4746L16.9434 23.2587L25.7539 13.0977Z',
},
+ chevron: {
+ viewBox: '0 0 12 7',
+ path: 'M11 1L6 6L1 1',
+ stroke: 'white',
+ strokeOpacity: 0.5,
+ strokeWidth: 1.5,
+ strokeLinecap: 'round',
+ strokeLinejoin: 'round',
+ },
};
export interface IconProps {
width: number;
- height: number;
- color: ColorOption;
+ height?: number;
+ color?: string;
icon: keyof IconInfoMapping;
}
export const Icon: React.SFC<IconProps> = props => {
@@ -52,6 +65,11 @@ export const Icon: React.SFC<IconProps> = props => {
fill={props.color}
fillRule={iconInfo.fillRule || 'nonzero'}
clipRule={iconInfo.clipRule || 'nonzero'}
+ stroke={iconInfo.stroke}
+ strokeOpacity={iconInfo.strokeOpacity}
+ strokeWidth={iconInfo.strokeWidth}
+ strokeLinecap={iconInfo.strokeLinecap}
+ strokeLinejoin={iconInfo.strokeLinejoin}
/>
</svg>
);
diff --git a/packages/instant/src/components/ui/index.ts b/packages/instant/src/components/ui/index.ts
index bf5f6c700..28f76c262 100644
--- a/packages/instant/src/components/ui/index.ts
+++ b/packages/instant/src/components/ui/index.ts
@@ -1,5 +1,7 @@
export { Text, Title } from './text';
-export { Button } from './button';
-export { Flex } from './flex';
-export { Container } from './container';
-export { Input } from './input';
+export { Button, ButtonProps } from './button';
+export { Flex, FlexProps } from './flex';
+export { Container, ContainerProps } from './container';
+export { Input, InputProps } from './input';
+export { Icon, IconProps } from './icon';
+export { Spinner, SpinnerProps } from './spinner';
diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx
index ff19351ff..009ad9b2a 100644
--- a/packages/instant/src/components/zero_ex_instant_container.tsx
+++ b/packages/instant/src/components/zero_ex_instant_container.tsx
@@ -4,32 +4,61 @@ import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order
import { LatestError } from '../containers/latest_error';
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 { SlideAnimationState } from './animations/slide_animation';
+import { SlidingPanel } from './sliding_panel';
import { Container, Flex } from './ui';
export interface ZeroExInstantContainerProps {}
+export interface ZeroExInstantContainerState {
+ tokenSelectionPanelAnimationState: SlideAnimationState;
+}
-export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantContainerProps> = props => (
- <Container width="350px">
- <Container zIndex={1} position="relative">
- <LatestError />
- </Container>
- <Container
- zIndex={2}
- position="relative"
- backgroundColor={ColorOption.white}
- borderRadius="3px"
- hasBoxShadow={true}
- >
- <Flex direction="column" justify="flex-start">
- <SelectedAssetInstantHeading />
- <LatestBuyQuoteOrderDetails />
- <Container padding="20px" width="100%">
- <SelectedAssetBuyOrderStateButtons />
+export class ZeroExInstantContainer extends React.Component<ZeroExInstantContainerProps, ZeroExInstantContainerState> {
+ public state = {
+ tokenSelectionPanelAnimationState: 'none' as SlideAnimationState,
+ };
+ public render(): React.ReactNode {
+ return (
+ <Container width="350px" position="relative">
+ <Container zIndex={zIndex.errorPopup} position="relative">
+ <LatestError />
+ </Container>
+ <Container
+ zIndex={zIndex.mainContainer}
+ position="relative"
+ backgroundColor={ColorOption.white}
+ borderRadius="3px"
+ hasBoxShadow={true}
+ overflow="hidden"
+ >
+ <Flex direction="column" justify="flex-start">
+ <SelectedAssetInstantHeading onSelectAssetClick={this._handleSymbolClick} />
+ <LatestBuyQuoteOrderDetails />
+ <Container padding="20px" width="100%">
+ <SelectedAssetBuyOrderStateButtons />
+ </Container>
+ </Flex>
+ <SlidingPanel
+ animationState={this.state.tokenSelectionPanelAnimationState}
+ onClose={this._handlePanelClose}
+ >
+ Select Your Token
+ </SlidingPanel>
</Container>
- </Flex>
- </Container>
- </Container>
-);
+ </Container>
+ );
+ }
+ private readonly _handleSymbolClick = (): void => {
+ this.setState({
+ tokenSelectionPanelAnimationState: 'slidIn',
+ });
+ };
+ private readonly _handlePanelClose = (): void => {
+ this.setState({
+ tokenSelectionPanelAnimationState: 'slidOut',
+ });
+ };
+}
diff --git a/packages/instant/src/containers/latest_error.tsx b/packages/instant/src/containers/latest_error.tsx
index 45ca09673..99e55a6c4 100644
--- a/packages/instant/src/containers/latest_error.tsx
+++ b/packages/instant/src/containers/latest_error.tsx
@@ -2,6 +2,7 @@ import * as React from 'react';
import { connect } from 'react-redux';
+import { SlideAnimationState } from '../components/animations/slide_animation';
import { SlidingError } from '../components/sliding_error';
import { State } from '../redux/reducer';
import { Asset, DisplayStatus } from '../types';
@@ -9,26 +10,26 @@ import { Asset, DisplayStatus } from '../types';
export interface LatestErrorComponentProps {
asset?: Asset;
latestErrorMessage?: string;
- slidingDirection: 'down' | 'up';
+ animationState: SlideAnimationState;
}
export const LatestErrorComponent: React.StatelessComponent<LatestErrorComponentProps> = props => {
if (!props.latestErrorMessage) {
return <div />;
}
- return <SlidingError direction={props.slidingDirection} icon="😢" message={props.latestErrorMessage} />;
+ return <SlidingError animationState={props.animationState} icon="😢" message={props.latestErrorMessage} />;
};
interface ConnectedState {
asset?: Asset;
latestErrorMessage?: string;
- slidingDirection: 'down' | 'up';
+ animationState: SlideAnimationState;
}
export interface LatestErrorProps {}
const mapStateToProps = (state: State, _ownProps: LatestErrorProps): ConnectedState => ({
asset: state.selectedAsset,
latestErrorMessage: state.latestErrorMessage,
- slidingDirection: state.latestErrorDisplayStatus === DisplayStatus.Present ? 'up' : 'down',
+ animationState: state.latestErrorDisplayStatus === DisplayStatus.Present ? 'slidIn' : 'slidOut',
});
export const LatestError = connect(mapStateToProps)(LatestErrorComponent);
diff --git a/packages/instant/src/containers/selected_asset_instant_heading.ts b/packages/instant/src/containers/selected_asset_instant_heading.ts
index 6b2a29b07..a407279e6 100644
--- a/packages/instant/src/containers/selected_asset_instant_heading.ts
+++ b/packages/instant/src/containers/selected_asset_instant_heading.ts
@@ -5,11 +5,13 @@ import { connect } from 'react-redux';
import { oc } from 'ts-optchain';
import { State } from '../redux/reducer';
-import { AsyncProcessState, OrderState } from '../types';
+import { AsyncProcessState, ERC20Asset, OrderState } from '../types';
import { InstantHeading } from '../components/instant_heading';
-export interface InstantHeadingProps {}
+export interface InstantHeadingProps {
+ onSelectAssetClick?: (asset?: ERC20Asset) => void;
+}
interface ConnectedState {
selectedAssetAmount?: BigNumber;
diff --git a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
index 4767b15d4..217d603d2 100644
--- a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
+++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
@@ -19,6 +19,7 @@ import { errorFlasher } from '../util/error_flasher';
export interface SelectedERC20AssetAmountInputProps {
fontColor?: ColorOption;
startingFontSizePx: number;
+ onSelectAssetClick?: (asset?: ERC20Asset) => void;
}
interface ConnectedState {
diff --git a/packages/instant/src/style/z_index.ts b/packages/instant/src/style/z_index.ts
new file mode 100644
index 000000000..727a5189d
--- /dev/null
+++ b/packages/instant/src/style/z_index.ts
@@ -0,0 +1,5 @@
+export const zIndex = {
+ errorPopup: 1,
+ mainContainer: 2,
+ panel: 3,
+};