diff options
| author | Maze Winther <mazewinther@gmail.com> | 2025-06-22 14:50:22 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2025-06-22 14:50:22 +0200 |
| commit | cb2a7484ed3c6797a33acc2d4ab21130f8f00008 (patch) | |
| tree | cf33e28081945558309d1d0ca4b4e222cd81fb9e /apps/web | |
| parent | 6c8d6d99a94cdd1317872efc41028a0706a9d52d (diff) | |
| download | opencut-cb2a7484ed3c6797a33acc2d4ab21130f8f00008.tar.gz | |
add drag and drop to the timeline
Diffstat (limited to 'apps/web')
| -rw-r--r-- | apps/web/src/components/editor/media-panel.tsx | 59 | ||||
| -rw-r--r-- | apps/web/src/components/editor/timeline.tsx | 22 | ||||
| -rw-r--r-- | apps/web/src/components/ui/drag-overlay.tsx | 25 | ||||
| -rw-r--r-- | apps/web/src/hooks/use-drag-drop.ts | 52 |
4 files changed, 110 insertions, 48 deletions
diff --git a/apps/web/src/components/editor/media-panel.tsx b/apps/web/src/components/editor/media-panel.tsx index 1e5aa465..565a4e7d 100644 --- a/apps/web/src/components/editor/media-panel.tsx +++ b/apps/web/src/components/editor/media-panel.tsx @@ -2,14 +2,20 @@ import { Button } from "../ui/button"; import { AspectRatio } from "../ui/aspect-ratio"; +import { DragOverlay } from "../ui/drag-overlay"; import { useMediaStore } from "@/stores/media-store"; -import { Plus, Image, Video, Music, Upload } from "lucide-react"; -import { useState, useRef } from "react"; +import { Plus, Image, Video, Music } from "lucide-react"; +import { useDragDrop } from "@/hooks/use-drag-drop"; export function MediaPanel() { const { mediaItems, addMediaItem } = useMediaStore(); - const [isDragOver, setIsDragOver] = useState(false); - const dragCounterRef = useRef(0); + + const { isDragOver, dragProps } = useDragDrop({ + onDrop: (files) => { + // TODO: Handle file drop functionality + console.log("Files dropped on media panel:", files); + }, + }); const handleAddSampleMedia = () => { // Just for testing - add a sample media item @@ -30,55 +36,14 @@ export function MediaPanel() { } }; - const handleDragEnter = (e: React.DragEvent) => { - e.preventDefault(); - dragCounterRef.current += 1; - if (!isDragOver) { - setIsDragOver(true); - } - }; - - const handleDragOver = (e: React.DragEvent) => { - e.preventDefault(); - }; - - const handleDragLeave = (e: React.DragEvent) => { - e.preventDefault(); - dragCounterRef.current -= 1; - if (dragCounterRef.current === 0) { - setIsDragOver(false); - } - }; - - const handleDrop = (e: React.DragEvent) => { - e.preventDefault(); - setIsDragOver(false); - dragCounterRef.current = 0; - // TODO: Handle file drop functionality - }; - return ( <div className={`h-full overflow-y-auto transition-colors duration-200 relative ${ isDragOver ? "bg-accent/30 border-accent" : "" }`} - onDragEnter={handleDragEnter} - onDragOver={handleDragOver} - onDragLeave={handleDragLeave} - onDrop={handleDrop} + {...dragProps} > - {/* Drag Overlay */} - {isDragOver && ( - <div className="absolute inset-0 bg-accent/20 backdrop-blur-lg border-2 border-dashed border-accent rounded-lg flex items-center justify-center z-10 pointer-events-none"> - <div className="text-center"> - <Upload className="h-8 w-8 text-accent mx-auto mb-2" /> - <p className="text-sm font-medium text-accent">Drop files here</p> - <p className="text-xs text-muted-foreground"> - Images, videos, and audio files - </p> - </div> - </div> - )} + <DragOverlay isVisible={isDragOver} /> <div className="space-y-4 p-2 h-full"> {/* Media Grid */} diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index da44022e..dd312764 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -17,13 +17,33 @@ import { TooltipTrigger,
TooltipProvider,
} from "../ui/tooltip";
+import { DragOverlay } from "../ui/drag-overlay";
import { useTimelineStore, type TimelineTrack } from "@/stores/timeline-store";
+import { useDragDrop } from "@/hooks/use-drag-drop";
export function Timeline() {
const { tracks, addTrack } = useTimelineStore();
+ const { isDragOver, dragProps } = useDragDrop({
+ onDrop: (files) => {
+ // TODO: Handle file drop functionality for timeline
+ console.log("Files dropped on timeline:", files);
+ },
+ });
+
return (
- <div className="h-full flex flex-col">
+ <div
+ className={`h-full flex flex-col transition-colors duration-200 relative ${
+ isDragOver ? "bg-accent/30 border-accent" : ""
+ }`}
+ {...dragProps}
+ >
+ <DragOverlay
+ isVisible={isDragOver}
+ title="Drop files here"
+ description="Add media to timeline tracks"
+ />
+
{/* Toolbar */}
<div className="border-b flex items-center px-2 py-1 gap-1">
<TooltipProvider delayDuration={500}>
diff --git a/apps/web/src/components/ui/drag-overlay.tsx b/apps/web/src/components/ui/drag-overlay.tsx new file mode 100644 index 00000000..ba6fe08f --- /dev/null +++ b/apps/web/src/components/ui/drag-overlay.tsx @@ -0,0 +1,25 @@ +import { Upload } from "lucide-react";
+
+interface DragOverlayProps {
+ isVisible: boolean;
+ title?: string;
+ description?: string;
+}
+
+export function DragOverlay({
+ isVisible,
+ title = "Drop files here",
+ description = "Images, videos, and audio files",
+}: DragOverlayProps) {
+ if (!isVisible) return null;
+
+ return (
+ <div className="absolute inset-0 bg-accent/20 backdrop-blur-lg border-2 border-dashed border-accent flex items-center justify-center z-10 pointer-events-none">
+ <div className="text-center">
+ <Upload className="h-8 w-8 text-accent mx-auto mb-2" />
+ <p className="text-sm font-medium text-accent">{title}</p>
+ <p className="text-xs text-muted-foreground">{description}</p>
+ </div>
+ </div>
+ );
+}
diff --git a/apps/web/src/hooks/use-drag-drop.ts b/apps/web/src/hooks/use-drag-drop.ts new file mode 100644 index 00000000..6b0c5a9f --- /dev/null +++ b/apps/web/src/hooks/use-drag-drop.ts @@ -0,0 +1,52 @@ +import { useState, useRef } from "react";
+
+interface UseDragDropOptions {
+ onDrop?: (files: FileList) => void;
+}
+
+export function useDragDrop(options: UseDragDropOptions = {}) {
+ const [isDragOver, setIsDragOver] = useState(false);
+ const dragCounterRef = useRef(0);
+
+ const handleDragEnter = (e: React.DragEvent) => {
+ e.preventDefault();
+ dragCounterRef.current += 1;
+ if (!isDragOver) {
+ setIsDragOver(true);
+ }
+ };
+
+ const handleDragOver = (e: React.DragEvent) => {
+ e.preventDefault();
+ };
+
+ const handleDragLeave = (e: React.DragEvent) => {
+ e.preventDefault();
+ dragCounterRef.current -= 1;
+ if (dragCounterRef.current === 0) {
+ setIsDragOver(false);
+ }
+ };
+
+ const handleDrop = (e: React.DragEvent) => {
+ e.preventDefault();
+ setIsDragOver(false);
+ dragCounterRef.current = 0;
+
+ if (options.onDrop && e.dataTransfer.files) {
+ options.onDrop(e.dataTransfer.files);
+ }
+ };
+
+ const dragProps = {
+ onDragEnter: handleDragEnter,
+ onDragOver: handleDragOver,
+ onDragLeave: handleDragLeave,
+ onDrop: handleDrop,
+ };
+
+ return {
+ isDragOver,
+ dragProps,
+ };
+}
|
