import PropTypes from 'prop-types'; import React, { Component } from 'react'; import TextInput from './TextInput'; function parseValue(props, value) { const { isFloat, min, max } = props; if (value == null || value === '') { return null; } let newValue = isFloat ? parseFloat(value) : parseInt(value); if (min != null && newValue != null && newValue < min) { newValue = min; } else if (max != null && newValue != null && newValue > max) { newValue = max; } return newValue; } class NumberInput extends Component { // // Lifecycle constructor(props, context) { super(props, context); this.state = { value: props.value == null ? '' : props.value.toString(), isFocused: false }; } componentDidUpdate(prevProps, prevState) { const { value } = this.props; if (value !== prevProps.value && !this.state.isFocused) { this.setState({ value: value == null ? '' : value.toString() }); } } // // Listeners onChange = ({ name, value }) => { this.setState({ value }); this.props.onChange({ name, value: parseValue(this.props, value) }); }; onFocus = () => { this.setState({ isFocused: true }); }; onBlur = () => { const { name, onChange } = this.props; const { value } = this.state; const parsedValue = parseValue(this.props, value); const stringValue = parsedValue == null ? '' : parsedValue.toString(); if (stringValue === value) { this.setState({ isFocused: false }); } else { this.setState({ value: stringValue, isFocused: false }); } onChange({ name, value: parsedValue }); }; // // Render render() { const value = this.state.value; return ( ); } } NumberInput.propTypes = { name: PropTypes.string.isRequired, value: PropTypes.number, min: PropTypes.number, max: PropTypes.number, isFloat: PropTypes.bool.isRequired, onChange: PropTypes.func.isRequired }; NumberInput.defaultProps = { value: null, isFloat: false }; export default NumberInput;