2nd iteration: remove scss and more unused components

This commit is contained in:
Victor Baranov 2019-09-02 13:10:44 +03:00
parent 36b863438f
commit 0b6be5f7fd
130 changed files with 0 additions and 10994 deletions

View File

@ -214,21 +214,6 @@ gulp.task('dev:copy',
)
)
// scss compilation and autoprefixing tasks
gulp.task('build:scss', createScssBuildTask({
src: 'ui/app/css/index.scss',
dest: 'ui/app/css/output',
devMode: false,
}))
gulp.task('dev:scss', createScssBuildTask({
src: 'ui/app/css/index.scss',
dest: 'ui/app/css/output',
devMode: true,
pattern: 'ui/app/**/*.scss',
}))
function createScssBuildTask ({ src, dest, devMode, pattern }) {
return function () {
if (devMode) {
@ -371,7 +356,6 @@ gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge', 'zip:ope
gulp.task('dev',
gulp.series(
'clean',
'dev:scss',
gulp.parallel(
'dev:extension:js',
'dev:mascara:js',
@ -384,7 +368,6 @@ gulp.task('dev',
gulp.task('dev:extension',
gulp.series(
'clean',
'dev:scss',
gulp.parallel(
'dev:extension:js',
'dev:copy',
@ -396,7 +379,6 @@ gulp.task('dev:extension',
gulp.task('dev:mascara',
gulp.series(
'clean',
'dev:scss',
gulp.parallel(
'dev:mascara:js',
'dev:copy',
@ -408,7 +390,6 @@ gulp.task('dev:mascara',
gulp.task('build',
gulp.series(
'clean',
'build:scss',
gulpParallel(
'build:extension:js',
'build:mascara:js',
@ -420,7 +401,6 @@ gulp.task('build',
gulp.task('build:extension',
gulp.series(
'clean',
'build:scss',
gulp.parallel(
'build:extension:js',
'copy'
@ -431,7 +411,6 @@ gulp.task('build:extension',
gulp.task('build:mascara',
gulp.series(
'clean',
'build:scss',
gulp.parallel(
'build:mascara:js',
'copy'

View File

@ -1,324 +0,0 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const actions = require('../actions')
const ethNetProps = require('eth-net-props')
const connect = require('react-redux').connect
const Dropdown = require('./dropdown').Dropdown
const DropdownMenuItem = require('./dropdown').DropdownMenuItem
const Identicon = require('./identicon')
const copyToClipboard = require('copy-to-clipboard')
const { checksumAddress } = require('../util')
class AccountDropdowns extends Component {
constructor (props) {
super(props)
this.state = {
accountSelectorActive: false,
optionsMenuActive: false,
}
this.accountSelectorToggleClassName = 'accounts-selector'
this.optionsMenuToggleClassName = 'fa-ellipsis-h'
}
renderAccounts () {
const { identities, selected, keyrings } = this.props
return Object.keys(identities).map((key, index) => {
const identity = identities[key]
const isSelected = identity.address === selected
const simpleAddress = identity.address.substring(2).toLowerCase()
const keyring = keyrings.find((kr) => {
return kr.accounts.includes(simpleAddress) ||
kr.accounts.includes(identity.address)
})
return h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
this.props.actions.showAccountDetail(identity.address)
},
style: {
marginTop: index === 0 ? '5px' : '',
fontSize: '24px',
},
},
[
h(
Identicon,
{
address: identity.address,
diameter: 32,
style: {
marginLeft: '10px',
},
},
),
this.indicateIfLoose(keyring),
h('span', {
style: {
marginLeft: '20px',
fontSize: '24px',
maxWidth: '145px',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
}, identity.name || ''),
h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, isSelected ? h('.check', '✓') : null),
]
)
})
}
indicateIfLoose (keyring) {
try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
return isLoose ? h('.keyring-label.allcaps', this.context.t('loose')) : null
} catch (e) { return }
}
renderAccountSelector () {
const { actions } = this.props
const { accountSelectorActive } = this.state
return h(
Dropdown,
{
useCssTransition: true, // Hardcoded because account selector is temporarily in app-header
style: {
marginLeft: '-238px',
marginTop: '38px',
minWidth: '180px',
overflowY: 'auto',
maxHeight: '300px',
width: '300px',
},
innerStyle: {
padding: '8px 25px',
},
isOpen: accountSelectorActive,
onClickOutside: (event) => {
const { classList } = event.target
const isNotToggleElement = !classList.contains(this.accountSelectorToggleClassName)
if (accountSelectorActive && isNotToggleElement) {
this.setState({ accountSelectorActive: false })
}
},
},
[
...this.renderAccounts(),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => actions.addNewAccount(),
},
[
h(
Identicon,
{
style: {
marginLeft: '10px',
},
diameter: 32,
},
),
h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, this.context.t('createAccount')),
],
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => actions.showImportPage(),
},
[
h(
Identicon,
{
style: {
marginLeft: '10px',
},
diameter: 32,
},
),
h('span', {
style: {
marginLeft: '20px',
fontSize: '24px',
marginBottom: '5px',
},
}, this.context.t('importAccount')),
]
),
]
)
}
renderAccountOptions () {
const { actions } = this.props
const { optionsMenuActive } = this.state
return h(
Dropdown,
{
style: {
marginLeft: '-215px',
minWidth: '180px',
},
isOpen: optionsMenuActive,
onClickOutside: (event) => {
const { classList } = event.target
const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName)
if (optionsMenuActive && isNotToggleElement) {
this.setState({ optionsMenuActive: false })
}
},
},
[
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
const { selected, network } = this.props
const url = ethNetProps.explorerLinks.getExplorerAccountLinkFor(selected, network)
global.platform.openWindow({ url })
},
},
this.context.t('etherscanView'),
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
const { selected, identities } = this.props
var identity = identities[selected]
actions.showQrView(selected, identity ? identity.name : '')
},
},
this.context.t('showQRCode'),
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
const { selected } = this.props
copyToClipboard(checksumAddress(selected))
},
},
this.context.t('copyAddress'),
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
actions.requestAccountExport()
},
},
this.context.t('exportPrivateKey'),
),
]
)
}
render () {
const { style, enableAccountsSelector, enableAccountOptions } = this.props
const { optionsMenuActive, accountSelectorActive } = this.state
return h(
'span',
{
style: style,
},
[
enableAccountsSelector && h(
// 'i.fa.fa-angle-down',
'div.cursor-pointer.color-orange.accounts-selector',
{
style: {
// fontSize: '1.8em',
background: 'url(images/switch_acc.svg) white center center no-repeat',
height: '25px',
width: '25px',
transform: 'scale(0.75)',
marginRight: '3px',
},
onClick: (event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: !accountSelectorActive,
optionsMenuActive: false,
})
},
},
this.renderAccountSelector(),
),
enableAccountOptions && h(
'i.fa.fa-ellipsis-h',
{
style: {
marginRight: '0.5em',
fontSize: '1.8em',
},
onClick: (event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: false,
optionsMenuActive: !optionsMenuActive,
})
},
},
this.renderAccountOptions()
),
]
)
}
}
AccountDropdowns.defaultProps = {
enableAccountsSelector: false,
enableAccountOptions: false,
}
AccountDropdowns.propTypes = {
identities: PropTypes.objectOf(PropTypes.object),
selected: PropTypes.string,
keyrings: PropTypes.array,
actions: PropTypes.objectOf(PropTypes.func),
network: PropTypes.string,
style: PropTypes.object,
enableAccountOptions: PropTypes.bool,
enableAccountsSelector: PropTypes.bool,
t: PropTypes.func,
}
const mapDispatchToProps = (dispatch) => {
return {
actions: {
showConfigPage: () => dispatch(actions.showConfigPage()),
requestAccountExport: () => dispatch(actions.requestExportAccount()),
showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)),
addNewAccount: () => dispatch(actions.addNewAccount()),
showImportPage: () => dispatch(actions.showImportPage()),
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
},
}
}
AccountDropdowns.contextTypes = {
t: PropTypes.func,
}
module.exports = {
AccountDropdowns: connect(null, mapDispatchToProps)(AccountDropdowns),
}

View File

@ -1,138 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const PropTypes = require('prop-types')
const inherits = require('util').inherits
const exportAsFile = require('../util').exportAsFile
const copyToClipboard = require('copy-to-clipboard')
const actions = require('../actions')
const ethUtil = require('ethereumjs-util')
const connect = require('react-redux').connect
ExportAccountView.contextTypes = {
t: PropTypes.func,
}
module.exports = connect(mapStateToProps)(ExportAccountView)
inherits(ExportAccountView, Component)
function ExportAccountView () {
Component.call(this)
}
function mapStateToProps (state) {
return {
warning: state.appState.warning,
}
}
ExportAccountView.prototype.render = function () {
const state = this.props
const accountDetail = state.accountDetail
const nickname = state.identities[state.address].name
if (!accountDetail) return h('div')
const accountExport = accountDetail.accountExport
const notExporting = accountExport === 'none'
const exportRequested = accountExport === 'requested'
const accountExported = accountExport === 'completed'
if (notExporting) return h('div')
if (exportRequested) {
const warning = this.context.t('exportPrivateKeyWarning')
return (
h('div', {
style: {
display: 'inline-block',
textAlign: 'center',
},
},
[
h('div', {
key: 'exporting',
style: {
margin: '0 20px',
},
}, [
h('p.error', warning),
h('input#exportAccount.sizing-input', {
type: 'password',
placeholder: this.context.t('confirmPassword').toLowerCase(),
onKeyPress: this.onExportKeyPress.bind(this),
style: {
position: 'relative',
top: '1.5px',
marginBottom: '7px',
},
}),
]),
h('div', {
key: 'buttons',
style: {
margin: '0 20px',
},
},
[
h('button', {
onClick: () => this.onExportKeyPress({ key: 'Enter', preventDefault: () => {} }),
style: {
marginRight: '10px',
},
}, this.context.t('submit')),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
}, this.context.t('cancel')),
]),
(this.props.warning) && (
h('span.error', {
style: {
margin: '20px',
},
}, this.props.warning.split('-'))
),
])
)
}
if (accountExported) {
const plainKey = ethUtil.stripHexPrefix(accountDetail.privateKey)
return h('div.privateKey', {
style: {
margin: '0 20px',
},
}, [
h('label', this.context.t('copyPrivateKey') + ':'),
h('p.error.cursor-pointer', {
style: {
textOverflow: 'ellipsis',
overflow: 'hidden',
webkitUserSelect: 'text',
maxWidth: '275px',
},
onClick: function (event) {
copyToClipboard(ethUtil.stripHexPrefix(accountDetail.privateKey))
},
}, plainKey),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
}, this.context.t('done')),
h('button', {
style: {
marginLeft: '10px',
},
onClick: () => exportAsFile(`Nifty Wallet ${nickname} Private Key`, plainKey),
}, this.context.t('saveAsFile')),
])
}
}
ExportAccountView.prototype.onExportKeyPress = function (event) {
if (event.key !== 'Enter') return
event.preventDefault()
const input = document.getElementById('exportAccount').value
this.props.dispatch(actions.exportAccount(input, this.props.address))
}

View File

@ -1,86 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const Identicon = require('./identicon')
const formatBalance = require('../util').formatBalance
const addressSummary = require('../util').addressSummary
module.exports = AccountPanel
inherits(AccountPanel, Component)
function AccountPanel () {
Component.call(this)
}
AccountPanel.prototype.render = function () {
var state = this.props
var identity = state.identity || {}
var account = state.account || {}
var isFauceting = state.isFauceting
var panelState = {
key: `accountPanel${identity.address}`,
identiconKey: identity.address,
identiconLabel: identity.name || '',
attributes: [
{
key: 'ADDRESS',
value: addressSummary(identity.address),
},
balanceOrFaucetingIndication(account, isFauceting),
],
}
return (
h('.identity-panel.flex-row.flex-space-between', {
style: {
flex: '1 0 auto',
cursor: panelState.onClick ? 'pointer' : undefined,
},
onClick: panelState.onClick,
}, [
// account identicon
h('.identicon-wrapper.flex-column.select-none', [
h(Identicon, {
address: panelState.identiconKey,
imageify: state.imageifyIdenticons,
}),
h('span.font-small', panelState.identiconLabel.substring(0, 7) + '...'),
]),
// account address, balance
h('.identity-data.flex-column.flex-justify-center.flex-grow.select-none', [
panelState.attributes.map((attr) => {
return h('.flex-row.flex-space-between', {
key: '' + Math.round(Math.random() * 1000000),
}, [
h('label.font-small.no-select', attr.key),
h('span.font-small', attr.value),
])
}),
]),
])
)
}
function balanceOrFaucetingIndication (account, isFauceting) {
// Temporarily deactivating isFauceting indication
// because it shows fauceting for empty restored accounts.
if (/* isFauceting*/ false) {
return {
key: 'Account is auto-funding.',
value: 'Please wait.',
}
} else {
return {
key: 'BALANCE',
value: formatBalance(account.balance),
}
}
}

View File

@ -1,54 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const InputNumber = require('../input-number.js')
// const GasSlider = require('./gas-slider.js')
module.exports = GasModalCard
inherits(GasModalCard, Component)
function GasModalCard () {
Component.call(this)
}
GasModalCard.prototype.render = function () {
const {
// memo,
onChange,
unitLabel,
value,
min,
// max,
step,
title,
copy,
} = this.props
return h('div.send-v2__gas-modal-card', [
h('div.send-v2__gas-modal-card__title', {}, title),
h('div.send-v2__gas-modal-card__copy', {}, copy),
h(InputNumber, {
unitLabel,
step,
// max,
min,
placeholder: '0',
value,
onChange,
}),
// h(GasSlider, {
// value,
// step,
// max,
// min,
// onChange,
// }),
])
}

View File

@ -1,50 +0,0 @@
// const Component = require('react').Component
// const h = require('react-hyperscript')
// const inherits = require('util').inherits
// module.exports = GasSlider
// inherits(GasSlider, Component)
// function GasSlider () {
// Component.call(this)
// }
// GasSlider.prototype.render = function () {
// const {
// memo,
// identities,
// onChange,
// unitLabel,
// value,
// id,
// step,
// max,
// min,
// } = this.props
// return h('div.gas-slider', [
// h('input.gas-slider__input', {
// type: 'range',
// step,
// max,
// min,
// value,
// id: 'gasSlider',
// onChange: event => onChange(event.target.value),
// }, []),
// h('div.gas-slider__bar', [
// h('div.gas-slider__low'),
// h('div.gas-slider__mid'),
// h('div.gas-slider__high'),
// ]),
// ])
// }

