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))}
/>
);
}