Files
OpenCut/apps/web/src/lib/timeline/group-move/build-group.ts
T

88 lines
2.0 KiB
TypeScript
Raw Normal View History

2026-04-15 05:43:29 +02:00
import type { ElementRef, SceneTracks } from "@/lib/timeline";
import { findTrackInSceneTracks } from "@/lib/timeline/track-element-update";
import type { GroupMember, MoveGroup } from "./types";
import { getTrackPlacementById } from "./track-placement";
export function buildMoveGroup({
anchorRef,
selectedElements,
tracks,
}: {
anchorRef: ElementRef;
selectedElements: ElementRef[];
tracks: SceneTracks;
}): MoveGroup | null {
const anchorTrack = findTrackInSceneTracks({
tracks,
trackId: anchorRef.trackId,
});
const anchorElement = anchorTrack?.elements.find(
(element) => element.id === anchorRef.elementId,
);
const anchorPlacement = getTrackPlacementById({
tracks,
trackId: anchorRef.trackId,
});
if (!anchorTrack || !anchorElement || !anchorPlacement) {
return null;
}
const seen = new Set<string>();
const orderedRefs = [anchorRef, ...selectedElements].filter((elementRef) => {
if (seen.has(elementRef.elementId)) {
return false;
}
seen.add(elementRef.elementId);
return true;
});
const members = orderedRefs.flatMap((elementRef): GroupMember[] => {
const track = findTrackInSceneTracks({
tracks,
trackId: elementRef.trackId,
});
const element = track?.elements.find(
(trackElement) => trackElement.id === elementRef.elementId,
);
const placement = getTrackPlacementById({
tracks,
trackId: elementRef.trackId,
});
if (!track || !element || !placement) {
return [];
}
return [
{
trackId: track.id,
elementId: element.id,
elementType: element.type,
duration: element.duration,
timeOffset: element.startTime - anchorElement.startTime,
trackSection: placement.section,
sectionIndex: placement.sectionIndex,
displayIndex: placement.displayIndex,
},
];
});
if (members.length === 0) {
return null;
}
const anchor = members.find(
(member) =>
member.trackId === anchorRef.trackId &&
member.elementId === anchorRef.elementId,
);
if (!anchor) {
return null;
}
return {
anchor,
members,
};
}