aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components/unit-input/unit-input.component.js
blob: 230eecfe66c66a48287eb5f46ffb4e10dc9d2554 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import { removeLeadingZeroes } from '../send/send.utils'

/**
 * Component that attaches a suffix or unit of measurement trailing user input, ex. 'ETH'. Also
 * allows rendering a child component underneath the input to, for example, display conversions of
 * the shown suffix.
 */
export default class UnitInput extends PureComponent {
  static propTypes = {
    children: PropTypes.node,
    actionComponent: PropTypes.node,
    error: PropTypes.bool,
    onBlur: PropTypes.func,
    onChange: PropTypes.func,
    placeholder: PropTypes.string,
    suffix: PropTypes.string,
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  }

  static defaultProps = {
    placeholder: '0',
  }

  constructor (props) {
    super(props)

    this.state = {
      value: props.value || '',
    }
  }

  componentDidUpdate (prevProps) {
    const { value: prevPropsValue } = prevProps
    const { value: propsValue } = this.props
    const { value: stateValue } = this.state

    if (prevPropsValue !== propsValue && propsValue !== stateValue) {
      this.setState({ value: propsValue })
    }
  }

  handleFocus = () => {
    this.unitInput.focus()
  }

  handleChange = event => {
    const { value: userInput } = event.target
    let value = userInput

    if (userInput.length && userInput.length > 1) {
      value = removeLeadingZeroes(userInput)
    }

    this.setState({ value })
    this.props.onChange(value)
  }

  handleBlur = event => {
    const { onBlur } = this.props
    typeof onBlur === 'function' && onBlur(this.state.value)
  }

  getInputWidth (value) {
    const valueString = String(value)
    const valueLength = valueString.length || 1
    const decimalPointDeficit = valueString.match(/\./) ? -0.5 : 0
    return (valueLength + decimalPointDeficit + 0.5) + 'ch'
  }

  render () {
    const { error, placeholder, suffix, actionComponent, children } = this.props
    const { value } = this.state

    return (
      <div
        className={classnames('unit-input', { 'unit-input--error': error })}
        onClick={this.handleFocus}
      >
        <div className="unit-input__inputs">
          <div className="unit-input__input-container">
            <input
              type="number"
              className="unit-input__input"
              value={value}
              placeholder={placeholder}
              onChange={this.handleChange}
              onBlur={this.handleBlur}
              style={{ width: this.getInputWidth(value) }}
              ref={ref => { this.unitInput = ref }}
            />
            {
              suffix && (
                <div className="unit-input__suffix">
                  { suffix }
                </div>
              )
            }
          </div>
          { children }
        </div>
        {actionComponent}
      </div>
    )
  }
}