import type { ReactNode } from "react"; import { timelineLogicalRowCellId } from "./timelineNavigationIdentity"; import type { TimelineLogicalRow } from "./timelineKeyboardNavigation"; interface TimelineTrackRowProps { index: number; rowKey: number; logicalRow: TimelineLogicalRow; propertyRows: readonly TimelineLogicalRow[]; /** Names the canvas-side content cell — the active clip's own property lanes, * minted with this single id in TimelinePropertyLanes. */ lanesId: string; /** Names the header cell. Space-separated because the caret it lives under * expands two disjoint subtrees (the clip's keyframe lanes AND the track's * automation lanes) — see TimelineTrackHeader for why they cannot share one id. */ headerLanesId: string; top: number; height: number; virtualized: boolean; background: string; borderColor: string; rovingTargetId?: string | null; children: ReactNode; } /** Accessible row shell; edit geometry owns its exact top and height. */ export function TimelineTrackRow({ index, rowKey, logicalRow, propertyRows, lanesId, headerLanesId, top, height, virtualized, background, borderColor, rovingTargetId = null, children, }: TimelineTrackRowProps) { return (
{children}
{propertyRows.map((row) => { const group = row.propertyGroup; const keyframeCount = row.items.filter((item) => item.kind === "keyframe").length; const easeCount = row.items.filter((item) => item.kind === "ease").length; return ( // ponytail: aria-owns maps this hidden logical row onto the two visible // property-lane cells without duplicating interactive controls.
{group}
{keyframeCount} keyframes, {easeCount} ease controls
); })}
); }