View File

@ -1,374 +0,0 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const GasModalCard = require('./gas-modal-card')
import Button from '../button'
const ethUtil = require('ethereumjs-util')
import {
updateSendErrors,
} from '../../ducks/send.duck'
const {
MIN_GAS_PRICE_DEC,
MIN_GAS_LIMIT_DEC,
MIN_GAS_PRICE_GWEI,
} = require('../send/send.constants')
const {
isBalanceSufficient,
} = require('../send/send.utils')
const {
conversionUtil,
multiplyCurrencies,
conversionGreaterThan,
conversionMax,
subtractCurrencies,
} = require('../../conversion-util')
const {
getGasIsLoading,
getForceGasMin,
conversionRateSelector,
getSendAmount,
getSelectedToken,
getSendFrom,
getCurrentAccountWithSendEtherInfo,
getSelectedTokenToFiatRate,
getSendMaxModeState,
} = require('../../selectors')
const {
getGasPrice,
getGasLimit,
} = require('../send/send.selectors')
function mapStateToProps (state) {
const selectedToken = getSelectedToken(state)
const currentAccount = getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state)
const conversionRate = conversionRateSelector(state)
return {
gasPrice: getGasPrice(state),
gasLimit: getGasLimit(state),
gasIsLoading: getGasIsLoading(state),
forceGasMin: getForceGasMin(state),
conversionRate,
amount: getSendAmount(state),
maxModeOn: getSendMaxModeState(state),
balance: currentAccount.balance,
primaryCurrency: selectedToken && selectedToken.symbol,
selectedToken,
amountConversionRate: selectedToken ? getSelectedTokenToFiatRate(state) : conversionRate,
}
}
function mapDispatchToProps (dispatch) {
return {
hideModal: () => dispatch(actions.hideModal()),
setGasPrice: newGasPrice => dispatch(actions.setGasPrice(newGasPrice)),
setGasLimit: newGasLimit => dispatch(actions.setGasLimit(newGasLimit)),
setGasTotal: newGasTotal => dispatch(actions.setGasTotal(newGasTotal)),
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
updateSendErrors: error => dispatch(updateSendErrors(error)),
}
}
function getFreshState (props) {
const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC
const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
})
return {
gasPrice,
gasLimit,
gasTotal,
error: null,
priceSigZeros: '',
priceSigDec: '',
}
}
inherits(CustomizeGasModal, Component)
function CustomizeGasModal (props) {
Component.call(this)
const originalState = getFreshState(props)
this.state = {
...originalState,
originalState,
}
}
CustomizeGasModal.contextTypes = {
t: PropTypes.func,
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal)
CustomizeGasModal.prototype.componentWillReceiveProps = function (nextProps) {
const currentState = getFreshState(this.props)
const {
gasPrice: currentGasPrice,
gasLimit: currentGasLimit,
} = currentState
const newState = getFreshState(nextProps)
const {
gasPrice: newGasPrice,
gasLimit: newGasLimit,
gasTotal: newGasTotal,
} = newState
const gasPriceChanged = currentGasPrice !== newGasPrice
const gasLimitChanged = currentGasLimit !== newGasLimit
if (gasPriceChanged) {
this.setState({
gasPrice: newGasPrice,
gasTotal: newGasTotal,
priceSigZeros: '',
priceSigDec: '',
})
}
if (gasLimitChanged) {
this.setState({ gasLimit: newGasLimit, gasTotal: newGasTotal })
}
if (gasLimitChanged || gasPriceChanged) {
this.validate({ gasLimit: newGasLimit, gasTotal: newGasTotal })
}
}
CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
const {
setGasPrice,
setGasLimit,
hideModal,
setGasTotal,
maxModeOn,
selectedToken,
balance,
updateSendAmount,
updateSendErrors,
} = this.props
if (maxModeOn && !selectedToken) {
const maxAmount = subtractCurrencies(
ethUtil.addHexPrefix(balance),
ethUtil.addHexPrefix(gasTotal),
{ toNumericBase: 'hex' }
)
updateSendAmount(maxAmount)
}
setGasPrice(ethUtil.addHexPrefix(gasPrice))
setGasLimit(ethUtil.addHexPrefix(gasLimit))
setGasTotal(ethUtil.addHexPrefix(gasTotal))
updateSendErrors({ insufficientFunds: false })
hideModal()
}
CustomizeGasModal.prototype.revert = function () {
this.setState(this.state.originalState)
}
CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
const {
amount,
balance,
selectedToken,
amountConversionRate,
conversionRate,
maxModeOn,
} = this.props
let error = null
const balanceIsSufficient = isBalanceSufficient({
amount: selectedToken || maxModeOn ? '0' : amount,
gasTotal,
balance,
selectedToken,
amountConversionRate,
conversionRate,
})
if (!balanceIsSufficient) {
error = this.context.t('balanceIsInsufficientGas')
}
const gasLimitTooLow = gasLimit && conversionGreaterThan(
{
value: MIN_GAS_LIMIT_DEC,
fromNumericBase: 'dec',
conversionRate,
},
{
value: gasLimit,
fromNumericBase: 'hex',
},
)
if (gasLimitTooLow) {
error = this.context.t('gasLimitTooLow')
}
this.setState({ error })
return error
}
CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) {
const { gasPrice } = this.state
const gasLimit = conversionUtil(newGasLimit, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
})
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
})
this.validate({ gasTotal, gasLimit })
this.setState({ gasTotal, gasLimit })
}
CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
const { gasLimit } = this.state
const sigZeros = String(newGasPrice).match(/^\d+[.]\d*?(0+)$/)
const sigDec = String(newGasPrice).match(/^\d+([.])0*$/)
this.setState({
priceSigZeros: sigZeros && sigZeros[1] || '',
priceSigDec: sigDec && sigDec[1] || '',
})
const gasPrice = conversionUtil(newGasPrice, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
fromDenomination: 'GWEI',
toDenomination: 'WEI',
})
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
})
this.validate({ gasTotal })
this.setState({ gasTotal, gasPrice })
}
CustomizeGasModal.prototype.render = function () {
const { hideModal, forceGasMin, gasIsLoading } = this.props
const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state
let convertedGasPrice = conversionUtil(gasPrice, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
toDenomination: 'GWEI',
})
convertedGasPrice += convertedGasPrice.match(/[.]/) ? priceSigZeros : `${priceSigDec}${priceSigZeros}`
let newGasPrice = gasPrice
if (forceGasMin) {
const convertedMinPrice = conversionUtil(forceGasMin, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
})
convertedGasPrice = conversionMax(
{ value: convertedMinPrice, fromNumericBase: 'dec' },
{ value: convertedGasPrice, fromNumericBase: 'dec' }
)
newGasPrice = conversionMax(
{ value: gasPrice, fromNumericBase: 'hex' },
{ value: forceGasMin, fromNumericBase: 'hex' }
)
}
const convertedGasLimit = conversionUtil(gasLimit, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
})
return !gasIsLoading && h('div.send-v2__customize-gas', {}, [
h('div.send-v2__customize-gas__content', {
}, [
h('div.send-v2__customize-gas__header', {}, [
h('div.send-v2__customize-gas__title', this.context.t('customGas')),
h('div.send-v2__customize-gas__close', {
onClick: hideModal,
}),
]),
h('div.send-v2__customize-gas__body', {}, [
h(GasModalCard, {
value: convertedGasPrice,
min: forceGasMin || MIN_GAS_PRICE_GWEI,
step: 1,
onChange: value => this.convertAndSetGasPrice(value),
title: this.context.t('gasPrice'),
copy: this.context.t('gasPriceCalculation'),
gasIsLoading,
}),
h(GasModalCard, {
value: convertedGasLimit,
min: 1,
step: 1,
onChange: value => this.convertAndSetGasLimit(value),
title: this.context.t('gasLimit'),
copy: this.context.t('gasLimitCalculation'),
gasIsLoading,
}),
]),
h('div.send-v2__customize-gas__footer', {}, [
error && h('div.send-v2__customize-gas__error-message', [
error,
]),
h('div.send-v2__customize-gas__revert', {
onClick: () => this.revert(),
}, [this.context.t('revert')]),
h('div.send-v2__customize-gas__buttons', [
h(Button, {
type: 'default',
className: 'send-v2__customize-gas__cancel',
onClick: this.props.hideModal,
}, [this.context.t('cancel')]),
h(Button, {
type: 'primary',
className: 'send-v2__customize-gas__save',
onClick: () => !error && this.save(newGasPrice, gasLimit, gasTotal),
disabled: error,
}, [this.context.t('save')]),
]),
]),
]),
])
}

View File

@ -1,2 +0,0 @@
const LoadingScreen = require('./loading-screen.component')
module.exports = LoadingScreen

View File

@ -1,31 +0,0 @@
const { Component } = require('react')
const h = require('react-hyperscript')
const PropTypes = require('prop-types')
const Spinner = require('../spinner')
class LoadingScreen extends Component {
renderMessage () {
const { loadingMessage } = this.props
return loadingMessage && h('span', loadingMessage)
}
render () {
return (
h('.loading-overlay', [
h('.loading-overlay__container', [
h(Spinner, {
color: '#F7C06C',
}),
this.renderMessage(),
]),
])
)
}
}
LoadingScreen.propTypes = {
loadingMessage: PropTypes.string,
}
module.exports = LoadingScreen

View File

@ -1,100 +0,0 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const AccountModalContainer = require('./account-modal-container')
const { getSelectedIdentity } = require('../../selectors')
const ethNetProps = require('eth-net-props')
const QrView = require('../qr-code')
const EditableLabel = require('../editable-label')
import Button from '../button'
function mapStateToProps (state) {
return {
network: state.metamask.network,
selectedIdentity: getSelectedIdentity(state),
keyrings: state.metamask.keyrings,
}
}
function mapDispatchToProps (dispatch) {
return {
// Is this supposed to be used somewhere?
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
showExportPrivateKeyModal: () => {
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
},
hideModal: () => dispatch(actions.hideModal()),
setAccountLabel: (address, label) => dispatch(actions.setAccountLabel(address, label)),
}
}
inherits(AccountDetailsModal, Component)
function AccountDetailsModal () {
Component.call(this)
}
AccountDetailsModal.contextTypes = {
t: PropTypes.func,
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailsModal)
// Not yet pixel perfect todos:
// fonts of qr-header
AccountDetailsModal.prototype.render = function () {
const {
selectedIdentity,
network,
showExportPrivateKeyModal,
setAccountLabel,
keyrings,
} = this.props
const { name, address } = selectedIdentity
const keyring = keyrings.find((kr) => {
return kr.accounts.includes(address)
})
let exportPrivateKeyFeatureEnabled = true
// This feature is disabled for hardware wallets
if (keyring && keyring.type.search('Hardware') !== -1) {
exportPrivateKeyFeatureEnabled = false
}
return h(AccountModalContainer, {}, [
h(EditableLabel, {
className: 'account-modal__name',
defaultValue: name,
onSubmit: label => setAccountLabel(address, label),
}),
h(QrView, {
Qr: {
data: address,
},
}),
h('div.account-modal-divider'),
h(Button, {
type: 'primary',
className: 'account-modal__button',
onClick: () => global.platform.openWindow({ url: ethNetProps.explorerLinks.getExplorerAccountLinkFor(address, network) }),
}, this.context.t('etherscanView')),
// Holding on redesign for Export Private Key functionality
exportPrivateKeyFeatureEnabled ? h(Button, {
type: 'primary',
className: 'account-modal__button',
onClick: () => showExportPrivateKeyModal(),
}, this.context.t('exportPrivateKey')) : null,
])
}

View File

@ -1,29 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import UserPreferencedCurrencyDisplay from '../../../user-preferenced-currency-display'
import { PRIMARY, SECONDARY } from '../../../../constants/common'
export default class CancelTransaction extends PureComponent {
static propTypes = {
value: PropTypes.string,
}
render () {
const { value } = this.props
return (
<div className="cancel-transaction-gas-fee">
<UserPreferencedCurrencyDisplay
className="cancel-transaction-gas-fee__eth"
value={value}
type={PRIMARY}
/>
<UserPreferencedCurrencyDisplay
className="cancel-transaction-gas-fee__fiat"
value={value}
type={SECONDARY}
/>
</div>
)
}
}

View File

@ -1 +0,0 @@
export { default } from './cancel-transaction-gas-fee.component'

View File

@ -1,17 +0,0 @@
.cancel-transaction-gas-fee {
background: #F1F4F9;
padding: 16px;
display: flex;
flex-direction: column;
align-items: center;
padding: 12px;
&__eth {
font-size: 1.5rem;
font-weight: 500;
}
&__fiat {
font-size: .75rem;
}
}

View File

@ -1,26 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import CancelTransactionGasFee from '../cancel-transaction-gas-fee.component'
import UserPreferencedCurrencyDisplay from '../../../../user-preferenced-currency-display'
describe('CancelTransactionGasFee Component', () => {
it('should render', () => {
const wrapper = shallow(
<CancelTransactionGasFee
value="0x3b9aca00"
/>
)
assert.ok(wrapper)
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
const ethDisplay = wrapper.find(UserPreferencedCurrencyDisplay).at(0)
const fiatDisplay = wrapper.find(UserPreferencedCurrencyDisplay).at(1)
assert.equal(ethDisplay.props().value, '0x3b9aca00')
assert.equal(ethDisplay.props().className, 'cancel-transaction-gas-fee__eth')
assert.equal(fiatDisplay.props().value, '0x3b9aca00')
assert.equal(fiatDisplay.props().className, 'cancel-transaction-gas-fee__fiat')
})
})

View File

