diff --git a/src/components/inputs/NumberInput.jsx b/src/components/inputs/NumberInput.jsx index b2d7dec..2be980b 100644 --- a/src/components/inputs/NumberInput.jsx +++ b/src/components/inputs/NumberInput.jsx @@ -1,6 +1,8 @@ import React from 'react' import PropTypes from 'prop-types' +let IDX = 0; + class NumberInput extends React.Component { static propTypes = { value: PropTypes.number, @@ -8,37 +10,52 @@ class NumberInput extends React.Component { min: PropTypes.number, max: PropTypes.number, onChange: PropTypes.func, + allowRange: PropTypes.bool, + rangeStep: PropTypes.number, + wdKey: PropTypes.string, required: PropTypes.bool, } + static defaultProps = { + rangeStep: 1 + } + constructor(props) { super(props) this.state = { + uuid: IDX++, editing: false, value: props.value, + dirtyValue: props.value, } } static getDerivedStateFromProps(props, state) { if (!state.editing) { return { - value: props.value + value: props.value, + dirtyValue: props.value, }; } - return {}; + return null; } changeValue(newValue) { - this.setState({editing: true}); const value = (newValue === "" || newValue === undefined) ? undefined : parseFloat(newValue); - const hasChanged = this.state.value !== value + const hasChanged = this.props.value !== value; if(this.isValid(value) && hasChanged) { this.props.onChange(value) + this.setState({ + dirtyValue: newValue, + }); } - this.setState({ value: newValue }) + + this.setState({ + value: newValue, + }) } isValid(v) { @@ -79,16 +96,112 @@ class NumberInput extends React.Component { } } + onChangeRange = (e) => { + let value = parseFloat(e.target.value, 10); + const step = this.props.rangeStep; + let dirtyValue = value; + + 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 = value % step; + + // Round up/down to step + if (this._keyboardEvent) { + // If it's keyboard event we might get a low positive/negative value, + // for example we might go from 13 to 13.23, however because we know + // that came from a keyboard event we always want to increase by a + // single step value. + if (value < this.state.dirtyValue) { + value = this.state.value - step; + } + else { + value = this.state.value + step + } + dirtyValue = value; + } + else { + if (snap < step/2) { + value = value - snap; + } + else { + value = value + (step - snap); + }; + } + } + + this._keyboardEvent = false; + + // Clamp between min/max + value = Math.max(this.props.min, Math.min(this.props.max, value)); + + this.setState({value, dirtyValue}); + this.props.onChange(value); + } + render() { - return this.changeValue(e.target.value)} - onBlur={this.resetValue} - required={this.props.required} - /> + if( + this.props.hasOwnProperty("min") && this.props.hasOwnProperty("max") && + this.props.min !== undefined && this.props.max !== undefined && + this.props.allowRange + ) { + const dirtyValue = this.state.dirtyValue === undefined ? this.props.default : this.state.dirtyValue + const value = this.state.value === undefined ? "" : this.state.value; + + return