import { FocusEvent, KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react'; import { Input } from '@chakra-ui/react'; import { millisToString, parseUserTime } from 'ontime-utils'; import { useEmitLog } from '../../../stores/logger'; interface TimeInputProps { name: T; submitHandler: (field: T, value: string) => void; time?: number; placeholder: string; disabled?: boolean; align?: 'left' | 'center'; className?: string; } export default function TimeInput(props: TimeInputProps) { const { name, submitHandler, time = 0, placeholder, disabled, align = 'center', className } = props; const { emitError } = useEmitLog(); const inputRef = useRef(null); const [value, setValue] = useState(''); const ignoreChange = useRef(false); /** * @description Resets input value to given */ const resetValue = useCallback(() => { try { setValue(millisToString(time)); } catch (error) { setValue(millisToString(0)); emitError(`Unable to parse time ${time}: ${error}`); } }, [emitError, time]); /** * @description Selects input text on focus */ const handleFocus = useCallback(() => { inputRef.current?.select(); }, []); /** * @description Submit handler * @param {string} newValue */ const handleSubmit = useCallback( (newValue: string) => { // Check if there is anything there if (newValue === '') { return false; } // we dont know the values in the rundown, escalate to handler if (newValue.startsWith('p') || newValue.startsWith('+')) { submitHandler(name, newValue); } const valueInMillis = parseUserTime(newValue); if (valueInMillis === time) { return false; } submitHandler(name, newValue); return true; }, [name, submitHandler, time], ); /** * @description Prepare time fields * @param {string} value string to be parsed */ const validateAndSubmit = useCallback( (newValue: string) => { const success = handleSubmit(newValue); if (!success) { resetValue(); } }, [handleSubmit, resetValue], ); /** * @description Handles common keys for submit and cancel * @param {KeyboardEvent} event */ const onKeyDownHandler = useCallback( (event: KeyboardEvent) => { if (event.key === 'Enter') { inputRef.current?.blur(); } if (event.key === 'Escape') { ignoreChange.current = true; inputRef.current?.blur(); resetValue(); } }, [resetValue], ); const onBlurHandler = useCallback( (event: FocusEvent) => { if (ignoreChange.current) { ignoreChange.current = false; return; } validateAndSubmit((event.target as HTMLInputElement).value); }, [validateAndSubmit], ); useEffect(() => { if (time == null) return; resetValue(); }, [resetValue, time]); return ( setValue(event.target.value)} onBlur={onBlurHandler} onKeyDown={onKeyDownHandler} value={value} maxLength={8} maxWidth='7.5em' letterSpacing='1px' autoComplete='off' textAlign={align} /> ); }