Files
kandev/apps/web/components/task/file-context-menu-items.tsx
T
Corey Fogg d4742089fc feat(web): add manual file upload and download to the Files panel (#3292)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
2026-09-03 08:07:13 +00:00

82 lines
2.7 KiB
TypeScript

"use client";
import { IconDownload, IconPencil, IconTrash, IconUpload } from "@tabler/icons-react";
import { ContextMenuItem, ContextMenuSeparator } from "@kandev/ui/context-menu";
import { useTranslation } from "react-i18next";
import type { FileTreeNode } from "@/lib/types/backend";
export type FileContextMenuItemsProps = {
node: FileTreeNode;
isBulk: boolean;
selectedCount: number;
onDeleteFile?: (path: string) => Promise<boolean>;
onRenameFile?: (oldPath: string, newPath: string) => Promise<boolean>;
onDownloadFile?: (path: string) => Promise<boolean>;
onUploadFilesHere?: (path: string) => void;
onStartRename: () => void;
onDelete: (event: Event) => void;
};
/**
* Which node-scoped actions apply, resolved once so the render stays flat.
*
* Download and upload are exact inverses: a file can be downloaded, a folder can
* receive an upload, and a multi-selection supports neither because it has no
* single subject or destination.
*/
function resolveActions({
node,
isBulk,
onRenameFile,
onDownloadFile,
onUploadFilesHere,
}: FileContextMenuItemsProps) {
return {
showRename: !!onRenameFile && !isBulk,
download: !node.is_dir && !isBulk ? onDownloadFile : undefined,
upload: node.is_dir && !isBulk ? onUploadFilesHere : undefined,
};
}
export function FileContextMenuItems(props: FileContextMenuItemsProps) {
const { node, isBulk, selectedCount, onDeleteFile, onStartRename, onDelete } = props;
const { t } = useTranslation();
const { showRename, download, upload } = resolveActions(props);
const deleteLabel = isBulk
? t("task:deleteItemsLabel", { count: selectedCount })
: t("task:delete");
const hasLeadingItem = showRename || !!onDeleteFile;
return (
<>
{onDeleteFile && (
<ContextMenuItem variant="destructive" onSelect={onDelete}>
<IconTrash className="h-3.5 w-3.5" />
{deleteLabel}
</ContextMenuItem>
)}
{showRename && onDeleteFile && <ContextMenuSeparator />}
{showRename && (
<ContextMenuItem onSelect={onStartRename}>
<IconPencil className="h-3.5 w-3.5" />
{t("task:rename")}
</ContextMenuItem>
)}
{download && hasLeadingItem && <ContextMenuSeparator />}
{download && (
<ContextMenuItem onSelect={() => void download(node.path)}>
<IconDownload className="h-3.5 w-3.5" />
{t("task:download")}
</ContextMenuItem>
)}
{upload && hasLeadingItem && <ContextMenuSeparator />}
{upload && (
<ContextMenuItem onSelect={() => upload(node.path)}>
<IconUpload className="h-3.5 w-3.5" />
{t("task:uploadFilesHere")}
</ContextMenuItem>
)}
</>
);
}