import { debounce } from 'lodash'; import { PureComponent } from 'react'; import * as React from 'react'; import { Field, Input } from '@grafana/ui'; interface Props { value?: number; placeholder?: string; autoFocus?: boolean; onChange: (number?: number) => void; min?: number; max?: number; step?: number; width?: number; fieldDisabled?: boolean; suffix?: React.ReactNode; } interface State { text: string; inputCorrected: boolean; } /** * This is an Input field that will call `onChange` for blur and enter * * @internal this is not exported to the `@grafana/ui` library, it is used * by options editor (number and slider), and direclty with in grafana core */ export class NumberInput extends PureComponent { state: State = { text: '', inputCorrected: false }; inputRef = React.createRef(); componentDidMount() { this.setState({ text: isNaN(this.props.value!) ? '' : `${this.props.value}`, }); } componentDidUpdate(oldProps: Props) { if (this.props.value !== oldProps.value) { const text = isNaN(this.props.value!) ? '' : `${this.props.value}`; if (text !== this.state.text) { this.setState({ text }); } } } updateValue = () => { const txt = this.inputRef.current?.value; let corrected = false; let newValue = ''; const min = this.props.min; const max = this.props.max; let currentValue = txt !== '' ? Number(txt) : undefined; if (currentValue && !Number.isNaN(currentValue)) { if (min != null && currentValue < min) { newValue = min.toString(); corrected = true; } else if (max != null && currentValue > max) { newValue = max.toString(); corrected = true; } else { newValue = txt ?? ''; } this.setState({ text: newValue, inputCorrected: corrected, }); } if (corrected) { this.updateValueDebounced(); } if (!Number.isNaN(currentValue) && currentValue !== this.props.value) { this.props.onChange(currentValue); } }; updateValueDebounced = debounce(this.updateValue, 500); // 1/2 second delay onChange = (e: React.FocusEvent) => { this.setState({ text: e.currentTarget.value, }); this.updateValueDebounced(); }; onKeyPress = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { this.updateValue(); } }; renderInput() { return ( ); } render() { const { inputCorrected } = this.state; if (inputCorrected) { let range = ''; let { min, max } = this.props; if (max == null) { if (min != null) { range = `< ${min}`; } } else if (min != null) { range = `${min} < > ${max}`; } else { range = `> ${max}`; } return ( {this.renderInput()} ); } return this.renderInput(); } }