mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
docs(studio): document timeline keyboard navigation (#3031)
* feat(studio): expose timeline treegrid semantics * feat(studio): coordinate logical timeline focus * feat(studio): add timeline keyboard controls * docs(studio): document timeline keyboard navigation
This commit is contained in:
@@ -1,14 +1,19 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { timelineLogicalRowCellId } from "./timelineNavigationIdentity";
|
||||
import type { TimelineLogicalRow } from "./timelineKeyboardNavigation";
|
||||
|
||||
interface TimelineTrackRowProps {
|
||||
index: number;
|
||||
rowKey: number;
|
||||
rowCount: number;
|
||||
logicalRow: TimelineLogicalRow;
|
||||
propertyRows: readonly TimelineLogicalRow[];
|
||||
lanesId: string;
|
||||
top: number;
|
||||
height: number;
|
||||
virtualized: boolean;
|
||||
background: string;
|
||||
borderColor: string;
|
||||
rovingTargetId?: string | null;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
@@ -16,23 +21,24 @@ interface TimelineTrackRowProps {
|
||||
export function TimelineTrackRow({
|
||||
index,
|
||||
rowKey,
|
||||
rowCount,
|
||||
logicalRow,
|
||||
propertyRows,
|
||||
lanesId,
|
||||
top,
|
||||
height,
|
||||
virtualized,
|
||||
background,
|
||||
borderColor,
|
||||
rovingTargetId = null,
|
||||
children,
|
||||
}: TimelineTrackRowProps) {
|
||||
return (
|
||||
<div
|
||||
role="listitem"
|
||||
aria-posinset={index + 1}
|
||||
aria-setsize={rowCount}
|
||||
role="rowgroup"
|
||||
data-index={index}
|
||||
data-timeline-row={index}
|
||||
data-timeline-row-key={rowKey}
|
||||
className={`${virtualized ? "absolute left-0 right-0" : "relative"} flex`}
|
||||
className={virtualized ? "absolute left-0 right-0" : "relative"}
|
||||
style={{
|
||||
top: virtualized ? top : undefined,
|
||||
height,
|
||||
@@ -40,7 +46,52 @@ export function TimelineTrackRow({
|
||||
borderBottom: `1px solid ${borderColor}`,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<div
|
||||
role="row"
|
||||
aria-rowindex={logicalRow.logicalIndex + 1}
|
||||
aria-level={logicalRow.level}
|
||||
aria-expanded={logicalRow.expandable ? logicalRow.expanded : undefined}
|
||||
data-timeline-logical-row-id={logicalRow.id}
|
||||
data-timeline-focus-id={logicalRow.id}
|
||||
tabIndex={rovingTargetId === logicalRow.id ? 0 : -1}
|
||||
className="flex"
|
||||
style={{ height }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
{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.
|
||||
<div
|
||||
key={row.id}
|
||||
role="row"
|
||||
aria-rowindex={row.logicalIndex + 1}
|
||||
aria-level={row.level}
|
||||
data-property-group={group}
|
||||
data-timeline-logical-row-id={row.id}
|
||||
className="sr-only"
|
||||
>
|
||||
<div
|
||||
role="rowheader"
|
||||
aria-colindex={1}
|
||||
aria-owns={timelineLogicalRowCellId(lanesId, row.id, "header")}
|
||||
>
|
||||
{group}
|
||||
</div>
|
||||
<div
|
||||
role="gridcell"
|
||||
aria-colindex={2}
|
||||
aria-owns={timelineLogicalRowCellId(lanesId, row.id, "content")}
|
||||
>
|
||||
{keyframeCount} keyframes, {easeCount} ease controls
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user