Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,23 @@ import { cn } from "@superset/ui/utils";
import { useNavigate, useParams } from "@tanstack/react-router";
import { useState } from "react";
import { HiChevronRight, HiMiniPlus } from "react-icons/hi2";
import { LuFolderOpen, LuPalette, LuSettings, LuX } from "react-icons/lu";
import {
LuFolderOpen,
LuPalette,
LuPencil,
LuSettings,
LuX,
} from "react-icons/lu";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { useUpdateProject } from "renderer/react-query/projects/useUpdateProject";
import { navigateToWorkspace } from "renderer/routes/_authenticated/_dashboard/utils/workspace-navigation";
import { useProjectRename } from "renderer/screens/main/hooks/useProjectRename";
import {
PROJECT_COLOR_DEFAULT,
PROJECT_COLORS,
} from "shared/constants/project-colors";
import { STROKE_WIDTH } from "../constants";
import { RenameInput } from "../RenameInput";
import { CloseProjectDialog } from "./CloseProjectDialog";
import { ProjectThumbnail } from "./ProjectThumbnail";

Expand Down Expand Up @@ -57,6 +65,7 @@ export function ProjectHeader({
const navigate = useNavigate();
const params = useParams({ strict: false }) as { workspaceId?: string };
const [isCloseDialogOpen, setIsCloseDialogOpen] = useState(false);
const rename = useProjectRename(projectId, projectName);

const closeProject = electronTrpc.projects.close.useMutation({
onMutate: async ({ id }) => {
Expand Down Expand Up @@ -200,6 +209,11 @@ export function ProjectHeader({
</TooltipContent>
</Tooltip>
<ContextMenuContent>
<ContextMenuItem onSelect={rename.startRename}>
<LuPencil className="size-4 mr-2" strokeWidth={STROKE_WIDTH} />
Rename
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem onSelect={handleOpenInFinder}>
<LuFolderOpen
className="size-4 mr-2"
Expand Down Expand Up @@ -249,22 +263,41 @@ export function ProjectHeader({
)}
>
{/* Main clickable area */}
<button
type="button"
onClick={onToggleCollapse}
className="flex items-center gap-2 flex-1 min-w-0 py-0.5 text-left cursor-pointer"
>
<ProjectThumbnail
projectId={projectId}
projectName={projectName}
projectColor={projectColor}
githubOwner={githubOwner}
/>
<span className="truncate">{projectName}</span>
<span className="text-xs text-muted-foreground tabular-nums">
({workspaceCount})
</span>
</button>
{rename.isRenaming ? (
<div className="flex items-center gap-2 flex-1 min-w-0 py-0.5">
<ProjectThumbnail
projectId={projectId}
projectName={projectName}
projectColor={projectColor}
githubOwner={githubOwner}
/>
<RenameInput
value={rename.renameValue}
onChange={rename.setRenameValue}
onSubmit={rename.submitRename}
onCancel={rename.cancelRename}
className="h-6 px-1 py-0 text-sm -ml-1 font-medium bg-transparent border-none outline-none flex-1 min-w-0"
/>
</div>
) : (
<button
type="button"
onClick={onToggleCollapse}
onDoubleClick={rename.startRename}
className="flex items-center gap-2 flex-1 min-w-0 py-0.5 text-left cursor-pointer"
>
<ProjectThumbnail
projectId={projectId}
projectName={projectName}
projectColor={projectColor}
githubOwner={githubOwner}
/>
<span className="truncate">{projectName}</span>
<span className="text-xs text-muted-foreground tabular-nums font-normal">
({workspaceCount})
</span>
</button>
)}

{/* Add workspace button */}
<Tooltip delayDuration={500}>
Expand Down Expand Up @@ -304,6 +337,11 @@ export function ProjectHeader({
</div>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem onSelect={rename.startRename}>
<LuPencil className="size-4 mr-2" strokeWidth={STROKE_WIDTH} />
Rename
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem onSelect={handleOpenInFinder}>
<LuFolderOpen className="size-4 mr-2" strokeWidth={STROKE_WIDTH} />
Open in Finder
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useEffect, useRef } from "react";

interface RenameInputProps {
value: string;
onChange: (value: string) => void;
onSubmit: () => void;
onCancel: () => void;
className?: string;
}

export function RenameInput({
value,
onChange,
onSubmit,
onCancel,
className,
}: RenameInputProps) {
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
// Delay to allow context menu to fully close
const timer = setTimeout(() => {
if (inputRef.current) {
inputRef.current.focus();
inputRef.current.select();
}
}, 100);
return () => clearTimeout(timer);
}, []);

const handleKeyDown = (e: React.KeyboardEvent) => {
e.stopPropagation();
if (e.key === "Enter") {
e.preventDefault();
onSubmit();
} else if (e.key === "Escape") {
e.preventDefault();
onCancel();
}
};

return (
<input
ref={inputRef}
type="text"
value={value}
onChange={(e) => onChange(e.target.value)}
onBlur={onSubmit}
onKeyDown={handleKeyDown}
onClick={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
className={className}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { RenameInput } from "./RenameInput";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useProjectRename } from "./useProjectRename";
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useEffect, useState } from "react";
import { useUpdateProject } from "renderer/react-query/projects/useUpdateProject";

export function useProjectRename(projectId: string, projectName: string) {
const [isRenaming, setIsRenaming] = useState(false);
const [renameValue, setRenameValue] = useState(projectName);
const updateProject = useUpdateProject();

useEffect(() => {
setRenameValue(projectName);
}, [projectName]);

const startRename = () => {
setIsRenaming(true);
};

const submitRename = () => {
const trimmedValue = renameValue.trim();
if (trimmedValue && trimmedValue !== projectName) {
updateProject.mutate({
id: projectId,
patch: { name: trimmedValue },
});
} else {
setRenameValue(projectName);
}
setIsRenaming(false);
};

const cancelRename = () => {
setRenameValue(projectName);
setIsRenaming(false);
};

const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
e.preventDefault();
submitRename();
} else if (e.key === "Escape") {
e.preventDefault();
cancelRename();
}
};

return {
isRenaming,
renameValue,
setRenameValue,
startRename,
submitRename,
cancelRename,
handleKeyDown,
};
}
Loading