| 
									
										
										
										
											2022-06-15 04:42:26 +08:00
										 |  |  | import { debounce } from 'lodash'; | 
					
						
							|  |  |  | import React, { PureComponent } 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; | 
					
						
							| 
									
										
										
										
											2023-04-17 16:44:05 +08:00
										 |  |  |   width?: number; | 
					
						
							|  |  |  |   fieldDisabled?: boolean; | 
					
						
							| 
									
										
										
										
											2023-06-22 02:17:10 +08:00
										 |  |  |   suffix?: React.ReactNode; | 
					
						
							| 
									
										
										
										
											2022-06-15 04:42:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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<Props, State> { | 
					
						
							|  |  |  |   state: State = { text: '', inputCorrected: false }; | 
					
						
							|  |  |  |   inputRef = React.createRef<HTMLInputElement>(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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; | 
					
						
							| 
									
										
										
										
											2022-10-07 20:06:50 +08:00
										 |  |  |     let corrected = false; | 
					
						
							|  |  |  |     let newValue = ''; | 
					
						
							|  |  |  |     const min = this.props.min; | 
					
						
							|  |  |  |     const max = this.props.max; | 
					
						
							| 
									
										
										
										
											2022-10-18 21:59:34 +08:00
										 |  |  |     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 ?? ''; | 
					
						
							| 
									
										
										
										
											2022-10-07 20:06:50 +08:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this.setState({ | 
					
						
							| 
									
										
										
										
											2022-10-18 21:59:34 +08:00
										 |  |  |         text: newValue, | 
					
						
							| 
									
										
										
										
											2022-10-07 20:06:50 +08:00
										 |  |  |         inputCorrected: corrected, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2022-10-18 21:59:34 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-10-07 20:06:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 21:59:34 +08:00
										 |  |  |     if (corrected) { | 
					
						
							|  |  |  |       this.updateValueDebounced(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-10-07 20:06:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 21:59:34 +08:00
										 |  |  |     if (!Number.isNaN(currentValue) && currentValue !== this.props.value) { | 
					
						
							|  |  |  |       this.props.onChange(currentValue); | 
					
						
							| 
									
										
										
										
											2022-06-15 04:42:26 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   updateValueDebounced = debounce(this.updateValue, 500); // 1/2 second delay
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   onChange = (e: React.FocusEvent<HTMLInputElement>) => { | 
					
						
							|  |  |  |     this.setState({ | 
					
						
							| 
									
										
										
										
											2022-10-07 20:06:50 +08:00
										 |  |  |       text: e.currentTarget.value, | 
					
						
							| 
									
										
										
										
											2022-06-15 04:42:26 +08:00
										 |  |  |     }); | 
					
						
							|  |  |  |     this.updateValueDebounced(); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => { | 
					
						
							|  |  |  |     if (e.key === 'Enter') { | 
					
						
							|  |  |  |       this.updateValue(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   renderInput() { | 
					
						
							|  |  |  |     return ( | 
					
						
							|  |  |  |       <Input | 
					
						
							|  |  |  |         type="number" | 
					
						
							|  |  |  |         ref={this.inputRef} | 
					
						
							|  |  |  |         min={this.props.min} | 
					
						
							|  |  |  |         max={this.props.max} | 
					
						
							|  |  |  |         step={this.props.step} | 
					
						
							|  |  |  |         autoFocus={this.props.autoFocus} | 
					
						
							|  |  |  |         value={this.state.text} | 
					
						
							|  |  |  |         onChange={this.onChange} | 
					
						
							|  |  |  |         onBlur={this.updateValue} | 
					
						
							|  |  |  |         onKeyPress={this.onKeyPress} | 
					
						
							|  |  |  |         placeholder={this.props.placeholder} | 
					
						
							| 
									
										
										
										
											2023-04-17 16:44:05 +08:00
										 |  |  |         disabled={this.props.fieldDisabled} | 
					
						
							|  |  |  |         width={this.props.width} | 
					
						
							| 
									
										
										
										
											2023-06-22 02:17:10 +08:00
										 |  |  |         suffix={this.props.suffix} | 
					
						
							| 
									
										
										
										
											2022-06-15 04:42:26 +08:00
										 |  |  |       /> | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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 ( | 
					
						
							| 
									
										
										
										
											2022-07-08 02:57:03 +08:00
										 |  |  |         <Field | 
					
						
							|  |  |  |           invalid={inputCorrected} | 
					
						
							|  |  |  |           error={`Out of range ${range}`} | 
					
						
							|  |  |  |           validationMessageHorizontalOverflow={true} | 
					
						
							|  |  |  |           style={{ direction: 'rtl' }} | 
					
						
							|  |  |  |         > | 
					
						
							| 
									
										
										
										
											2022-06-15 04:42:26 +08:00
										 |  |  |           {this.renderInput()} | 
					
						
							|  |  |  |         </Field> | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return this.renderInput(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |