diff --git a/.babelrc b/.babelrc index 53b6a302..f0a0c0bc 100644 --- a/.babelrc +++ b/.babelrc @@ -16,18 +16,7 @@ "regenerator": true }], ["module-resolver", { - "root": ["./src"], - "alias": { - "config": "./src/js/config", - "constants": "./src/js/constants", - "components": "./src/js/components", - "actions": "./src/js/actions", - "reducers": "./src/js/reducers", - "support": "./src/js/support", - "utils": "./src/js/utils", - "services": "./src/js/services", - "views": "./src/js/views" - } + "root": ["./src"] }], "babel-plugin-styled-components" ], diff --git a/.eslintignore b/.eslintignore index 813e13d4..4c35d7b5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,4 @@ -solidity \ No newline at end of file +solidity +coverage +images +node_modules \ No newline at end of file diff --git a/.flowconfig b/.flowconfig index faae2174..b28be239 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,6 +1,7 @@ [include] [ignore] +.*/node_modules/fbjs/.* .*/node_modules/rc-util/.* .*/node_modules/react-redux/.* .*/node_modules/redux/.* @@ -20,8 +21,6 @@ ./src/flowtype/npm/web3.js ./src/flowtype/css.js - - [options] esproposal.class_static_fields=enable esproposal.class_instance_fields=enable @@ -31,13 +30,15 @@ esproposal.decorators=ignore module.name_mapper='.*\(.less\)' -> 'CSSModule' module.name_mapper='^\(~/\)' -> '/src/' module.name_mapper='^universal' -> '/src/' -module.name_mapper='^config' -> '/src/js/config' -module.name_mapper='^constants' -> '/src/js/constants' -module.name_mapper='^components' -> '/src/js/components' -module.name_mapper='^actions' -> '/src/js/actions' -module.name_mapper='^reducers' -> '/src/js/reducers' -module.name_mapper='^support' -> '/src/js/support' -module.name_mapper='^utils' -> '/src/js/utils' -module.name_mapper='^services' -> '/src/js/services' -module.name_mapper='^views' -> '/src/js/views' +module.name_mapper='^flowtype' -> '/src/flowtype' +module.name_mapper='^components' -> '/src/components' +module.name_mapper='^config' -> '/src/config' +module.name_mapper='^constants' -> '/src/constants' +module.name_mapper='^utils' -> '/src/utils' +module.name_mapper='^reducers' -> '/src/reducers' +module.name_mapper='^actions' -> '/src/actions' +module.name_mapper='^views' -> '/src/views' +module.name_mapper='^data' -> '/src/data' +module.name_mapper='^services' -> '/src/services' +module.name_mapper='^support' -> '/src/support' module.system=haste diff --git a/.stylelintrc b/.stylelintrc index 045010a1..b8426f0f 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -8,6 +8,24 @@ ], "rules": { "indentation": 4, - "block-no-empty": null + "declaration-empty-line-before": null, + "custom-property-empty-line-before": null, + "max-empty-lines": 1, + "block-no-empty": null, + "property-no-unknown": [ + true, { + "ignoreProperties": ["composes", "font-smoothing", "font-smooth"] + } + ], + "at-rule-empty-line-before": [ + "always", { + "ignoreAtRules": [ + "import" + ] + } + ], + "at-rule-no-unknown": [ true, { + ignoreAtRules: ["each"] + }] } } \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 6b9dcaaa..d7e1c86a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,11 +2,10 @@ module.exports = { rootDir: './src', collectCoverage: true, testURL: 'http://localhost', - modulePathIgnorePatterns: [ 'node_modules', ], collectCoverageFrom: [ - 'js/utils/**.js', + 'utils/**.js', ], }; diff --git a/jsconfig.json b/jsconfig.json index d48f8ef8..2181c8a7 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -2,16 +2,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "*": ["src/*"], - "components/*": ["./src/js/components/*"], - "config/*": ["./src/js/config/*"], - "constants/*": ["./src/js/constants/*"], - "support/*": ["./src/js/support/*"], - "actions/*": ["./src/js/actions/*"], - "reducers/*": ["./src/js/reducers/*"], - "utils/*": ["./src/js/utils/*"], - "services/*": ["./src/js/services/*"], - "views/*": ["./src/js/views/*"], + "*": ["src/*"] } } } \ No newline at end of file diff --git a/package.json b/package.json index 9f764b5f..e627a418 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,8 @@ { - "name": "trezor-connect-react-boilerplate", + "name": "trezor-wallet", "version": "1.0.0", "author": "TREZOR ", "description": "", - "repository": { - "type": "git", - "url": "https://github.com/szymonlesisz/trezor-connect-react-boilerplate.git" - }, "bugs": { "url": "https://github.com/szymonlesisz/trezor-connect-react-boilerplate/issues" }, @@ -18,13 +14,13 @@ "dev": "webpack-dev-server --config ./webpack/config.dev.babel.js --mode development", "dev:local": "webpack-dev-server --config ./webpack/config.dev.local.babel.js --mode development", "build": "rm -rf build && webpack --config ./webpack/config.prod.babel.js --progress", - "flow": "flow check src/js", + "flow": "flow check src", "lint": "run-s lint:*", - "lint:js": "eslint ./src/js ./webpack", - "lint:css": "stylelint './src/js/**/*.js'", - "test:*": "run-s test:*", + "lint:js": "eslint ./src ./webpack", + "lint:css": "stylelint './src/**/*.js'", + "test": "run-s test:*", "test:unit": "jest", - "test-unit:watch": "jest -o --watchAll" + "test-unit:watch": "jest -o --watch" }, "dependencies": { "babel": "^6.23.0", @@ -52,8 +48,9 @@ "react-router-dom": "^4.2.2", "react-router-redux": "next", "react-scale-text": "^1.2.2", - "react-select": "^1.2.1", + "react-select": "2.0.0", "react-transition-group": "^2.2.1", + "redbox-react": "^1.6.0", "redux": "4.0.0", "redux-logger": "^3.0.6", "redux-raven-middleware": "^1.2.0", diff --git a/src/js/actions/AccountsActions.js b/src/actions/AccountsActions.js similarity index 100% rename from src/js/actions/AccountsActions.js rename to src/actions/AccountsActions.js diff --git a/src/js/actions/DiscoveryActions.js b/src/actions/DiscoveryActions.js similarity index 100% rename from src/js/actions/DiscoveryActions.js rename to src/actions/DiscoveryActions.js diff --git a/src/js/actions/HistoryActions.js b/src/actions/HistoryActions.js similarity index 100% rename from src/js/actions/HistoryActions.js rename to src/actions/HistoryActions.js diff --git a/src/js/actions/LocalStorageActions.js b/src/actions/LocalStorageActions.js similarity index 100% rename from src/js/actions/LocalStorageActions.js rename to src/actions/LocalStorageActions.js diff --git a/src/js/actions/LogActions.js b/src/actions/LogActions.js similarity index 100% rename from src/js/actions/LogActions.js rename to src/actions/LogActions.js diff --git a/src/js/actions/ModalActions.js b/src/actions/ModalActions.js similarity index 98% rename from src/js/actions/ModalActions.js rename to src/actions/ModalActions.js index 35266e27..7cc0fc96 100644 --- a/src/js/actions/ModalActions.js +++ b/src/actions/ModalActions.js @@ -1,7 +1,7 @@ /* @flow */ - import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect'; +import type { Device } from 'trezor-connect'; import * as MODAL from 'actions/constants/modal'; import * as CONNECT from 'actions/constants/TrezorConnect'; diff --git a/src/js/actions/NotificationActions.js b/src/actions/NotificationActions.js similarity index 100% rename from src/js/actions/NotificationActions.js rename to src/actions/NotificationActions.js diff --git a/src/js/actions/PendingTxActions.js b/src/actions/PendingTxActions.js similarity index 100% rename from src/js/actions/PendingTxActions.js rename to src/actions/PendingTxActions.js diff --git a/src/js/actions/ReceiveActions.js b/src/actions/ReceiveActions.js similarity index 100% rename from src/js/actions/ReceiveActions.js rename to src/actions/ReceiveActions.js diff --git a/src/js/actions/SelectedAccountActions.js b/src/actions/SelectedAccountActions.js similarity index 100% rename from src/js/actions/SelectedAccountActions.js rename to src/actions/SelectedAccountActions.js diff --git a/src/js/actions/SendFormActions.js b/src/actions/SendFormActions.js similarity index 99% rename from src/js/actions/SendFormActions.js rename to src/actions/SendFormActions.js index 9034d433..8455eb38 100644 --- a/src/js/actions/SendFormActions.js +++ b/src/actions/SendFormActions.js @@ -33,7 +33,7 @@ import type { Config, Coin } from 'reducers/LocalStorageReducer'; import type { Token } from 'reducers/TokensReducer'; import type { State, FeeLevel } from 'reducers/SendFormReducer'; import type { Account } from 'reducers/AccountsReducer'; -import type { Props } from 'components/wallet/account/send'; +import type { Props } from 'views/Wallet/views/AccountSend/Container'; import * as SessionStorageActions from './SessionStorageActions'; import { estimateGas, getGasPrice, pushTx } from './Web3Actions'; diff --git a/src/js/actions/SessionStorageActions.js b/src/actions/SessionStorageActions.js similarity index 100% rename from src/js/actions/SessionStorageActions.js rename to src/actions/SessionStorageActions.js diff --git a/src/js/actions/SummaryActions.js b/src/actions/SummaryActions.js similarity index 100% rename from src/js/actions/SummaryActions.js rename to src/actions/SummaryActions.js diff --git a/src/js/actions/TokenActions.js b/src/actions/TokenActions.js similarity index 89% rename from src/js/actions/TokenActions.js rename to src/actions/TokenActions.js index a986ac76..46a7772f 100644 --- a/src/js/actions/TokenActions.js +++ b/src/actions/TokenActions.js @@ -37,20 +37,21 @@ export const load = (input: string, network: string): AsyncAction => async (disp const tokens = getState().localStorage.tokens[network]; const value = input.toLowerCase(); const result = tokens.filter(t => t.symbol.toLowerCase().indexOf(value) >= 0 - || t.address.toLowerCase().indexOf(value) >= 0 - || t.name.toLowerCase().indexOf(value) >= 0); + || t.address.toLowerCase().indexOf(value) >= 0 + || t.name.toLowerCase().indexOf(value) >= 0); if (result.length > 0) { - return { options: result }; + // TODO: Temporary fix for async select + // async react-select starts getting very laggy + // when options is a large list (>200 items) + return result.slice(0, 100); } const web3instance = getState().web3.find(w3 => w3.network === network); if (!web3instance) return; const info = await getTokenInfoAsync(web3instance.erc20, input); if (info) { - return { - options: [info], - }; + return [info]; } //await resolveAfter(300000); //await resolveAfter(3000); diff --git a/src/js/actions/TrezorConnectActions.js b/src/actions/TrezorConnectActions.js similarity index 100% rename from src/js/actions/TrezorConnectActions.js rename to src/actions/TrezorConnectActions.js diff --git a/src/js/actions/WalletActions.js b/src/actions/WalletActions.js similarity index 100% rename from src/js/actions/WalletActions.js rename to src/actions/WalletActions.js diff --git a/src/js/actions/Web3Actions.js b/src/actions/Web3Actions.js similarity index 100% rename from src/js/actions/Web3Actions.js rename to src/actions/Web3Actions.js diff --git a/src/js/actions/constants/TrezorConnect.js b/src/actions/constants/TrezorConnect.js similarity index 100% rename from src/js/actions/constants/TrezorConnect.js rename to src/actions/constants/TrezorConnect.js diff --git a/src/js/actions/constants/account.js b/src/actions/constants/account.js similarity index 100% rename from src/js/actions/constants/account.js rename to src/actions/constants/account.js diff --git a/src/js/actions/constants/discovery.js b/src/actions/constants/discovery.js similarity index 100% rename from src/js/actions/constants/discovery.js rename to src/actions/constants/discovery.js diff --git a/src/js/actions/constants/localStorage.js b/src/actions/constants/localStorage.js similarity index 100% rename from src/js/actions/constants/localStorage.js rename to src/actions/constants/localStorage.js diff --git a/src/js/actions/constants/log.js b/src/actions/constants/log.js similarity index 100% rename from src/js/actions/constants/log.js rename to src/actions/constants/log.js diff --git a/src/js/actions/constants/modal.js b/src/actions/constants/modal.js similarity index 100% rename from src/js/actions/constants/modal.js rename to src/actions/constants/modal.js diff --git a/src/js/actions/constants/notification.js b/src/actions/constants/notification.js similarity index 100% rename from src/js/actions/constants/notification.js rename to src/actions/constants/notification.js diff --git a/src/js/actions/constants/pendingTx.js b/src/actions/constants/pendingTx.js similarity index 100% rename from src/js/actions/constants/pendingTx.js rename to src/actions/constants/pendingTx.js diff --git a/src/js/actions/constants/receive.js b/src/actions/constants/receive.js similarity index 100% rename from src/js/actions/constants/receive.js rename to src/actions/constants/receive.js diff --git a/src/js/actions/constants/send.js b/src/actions/constants/send.js similarity index 100% rename from src/js/actions/constants/send.js rename to src/actions/constants/send.js diff --git a/src/js/actions/constants/summary.js b/src/actions/constants/summary.js similarity index 100% rename from src/js/actions/constants/summary.js rename to src/actions/constants/summary.js diff --git a/src/js/actions/constants/token.js b/src/actions/constants/token.js similarity index 100% rename from src/js/actions/constants/token.js rename to src/actions/constants/token.js diff --git a/src/js/actions/constants/wallet.js b/src/actions/constants/wallet.js similarity index 100% rename from src/js/actions/constants/wallet.js rename to src/actions/constants/wallet.js diff --git a/src/js/actions/constants/web3.js b/src/actions/constants/web3.js similarity index 100% rename from src/js/actions/constants/web3.js rename to src/actions/constants/web3.js diff --git a/src/components/Checkbox/index.js b/src/components/Checkbox/index.js new file mode 100644 index 00000000..1f4eef2b --- /dev/null +++ b/src/components/Checkbox/index.js @@ -0,0 +1,94 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; +import colors from 'config/colors'; +import Icon from 'components/Icon'; +import icons from 'config/icons'; +import { FONT_SIZE } from 'config/variables'; + +const Wrapper = styled.div` + display: flex; + flex-direction: row; + cursor: pointer; + align-items: center; + + &:hover, + &:focus { + outline: none; + } +`; + +const Tick = styled.div` +`; + +const IconWrapper = styled.div` + display: flex; + border-radius: 2px; + justify-content: center; + align-items: center; + color: ${props => (props.checked ? colors.WHITE : colors.GREEN_PRIMARY)}; + background: ${props => (props.checked ? colors.GREEN_PRIMARY : colors.WHITE)}; + border: 1px solid ${props => (props.checked ? colors.GREEN_PRIMARY : colors.DIVIDER)}; + width: 24px; + height: 24px; + + &:hover, + &:focus { + border: 1px solid ${colors.TEXT_PRIMARY}; + background: ${props => (props.checked ? colors.TEXT_PRIMARY : colors.WHITE)}; + } +`; + +const Label = styled.div` + display: flex; + padding-left: 10px; + justify-content: center; + ${colors.TEXT_SECONDARY}; + font-size: ${FONT_SIZE.SMALL}; + + &:hover, + &:focus { + color: ${props => (props.checked ? colors.TEXT_PRIMARY : colors.TEXT_PRIMARY)}; + } +`; + +class Checkbox extends PureComponent { + handleKeyboard(e) { + if (e.keyCode === 32) { + this.props.onClick(e); + } + } + + render() { + const { + checked, + children, + onClick, + } = this.props; + return ( + this.handleKeyboard(e)} + tabIndex={0} + > + + {checked && ( + + + + ) + } + + + + ); + } +} + +Checkbox.propTypes = { + onClick: PropTypes.func.isRequired, + checked: PropTypes.bool, + children: PropTypes.string, +}; + +export default Checkbox; diff --git a/src/components/DeviceHeader/index.js b/src/components/DeviceHeader/index.js new file mode 100644 index 00000000..2bf43108 --- /dev/null +++ b/src/components/DeviceHeader/index.js @@ -0,0 +1,176 @@ +import React, { Component } from 'react'; +import styled, { css } from 'styled-components'; +import PropTypes from 'prop-types'; +import Icon from 'components/Icon'; +import icons from 'config/icons'; +import { + getStatusColor, + getStatusName, + isDisabled, + getStatus, + getVersion, +} from 'utils/device'; +import TrezorImage from 'components/images/TrezorImage'; +import colors from 'config/colors'; + +const Wrapper = styled.div` + position: relative; + height: 64px; + width: 320px; + display: flex; + align-items: center; + background: ${colors.WHITE}; + border-radius: 4px 0 0 0; + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04); + + ${props => props.isOpen && css` + box-shadow: none; + `} + + ${props => props.isHoverable && css` + &:hover { + background: ${colors.GRAY_LIGHT}; + } + `} +`; + +const ClickWrapper = styled.div` + width: 100%; + display: flex; + padding-left: 25px; + height: 100%; + align-items: center; + cursor: pointer; + + ${props => props.disabled && css` + cursor: initial; + `} +`; + +const LabelWrapper = styled.div` + flex: 1; + padding-left: 18px; +`; + +const Name = styled.div` + display: block; + text-overflow: ellipsis; + overflow: hidden; + white-space: no-wrap; + font-weight: 500; + font-size: 14px; + color: ${colors.TEXT_PRIMARY}; +`; + +const Status = styled.div` + display: block; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + font-size: 12px; + color: ${colors.TEXT_SECONDARY}; +`; + +const Counter = styled.div` + border: 1px solid ${colors.DIVIDER}; + border-radius: 50%; + color: ${colors.TEXT_SECONDARY}; + width: 24px; + height: 24px; + line-height: 22px; + text-align: center; + font-size: 11px; + margin-right: 8px; +`; + +const IconWrapper = styled.div` + padding-right: 25px; + display: flex; +`; + +const ImageWrapper = styled.div` + position: relative; +`; + +const Dot = styled.div` + border: 2px solid ${colors.WHITE}; + border-radius: 50%; + position: absolute; + z-index: 10; + background: ${props => props.color}; + top: -4px; + right: -3px; + width: 10px; + height: 10px; +`; + +class DeviceHeader extends Component { + constructor(props) { + super(props); + this.state = { + clicked: false, + }; + } + + isDisabled(device, devices, transport) { + return isDisabled(device, devices, transport); + } + + handleClickWrapper() { + this.setState({ clicked: true }); + if (!this.props.disabled) { + this.props.onClickWrapper(); + } + } + + render() { + const { + isOpen, icon, device, devices, transport, isHoverable, + } = this.props; + const status = getStatus(device); + const disabled = isDisabled(device, devices, transport); + const deviceCount = devices.length; + + return ( + + this.handleClickWrapper()}> + + + + + + {device.instanceLabel} + {getStatusName(status)} + + + {icon && icon} + {!icon && deviceCount > 1 && {deviceCount}} + {!icon && !disabled && ( + + ) + } + + + + ); + } +} + +DeviceHeader.propTypes = { + device: PropTypes.object, + devices: PropTypes.array, + transport: PropTypes.object, + icon: PropTypes.element, + isHoverable: PropTypes.bool, + disabled: PropTypes.bool, + isOpen: PropTypes.bool, + onClickWrapper: PropTypes.func.isRequired, +}; + +export default DeviceHeader; diff --git a/src/js/components/Footer/index.js b/src/components/Footer/index.js similarity index 65% rename from src/js/components/Footer/index.js rename to src/components/Footer/index.js index baac3557..602ba777 100644 --- a/src/js/components/Footer/index.js +++ b/src/components/Footer/index.js @@ -1,6 +1,7 @@ import styled from 'styled-components'; import PropTypes from 'prop-types'; import React from 'react'; +import Link from 'components/Link'; import { getYear } from 'date-fns'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; @@ -11,13 +12,13 @@ import * as LogActions from 'actions/LogActions'; const Wrapper = styled.div` width: 100%; font-size: 12px; + background: ${colors.LANDING}; color: ${colors.TEXT_SECONDARY}; padding: 22px 48px; - border-top: 1px solid ${colors.DIVIDER}; display: flex; `; -const A = styled.a` +const StyledLink = styled(Link)` margin: 0 6px; margin-right: 20px; `; @@ -28,10 +29,10 @@ const Copy = styled.div` const Footer = ({ toggle }) => ( - © {getYear(new Date())} - SatoshiLabs - Terms - Show Log + © {getYear(new Date())} + SatoshiLabs + Terms + Show Log ); diff --git a/src/js/components/Header/index.js b/src/components/Header/index.js similarity index 97% rename from src/js/components/Header/index.js rename to src/components/Header/index.js index 987c7ded..b64301c5 100644 --- a/src/js/components/Header/index.js +++ b/src/components/Header/index.js @@ -24,9 +24,12 @@ const LayoutWrapper = styled.div` height: 100%; max-width: 1170px; margin: 0 auto; - padding: 0 32px; display: flex; align-items: center; + + @media screen and (max-width: 1170px) { + padding: 0 25px; + } `; const A = styled.a` diff --git a/src/js/components/Heading/index.js b/src/components/Heading/index.js similarity index 94% rename from src/js/components/Heading/index.js rename to src/components/Heading/index.js index 899bcac8..af5ef892 100644 --- a/src/js/components/Heading/index.js +++ b/src/components/Heading/index.js @@ -4,7 +4,7 @@ import colors from 'config/colors'; const baseStyles = css` -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; - color: ${colors.BLACK}; + color: ${colors.TEXT_PRIMARY}; font-weight: bold; `; diff --git a/src/components/Icon/index.js b/src/components/Icon/index.js new file mode 100644 index 00000000..88a1d332 --- /dev/null +++ b/src/components/Icon/index.js @@ -0,0 +1,83 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import styled, { keyframes } from 'styled-components'; + +// TODO: make animation of icons better +const rotate180up = keyframes` + from { + transform: rotate(0deg); + } + to { + transform: rotate(180deg); + } +`; + +const rotate180down = keyframes` + from { + transform: rotate(180deg); + } + to { + transform: rotate(0deg); + } +`; + +const SvgWrapper = styled.svg` + animation: ${props => (props.canAnimate ? (props.isActive ? rotate180up : rotate180down) : null)} 0.2s linear 1 forwards; +`; + +const Path = styled.path``; + +const Icon = ({ + icon, + size = 32, + color = 'black', + isActive, + canAnimate, + className, + onMouseEnter, + onMouseLeave, + onFocus, + onClick, +}) => ( + + {icon.map(path => ( + + ))} + +); + +Icon.propTypes = { + className: PropTypes.string, + canAnimate: PropTypes.bool, + icon: PropTypes.arrayOf(PropTypes.string).isRequired, + size: PropTypes.number, + isActive: PropTypes.bool, + color: PropTypes.string, + onMouseEnter: PropTypes.func, + onMouseLeave: PropTypes.func, + onFocus: PropTypes.func, + onClick: PropTypes.func, +}; + + +export default Icon; \ No newline at end of file diff --git a/src/components/Link/index.js b/src/components/Link/index.js new file mode 100644 index 00000000..8040a1b4 --- /dev/null +++ b/src/components/Link/index.js @@ -0,0 +1,67 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; +import PropTypes from 'prop-types'; +import { FONT_SIZE, TRANSITION } from 'config/variables'; +import colors from 'config/colors'; + +const A = styled.a` + text-decoration: none; + cursor: pointer; + transition: ${TRANSITION.HOVER}; + font-size: ${FONT_SIZE.SMALLER}; + + ${props => props.isGreen && css` + border-bottom: 1px solid ${colors.GREEN_PRIMARY}; + `} + ${props => props.isGray && css` + border-bottom: 1px solid ${colors.TEXT_SECONDARY}; + `} + + &, + &:visited, + &:active, + &:hover { + ${props => props.isGreen && css` + color: ${colors.GREEN_PRIMARY}; + `} + ${props => props.isGray && css` + color: ${colors.TEXT_SECONDARY}; + `} + } + + &:hover { + border-color: transparent; + } +`; + +const Link = ({ + children, className, href, target, rel, onClick, isGreen = false, isGray = false, +}) => ( + {children} + +); + +Link.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.object, + PropTypes.array, + ]).isRequired, + className: PropTypes.string, + href: PropTypes.string, + target: PropTypes.string, + rel: PropTypes.string, + onClick: PropTypes.func, + isGreen: PropTypes.bool, + isGray: PropTypes.bool, +}; + +export default Link; diff --git a/src/components/Loader/index.js b/src/components/Loader/index.js new file mode 100644 index 00000000..d4f8f4c4 --- /dev/null +++ b/src/components/Loader/index.js @@ -0,0 +1,81 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; +import PropTypes from 'prop-types'; +import Paragraph from 'components/Paragraph'; +import { FONT_SIZE } from 'config/variables'; +import { DASH, GREEN_COLOR } from 'config/animations'; +import colors from 'config/colors'; + +const Wrapper = styled.div` + position: relative; + width: ${props => `${props.size}px`}; + height: ${props => `${props.size}px`}; + display: flex; + justify-content: center; + align-items: center; +`; + +const SvgWrapper = styled.svg` + position: absolute; + width: 100%; + height: 100%; + animation: rotate 2s linear infinite; + transform-origin: center center; +`; + +const CircleWrapper = styled.circle` + ${props => props.isRoute && css` + stroke: ${colors.GRAY_LIGHT}; + `} + + ${props => props.isPath && css` + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + animation: ${DASH} 1.5s ease-in-out infinite, ${GREEN_COLOR} 6s ease-in-out infinite; + stroke-linecap: round; + `}; +`; + +const StyledParagraph = styled(Paragraph)` + font-size: ${props => (props.isSmallText ? FONT_SIZE.SMALLER : FONT_SIZE.BASE)}; + color: ${props => (props.isWhiteText ? colors.WHITE : colors.TEXT_PRIMARY)}; +`; + +const Loader = ({ + className, text, isWhiteText = false, isSmallText, size = 100, +}) => ( + + {text} + + + + + +); + +Loader.propTypes = { + isWhiteText: PropTypes.bool, + isSmallText: PropTypes.bool, + className: PropTypes.string, + text: PropTypes.string, + size: PropTypes.number, +}; + +export default Loader; diff --git a/src/components/Log/index.js b/src/components/Log/index.js new file mode 100644 index 00000000..63db62fa --- /dev/null +++ b/src/components/Log/index.js @@ -0,0 +1,83 @@ +/* @flow */ +import React from 'react'; +import styled from 'styled-components'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import colors from 'config/colors'; +import { H2 } from 'components/Heading'; +import Icon from 'components/Icon'; +import Paragraph from 'components/Paragraph'; + +import * as LogActions from 'actions/LogActions'; +import icons from 'config/icons'; +import type { State, Dispatch } from 'flowtype'; + +type Props = { + log: $ElementType, + toggle: typeof LogActions.toggle +} + +const Wrapper = styled.div` + position: relative; + color: ${colors.INFO_PRIMARY}; + background: ${colors.INFO_SECONDARY}; + padding: 24px 48px; + display: flex; + flex-direction: column; + text-align: left; +`; + +const Click = styled.div` + cursor: pointer; + position: absolute; + top: 8px; + right: 0; + padding: 12px; + color: inherit; + transition: opacity 0.3s; + + &:active, + &:hover { + opacity: 0.6; + color: inherit; + } +`; + +const Textarea = styled.textarea` + width: 100%; + height: 200px; + min-height: 200px; + resize: vertical; + font-size: 10px; + + &:focus { + box-shadow: none; + } +`; + +const StyledParagraph = styled(Paragraph)` + margin: 10px 0; +`; + +const Log = (props: Props): ?React$Element => { + if (!props.log.opened) return null; + return ( + + + + +

Log

+ Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history. +