@ -1,68 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Modal from '../../modal'
import CancelTransactionGasFee from './cancel-transaction-gas-fee'
import { SUBMITTED_STATUS } from '../../../constants/transactions'
export default class CancelTransaction extends PureComponent {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
createCancelTransaction: PropTypes.func,
hideModal: PropTypes.func,
showTransactionConfirmedModal: PropTypes.func,
transactionStatus: PropTypes.string,
newGasFee: PropTypes.string,
}
componentDidUpdate () {
const { transactionStatus, showTransactionConfirmedModal } = this.props
if (transactionStatus !== SUBMITTED_STATUS) {
showTransactionConfirmedModal()
return
}
}
handleSubmit = async () => {
const { createCancelTransaction, hideModal } = this.props
await createCancelTransaction()
hideModal()
}
handleCancel = () => {
this.props.hideModal()
}
render () {
const { t } = this.context
const { newGasFee } = this.props
return (
<Modal
headerText={t('attemptToCancel')}
onClose={this.handleCancel}
onSubmit={this.handleSubmit}
onCancel={this.handleCancel}
submitText={t('yesLetsTry')}
cancelText={t('nevermind')}
submitType="secondary"
>
<div>
<div className="cancel-transaction__title">
{ t('cancellationGasFee') }
</div>
<div className="cancel-transaction__cancel-transaction-gas-fee-container">
<CancelTransactionGasFee value={newGasFee} />
</div>
<div className="cancel-transaction__description">
{ t('attemptToCancelDescription') }
</div>
</div>
</Modal>
)
}
}

View File

@ -1,62 +0,0 @@
import { connect } from 'react-redux'
import { compose } from 'recompose'
import ethUtil from 'ethereumjs-util'
import { multiplyCurrencies } from '../../../conversion-util'
import withModalProps from '../../../higher-order-components/with-modal-props'
import CancelTransaction from './cancel-transaction.component'
import { showModal, createCancelTransaction } from '../../../actions'
import { getHexGasTotal } from '../../../helpers/confirm-transaction/util'
const mapStateToProps = (state, ownProps) => {
const { metamask } = state
const { transactionId, originalGasPrice } = ownProps
const { selectedAddressTxList } = metamask
const transaction = selectedAddressTxList.find(({ id }) => id === transactionId)
const transactionStatus = transaction ? transaction.status : ''
const defaultNewGasPrice = ethUtil.addHexPrefix(
multiplyCurrencies(originalGasPrice, 1.1, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 10,
})
)
const newGasFee = getHexGasTotal({ gasPrice: defaultNewGasPrice, gasLimit: '0x5208' })
return {
transactionId,
transactionStatus,
originalGasPrice,
newGasFee,
}
}
const mapDispatchToProps = dispatch => {
return {
createCancelTransaction: txId => dispatch(createCancelTransaction(txId)),
showTransactionConfirmedModal: () => dispatch(showModal({ name: 'TRANSACTION_CONFIRMED' })),
}
}
const mergeProps = (stateProps, dispatchProps, ownProps) => {
const { transactionId, ...restStateProps } = stateProps
const {
createCancelTransaction: dispatchCreateCancelTransaction,
...restDispatchProps
} = dispatchProps
return {
...restStateProps,
...restDispatchProps,
...ownProps,
createCancelTransaction: newGasPrice => {
return dispatchCreateCancelTransaction(transactionId, newGasPrice)
},
}
}
export default compose(
withModalProps,
connect(mapStateToProps, mapDispatchToProps, mergeProps),
)(CancelTransaction)

View File

@ -1 +0,0 @@
export { default } from './cancel-transaction.container'

View File

@ -1,18 +0,0 @@
@import './cancel-transaction-gas-fee/index';
.cancel-transaction {
&__title {
font-weight: 500;
padding-bottom: 16px;
text-align: center;
}
&__description {
text-align: center;
font-size: .875rem;
}
&__cancel-transaction-gas-fee-container {
margin-bottom: 16px;
}
}

View File

@ -1,56 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import CancelTransaction from '../cancel-transaction.component'
import CancelTransactionGasFee from '../cancel-transaction-gas-fee'
import Modal from '../../../modal'
describe('CancelTransaction Component', () => {
const t = key => key
it('should render a CancelTransaction modal', () => {
const wrapper = shallow(
<CancelTransaction
newGasFee="0x1319718a5000"
/>,
{ context: { t }}
)
assert.ok(wrapper)
assert.equal(wrapper.find(Modal).length, 1)
assert.equal(wrapper.find(CancelTransactionGasFee).length, 1)
assert.equal(wrapper.find(CancelTransactionGasFee).props().value, '0x1319718a5000')
assert.equal(wrapper.find('.cancel-transaction__title').text(), 'cancellationGasFee')
assert.equal(wrapper.find('.cancel-transaction__description').text(), 'attemptToCancelDescription')
})
it('should pass the correct props to the Modal component', async () => {
const createCancelTransactionSpy = sinon.stub().callsFake(() => Promise.resolve())
const hideModalSpy = sinon.spy()
const wrapper = shallow(
<CancelTransaction
defaultNewGasPrice="0x3b9aca00"
createCancelTransaction={createCancelTransactionSpy}
hideModal={hideModalSpy}
/>,
{ context: { t }}
)
assert.equal(wrapper.find(Modal).length, 1)
const modalProps = wrapper.find(Modal).props()
assert.equal(modalProps.headerText, 'attemptToCancel')
assert.equal(modalProps.submitText, 'yesLetsTry')
assert.equal(modalProps.cancelText, 'nevermind')
assert.equal(createCancelTransactionSpy.callCount, 0)
assert.equal(hideModalSpy.callCount, 0)
await modalProps.onSubmit()
assert.equal(createCancelTransactionSpy.callCount, 1)
assert.equal(hideModalSpy.callCount, 1)
modalProps.onCancel()
assert.equal(hideModalSpy.callCount, 2)
})
})

View File

@ -1,89 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Modal from '../../modal'
import { addressSummary } from '../../../util'
import Identicon from '../../identicon'
import ethNetProps from 'eth-net-props'
export default class ConfirmRemoveAccount extends Component {
static propTypes = {
hideModal: PropTypes.func.isRequired,
removeAccount: PropTypes.func.isRequired,
identity: PropTypes.object.isRequired,
network: PropTypes.string.isRequired,
}
static contextTypes = {
t: PropTypes.func,
}
handleRemove = () => {
this.props.removeAccount(this.props.identity.address)
.then(() => this.props.hideModal())
}
handleCancel = () => {
this.props.hideModal()
}
renderSelectedAccount () {
const { identity } = this.props
return (
<div className="confirm-remove-account__account">
<div className="confirm-remove-account__account__identicon">
<Identicon
address={identity.address}
diameter={32}
/>
</div>
<div className="confirm-remove-account__account__name">
<span className="confirm-remove-account__account__label">Name</span>
<span className="account_value">{identity.name}</span>
</div>
<div className="confirm-remove-account__account__address">
<span className="confirm-remove-account__account__label">Public Address</span>
<span className="account_value">{ addressSummary(identity.address, 4, 4) }</span>
</div>
<div className="confirm-remove-account__account__link">
<a
className=""
href={ethNetProps.explorerLinks.getExplorerAccountLinkFor(identity.address, this.props.network)}
target={'_blank'}
title={this.context.t('etherscanView')}
>
<img src="images/popout.svg" />
</a>
</div>
</div>
)
}
render () {
const { t } = this.context
return (
<Modal
headerText={`${t('removeAccount')}?`}
onClose={this.handleCancel}
onSubmit={this.handleRemove}
onCancel={this.handleCancel}
submitText={t('remove')}
cancelText={t('nevermind')}
submitType="secondary"
>
<div>
{ this.renderSelectedAccount() }
<div className="confirm-remove-account__description">
{ t('removeAccountDescription') }
<a
className="confirm-remove-account__link"
rel="noopener noreferrer"
target="_blank" href="https://metamask.zendesk.com/hc/en-us/articles/360015289932">
{ t('learnMore') }
</a>
</div>
</div>
</Modal>
)
}
}

View File

@ -1,22 +0,0 @@
import { connect } from 'react-redux'
import { compose } from 'recompose'
import ConfirmRemoveAccount from './confirm-remove-account.component'
import withModalProps from '../../../higher-order-components/with-modal-props'
import { removeAccount } from '../../../actions'
const mapStateToProps = state => {
return {
network: state.metamask.network,
}
}
const mapDispatchToProps = dispatch => {
return {
removeAccount: (address) => dispatch(removeAccount(address)),
}
}
export default compose(
withModalProps,
connect(mapStateToProps, mapDispatchToProps)
)(ConfirmRemoveAccount)

View File

@ -1 +0,0 @@
export { default } from './confirm-remove-account.container'

View File

@ -1,58 +0,0 @@
.confirm-remove-account {
&__description {
text-align: center;
font-size: .875rem;
}
&__account {
border: 1px solid #b7b7b7;
border-radius: 4px;
padding: 10px;
display: flex;
margin-top: 10px;
margin-bottom: 20px;
width: 100%;
&__identicon {
margin-right: 10px;
}
&__name,
&__address {
margin-right: 10px;
font-size: 14px;
}
&__name {
width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&__label {
font-size: 11px;
display: block;
color: #9b9b9b;
}
&__link {
margin-top: 14px;
img {
width: 15px;
height: 15px;
}
}
@media screen and (max-width: 575px) {
&__name {
width: 90px;
}
}
}
&__link {
color: #2f9ae0;
}
}

View File

@ -1,38 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Modal, { ModalContent } from '../../modal'
export default class ConfirmResetAccount extends PureComponent {
static propTypes = {
hideModal: PropTypes.func.isRequired,
resetAccount: PropTypes.func.isRequired,
}
static contextTypes = {
t: PropTypes.func,
}
handleReset = () => {
this.props.resetAccount()
.then(() => this.props.hideModal())
}
render () {
const { t } = this.context
return (
<Modal
onSubmit={this.handleReset}
onCancel={() => this.props.hideModal()}
submitText={t('reset')}
cancelText={t('nevermind')}
submitType="secondary"
>
<ModalContent
title={`${t('resetAccount')}?`}
description={t('resetAccountDescription')}
/>
</Modal>
)
}
}

View File

@ -1,16 +0,0 @@
import { connect } from 'react-redux'
import { compose } from 'recompose'
import withModalProps from '../../../higher-order-components/with-modal-props'
import ConfirmResetAccount from './confirm-reset-account.component'
import { resetAccount } from '../../../actions'
const mapDispatchToProps = dispatch => {
return {
resetAccount: () => dispatch(resetAccount()),
}
}
export default compose(
withModalProps,
connect(null, mapDispatchToProps)
)(ConfirmResetAccount)

View File

@ -1 +0,0 @@
export { default } from './confirm-reset-account.container'

View File

@ -1,143 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import GasModalCard from '../../customize-gas-modal/gas-modal-card'
import { MIN_GAS_PRICE_GWEI } from '../../send/send.constants'
import Button from '../../button'
import {
getDecimalGasLimit,
getDecimalGasPrice,
getPrefixedHexGasLimit,
getPrefixedHexGasPrice,
} from './customize-gas.util'
export default class CustomizeGas extends Component {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
txData: PropTypes.object.isRequired,
hideModal: PropTypes.func,
validate: PropTypes.func,
onSubmit: PropTypes.func,
}
state = {
gasPrice: 0,
gasLimit: 0,
originalGasPrice: 0,
originalGasLimit: 0,
}
componentDidMount () {
const { txData = {} } = this.props
const { txParams: { gas: hexGasLimit, gasPrice: hexGasPrice } = {} } = txData
const gasLimit = getDecimalGasLimit(hexGasLimit)
const gasPrice = getDecimalGasPrice(hexGasPrice)
this.setState({
gasPrice,
gasLimit,
originalGasPrice: gasPrice,
originalGasLimit: gasLimit,
})
}
handleRevert () {
const { originalGasPrice, originalGasLimit } = this.state
this.setState({
gasPrice: originalGasPrice,
gasLimit: originalGasLimit,
})
}
handleSave () {
const { onSubmit, hideModal } = this.props
const { gasLimit, gasPrice } = this.state
const prefixedHexGasPrice = getPrefixedHexGasPrice(gasPrice)
const prefixedHexGasLimit = getPrefixedHexGasLimit(gasLimit)
Promise.resolve(onSubmit({ gasPrice: prefixedHexGasPrice, gasLimit: prefixedHexGasLimit }))
.then(() => hideModal())
}
validate () {
const { gasLimit, gasPrice } = this.state
return this.props.validate({
gasPrice: getPrefixedHexGasPrice(gasPrice),
gasLimit: getPrefixedHexGasLimit(gasLimit),
})
}
render () {
const { t } = this.context
const { hideModal } = this.props
const { gasPrice, gasLimit } = this.state
const { valid, errorKey } = this.validate()
return (
<div className="customize-gas">
<div className="customize-gas__content">
<div className="customize-gas__header">
<div className="customize-gas__title">
{ this.context.t('customGas') }
</div>
<div
className="customize-gas__close"
onClick={() => hideModal()}
/>
</div>
<div className="customize-gas__body">
<GasModalCard
value={gasPrice}
min={MIN_GAS_PRICE_GWEI}
step={1}
onChange={value => this.setState({ gasPrice: value })}
title={t('gasPrice')}
copy={t('gasPriceCalculation')}
/>
<GasModalCard
value={gasLimit}
min={1}
step={1}
onChange={value => this.setState({ gasLimit: value })}
title={t('gasLimit')}
copy={t('gasLimitCalculation')}
/>
</div>
<div className="customize-gas__footer">
{ !valid && <div className="customize-gas__error-message">{ t(errorKey) }</div> }
<div
className="customize-gas__revert"
onClick={() => this.handleRevert()}
>
{ t('revert') }
</div>
<div className="customize-gas__buttons">
<Button
type="default"
className="customize-gas__cancel"
onClick={() => hideModal()}
style={{ marginRight: '10px' }}
>
{ t('cancel') }
</Button>
<Button
type="primary"
className="customize-gas__save"
onClick={() => this.handleSave()}
style={{ marginRight: '10px' }}
disabled={!valid}
>
{ t('save') }
</Button>
</div>
</div>
</div>
</div>
)
}
}

View File

@ -1,22 +0,0 @@
import { connect } from 'react-redux'
import CustomizeGas from './customize-gas.component'
import { hideModal } from '../../../actions'
const mapStateToProps = state => {
const { appState: { modal: { modalState: { props } } } } = state
const { txData, onSubmit, validate } = props
return {
txData,
onSubmit,
validate,
}
}
const mapDispatchToProps = dispatch => {
return {
hideModal: () => dispatch(hideModal()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CustomizeGas)

View File

@ -1,34 +0,0 @@
import ethUtil from 'ethereumjs-util'
import { conversionUtil } from '../../../conversion-util'
export function getDecimalGasLimit (hexGasLimit) {
return conversionUtil(hexGasLimit, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
})
}
export function getDecimalGasPrice (hexGasPrice) {
return conversionUtil(hexGasPrice, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
toDenomination: 'GWEI',
})
}
export function getPrefixedHexGasLimit (gasLimit) {
return ethUtil.addHexPrefix(conversionUtil(gasLimit, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
}))
}
export function getPrefixedHexGasPrice (gasPrice) {
return ethUtil.addHexPrefix(conversionUtil(gasPrice, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
fromDenomination: 'GWEI',
toDenomination: 'WEI',
}))
}

