Files
editor/src/components/inputs/NumberInput.jsx

141 lines
3.2 KiB
JavaScript

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 <input/> 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 = (
<input
className="maputnik-number-range"
key="range"
type="range"
max={this.props.max}
min={this.props.min}
step="any"
spellCheck="false"
value={this.state.value}
onChange={this.onChangeRange}
onBlur={this.resetValue}
/>
);
}
return <div className="maputnik-number-container">
{rangeEl}
<input
key="text"
spellCheck="false"
className="maputnik-number"
placeholder={this.props.default}
value={this.state.value}
onChange={e => this.changeValue(e.target.value)}
onBlur={this.resetValue}
/>
</div>
}
}
export default NumberInput