Skip to content

Commit

Permalink
feat(Drag-and-Drop): enable moving files and folders by dragging to t…
Browse files Browse the repository at this point in the history
…arget directories

Allows users to organize files effortlessly by dragging selected items into desired folders, enhancing overall usability.
  • Loading branch information
Saifullah-dev committed Nov 14, 2024
1 parent cfe891c commit 3991d4f
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 44 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ An open-source React.js package for easy integration of a file manager into appl
- **Toolbar & Context Menu**: Access all common actions (upload, download, delete, copy, move, rename, etc.) via the toolbar or right-click for the same options in the context menu.
- **Multi-Selection**: Select multiple files and folders at once to perform bulk actions like delete, copy, move, or download.
- **Keyboard Shortcuts**: Quickly perform file operations like copy, paste, delete, and more using intuitive keyboard shortcuts.
- **Drag-and-Drop**: Move selected files and folders by dragging them to the desired directory, making file organization effortless.

![React File Manager](https://github.com/user-attachments/assets/e68f750b-86bf-450d-b27e-fd3dedebf1bd)

Expand Down Expand Up @@ -89,7 +90,7 @@ type File = {

| Name | Type | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `acceptedFileTypes` | string | (Optional) A comma-separated list of allowed file extensions for uploading specific file types (e.g., `.txt, .png, .pdf`). If omitted, all file types are accepted. |
| `acceptedFileTypes` | string | (Optional) A comma-separated list of allowed file extensions for uploading specific file types (e.g., `.txt, .png, .pdf`). If omitted, all file types are accepted. |
| `enableFilePreview` | boolean | A boolean flag indicating whether to use the default file previewer in the file manager `default: true`. |
| `filePreviewPath` | string | The base URL for file previews e.g.`https://example.com`, file path will be appended automatically to it i.e. `https://example.com/yourFilePath`. |
| `fileUploadConfig` | { url: string; headers?: { [key: string]: string } } | Configuration object for file uploads. It includes the upload URL (`url`) and an optional `headers` object for setting custom HTTP headers in the upload request. The `headers` object can accept any standard or custom headers required by the server. Example: `{ url: "https://example.com/fileupload", headers: { Authorization: "Bearer" + TOKEN, "X-Custom-Header": "value" } }` |
Expand Down
1 change: 1 addition & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ An open-source React.js package for easy integration of a file manager into appl
- **Toolbar & Context Menu**: Access all common actions (upload, download, delete, copy, move, rename, etc.) via the toolbar or right-click for the same options in the context menu.
- **Multi-Selection**: Select multiple files and folders at once to perform bulk actions like delete, copy, move, or download.
- **Keyboard Shortcuts**: Quickly perform file operations like copy, paste, delete, and more using intuitive keyboard shortcuts.
- **Drag-and-Drop**: Move selected files and folders by dragging them to the desired directory, making file organization effortless.

![React File Manager](https://github.com/user-attachments/assets/e68f750b-86bf-450d-b27e-fd3dedebf1bd)

Expand Down
80 changes: 38 additions & 42 deletions frontend/src/FileManager/FileList/FileItem.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { FaRegFile, FaRegFolderOpen } from "react-icons/fa6";
import { useFileIcons } from "../../hooks/useFileIcons";
import CreateFolderAction from "../Actions/CreateFolder/CreateFolder.action";
Expand All @@ -11,6 +11,8 @@ import { useClipBoard } from "../../contexts/ClipboardContext";
import { useLayout } from "../../contexts/LayoutContext";
import Checkbox from "../../components/Checkbox/Checkbox";

const dragIconSize = 50;

const FileItem = ({
index,
file,
Expand All @@ -28,13 +30,16 @@ const FileItem = ({
const [lastClickTime, setLastClickTime] = useState(0);
const [checkboxClassName, setCheckboxClassName] = useState("hidden");
const [dropZoneClass, setDropZoneClass] = useState("");
const [tooltipPosition, setTooltipPosition] = useState(null);

const { activeLayout } = useLayout();
const iconSize = activeLayout === "grid" ? 48 : 20;
const fileIcons = useFileIcons(iconSize);
const { setCurrentPath, currentPathFiles } = useFileNavigation();
const { setSelectedFiles } = useSelection();
const { clipBoard, handleCutCopy, setClipBoard, handlePasting } = useClipBoard();
const dragIconRef = useRef(null);
const dragIcons = useFileIcons(dragIconSize);

const isFileMoving =
clipBoard?.isMoving &&
Expand Down Expand Up @@ -136,28 +141,9 @@ const FileItem = ({
};
//

const createDragElement = () => {
const dragImage = document.createElement("div");
dragImage.style.position = "absolute";
dragImage.style.top = "-9999px";
dragImage.style.left = "-9999px";
dragImage.style.pointerEvents = "none";
dragImage.style.backgroundColor = "#ddd";
dragImage.style.padding = "8px 16px";
dragImage.style.borderRadius = "4px";
dragImage.style.fontSize = "14px";
dragImage.style.color = "#333";
dragImage.style.boxShadow = "0px 4px 12px rgba(0, 0, 0, 0.1)";
dragImage.innerHTML = `Moving File`;

document.body.appendChild(dragImage);
return dragImage;
};

const handleDragStart = (e) => {
const dragImage = createDragElement();
e.dataTransfer.setDragImage(dragImage, 60, 25);
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.setDragImage(dragIconRef.current, 30, 50);
e.dataTransfer.effectAllowed = "copy";
handleCutCopy(true);
};

Expand All @@ -168,7 +154,8 @@ const FileItem = ({
if (fileSelected || !file.isDirectory) {
e.dataTransfer.dropEffect = "none";
} else {
e.dataTransfer.dropEffect = "move";
setTooltipPosition({ x: e.clientX, y: e.clientY + 12 });
e.dataTransfer.dropEffect = "copy";
setDropZoneClass("file-drop-zone");
}
};
Expand All @@ -177,6 +164,7 @@ const FileItem = ({
// To stay in dragging state for the child elements of the target drop-zone
if (!e.currentTarget.contains(e.relatedTarget)) {
setDropZoneClass((prev) => (prev ? "" : prev));
setTooltipPosition(null);
}
};

Expand All @@ -186,6 +174,7 @@ const FileItem = ({

handlePasting(file);
setDropZoneClass((prev) => (prev ? "" : prev));
setTooltipPosition(null);
};

useEffect(() => {
Expand Down Expand Up @@ -261,27 +250,34 @@ const FileItem = ({
<div className="size">{file?.size > 0 ? getDataSize(file?.size) : ""}</div>
</>
)}

{/* Drag Icon & Tooltip Setup */}
{tooltipPosition && (
<div
style={{
top: `${tooltipPosition.y}px`,
left: `${tooltipPosition.x}px`,
}}
className="drag-move-tooltip"
>
Move to <span className="drop-zone-file-name">{file.name}</span>
</div>
)}

<div ref={dragIconRef} className="drag-icon">
{file.isDirectory ? (
<FaRegFolderOpen size={dragIconSize} />
) : (
<>
{dragIcons[file.name?.split(".").pop()?.toLowerCase()] ?? (
<FaRegFile size={dragIconSize} />
)}
</>
)}
</div>
{/* Drag Icon & Tooltip Setup */}
</div>
);
};

export default FileItem;

// Drag & Drop Files to move
// There can be 2 types of d&d situations
// 1. Single file/folder move
// 2. Multiple files/folders move
// We're gonna need to show 2 types of images based on the situation
// For Single, we'll show exactly that file/folder image without the name.
// For Multiple, we'll show a stacked image for multiple files and add a counter on top of it.
// And when dragged over a folder, we'll show toolip "Move to FolderName".
// For dragging over a file case, we'll not show hover (Dragenter) for it.
// That is it for the UI.

// On drag start from any of the selected files
// We'll set Drag Image based on the no. of selected files
// For the target i.e. Folders only, when drag enter event fires
// We'll set it's hover class to be true and add it to the target drop state
// On dragend we'll see if the source was dropped over a target by checking target state
// If yes, then we'll trigger onPaste callback with selectedFiles and destination folder
// If no, then we'll simply do nothing
31 changes: 30 additions & 1 deletion frontend/src/FileManager/FileList/FileList.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,42 @@
padding: 8px;
padding-right: 4px;

.drag-move-tooltip {
background-color: white;
font-size: .78em;
position: fixed;
text-wrap: nowrap;
border: 1px dashed black;
padding: 1px 5px 2px 24px;
border-radius: 3px;
font-weight: bold;
color: $primary-color-dark;
z-index: 2;

.drop-zone-file-name {
color: rgb(48, 48, 48);
}
}

.file-item-container {
border-radius: 5px;
margin: 5px 0;

.drag-icon {
position: absolute !important;
top: -1000px;
left: -1000px;
z-Index: -1;
border-radius: 4px;
position: relative;
color: white;
background-color: $primary-color;
padding: 3px 8px;
}
}

.file-item-container.file-drop-zone {
background-color: rgb(0, 0, 0, 0.04) !important;
background-color: rgb(0, 0, 0, 0.08) !important;
}

.file-item {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/styles/_variables.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// App Colors
$secondary-color: #6155b4;
$primary-color: #6155b4;
$primary-color-dark: #40377a;
$secondary-color-lighter: #5549a3;
$border-color: #cfcfcf;
$item-hover-color: rgb(0, 0, 0, 0.05);
Expand Down

0 comments on commit 3991d4f

Please sign in to comment.