diff options
| author | iza <59828082+izadoesdev@users.noreply.github.com> | 2025-06-23 17:49:20 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-23 17:49:20 +0300 |
| commit | c8f92a76e30fba860b30a9b528e5b42291d2d54a (patch) | |
| tree | c2101ee046b5c557ff343ec4ee2b7e3c1d55d6ae /apps/web/src | |
| parent | 14a720256f6cd17aca0e3e150ddef7977f46a4e1 (diff) | |
| parent | 12d26c574b4517f358d97d130b0838f4868a8b85 (diff) | |
| download | opencut-c8f92a76e30fba860b30a9b528e5b42291d2d54a.tar.gz | |
Merge pull request #16 from aashishparuvada/main
enabled-clip-selection-and-deletion
Diffstat (limited to 'apps/web/src')
| -rw-r--r-- | apps/web/src/components/editor/timeline.tsx | 35 | ||||
| -rw-r--r-- | apps/web/src/stores/timeline-store.ts | 13 |
2 files changed, 40 insertions, 8 deletions
diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 1d2df66d..a16284ab 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -33,7 +33,7 @@ export function Timeline() { // Timeline shows all tracks (video, audio, effects) and their clips.
// You can drag media here to add it to your project.
// Clips can be trimmed, deleted, and moved.
- const { tracks, addTrack, addClipToTrack, removeTrack, toggleTrackMute, removeClipFromTrack, moveClipToTrack, getTotalDuration } =
+ const { tracks, addTrack, addClipToTrack, removeTrack, toggleTrackMute, removeClipFromTrack, moveClipToTrack, getTotalDuration, selectedClip, clearSelectedClip } =
useTimelineStore();
const { mediaItems, addMediaItem } = useMediaStore();
const { currentTime, duration, seek, setDuration, isPlaying, play, pause, toggle } = usePlaybackStore();
@@ -67,6 +67,18 @@ export function Timeline() { }
}, [contextMenu]);
+ // Keyboard event for deleting selected clip
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if ((e.key === "Delete" || e.key === "Backspace") && selectedClip) {
+ removeClipFromTrack(selectedClip.trackId, selectedClip.clipId);
+ clearSelectedClip();
+ }
+ };
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [selectedClip, removeClipFromTrack, clearSelectedClip]);
+
const handleDragEnter = (e: React.DragEvent) => {
// When something is dragged over the timeline, show overlay
e.preventDefault();
@@ -699,6 +711,8 @@ function TimelineTrackContent({ addClipToTrack,
removeClipFromTrack,
toggleTrackMute,
+ selectedClip,
+ selectClip,
} = useTimelineStore();
const { currentTime } = usePlaybackStore();
const [isDropping, setIsDropping] = useState(false);
@@ -1261,11 +1275,19 @@ function TimelineTrackContent({ );
const clipLeft = clip.startTime * 50 * zoomLevel;
+ // Correctly declare isSelected inside the map
+ const isSelected = selectedClip && selectedClip.trackId === track.id && selectedClip.clipId === clip.id;
+
return (
<div
key={clip.id}
- className={`timeline-clip absolute h-full rounded-sm border transition-all duration-200 ${getTrackColor(track.type)} flex items-center py-3 min-w-[80px] overflow-hidden group hover:shadow-lg`}
+ className={`timeline-clip absolute h-full rounded-sm border transition-all duration-200 ${getTrackColor(track.type)} flex items-center py-3 min-w-[80px] overflow-hidden group hover:shadow-lg ${isSelected ? "ring-2 ring-blue-500 z-10" : ""}`}
style={{ width: `${clipWidth}px`, left: `${clipLeft}px` }}
+ onClick={(e) => {
+ e.stopPropagation();
+ selectClip(track.id, clip.id);
+ }}
+ tabIndex={0}
onContextMenu={(e) => {
e.preventDefault();
e.stopPropagation();
@@ -1342,16 +1364,13 @@ function TimelineTrackContent({ }}
>
<div
- className={`absolute -top-2 left-1/2 transform -translate-x-1/2 w-3 h-3 rounded-full border-2 border-white shadow-md ${wouldOverlap ? "bg-red-500" : "bg-blue-500"
- }`}
+ className={`absolute -top-2 left-1/2 transform -translate-x-1/2 w-3 h-3 rounded-full border-2 border-white shadow-md ${wouldOverlap ? "bg-red-500" : "bg-blue-500"}`}
/>
<div
- className={`absolute -bottom-2 left-1/2 transform -translate-x-1/2 w-3 h-3 rounded-full border-2 border-white shadow-md ${wouldOverlap ? "bg-red-500" : "bg-blue-500"
- }`}
+ className={`absolute -bottom-2 left-1/2 transform -translate-x-1/2 w-3 h-3 rounded-full border-2 border-white shadow-md ${wouldOverlap ? "bg-red-500" : "bg-blue-500"}`}
/>
<div
- className={`absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-xs text-white px-1 py-0.5 rounded whitespace-nowrap ${wouldOverlap ? "bg-red-500" : "bg-blue-500"
- }`}
+ className={`absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-xs text-white px-1 py-0.5 rounded whitespace-nowrap ${wouldOverlap ? "bg-red-500" : "bg-blue-500"}`}
>
{wouldOverlap ? "⚠️" : ""}
{dropPosition.toFixed(1)}s
diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts index c569ae11..ea6fc35e 100644 --- a/apps/web/src/stores/timeline-store.ts +++ b/apps/web/src/stores/timeline-store.ts @@ -21,6 +21,11 @@ export interface TimelineTrack { interface TimelineStore {
tracks: TimelineTrack[];
+ // Selection
+ selectedClip: { trackId: string; clipId: string } | null;
+ selectClip: (trackId: string, clipId: string) => void;
+ clearSelectedClip: () => void;
+
// Actions
addTrack: (type: "video" | "audio" | "effects") => string;
removeTrack: (trackId: string) => void;
@@ -50,6 +55,14 @@ interface TimelineStore { export const useTimelineStore = create<TimelineStore>((set, get) => ({
tracks: [],
+ selectedClip: null,
+
+ selectClip: (trackId, clipId) => {
+ set({ selectedClip: { trackId, clipId } });
+ },
+ clearSelectedClip: () => {
+ set({ selectedClip: null });
+ },
addTrack: (type) => {
const newTrack: TimelineTrack = {
|
