import { useState, type ComponentProps } from "react";
import { formatTimecode, parseTimecode } from "../../lib/timecode";
import { Input } from "./Input";
// A field a cut's time is typed into (lib/timecode): the editor's segment ends, the clip panel's In /
// Out. A typed draft is the field's until Enter or leaving the field takes it; then the field shows
// the value as it landed — the caller may clamp it (a time past the end) — and a time it cannot read
// stays, marked invalid, for fixing rather than vanishing or seeming taken. One element throughout,
// so Enter leaves the focus in the field (a field re-created per value dropped it on
).
type Props = Omit<
ComponentProps,
"value" | "defaultValue" | "onChange" | "invalid"
> & {
/** Seconds. */
value: number;
/** Show hours (pass `duration >= 3600`, so one item's fields agree). */
hours?: boolean;
/** A readable time was typed (not called when it equals `value`). */
onCommit: (seconds: number) => void;
};
export function TimecodeField({
value,
hours = false,
onCommit,
onBlur,
onKeyDown,
...rest
}: Props) {
const [draft, setDraft] = useState(null);
const invalid = draft != null && parseTimecode(draft) == null;
const commit = () => {
if (draft == null) return;
const v = parseTimecode(draft);
if (v == null) return;
setDraft(null);
if (Math.abs(v - value) > 1e-3) onCommit(v);
};
return (
setDraft(e.target.value)}
onBlur={(e) => {
commit();
onBlur?.(e);
}}
onKeyDown={(e) => {
onKeyDown?.(e);
if (e.key !== "Enter" || e.defaultPrevented) return;
e.preventDefault();
commit();
}}
/>
);
}