aboutsummaryrefslogtreecommitdiffstats
path: root/packages/instant/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/instant/src/components')
-rw-r--r--packages/instant/src/components/amount_placeholder.tsx32
-rw-r--r--packages/instant/src/components/animations/pulse.tsx15
-rw-r--r--packages/instant/src/components/instant_heading.tsx145
-rw-r--r--packages/instant/src/components/order_details.tsx91
-rw-r--r--packages/instant/src/components/ui/container.tsx2
-rw-r--r--packages/instant/src/components/zero_ex_instant.tsx52
6 files changed, 206 insertions, 131 deletions
diff --git a/packages/instant/src/components/amount_placeholder.tsx b/packages/instant/src/components/amount_placeholder.tsx
new file mode 100644
index 000000000..6ef8f0ac3
--- /dev/null
+++ b/packages/instant/src/components/amount_placeholder.tsx
@@ -0,0 +1,32 @@
+import * as React from 'react';
+
+import { ColorOption } from '../style/theme';
+
+import { Pulse } from './animations/pulse';
+
+import { Text } from './ui';
+
+interface PlainPlaceholder {
+ color: ColorOption;
+}
+const PlainPlaceholder: React.StatelessComponent<PlainPlaceholder> = props => (
+ <Text fontWeight="bold" fontColor={props.color}>
+ &mdash;
+ </Text>
+);
+
+export interface AmountPlaceholderProps {
+ color: ColorOption;
+ isPulsating: boolean;
+}
+export const AmountPlaceholder: React.StatelessComponent<AmountPlaceholderProps> = props => {
+ if (props.isPulsating) {
+ return (
+ <Pulse>
+ <PlainPlaceholder color={props.color} />
+ </Pulse>
+ );
+ } else {
+ return <PlainPlaceholder color={props.color} />;
+ }
+};
diff --git a/packages/instant/src/components/animations/pulse.tsx b/packages/instant/src/components/animations/pulse.tsx
new file mode 100644
index 000000000..01d6ea070
--- /dev/null
+++ b/packages/instant/src/components/animations/pulse.tsx
@@ -0,0 +1,15 @@
+import { keyframes, styled } from '../../style/theme';
+
+const pulsingKeyframes = keyframes`
+ 0%, 100% {
+ opacity: 0.2;
+ }
+ 50% {
+ opacity: 100;
+ }
+`;
+export const Pulse = styled.div`
+ animation-name: ${pulsingKeyframes}
+ animation-duration: 2s;
+ animation-iteration-count: infinite;
+`;
diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx
index 6fc5a0963..5d39e4048 100644
--- a/packages/instant/src/components/instant_heading.tsx
+++ b/packages/instant/src/components/instant_heading.tsx
@@ -7,95 +7,90 @@ import { ColorOption } from '../style/theme';
import { AsyncProcessState } from '../types';
import { format } from '../util/format';
+import { AmountPlaceholder } from './amount_placeholder';
import { Container, Flex, Text } from './ui';
export interface InstantHeadingProps {
selectedAssetAmount?: BigNumber;
totalEthBaseAmount?: BigNumber;
ethUsdPrice?: BigNumber;
- quoteState: AsyncProcessState;
+ quoteRequestState: AsyncProcessState;
buyOrderState: AsyncProcessState;
}
-const Placeholder = () => (
- <Text fontWeight="bold" fontColor={ColorOption.white}>
- &mdash;
- </Text>
-);
-const displaytotalEthBaseAmount = ({
- selectedAssetAmount,
- totalEthBaseAmount,
-}: InstantHeadingProps): React.ReactNode => {
- if (_.isUndefined(selectedAssetAmount)) {
- return '0 ETH';
+const placeholderColor = ColorOption.white;
+export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
+ public render(): React.ReactNode {
+ return (
+ <Container
+ backgroundColor={ColorOption.primaryColor}
+ padding="20px"
+ width="100%"
+ borderRadius="3px 3px 0px 0px"
+ >
+ <Container marginBottom="5px">
+ <Text
+ letterSpacing="1px"
+ fontColor={ColorOption.white}
+ opacity={0.7}
+ fontWeight={500}
+ textTransform="uppercase"
+ fontSize="12px"
+ >
+ {this._renderTopText()}
+ </Text>
+ </Container>
+ <Flex direction="row" justify="space-between">
+ <SelectedAssetAmountInput fontSize="45px" />
+ <Flex direction="column" justify="space-between">
+ <Container marginBottom="5px">{this._placeholderOrAmount(this._ethAmount)}</Container>
+ <Container opacity={0.7}>{this._placeholderOrAmount(this._dollarAmount)}</Container>
+ </Flex>
+ </Flex>
+ </Container>
+ );
}
- return format.ethBaseAmount(totalEthBaseAmount, 4, <Placeholder />);
-};
-const displayUsdAmount = ({
- totalEthBaseAmount,
- selectedAssetAmount,
- ethUsdPrice,
-}: InstantHeadingProps): React.ReactNode => {
- if (_.isUndefined(selectedAssetAmount)) {
- return '$0.00';
- }
- return format.ethBaseAmountInUsd(totalEthBaseAmount, ethUsdPrice, 2, <Placeholder />);
-};
+ private _renderTopText(): React.ReactNode {
+ if (this.props.buyOrderState === AsyncProcessState.FAILURE) {
+ return 'Order failed';
+ }
-const loadingOrAmount = (quoteState: AsyncProcessState, amount: React.ReactNode): React.ReactNode => {
- if (quoteState === AsyncProcessState.PENDING) {
- return (
- <Text fontWeight="bold" fontColor={ColorOption.white}>
- &hellip;loading
- </Text>
- );
- } else {
- return amount;
+ return 'I want to buy';
}
-};
-const topText = (buyOrderState: AsyncProcessState): string => {
- if (buyOrderState === AsyncProcessState.FAILURE) {
- return 'Order failed';
+ private _placeholderOrAmount(amountFunction: () => React.ReactNode): React.ReactNode {
+ if (this.props.quoteRequestState === AsyncProcessState.PENDING) {
+ return <AmountPlaceholder isPulsating={true} color={placeholderColor} />;
+ }
+ if (_.isUndefined(this.props.selectedAssetAmount)) {
+ return <AmountPlaceholder isPulsating={false} color={placeholderColor} />;
+ }
+ return amountFunction();
}
- return 'I want to buy';
-};
+ private readonly _ethAmount = (): React.ReactNode => {
+ return (
+ <Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}>
+ {format.ethBaseAmount(
+ this.props.totalEthBaseAmount,
+ 4,
+ <AmountPlaceholder isPulsating={false} color={placeholderColor} />,
+ )}
+ </Text>
+ );
+ };
-export const InstantHeading: React.StatelessComponent<InstantHeadingProps> = props => {
- return (
- <Container
- backgroundColor={ColorOption.primaryColor}
- padding="20px"
- width="100%"
- borderRadius="3px 3px 0px 0px"
- >
- <Container marginBottom="5px">
- <Text
- letterSpacing="1px"
- fontColor={ColorOption.white}
- opacity={0.7}
- fontWeight={500}
- textTransform="uppercase"
- fontSize="12px"
- >
- {topText(props.buyOrderState)}
- </Text>
- </Container>
- <Flex direction="row" justify="space-between">
- <SelectedAssetAmountInput fontSize="45px" />
- <Flex direction="column" justify="space-between">
- <Container marginBottom="5px">
- <Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}>
- {loadingOrAmount(props.quoteState, displaytotalEthBaseAmount(props))}
- </Text>
- </Container>
- <Text fontSize="16px" fontColor={ColorOption.white} opacity={0.7}>
- {loadingOrAmount(props.quoteState, displayUsdAmount(props))}
- </Text>
- </Flex>
- </Flex>
- </Container>
- );
-};
+ private readonly _dollarAmount = (): React.ReactNode => {
+ return (
+ <Text fontSize="16px" fontColor={ColorOption.white}>
+ {format.ethBaseAmountInUsd(
+ this.props.totalEthBaseAmount,
+ this.props.ethUsdPrice,
+ 2,
+ <AmountPlaceholder isPulsating={false} color={ColorOption.white} />,
+ )}
+ </Text>
+ );
+ };
+}
diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx
index ad4a87714..704009d89 100644
--- a/packages/instant/src/components/order_details.tsx
+++ b/packages/instant/src/components/order_details.tsx
@@ -7,13 +7,14 @@ import { oc } from 'ts-optchain';
import { ColorOption } from '../style/theme';
import { format } from '../util/format';
+import { AmountPlaceholder } from './amount_placeholder';
import { Container, Flex, Text } from './ui';
export interface OrderDetailsProps {
buyQuoteInfo?: BuyQuoteInfo;
ethUsdPrice?: BigNumber;
+ isLoading: boolean;
}
-
export class OrderDetails extends React.Component<OrderDetailsProps> {
public render(): React.ReactNode {
const { buyQuoteInfo, ethUsdPrice } = this.props;
@@ -39,13 +40,20 @@ export class OrderDetails extends React.Component<OrderDetailsProps> {
ethAmount={ethAssetPrice}
ethUsdPrice={ethUsdPrice}
isEthAmountInBaseUnits={false}
+ isLoading={this.props.isLoading}
+ />
+ <EthAmountRow
+ rowLabel="Fee"
+ ethAmount={ethTokenFee}
+ ethUsdPrice={ethUsdPrice}
+ isLoading={this.props.isLoading}
/>
- <EthAmountRow rowLabel="Fee" ethAmount={ethTokenFee} ethUsdPrice={ethUsdPrice} />
<EthAmountRow
rowLabel="Total Cost"
ethAmount={totalEthAmount}
ethUsdPrice={ethUsdPrice}
shouldEmphasize={true}
+ isLoading={this.props.isLoading}
/>
</Container>
);
@@ -58,43 +66,50 @@ export interface EthAmountRowProps {
isEthAmountInBaseUnits?: boolean;
ethUsdPrice?: BigNumber;
shouldEmphasize?: boolean;
+ isLoading: boolean;
}
-export const EthAmountRow: React.StatelessComponent<EthAmountRowProps> = ({
- rowLabel,
- ethAmount,
- isEthAmountInBaseUnits,
- ethUsdPrice,
- shouldEmphasize,
-}) => {
- const fontWeight = shouldEmphasize ? 700 : 400;
- const usdFormatter = isEthAmountInBaseUnits ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd;
- const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseAmount : format.ethUnitAmount;
- const usdPriceSection = _.isUndefined(ethUsdPrice) ? null : (
- <Container marginRight="3px" display="inline-block">
- <Text fontColor={ColorOption.lightGrey}>({usdFormatter(ethAmount, ethUsdPrice)})</Text>
- </Container>
- );
- return (
- <Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}>
- <Flex justify="space-between">
- <Text fontWeight={fontWeight} fontColor={ColorOption.grey}>
- {rowLabel}
- </Text>
- <Container>
- {usdPriceSection}
+export class EthAmountRow extends React.Component<EthAmountRowProps> {
+ public static defaultProps = {
+ shouldEmphasize: false,
+ isEthAmountInBaseUnits: true,
+ };
+ public render(): React.ReactNode {
+ const { rowLabel, ethAmount, isEthAmountInBaseUnits, shouldEmphasize, isLoading } = this.props;
+
+ const fontWeight = shouldEmphasize ? 700 : 400;
+ const ethFormatter = isEthAmountInBaseUnits ? format.ethBaseAmount : format.ethUnitAmount;
+ return (
+ <Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}>
+ <Flex justify="space-between">
<Text fontWeight={fontWeight} fontColor={ColorOption.grey}>
- {ethFormatter(ethAmount)}
+ {rowLabel}
</Text>
- </Container>
- </Flex>
- </Container>
- );
-};
-
-EthAmountRow.defaultProps = {
- shouldEmphasize: false,
- isEthAmountInBaseUnits: true,
-};
-
-EthAmountRow.displayName = 'EthAmountRow';
+ <Container>
+ {this._renderUsdSection()}
+ <Text fontWeight={fontWeight} fontColor={ColorOption.grey}>
+ {ethFormatter(
+ ethAmount,
+ 4,
+ <Container opacity={0.5}>
+ <AmountPlaceholder color={ColorOption.lightGrey} isPulsating={isLoading} />
+ </Container>,
+ )}
+ </Text>
+ </Container>
+ </Flex>
+ </Container>
+ );
+ }
+ private _renderUsdSection(): React.ReactNode {
+ const usdFormatter = this.props.isEthAmountInBaseUnits ? format.ethBaseAmountInUsd : format.ethUnitAmountInUsd;
+ const shouldHideUsdPriceSection = _.isUndefined(this.props.ethUsdPrice) || _.isUndefined(this.props.ethAmount);
+ return shouldHideUsdPriceSection ? null : (
+ <Container marginRight="3px" display="inline-block">
+ <Text fontColor={ColorOption.lightGrey}>
+ ({usdFormatter(this.props.ethAmount, this.props.ethUsdPrice)})
+ </Text>
+ </Container>
+ );
+ }
+}
diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx
index 02b16e39f..5e2218c68 100644
--- a/packages/instant/src/components/ui/container.tsx
+++ b/packages/instant/src/components/ui/container.tsx
@@ -27,6 +27,7 @@ export interface ContainerProps {
backgroundColor?: ColorOption;
hasBoxShadow?: boolean;
zIndex?: number;
+ opacity?: number;
}
const PlainContainer: React.StatelessComponent<ContainerProps> = ({ children, className }) => (
@@ -54,6 +55,7 @@ export const Container = styled(PlainContainer)`
${props => cssRuleIfExists(props, 'border-top')}
${props => cssRuleIfExists(props, 'border-bottom')}
${props => cssRuleIfExists(props, 'z-index')}
+ ${props => cssRuleIfExists(props, 'opacity')}
${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/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx
index 5b75a7556..142e74dae 100644
--- a/packages/instant/src/components/zero_ex_instant.tsx
+++ b/packages/instant/src/components/zero_ex_instant.tsx
@@ -3,13 +3,12 @@ import { ObjectMap } from '@0x/types';
import * as React from 'react';
import { Provider } from 'react-redux';
-import { assetMetaDataMap } from '../data/asset_meta_data_map';
+import { SelectedAssetThemeProvider } from '../containers/selected_asset_theme_provider';
import { asyncData } from '../redux/async_data';
-import { State } from '../redux/reducer';
+import { INITIAL_STATE, State } from '../redux/reducer';
import { store, Store } from '../redux/store';
import { fonts } from '../style/fonts';
-import { theme, ThemeProvider } from '../style/theme';
-import { AssetMetaData } from '../types';
+import { AssetMetaData, Network } from '../types';
import { assetUtils } from '../util/asset';
import { getProvider } from '../util/provider';
@@ -17,32 +16,49 @@ import { ZeroExInstantContainer } from './zero_ex_instant_container';
fonts.include();
-export interface ZeroExInstantProps {
+export type ZeroExInstantProps = ZeroExInstantRequiredProps & Partial<ZeroExInstantOptionalProps>;
+
+export interface ZeroExInstantRequiredProps {
// TODO: Change API when we allow the selection of different assetDatas
assetData: string;
- sraApiUrl: string;
+ // TODO: Allow for a function that returns orders
+ liquiditySource: string;
+}
+
+export interface ZeroExInstantOptionalProps {
additionalAssetMetaDataMap: ObjectMap<AssetMetaData>;
+ network: Network;
}
export class ZeroExInstant extends React.Component<ZeroExInstantProps> {
- public static defaultProps = {
- additionalAssetMetaDataMap: {},
- };
public store: Store;
- constructor(props: ZeroExInstantProps) {
- super(props);
+ private static _mergeInitialStateWithProps(props: ZeroExInstantProps, state: State = INITIAL_STATE): State {
+ // Create merged object such that properties in props override default settings
+ const optionalPropsWithDefaults: ZeroExInstantOptionalProps = {
+ additionalAssetMetaDataMap: props.additionalAssetMetaDataMap || {},
+ network: props.network || state.network,
+ };
+ const { network } = optionalPropsWithDefaults;
// TODO: Provider needs to not be hard-coded to injected web3.
- const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(getProvider(), props.sraApiUrl);
+ const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(getProvider(), props.liquiditySource, {
+ networkId: network,
+ });
const completeAssetMetaDataMap = {
...props.additionalAssetMetaDataMap,
- ...assetMetaDataMap,
+ ...state.assetMetaDataMap,
};
- const storeStateFromProps: Partial<State> = {
+ const storeStateFromProps: State = {
+ ...state,
assetBuyer,
- selectedAsset: assetUtils.createAssetFromAssetData(props.assetData, completeAssetMetaDataMap),
+ network,
+ selectedAsset: assetUtils.createAssetFromAssetData(props.assetData, completeAssetMetaDataMap, network),
assetMetaDataMap: completeAssetMetaDataMap,
};
- this.store = store.create(storeStateFromProps);
+ return storeStateFromProps;
+ }
+ constructor(props: ZeroExInstantProps) {
+ super(props);
+ this.store = store.create(ZeroExInstant._mergeInitialStateWithProps(this.props, INITIAL_STATE));
// tslint:disable-next-line:no-floating-promises
asyncData.fetchAndDispatchToStore(this.store);
}
@@ -50,9 +66,9 @@ export class ZeroExInstant extends React.Component<ZeroExInstantProps> {
public render(): React.ReactNode {
return (
<Provider store={this.store}>
- <ThemeProvider theme={theme}>
+ <SelectedAssetThemeProvider>
<ZeroExInstantContainer />
- </ThemeProvider>
+ </SelectedAssetThemeProvider>
</Provider>
);
}