View File

@ -1 +0,0 @@
export { default } from './customize-gas.container'

View File

@ -1,110 +0,0 @@
.customize-gas {
border: 1px solid #D8D8D8;
border-radius: 4px;
background-color: #FFFFFF;
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.14);
font-family: Roboto;
display: flex;
flex-flow: column;
@media screen and (max-width: $break-small) {
width: 100vw;
height: 100vh;
}
&__header {
height: 52px;
border-bottom: 1px solid $alto;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 22px;
@media screen and (max-width: $break-small) {
flex: 0 0 auto;
}
}
&__title {
margin-left: 19.25px;
}
&__close::after {
content: '\00D7';
font-size: 1.8em;
color: $dusty-gray;
font-family: sans-serif;
cursor: pointer;
margin-right: 19.25px;
}
&__content {
display: flex;
flex-flow: column nowrap;
height: 100%;
}
&__body {
display: flex;
margin-bottom: 24px;
@media screen and (max-width: $break-small) {
flex-flow: column;
flex: 1 1 auto;
}
}
&__footer {
height: 75px;
border-top: 1px solid $alto;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 22px;
position: relative;
@media screen and (max-width: $break-small) {
flex: 0 0 auto;
}
}
&__buttons {
display: flex;
justify-content: space-between;
margin-right: 21.25px;
}
&__revert, &__cancel, &__save, &__save__error {
display: flex;
justify-content: center;
align-items: center;
padding: 0 3px;
cursor: pointer;
}
&__revert {
color: $silver-chalice;
font-size: 16px;
margin-left: 21.25px;
}
&__cancel, &__save, &__save__error {
width: 85.74px;
min-width: initial;
}
&__save__error {
opacity: 0.5;
cursor: auto;
}
&__error-message {
display: block;
position: absolute;
top: 4px;
right: 4px;
font-size: 12px;
line-height: 12px;
color: $red;
}
}

View File

@ -1,9 +0,0 @@
@import './cancel-transaction/index';
@import './confirm-remove-account/index';
@import './customize-gas/index';
@import './qr-scanner/index';
@import './transaction-confirmed/index';

View File

