2025-12-08 22:08:30 +00:00
|
|
|
import React, { useState } from "react";
|
|
|
|
|
import { FolderTreeNode } from "../../api/folders";
|
2025-12-22 15:23:40 +00:00
|
|
|
import {
|
|
|
|
|
useCreateFolder,
|
|
|
|
|
useUpdateFolder,
|
|
|
|
|
useDeleteFolder,
|
|
|
|
|
} from "../../hooks/useFolders";
|
2025-12-08 22:08:30 +00:00
|
|
|
|
|
|
|
|
interface FolderContextMenuProps {
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
folder: FolderTreeNode;
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const FolderContextMenu: React.FC<FolderContextMenuProps> = ({
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
folder,
|
|
|
|
|
onClose,
|
|
|
|
|
}) => {
|
2025-12-22 15:23:40 +00:00
|
|
|
const createFolderMutation = useCreateFolder();
|
|
|
|
|
const updateFolderMutation = useUpdateFolder();
|
|
|
|
|
const deleteFolderMutation = useDeleteFolder();
|
|
|
|
|
|
2025-12-08 22:08:30 +00:00
|
|
|
const [isRenaming, setIsRenaming] = useState(false);
|
|
|
|
|
const [newName, setNewName] = useState(folder.name);
|
|
|
|
|
|
|
|
|
|
const handleDelete = async () => {
|
|
|
|
|
if (!confirm(`Delete "${folder.name}" and all its contents?`)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try {
|
2025-12-22 15:23:40 +00:00
|
|
|
await deleteFolderMutation.mutateAsync(folder.id);
|
2025-12-08 22:08:30 +00:00
|
|
|
onClose();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to delete folder:", error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRename = async () => {
|
|
|
|
|
if (newName.trim() && newName !== folder.name) {
|
2025-12-22 15:23:40 +00:00
|
|
|
try {
|
|
|
|
|
await updateFolderMutation.mutateAsync({
|
|
|
|
|
folderId: folder.id,
|
|
|
|
|
folder: { name: newName },
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to rename folder:", error);
|
|
|
|
|
}
|
2025-12-08 22:08:30 +00:00
|
|
|
}
|
|
|
|
|
setIsRenaming(false);
|
|
|
|
|
onClose();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleCreateSubfolder = async () => {
|
|
|
|
|
try {
|
2025-12-22 15:23:40 +00:00
|
|
|
await createFolderMutation.mutateAsync({
|
2025-12-08 22:08:30 +00:00
|
|
|
name: "New Folder",
|
2026-01-15 22:26:08 +00:00
|
|
|
parentId: folder.id,
|
2025-12-08 22:08:30 +00:00
|
|
|
});
|
|
|
|
|
onClose();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to create subfolder:", error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (isRenaming) {
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
position: "fixed",
|
|
|
|
|
top: y,
|
|
|
|
|
left: x,
|
|
|
|
|
}}
|
2026-01-15 22:26:08 +00:00
|
|
|
className="bg-overlay0 border border-surface1 rounded-md shadow-lg p-2 min-w-[200px] z-50"
|
2025-12-08 22:08:30 +00:00
|
|
|
onClick={(e) => e.stopPropagation()}
|
|
|
|
|
>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={newName}
|
|
|
|
|
onChange={(e) => setNewName(e.target.value)}
|
|
|
|
|
onKeyDown={(e) => {
|
|
|
|
|
if (e.key === "Enter") handleRename();
|
|
|
|
|
if (e.key === "Escape") {
|
|
|
|
|
setIsRenaming(false);
|
|
|
|
|
onClose();
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
onBlur={handleRename}
|
|
|
|
|
autoFocus
|
2026-01-15 22:26:08 +00:00
|
|
|
className="w-full px-2 py-1 bg-surface1 border border-surface1 rounded text-sm text-text focus:outline-none focus:border-accent"
|
2025-12-08 22:08:30 +00:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
position: "fixed",
|
|
|
|
|
top: y,
|
|
|
|
|
left: x,
|
|
|
|
|
}}
|
2026-01-15 22:26:08 +00:00
|
|
|
className="bg-overlay0 border border-surface1 rounded-md shadow-lg py-1 min-w-[160px] z-50"
|
2025-12-08 22:08:30 +00:00
|
|
|
onClick={(e) => e.stopPropagation()}
|
|
|
|
|
>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setIsRenaming(true)}
|
2026-01-15 22:26:08 +00:00
|
|
|
className="w-full text-left px-3 py-1.5 hover:bg-surface1 text-sm text-text transition-colors"
|
2025-12-08 22:08:30 +00:00
|
|
|
>
|
|
|
|
|
Rename
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleCreateSubfolder}
|
2026-01-15 22:26:08 +00:00
|
|
|
className="w-full text-left px-3 py-1.5 hover:bg-surface1 text-sm text-text transition-colors"
|
2025-12-08 22:08:30 +00:00
|
|
|
>
|
|
|
|
|
New Subfolder
|
|
|
|
|
</button>
|
2026-01-15 22:26:08 +00:00
|
|
|
<div className="border-t border-surface1 my-1" />
|
2025-12-08 22:08:30 +00:00
|
|
|
<button
|
|
|
|
|
onClick={handleDelete}
|
2026-01-15 22:26:08 +00:00
|
|
|
className="w-full text-left px-3 py-1.5 hover:bg-danger hover:text-base text-sm text-danger transition-colors"
|
2025-12-08 22:08:30 +00:00
|
|
|
>
|
|
|
|
|
Delete
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|