Files
myDrive/src/components/ContextMenu/ContextMenu.tsx
T
2025-02-20 16:47:53 -05:00

245 lines
6.9 KiB
TypeScript

import React, { memo, useEffect, useState } from "react";
import { useClickOutOfBounds, useUtils } from "../../hooks/utils";
import TrashIcon from "../../icons/TrashIcon";
import MultiSelectIcon from "../../icons/MultiSelectIcon";
import RenameIcon from "../../icons/RenameIcon";
import ShareIcon from "../../icons/ShareIcon";
import DownloadIcon from "../../icons/DownloadIcon";
import MoveIcon from "../../icons/MoveIcon";
import RestoreIcon from "../../icons/RestoreIcon";
import { FileInterface } from "../../types/file";
import { useNavigate } from "react-router-dom";
import { useActions } from "../../hooks/actions";
import { FolderInterface } from "../../types/folders";
export interface ContextMenuProps {
closeContext: () => void;
contextSelected: {
selected: boolean;
X: number;
Y: number;
};
folderMode?: boolean;
quickItemMode?: boolean;
parentBarMode?: boolean;
file?: FileInterface | null;
folder?: FolderInterface | null;
stopPropagation?: () => void;
}
const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
const [fixedCoords, setFixedCoords] = useState({
X: 0,
Y: 0,
set: false,
});
const {
closeContext,
contextSelected,
folderMode,
file,
quickItemMode,
stopPropagation,
folder,
parentBarMode,
} = props;
const { wrapperRef } = useClickOutOfBounds(closeContext);
const { isTrash, isMedia } = useUtils();
const navigate = useNavigate();
const {
renameItem,
trashItem,
deleteItem,
restoreItem,
openMoveItemModal,
openShareItemModal,
downloadItem,
selectItemMultiSelect,
} = useActions({ quickItemMode });
useEffect(() => {
if (!wrapperRef.current) return;
const modalWidth = wrapperRef.current.clientWidth;
const modalHeight = wrapperRef.current.clientHeight;
const { innerWidth: windowWidth, innerHeight: windowHeight } = window;
let X = contextSelected.X;
let Y = contextSelected.Y;
if (X + modalWidth > windowWidth) {
X = windowWidth - modalWidth - 10;
}
if (Y + modalHeight > windowHeight) {
Y = windowHeight - modalHeight - 10;
}
setFixedCoords({
X,
Y,
set: true,
});
}, [wrapperRef, contextSelected.X, contextSelected.Y]);
const onAction = async (
action:
| "rename"
| "trash"
| "delete"
| "restore"
| "move"
| "share"
| "download"
| "multi-select"
) => {
closeContext();
switch (action) {
case "rename":
await renameItem(file, folder);
break;
case "trash":
await trashItem(file, folder);
break;
case "delete":
await deleteItem(file, folder);
break;
case "restore":
await restoreItem(file, folder);
break;
case "move":
await openMoveItemModal(file, folder);
break;
case "share":
openShareItemModal(file);
break;
case "download":
downloadItem(file, folder);
break;
case "multi-select":
selectItemMultiSelect(file, folder);
}
if (
folder &&
parentBarMode &&
["trash", "delete", "restore"].includes(action)
) {
if (folder.parent === "/") {
navigate("/trash");
} else {
navigate(`/folder-trash/${folder.parent}`);
}
}
};
const outterWrapperClick = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
if ((e.target as HTMLDivElement).id !== "context-wrapper") {
return;
}
closeContext();
};
return (
<div
id="context-wrapper"
className="w-screen dynamic-height absolute top-0 left-0 right-0 bottom-0 z-50 flex justify-center items-center flex-col"
onClick={outterWrapperClick}
>
<div
onClick={stopPropagation}
ref={wrapperRef}
className="fixed min-w-[215px] bg-white shadow-lg rounded-md z-50"
style={
fixedCoords.set
? {
display: "block",
left: `${fixedCoords.X}px`,
top: `${fixedCoords.Y}px`,
}
: { opacity: 0 }
}
>
<div>
{!parentBarMode && (
<div
onClick={() => onAction("multi-select")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary rounded-t-md"
>
<MultiSelectIcon className="w-5 h-5" />
<p className="ml-2.5 text-sm">Multi-select</p>
</div>
)}
{!isTrash && !isMedia && (
<div
onClick={() => onAction("rename")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<RenameIcon className="w-5 h-5" />
<p className="ml-2.5 text-sm">Rename</p>
</div>
)}
{!folderMode && !isTrash && (
<div
onClick={() => onAction("share")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<ShareIcon className="w-5 h-5" />
<p className="ml-2.5 text-sm">Share</p>
</div>
)}
{!isTrash && (
<div
onClick={() => onAction("download")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<DownloadIcon className="w-5 h-5" />
<p className="ml-2.5 text-sm">Download</p>
</div>
)}
{!isTrash && !isMedia && (
<div
onClick={() => onAction("move")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<MoveIcon className="w-5 h-5" />
<p className="ml-2.5 text-sm">Move</p>
</div>
)}
{!isTrash && (
<div
onClick={() => onAction("trash")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary rounded-b-md"
>
<TrashIcon className="w-5 h-5" />
<p className="ml-2.5 text-sm">Trash</p>
</div>
)}
{isTrash && (
<div
onClick={() => onAction("restore")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-primary"
>
<RestoreIcon className="w-5 h-5" />
<p className="ml-2.5 text-sm">Restore</p>
</div>
)}
{isTrash && (
<div
onClick={() => onAction("delete")}
className="text-gray-primary flex flex-row p-4 hover:bg-white-hover hover:text-red-500 rounded-b-md"
>
<TrashIcon className="w-5 h-5" />
<p className="ml-2.5 text-sm">Delete</p>
</div>
)}
</div>
</div>
</div>
);
});
export default ContextMenu;