Basic timecode field
A compact read-first value that temporarily opens a TimecodeInput for edits.
Installation
pnpm add @techsquidtv/canvas-timeline-reactnpm install @techsquidtv/canvas-timeline-reactyarn add @techsquidtv/canvas-timeline-reactbun add @techsquidtv/canvas-timeline-react- 1
Install the package.
The command above adds the React bindings and their package dependencies.
- 2
Import from the React entrypoint.
@techsquidtv/canvas-timeline-react/timecode-field - 3
Add the timeline styles.
Use the full stylesheet with shadcn-compatible tokens, or base.css when your app owns the visual theme.
Default visuals import '@techsquidtv/canvas-timeline-react/styles.css';Base geometry import '@techsquidtv/canvas-timeline-react/base.css';
Usage
import { useState } from 'react';import { fromSeconds } from '@techsquidtv/canvas-timeline-utils';import { TimecodeField } from '@techsquidtv/canvas-timeline-react/timecode-field';
export function ClipStartField() { const [time, setTime] = useState(() => fromSeconds(90.5, 24000));
return ( <TimecodeField.Root ariaLabel="Clip start" value={time} formatOptions={{ format: 'seconds' }} timebase={time.r} onCommit={(_seconds, details) => setTime(details.time)} > <TimecodeField.Trigger /> <TimecodeField.Input /> </TimecodeField.Root> );}Examples
Formatting
Switch the compact display format while preserving the committed RationalTime value.
90.50s
import { useState } from 'react';import { type RationalTime } from '@techsquidtv/canvas-timeline-utils';import { TimecodeField } from '@techsquidtv/canvas-timeline-react/timecode-field';import { type TimecodeInputFormatOptions } from '@techsquidtv/canvas-timeline-react/timecode-input';
const formatOptions = [ { value: 'seconds', label: 'Seconds', formatOptions: { format: 'seconds' } }, { value: 'minutes', label: 'Minutes', formatOptions: { format: 'minutes' } }, { value: 'hours', label: 'Hours', formatOptions: { format: 'hours' } }, { value: 'frames-24', label: '24 fps', formatOptions: { format: 'frames', frameRate: 24 }, }, { value: 'drop-frame-2997', label: '29.97 DF', formatOptions: { format: 'drop-frame', frameRate: { numerator: 30000, denominator: 1001 }, dropFrame: true, }, },] satisfies Array<{ value: string; label: string; formatOptions: TimecodeInputFormatOptions;}>;
const sequenceRate = 24000;
export function ClipStartField({ value, onCommit,}: { value: RationalTime; onCommit: (time: RationalTime) => void;}) { const [formatValue, setFormatValue] = useState('seconds'); const selectedFormat = formatOptions.find((option) => option.value === formatValue) ?? formatOptions[0];
return ( <div> <TimecodeField.Root ariaLabel="Clip start" value={value} formatOptions={selectedFormat.formatOptions} timebase={sequenceRate} onCommit={(_seconds, details) => onCommit(details.time)} > <TimecodeField.Trigger /> <TimecodeField.Input /> </TimecodeField.Root> <select aria-label="Timecode field format" value={formatValue} onChange={(event) => setFormatValue(event.currentTarget.value)} > {formatOptions.map((option) => ( <option key={option.value} value={option.value}> {option.label} </option> ))} </select> </div> );}Notes
- Choose
TimecodeFieldfor playhead clocks, clip boundaries, trim controls, and other dense editor chrome where an always-visible input adds visual noise. TimecodeFieldrenders as a compact value first, then swaps toTimecodeInputonly while the user is making a typed correction.- Root owns draft text, invalid state, Enter/Escape handling, blur commit/cancel, focus restore, and width reservation.
onCommitreceives both seconds andRationalTimedetails so frame-perfect consumers can storedetails.timedirectly.- Frame-rate and drop-frame parsing are inferred from
formatOptions; passparseOptionsonly when draft parsing should differ from display formatting. - Choose
TimecodeInputinstead when the text box should stay visible and your own form should own apply/cancel/error behavior.
API Reference
Timecode Field
A read-first inline timecode control for dense editor chrome: it displays a stable value, opens TimecodeInput only while editing, and commits seconds plus RationalTime details.
Exports
| Name | Reference | Description |
|---|---|---|
TimecodeField | API reference | Compound primitive for compact display-to-input timecode editing in dense editor chrome. |
TimecodeFieldRootProps | API reference | Value, parser, formatter, editing, and commit props for the inline editing root. |
TimecodeFieldTriggerProps | API reference | Button props for the compact displayed timecode value. |
TimecodeFieldInputProps | API reference | TimecodeInput props for the temporary editor shown only while editing. |
TimecodeFieldCommitDetails | API reference | Commit metadata including seconds, RationalTime, text, and reason. |
TimecodeFieldCommitReason | API reference | Reason the field committed the draft text. |
TimecodeInputFormatOptions | API reference | Formatting options used by TimecodeField.Root formatOptions. |
TimecodeInputParseOptions | API reference | Optional parsing overrides used by TimecodeField.Root parseOptions. |
Props and Inputs
| Name | Type | Description |
|---|---|---|
ariaLabel | string | Human-readable control name used for the trigger and input labels. |
value | number | RationalTime | Current timecode value shown by the trigger and used to seed the draft. |
formatOptions | TimecodeInputFormatOptions | Formats the trigger text and the draft value when editing starts. |
timebase | number | Tick rate used for details.time in onCommit. Defaults to the value rate, or 60000 for seconds. |
onCommit | (seconds, details) => void | Promise<void> | Called after valid Enter or blur commits with parsed seconds and RationalTime details. |
parseOptions | TimecodeInputParseOptions | Optional parser overrides when draft validation should differ from formatOptions. |
editing | boolean | Optional controlled editing state. |