React Registry

primitiveTimecodeField@techsquidtv/canvas-timeline-react/timecode-field

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.

Basic timecode field

A compact read-first value that temporarily opens a TimecodeInput for edits.

Installation

pnpm
pnpm add @techsquidtv/canvas-timeline-react
  1. 1

    Install the package.

    The command above adds the React bindings and their package dependencies.

  2. 2

    Import from the React entrypoint.

    @techsquidtv/canvas-timeline-react/timecode-field
  3. 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

ClipStartField.tsx
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
Example source
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 TimecodeField for playhead clocks, clip boundaries, trim controls, and other dense editor chrome where an always-visible input adds visual noise.
  • TimecodeField renders as a compact value first, then swaps to TimecodeInput only 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.
  • onCommit receives both seconds and RationalTime details so frame-perfect consumers can store details.time directly.
  • Frame-rate and drop-frame parsing are inferred from formatOptions; pass parseOptions only when draft parsing should differ from display formatting.
  • Choose TimecodeInput instead 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

NameReferenceDescription
TimecodeFieldAPI referenceCompound primitive for compact display-to-input timecode editing in dense editor chrome.
TimecodeFieldRootPropsAPI referenceValue, parser, formatter, editing, and commit props for the inline editing root.
TimecodeFieldTriggerPropsAPI referenceButton props for the compact displayed timecode value.
TimecodeFieldInputPropsAPI referenceTimecodeInput props for the temporary editor shown only while editing.
TimecodeFieldCommitDetailsAPI referenceCommit metadata including seconds, RationalTime, text, and reason.
TimecodeFieldCommitReasonAPI referenceReason the field committed the draft text.
TimecodeInputFormatOptionsAPI referenceFormatting options used by TimecodeField.Root formatOptions.
TimecodeInputParseOptionsAPI referenceOptional parsing overrides used by TimecodeField.Root parseOptions.

Props and Inputs

NameTypeDescription
ariaLabelstringHuman-readable control name used for the trigger and input labels.
valuenumber | RationalTimeCurrent timecode value shown by the trigger and used to seed the draft.
formatOptionsTimecodeInputFormatOptionsFormats the trigger text and the draft value when editing starts.
timebasenumberTick 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.
parseOptionsTimecodeInputParseOptionsOptional parser overrides when draft validation should differ from formatOptions.
editingbooleanOptional controlled editing state.