This commit is contained in:
Maze Winther
2026-01-05 01:35:10 +01:00
parent d5ca991501
commit c19f085e48
95 changed files with 4133 additions and 4196 deletions
+27 -48
View File
@@ -1,16 +1,19 @@
import type { TrackType, TimelineTrack } from "@/types/timeline";
import { TRACK_COLORS, TRACK_HEIGHTS } from "@/constants/timeline-constants";
import type {
TrackType,
TimelineTrack,
TimelineElement,
} from "@/types/timeline";
import {
TRACK_COLORS,
TRACK_HEIGHTS,
TRACK_GAP,
} from "@/constants/timeline-constants";
import { generateUUID } from "@/lib/utils";
export function getTrackColors({ type }: { type: TrackType }) {
export function getTrackColor({ type }: { type: TrackType }) {
return TRACK_COLORS[type];
}
export function getTrackElementClasses({ type }: { type: TrackType }) {
const colors = getTrackColors({ type });
return `${colors.background} ${colors.border}`;
}
export function getTrackHeight({ type }: { type: TrackType }): number {
return TRACK_HEIGHTS[type];
}
@@ -22,11 +25,10 @@ export function getCumulativeHeightBefore({
tracks: Array<{ type: TrackType }>;
trackIndex: number;
}): number {
const GAP = 4;
return tracks
.slice(0, trackIndex)
.reduce(
(sum, track) => sum + getTrackHeight({ type: track.type }) + GAP,
(sum, track) => sum + getTrackHeight({ type: track.type }) + TRACK_GAP,
0,
);
}
@@ -36,42 +38,20 @@ export function getTotalTracksHeight({
}: {
tracks: Array<{ type: TrackType }>;
}): number {
const GAP = 4;
const tracksHeight = tracks.reduce(
(sum, track) => sum + getTrackHeight({ type: track.type }),
0,
);
const gapsHeight = Math.max(0, tracks.length - 1) * GAP;
const gapsHeight = Math.max(0, tracks.length - 1) * TRACK_GAP;
return tracksHeight + gapsHeight;
}
export function sortTracksByOrder({
tracks,
}: {
tracks: TimelineTrack[];
}): TimelineTrack[] {
return [...tracks].sort((a, b) => {
if (a.type === "text" && b.type !== "text") return -1;
if (b.type === "text" && a.type !== "text") return 1;
if (a.type === "audio" && b.type !== "audio") return 1;
if (b.type === "audio" && a.type !== "audio") return -1;
if (a.type === "media" && b.type === "media") {
if (a.isMain && !b.isMain) return 1;
if (!a.isMain && b.isMain) return -1;
}
return 0;
});
}
export function getMainTrack({
tracks,
}: {
tracks: TimelineTrack[];
}): TimelineTrack | null {
return tracks.find((track) => track.isMain) || null;
return tracks.find((track) => track.type === "media" && track.isMain) ?? null;
}
export function ensureMainTrack({
@@ -79,7 +59,9 @@ export function ensureMainTrack({
}: {
tracks: TimelineTrack[];
}): TimelineTrack[] {
const hasMainTrack = tracks.some((track) => track.isMain);
const hasMainTrack = tracks.some(
(track) => track.type === "media" && track.isMain,
);
if (!hasMainTrack) {
const mainTrack: TimelineTrack = {
@@ -100,14 +82,13 @@ export function canElementGoOnTrack({
elementType,
trackType,
}: {
elementType: "text" | "media";
elementType: TimelineElement["type"];
trackType: TrackType;
}): boolean {
if (elementType === "text") {
return trackType === "text";
}
if (elementType === "media") {
return trackType === "media" || trackType === "audio";
if (elementType === "text") return trackType === "text";
if (elementType === "audio") return trackType === "audio";
if (elementType === "video" || elementType === "image") {
return trackType === "media";
}
return false;
}
@@ -116,7 +97,7 @@ export function validateElementTrackCompatibility({
element,
track,
}: {
element: { type: "text" | "media" };
element: { type: TimelineElement["type"] };
track: { type: TrackType };
}): { isValid: boolean; errorMessage?: string } {
const isValid = canElementGoOnTrack({
@@ -125,12 +106,10 @@ export function validateElementTrackCompatibility({
});
if (!isValid) {
const errorMessage =
element.type === "text"
? "Text elements can only be placed on text tracks"
: "Media elements can only be placed on media or audio tracks";
return { isValid: false, errorMessage };
return {
isValid: false,
errorMessage: `${element.type} elements cannot be placed on ${track.type} tracks`,
};
}
return { isValid: true };