mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-14 13:27:13 +00:00
Rework the timeline row model from one-row-per-stacking-layer to NLE "lanes": a row holds a time-sequence of non-overlapping clips, ordered top = front. Stacking only matters between clips that overlap in time. - Lane packing: per stacking context, sort clips by effective z-index desc (DOM order tiebreak) and greedily pack each onto the first lane whose members don't overlap it in time; else open a new lane. Lane's representative z is the max member z. Audio stays one clip per lane. - Lane-aware drag: dropping onto a lane joins it (clip takes the lane's z) when there's no time-conflict; a conflicting drop is rejected and the preview snaps to the nearest valid lane or a new-lane insertion. Between/above/below still create a new lane at that stacking level. - Overlap checks use the drag-preview start/duration, so conflict is judged by where the clip lands, not where it started. Only explicit drags write z-index; authored z is preserved and data-track-index is never rewritten.
63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
import { getTimelineElementIdentity } from "../lib/timelineElementHelpers";
|
|
|
|
export interface TimelineStackingElement {
|
|
id: string;
|
|
key?: string;
|
|
tag?: string;
|
|
start: number;
|
|
duration: number;
|
|
track: number;
|
|
zIndex?: number;
|
|
stackingContextId?: string | null;
|
|
parentCompositionId?: string | null;
|
|
compositionAncestors?: string[];
|
|
// Locator for resolving the live element at commit time (sub-comp children
|
|
// aren't in the top-level element list, so the reorder intent must be
|
|
// self-contained rather than re-looked-up by identity).
|
|
domId?: string;
|
|
selector?: string;
|
|
selectorIndex?: number;
|
|
sourceFile?: string;
|
|
}
|
|
|
|
export interface TimelineStackingOrderItem {
|
|
key: string;
|
|
track: number;
|
|
zIndex: number;
|
|
stackingContextId: string | null;
|
|
parentCompositionId: string | null;
|
|
compositionAncestors: readonly string[];
|
|
}
|
|
|
|
export type TimelineLayerDropPlacement =
|
|
| { type: "onto"; layerId: string }
|
|
| { type: "between"; beforeLayerId: string; afterLayerId: string }
|
|
| { type: "above"; layerId: string }
|
|
| { type: "below"; layerId: string };
|
|
|
|
export interface TimelineStackingZIndexChange {
|
|
key: string;
|
|
zIndex: number;
|
|
domId?: string;
|
|
selector?: string;
|
|
selectorIndex?: number;
|
|
sourceFile?: string;
|
|
}
|
|
|
|
export interface TimelineStackingReorderIntent {
|
|
contextKey: string;
|
|
placement: TimelineLayerDropPlacement;
|
|
zIndexChanges: TimelineStackingZIndexChange[];
|
|
}
|
|
|
|
export function toStackingOrderItem(element: TimelineStackingElement): TimelineStackingOrderItem {
|
|
return {
|
|
key: getTimelineElementIdentity(element),
|
|
track: element.track,
|
|
zIndex: element.zIndex ?? 0,
|
|
stackingContextId: element.stackingContextId ?? null,
|
|
parentCompositionId: element.parentCompositionId ?? null,
|
|
compositionAncestors: element.compositionAncestors ?? [],
|
|
};
|
|
}
|