From 3660ba28d73d70d08bf14c33ef680e5ef3ec7f3b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 21 Nov 2017 14:03:08 -0600 Subject: Add website to mono repo, update packages to align with existing sub-packages, use new subscribeAsync 0x.js method --- packages/website/ts/components/ui/alert.tsx | 27 ++++ packages/website/ts/components/ui/badge.tsx | 58 ++++++++ packages/website/ts/components/ui/copy_icon.tsx | 81 +++++++++++ .../ts/components/ui/drop_down_menu_item.tsx | 117 ++++++++++++++++ .../website/ts/components/ui/ethereum_address.tsx | 35 +++++ .../website/ts/components/ui/etherscan_icon.tsx | 50 +++++++ .../website/ts/components/ui/fake_text_field.tsx | 35 +++++ .../website/ts/components/ui/flash_message.tsx | 40 ++++++ packages/website/ts/components/ui/help_tooltip.tsx | 22 +++ packages/website/ts/components/ui/identicon.tsx | 36 +++++ packages/website/ts/components/ui/input_label.tsx | 27 ++++ .../website/ts/components/ui/labeled_switcher.tsx | 76 +++++++++++ .../ts/components/ui/lifecycle_raised_button.tsx | 105 +++++++++++++++ packages/website/ts/components/ui/loading.tsx | 36 +++++ packages/website/ts/components/ui/menu_item.tsx | 54 ++++++++ packages/website/ts/components/ui/party.tsx | 150 +++++++++++++++++++++ .../website/ts/components/ui/required_label.tsx | 15 +++ .../website/ts/components/ui/simple_loading.tsx | 23 ++++ packages/website/ts/components/ui/swap_icon.tsx | 46 +++++++ packages/website/ts/components/ui/token_icon.tsx | 29 ++++ 20 files changed, 1062 insertions(+) create mode 100644 packages/website/ts/components/ui/alert.tsx create mode 100644 packages/website/ts/components/ui/badge.tsx create mode 100644 packages/website/ts/components/ui/copy_icon.tsx create mode 100644 packages/website/ts/components/ui/drop_down_menu_item.tsx create mode 100644 packages/website/ts/components/ui/ethereum_address.tsx create mode 100644 packages/website/ts/components/ui/etherscan_icon.tsx create mode 100644 packages/website/ts/components/ui/fake_text_field.tsx create mode 100644 packages/website/ts/components/ui/flash_message.tsx create mode 100644 packages/website/ts/components/ui/help_tooltip.tsx create mode 100644 packages/website/ts/components/ui/identicon.tsx create mode 100644 packages/website/ts/components/ui/input_label.tsx create mode 100644 packages/website/ts/components/ui/labeled_switcher.tsx create mode 100644 packages/website/ts/components/ui/lifecycle_raised_button.tsx create mode 100644 packages/website/ts/components/ui/loading.tsx create mode 100644 packages/website/ts/components/ui/menu_item.tsx create mode 100644 packages/website/ts/components/ui/party.tsx create mode 100644 packages/website/ts/components/ui/required_label.tsx create mode 100644 packages/website/ts/components/ui/simple_loading.tsx create mode 100644 packages/website/ts/components/ui/swap_icon.tsx create mode 100644 packages/website/ts/components/ui/token_icon.tsx (limited to 'packages/website/ts/components/ui') diff --git a/packages/website/ts/components/ui/alert.tsx b/packages/website/ts/components/ui/alert.tsx new file mode 100644 index 000000000..bf2f0baf5 --- /dev/null +++ b/packages/website/ts/components/ui/alert.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import {colors} from 'material-ui/styles'; +import {AlertTypes} from 'ts/types'; + +const CUSTOM_GREEN = 'rgb(137, 199, 116)'; + +interface AlertProps { + type: AlertTypes; + message: string|React.ReactNode; +} + +export function Alert(props: AlertProps) { + const isAlert = props.type === AlertTypes.ERROR; + const errMsgStyles = { + background: isAlert ? colors.red200 : CUSTOM_GREEN, + color: 'white', + marginTop: 10, + padding: 4, + paddingLeft: 8, + }; + + return ( +
+ {props.message} +
+ ); +} diff --git a/packages/website/ts/components/ui/badge.tsx b/packages/website/ts/components/ui/badge.tsx new file mode 100644 index 000000000..1e3bbdb99 --- /dev/null +++ b/packages/website/ts/components/ui/badge.tsx @@ -0,0 +1,58 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import {colors} from 'material-ui/styles'; +import {Styles} from 'ts/types'; + +const styles: Styles = { + badge: { + width: 50, + fontSize: 11, + height: 10, + borderRadius: 5, + marginTop: 25, + lineHeight: 0.9, + fontFamily: 'Roboto Mono', + marginLeft: 3, + marginRight: 3, + }, +}; + +interface BadgeProps { + title: string; + backgroundColor: string; +} + +interface BadgeState { + isHovering: boolean; +} + +export class Badge extends React.Component { + constructor(props: BadgeProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public render() { + const badgeStyle = { + ...styles.badge, + backgroundColor: this.props.backgroundColor, + opacity: this.state.isHovering ? 0.7 : 1, + }; + return ( +
+ {this.props.title} +
+ ); + } + private setHoverState(isHovering: boolean) { + this.setState({ + isHovering, + }); + } +} diff --git a/packages/website/ts/components/ui/copy_icon.tsx b/packages/website/ts/components/ui/copy_icon.tsx new file mode 100644 index 000000000..f8abaa59e --- /dev/null +++ b/packages/website/ts/components/ui/copy_icon.tsx @@ -0,0 +1,81 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import * as CopyToClipboard from 'react-copy-to-clipboard'; +import {colors} from 'material-ui/styles'; +import ReactTooltip = require('react-tooltip'); + +interface CopyIconProps { + data: string; + callToAction?: string; +} + +interface CopyIconState { + isHovering: boolean; +} + +export class CopyIcon extends React.Component { + private copyTooltipTimeoutId: number; + private copyable: HTMLInputElement; + constructor(props: CopyIconProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public componentDidUpdate() { + // Remove tooltip if hover away + if (!this.state.isHovering && this.copyTooltipTimeoutId) { + clearInterval(this.copyTooltipTimeoutId); + this.hideTooltip(); + } + } + public render() { + return ( +
+ +
+
+ +
+ {this.props.callToAction && +
{this.props.callToAction}
+ } +
+
+ Copied! +
+ ); + } + private setRefToProperty(el: HTMLInputElement) { + this.copyable = el; + } + private setHoverState(isHovering: boolean) { + this.setState({ + isHovering, + }); + } + private onCopy() { + if (this.copyTooltipTimeoutId) { + clearInterval(this.copyTooltipTimeoutId); + } + + const tooltipLifespanMs = 1000; + this.copyTooltipTimeoutId = window.setTimeout(() => { + this.hideTooltip(); + }, tooltipLifespanMs); + } + private hideTooltip() { + ReactTooltip.hide(ReactDOM.findDOMNode(this.copyable)); + } +} diff --git a/packages/website/ts/components/ui/drop_down_menu_item.tsx b/packages/website/ts/components/ui/drop_down_menu_item.tsx new file mode 100644 index 000000000..b8b7eb167 --- /dev/null +++ b/packages/website/ts/components/ui/drop_down_menu_item.tsx @@ -0,0 +1,117 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import { + Link as ScrollLink, +} from 'react-scroll'; +import {Link} from 'react-router-dom'; +import Popover from 'material-ui/Popover'; +import Menu from 'material-ui/Menu'; +import MenuItem from 'material-ui/MenuItem'; +import {Styles, WebsitePaths} from 'ts/types'; + +const CHECK_CLOSE_POPOVER_INTERVAL_MS = 300; +const CUSTOM_LIGHT_GRAY = '#848484'; +const DEFAULT_STYLE = { + fontSize: 14, +}; + +interface DropDownMenuItemProps { + title: string; + subMenuItems: React.ReactNode[]; + style?: React.CSSProperties; + menuItemStyle?: React.CSSProperties; + isNightVersion?: boolean; +} + +interface DropDownMenuItemState { + isDropDownOpen: boolean; + anchorEl?: HTMLInputElement; +} + +export class DropDownMenuItem extends React.Component { + public static defaultProps: Partial = { + style: DEFAULT_STYLE, + menuItemStyle: DEFAULT_STYLE, + isNightVersion: false, + }; + private isHovering: boolean; + private popoverCloseCheckIntervalId: number; + constructor(props: DropDownMenuItemProps) { + super(props); + this.state = { + isDropDownOpen: false, + }; + } + public componentDidMount() { + this.popoverCloseCheckIntervalId = window.setInterval(() => { + this.checkIfShouldClosePopover(); + }, CHECK_CLOSE_POPOVER_INTERVAL_MS); + } + public componentWillUnmount() { + window.clearInterval(this.popoverCloseCheckIntervalId); + } + public render() { + const colorStyle = this.props.isNightVersion ? 'white' : this.props.style.color; + return ( +
+
+
+ {this.props.title} +
+
+ +
+
+ +
+ + {this.props.subMenuItems} + +
+
+
+ ); + } + private onHover(event: React.FormEvent) { + this.isHovering = true; + this.checkIfShouldOpenPopover(event); + } + private checkIfShouldOpenPopover(event: React.FormEvent) { + if (this.state.isDropDownOpen) { + return; // noop + } + + this.setState({ + isDropDownOpen: true, + anchorEl: event.currentTarget, + }); + } + private onHoverOff(event: React.FormEvent) { + this.isHovering = false; + } + private checkIfShouldClosePopover() { + if (!this.state.isDropDownOpen || this.isHovering) { + return; // noop + } + this.closePopover(); + } + private closePopover() { + this.setState({ + isDropDownOpen: false, + }); + } +} diff --git a/packages/website/ts/components/ui/ethereum_address.tsx b/packages/website/ts/components/ui/ethereum_address.tsx new file mode 100644 index 000000000..c3d03b78c --- /dev/null +++ b/packages/website/ts/components/ui/ethereum_address.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import {EtherScanIcon} from 'ts/components/ui/etherscan_icon'; +import ReactTooltip = require('react-tooltip'); +import {EtherscanLinkSuffixes} from 'ts/types'; +import {utils} from 'ts/utils/utils'; + +interface EthereumAddressProps { + address: string; + networkId: number; +} + +export const EthereumAddress = (props: EthereumAddressProps) => { + const tooltipId = `${props.address}-ethereum-address`; + const truncatedAddress = utils.getAddressBeginAndEnd(props.address); + return ( +
+
+ {truncatedAddress} +
+
+ +
+ {props.address} +
+ ); +}; diff --git a/packages/website/ts/components/ui/etherscan_icon.tsx b/packages/website/ts/components/ui/etherscan_icon.tsx new file mode 100644 index 000000000..12044f44b --- /dev/null +++ b/packages/website/ts/components/ui/etherscan_icon.tsx @@ -0,0 +1,50 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import ReactTooltip = require('react-tooltip'); +import {colors} from 'material-ui/styles'; +import {EtherscanLinkSuffixes} from 'ts/types'; +import {utils} from 'ts/utils/utils'; + +interface EtherScanIconProps { + addressOrTxHash: string; + etherscanLinkSuffixes: EtherscanLinkSuffixes; + networkId: number; +} + +export const EtherScanIcon = (props: EtherScanIconProps) => { + const etherscanLinkIfExists = utils.getEtherScanLinkIfExists( + props.addressOrTxHash, props.networkId, EtherscanLinkSuffixes.address, + ); + const transactionTooltipId = `${props.addressOrTxHash}-etherscan-icon-tooltip`; + return ( +
+ {!_.isUndefined(etherscanLinkIfExists) ? + + {renderIcon()} + : +
+ {renderIcon()} + + Your network (id: {props.networkId}) is not supported by Etherscan + +
+ } +
+ ); +}; + +function renderIcon() { + return ( + + ); +} diff --git a/packages/website/ts/components/ui/fake_text_field.tsx b/packages/website/ts/components/ui/fake_text_field.tsx new file mode 100644 index 000000000..372785c2f --- /dev/null +++ b/packages/website/ts/components/ui/fake_text_field.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import {colors} from 'material-ui/styles'; +import {InputLabel} from 'ts/components/ui/input_label'; +import {Styles} from 'ts/types'; + +const styles: Styles = { + hr: { + borderBottom: '1px solid rgb(224, 224, 224)', + borderLeft: 'none rgb(224, 224, 224)', + borderRight: 'none rgb(224, 224, 224)', + borderTop: 'none rgb(224, 224, 224)', + bottom: 6, + boxSizing: 'content-box', + margin: 0, + position: 'absolute', + width: '100%', + }, +}; + +interface FakeTextFieldProps { + label?: React.ReactNode | string; + children?: any; +} + +export function FakeTextField(props: FakeTextFieldProps) { + return ( +
+ {props.label !== '' && } +
+ {props.children} +
+
+
+ ); +} diff --git a/packages/website/ts/components/ui/flash_message.tsx b/packages/website/ts/components/ui/flash_message.tsx new file mode 100644 index 000000000..684aeef68 --- /dev/null +++ b/packages/website/ts/components/ui/flash_message.tsx @@ -0,0 +1,40 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import Snackbar from 'material-ui/Snackbar'; +import {Dispatcher} from 'ts/redux/dispatcher'; + +const SHOW_DURATION_MS = 4000; + +interface FlashMessageProps { + dispatcher: Dispatcher; + flashMessage?: string|React.ReactNode; + showDurationMs?: number; + bodyStyle?: React.CSSProperties; +} + +interface FlashMessageState {} + +export class FlashMessage extends React.Component { + public static defaultProps: Partial = { + showDurationMs: SHOW_DURATION_MS, + bodyStyle: {}, + }; + public render() { + if (!_.isUndefined(this.props.flashMessage)) { + return ( + + ); + } else { + return null; + } + } + private onClose() { + this.props.dispatcher.hideFlashMessage(); + } +} diff --git a/packages/website/ts/components/ui/help_tooltip.tsx b/packages/website/ts/components/ui/help_tooltip.tsx new file mode 100644 index 000000000..003b795ef --- /dev/null +++ b/packages/website/ts/components/ui/help_tooltip.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import ReactTooltip = require('react-tooltip'); + +interface HelpTooltipProps { + style?: React.CSSProperties; + explanation: React.ReactNode; +} + +export const HelpTooltip = (props: HelpTooltipProps) => { + return ( +
+ + +
+ ); +}; diff --git a/packages/website/ts/components/ui/identicon.tsx b/packages/website/ts/components/ui/identicon.tsx new file mode 100644 index 000000000..814548fb4 --- /dev/null +++ b/packages/website/ts/components/ui/identicon.tsx @@ -0,0 +1,36 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import {constants} from 'ts/utils/constants'; +import blockies = require('blockies'); + +interface IdenticonProps { + address: string; + diameter: number; + style?: React.CSSProperties; +} + +interface IdenticonState {} + +export class Identicon extends React.Component { + public static defaultProps: Partial = { + style: {}, + }; + public render() { + let address = this.props.address; + if (_.isEmpty(address)) { + address = constants.NULL_ADDRESS; + } + const diameter = this.props.diameter; + const icon = blockies({ + seed: address.toLowerCase(), + }); + return ( +
+ +
+ ); + } +} diff --git a/packages/website/ts/components/ui/input_label.tsx b/packages/website/ts/components/ui/input_label.tsx new file mode 100644 index 000000000..5866c70b6 --- /dev/null +++ b/packages/website/ts/components/ui/input_label.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import {colors} from 'material-ui/styles'; + +export interface InputLabelProps { + text: string | Element | React.ReactNode; +} + +const styles = { + label: { + color: colors.grey500, + fontSize: 12, + pointerEvents: 'none', + textAlign: 'left', + transform: 'scale(0.75) translate(0px, -28px)', + transformOrigin: 'left top 0px', + transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms', + userSelect: 'none', + width: 240, + zIndex: 1, + }, +}; + +export const InputLabel = (props: InputLabelProps) => { + return ( + + ); +}; diff --git a/packages/website/ts/components/ui/labeled_switcher.tsx b/packages/website/ts/components/ui/labeled_switcher.tsx new file mode 100644 index 000000000..3ed8ba0a4 --- /dev/null +++ b/packages/website/ts/components/ui/labeled_switcher.tsx @@ -0,0 +1,76 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import {colors} from 'material-ui/styles'; + +const CUSTOM_BLUE = '#63A6F1'; + +interface LabeledSwitcherProps { + labelLeft: string; + labelRight: string; + isLeftInitiallySelected: boolean; + onLeftLabelClickAsync: () => Promise; + onRightLabelClickAsync: () => Promise; +} + +interface LabeledSwitcherState { + isLeftSelected: boolean; +} + +export class LabeledSwitcher extends React.Component { + constructor(props: LabeledSwitcherProps) { + super(props); + this.state = { + isLeftSelected: props.isLeftInitiallySelected, + }; + } + public render() { + const isLeft = true; + return ( +
+ {this.renderLabel(this.props.labelLeft, isLeft, this.state.isLeftSelected)} + {this.renderLabel(this.props.labelRight, !isLeft, !this.state.isLeftSelected)} +
+ ); + } + private renderLabel(title: string, isLeft: boolean, isSelected: boolean) { + const borderStyle = `2px solid ${isSelected ? '#4F8BCF' : '#DADADA'}`; + const style = { + cursor: 'pointer', + backgroundColor: isSelected ? CUSTOM_BLUE : colors.grey200, + color: isSelected ? 'white' : '#A5A5A5', + boxShadow: isSelected ? `inset 0px 0px 4px #4083CE` : 'inset 0px 0px 4px #F7F6F6', + borderTop: borderStyle, + borderBottom: borderStyle, + [isLeft ? 'borderLeft' : 'borderRight']: borderStyle, + paddingTop: 12, + paddingBottom: 12, + }; + return ( +
+ {title} +
+ ); + } + private async onLabelClickAsync(isLeft: boolean): Promise { + this.setState({ + isLeftSelected: isLeft, + }); + let didSucceed; + if (isLeft) { + didSucceed = await this.props.onLeftLabelClickAsync(); + } else { + didSucceed = await this.props.onRightLabelClickAsync(); + } + if (!didSucceed) { + this.setState({ + isLeftSelected: !isLeft, + }); + } + } +} diff --git a/packages/website/ts/components/ui/lifecycle_raised_button.tsx b/packages/website/ts/components/ui/lifecycle_raised_button.tsx new file mode 100644 index 000000000..e93c80ba4 --- /dev/null +++ b/packages/website/ts/components/ui/lifecycle_raised_button.tsx @@ -0,0 +1,105 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import {utils} from 'ts/utils/utils'; +import {Token} from 'ts/types'; +import {Blockchain} from 'ts/blockchain'; +import RaisedButton from 'material-ui/RaisedButton'; + +const COMPLETE_STATE_SHOW_LENGTH_MS = 2000; + +enum ButtonState { + READY, + LOADING, + COMPLETE, +}; + +interface LifeCycleRaisedButtonProps { + isHidden?: boolean; + isDisabled?: boolean; + isPrimary?: boolean; + labelReady: React.ReactNode|string; + labelLoading: React.ReactNode|string; + labelComplete: React.ReactNode|string; + onClickAsyncFn: () => boolean; + backgroundColor?: string; + labelColor?: string; +} + +interface LifeCycleRaisedButtonState { + buttonState: ButtonState; +} + +export class LifeCycleRaisedButton extends + React.Component { + public static defaultProps: Partial = { + isDisabled: false, + backgroundColor: 'white', + labelColor: 'rgb(97, 97, 97)', + }; + private buttonTimeoutId: number; + private didUnmount: boolean; + constructor(props: LifeCycleRaisedButtonProps) { + super(props); + this.state = { + buttonState: ButtonState.READY, + }; + } + public componentWillUnmount() { + clearTimeout(this.buttonTimeoutId); + this.didUnmount = true; + } + public render() { + if (this.props.isHidden === true) { + return ; + } + + let label; + switch (this.state.buttonState) { + case ButtonState.READY: + label = this.props.labelReady; + break; + case ButtonState.LOADING: + label = this.props.labelLoading; + break; + case ButtonState.COMPLETE: + label = this.props.labelComplete; + break; + default: + throw utils.spawnSwitchErr('ButtonState', this.state.buttonState); + } + return ( + + ); + } + public async onClickAsync() { + this.setState({ + buttonState: ButtonState.LOADING, + }); + const didSucceed = await this.props.onClickAsyncFn(); + if (this.didUnmount) { + return; // noop since unmount called before async callback returned. + } + if (didSucceed) { + this.setState({ + buttonState: ButtonState.COMPLETE, + }); + this.buttonTimeoutId = window.setTimeout(() => { + this.setState({ + buttonState: ButtonState.READY, + }); + }, COMPLETE_STATE_SHOW_LENGTH_MS); + } else { + this.setState({ + buttonState: ButtonState.READY, + }); + } + } +} diff --git a/packages/website/ts/components/ui/loading.tsx b/packages/website/ts/components/ui/loading.tsx new file mode 100644 index 000000000..39c119d8f --- /dev/null +++ b/packages/website/ts/components/ui/loading.tsx @@ -0,0 +1,36 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import Paper from 'material-ui/Paper'; +import {utils} from 'ts/utils/utils'; +import {DefaultPlayer as Video} from 'react-html5video'; +import 'react-html5video/dist/styles.css'; + +interface LoadingProps {} + +interface LoadingState {} + +export class Loading extends React.Component { + public render() { + return ( +
+ + {utils.isUserOnMobile() ? + : +
+ +
+ } +
Connecting to the blockchain...
+
+
+ ); + } +} diff --git a/packages/website/ts/components/ui/menu_item.tsx b/packages/website/ts/components/ui/menu_item.tsx new file mode 100644 index 000000000..b9caa91fb --- /dev/null +++ b/packages/website/ts/components/ui/menu_item.tsx @@ -0,0 +1,54 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import {Link} from 'react-router-dom'; +import {Styles} from 'ts/types'; +import {constants} from 'ts/utils/constants'; +import {colors} from 'material-ui/styles'; + +interface MenuItemProps { + to: string; + style?: React.CSSProperties; + onClick?: () => void; + className?: string; +} + +interface MenuItemState { + isHovering: boolean; +} + +export class MenuItem extends React.Component { + public static defaultProps: Partial = { + onClick: _.noop, + className: '', + }; + public constructor(props: MenuItemProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public render() { + const menuItemStyles = { + cursor: 'pointer', + opacity: this.state.isHovering ? 0.5 : 1, + }; + return ( + +
+ {this.props.children} +
+ + ); + } + private onToggleHover(isHovering: boolean) { + this.setState({ + isHovering, + }); + } +} diff --git a/packages/website/ts/components/ui/party.tsx b/packages/website/ts/components/ui/party.tsx new file mode 100644 index 000000000..b72e75181 --- /dev/null +++ b/packages/website/ts/components/ui/party.tsx @@ -0,0 +1,150 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import ReactTooltip = require('react-tooltip'); +import {colors} from 'material-ui/styles'; +import {Identicon} from 'ts/components/ui/identicon'; +import {EtherscanLinkSuffixes} from 'ts/types'; +import {utils} from 'ts/utils/utils'; +import {EthereumAddress} from 'ts/components/ui/ethereum_address'; + +const MIN_ADDRESS_WIDTH = 60; +const IMAGE_DIMENSION = 100; +const IDENTICON_DIAMETER = 95; +const CHECK_MARK_GREEN = 'rgb(0, 195, 62)'; + +interface PartyProps { + label: string; + address: string; + networkId: number; + alternativeImage?: string; + identiconDiameter?: number; + identiconStyle?: React.CSSProperties; + isInTokenRegistry?: boolean; + hasUniqueNameAndSymbol?: boolean; +} + +interface PartyState {} + +export class Party extends React.Component { + public static defaultProps: Partial = { + identiconStyle: {}, + identiconDiameter: IDENTICON_DIAMETER, + }; + public render() { + const label = this.props.label; + const address = this.props.address; + const tooltipId = `${label}-${address}-tooltip`; + const identiconDiameter = this.props.identiconDiameter; + const addressWidth = identiconDiameter > MIN_ADDRESS_WIDTH ? + identiconDiameter : MIN_ADDRESS_WIDTH; + const emptyIdenticonStyles = { + width: identiconDiameter, + height: identiconDiameter, + backgroundColor: 'lightgray', + marginTop: 13, + marginBottom: 10, + }; + const tokenImageStyle = { + width: IMAGE_DIMENSION, + height: IMAGE_DIMENSION, + }; + const etherscanLinkIfExists = utils.getEtherScanLinkIfExists( + this.props.address, this.props.networkId, EtherscanLinkSuffixes.address, + ); + const isRegistered = this.props.isInTokenRegistry; + const registeredTooltipId = `${this.props.address}-${isRegistered}-registeredTooltip`; + const uniqueNameAndSymbolTooltipId = `${this.props.address}-${isRegistered}-uniqueTooltip`; + return ( +
+
{label}
+ {_.isEmpty(address) ? +
: + + {isRegistered && !_.isUndefined(this.props.alternativeImage) ? + : +
+ +
+ } +
+ } +
+
+ +
+ {!_.isUndefined(this.props.isInTokenRegistry) && +
+
+ + + {' '} + {isRegistered ? 'Registered' : 'Unregistered'} token + + {isRegistered ? +
+ This token address was found in the token registry
+ smart contract and is therefore believed to be a
+ legitimate token. +
: +
+ This token is not included in the token registry
+ smart contract. We cannot guarantee the legitimacy
+ of this token. Make sure to verify its address on Etherscan. +
+ } +
+
+
+ } + {!_.isUndefined(this.props.hasUniqueNameAndSymbol) && !this.props.hasUniqueNameAndSymbol && +
+
+ + + {' '} + Suspicious token + + This token shares it's name, symbol or both with
+ a token in the 0x Token Registry but it has a different
+ smart contract address. This is most likely a scam token! +
+
+
+ } +
+
+ ); + } +} diff --git a/packages/website/ts/components/ui/required_label.tsx b/packages/website/ts/components/ui/required_label.tsx new file mode 100644 index 000000000..f9c73157a --- /dev/null +++ b/packages/website/ts/components/ui/required_label.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import {colors} from 'material-ui/styles'; + +export interface RequiredLabelProps { + label: string|React.ReactNode; +} + +export const RequiredLabel = (props: RequiredLabelProps) => { + return ( + + {props.label} + * + + ); +}; diff --git a/packages/website/ts/components/ui/simple_loading.tsx b/packages/website/ts/components/ui/simple_loading.tsx new file mode 100644 index 000000000..12d09ecc4 --- /dev/null +++ b/packages/website/ts/components/ui/simple_loading.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import {colors} from 'material-ui/styles'; +import CircularProgress from 'material-ui/CircularProgress'; + +export interface SimpleLoadingProps { + message: string; +} + +export const SimpleLoading = (props: SimpleLoadingProps) => { + return ( +
+
+ +
+ {props.message} +
+
+
+ ); +}; diff --git a/packages/website/ts/components/ui/swap_icon.tsx b/packages/website/ts/components/ui/swap_icon.tsx new file mode 100644 index 000000000..89bb33d55 --- /dev/null +++ b/packages/website/ts/components/ui/swap_icon.tsx @@ -0,0 +1,46 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import {constants} from 'ts/utils/constants'; +import {colors} from 'material-ui/styles'; + +interface SwapIconProps { + swapTokensFn: () => void; +} + +interface SwapIconState { + isHovering: boolean; +} + +export class SwapIcon extends React.Component { + public constructor(props: SwapIconProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public render() { + const swapStyles = { + color: this.state.isHovering ? colors.amber600 : colors.amber800, + fontSize: 50, + }; + return ( +
+ +
+ ); + } + private onToggleHover(isHovering: boolean) { + this.setState({ + isHovering, + }); + } +} diff --git a/packages/website/ts/components/ui/token_icon.tsx b/packages/website/ts/components/ui/token_icon.tsx new file mode 100644 index 000000000..168c09bd4 --- /dev/null +++ b/packages/website/ts/components/ui/token_icon.tsx @@ -0,0 +1,29 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import {Token} from 'ts/types'; +import {Identicon} from 'ts/components/ui/identicon'; + +interface TokenIconProps { + token: Token; + diameter: number; +} + +interface TokenIconState {} + +export class TokenIcon extends React.Component { + public render() { + const token = this.props.token; + const diameter = this.props.diameter; + return ( +
+ {(token.isRegistered && !_.isUndefined(token.iconUrl)) ? + : + + } +
+ ); + } +} -- cgit v1.2.3