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 < Handle , Props > ( ( props , ref ) => { const { bound , value , isEditing , hasError , onEditToggle , onChange } = props ; const id = `${ bound }-bound` ; const inputRef = useRef < HTMLInputElement > ( null ) ; const [ inputValue , setInputValue ] = useState ( '' ) ; function cancel ( ) { onEditToggle ( false ) ; setInputValue ( formatBoundInput ( value ) ) ; } useImperativeHandle ( ref , ( ) => ( { cancel } ) ) ; useEffect ( ( ) => { setInputValue ( formatBoundInput ( value ) ) ; } , [ value , setInputValue ] ) ; useEffect ( ( ) => { if ( ! isEditing ) { inputRef . current ?. blur ( ) ; } if ( isEditing && bound === 'min' ) { inputRef . current ?. focus ( ) ; } } , [ isEditing , bound ] ) ; return ( < form className = { styles . boundEditor } data - error = { hasError || undefined } data - editing = { isEditing } onSubmit = { ( evt ) => { evt . preventDefault ( ) ; const parsedValue = Number . parseFloat ( inputValue . replace ( '−' , '-' ) ) ; const newValue = Number . isNaN ( parsedValue ) ? value : clampBound ( parsedValue ) ; setInputValue ( formatBoundInput ( newValue ) ) ; onChange ( newValue ) ; onEditToggle ( false ) ; } } > < label id = { `${ id }-label` } className = { styles . label } htmlFor = { id } > { bound } < /label> < input id = { id } ref = { inputRef } className = { styles . value } type = "text" name = "bound" value = { inputValue } title = { isEditing ? undefined : value . toString ( ) } aria - labelledby = { `${ id }-label` } onChange = { ( evt ) => setInputValue ( evt . target . value ) } onFocus = { ( ) => { if ( ! isEditing ) { onEditToggle ( true ) ; } } } /> < button className = { styles . actionBtn } type = "submit" disabled = { ! isEditing } aria - label = { `Apply ${ bound }` } > < FiCheck /> < /button> < button className = { styles . actionBtn } type = "button" disabled = { ! isEditing } aria - label = { `Cancel ${ bound }` } onClick = { ( ) => cancel ( ) } > < FiSlash /> < /button> < /form>); } ) ; export type { Handle as BoundEditorHandle } ; export default BoundEditor ;