refactor: move timeline element context menu into timeline-element.tsx
This commit is contained in:
@ -11,6 +11,7 @@ import {
|
|||||||
ChevronRight,
|
ChevronRight,
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
Type,
|
Type,
|
||||||
|
Copy,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useMediaStore } from "@/stores/media-store";
|
import { useMediaStore } from "@/stores/media-store";
|
||||||
import { useTimelineStore } from "@/stores/timeline-store";
|
import { useTimelineStore } from "@/stores/timeline-store";
|
||||||
@ -33,6 +34,13 @@ import {
|
|||||||
DropdownMenuSubContent,
|
DropdownMenuSubContent,
|
||||||
DropdownMenuSubTrigger,
|
DropdownMenuSubTrigger,
|
||||||
} from "../ui/dropdown-menu";
|
} from "../ui/dropdown-menu";
|
||||||
|
import {
|
||||||
|
ContextMenu,
|
||||||
|
ContextMenuContent,
|
||||||
|
ContextMenuItem,
|
||||||
|
ContextMenuSeparator,
|
||||||
|
ContextMenuTrigger,
|
||||||
|
} from "../ui/context-menu";
|
||||||
|
|
||||||
export function TimelineElement({
|
export function TimelineElement({
|
||||||
element,
|
element,
|
||||||
@ -52,6 +60,7 @@ export function TimelineElement({
|
|||||||
splitAndKeepLeft,
|
splitAndKeepLeft,
|
||||||
splitAndKeepRight,
|
splitAndKeepRight,
|
||||||
separateAudio,
|
separateAudio,
|
||||||
|
addElementToTrack,
|
||||||
} = useTimelineStore();
|
} = useTimelineStore();
|
||||||
const { currentTime } = usePlaybackStore();
|
const { currentTime } = usePlaybackStore();
|
||||||
|
|
||||||
@ -172,6 +181,38 @@ export function TimelineElement({
|
|||||||
return mediaItem?.type === "video" && track.type === "media";
|
return mediaItem?.type === "video" && track.type === "media";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleElementSplitContext = () => {
|
||||||
|
const effectiveStart = element.startTime;
|
||||||
|
const effectiveEnd =
|
||||||
|
element.startTime +
|
||||||
|
(element.duration - element.trimStart - element.trimEnd);
|
||||||
|
|
||||||
|
if (currentTime > effectiveStart && currentTime < effectiveEnd) {
|
||||||
|
const secondElementId = splitElement(track.id, element.id, currentTime);
|
||||||
|
if (!secondElementId) {
|
||||||
|
toast.error("Failed to split element");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast.error("Playhead must be within element to split");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleElementDuplicateContext = () => {
|
||||||
|
const { id, ...elementWithoutId } = element;
|
||||||
|
addElementToTrack(track.id, {
|
||||||
|
...elementWithoutId,
|
||||||
|
name: element.name + " (copy)",
|
||||||
|
startTime:
|
||||||
|
element.startTime +
|
||||||
|
(element.duration - element.trimStart - element.trimEnd) +
|
||||||
|
0.1,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleElementDeleteContext = () => {
|
||||||
|
removeElementFromTrack(track.id, element.id);
|
||||||
|
};
|
||||||
|
|
||||||
const renderElementContent = () => {
|
const renderElementContent = () => {
|
||||||
if (element.type === "text") {
|
if (element.type === "text") {
|
||||||
return (
|
return (
|
||||||
@ -255,6 +296,8 @@ export function TimelineElement({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ContextMenu>
|
||||||
|
<ContextMenuTrigger asChild>
|
||||||
<div
|
<div
|
||||||
className={`absolute top-0 h-full select-none timeline-element ${
|
className={`absolute top-0 h-full select-none timeline-element ${
|
||||||
isBeingDragged ? "z-50" : "z-10"
|
isBeingDragged ? "z-50" : "z-10"
|
||||||
@ -297,5 +340,25 @@ export function TimelineElement({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</ContextMenuTrigger>
|
||||||
|
<ContextMenuContent>
|
||||||
|
<ContextMenuItem onClick={handleElementSplitContext}>
|
||||||
|
<Scissors className="h-4 w-4 mr-2" />
|
||||||
|
Split at playhead
|
||||||
|
</ContextMenuItem>
|
||||||
|
<ContextMenuItem onClick={handleElementDuplicateContext}>
|
||||||
|
<Copy className="h-4 w-4 mr-2" />
|
||||||
|
Duplicate {element.type === "text" ? "text" : "clip"}
|
||||||
|
</ContextMenuItem>
|
||||||
|
<ContextMenuSeparator />
|
||||||
|
<ContextMenuItem
|
||||||
|
onClick={handleElementDeleteContext}
|
||||||
|
className="text-destructive focus:text-destructive"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 mr-2" />
|
||||||
|
Delete {element.type === "text" ? "text" : "clip"}
|
||||||
|
</ContextMenuItem>
|
||||||
|
</ContextMenuContent>
|
||||||
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,7 @@ import { useRef, useState, useEffect } from "react";
|
|||||||
import { useTimelineStore } from "@/stores/timeline-store";
|
import { useTimelineStore } from "@/stores/timeline-store";
|
||||||
import { useMediaStore } from "@/stores/media-store";
|
import { useMediaStore } from "@/stores/media-store";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Copy, Scissors, Trash2 } from "lucide-react";
|
|
||||||
import { TimelineElement } from "./timeline-element";
|
import { TimelineElement } from "./timeline-element";
|
||||||
import {
|
|
||||||
ContextMenu,
|
|
||||||
ContextMenuContent,
|
|
||||||
ContextMenuItem,
|
|
||||||
ContextMenuSeparator,
|
|
||||||
ContextMenuTrigger,
|
|
||||||
} from "../ui/context-menu";
|
|
||||||
import {
|
import {
|
||||||
TimelineTrack,
|
TimelineTrack,
|
||||||
sortTracksByOrder,
|
sortTracksByOrder,
|
||||||
@ -882,10 +874,8 @@ export function TimelineTrackContent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenu key={element.id}>
|
|
||||||
<ContextMenuTrigger asChild>
|
|
||||||
<div>
|
|
||||||
<TimelineElement
|
<TimelineElement
|
||||||
|
key={element.id}
|
||||||
element={element}
|
element={element}
|
||||||
track={track}
|
track={track}
|
||||||
zoomLevel={zoomLevel}
|
zoomLevel={zoomLevel}
|
||||||
@ -893,27 +883,6 @@ export function TimelineTrackContent({
|
|||||||
onElementMouseDown={handleElementMouseDown}
|
onElementMouseDown={handleElementMouseDown}
|
||||||
onElementClick={handleElementClick}
|
onElementClick={handleElementClick}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</ContextMenuTrigger>
|
|
||||||
<ContextMenuContent>
|
|
||||||
<ContextMenuItem onClick={handleElementSplit}>
|
|
||||||
<Scissors className="h-4 w-4 mr-2" />
|
|
||||||
Split at playhead
|
|
||||||
</ContextMenuItem>
|
|
||||||
<ContextMenuItem onClick={handleElementDuplicate}>
|
|
||||||
<Copy className="h-4 w-4 mr-2" />
|
|
||||||
Duplicate {element.type === "text" ? "text" : "clip"}
|
|
||||||
</ContextMenuItem>
|
|
||||||
<ContextMenuSeparator />
|
|
||||||
<ContextMenuItem
|
|
||||||
onClick={handleElementDelete}
|
|
||||||
className="text-destructive focus:text-destructive"
|
|
||||||
>
|
|
||||||
<Trash2 className="h-4 w-4 mr-2" />
|
|
||||||
Delete {element.type === "text" ? "text" : "clip"}
|
|
||||||
</ContextMenuItem>
|
|
||||||
</ContextMenuContent>
|
|
||||||
</ContextMenu>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
|
Reference in New Issue
Block a user