Merge pull request #746 from 0xProject/feature/website/relayer-grid-polish

Relayer grid polish
This commit is contained in:
Francesco Agosti
2018-06-22 13:11:33 -07:00
committed by GitHub
11 changed files with 138 additions and 152 deletions

View File

@@ -42,7 +42,11 @@ export class OnboardingFlow extends React.Component<OnboardingFlowProps> {
onboardingElement = <Animation type="easeUpFromBottom">{this._renderOnboardignCard()}</Animation>;
} else {
onboardingElement = (
<Popper referenceElement={this._getElementForStep()} placement={this._getCurrentStep().placement}>
<Popper
referenceElement={this._getElementForStep()}
placement={this._getCurrentStep().placement}
positionFixed={true}
>
{this._renderPopperChildren.bind(this)}
</Popper>
);

View File

@@ -1,4 +1,4 @@
import { colors, constants as sharedConstants, Styles } from '@0xproject/react-shared';
import { colors, constants as sharedConstants } from '@0xproject/react-shared';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
@@ -107,26 +107,7 @@ const TOP_BAR_HEIGHT = TopBar.heightForDisplayType(TopBarDisplayType.Expanded);
const LEFT_COLUMN_WIDTH = 346;
const MENU_PADDING_LEFT = 185;
const LARGE_LAYOUT_MAX_WIDTH = 1200;
const styles: Styles = {
root: {
width: '100%',
height: '100%',
backgroundColor: colors.lightestGrey,
},
body: {
height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`,
},
leftColumn: {
width: LEFT_COLUMN_WIDTH,
height: '100%',
},
scrollContainer: {
height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`,
WebkitOverflowScrolling: 'touch',
overflow: 'auto',
},
};
const LARGE_LAYOUT_MARGIN = 30;
export class Portal extends React.Component<PortalProps, PortalState> {
private _blockchain: Blockchain;
@@ -245,7 +226,7 @@ export class Portal extends React.Component<PortalProps, PortalState> {
? TokenVisibility.UNTRACKED
: TokenVisibility.TRACKED;
return (
<div style={styles.root}>
<Container>
<DocumentTitle title="0x Portal DApp" />
<TopBar
userAddress={this.props.userAddress}
@@ -259,10 +240,15 @@ export class Portal extends React.Component<PortalProps, PortalState> {
blockchain={this._blockchain}
translate={this.props.translate}
displayType={TopBarDisplayType.Expanded}
style={{ backgroundColor: colors.lightestGrey }}
style={{
backgroundColor: colors.lightestGrey,
position: 'fixed',
// Hack: used to make onboarding z-index logic work for both mobile and desktop
zIndex: utils.isMobile(this.props.screenWidth) ? zIndex.topBar : undefined,
}}
maxWidth={LARGE_LAYOUT_MAX_WIDTH}
/>
<div id="portal" style={styles.body}>
<Container marginTop={TOP_BAR_HEIGHT} minHeight="100vh" backgroundColor={colors.lightestGrey}>
<Switch>
<Route path={`${WebsitePaths.Portal}/:route`} render={this._renderOtherRoutes.bind(this)} />
<Route
@@ -301,13 +287,8 @@ export class Portal extends React.Component<PortalProps, PortalState> {
tokenByAddress={this.props.tokenByAddress}
tokenVisibility={tokenVisibility}
/>
</div>
<PortalOnboardingFlow
blockchain={this._blockchain}
trackedTokenStateByAddress={this.state.trackedTokenStateByAddress}
refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)}
/>
</div>
</Container>
</Container>
);
}
private _renderMainRoute(): React.ReactNode {
@@ -341,41 +322,48 @@ export class Portal extends React.Component<PortalProps, PortalState> {
}
private _renderWallet(): React.ReactNode {
const startOnboarding = this._renderStartOnboarding();
const isMobile = this.props.screenWidth === ScreenWidths.Sm;
const isMobile = utils.isMobile(this.props.screenWidth);
// We need room to scroll down for mobile onboarding
const marginBottom = isMobile ? '200px' : '15px';
return (
<div>
{isMobile && <Container marginBottom="15px">{startOnboarding}</Container>}
<Container marginBottom={marginBottom}>
<Wallet
style={
!isMobile && this.props.isPortalOnboardingShowing
? { zIndex: zIndex.aboveOverlay, position: 'relative' }
: undefined
}
userAddress={this.props.userAddress}
networkId={this.props.networkId}
blockchain={this._blockchain}
blockchainIsLoaded={this.props.blockchainIsLoaded}
blockchainErr={this.props.blockchainErr}
dispatcher={this.props.dispatcher}
tokenByAddress={this.props.tokenByAddress}
trackedTokens={this._getCurrentTrackedTokens()}
userEtherBalanceInWei={this.props.userEtherBalanceInWei}
lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
injectedProviderName={this.props.injectedProviderName}
providerType={this.props.providerType}
screenWidth={this.props.screenWidth}
location={this.props.location}
trackedTokenStateByAddress={this.state.trackedTokenStateByAddress}
onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)}
onAddToken={this._onAddToken.bind(this)}
onRemoveToken={this._onRemoveToken.bind(this)}
refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)}
/>
<Container>
{isMobile && <Container marginBottom="15px">{startOnboarding}</Container>}
<Container marginBottom={marginBottom}>
<Wallet
style={
!isMobile && this.props.isPortalOnboardingShowing
? { zIndex: zIndex.aboveOverlay, position: 'relative' }
: undefined
}
userAddress={this.props.userAddress}
networkId={this.props.networkId}
blockchain={this._blockchain}
blockchainIsLoaded={this.props.blockchainIsLoaded}
blockchainErr={this.props.blockchainErr}
dispatcher={this.props.dispatcher}
tokenByAddress={this.props.tokenByAddress}
trackedTokens={this._getCurrentTrackedTokens()}
userEtherBalanceInWei={this.props.userEtherBalanceInWei}
lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
injectedProviderName={this.props.injectedProviderName}
providerType={this.props.providerType}
screenWidth={this.props.screenWidth}
location={this.props.location}
trackedTokenStateByAddress={this.state.trackedTokenStateByAddress}
onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)}
onAddToken={this._onAddToken.bind(this)}
onRemoveToken={this._onRemoveToken.bind(this)}
refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)}
/>
</Container>
{!isMobile && <Container marginTop="15px">{startOnboarding}</Container>}
</Container>
{!isMobile && <Container marginTop="15px">{startOnboarding}</Container>}
<PortalOnboardingFlow
blockchain={this._blockchain}
trackedTokenStateByAddress={this.state.trackedTokenStateByAddress}
refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this)}
/>
</div>
);
}
@@ -551,7 +539,7 @@ export class Portal extends React.Component<PortalProps, PortalState> {
private _renderRelayerIndexSection(): React.ReactNode {
return (
<Section
header={<TextHeader labelText="Explore 0x Relayers" />}
header={<TextHeader labelText="0x Relayers" />}
body={<RelayerIndex networkId={this.props.networkId} screenWidth={this.props.screenWidth} />}
/>
);
@@ -709,12 +697,16 @@ interface LargeLayoutProps {
const LargeLayout = (props: LargeLayoutProps) => {
return (
<div className="mx-auto flex flex-center" style={{ maxWidth: LARGE_LAYOUT_MAX_WIDTH }}>
<div className="flex-last px2">
<div style={styles.leftColumn}>{props.left}</div>
</div>
<div className="flex-auto px2" style={styles.scrollContainer}>
{props.right}
<div className="flex-last">
<Container width={LEFT_COLUMN_WIDTH} position="fixed" marginLeft={LARGE_LAYOUT_MARGIN}>
{props.left}
</Container>
</div>
<Container className="flex-auto" marginLeft={LEFT_COLUMN_WIDTH + LARGE_LAYOUT_MARGIN}>
<Container className="flex-auto" marginLeft={LARGE_LAYOUT_MARGIN} marginRight={LARGE_LAYOUT_MARGIN}>
{props.right}
</Container>
</Container>
</div>
);
};
@@ -725,9 +717,7 @@ interface SmallLayoutProps {
const SmallLayout = (props: SmallLayoutProps) => {
return (
<div className="flex flex-center">
<div className="flex-auto px3" style={styles.scrollContainer}>
{props.content}
</div>
<div className="flex-auto px3">{props.content}</div>
</div>
);
}; // tslint:disable:max-file-line-count

View File

@@ -1,21 +1,16 @@
import { Styles } from '@0xproject/react-shared';
import { colors } from '@0xproject/react-shared';
import * as React from 'react';
import { Text } from 'ts/components/ui/text';
export interface TextHeaderProps {
labelText: string;
}
const styles: Styles = {
title: {
fontWeight: 'bold',
fontSize: 20,
},
};
export const TextHeader = (props: TextHeaderProps) => {
return (
<div className="py3" style={styles.title}>
<Text className="pt3 pb2" fontWeight="bold" fontSize="16px" fontColor={colors.darkestGrey}>
{props.labelText}
</div>
</Text>
);
};

View File

@@ -1,6 +1,6 @@
import { constants as sharedConstants, Styles } from '@0xproject/react-shared';
import * as _ from 'lodash';
import { GridTile } from 'material-ui/GridList';
import { GridTile as PlainGridTile } from 'material-ui/GridList';
import * as React from 'react';
import { analytics } from 'ts/utils/analytics';
@@ -9,7 +9,9 @@ import { Container } from 'ts/components/ui/container';
import { Image } from 'ts/components/ui/image';
import { Island } from 'ts/components/ui/island';
import { colors } from 'ts/style/colors';
import { styled } from 'ts/style/theme';
import { WebsiteBackendRelayerInfo } from 'ts/types';
import { utils } from 'ts/utils/utils';
export interface RelayerGridTileProps {
relayerInfo: WebsiteBackendRelayerInfo;
@@ -19,29 +21,23 @@ export interface RelayerGridTileProps {
const styles: Styles = {
root: {
boxSizing: 'border-box',
// All material UI components have position: relative
// which creates a new stacking context and makes z-index stuff impossible. So reset.
position: 'static',
},
innerDiv: {
padding: 6,
height: '100%',
boxSizing: 'border-box',
},
header: {
height: '50%',
width: '100%',
borderBottomRightRadius: 4,
borderBottomLeftRadius: 4,
borderTopRightRadius: 4,
borderTopLeftRadius: 4,
borderWidth: 1,
borderStyle: 'solid',
borderColor: colors.walletBorder,
},
body: {
paddingLeft: 6,
paddingRight: 6,
height: '50%',
width: '100%',
boxSizing: 'border-box',
padding: 12,
},
weeklyTradeVolumeLabel: {
fontSize: 14,
@@ -69,7 +65,10 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = (
const weeklyTxnVolume = props.relayerInfo.weeklyTxnVolume;
const networkName = sharedConstants.NETWORK_NAME_BY_ID[props.networkId];
const eventLabel = `${props.relayerInfo.name}-${networkName}`;
const trackRelayerClick = () => analytics.logEvent('Portal', 'Relayer Click', eventLabel);
const onClick = () => {
analytics.logEvent('Portal', 'Relayer Click', eventLabel);
utils.openUrl(link);
};
const headerImageUrl = props.relayerInfo.logoImgUrl;
const headerBackgroundColor =
!_.isUndefined(headerImageUrl) && !_.isUndefined(props.relayerInfo.primaryColor)
@@ -77,22 +76,17 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = (
: FALLBACK_PRIMARY_COLOR;
return (
<Island style={styles.root} Component={GridTile}>
<div style={styles.innerDiv}>
<a href={link} target="_blank" style={{ textDecoration: 'none' }} onClick={trackRelayerClick}>
<div
className="flex items-center"
style={{ ...styles.header, backgroundColor: headerBackgroundColor }}
>
<Image
className="mx-auto"
src={props.relayerInfo.logoImgUrl}
fallbackSrc={FALLBACK_IMG_SRC}
height={RELAYER_ICON_HEIGHT}
/>
</div>
</a>
<div style={styles.innerDiv} onClick={onClick}>
<div className="flex items-center" style={{ ...styles.header, backgroundColor: headerBackgroundColor }}>
<Image
className="mx-auto"
src={props.relayerInfo.logoImgUrl}
fallbackSrc={FALLBACK_IMG_SRC}
height={RELAYER_ICON_HEIGHT}
/>
</div>
<div style={styles.body}>
<div className="py1" style={styles.relayerNameLabel}>
<div className="pb1" style={styles.relayerNameLabel}>
{props.relayerInfo.name}
</div>
<Section titleText="Weekly Trade Volume">
@@ -111,6 +105,14 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = (
);
};
const GridTile = styled(PlainGridTile)`
cursor: pointer;
transition: transform 0.2s ease;
&:hover {
transform: translate(0px, -3px);
}
`;
interface SectionProps {
titleText: string;
children?: React.ReactNode;

View File

@@ -1,4 +1,3 @@
import { Styles } from '@0xproject/react-shared';
import * as _ from 'lodash';
import CircularProgress from 'material-ui/CircularProgress';
import { GridList } from 'material-ui/GridList';
@@ -6,7 +5,6 @@ import * as React from 'react';
import { RelayerGridTile } from 'ts/components/relayer_index/relayer_grid_tile';
import { Retry } from 'ts/components/ui/retry';
import { colors } from 'ts/style/colors';
import { ScreenWidths, WebsiteBackendRelayerInfo } from 'ts/types';
import { backendClient } from 'ts/utils/backend_client';
@@ -20,22 +18,6 @@ interface RelayerIndexState {
error?: Error;
}
const styles: Styles = {
root: {
width: '100%',
},
item: {
backgroundColor: colors.white,
borderBottomRightRadius: 10,
borderBottomLeftRadius: 10,
borderTopRightRadius: 10,
borderTopLeftRadius: 10,
boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`,
overflow: 'hidden',
padding: 4,
},
};
const CELL_HEIGHT = 290;
const NUMBER_OF_COLUMNS_LARGE = 3;
const NUMBER_OF_COLUMNS_MEDIUM = 2;
@@ -76,18 +58,16 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde
} else {
const numberOfColumns = this._numberOfColumnsForScreenWidth(this.props.screenWidth);
return (
<div style={styles.root}>
<GridList
cellHeight={CELL_HEIGHT}
cols={numberOfColumns}
padding={GRID_PADDING}
style={styles.gridList}
>
{this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo, index) => (
<RelayerGridTile key={index} relayerInfo={relayerInfo} networkId={this.props.networkId} />
))}
</GridList>
</div>
<GridList
cellHeight={CELL_HEIGHT}
cols={numberOfColumns}
padding={GRID_PADDING}
style={{ marginTop: -10, marginBottom: 0 }}
>
{this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo, index) => (
<RelayerGridTile key={index} relayerInfo={relayerInfo} networkId={this.props.networkId} />
))}
</GridList>
);
}
}

View File

@@ -70,7 +70,10 @@ class TokenLink extends React.Component<TokenLinkProps, TokenLinkState> {
};
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
const eventLabel = `${this.props.tokenInfo.symbol}-${networkName}`;
const trackTokenClick = () => analytics.logEvent('Portal', 'Token Click', eventLabel);
const onClick = (event: React.MouseEvent<HTMLElement>) => {
event.stopPropagation();
analytics.logEvent('Portal', 'Token Click', eventLabel);
};
return (
<a
href={tokenLinkFromToken(this.props.tokenInfo, this.props.networkId)}
@@ -78,7 +81,7 @@ class TokenLink extends React.Component<TokenLinkProps, TokenLinkState> {
style={style}
onMouseEnter={this._onToggleHover.bind(this, true)}
onMouseLeave={this._onToggleHover.bind(this, false)}
onClick={trackTokenClick}
onClick={onClick}
>
{this.props.tokenInfo.symbol}
</a>

View File

@@ -11,13 +11,15 @@ const PlainAnimation: React.StatelessComponent<AnimationProps> = props => <div {
const appearFromBottomFrames = keyframes`
from {
position: absolute;
position: fixed;
bottom: -500px;
left: 0px;
}
to {
position: absolute;
position: fixed;
bottom: 0px;
left: 0px;
}
`;

View File

@@ -15,6 +15,7 @@ export interface ContainerProps {
borderRadius?: StringOrNum;
maxWidth?: StringOrNum;
width?: StringOrNum;
minHeight?: StringOrNum;
isHidden?: boolean;
className?: string;
position?: 'absolute' | 'fixed' | 'relative' | 'unset';

View File

@@ -29,6 +29,7 @@ import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item';
import { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle';
import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors';
import { styled } from 'ts/style/theme';
import {
BlockchainErrs,
ProviderType,
@@ -138,6 +139,12 @@ const USD_DECIMAL_PLACES = 2;
const NO_ALLOWANCE_TOGGLE_SPACE_WIDTH = 56;
const ACCOUNT_PATH = `${WebsitePaths.Portal}/account`;
const ActionButton = styled(FloatingActionButton)`
button {
position: static !important;
}
`;
export class Wallet extends React.Component<WalletProps, WalletState> {
public static defaultProps = {
style: {},
@@ -244,17 +251,12 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
<ListItem
primaryText={
<div className="flex">
<FloatingActionButton mini={true} zDepth={0} onClick={this.props.onAddToken}>
<ActionButton mini={true} zDepth={0} onClick={this.props.onAddToken}>
<ContentAdd />
</FloatingActionButton>
<FloatingActionButton
mini={true}
zDepth={0}
className="px1"
onClick={this.props.onRemoveToken}
>
</ActionButton>
<ActionButton mini={true} zDepth={0} className="px1" onClick={this.props.onRemoveToken}>
<ContentRemove />
</FloatingActionButton>
</ActionButton>
<div
style={{
paddingLeft: 10,