import { formatBoundInput } from '@h5web/shared'; import { forwardRef, useEffect, useImperativeHandle, useRef, useState, } from 'react'; import { FiCheck, FiSlash } from 'react-icons/fi'; import type { Bound } from '../../../vis/models'; import { clampBound } from '../../../vis/utils'; import styles from './BoundEditor.module.css'; interface Props { bound: Bound; value: number; isEditing: boolean; hasError: boolean; onEditToggle: (force: boolean) => void; onChange: (val: number) => void; } interface Handle { cancel: () => void; } const BoundEditor = forwardRef((props, ref) => { const { bound, value, isEditing, hasError, onEditToggle, onChange } = props; const id = `${bound}-bound`; const inputRef = useRef(null); const [inputValue, setInputValue] = useState(''); function cancel() { onEditToggle(false); setInputValue(formatBoundInput(value)); } /* Expose `cancel` function to parent component through ref handle so that `inputValue` can be reset when the user closes the domain tooltip. */ useImperativeHandle(ref, () => ({ cancel })); useEffect(() => { setInputValue(formatBoundInput(value)); }, [value, setInputValue]); useEffect(() => { if (!isEditing) { // Remove focus from min field when editing is turned off inputRef.current?.blur(); } if (isEditing && bound === 'min') { // Give focus to min field when opening tooltip in edit mode inputRef.current?.focus(); } }, [isEditing, bound]); return (
{ evt.preventDefault(); const parsedValue = Number.parseFloat(inputValue.replace('−', '-')); // U+2212 minus gives `NaN` const newValue = Number.isNaN(parsedValue) ? value : clampBound(parsedValue); // Clean up input in case value hasn't changed (since `useEffect` won't be triggered) setInputValue(formatBoundInput(newValue)); onChange(newValue); onEditToggle(false); }} > setInputValue(evt.target.value)} onFocus={() => { if (!isEditing) { onEditToggle(true); } }} />
); }); export type { Handle as BoundEditorHandle }; export default BoundEditor;