import { useEffect, useState, type CSSProperties } from "react"; /** * Ввод времени суток в строгом 24-часовом формате `ЧЧ:ММ[:СС]` (UTC). * * Нативные ``/`type="datetime-local"` рендерят время по локали * браузера/ОС (часто AM/PM) и не подчиняются атрибуту `lang`, поэтому время вводим * обычным текстовым полем с собственной валидацией. Локальный черновик позволяет * печатать промежуточные значения; наружу отдаём только корректное `ЧЧ:ММ[:СС]`. */ type Time24FieldProps = { /** Текущее значение в формате `ЧЧ:ММ` или `ЧЧ:ММ:СС`. */ value: string; /** Показывать и принимать секунды. */ withSeconds?: boolean; /** Вызывается при вводе корректного значения. */ onChange: (value: string) => void; className?: string; style?: CSSProperties; "aria-label"?: string; }; const TIME_RE_HM = /^([01]\d|2[0-3]):[0-5]\d$/; const TIME_RE_HMS = /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$/; export function Time24Field({ value, withSeconds = false, onChange, className, style, ...rest }: Time24FieldProps) { const re = withSeconds ? TIME_RE_HMS : TIME_RE_HM; const [draft, setDraft] = useState(value); const [editing, setEditing] = useState(false); // Пока поле не редактируется — синхронизируем отображение с внешним значением. useEffect(() => { if (!editing) setDraft(value); }, [value, editing]); return ( setEditing(true)} onChange={(event) => { const next = event.target.value; setDraft(next); if (re.test(next)) onChange(next); }} onBlur={() => { setEditing(false); if (re.test(draft)) onChange(draft); else setDraft(value); // невалидный ввод — откат к последнему корректному }} /> ); } /** * Замена нативного ``: нативный календарь для даты * (у даты нет проблемы AM/PM) + [Time24Field] для времени в 24-часовом формате. * Контракт значения тот же — строка `YYYY-MM-DDTHH:mm` (UTC), как у datetime-local. */ type DateTime24FieldProps = { value: string; onChange: (value: string) => void; className?: string; }; export function DateTime24Field({ value, onChange, className }: DateTime24FieldProps) { const sep = value.indexOf("T"); const datePart = sep >= 0 ? value.slice(0, sep) : value; const timePart = sep >= 0 ? value.slice(sep + 1, sep + 6) : ""; const combine = (date: string, time: string) => `${date}T${time || "00:00"}`; return ( onChange(combine(event.target.value, timePart))} /> onChange(combine(datePart, time))} /> ); }