const Component = require('react').Component const PropTypes = require('prop-types') const h = require('react-hyperscript') const inherits = require('util').inherits const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN const extend = require('xtend') const connect = require('react-redux').connect BnAsDecimalInput.contextTypes = { t: PropTypes.func, } module.exports = connect()(BnAsDecimalInput) inherits(BnAsDecimalInput, Component) function BnAsDecimalInput () { this.state = { invalid: null } Component.call(this) } /* Bn as Decimal Input * * A component for allowing easy, decimal editing * of a passed in bn string value. * * On change, calls back its `onChange` function parameter * and passes it an updated bn string. */ BnAsDecimalInput.prototype.render = function () { const props = this.props const state = this.state const { value, scale, precision, onChange, min, max } = props const suffix = props.suffix const style = props.style const valueString = value.toString(10) const newMin = min && this.downsize(min.toString(10), scale) const newMax = max && this.downsize(max.toString(10), scale) const newValue = this.downsize(valueString, scale) return ( h('.flex-column', [ h('.flex-row', { style: { alignItems: 'flex-end', lineHeight: '13px', fontFamily: 'Montserrat Light', textRendering: 'geometricPrecision', }, }, [ h('input.hex-input', { type: 'number', step: 'any', required: true, min: newMin, max: newMax, style: extend({ display: 'block', textAlign: 'right', backgroundColor: 'transparent', border: '1px solid #bdbdbd', }, style), value: newValue, onBlur: (event) => { this.updateValidity(event) }, onChange: (event) => { this.updateValidity(event) const value = (event.target.value === '') ? '' : event.target.value const scaledNumber = this.upsize(value, scale, precision) const precisionBN = new BN(scaledNumber, 10) onChange(precisionBN, event.target.checkValidity()) }, onInvalid: (event) => { const msg = this.constructWarning() if (msg === state.invalid) { return } this.setState({ invalid: msg }) event.preventDefault() return false }, }), h('div', { style: { color: ' #AEAEAE', fontSize: '12px', marginLeft: '5px', marginRight: '6px', width: '20px', }, }, suffix), ]), state.invalid ? h('span.error', { style: { position: 'absolute', right: '0px', textAlign: 'right', transform: 'translateY(26px)', padding: '3px', background: 'rgba(255,255,255,0.85)', zIndex: '1', textTransform: 'capitalize', border: '2px solid #E20202', }, }, state.invalid) : null, ]) ) } BnAsDecimalInput.prototype.setValid = function (message) { this.setState({ invalid: null }) } BnAsDecimalInput.prototype.updateValidity = function (event) { const target = event.target const value = this.props.value const newValue = target.value if (value === newValue) { return } const valid = target.checkValidity() if (valid) { this.setState({ invalid: null }) } } BnAsDecimalInput.prototype.constructWarning = function () { const { name, min, max, scale, suffix } = this.props const newMin = min && this.downsize(min.toString(10), scale) const newMax = max && this.downsize(max.toString(10), scale) let message = name ? name + ' ' : '' if (min && max) { message += this.context.t('betweenMinAndMax', [`${newMin} ${suffix}`, `${newMax} ${suffix}`]) } else if (min) { message += this.context.t('greaterThanMin', [`${newMin} ${suffix}`]) } else if (max) { message += this.context.t('lessThanMax', [`${newMax} ${suffix}`]) } else { message += this.context.t('invalidInput') } return message } BnAsDecimalInput.prototype.downsize = function (number, scale) { // if there is no scaling, simply return the number if (scale === 0) { return Number(number) } else { // if the scale is the same as the precision, account for this edge case. var adjustedNumber = number while (adjustedNumber.length < scale) { adjustedNumber = '0' + adjustedNumber } return Number(adjustedNumber.slice(0, -scale) + '.' + adjustedNumber.slice(-scale)) } } BnAsDecimalInput.prototype.upsize = function (number, scale, precision) { var stringArray = number.toString().split('.') var decimalLength = stringArray[1] ? stringArray[1].length : 0 var newString = stringArray[0] // If there is scaling and decimal parts exist, integrate them in. if ((scale !== 0) && (decimalLength !== 0)) { newString += stringArray[1].slice(0, precision) } // Add 0s to account for the upscaling. for (var i = decimalLength; i < scale; i++) { newString += '0' } return newString }