import {ChangeEvent, memo, ReactNode, useEffect, useState} from 'react'; import { InputAdornment, SxProps, TextField, Theme } from "@mui/material"; type MyProps = { start_val?: number, default?: number, label: string, callback: (val: number, err: boolean) => void, counter?: number, min?: number, max?: number, sx?: SxProps, units?: string, disabled?: boolean, fullWidth?: boolean, float?: boolean } function unitsNode(str: string | undefined): ReactNode | null { if (str === undefined) return null; else if (str === "us") return µs; else if (str === "A-1") return -1 ; else return {str}; } function NumberTextField({start_val, default: defaultValue, label, callback, counter, min, max, sx, units, disabled, float}: MyProps) { const [text, setText] = useState("."); const [err, setErr] = useState(true); const [val, setVal] = useState(0); // Something has to change in parent to trigger setup => otherwise this makes infinite loop. // In case of error state start_val could stay same, but we want to return to "safe" set // so there is external counter that if changed reset is triggered. useEffect(() => { let v : number = 0; if (start_val !== undefined) v = start_val; else if (defaultValue !== undefined) v = defaultValue; if (float !== true) v = Math.round(v); else v = Math.round(v * 1000.0) / 1000.0 setErr(false); setVal(v); setText(v.toString()); // eslint-disable-next-line react-hooks/exhaustive-deps }, [counter, start_val]); const updateValue = (event: ChangeEvent) => { let new_text = event.target.value; if (!new_text) new_text = "0"; let num_val : number = Number(new_text); let new_err : boolean = !Number.isFinite(num_val) || (!float && !Number.isInteger(num_val)) || ((min !== undefined) && (num_val < min)) || ((max !== undefined) && (num_val > max)); // If error I keep old numeric value to return (so there is always a proper value kept) let new_val : number = new_err ? val : num_val; setErr(new_err); setVal(new_val); setText(new_text); callback(new_val, new_err); }; return } export default memo(NumberTextField);