JimmyMow
6 years ago
committed by
GitHub
9 changed files with 466 additions and 2 deletions
@ -0,0 +1,149 @@ |
|||
/* eslint-disable react/no-multi-comp */ |
|||
|
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import { asField } from 'informed' |
|||
import * as yup from 'yup' |
|||
import { convert } from 'lib/utils/btc' |
|||
import { formatValue, parseNumber } from 'lib/utils/crypto' |
|||
import Input from 'components/UI/Input' |
|||
|
|||
/** |
|||
* @render react |
|||
* @name CryptoAmountInput |
|||
*/ |
|||
class CryptoAmountInput extends React.Component { |
|||
static propTypes = { |
|||
currency: PropTypes.string.isRequired, |
|||
required: PropTypes.bool, |
|||
onChange: PropTypes.func, |
|||
onBlur: PropTypes.func |
|||
} |
|||
|
|||
/** |
|||
* Reformat the value when the currency unit has changed. |
|||
*/ |
|||
componentDidUpdate(prevProps) { |
|||
const { currency, fieldApi } = this.props |
|||
|
|||
// Reformat the value when the currency unit has changed.
|
|||
if (currency !== prevProps.currency) { |
|||
const { fieldApi } = this.props |
|||
let value = fieldApi.getValue() |
|||
const convertedValue = convert(prevProps.currency, currency, value) |
|||
const [integer, fractional] = parseNumber(convertedValue, this.getRules().precision) |
|||
value = formatValue(integer, fractional) |
|||
fieldApi.setValue(value) |
|||
} |
|||
|
|||
// If the value has changed, reformat it if needed.
|
|||
const valueBefore = prevProps.fieldState.value |
|||
const valueAfter = fieldApi.getValue() |
|||
if (valueAfter !== valueBefore) { |
|||
const [integer, fractional] = parseNumber(valueAfter, this.getRules().precision) |
|||
const formattedValue = formatValue(integer, fractional) |
|||
if (formattedValue !== valueAfter) { |
|||
fieldApi.setValue(formattedValue) |
|||
} |
|||
} |
|||
} |
|||
|
|||
getRules = () => { |
|||
const { currency } = this.props |
|||
switch (currency) { |
|||
case 'btc': |
|||
return { |
|||
precision: 8, |
|||
placeholder: '0.00000000', |
|||
pattern: '[0-9]*.?[0-9]{0,8}?' |
|||
} |
|||
case 'bits': |
|||
return { |
|||
precision: 2, |
|||
placeholder: '0.00', |
|||
pattern: '[0-9]*.?[0-9]{0,2}?' |
|||
} |
|||
case 'sats': |
|||
return { |
|||
precision: 0, |
|||
placeholder: '00000000', |
|||
pattern: '[0-9]*' |
|||
} |
|||
default: |
|||
return { |
|||
precision: 2, |
|||
pattern: '[0-9]*' |
|||
} |
|||
} |
|||
} |
|||
|
|||
handleKeyDown = e => { |
|||
// Do nothing if the user did select all key combo.
|
|||
if (e.metaKey && e.key === 'a') { |
|||
return |
|||
} |
|||
|
|||
// Do not allow multiple dots.
|
|||
let { value } = e.target |
|||
if (e.key === '.') { |
|||
if (value.search(/\./) >= 0) { |
|||
e.preventDefault() |
|||
} |
|||
return |
|||
} |
|||
|
|||
if (e.key.length === 1 && !e.key.match(/^[0-9.]$/)) { |
|||
e.preventDefault() |
|||
return |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
const rules = this.getRules() |
|||
return ( |
|||
<Input |
|||
{...this.props} |
|||
type="text" |
|||
placeholder={rules.placeholder} |
|||
pattern={rules.pattern} |
|||
onKeyDown={this.handleKeyDown} |
|||
/> |
|||
) |
|||
} |
|||
} |
|||
|
|||
const CryptoAmountInputAsField = asField(CryptoAmountInput) |
|||
|
|||
class WrappedCryptoAmountInputAsField extends React.Component { |
|||
validate = value => { |
|||
const { disabled, required } = this.props |
|||
if (disabled) { |
|||
return |
|||
} |
|||
try { |
|||
const validator = yup |
|||
.number() |
|||
.positive() |
|||
.min(0) |
|||
.typeError('A number is required') |
|||
if (required) { |
|||
validator.required() |
|||
} |
|||
validator.validateSync(Number(value)) |
|||
} catch (error) { |
|||
return error.message |
|||
} |
|||
|
|||
// Run any additional validation provided by the caller.
|
|||
const { validate } = this.props |
|||
if (validate) { |
|||
return validate(value) |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
return <CryptoAmountInputAsField validate={this.validate} {...this.props} /> |
|||
} |
|||
} |
|||
|
|||
export default WrappedCryptoAmountInputAsField |
@ -0,0 +1,131 @@ |
|||
/* eslint-disable react/no-multi-comp */ |
|||
|
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import { asField } from 'informed' |
|||
import * as yup from 'yup' |
|||
import { convert } from 'lib/utils/btc' |
|||
import { formatValue, parseNumber } from 'lib/utils/crypto' |
|||
import Input from 'components/UI/Input' |
|||
|
|||
/** |
|||
* @render react |
|||
* @name FiatAmountInput |
|||
*/ |
|||
class FiatAmountInput extends React.Component { |
|||
static propTypes = { |
|||
currency: PropTypes.string.isRequired, |
|||
currentTicker: PropTypes.object.isRequired, |
|||
required: PropTypes.bool, |
|||
onChange: PropTypes.func, |
|||
onBlur: PropTypes.func |
|||
} |
|||
|
|||
componentDidUpdate(prevProps) { |
|||
const { currency, currentTicker, fieldApi } = this.props |
|||
|
|||
// Reformat the value when the currency unit has changed.
|
|||
if (currency !== prevProps.currency) { |
|||
const { fieldApi } = this.props |
|||
let value = fieldApi.getValue() |
|||
const lastPriceInOrigCurrency = currentTicker[prevProps.currency].last |
|||
const lastPriceInNewCurrency = currentTicker[currency].last |
|||
// Convert to BTC.
|
|||
const btcValue = convert('fiat', 'btc', value, lastPriceInOrigCurrency) |
|||
// Convert to new currency.
|
|||
const newFiatValue = convert('btc', 'fiat', btcValue, lastPriceInNewCurrency) |
|||
const [integer, fractional] = parseNumber(newFiatValue, this.getRules().precision) |
|||
value = formatValue(integer, fractional) |
|||
fieldApi.setValue(value) |
|||
} |
|||
|
|||
// If the value has changed, reformat it if needed.
|
|||
const valueBefore = prevProps.fieldState.value |
|||
const valueAfter = fieldApi.getValue() |
|||
if (valueAfter !== valueBefore) { |
|||
const [integer, fractional] = parseNumber(valueAfter, this.getRules().precision) |
|||
const formattedValue = formatValue(integer, fractional) |
|||
if (formattedValue !== valueAfter) { |
|||
fieldApi.setValue(formattedValue) |
|||
} |
|||
} |
|||
} |
|||
|
|||
getRules() { |
|||
return { |
|||
precision: 2, |
|||
placeholder: '0.00', |
|||
pattern: '[0-9]*.?[0-9]{0,2}?' |
|||
} |
|||
} |
|||
|
|||
handleKeyDown = e => { |
|||
// Do nothing if the user did select all key combo.
|
|||
if (e.metaKey && e.key === 'a') { |
|||
return |
|||
} |
|||
|
|||
// Do not allow multiple dots.
|
|||
let { value } = e.target |
|||
if (e.key === '.') { |
|||
if (value.search(/\./) >= 0) { |
|||
e.preventDefault() |
|||
} |
|||
return |
|||
} |
|||
|
|||
if (e.key.length === 1 && !e.key.match(/^[0-9.]$/)) { |
|||
e.preventDefault() |
|||
return |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
const rules = this.getRules() |
|||
return ( |
|||
<Input |
|||
{...this.props} |
|||
type="text" |
|||
placeholder={rules.placeholder} |
|||
pattern={rules.pattern} |
|||
onKeyDown={this.handleKeyDown} |
|||
/> |
|||
) |
|||
} |
|||
} |
|||
|
|||
const FiatAmountInputAsField = asField(FiatAmountInput) |
|||
|
|||
class WrappedFiatAmountInputAsField extends React.Component { |
|||
validate = value => { |
|||
const { disabled, required } = this.props |
|||
if (disabled) { |
|||
return |
|||
} |
|||
try { |
|||
const validator = yup |
|||
.number() |
|||
.positive() |
|||
.min(0) |
|||
.typeError('A number is required') |
|||
if (required) { |
|||
validator.required() |
|||
} |
|||
validator.validateSync(Number(value)) |
|||
} catch (error) { |
|||
return error.message |
|||
} |
|||
|
|||
// Run any additional validation provided by the caller.
|
|||
const { validate } = this.props |
|||
if (validate) { |
|||
return validate(value) |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
return <FiatAmountInputAsField validate={this.validate} {...this.props} /> |
|||
} |
|||
} |
|||
|
|||
export default WrappedFiatAmountInputAsField |
Loading…
Reference in new issue