import React from 'react' import PropTypes from 'prop-types' class NumberInput extends React.Component { static propTypes = { value: PropTypes.number, default: PropTypes.number, min: PropTypes.number, max: PropTypes.number, onChange: PropTypes.func, allowRange: PropTypes.bool, rangeStep: PropTypes.number, } static defaultProps = { rangeStep: 0.01 } constructor(props) { super(props) this.state = { editing: false, value: props.value, } } static getDerivedStateFromProps(props, state) { if (!state.editing) { return { value: props.value }; } } changeValue(newValue) { this.setState({editing: true}); const value = parseFloat(newValue) const hasChanged = this.state.value !== value if(this.isValid(value) && hasChanged) { this.props.onChange(value) } this.setState({ value: newValue }) } isValid(v) { const value = parseFloat(v) if(isNaN(value)) { return false } if(!isNaN(this.props.min) && value < this.props.min) { return false } if(!isNaN(this.props.max) && value > this.props.max) { return false } return true } resetValue = () => { this.setState({editing: false}); // Reset explicitly to default value if value has been cleared if(this.state.value === "") { return this.changeValue(this.props.default) } // If set value is invalid fall back to the last valid value from props or at last resort the default value if(!this.isValid(this.state.value)) { if(this.isValid(this.props.value)) { this.changeValue(this.props.value) } else { this.changeValue(this.props.default) } } } onChangeRange = (e) => { const val = parseFloat(e.target.value, 10); const step = this.props.rangeStep; let out = val; if(step) { // Can't do this with the range step attribute else we won't be able to set a high precision value via the text input. const snap = val % step; // Round up/down to step if (snap < step/2) { out = val - snap; } else { out = val + (step - snap); }; } this.changeValue(out); } render() { let rangeEl; if( this.props.hasOwnProperty("min") && this.props.hasOwnProperty("max") && this.props.min !== undefined && this.props.max !== undefined && this.props.allowRange ) { rangeEl = ( ); } return