import { millisToString, parseUserTime } from 'ontime-utils'; import { FocusEvent, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { cx } from '../../../utils/styleUtils'; import { formatTime, getFormatFromSettings } from '../../../utils/time'; import Input from '../input/Input'; import style from './TimeInput.module.scss'; interface TimeInputProps { id?: T; name: T; submitHandler: (field: T, value: string) => void; time?: number; placeholder?: string; disabled?: boolean; align?: 'left' | 'center'; delayed?: boolean; className?: string; shouldFormat?: boolean; } export default function TimeInput({ id, name, submitHandler, time, placeholder, disabled, align = 'center', delayed, className, shouldFormat, }: TimeInputProps) { const inputRef = useRef(null); const [value, setValue] = useState(''); const ignoreChange = useRef(false); const formatAs12Hr = useMemo(() => shouldFormat && getFormatFromSettings() === '12', [shouldFormat]); /** * @description Resets input value to given */ const resetValue = useCallback(() => { if (typeof time !== 'number' || isNaN(time)) { setValue('00:00:00'); } else if (shouldFormat) { setValue(formatTime(time)); } else { setValue(millisToString(time)); } }, [time, shouldFormat]); /** * @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); return true; } 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(() => { resetValue(); }, [resetValue]); return ( setValue(event.target.value)} onBlur={onBlurHandler} onKeyDown={onKeyDownHandler} value={value} maxLength={formatAs12Hr ? 11 : 8} style={{ textAlign: align, width: formatAs12Hr ? '7.5em' : '6.5em', }} /> ); }