@ -11,24 +11,11 @@ const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums')
// Modal Components
const BuyOptions = require('./buy-options-modal')
const DepositEtherModal = require('./deposit-ether-modal')
const AccountDetailsModal = require('./account-details-modal')
const EditAccountNameModal = require('./edit-account-name-modal')
const ExportPrivateKeyModal = require('./export-private-key-modal')
const NewAccountModal = require('./new-account-modal')
const ShapeshiftDepositTxModal = require('./shapeshift-deposit-tx-modal.js')
const HideTokenConfirmationModal = require('./hide-token-confirmation-modal')
const CustomizeGasModal = require('../customize-gas-modal')
const NotifcationModal = require('./notification-modal')
const QRScanner = require('./qr-scanner')
import ConfirmRemoveAccount from './confirm-remove-account'
import ConfirmResetAccount from './confirm-reset-account'
import TransactionConfirmed from './transaction-confirmed'
import ConfirmCustomizeGasModal from './customize-gas'
import CancelTransaction from './cancel-transaction'
import WelcomeBeta from './welcome-beta'
import TransactionDetails from './transaction-details'
import RejectTransactions from './reject-transactions'
const modalContainerBaseStyle = {
transform: 'translate3d(-50%, 0, 0px)',
@ -165,13 +152,6 @@ const MODALS = {
},
},
ACCOUNT_DETAILS: {
contents: [
h(AccountDetailsModal, {}, []),
],
...accountModalStyle,
},
EXPORT_PRIVATE_KEY: {
contents: [
h(ExportPrivateKeyModal, {}, []),
@ -179,13 +159,6 @@ const MODALS = {
...accountModalStyle,
},
SHAPESHIFT_DEPOSIT_TX: {
contents: [
h(ShapeshiftDepositTxModal),
],
...accountModalStyle,
},
HIDE_TOKEN_CONFIRMATION: {
contents: [
h(HideTokenConfirmationModal, {}, []),
@ -200,19 +173,6 @@ const MODALS = {
},
},
BETA_UI_NOTIFICATION_MODAL: {
contents: h(WelcomeBeta),
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
OLD_UI_NOTIFICATION_MODAL: {
contents: [
h(NotifcationModal, {
@ -230,32 +190,6 @@ const MODALS = {
},
},
CONFIRM_RESET_ACCOUNT: {
contents: h(ConfirmResetAccount),
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
CONFIRM_REMOVE_ACCOUNT: {
contents: h(ConfirmRemoveAccount),
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
NEW_ACCOUNT: {
contents: [
h(NewAccountModal, {}, []),
@ -280,118 +214,6 @@ const MODALS = {
},
},
CUSTOMIZE_GAS: {
contents: [
h(CustomizeGasModal),
],
mobileModalStyle: {
width: '100vw',
height: '100vh',
top: '0',
transform: 'none',
left: '0',
right: '0',
margin: '0 auto',
},
laptopModalStyle: {
width: '720px',
height: '377px',
top: '80px',
transform: 'none',
left: '0',
right: '0',
margin: '0 auto',
},
},
CONFIRM_CUSTOMIZE_GAS: {
contents: h(ConfirmCustomizeGasModal),
mobileModalStyle: {
width: '100vw',
height: '100vh',
top: '0',
transform: 'none',
left: '0',
right: '0',
margin: '0 auto',
},
laptopModalStyle: {
width: '720px',
height: '377px',
top: '80px',
transform: 'none',
left: '0',
right: '0',
margin: '0 auto',
},
},
TRANSACTION_CONFIRMED: {
disableBackdropClick: true,
contents: h(TransactionConfirmed),
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
QR_SCANNER: {
contents: h(QRScanner),
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
CANCEL_TRANSACTION: {
contents: h(CancelTransaction),
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
TRANSACTION_DETAILS: {
contents: h(TransactionDetails),
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
REJECT_TRANSACTIONS: {
contents: h(RejectTransactions),
mobileModalStyle: {
...modalContainerMobileStyle,
},
laptopModalStyle: {
...modalContainerLaptopStyle,
},
contentStyle: {
borderRadius: '8px',
},
},
DEFAULT: {
contents: [],
mobileModalStyle: {},

View File

@ -1,2 +0,0 @@
import QrScanner from './qr-scanner.container'
module.exports = QrScanner

View File

@ -1,83 +0,0 @@
.qr-scanner {
width: 100%;
height: 100%;
background-color: #fff;
display: flex;
flex-flow: column;
border-radius: 8px;
&__title {
font-size: 1.5rem;
font-weight: 500;
padding: 16px 0;
text-align: center;
}
&__content {
padding-left: 20px;
padding-right: 20px;
&__video-wrapper {
overflow: hidden;
width: 100%;
height: 275px;
display: flex;
align-items: center;
justify-content: center;
video {
transform: scaleX(-1);
width: auto;
height: 275px;
}
}
}
&__status {
text-align: center;
font-size: 14px;
padding: 15px;
}
&__image {
font-size: 1.5rem;
font-weight: 500;
padding: 16px 0 0;
text-align: center;
}
&__error {
text-align: center;
font-size: 16px;
padding: 15px;
}
&__footer {
padding: 20px;
flex-direction: row;
display: flex;
button {
margin-right: 15px;
}
button:last-of-type {
margin-right: 0;
background-color: #009eec;
border: none;
color: #fff;
}
}
&__close::after {
content: '\00D7';
font-size: 35px;
color: #9b9b9b;
position: absolute;
top: 4px;
right: 20px;
cursor: pointer;
font-weight: 300;
}
}

View File

@ -1,216 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { BrowserQRCodeReader } from '@zxing/library'
// import adapter from 'webrtc-adapter' // eslint-disable-line import/no-nodejs-modules, no-unused-vars
import Spinner from '../../spinner'
import WebcamUtils from '../../../../lib/webcam-utils'
import PageContainerFooter from '../../page-container/page-container-footer/page-container-footer.component'
export default class QrScanner extends Component {
static propTypes = {
hideModal: PropTypes.func.isRequired,
qrCodeDetected: PropTypes.func,
scanQrCode: PropTypes.func,
error: PropTypes.bool,
errorType: PropTypes.string,
}
static contextTypes = {
t: PropTypes.func,
}
constructor (props, context) {
super(props)
this.state = {
ready: false,
msg: context.t('accessingYourCamera'),
}
this.codeReader = null
this.permissionChecker = null
this.needsToReinit = false
// Clear pre-existing qr code data before scanning
this.props.qrCodeDetected(null)
}
componentDidMount () {
this.initCamera()
}
async checkPermisisions () {
const { permissions } = await WebcamUtils.checkStatus()
if (permissions) {
clearTimeout(this.permissionChecker)
// Let the video stream load first...
setTimeout(_ => {
this.setState({
ready: true,
msg: this.context.t('scanInstructions'),
})
if (this.needsToReinit) {
this.initCamera()
this.needsToReinit = false
}
}, 2000)
} else {
// Keep checking for permissions
this.permissionChecker = setTimeout(_ => {
this.checkPermisisions()
}, 1000)
}
}
componentWillUnmount () {
clearTimeout(this.permissionChecker)
if (this.codeReader) {
this.codeReader.reset()
}
}
initCamera () {
this.codeReader = new BrowserQRCodeReader()
this.codeReader.getVideoInputDevices()
.then(videoInputDevices => {
clearTimeout(this.permissionChecker)
this.checkPermisisions()
this.codeReader.decodeFromInputVideoDevice(undefined, 'video')
.then(content => {
const result = this.parseContent(content.text)
if (result.type !== 'unknown') {
this.props.qrCodeDetected(result)
this.stopAndClose()
} else {
this.setState({msg: this.context.t('unknownQrCode')})
}
})
.catch(err => {
if (err && err.name === 'NotAllowedError') {
this.setState({msg: this.context.t('youNeedToAllowCameraAccess')})
clearTimeout(this.permissionChecker)
this.needsToReinit = true
this.checkPermisisions()
}
})
}).catch(err => {
console.error('[QR-SCANNER]: getVideoInputDevices threw an exception: ', err)
})
}
parseContent (content) {
let type = 'unknown'
let values = {}
// Here we could add more cases
// To parse other type of links
// For ex. EIP-681 (https://eips.ethereum.org/EIPS/eip-681)
// Ethereum address links - fox ex. ethereum:0x.....1111
if (content.split('ethereum:').length > 1) {
type = 'address'
values = {'address': content.split('ethereum:')[1] }
// Regular ethereum addresses - fox ex. 0x.....1111
} else if (content.substring(0, 2).toLowerCase() === '0x') {
type = 'address'
values = {'address': content }
}
return {type, values}
}
stopAndClose = () => {
if (this.codeReader) {
this.codeReader.reset()
}
this.setState({ ready: false })
this.props.hideModal()
}
tryAgain = () => {
// close the modal
this.stopAndClose()
// wait for the animation and try again
setTimeout(_ => {
this.props.scanQrCode()
}, 1000)
}
renderVideo () {
return (
<div className={'qr-scanner__content__video-wrapper'}>
<video
id="video"
style={{
display: this.state.ready ? 'block' : 'none',
}}
/>
{ !this.state.ready ? <Spinner color={'#F7C06C'} /> : null}
</div>
)
}
renderErrorModal () {
let title, msg
if (this.props.error) {
if (this.props.errorType === 'NO_WEBCAM_FOUND') {
title = this.context.t('noWebcamFoundTitle')
msg = this.context.t('noWebcamFound')
} else {
title = this.context.t('unknownCameraErrorTitle')
msg = this.context.t('unknownCameraError')
}
}
return (
<div className="qr-scanner">
<div className="qr-scanner__close" onClick={this.stopAndClose}></div>
<div className="qr-scanner__image">
<img src={'images/webcam.svg'} width={70} height={70} />
</div>
<div className="qr-scanner__title">
{ title }
</div>
<div className={'qr-scanner__error'}>
{msg}
</div>
<PageContainerFooter
onCancel={this.stopAndClose}
onSubmit={this.tryAgain}
cancelText={this.context.t('cancel')}
submitText={this.context.t('tryAgain')}
submitButtonType="confirm"
/>
</div>
)
}
render () {
const { t } = this.context
if (this.props.error) {
return this.renderErrorModal()
}
return (
<div className="qr-scanner">
<div className="qr-scanner__close" onClick={this.stopAndClose}></div>
<div className="qr-scanner__title">
{ `${t('scanQrCode')}` }
</div>
<div className="qr-scanner__content">
{ this.renderVideo() }
</div>
<div className={'qr-scanner__status'}>
{this.state.msg}
</div>
</div>
)
}
}

View File

@ -1,24 +0,0 @@
import { connect } from 'react-redux'
import QrScanner from './qr-scanner.component'
const { hideModal, qrCodeDetected, showQrScanner } = require('../../../actions')
import {
SEND_ROUTE,
} from '../../../routes'
const mapStateToProps = state => {
return {
error: state.appState.modal.modalState.props.error,
errorType: state.appState.modal.modalState.props.errorType,
}
}
const mapDispatchToProps = dispatch => {
return {
hideModal: () => dispatch(hideModal()),
qrCodeDetected: (data) => dispatch(qrCodeDetected(data)),
scanQrCode: () => dispatch(showQrScanner(SEND_ROUTE)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(QrScanner)

View File

@ -1 +0,0 @@
export { default } from './reject-transactions.container'

View File

@ -1,6 +0,0 @@
.reject-transactions {
&__description {
text-align: center;
font-size: .875rem;
}
}

View File

@ -1,45 +0,0 @@
import PropTypes from 'prop-types'
import React, { PureComponent } from 'react'
import Modal from '../../modal'
export default class RejectTransactionsModal extends PureComponent {
static contextTypes = {
t: PropTypes.func.isRequired,
}
static propTypes = {
onSubmit: PropTypes.func.isRequired,
hideModal: PropTypes.func.isRequired,
unapprovedTxCount: PropTypes.number.isRequired,
}
onSubmit = async () => {
const { onSubmit, hideModal } = this.props
await onSubmit()
hideModal()
}
render () {
const { t } = this.context
const { hideModal, unapprovedTxCount } = this.props
return (
<Modal
headerText={t('rejectTxsN', [unapprovedTxCount])}
onClose={hideModal}
onSubmit={this.onSubmit}
onCancel={hideModal}
submitText={t('rejectAll')}
cancelText={t('cancel')}
submitType="secondary"
>
<div>
<div className="reject-transactions__description">
{ t('rejectTxsDescription', [unapprovedTxCount]) }
</div>
</div>
</Modal>
)
}
}

View File

@ -1,17 +0,0 @@
import { connect } from 'react-redux'
import { compose } from 'recompose'
import RejectTransactionsModal from './reject-transactions.component'
import withModalProps from '../../../higher-order-components/with-modal-props'
const mapStateToProps = (state, ownProps) => {
const { unapprovedTxCount } = ownProps
return {
unapprovedTxCount,
}
}
export default compose(
withModalProps,
connect(mapStateToProps),
)(RejectTransactionsModal)

View File

@ -1,40 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const QrView = require('../qr-code')
const AccountModalContainer = require('./account-modal-container')
function mapStateToProps (state) {
return {
Qr: state.appState.modal.modalState.props.Qr,
}
}
function mapDispatchToProps (dispatch) {
return {
hideModal: () => {
dispatch(actions.hideModal())
},
}
}
inherits(ShapeshiftDepositTxModal, Component)
function ShapeshiftDepositTxModal () {
Component.call(this)
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(ShapeshiftDepositTxModal)
ShapeshiftDepositTxModal.prototype.render = function () {
const { Qr } = this.props
return h(AccountModalContainer, {
}, [
h('div', {}, [
h(QrView, {key: 'qr', Qr}),
]),
])
}

View File

@ -1 +0,0 @@
export { default } from './transaction-confirmed.container'

View File

@ -1,22 +0,0 @@
.transaction-confirmed {
&__title {
font-size: 1.5rem;
font-weight: 500;
padding: 16px 0;
text-align: center;
}
&__description {
text-align: center;
font-size: .875rem;
}
&__content {
overflow-y: auto;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
}
}

View File

@ -1,45 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Modal from '../../modal'
export default class TransactionConfirmed extends PureComponent {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
onSubmit: PropTypes.func,
hideModal: PropTypes.func,
}
handleSubmit = () => {
const { hideModal, onSubmit } = this.props
hideModal()
if (onSubmit && typeof onSubmit === 'function') {
onSubmit()
}
}
render () {
const { t } = this.context
return (
<Modal
onSubmit={this.handleSubmit}
submitText={t('ok')}
>
<div className="transaction-confirmed__content">
<img src="images/check-icon.svg" />
<div className="transaction-confirmed__title">
{ `${t('confirmed')}!` }
</div>
<div className="transaction-confirmed__description">
{ t('initialTransactionConfirmed') }
</div>
</div>
</Modal>
)
}
}

View File

@ -1,4 +0,0 @@
import TransactionConfirmed from './transaction-confirmed.component'
import withModalProps from '../../../higher-order-components/with-modal-props'
export default withModalProps(TransactionConfirmed)

View File

@ -1 +0,0 @@
export { default } from './transaction-details.container'

View File

@ -1,54 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Modal from '../../modal'
import TransactionListItemDetails from '../../transaction-list-item-details'
import { hexToDecimal } from '../../../helpers/conversions.util'
export default class TransactionConfirmed extends PureComponent {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
hideModal: PropTypes.func,
transaction: PropTypes.object,
onRetry: PropTypes.func,
showRetry: PropTypes.bool,
onCancel: PropTypes.func,
showCancel: PropTypes.bool,
}
handleSubmit = () => {
this.props.hideModal()
}
handleRetry = () => {
const { onRetry, hideModal } = this.props
Promise.resolve(onRetry()).then(() => hideModal())
}
render () {
const { t } = this.context
const { transaction, showRetry, onCancel, showCancel } = this.props
const { txParams: { nonce } = {} } = transaction
const decimalNonce = nonce && hexToDecimal(nonce)
return (
<Modal
onSubmit={this.handleSubmit}
onClose={this.handleSubmit}
submitText={t('ok')}
headerText={t('transactionWithNonce', [`#${decimalNonce}`])}
>
<TransactionListItemDetails
transaction={transaction}
onRetry={this.handleRetry}
showRetry={showRetry}
onCancel={() => onCancel()}
showCancel={showCancel}
/>
</Modal>
)
}
}

View File

@ -1,4 +0,0 @@
import TransactionDetails from './transaction-details.component'
import withModalProps from '../../../higher-order-components/with-modal-props'
export default withModalProps(TransactionDetails)

View File

@ -1 +0,0 @@
export { default } from './welcome-beta.container'

View File

@ -1,30 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Modal, { ModalContent } from '../../modal'
const TransactionConfirmed = (props, context) => {
const { t } = context
const { hideModal } = props
return (
<Modal
onSubmit={() => hideModal()}
submitText={t('ok')}
>
<ModalContent
title={t('uiWelcome')}
description={t('uiWelcomeMessage')}
/>
</Modal>
)
}
TransactionConfirmed.contextTypes = {
t: PropTypes.func,
}
TransactionConfirmed.propTypes = {
hideModal: PropTypes.func,
}
export default TransactionConfirmed

View File

@ -1,4 +0,0 @@
import WelcomeBeta from './welcome-beta.component'
import withModalProps from '../../../higher-order-components/with-modal-props'
export default withModalProps(WelcomeBeta)

View File

@ -1,2 +0,0 @@
import NetworkDisplay from './network-display.container'
module.exports = NetworkDisplay

View File

@ -1,62 +0,0 @@
.network-display {
&__container {
display: flex;
align-items: center;
justify-content: flex-start;
background-color: lighten(rgb(125, 128, 130), 45%);
padding: 0 10px;
border-radius: 4px;
height: 25px;
&--mainnet {
background-color: lighten($blue-lagoon, 68%);
}
&--ropsten {
background-color: lighten($crimson, 45%);
}
&--kovan {
background-color: lighten($purple, 65%);
}
&--rinkeby {
background-color: lighten($tulip-tree, 35%);
}
&--poa {
background-color: lighten(#5c34a2, 45%);
}
}
&__name {
font-size: .75rem;
padding-left: 5px;
}
&__icon {
height: 10px;
width: 10px;
border-radius: 10px;
&--mainnet {
background-color: $blue-lagoon;
}
&--ropsten {
background-color: $crimson;
}
&--kovan {
background-color: $purple;
}
&--rinkeby {
background-color: $tulip-tree;
}
&--poa {
background-color: #5c34a2;
}
}
}

View File

@ -1,71 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import {
MAINNET_CODE,
ROPSTEN_CODE,
RINKEYBY_CODE,
KOVAN_CODE,
POA_CODE,
} from '../../../../app/scripts/controllers/network/enums'
const networkToClassHash = {
[MAINNET_CODE]: 'mainnet',
[ROPSTEN_CODE]: 'ropsten',
[RINKEYBY_CODE]: 'rinkeby',
[KOVAN_CODE]: 'kovan',
[POA_CODE]: 'poa',
}
export default class NetworkDisplay extends Component {
static propTypes = {
network: PropTypes.string,
provider: PropTypes.object,
}
static contextTypes = {
t: PropTypes.func,
}
renderNetworkIcon () {
const { network } = this.props
const networkClass = networkToClassHash[network]
return networkClass
? <div className={`network-display__icon network-display__icon--${networkClass}`} />
: <div
className="i fa fa-question-circle fa-med"
style={{
margin: '0 4px',
color: 'rgb(125, 128, 130)',
}}
/>
}
render () {
const { network, provider: { type } } = this.props
const networkClass = networkToClassHash[network]
return (
<div className={classnames(
'network-display__container',
networkClass && ('network-display__container--' + networkClass)
)}>
{
networkClass
? <div className={`network-display__icon network-display__icon--${networkClass}`} />
: <div
className="i fa fa-question-circle fa-med"
style={{
margin: '0 4px',
color: 'rgb(125, 128, 130)',
}}
/>
}
<div className="network-display__name">
{ this.context.t(type) }
</div>
</div>
)
}
}

View File

@ -1,11 +0,0 @@
import { connect } from 'react-redux'
import NetworkDisplay from './network-display.component'
const mapStateToProps = ({ metamask: { network, provider } }) => {
return {
network,
provider,
}
}
export default connect(mapStateToProps)(NetworkDisplay)

View File

@ -1,4 +0,0 @@
import PageContainerHeader from './page-container-header'
import PageContainerFooter from './page-container-footer'
export { default } from './page-container.component'
export { PageContainerHeader, PageContainerFooter }

View File

@ -1,211 +0,0 @@
.page-container {
width: 408px;
background-color: $white;
box-shadow: 0 0 7px 0 rgba(0, 0, 0, .08);
z-index: 25;
display: flex;
flex-flow: column;
border-radius: 8px;
&__header {
display: flex;
flex-flow: column;
border-bottom: 1px solid $geyser;
padding: 16px;
flex: 0 0 auto;
position: relative;
&--no-padding-bottom {
padding-bottom: 0;
}
}
&__header-close {
color: $tundora;
position: absolute;
top: 16px;
right: 16px;
cursor: pointer;
overflow: hidden;
&::after {
content: '\00D7';
font-size: 40px;
line-height: 20px;
}
}
&__header-row {
padding-bottom: 10px;
display: flex;
justify-content: space-between;
}
&__footer {
display: flex;
flex-flow: column;
justify-content: center;
border-top: 1px solid $geyser;
flex: 0 0 auto;
.btn-default,
.btn-confirm {
font-size: 1rem;
}
header {
display: flex;
flex-flow: row;
justify-content: center;
padding: 16px;
flex: 0 0 auto;
}
footer {
display: flex;
flex-flow: row;
justify-content: space-around;
padding: 0 16px 16px;
flex: 0 0 auto;
a, a:hover {
text-decoration: none;
cursor: pointer;
font-size: 0.75rem;
text-transform: uppercase;
color: #2f9ae0;
}
}
}
&__footer-button {
height: 55px;
font-size: 1rem;
text-transform: uppercase;
margin-right: 16px;
&:last-of-type {
margin-right: 0;
}
}
&__back-button {
color: #2f9ae0;
font-size: 1rem;
cursor: pointer;
font-weight: 400;
}
&__title {
color: $black;
font-size: 2rem;
font-weight: 500;
line-height: 2rem;
}
&__subtitle {
padding-top: .5rem;
line-height: initial;
font-size: .9rem;
color: $gray;
}
&__tabs {
display: flex;
margin-top: 16px;
}
&__tab {
min-width: 5rem;
padding: 8px;
color: $dusty-gray;
font-family: Roboto;
font-size: 1rem;
text-align: center;
cursor: pointer;
border-bottom: none;
margin-right: 16px;
&:last-of-type {
margin-right: 0;
}
&--selected {
color: $curious-blue;
border-bottom: 2px solid $curious-blue;
}
}
&--full-width {
width: 100% !important;
}
&--full-height {
height: 100% !important;
max-height: initial !important;
min-height: initial !important;
}
&__content {
overflow-y: auto;
flex: 1;
}
&__warning-container {
background: $linen;
padding: 20px;
display: flex;
align-items: start;
}
&__warning-message {
padding-left: 15px;
}
&__warning-title {
font-weight: 500;
}
&__warning-icon {
padding-top: 5px;
}
}
@media screen and (max-width: 250px) {
.page-container {
&__footer {
flex-flow: column-reverse;
}
&__footer-button {
width: 100%;
margin-bottom: 1rem;
margin-right: 0;
&:first-of-type {
margin-bottom: 0;
}
}
}
}
@media screen and (max-width: 575px) {
.page-container {
height: 100%;
width: 100%;
overflow-y: auto;
background-color: $white;
border-radius: 0;
flex: 1;
}
}
@media screen and (min-width: 576px) {
.page-container {
max-height: 82vh;
min-height: 570px;
flex: 0 0 auto;
margin-right: auto;
margin-left: auto;
}
}

View File

@ -1,18 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class PageContainerContent extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
render () {
return (
<div className="page-container__content">
{this.props.children}
</div>
)
}
}

View File

@ -1 +0,0 @@
export { default } from './page-container-footer.component'

View File

@ -1,66 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Button from '../../button'
export default class PageContainerFooter extends Component {
static propTypes = {
children: PropTypes.node,
onCancel: PropTypes.func,
cancelText: PropTypes.string,
onSubmit: PropTypes.func,
submitText: PropTypes.string,
disabled: PropTypes.bool,
submitButtonType: PropTypes.string,
}
static contextTypes = {
t: PropTypes.func,
}
render () {
const {
children,
onCancel,
cancelText,
onSubmit,
submitText,
disabled,
submitButtonType,
} = this.props
return (
<div className="page-container__footer">
<header>
<Button
type="default"
large
className="page-container__footer-button"
onClick={e => onCancel(e)}
>
{ cancelText || this.context.t('cancel') }
</Button>
<Button
type={submitButtonType || 'primary'}
large
className="page-container__footer-button"
disabled={disabled}
onClick={e => onSubmit(e)}
>
{ submitText || this.context.t('next') }
</Button>
</header>
{children && (
<footer>
{children}
</footer>
)}
</div>
)
}
}

View File

@ -1,79 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import Button from '../../../button'
import PageFooter from '../page-container-footer.component'
describe('Page Footer', () => {
let wrapper
const onCancel = sinon.spy()
const onSubmit = sinon.spy()
beforeEach(() => {
wrapper = shallow(<PageFooter
onCancel = {onCancel}
onSubmit = {onSubmit}
cancelText = {'Cancel'}
submitText = {'Submit'}
disabled = {false}
submitButtonType = {'Test Type'}
/>)
})
it('renders page container footer', () => {
assert.equal(wrapper.find('.page-container__footer').length, 1)
})
it('should render a footer inside page-container__footer when given children', () => {
const wrapper = shallow(
<PageFooter>
<div>Works</div>
</PageFooter>,
{ context: { t: sinon.spy((k) => `[${k}]`) } }
)
assert.equal(wrapper.find('.page-container__footer footer').length, 1)
})
it('renders two button components', () => {
assert.equal(wrapper.find(Button).length, 2)
})
describe('Cancel Button', () => {
it('has button type of default', () => {
assert.equal(wrapper.find('.page-container__footer-button').first().prop('type'), 'default')
})
it('has children text of Cancel', () => {
assert.equal(wrapper.find('.page-container__footer-button').first().prop('children'), 'Cancel')
})
it('should call cancel when click is simulated', () => {
wrapper.find('.page-container__footer-button').first().prop('onClick')()
assert.equal(onCancel.callCount, 1)
})
})
describe('Submit Button', () => {
it('assigns button type based on props', () => {
assert.equal(wrapper.find('.page-container__footer-button').last().prop('type'), 'Test Type')
})
it('has disabled prop', () => {
assert.equal(wrapper.find('.page-container__footer-button').last().prop('disabled'), false)
})
it('has children text when submitText prop exists', () => {
assert.equal(wrapper.find('.page-container__footer-button').last().prop('children'), 'Submit')
})
it('should call submit when click is simulated', () => {
wrapper.find('.page-container__footer-button').last().prop('onClick')()
assert.equal(onSubmit.callCount, 1)
})
})
})

View File

@ -1 +0,0 @@
export { default } from './page-container-header.component'

View File

@ -1,80 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
export default class PageContainerHeader extends Component {
static propTypes = {
title: PropTypes.string,
subtitle: PropTypes.string,
onClose: PropTypes.func,
showBackButton: PropTypes.bool,
onBackButtonClick: PropTypes.func,
backButtonStyles: PropTypes.object,
backButtonString: PropTypes.string,
tabs: PropTypes.node,
}
renderTabs () {
const { tabs } = this.props
return tabs && (
<ul className="page-container__tabs">
{ tabs }
</ul>
)
}
renderHeaderRow () {
const { showBackButton, onBackButtonClick, backButtonStyles, backButtonString } = this.props
return showBackButton && (
<div className="page-container__header-row">
<span
className="page-container__back-button"
onClick={onBackButtonClick}
style={backButtonStyles}
>
{ backButtonString || 'Back' }
</span>
</div>
)
}
render () {
const { title, subtitle, onClose, tabs } = this.props
return (
<div className={
classnames(
'page-container__header',
{ 'page-container__header--no-padding-bottom': Boolean(tabs) }
)
}>
{ this.renderHeaderRow() }
{
title && <div className="page-container__title">
{ title }
</div>
}
{
subtitle && <div className="page-container__subtitle">
{ subtitle }
</div>
}
{
onClose && <div
className="page-container__header-close"
onClick={() => onClose()}
/>
}
{ this.renderTabs() }
</div>
)
}
}

View File

@ -1,82 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import PageContainerHeader from '../page-container-header.component'
describe('Page Container Header', () => {
let wrapper, style, onBackButtonClick, onClose
beforeEach(() => {
style = {test: 'style'}
onBackButtonClick = sinon.spy()
onClose = sinon.spy()
wrapper = shallow(<PageContainerHeader
showBackButton = {true}
onBackButtonClick = {onBackButtonClick}
backButtonStyles = {style}
title = {'Test Title'}
subtitle = {'Test Subtitle'}
tabs = {'Test Tab'}
onClose = {onClose}
/>)
})
describe('Render Header Row', () => {
it('renders back button', () => {
assert.equal(wrapper.find('.page-container__back-button').length, 1)
assert.equal(wrapper.find('.page-container__back-button').text(), 'Back')
})
it('ensures style prop', () => {
assert.equal(wrapper.find('.page-container__back-button').props().style, style)
})
it('should call back button when click is simulated', () => {
wrapper.find('.page-container__back-button').prop('onClick')()
assert.equal(onBackButtonClick.callCount, 1)
})
})
describe('Render', () => {
let header, headerRow, pageTitle, pageSubtitle, pageClose, pageTab
beforeEach(() => {
header = wrapper.find('.page-container__header--no-padding-bottom')
headerRow = wrapper.find('.page-container__header-row')
pageTitle = wrapper.find('.page-container__title')
pageSubtitle = wrapper.find('.page-container__subtitle')
pageClose = wrapper.find('.page-container__header-close')
pageTab = wrapper.find('.page-container__tabs')
})
it('renders page container', () => {
assert.equal(header.length, 1)
assert.equal(headerRow.length, 1)
assert.equal(pageTitle.length, 1)
assert.equal(pageSubtitle.length, 1)
assert.equal(pageClose.length, 1)
assert.equal(pageTab.length, 1)
})
it('renders title', () => {
assert.equal(pageTitle.text(), 'Test Title')
})
it('renders subtitle', () => {
assert.equal(pageSubtitle.text(), 'Test Subtitle')
})
it('renders tabs', () => {
assert.equal(pageTab.text(), 'Test Tab')
})
it('should call close when click is simulated', () => {
pageClose.prop('onClick')()
assert.equal(onClose.callCount, 1)
})
})
})

View File

@ -1,122 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import PageContainerHeader from './page-container-header'
import PageContainerFooter from './page-container-footer'
export default class PageContainer extends PureComponent {
static propTypes = {
// PageContainerHeader props
backButtonString: PropTypes.string,
backButtonStyles: PropTypes.object,
onBackButtonClick: PropTypes.func,
onClose: PropTypes.func,
showBackButton: PropTypes.bool,
subtitle: PropTypes.string,
title: PropTypes.string.isRequired,
// Tabs-related props
defaultActiveTabIndex: PropTypes.number,
tabsComponent: PropTypes.node,
// Content props
contentComponent: PropTypes.node,
// PageContainerFooter props
cancelText: PropTypes.string,
disabled: PropTypes.bool,
onCancel: PropTypes.func,
onSubmit: PropTypes.func,
submitText: PropTypes.string,
}
state = {
activeTabIndex: this.props.defaultActiveTabIndex || 0,
}
handleTabClick (activeTabIndex) {
this.setState({ activeTabIndex })
}
renderTabs () {
const { tabsComponent } = this.props
if (!tabsComponent) {
return
}
const numberOfTabs = React.Children.count(tabsComponent.props.children)
return React.Children.map(tabsComponent.props.children, (child, tabIndex) => {
return child && React.cloneElement(child, {
onClick: index => this.handleTabClick(index),
tabIndex,
isActive: numberOfTabs > 1 && tabIndex === this.state.activeTabIndex,
key: tabIndex,
className: 'page-container__tab',
activeClassName: 'page-container__tab--selected',
})
})
}
renderActiveTabContent () {
const { tabsComponent } = this.props
const { children } = tabsComponent.props
const { activeTabIndex } = this.state
return children[activeTabIndex]
? children[activeTabIndex].props.children
: children.props.children
}
renderContent () {
const { contentComponent, tabsComponent } = this.props
if (contentComponent) {
return contentComponent
} else if (tabsComponent) {
return this.renderActiveTabContent()
} else {
return null
}
}
render () {
const {
title,
subtitle,
onClose,
showBackButton,
onBackButtonClick,
backButtonStyles,
backButtonString,
onCancel,
cancelText,
onSubmit,
submitText,
disabled,
} = this.props
return (
<div className="page-container">
<PageContainerHeader
title={title}
subtitle={subtitle}
onClose={onClose}
showBackButton={showBackButton}
onBackButtonClick={onBackButtonClick}
backButtonStyles={backButtonStyles}
backButtonString={backButtonString}
tabs={this.renderTabs()}
/>
<div className="page-container__content">
{ this.renderContent() }
</div>
<PageContainerFooter
onCancel={onCancel}
cancelText={cancelText}
onSubmit={onSubmit}
submitText={submitText}
disabled={disabled}
/>
</div>
)
}
}

View File

@ -1,7 +0,0 @@
@import './unlock-page/index';
@import './add-token/index';
@import './confirm-add-token/index';
@import './settings/index';

View File

@ -1,2 +0,0 @@
import UnlockPage from './unlock-page.container'
module.exports = UnlockPage

View File

@ -1,48 +0,0 @@
.unlock-page {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
width: 357px;
padding: 30px;
font-weight: 400;
color: $silver-chalice;
&__container {
background: $white;
display: flex;
align-self: stretch;
justify-content: center;
flex: 1 0 auto;
height: 100vh;
}
&__title {
margin-top: 5px;
font-size: 2rem;
font-weight: 800;
color: $tundora;
}
&__form {
width: 100%;
margin: 56px 0 8px;
}
&__links {
margin-top: 25px;
width: 100%;
}
&__link {
cursor: pointer;
&--import {
color: $ecstasy;
}
&--use-classic {
margin-top: 10px;
}
}
}

View File

@ -1,161 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Button from '@material-ui/core/Button'
import TextField from '../../text-field'
import { ENVIRONMENT_TYPE_POPUP } from '../../../../../app/scripts/lib/enums'
import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
import { EventEmitter } from 'events'
import { DEFAULT_ROUTE, RESTORE_VAULT_ROUTE } from '../../../routes'
export default class UnlockPage extends Component {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
forgotPassword: PropTypes.func,
tryUnlockMetamask: PropTypes.func,
markPasswordForgotten: PropTypes.func,
history: PropTypes.object,
isUnlocked: PropTypes.bool,
useOldInterface: PropTypes.func,
}
constructor (props) {
super(props)
this.state = {
password: '',
error: null,
}
this.submitting = false
this.animationEventEmitter = new EventEmitter()
}
componentWillMount () {
const { isUnlocked, history } = this.props
if (isUnlocked) {
history.push(DEFAULT_ROUTE)
}
}
async handleSubmit (event) {
event.preventDefault()
event.stopPropagation()
const { password } = this.state
const { tryUnlockMetamask, history } = this.props
if (password === '' || this.submitting) {
return
}
this.setState({ error: null })
this.submitting = true
try {
await tryUnlockMetamask(password)
this.submitting = false
history.push(DEFAULT_ROUTE)
} catch ({ message }) {
this.setState({ error: message })
this.submitting = false
}
}
handleInputChange ({ target }) {
this.setState({ password: target.value, error: null })
}
renderSubmitButton () {
const style = {
backgroundColor: '#f7861c',
color: 'white',
marginTop: '20px',
height: '60px',
fontWeight: '400',
boxShadow: 'none',
borderRadius: '4px',
}
return (
<Button
type="submit"
style={style}
disabled={!this.state.password}
fullWidth
variant="raised"
size="large"
onClick={event => this.handleSubmit(event)}
disableRipple
>
{ this.context.t('login') }
</Button>
)
}
render () {
const { password, error } = this.state
const { t } = this.context
const { markPasswordForgotten, history } = this.props
return (
<div className="unlock-page__container">
<div className="unlock-page">
<h1 className="unlock-page__title">
{ t('welcomeBack') }
</h1>
<div>{ t('unlockMessage') }</div>
<form
className="unlock-page__form"
onSubmit={event => this.handleSubmit(event)}
>
<TextField
id="password"
label={t('password')}
type="password"
value={password}
onChange={event => this.handleInputChange(event)}
error={error}
autoFocus
autoComplete="current-password"
material
fullWidth
/>
</form>
{ this.renderSubmitButton() }
<div className="unlock-page__links">
<div
className="unlock-page__link"
onClick={() => {
markPasswordForgotten()
history.push(RESTORE_VAULT_ROUTE)
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
global.platform.openExtensionInBrowser()
}
}}
>
{ t('restoreFromSeed') }
</div>
<div
className="unlock-page__link unlock-page__link--import"
onClick={() => {
markPasswordForgotten()
history.push(RESTORE_VAULT_ROUTE)
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
global.platform.openExtensionInBrowser()
}
}}
>
{ t('importUsingSeed') }
</div>
</div>
</div>
</div>
)
}
}

View File

@ -1,31 +0,0 @@
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import { compose } from 'recompose'
const {
tryUnlockMetamask,
forgotPassword,
markPasswordForgotten,
} = require('../../../actions')
import UnlockPage from './unlock-page.component'
const mapStateToProps = state => {
const { metamask: { isUnlocked } } = state
return {
isUnlocked,
}
}
const mapDispatchToProps = dispatch => {
return {
forgotPassword: () => dispatch(forgotPassword()),
tryUnlockMetamask: password => dispatch(tryUnlockMetamask(password)),
markPasswordForgotten: () => dispatch(markPasswordForgotten()),
}
}
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(UnlockPage)

View File

@ -1,63 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const qrCode = require('qrcode-npm').qrcode
const inherits = require('util').inherits
const connect = require('react-redux').connect
const { isHexPrefixed } = require('ethereumjs-util')
const ReadOnlyInput = require('./readonly-input')
const { checksumAddress } = require('../util')
module.exports = connect(mapStateToProps)(QrCodeView)
function mapStateToProps (state) {
return {
// Qr code is not fetched from state. 'message' and 'data' props are passed instead.
buyView: state.appState.buyView,
warning: state.appState.warning,
}
}
inherits(QrCodeView, Component)
function QrCodeView () {
Component.call(this)
}
QrCodeView.prototype.render = function () {
const props = this.props
const { message, data } = props.Qr
const address = `${isHexPrefixed(data) ? 'ethereum:' : ''}${checksumAddress(data)}`
const qrImage = qrCode(4, 'M')
qrImage.addData(address)
qrImage.make()
return h('.div.flex-column.flex-center', [
Array.isArray(message)
? h('.message-container', this.renderMultiMessage())
: message && h('.qr-header', message),
this.props.warning ? this.props.warning && h('span.error.flex-center', {
style: {
},
},
this.props.warning) : null,
h('.div.qr-wrapper', {
style: {},
dangerouslySetInnerHTML: {
__html: qrImage.createTableTag(4),
},
}),
h(ReadOnlyInput, {
wrapperClass: 'ellip-address-wrapper',
inputClass: 'qr-ellip-address',
value: checksumAddress(data),
}),
])
}
QrCodeView.prototype.renderMultiMessage = function () {
var Qr = this.props.Qr
var multiMessage = Qr.message.map((message) => h('.qr-message', message))
return multiMessage
}

View File

@ -1,2 +0,0 @@
import SelectedAccount from './selected-account.container'
module.exports = SelectedAccount

View File

@ -1,38 +0,0 @@
.selected-account {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1;
&__name {
max-width: 200px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
text-align: center;
}
&__address {
font-size: .75rem;
color: $silver-chalice;
}
&__clickable {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 5px 15px;
border-radius: 10px;
cursor: pointer;
&:hover {
background-color: #e8e6e8;
}
&:active {
background-color: #d9d7da;
}
}
}

View File

@ -1,54 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import copyToClipboard from 'copy-to-clipboard'
import { addressSlicer, checksumAddress } from '../../util'
const Tooltip = require('../tooltip-v2.js').default
class SelectedAccount extends Component {
state = {
copied: false,
}
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
selectedAddress: PropTypes.string,
selectedIdentity: PropTypes.object,
}
render () {
const { t } = this.context
const { selectedAddress, selectedIdentity } = this.props
const checksummedAddress = checksumAddress(selectedAddress)
return (
<div className="selected-account">
<Tooltip
position="bottom"
title={this.state.copied ? t('copiedExclamation') : t('copyToClipboard')}
>
<div
className="selected-account__clickable"
onClick={() => {
this.setState({ copied: true })
setTimeout(() => this.setState({ copied: false }), 3000)
copyToClipboard(checksummedAddress)
}}
>
<div className="selected-account__name">
{ selectedIdentity.name }
</div>
<div className="selected-account__address">
{ addressSlicer(checksummedAddress) }
</div>
</div>
</Tooltip>
</div>
)
}
}
export default SelectedAccount

View File

@ -1,13 +0,0 @@
import { connect } from 'react-redux'
import SelectedAccount from './selected-account.component'
const selectors = require('../../selectors')
const mapStateToProps = state => {
return {
selectedAddress: selectors.getSelectedAddress(state),
selectedIdentity: selectors.getSelectedIdentity(state),
}
}
export default connect(mapStateToProps)(SelectedAccount)

View File

@ -1,16 +0,0 @@
import React from 'react'
import assert from 'assert'
import { render } from 'enzyme'
import SelectedAccount from '../selected-account.component'
describe('SelectedAccount Component', () => {
it('should render checksummed address', () => {
const wrapper = render(<SelectedAccount
selectedAddress="0x1b82543566f41a7db9a9a75fc933c340ffb55c9d"
selectedIdentity={{ name: 'testName' }}
/>, { context: { t: () => {}}})
// Checksummed version of address is displayed
assert.equal(wrapper.find('.selected-account__address').text(), '0x1B82...5C9D')
assert.equal(wrapper.find('.selected-account__name').text(), 'testName')
})
})

View File

@ -1 +0,0 @@
export { default } from './sidebar.component'

View File

@ -1,74 +0,0 @@
.sidebar-right-enter {
transition: transform 300ms ease-in-out;
transform: translateX(-100%);
}
.sidebar-right-enter.sidebar-right-enter-active {
transition: transform 300ms ease-in-out;
transform: translateX(0%);
}
.sidebar-right-leave {
transition: transform 200ms ease-out;
transform: translateX(0%);
}
.sidebar-right-leave.sidebar-right-leave-active {
transition: transform 200ms ease-out;
transform: translateX(-100%);
}
.sidebar-left-enter {
transition: transform 300ms ease-in-out;
transform: translateX(100%);
}
.sidebar-left-enter.sidebar-left-enter-active {
transition: transform 300ms ease-in-out;
transform: translateX(0%);
}
.sidebar-left-leave {
transition: transform 200ms ease-out;
transform: translateX(0%);
}
.sidebar-left-leave.sidebar-left-leave-active {
transition: transform 200ms ease-out;
transform: translateX(100%);
}
.sidebar-left {
flex: 1 0 230px;
background: rgb(250, 250, 250);
z-index: $sidebar-z-index;
position: fixed;
left: 15%;
right: 0;
bottom: 0;
opacity: 1;
visibility: visible;
will-change: transform;
overflow-y: auto;
box-shadow: rgba(0, 0, 0, .15) 2px 2px 4px;
width: 85%;
height: 100%;
@media screen and (min-width: 769px) {
width: 408px;
left: calc(100% - 408px);
}
}
.sidebar-overlay {
z-index: $sidebar-overlay-z-index;
position: fixed;
height: 100%;
width: 100%;
left: 0;
right: 0;
bottom: 0;
opacity: 1;
visibility: visible;
background-color: rgba(0, 0, 0, .3);
}

View File

@ -1,49 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { CSSTransitionGroup } from 'react-transition-group'
import WalletView from '../wallet-view'
import { WALLET_VIEW_SIDEBAR } from './sidebar.constants'
export default class Sidebar extends Component {
static propTypes = {
sidebarOpen: PropTypes.bool,
hideSidebar: PropTypes.func,
transitionName: PropTypes.string,
type: PropTypes.string,
};
renderOverlay () {
return <div className="sidebar-overlay" onClick={() => this.props.hideSidebar()} />
}
renderSidebarContent () {
const { type } = this.props
switch (type) {
case WALLET_VIEW_SIDEBAR:
return <WalletView responsiveDisplayClassname={'sidebar-right' } />
default:
return null
}
}
render () {
const { transitionName, sidebarOpen } = this.props
return (
<div>
<CSSTransitionGroup
transitionName={transitionName}
transitionEnterTimeout={300}
transitionLeaveTimeout={200}
>
{ sidebarOpen ? this.renderSidebarContent() : null }
</CSSTransitionGroup>
{ sidebarOpen ? this.renderOverlay() : null }
</div>
)
}
}

View File

@ -1 +0,0 @@
export const WALLET_VIEW_SIDEBAR = 'wallet-view'

View File

@ -1,88 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import { CSSTransitionGroup } from 'react-transition-group'
import Sidebar from '../sidebar.component.js'
import WalletView from '../../wallet-view'
const propsMethodSpies = {
hideSidebar: sinon.spy(),
}
describe('Sidebar Component', function () {
let wrapper
beforeEach(() => {
wrapper = shallow(<Sidebar
sidebarOpen={false}
hideSidebar={propsMethodSpies.hideSidebar}
transitionName={'someTransition'}
type={'wallet-view'}
/>)
})
afterEach(() => {
propsMethodSpies.hideSidebar.resetHistory()
})
describe('renderOverlay', () => {
let renderOverlay
beforeEach(() => {
renderOverlay = shallow(wrapper.instance().renderOverlay())
})
it('should render a overlay element', () => {
assert(renderOverlay.hasClass('sidebar-overlay'))
})
it('should pass the correct onClick function to the element', () => {
assert.equal(propsMethodSpies.hideSidebar.callCount, 0)
renderOverlay.props().onClick()
assert.equal(propsMethodSpies.hideSidebar.callCount, 1)
})
})
describe('renderSidebarContent', () => {
let renderSidebarContent
beforeEach(() => {
wrapper.setProps({ type: 'wallet-view' })
renderSidebarContent = wrapper.instance().renderSidebarContent()
})
it('should render sidebar content with the correct props', () => {
wrapper.setProps({ type: 'wallet-view' })
renderSidebarContent = wrapper.instance().renderSidebarContent()
assert.equal(renderSidebarContent.props.responsiveDisplayClassname, 'sidebar-right')
})
it('should not render with an unrecognized type', () => {
wrapper.setProps({ type: 'foobar' })
renderSidebarContent = wrapper.instance().renderSidebarContent()
assert.equal(renderSidebarContent, undefined)
})
})
describe('render', () => {
it('should render a div with one child', () => {
assert(wrapper.is('div'))
assert.equal(wrapper.children().length, 1)
})
it('should render the CSSTransitionGroup without any children', () => {
assert(wrapper.children().at(0).is(CSSTransitionGroup))
assert.equal(wrapper.children().at(0).children().length, 0)
})
it('should render sidebar content and the overlay if sidebarOpen is true', () => {
wrapper.setProps({ sidebarOpen: true })
assert.equal(wrapper.children().length, 2)
assert(wrapper.children().at(1).hasClass('sidebar-overlay'))
assert.equal(wrapper.children().at(0).children().length, 1)
assert(wrapper.children().at(0).children().at(0).is(WalletView))
})
})
})

View File

@ -1,3 +0,0 @@
import Tabs from './tabs.component'
import Tab from './tab'
export { Tabs, Tab }

View File

@ -1,11 +0,0 @@
@import './tab/index';
.tabs {
&__list {
display: flex;
justify-content: flex-start;
background-color: #f9fafa;
border-bottom: 1px solid $geyser;
padding: 0 16px;
}
}

View File

@ -1,2 +0,0 @@
import Tab from './tab.component'
module.exports = Tab

View File

@ -1,15 +0,0 @@
.tab {
color: #8C8E94;
font-size: .75rem;
text-transform: uppercase;
cursor: pointer;
padding: 8px 0;
margin: 0 8px;
min-width: 50px;
text-align: center;
&--active {
color: $black;
border-bottom: 2px solid $curious-blue;
}
}

View File

@ -1,38 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
const Tab = props => {
const { name, onClick, isActive, tabIndex, className, activeClassName } = props
return (
<li
className={classnames(
className,
{ [activeClassName]: isActive },
)}
onClick={event => {
event.preventDefault()
onClick(tabIndex)
}}
>
{ name }
</li>
)
}
Tab.propTypes = {
name: PropTypes.string.isRequired,
onClick: PropTypes.func,
isActive: PropTypes.bool,
tabIndex: PropTypes.number,
className: PropTypes.string,
activeClassName: PropTypes.string,
}
Tab.defaultProps = {
className: 'tab',
activeClassName: 'tab--active',
}
export default Tab

View File

@ -1,62 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Tabs extends Component {
static propTypes = {
defaultActiveTabIndex: PropTypes.number,
children: PropTypes.node,
}
constructor (props) {
super(props)
this.state = {
activeTabIndex: props.defaultActiveTabIndex || 0,
}
}
handleTabClick (tabIndex) {
const { activeTabIndex } = this.state
if (tabIndex !== activeTabIndex) {
this.setState({
activeTabIndex: tabIndex,
})
}
}
renderTabs () {
const numberOfTabs = React.Children.count(this.props.children)
return React.Children.map(this.props.children, (child, index) => {
return child && React.cloneElement(child, {
onClick: index => this.handleTabClick(index),
tabIndex: index,
isActive: numberOfTabs > 1 && index === this.state.activeTabIndex,
key: index,
})
})
}
renderActiveTabContent () {
const { children } = this.props
const { activeTabIndex } = this.state
return children[activeTabIndex]
? children[activeTabIndex].props.children
: children.props.children
}
render () {
return (
<div className="tabs">
<ul className="tabs__list">
{ this.renderTabs() }
</ul>
<div className="tabs__content">
{ this.renderActiveTabContent() }
</div>
</div>
)
}
}

View File

@ -1,20 +0,0 @@
/*
ITCSS
http://www.creativebloq.com/web-design/manage-large-css-projects-itcss-101517528
https://www.xfive.co/blog/itcss-scalable-maintainable-css-architecture/
*/
@import './itcss/settings/index.scss';
@import './itcss/tools/index.scss';
@import './itcss/generic/index.scss';
@import './itcss/base/index.scss';
@import './itcss/objects/index.scss';
@import './itcss/components/index.scss';
@import './itcss/trumps/index.scss';

View File

@ -1,7 +0,0 @@
// Base
.mouse-user-styles {
button:focus {
outline: 0;
}
}

View File

@ -1,7 +0,0 @@
.account-details-dropdown {
width: 60%;
position: absolute;
top: 120px;
right: 15px;
z-index: 2000;
}

View File

@ -1,48 +0,0 @@
.account-dropdown-mini {
height: 22px;
background-color: $white;
font-family: Roboto;
line-height: 16px;
font-size: 12px;
width: 124px;
&__close-area {
position: fixed;
top: 0;
left: 0;
z-index: 1000;
width: 100%;
height: 100%;
}
&__list {
z-index: 1050;
position: absolute;
height: 180px;
width: 96pxpx;
border: 1px solid $geyser;
border-radius: 4px;
background-color: $white;
box-shadow: 0 3px 6px 0 rgba(0 ,0 ,0 ,.11);
overflow-y: scroll;
}
.account-list-item {
margin-top: 6px;
}
.account-list-item__account-name {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 80px;
}
.account-list-item__top-row {
margin: 0;
}
.account-list-item__icon {
position: initial;
}
}

View File

@ -1,82 +0,0 @@
.account-dropdown-name {
font-family: Roboto;
}
.account-dropdown-balance {
color: $dusty-gray;
line-height: 19px;
}
.account-dropdown-edit-button {
color: $dusty-gray;
font-family: Roboto;
&:hover {
color: $white;
}
}
.account-list-item {
&__top-row {
display: flex;
margin-top: 10px;
margin-left: 8px;
position: relative;
}
&__account-balances {
height: auto;
border: none;
background-color: transparent;
color: #9b9b9b;
margin-left: 34px;
margin-top: 4px;
position: relative;
}
&__account-name {
font-size: 16px;
margin-left: 8px;
}
&__icon {
position: absolute;
right: 12px;
top: 1px;
}
&__account-primary-balance,
&__account-secondary-balance {
font-family: Roboto;
line-height: 16px;
font-size: 12px;
}
&__account-primary-balance {
color: $scorpion;
border: none;
outline: 0 !important;
}
&__account-secondary-balance {
color: $dusty-gray;
}
&__account-address {
margin-left: 35px;
width: 80%;
overflow: hidden;
text-overflow: ellipsis;
}
&__dropdown {
&:hover {
background: rgba($alto, .2);
cursor: pointer;
input {
background: rgba($alto, .1);
}
}
}
}

View File

@ -1,153 +0,0 @@
.account-menu {
position: fixed;
z-index: 100;
top: 58px;
width: 310px;
@media screen and (max-width: 575px) {
right: calc(((100vw - 100%) / 2) + 8px);
}
@media screen and (min-width: 576px) {
right: calc((100vw - 85vw) / 2);
}
@media screen and (min-width: 769px) {
right: calc((100vw - 80vw) / 2);
}
@media screen and (min-width: 1281px) {
right: calc((100vw - 65vw) / 2);
}
&__icon {
margin-left: 20px;
cursor: pointer;
&--disabled {
cursor: initial;
}
}
&__header {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
}
&__logout-button {
border: 1px solid $dusty-gray;
background-color: transparent;
color: $white;
border-radius: 4px;
font-size: 12px;
line-height: 23px;
padding: 0 24px;
}
&__item-icon {
width: 16px;
height: 16px;
}
&__accounts {
display: flex;
flex-flow: column nowrap;
overflow-y: auto;
max-height: 240px;
position: relative;
z-index: 200;
&::-webkit-scrollbar {
display: none;
}
@media screen and (max-width: 575px) {
max-height: 215px;
}
.keyring-label {
margin-top: 5px;
background-color: $dusty-gray;
color: $black;
font-weight: normal;
letter-spacing: .5px;
}
}
&__account {
display: flex;
flex-flow: row nowrap;
padding: 16px 14px;
flex: 0 0 auto;
@media screen and (max-width: 575px) {
padding: 12px 14px;
}
.remove-account-icon {
width: 15px;
margin-left: 10px;
height: 15px;
}
&:hover {
.remove-account-icon::after {
content: '\00D7';
font-size: 25px;
color: $white;
cursor: pointer;
position: absolute;
margin-top: -5px;
}
}
}
&__account-info {
flex: 1 0 auto;
display: flex;
flex-flow: column nowrap;
}
&__check-mark {
width: 14px;
margin-right: 12px;
flex: 0 0 auto;
}
&__check-mark-icon {
background-image: url("images/check-white.svg");
height: 18px;
width: 18px;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
margin: 3px 0;
}
.identicon {
margin: 0 12px 0 0;
flex: 0 0 auto;
}
&__name {
color: $white;
font-size: 18px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
max-width: 200px;
}
&__balance {
color: $dusty-gray;
font-size: 14px;
}
&__action {
font-size: 16px;
line-height: 18px;
cursor: pointer;
}
}

View File

@ -1,57 +0,0 @@
.global-alert {
position: relative;
width: 100%;
background-color: #33A4E7;
.msg {
width: 100%;
display: block;
color: white;
font-size: 12px;
text-align: center;
}
}
.global-alert.hidden {
animation: alertHidden .5s ease forwards;
}
.global-alert.visible {
animation: alert .5s ease forwards;
}
/* Animation */
@keyframes alert {
0% {
opacity: 0;
top: -50px;
padding: 0px;
line-height: 12px;
}
50% {
opacity: 1;
}
100% {
top: 0;
padding: 8px;
line-height: 12px;
}
}
@keyframes alertHidden {
0% {
top: 0;
opacity: 1;
padding: 8px;
line-height: 12px;
}
100% {
opacity: 0;
top: -50px;
padding: 0px;
line-height: 0px;
}
}

View File

@ -1,217 +0,0 @@
/*
Buttons
*/
.button {
height: 44px;
background: $white;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
transition: border-color .3s ease;
padding: 0 16px;
min-width: 140px;
width: 100%;
text-transform: uppercase;
outline: none;
font-family: Roboto;
&--disabled,
&[disabled] {
cursor: auto;
opacity: .5;
pointer-events: none;
}
}
.btn-primary {
color: $curious-blue;
border: 2px solid $spindle;
&:active {
background: $zumthor;
border-color: $curious-blue;
}
&:hover {
border-color: $curious-blue;
}
}
.btn-secondary {
color: $monzo;
border: 2px solid lighten($monzo, 40%);
&:active {
background: lighten($monzo, 55%);
border-color: $monzo;
}
&:hover {
border-color: $monzo;
}
}
.btn-default {
color: $scorpion;
border: 2px solid $dusty-gray;
&:active {
background: $gallery;
border-color: $dusty-gray;
}
&:hover {
border-color: $scorpion;
}
}
.btn-confirm {
color: $white;
border: 2px solid $curious-blue;
background-color: $curious-blue;
}
.btn-raised {
color: $curious-blue;
background-color: $white;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);
padding: 6px;
height: initial;
width: initial;
min-width: initial;
}
.btn--large {
height: 54px;
}
.btn-green {
background-color: #02c9b1; // TODO: reusable color in colors.css
}
.btn-clear {
background: $white;
text-align: center;
padding: .8rem 1rem;
color: $curious-blue;
border: 2px solid $spindle;
border-radius: 4px;
font-size: .85rem;
font-weight: 400;
transition: border-color .3s ease;
&:hover {
border-color: $curious-blue;
}
&--disabled,
&[disabled] {
cursor: auto;
opacity: .5;
pointer-events: none;
}
}
.btn-cancel {
background: $white;
text-align: center;
padding: .9rem 1rem;
color: $scorpion;
border: 2px solid $dusty-gray;
border-radius: 4px;
font-size: .85rem;
font-weight: 400;
transition: border-color .3s ease;
width: 100%;
&:hover {
border-color: $scorpion;
}
}
// No longer used in flat design, remove when modal buttons done
// div.wallet-btn {
// border: 1px solid rgb(91, 93, 103);
// border-radius: 2px;
// height: 30px;
// width: 75px;
// font-size: 0.8em;
// text-align: center;
// line-height: 25px;
// }
// .btn-red {
// background: rgba(254, 35, 17, 1);
// box-shadow: 0px 3px 6px rgba(254, 35, 17, 0.36);
// }
button[disabled],
input[type="submit"][disabled] {
cursor: not-allowed;
opacity: .5;
// background: rgba(197, 197, 197, 1);
// box-shadow: 0 3px 6px rgba(197, 197, 197, .36);
}
// button.spaced {
// margin: 2px;
// }
// button:not([disabled]):hover, input[type="submit"]:not([disabled]):hover {
// transform: scale(1.1);
// }
// button:not([disabled]):active, input[type="submit"]:not([disabled]):active {
// transform: scale(0.95);
// }
button.primary {
padding: 8px 12px;
background: #f7861c;
box-shadow: 0 3px 6px rgba(247, 134, 28, .36);
color: $white;
font-size: 1.1em;
font-family: Roboto;
text-transform: uppercase;
}
.btn-light {
padding: 8px 12px;
// background: #FFFFFF; // $bg-white
box-shadow: 0 3px 6px rgba(247, 134, 28, .36);
color: #585d67; // TODO: make reusable light button color
font-size: 1.1em;
font-family: Roboto;
text-transform: uppercase;
text-align: center;
line-height: 20px;
border-radius: 2px;
border: 1px solid #979797; // #TODO: make reusable light border color
opacity: .5;
}
// TODO: cleanup: not used anywhere
button.btn-thin {
border: 1px solid;
border-color: #4d4d4d;
color: #4d4d4d;
background: rgb(255, 174, 41);
border-radius: 4px;
min-width: 200px;
margin: 12px 0;
padding: 6px;
font-size: 13px;
}
.btn-tertiary {
border: 1px solid transparent;
border-radius: 2px;
background-color: transparent;
font-size: 16px;
line-height: 24px;
padding: 16px 42px;
}

View File

@ -1,354 +0,0 @@
.confirm-screen-container {
position: relative;
align-items: center;
font-family: Roboto;
flex: 1 0 auto;
flex-flow: column nowrap;
box-shadow: 0 2px 4px 0 rgba($black, .08);
border-radius: 8px;
display: flex;
@media screen and (max-width: 575px) {
width: 100%;
box-shadow: initial;
}
@media screen and (min-width: 576px) {
// top: -26px;
}
}
.notification {
.confirm-screen-wrapper {
@media screen and (max-width: $break-small) {
height: calc(100vh - 85px);
}
}
}
.confirm-screen-wrapper {
height: 100%;
width: 380px;
background-color: $white;
display: flex;
flex-flow: column nowrap;
z-index: 25;
align-items: center;
font-family: Roboto;
position: relative;
overflow-y: auto;
overflow-x: hidden;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
@media screen and (max-width: $break-small) {
width: 100%;
overflow-x: hidden;
overflow-y: auto;
top: 0;
box-shadow: none;
height: calc(100vh - 58px - 85px);
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}
.confirm-screen-wrapper > .confirm-screen-total-box {
margin-left: 10px;
margin-right: 10px;
}
.confirm-screen-wrapper > .confirm-memo-wrapper {
margin: 0;
}
.confirm-screen-header {
height: 88px;
background-color: $athens-grey;
position: relative;
display: flex;
justify-content: center;
align-items: center;
font-size: 22px;
line-height: 29px;
width: 100%;
padding: 25px 0;
flex: 0 0 auto;
@media screen and (max-width: $break-small) {
font-size: 20px;
}
}
.confirm-screen-header-tip {
height: 25px;
width: 25px;
background: $athens-grey;
position: absolute;
transform: rotate(45deg);
top: 71px;
left: 0;
right: 0;
margin: 0 auto;
}
.confirm-screen-title {
line-height: 27px;
@media screen and (max-width: $break-small) {
margin-left: 22px;
margin-right: 8px;
}
}
.confirm-screen-back-button {
color: $curious-blue;
font-family: Roboto;
font-size: 1rem;
position: absolute;
top: 38px;
right: 38px;
background: none;
}
.confirm-screen-account-wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
.confirm-screen-account-name {
margin-top: 12px;
font-size: 14px;
line-height: 19px;
color: $scorpion;
text-align: center;
}
.confirm-screen-row-info {
font-size: 16px;
line-height: 21px;
}
.confirm-screen-account-number {
font-size: 10px;
line-height: 16px;
color: $dusty-gray;
text-align: center;
height: 16px;
}
.confirm-send-ether,
.confirm-send-token {
i.fa-arrow-right {
align-self: start;
margin: 24px 14px 0 !important;
}
}
.confirm-screen-identicons {
margin-top: 24px;
flex: 0 0 auto;
i.fa-arrow-right {
align-self: start;
margin: 42px 14px 0;
}
i.fa-file-text-o {
font-size: 60px;
margin: 16px 8px 0 8px;
text-align: center;
}
}
.confirm-screen-sending-to-message {
text-align: center;
font-size: 16px;
margin-top: 30px;
font-family: 'DIN NEXT Light';
}
.confirm-screen-send-amount {
color: $scorpion;
margin-top: 12px;
text-align: center;
font-size: 40px;
line-height: 53px;
flex: 0 0 auto;
}
.confirm-screen-send-amount-currency {
font-size: 20px;
line-height: 20px;
text-align: center;
flex: 0 0 auto;
}
.confirm-memo-wrapper {
min-height: 24px;
width: 100%;
border-bottom: 1px solid $alto;
flex: 0 0 auto;
}
.confirm-screen-send-memo {
color: $scorpion;
font-size: 16px;
line-height: 19px;
font-weight: 400;
}
.confirm-screen-label {
font-size: 16px;
line-height: 40px;
color: $scorpion;
text-align: left;
}
section .confirm-screen-account-name,
section .confirm-screen-account-number,
.confirm-screen-row-info,
.confirm-screen-row-detail {
text-align: left;
}
.confirm-screen-rows {
display: flex;
flex-flow: column nowrap;
width: 100%;
flex: 0 0 auto;
}
.confirm-screen-section-column {
flex: .5;
}
.confirm-screen-row {
display: flex;
flex-flow: row nowrap;
width: 100%;
align-items: center;
padding: 12px;
padding-left: 35px;
font-size: 16px;
line-height: 22px;
&:not(:last-of-type) {
border-bottom: 1px solid $alto;
}
}
@media screen and (max-width: 379px) {
.confirm-screen-row {
span.confirm-screen-section-column {
flex: 0.4;
}
div.confirm-screen-section-column {
flex: 0.6;
}
.currency-display__input {
font-size: 14px;
}
}
}
.confirm-screen-row-detail {
font-size: 12px;
line-height: 16px;
color: $dusty-gray;
}
.confirm-screen-total-box {
background-color: $wild-sand;
position: relative;
.confirm-screen-label {
line-height: 21px;
}
.confirm-screen-row-detail {
color: $scorpion;
}
&__subtitle {
font-size: 12px;
line-height: 16px;
}
.confirm-screen-row-info {
font-size: 16px;
font-weight: 500;
line-height: 21px;
}
}
.confirm-screen-error {
font-size: 12px;
line-height: 12px;
color: #f00;
position: absolute;
right: 12px;
width: 80px;
text-align: right;
}
.confirm-screen-row.confirm-screen-total-box {
.confirm-screen-section-column--with-error {
flex: 0.6;
}
}
@media screen and (max-width: 379px) {
.confirm-screen-row.confirm-screen-total-box {
.confirm-screen-section-column--with-error {
flex: 0.4;
}
}
}
.confirm-screen-form {
position: relative;
.confirm-screen-error {
right: 0;
width: 100%;
margin-top: 7px;
text-align: center;
}
}
.confirm-screen-confirm-button {
height: 50px;
border-radius: 4px;
background-color: #02c9b1;
font-size: 16px;
color: $white;
text-align: center;
font-family: Roboto;
padding-top: 15px;
padding-bottom: 15px;
border-width: 0;
box-shadow: none;
flex: 1 0 auto;
margin: 0 5px;
}
.btn-light.confirm-screen-cancel-button {
height: 50px;
background: none;
border: none;
opacity: 1;
font-family: Roboto;
border-width: 0;
padding-top: 15px;
padding-bottom: 15px;
font-size: 16px;
box-shadow: none;
cursor: pointer;
flex: 1 0 auto;
margin: 0 5px;
}

View File

@ -1,87 +0,0 @@
.currency-display {
height: 54px;
border: 1px solid $alto;
border-radius: 4px;
background-color: $white;
color: $scorpion;
font-family: Roboto;
font-size: 16px;
padding: 8px 10px;
position: relative;
&__primary-row {
display: flex;
}
&__input {
color: $scorpion;
font-family: Roboto;
font-size: 16px;
line-height: 22px;
border: none;
outline: 0 !important;
max-width: 22ch;
}
&__primary-currency {
color: $scorpion;
font-weight: 400;
font-family: Roboto;
font-size: 16px;
line-height: 22px;
}
&__converted-row {
display: flex;
}
&__converted-value,
&__converted-currency {
color: $dusty-gray;
font-family: Roboto;
font-size: 12px;
line-height: 12px;
}
&__input-wrapper {
position: relative;
display: flex;
flex: 1;
max-width: 100%;
input[type="number"] {
-moz-appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
display: none;
}
input[type="number"]:hover::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
display: none;
}
}
&__currency-symbol {
margin-top: 1px;
color: $scorpion;
}
.react-numeric-input {
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
display: none;
}
input[type="number"]:hover::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
display: none;
}
}
}

View File

@ -1,35 +0,0 @@
.editable-label {
display: flex;
align-items: center;
justify-content: center;
position: relative;
&__value {
max-width: 250px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&__input {
width: 250px;
font-size: 14px;
text-align: center;
border: 1px solid $alto;
&--error {
border: 1px solid $monzo;
}
}
&__icon-wrapper {
position: absolute;
margin-left: 10px;
left: 100%;
}
&__icon {
cursor: pointer;
color: $dusty-gray;
}
}

Some files were not shown because too many files have changed in this diff Show More