aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrandon Millman <brandon@0xproject.com>2018-05-24 08:03:54 +0800
committerGitHub <noreply@github.com>2018-05-24 08:03:54 +0800
commit2f1a4042bf924cf5d07d724d5d1a1c0a2c181f4f (patch)
tree98ff18202c0d230a556425145221ce56aa897efa
parent71266c0220ad14908c54b7e39e3d0a0715b35599 (diff)
parent0801457ff02ac1ab16693be8bfb049fe3c284253 (diff)
downloaddexon-sol-tools-2f1a4042bf924cf5d07d724d5d1a1c0a2c181f4f.tar
dexon-sol-tools-2f1a4042bf924cf5d07d724d5d1a1c0a2c181f4f.tar.gz
dexon-sol-tools-2f1a4042bf924cf5d07d724d5d1a1c0a2c181f4f.tar.bz2
dexon-sol-tools-2f1a4042bf924cf5d07d724d5d1a1c0a2c181f4f.tar.lz
dexon-sol-tools-2f1a4042bf924cf5d07d724d5d1a1c0a2c181f4f.tar.xz
dexon-sol-tools-2f1a4042bf924cf5d07d724d5d1a1c0a2c181f4f.tar.zst
dexon-sol-tools-2f1a4042bf924cf5d07d724d5d1a1c0a2c181f4f.zip
Merge pull request #603 from 0xProject/feature/website/wallet-flex-box
Improve wallet layout using flexbox
-rw-r--r--packages/website/ts/components/ui/icon_button.tsx63
-rw-r--r--packages/website/ts/components/ui/token_icon.tsx25
-rw-r--r--packages/website/ts/components/wallet/wallet.tsx153
-rw-r--r--packages/website/ts/components/wallet/wrap_ether_item.tsx49
4 files changed, 161 insertions, 129 deletions
diff --git a/packages/website/ts/components/ui/icon_button.tsx b/packages/website/ts/components/ui/icon_button.tsx
new file mode 100644
index 000000000..2f5172f05
--- /dev/null
+++ b/packages/website/ts/components/ui/icon_button.tsx
@@ -0,0 +1,63 @@
+import { colors, Styles } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+
+export interface IconButtonProps {
+ iconName: string;
+ labelText?: string;
+ onClick: () => void;
+ color?: string;
+}
+interface IconButtonState {
+ isHovering: boolean;
+}
+export class IconButton extends React.Component<IconButtonProps, IconButtonState> {
+ public static defaultProps: Partial<IconButtonProps> = {
+ onClick: _.noop,
+ labelText: '',
+ color: colors.mediumBlue,
+ };
+ public constructor(props: IconButtonProps) {
+ super(props);
+ this.state = {
+ isHovering: false,
+ };
+ }
+ public render(): React.ReactNode {
+ const styles: Styles = {
+ root: {
+ cursor: 'pointer',
+ opacity: this.state.isHovering ? 0.5 : 1,
+ },
+ icon: {
+ color: this.props.color,
+ fontSize: 20,
+ },
+ label: {
+ color: this.props.color,
+ fontSize: 10,
+ },
+ };
+ return (
+ <div
+ className="flex items-center py2"
+ onClick={this.props.onClick}
+ onMouseEnter={this._onToggleHover.bind(this, true)}
+ onMouseLeave={this._onToggleHover.bind(this, false)}
+ style={styles.root}
+ >
+ <i style={styles.icon} className={`zmdi ${this.props.iconName}`} />
+ {!_.isEmpty(this.props.labelText) && (
+ <div className="pl1" style={styles.label}>
+ {this.props.labelText}
+ </div>
+ )}
+ </div>
+ );
+ }
+ private _onToggleHover(isHovering: boolean): void {
+ this.setState({
+ isHovering,
+ });
+ }
+}
diff --git a/packages/website/ts/components/ui/token_icon.tsx b/packages/website/ts/components/ui/token_icon.tsx
index a9ad567ef..0875cc56b 100644
--- a/packages/website/ts/components/ui/token_icon.tsx
+++ b/packages/website/ts/components/ui/token_icon.tsx
@@ -6,6 +6,7 @@ import { Token } from 'ts/types';
interface TokenIconProps {
token: Token;
diameter: number;
+ link?: string;
}
interface TokenIconState {}
@@ -14,14 +15,20 @@ export class TokenIcon extends React.Component<TokenIconProps, TokenIconState> {
public render(): React.ReactNode {
const token = this.props.token;
const diameter = this.props.diameter;
- return (
- <div>
- {token.isRegistered && !_.isUndefined(token.iconUrl) ? (
- <img style={{ width: diameter, height: diameter }} src={token.iconUrl} />
- ) : (
- <Identicon address={token.address} diameter={diameter} />
- )}
- </div>
- );
+ const icon =
+ token.isRegistered && !_.isUndefined(token.iconUrl) ? (
+ <img style={{ width: diameter, height: diameter }} src={token.iconUrl} />
+ ) : (
+ <Identicon address={token.address} diameter={diameter} />
+ );
+ if (_.isEmpty(this.props.link)) {
+ return icon;
+ } else {
+ return (
+ <a href={this.props.link} target="_blank" style={{ textDecoration: 'none' }}>
+ {icon}
+ </a>
+ );
+ }
}
}
diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx
index afcf3862f..d0354580d 100644
--- a/packages/website/ts/components/wallet/wallet.tsx
+++ b/packages/website/ts/components/wallet/wallet.tsx
@@ -9,7 +9,7 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
import FlatButton from 'material-ui/FlatButton';
import FloatingActionButton from 'material-ui/FloatingActionButton';
-import { List, ListItem } from 'material-ui/List';
+import { ListItem } from 'material-ui/List';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
import ContentAdd from 'material-ui/svg-icons/content/add';
import ContentRemove from 'material-ui/svg-icons/content/remove';
@@ -23,6 +23,7 @@ import firstBy = require('thenby');
import { Blockchain } from 'ts/blockchain';
import { AllowanceToggle } from 'ts/components/inputs/allowance_toggle';
+import { IconButton } from 'ts/components/ui/icon_button';
import { Identicon } from 'ts/components/ui/identicon';
import { TokenIcon } from 'ts/components/ui/token_icon';
import { WalletDisconnectedItem } from 'ts/components/wallet/wallet_disconnected_item';
@@ -91,12 +92,6 @@ const styles: Styles = {
boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`,
overflow: 'hidden',
},
- list: {
- padding: 0,
- },
- tokenItemInnerDiv: {
- paddingLeft: 60,
- },
headerItemInnerDiv: {
paddingLeft: 65,
},
@@ -114,23 +109,19 @@ const styles: Styles = {
tokenItem: {
backgroundColor: colors.walletDefaultItemBackground,
},
- wrappedEtherOpenButtonLabel: {
- fontSize: 10,
- },
amountLabel: {
fontWeight: 'bold',
color: colors.black,
},
+ valueLabel: {
+ color: colors.grey,
+ fontSize: 14,
+ },
paddedItem: {
paddingTop: 8,
paddingBottom: 8,
},
- accessoryItemsContainer: {
- width: 150,
- right: 8,
- },
bodyInnerDiv: {
- padding: 0,
// TODO: make this completely responsive
maxHeight: 475,
overflow: 'auto',
@@ -154,6 +145,7 @@ const FOOTER_ITEM_KEY = 'FOOTER';
const DISCONNECTED_ITEM_KEY = 'DISCONNECTED';
const ETHER_ITEM_KEY = 'ETHER';
const USD_DECIMAL_PLACES = 2;
+const NO_ALLOWANCE_TOGGLE_SPACE_WIDTH = 56;
export class Wallet extends React.Component<WalletProps, WalletState> {
private _isUnmounted: boolean;
@@ -204,16 +196,13 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
}
public render(): React.ReactNode {
const isReadyToRender = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError;
- return <div style={styles.root}>{isReadyToRender && this._renderRows()}</div>;
- }
- private _renderRows(): React.ReactNode {
const isAddressAvailable = !_.isEmpty(this.props.userAddress);
return (
- <List style={styles.list}>
- {isAddressAvailable
+ <div className="flex flex-column" style={styles.root}>
+ {isReadyToRender && isAddressAvailable
? _.concat(this._renderConnectedHeaderRows(), this._renderBody(), this._renderFooterRows())
: _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows())}
- </List>
+ </div>
);
}
private _renderDisconnectedHeaderRows(): React.ReactElement<{}> {
@@ -259,15 +248,15 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden',
};
return (
- <ListItem
+ <div
+ style={bodyStyle}
key={BODY_ITEM_KEY}
- innerDivStyle={bodyStyle}
onMouseEnter={this._onSidebarHover.bind(this)}
onMouseLeave={this._onSidebarHoverOff.bind(this)}
>
{this._renderEthRows()}
{this._renderTokenRows()}
- </ListItem>
+ </div>
);
}
private _onSidebarHover(event: React.FormEvent<HTMLInputElement>): void {
@@ -329,6 +318,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
);
}
private _renderEthRows(): React.ReactNode {
+ const icon = <img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />;
const primaryText = this._renderAmount(
this.props.userEtherBalanceInWei,
constants.DECIMAL_PLACES_ETH,
@@ -350,33 +340,8 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
const style = isInWrappedEtherState
? { ...walletItemStyles.focusedItem, ...styles.paddedItem }
: { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem };
- return (
- <div key={ETHER_ITEM_KEY}>
- <ListItem
- primaryText={primaryText}
- secondaryText={secondaryText}
- leftIcon={<img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />}
- rightAvatar={this._renderAccessoryItems(accessoryItemConfig)}
- disableTouchRipple={true}
- style={style}
- innerDivStyle={styles.tokenItemInnerDiv}
- />
- {isInWrappedEtherState && (
- <WrapEtherItem
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- blockchain={this.props.blockchain}
- dispatcher={this.props.dispatcher}
- userEtherBalanceInWei={this.props.userEtherBalanceInWei}
- direction={accessoryItemConfig.wrappedEtherDirection}
- etherToken={etherToken}
- lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
- onConversionSuccessful={this._closeWrappedEtherActionRow.bind(this)}
- refetchEthTokenStateAsync={this._refetchTokenStateAsync.bind(this, etherToken.address)}
- />
- )}
- </div>
- );
+ const key = ETHER_ITEM_KEY;
+ return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig);
}
private _renderTokenRows(): React.ReactNode {
const trackedTokens = this.props.trackedTokens;
@@ -394,6 +359,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
this.props.networkId,
EtherscanLinkSuffixes.Address,
);
+ const icon = <TokenIcon token={token} diameter={ICON_DIMENSION} link={tokenLink} />;
const primaryText = this._renderAmount(tokenState.balance, token.decimals, token.symbol);
const secondaryText = this._renderValue(tokenState.balance, token.decimals, tokenState.price);
const wrappedEtherDirection = token.symbol === ETHER_TOKEN_SYMBOL ? Side.Receive : undefined;
@@ -404,26 +370,34 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
tokenState,
},
};
- // if this is the last item in the list, do not render the border, it is rendered by the footer
- const borderedStyle = index !== this.props.trackedTokens.length - 1 ? styles.borderedItem : {};
+ const key = token.address;
+ return this._renderBalanceRow(key, icon, primaryText, secondaryText, accessoryItemConfig);
+ }
+ private _renderBalanceRow(
+ key: string,
+ icon: React.ReactNode,
+ primaryText: React.ReactNode,
+ secondaryText: React.ReactNode,
+ accessoryItemConfig: AccessoryItemConfig,
+ ): React.ReactNode {
const shouldShowWrapEtherItem =
!_.isUndefined(this.state.wrappedEtherDirection) &&
this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection;
const style = shouldShowWrapEtherItem
? { ...walletItemStyles.focusedItem, ...styles.paddedItem }
- : { ...styles.tokenItem, ...borderedStyle, ...styles.paddedItem };
+ : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem };
const etherToken = this._getEthToken();
return (
- <div key={token.address}>
- <ListItem
- primaryText={primaryText}
- secondaryText={secondaryText}
- leftIcon={this._renderTokenIcon(token, tokenLink)}
- rightAvatar={this._renderAccessoryItems(accessoryItemConfig)}
- disableTouchRipple={true}
- style={style}
- innerDivStyle={styles.tokenItemInnerDiv}
- />
+ <div key={key} className="flex flex-column">
+ <div className="flex items-center" style={style}>
+ <div className="px2">{icon}</div>
+ <div className="flex-none pr2 pt2 pb2">
+ {primaryText}
+ {secondaryText}
+ </div>
+ <div className="flex-auto" />
+ <div>{this._renderAccessoryItems(accessoryItemConfig)}</div>
+ </div>
{shouldShowWrapEtherItem && (
<WrapEtherItem
userAddress={this.props.userAddress}
@@ -444,16 +418,19 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
private _renderAccessoryItems(config: AccessoryItemConfig): React.ReactElement<{}> {
const shouldShowWrappedEtherAction = !_.isUndefined(config.wrappedEtherDirection);
const shouldShowToggle = !_.isUndefined(config.allowanceToggleConfig);
+ // if we don't have a toggle, we still want some space to the right of the "wrap" button so that it aligns with
+ // the "unwrap" button in the row below
+ const toggle = shouldShowToggle ? (
+ this._renderAllowanceToggle(config.allowanceToggleConfig)
+ ) : (
+ <div style={{ width: NO_ALLOWANCE_TOGGLE_SPACE_WIDTH }} />
+ );
return (
- <div style={styles.accessoryItemsContainer}>
- <div className="flex">
- <div className="flex-auto">
- {shouldShowWrappedEtherAction && this._renderWrappedEtherButton(config.wrappedEtherDirection)}
- </div>
- <div className="flex-last py1">
- {shouldShowToggle && this._renderAllowanceToggle(config.allowanceToggleConfig)}
- </div>
+ <div className="flex items-center">
+ <div className="flex-auto">
+ {shouldShowWrappedEtherAction && this._renderWrappedEtherButton(config.wrappedEtherDirection)}
</div>
+ <div className="flex-last pl2">{toggle}</div>
</div>
);
}
@@ -486,37 +463,24 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
const value = unitAmount.mul(price);
const formattedAmount = value.toFixed(USD_DECIMAL_PLACES);
const result = `$${formattedAmount}`;
- return result;
- }
- private _renderTokenIcon(token: Token, tokenLink?: string): React.ReactElement<{}> {
- const tooltipId = `tooltip-${token.address}`;
- const tokenIcon = <TokenIcon token={token} diameter={ICON_DIMENSION} />;
- if (_.isUndefined(tokenLink)) {
- return tokenIcon;
- } else {
- return (
- <a href={tokenLink} target="_blank" style={{ textDecoration: 'none' }}>
- {tokenIcon}
- </a>
- );
- }
+ return <div style={styles.valueLabel}>{result}</div>;
}
private _renderWrappedEtherButton(wrappedEtherDirection: Side): React.ReactNode {
const isWrappedEtherDirectionOpen = this.state.wrappedEtherDirection === wrappedEtherDirection;
let buttonLabel;
- let buttonIcon;
+ let buttonIconName;
if (isWrappedEtherDirectionOpen) {
buttonLabel = 'cancel';
- buttonIcon = <Close />;
+ buttonIconName = 'zmdi-close';
} else {
switch (wrappedEtherDirection) {
case Side.Deposit:
buttonLabel = 'wrap';
- buttonIcon = <NavigationArrowDownward />;
+ buttonIconName = 'zmdi-long-arrow-down';
break;
case Side.Receive:
buttonLabel = 'unwrap';
- buttonIcon = <NavigationArrowUpward />;
+ buttonIconName = 'zmdi-long-arrow-up';
break;
default:
throw utils.spawnSwitchErr('wrappedEtherDirection', wrappedEtherDirection);
@@ -526,14 +490,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
? this._closeWrappedEtherActionRow.bind(this)
: this._openWrappedEtherActionRow.bind(this, wrappedEtherDirection);
return (
- <FlatButton
- label={buttonLabel}
- labelPosition="after"
- primary={true}
- icon={buttonIcon}
- labelStyle={styles.wrappedEtherOpenButtonLabel}
- onClick={onClick}
- />
+ <IconButton iconName={buttonIconName} labelText={buttonLabel} onClick={onClick} color={colors.mediumBlue} />
);
}
private _getInitialTrackedTokenStateByAddress(tokenAddresses: string[]): TokenStateByAddress {
diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx
index 0a0a8a1fe..1dfcffadf 100644
--- a/packages/website/ts/components/wallet/wrap_ether_item.tsx
+++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx
@@ -37,7 +37,10 @@ interface WrapEtherItemState {
}
const styles: Styles = {
- topLabel: { color: colors.black, fontSize: 11 },
+ topLabel: {
+ color: colors.black,
+ fontSize: 11,
+ },
inputContainer: {
backgroundColor: colors.white,
borderBottomRightRadius: 3,
@@ -45,9 +48,10 @@ const styles: Styles = {
borderTopRightRadius: 3,
borderTopLeftRadius: 3,
padding: 4,
- width: 125,
},
- amountInput: { height: 34 },
+ amountInput: {
+ height: 34,
+ },
amountInputLabel: {
paddingTop: 10,
paddingRight: 10,
@@ -58,8 +62,6 @@ const styles: Styles = {
amountInputHint: {
bottom: 18,
},
- innerDiv: { paddingLeft: 60, paddingTop: 0, paddingBottom: 10 },
- wrapEtherConfirmationButtonContainer: { width: 128, top: 19 },
wrapEtherConfirmationButtonLabel: {
fontSize: 12,
color: colors.white,
@@ -70,6 +72,9 @@ const styles: Styles = {
color: colors.red,
minHeight: 20,
},
+ conversionSpinner: {
+ paddingTop: 26,
+ },
};
export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEtherItemState> {
@@ -88,11 +93,13 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
);
const isWrappingEth = this.props.direction === Side.Deposit;
const topLabelText = isWrappingEth ? 'Convert ETH into WETH 1:1' : 'Convert WETH into ETH 1:1';
+
return (
- <ListItem
- primaryText={
- <div>
- <div style={styles.topLabel}>{topLabelText}</div>
+ <div className="flex" style={walletItemStyles.focusedItem}>
+ <div>{this._renderIsEthConversionHappeningSpinner()} </div>
+ <div className="flex flex-column">
+ <div style={styles.topLabel}>{topLabelText}</div>
+ <div className="flex items-center">
<div style={styles.inputContainer}>
{isWrappingEth ? (
<EthAmountInput
@@ -131,16 +138,12 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
/>
)}
</div>
- {this._renderErrorMsg()}
+ <div>{this._renderWrapEtherConfirmationButton()}</div>
</div>
- }
- secondaryTextLines={2}
- disableTouchRipple={true}
- style={walletItemStyles.focusedItem}
- innerDivStyle={styles.innerDiv}
- leftIcon={this._renderIsEthConversionHappeningSpinner()}
- rightAvatar={this._renderWrapEtherConfirmationButton()}
- />
+
+ {this._renderErrorMsg()}
+ </div>
+ </div>
);
}
private _onValueChange(isValid: boolean, amount?: BigNumber): void {
@@ -154,17 +157,19 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
});
}
private _renderIsEthConversionHappeningSpinner(): React.ReactElement<{}> {
- return this.state.isEthConversionHappening ? (
- <div className="pl1" style={{ paddingTop: 10 }}>
+ const visibility = this.state.isEthConversionHappening ? 'visible' : 'hidden';
+ const style: React.CSSProperties = { ...styles.conversionSpinner, visibility };
+ return (
+ <div className="pl3 pr2" style={style}>
<i className="zmdi zmdi-spinner zmdi-hc-spin" />
</div>
- ) : null;
+ );
}
private _renderWrapEtherConfirmationButton(): React.ReactElement<{}> {
const isWrappingEth = this.props.direction === Side.Deposit;
const labelText = isWrappingEth ? 'wrap' : 'unwrap';
return (
- <div style={styles.wrapEtherConfirmationButtonContainer}>
+ <div className="pl1 pr3">
<FlatButton
backgroundColor={colors.wrapEtherConfirmationButton}
label={labelText}