You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
4.7 KiB
206 lines
4.7 KiB
// @flow
|
|
|
|
import React, { PureComponent } from 'react'
|
|
import uncontrollable from 'uncontrollable'
|
|
import styled from 'styled-components'
|
|
import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/helpers/currencies'
|
|
|
|
import noop from 'lodash/noop'
|
|
import isNaN from 'lodash/isNaN'
|
|
|
|
import Box from 'components/base/Box'
|
|
import Input from 'components/base/Input'
|
|
import Select from 'components/base/Select'
|
|
|
|
import type { Unit } from '@ledgerhq/live-common/lib/types'
|
|
|
|
function parseValue(value) {
|
|
return value.toString().replace(/,/g, '.')
|
|
}
|
|
|
|
function format(unit: Unit, value: number, { isFocused, showAllDigits }) {
|
|
// FIXME do we need locale for the input too ?
|
|
return formatCurrencyUnit(unit, value, {
|
|
useGrouping: !isFocused,
|
|
disableRounding: true,
|
|
showAllDigits: !!showAllDigits && !isFocused,
|
|
})
|
|
}
|
|
|
|
const Currencies = styled(Box)`
|
|
position: relative;
|
|
top: -1px;
|
|
right: -1px;
|
|
`
|
|
|
|
const Currency = styled(Box).attrs({
|
|
color: 'grey',
|
|
fontSize: 2,
|
|
pl: 2,
|
|
pr: 1,
|
|
})``
|
|
|
|
function stopPropagation(e) {
|
|
e.stopPropagation()
|
|
}
|
|
|
|
type Props = {
|
|
onChangeFocus: boolean => void,
|
|
onChange: (number, Unit) => void, // FIXME Unit shouldn't be provided (this is not "standard" onChange)
|
|
onChangeUnit: Unit => void,
|
|
renderRight: any,
|
|
unit: Unit,
|
|
units: Unit[],
|
|
value: number,
|
|
showAllDigits?: boolean,
|
|
}
|
|
|
|
type State = {
|
|
isFocused: boolean,
|
|
displayValue: string,
|
|
}
|
|
|
|
class InputCurrency extends PureComponent<Props, State> {
|
|
static defaultProps = {
|
|
onChangeFocus: noop,
|
|
onChange: noop,
|
|
renderRight: null,
|
|
units: [],
|
|
value: 0,
|
|
showAllDigits: false,
|
|
}
|
|
|
|
state = {
|
|
isFocused: false,
|
|
displayValue: '',
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.syncInput({ isFocused: false })
|
|
}
|
|
|
|
componentWillReceiveProps(nextProps: Props) {
|
|
const { value, showAllDigits, unit } = this.props
|
|
const needsToBeReformatted =
|
|
value !== nextProps.value ||
|
|
showAllDigits !== nextProps.showAllDigits ||
|
|
unit !== nextProps.unit
|
|
if (needsToBeReformatted) {
|
|
const { isFocused } = this.state
|
|
this.setState({
|
|
displayValue:
|
|
nextProps.value === 0
|
|
? ''
|
|
: format(nextProps.unit, nextProps.value, {
|
|
isFocused,
|
|
showAllDigits: nextProps.showAllDigits,
|
|
}),
|
|
})
|
|
}
|
|
}
|
|
|
|
handleChange = (v: string) => {
|
|
v = parseValue(v)
|
|
|
|
// allow to type directly `.` in input to have `0.`
|
|
if (v.startsWith('.')) {
|
|
v = `0${v}`
|
|
}
|
|
|
|
// forbid multiple 0 at start
|
|
if (v === '' || v.startsWith('00')) {
|
|
const { onChange, unit } = this.props
|
|
onChange(0, unit)
|
|
this.setState({ displayValue: '' })
|
|
return
|
|
}
|
|
|
|
// Check if value is valid Number
|
|
if (isNaN(Number(v))) {
|
|
return
|
|
}
|
|
|
|
this.emitOnChange(v)
|
|
this.setState({ displayValue: v || '' })
|
|
}
|
|
|
|
handleBlur = () => {
|
|
this.syncInput({ isFocused: false })
|
|
this.props.onChangeFocus(false)
|
|
}
|
|
|
|
handleFocus = () => {
|
|
this.syncInput({ isFocused: true })
|
|
this.props.onChangeFocus(true)
|
|
}
|
|
|
|
syncInput = ({ isFocused }: { isFocused: boolean }) => {
|
|
const { value, showAllDigits, unit } = this.props
|
|
this.setState({
|
|
isFocused,
|
|
displayValue:
|
|
value === '' || value === 0 ? '' : format(unit, value, { isFocused, showAllDigits }),
|
|
})
|
|
}
|
|
|
|
emitOnChange = (v: string) => {
|
|
const { onChange, unit } = this.props
|
|
const { displayValue } = this.state
|
|
|
|
if (displayValue.toString() !== v.toString()) {
|
|
const satoshiValue = Number(v) * 10 ** unit.magnitude
|
|
onChange(satoshiValue, unit)
|
|
}
|
|
}
|
|
|
|
renderItem = item => item.code
|
|
|
|
renderSelected = item => <Currency>{item.code}</Currency>
|
|
|
|
renderListUnits = () => {
|
|
const { units, onChangeUnit, unit } = this.props
|
|
const { isFocused } = this.state
|
|
|
|
if (units.length <= 1) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<Currencies onClick={stopPropagation}>
|
|
<Select
|
|
bg="lightGraphite"
|
|
keyProp="code"
|
|
flatLeft
|
|
onChange={onChangeUnit}
|
|
items={units}
|
|
value={unit}
|
|
renderItem={this.renderItem}
|
|
renderSelected={this.renderSelected}
|
|
fakeFocusRight={isFocused}
|
|
/>
|
|
</Currencies>
|
|
)
|
|
}
|
|
|
|
render() {
|
|
const { renderRight, showAllDigits, unit } = this.props
|
|
const { displayValue } = this.state
|
|
|
|
return (
|
|
<Input
|
|
{...this.props}
|
|
ff="Rubik"
|
|
value={displayValue}
|
|
onChange={this.handleChange}
|
|
onFocus={this.handleFocus}
|
|
onBlur={this.handleBlur}
|
|
renderRight={renderRight || this.renderListUnits()}
|
|
placeholder={format(unit, 0, { isFocused: false, showAllDigits })}
|
|
/>
|
|
)
|
|
}
|
|
}
|
|
|
|
export default uncontrollable(InputCurrency, {
|
|
unit: 'onChangeUnit',
|
|
})
|
|
|