@molecule/app-feature-video-timeline-react
v1.0.1
Published
Multi-track video timeline composing track-lane rows with a time ruler, playhead, zoom controls, and ripple/insert edit modes
Downloads
520
Maintainers
Readme
@molecule/app-feature-video-timeline-react
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Multi-track video timeline composing one <TrackLane> per track row
with a shared time ruler, playhead, zoom slider + Ctrl+wheel zoom,
and ripple/insert edit modes.
Exports <VideoTimeline>, the Track and VideoTimelineMode types,
and the pure-function helpers clampZoom, zoomFromWheelDelta,
computeRulerTicks, computeRippleUpdates, and formatTickTime
that back the zoom math, ruler tick generation, and ripple-mode
fan-out.
Quick Start
import { VideoTimeline } from '@molecule/app-feature-video-timeline-react'
;<VideoTimeline
tracks={[
{ id: 'v1', kind: 'video', clips: [{ id: 'a', startTime: 0, duration: 5, label: 'Intro' }] },
{ id: 'a1', kind: 'audio', clips: [{ id: 'b', startTime: 0, duration: 12 }] },
]}
currentTime={3.2}
duration={60}
pixelsPerSecond={20}
mode="ripple"
onSeek={(t) => setCurrentTime(t)}
onClipMove={(id, startTime, trackId) => updateClip(trackId, id, { startTime })}
onClipResize={(id, duration, trackId) => updateClip(trackId, id, { duration })}
onZoomChange={(pps) => setZoom(pps)}
/>Type
feature
Installation
npm install @molecule/app-feature-video-timeline-react @molecule/app-feature-track-lane-react @molecule/app-react @molecule/app-ui react
npm install -D @types/reactAPI
Interfaces
Track
One track row on a <VideoTimeline>. Tracks are rendered in array
order top-to-bottom and each track delegates clip rendering to a
<TrackLane> row.
interface Track {
/** Stable identifier — used as the React key and lane id passed to handlers. */
id: string
/** The kind of media on this track. */
kind: TrackKind
/** Clips on this track. Order is preserved as-is. */
clips: Clip[]
/** Optional human-friendly track name; falls back to a translated kind label. */
name?: string
}VideoTimelineProps
Props for <VideoTimeline>.
interface VideoTimelineProps {
/** Tracks to render. Order is preserved as-is. */
tracks: Track[]
/** Current playhead time in seconds. Drives the playhead vertical line. */
currentTime: number
/** Total timeline duration in seconds — used to size the scrollable body. */
duration: number
/** Called when the user clicks/scrubs the ruler. */
onSeek?: (time: number) => void
/** Called when a clip on a lane is dragged horizontally. */
onClipMove?: (clipId: string, startTime: number, trackId: string) => void
/** Called when a clip's right-edge resize handle is dragged. */
onClipResize?: (clipId: string, duration: number, trackId: string) => void
/** Horizontal scale (px per second). Defaults to 20. */
pixelsPerSecond?: number
/** Lower zoom bound for Ctrl+wheel / slider. Defaults to 2. */
zoomMin?: number
/** Upper zoom bound for Ctrl+wheel / slider. Defaults to 200. */
zoomMax?: number
/** Edit mode for clip drags. Defaults to `'ripple'`. */
mode?: VideoTimelineMode
/** Called when the user changes the zoom (slider or Ctrl+wheel). */
onZoomChange?: (pixelsPerSecond: number) => void
/** Optional currently-selected clip id. */
selectedClipId?: string
/** Called when a clip is clicked (no drag). */
onClipClick?: (clipId: string, trackId: string) => void
/** Per-track row height in pixels. Defaults to 44. */
trackHeight?: number
/** Width of the leading lane-header column in pixels. Defaults to 120. */
laneHeaderWidth?: number
/** Height of the time ruler row in pixels. Defaults to 28. */
rulerHeight?: number
/** Extra classes merged onto the root element. */
className?: string
}Types
TrackKind
The kind of media a track holds. Drives the lane name fallback, the default clip color, and the data attribute parents can hook onto for kind-specific styling.
type TrackKind = 'video' | 'audio' | 'subtitle'VideoTimelineMode
Edit mode applied to clip drags.
insert— dragging a clip just drops it at the new position; other clips on the lane stay where they are.ripple— dragging a clip shifts every later clip on the same lane by the same delta, preserving inter-clip gaps.
type VideoTimelineMode = 'ripple' | 'insert'Functions
clampZoom(value, min, max)
Clamp a candidate pixelsPerSecond value into [min, max]. Used by
the zoom slider, Ctrl+wheel, and any external zoom button that calls
onZoomChange.
function clampZoom(value: number, min: number, max: number): numbervalue— Candidate horizontal scale in px/sec.min— Lower bound (inclusive).max— Upper bound (inclusive).
Returns: The clamped value.
computeRippleUpdates(clips, draggedClipId, proposedStartTime)
Compute the ripple-mode update for a clip drag on a single lane.
Returns one update per affected clip — at minimum the dragged clip,
plus every clip whose original startTime is strictly greater (which
shifts by the same delta).
Negative deltas are clamped so no clip's startTime goes below 0
(the dragged clip is the limiting factor — later clips can never
cross zero before it does, since they all started later).
function computeRippleUpdates(
clips: Clip[],
draggedClipId: string,
proposedStartTime: number,
): { id: string; startTime: number }[]clips— All clips on the lane (any order).draggedClipId— The id of the clip being dragged.proposedStartTime— The candidate newstartTimefor the dragged clip.
Returns: Array of { id, startTime } updates to apply.
computeRulerTicks(duration, pixelsPerSecond)
Compute time tick marks for the ruler. Picks an interval (1, 2, 5, 10,
30, or 60 seconds, etc.) so that adjacent ticks are at least
MIN_TICK_PIXEL_SPACING apart on screen. Always emits a tick at 0 and
stops at the last tick <= duration.
function computeRulerTicks(
duration: number,
pixelsPerSecond: number,
): { time: number; pixel: number }[]duration— Total duration in seconds.pixelsPerSecond— Horizontal scale.
Returns: Tick descriptors with { time, pixel }.
formatTickTime(seconds)
Format a tick time for the ruler — mm:ss over a minute, s.s below.
function formatTickTime(seconds: number): stringseconds— Time in seconds.
Returns: Formatted string for the tick label.
VideoTimeline(props)
Multi-track video timeline. Composes one <TrackLane> per track with
a shared time ruler at the top, a vertical playhead, and a zoom slider
row. Pointer-down on the ruler scrubs onSeek; Ctrl+wheel anywhere
over the body changes zoom; the slider is keyboard-accessible.
In 'ripple' mode (default), dragging a clip on a lane shifts every
subsequent clip on that lane by the same delta — onClipMove is
called once per affected clip. In 'insert' mode, only the dragged
clip moves.
function VideoTimeline(props: VideoTimelineProps): JSX.Elementprops— Component props.
Returns: The multi-track timeline element.
zoomFromWheelDelta(current, deltaY, min, max)
Compute the next zoom value for a Ctrl+wheel notch. Negative deltaY
(scroll-up) zooms in; positive zooms out. Result is clamped into
[min, max].
function zoomFromWheelDelta(current: number, deltaY: number, min: number, max: number): numbercurrent— CurrentpixelsPerSecond.deltaY— WheeldeltaY(sign-only — magnitude is ignored).min— Lower zoom bound.max— Upper zoom bound.
Returns: The clamped next zoom value.
Constants
DEFAULT_LANE_HEADER_WIDTH
Default lane-header column width.
const DEFAULT_LANE_HEADER_WIDTH: 120DEFAULT_PIXELS_PER_SECOND
Default horizontal scale.
const DEFAULT_PIXELS_PER_SECOND: 20DEFAULT_RULER_HEIGHT
Default ruler height.
const DEFAULT_RULER_HEIGHT: 28DEFAULT_TRACK_HEIGHT
Default per-track row height.
const DEFAULT_TRACK_HEIGHT: 44DEFAULT_ZOOM_MAX
Default upper zoom bound.
const DEFAULT_ZOOM_MAX: 200DEFAULT_ZOOM_MIN
Default lower zoom bound.
const DEFAULT_ZOOM_MIN: 2MIN_TICK_PIXEL_SPACING
Minimum spacing (px) between adjacent ruler ticks.
const MIN_TICK_PIXEL_SPACING: 60ZOOM_WHEEL_FACTOR
Multiplier per Ctrl+wheel notch (positive deltaY zooms out).
const ZOOM_WHEEL_FACTOR: 1.1Injection Notes
Requirements
Peer dependencies:
@molecule/app-feature-track-lane-react^1.0.1@molecule/app-react^1.0.1@molecule/app-ui^1.0.1react^18.0.0 || ^19.0.0
Runtime Dependencies
@molecule/app-feature-track-lane-react@molecule/app-react@molecule/app-uireact
Ripple mode (default) shifts all later clips on the same lane by the
same delta when a clip is dragged, preserving inter-clip gaps. Switch
to mode="insert" to drop the dragged clip at its new position
without disturbing the rest of the lane.
Translations
Translation strings are provided by @molecule/app-locales-feature-video-timeline.
