diff options
Diffstat (limited to 'apps/web')
| -rw-r--r-- | apps/web/src/components/editor-header.tsx | 17 | ||||
| -rw-r--r-- | apps/web/src/components/editor/project-name-editor.tsx | 110 | ||||
| -rw-r--r-- | apps/web/src/stores/project-store.ts | 13 |
3 files changed, 133 insertions, 7 deletions
diff --git a/apps/web/src/components/editor-header.tsx b/apps/web/src/components/editor-header.tsx index c6cecbe8..6f9f0b2f 100644 --- a/apps/web/src/components/editor-header.tsx +++ b/apps/web/src/components/editor-header.tsx @@ -6,6 +6,7 @@ import { ChevronLeft, Download } from "lucide-react"; import { useProjectStore } from "@/stores/project-store";
import { useTimelineStore } from "@/stores/timeline-store";
import { HeaderBase } from "./header-base";
+import { ProjectNameEditor } from "./editor/project-name-editor";
export function EditorHeader() {
const { activeProject } = useProjectStore();
@@ -24,13 +25,15 @@ export function EditorHeader() { };
const leftContent = (
- <Link
- href="/"
- className="font-medium tracking-tight flex items-center gap-2 hover:opacity-80 transition-opacity"
- >
- <ChevronLeft className="h-4 w-4" />
- <span className="text-sm">{activeProject?.name || "Loading..."}</span>
- </Link>
+ <div className="flex items-center gap-2">
+ <Link
+ href="/"
+ className="font-medium tracking-tight flex items-center gap-2 hover:opacity-80 transition-opacity"
+ >
+ <ChevronLeft className="h-4 w-4" />
+ </Link>
+ <ProjectNameEditor />
+ </div>
);
const centerContent = (
diff --git a/apps/web/src/components/editor/project-name-editor.tsx b/apps/web/src/components/editor/project-name-editor.tsx new file mode 100644 index 00000000..aa0141ae --- /dev/null +++ b/apps/web/src/components/editor/project-name-editor.tsx @@ -0,0 +1,110 @@ +"use client"; + +import { useState, useRef, useEffect } from "react"; +import { Input } from "../ui/input"; +import { useProjectStore } from "@/stores/project-store"; +import { Edit2, Check, X } from "lucide-react"; +import { Button } from "../ui/button"; + +interface ProjectNameEditorProps { + className?: string; +} + +export function ProjectNameEditor({ className }: ProjectNameEditorProps) { + const { activeProject, updateProjectName } = useProjectStore(); + const [isEditing, setIsEditing] = useState(false); + const [editValue, setEditValue] = useState(""); + const inputRef = useRef<HTMLInputElement>(null); + + useEffect(() => { + if (activeProject) { + setEditValue(activeProject.name); + } + }, [activeProject]); + + useEffect(() => { + if (isEditing && inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, [isEditing]); + + const handleStartEdit = () => { + if (activeProject) { + setEditValue(activeProject.name); + setIsEditing(true); + } + }; + + const handleSave = () => { + if (editValue.trim()) { + updateProjectName(editValue.trim()); + setIsEditing(false); + } + }; + + const handleCancel = () => { + if (activeProject) { + setEditValue(activeProject.name); + } + setIsEditing(false); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + handleSave(); + } else if (e.key === "Escape") { + handleCancel(); + } + }; + + if (!activeProject) { + return <span className="text-sm text-muted-foreground">Loading...</span>; + } + + if (isEditing) { + return ( + <div className="flex items-center gap-1"> + <Input + ref={inputRef} + value={editValue} + onChange={(e) => setEditValue(e.target.value)} + onKeyDown={handleKeyDown} + className="h-7 text-sm px-3 py-1 min-w-[200px]" + size={1} + /> + <Button + size="sm" + variant="text" + onClick={handleSave} + className="h-7 w-7 p-0" + disabled={!editValue.trim()} + > + <Check className="h-3 w-3" /> + </Button> + <Button + size="sm" + variant="text" + onClick={handleCancel} + className="h-7 w-7 p-0" + > + <X className="h-3 w-3" /> + </Button> + </div> + ); + } + + return ( + <div className="flex items-center gap-1 group"> + <span className="text-sm font-medium">{activeProject.name}</span> + <Button + size="sm" + variant="text" + onClick={handleStartEdit} + className="h-6 w-6 p-0 opacity-0 group-hover:opacity-100 transition-opacity" + > + <Edit2 className="h-3 w-3" /> + </Button> + </div> + ); +}
\ No newline at end of file diff --git a/apps/web/src/stores/project-store.ts b/apps/web/src/stores/project-store.ts index 734c07e2..0d0812cd 100644 --- a/apps/web/src/stores/project-store.ts +++ b/apps/web/src/stores/project-store.ts @@ -7,6 +7,7 @@ interface ProjectStore { // Actions
createNewProject: (name: string) => void;
closeProject: () => void;
+ updateProjectName: (name: string) => void;
}
export const useProjectStore = create<ProjectStore>((set) => ({
@@ -25,4 +26,16 @@ export const useProjectStore = create<ProjectStore>((set) => ({ closeProject: () => {
set({ activeProject: null });
},
+
+ updateProjectName: (name: string) => {
+ set((state) => ({
+ activeProject: state.activeProject
+ ? {
+ ...state.activeProject,
+ name,
+ updatedAt: new Date(),
+ }
+ : null,
+ }));
+ },
}));
|
