diff --git a/README.md b/README.md index 036f739..d56ea60 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ An open-source React.js package for easy integration of a file manager into appl - **Navigation**: Use the breadcrumb trail and sidebar navigation pane for quick directory traversal. - **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.  @@ -110,6 +111,26 @@ type File = { | `onRename` | (file: [File](#-file-structure), newName: string) => void | A callback function triggered when a file or folder is renamed. | | `width` | string \| number | The width of the component `default: 100%`. Can be a string (e.g., `'100%'`, `'10rem'`) or a number (in pixels). | +## ⌨️ Keyboard Shortcuts + +| **Action** | **Shortcut** | +| ------------------------------ | ------------------ | +| New Folder | `Alt + Shift + N` | +| Upload Files | `CTRL + U` | +| Cut | `CTRL + X` | +| Copy | `CTRL + C` | +| Paste | `CTRL + V` | +| Rename | `F2` | +| Download | `CTRL + D` | +| Delete | `DEL` | +| Select All Files | `CTRL + A` | +| Jump to First File in the List | `Home` | +| Jump to Last File in the List | `End` | +| Switch to List Layout | `CTRL + Shift + 1` | +| Switch to Grid Layout | `CTRL + Shift + 2` | +| Refresh File List | `F5` | +| Clear Selection | `Esc` | + ## 🤝 Contributing Contributions are welcome! To contribute: diff --git a/backend/Readme.md b/backend/Readme.md index ab67d7f..61da1bc 100644 --- a/backend/Readme.md +++ b/backend/Readme.md @@ -65,12 +65,12 @@ The backend supports the following file system operations: - **📁 Create a Folder**: `/folder` - **⬆️ Upload a File**: `/upload` -- **📋 Copy a File/Folder**: `/copy` +- **📋 Copy File(s) or Folder(s)**: `/copy` - **📂 Get All Files/Folders**: `/` -- **⬇️ Download a File**: `/download/:id` -- **📤 Move a File/Folder**: `/move` -- **✏️ Rename a File/Folder**: `/rename` -- **🗑️ Delete a File/Folder**: `/:id` +- **⬇️ Download File(s) or Folder(s)**: `/download` +- **📤 Move File(s) or Folder(s)**: `/move` +- **✏️ Rename a File or Folder**: `/rename` +- **🗑️ Delete File(s) or Folder(s)**: `/` Refer to the [Swagger Documentation](http://localhost:3000/api-docs/) for detailed request/response formats. diff --git a/backend/app/controllers/deleteItem.controller.js b/backend/app/controllers/deleteItem.controller.js index c18db64..2728701 100644 --- a/backend/app/controllers/deleteItem.controller.js +++ b/backend/app/controllers/deleteItem.controller.js @@ -14,7 +14,7 @@ const deleteRecursive = async (item) => { }; const deleteItem = async (req, res) => { - // #swagger.summary = 'Deletes a file/folder(s).' + // #swagger.summary = 'Deletes file/folder(s).' /* #swagger.parameters['body'] = { in: 'body', required: true, diff --git a/backend/app/controllers/downloadFile.controller.js b/backend/app/controllers/downloadFile.controller.js index cf9e78d..61bbf36 100644 --- a/backend/app/controllers/downloadFile.controller.js +++ b/backend/app/controllers/downloadFile.controller.js @@ -5,7 +5,8 @@ const mongoose = require("mongoose"); const archiver = require("archiver"); const downloadFile = async (req, res) => { - // #swagger.summary = 'Downloads a file.' + // Todo: Update download request query swagger docs. + // #swagger.summary = 'Downloads file/folder(s).' /* #swagger.parameters['filePath'] = { in: 'query', type: 'string', diff --git a/frontend/README.md b/frontend/README.md index 036f739..d56ea60 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -17,6 +17,7 @@ An open-source React.js package for easy integration of a file manager into appl - **Navigation**: Use the breadcrumb trail and sidebar navigation pane for quick directory traversal. - **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.  @@ -110,6 +111,26 @@ type File = { | `onRename` | (file: [File](#-file-structure), newName: string) => void | A callback function triggered when a file or folder is renamed. | | `width` | string \| number | The width of the component `default: 100%`. Can be a string (e.g., `'100%'`, `'10rem'`) or a number (in pixels). | +## ⌨️ Keyboard Shortcuts + +| **Action** | **Shortcut** | +| ------------------------------ | ------------------ | +| New Folder | `Alt + Shift + N` | +| Upload Files | `CTRL + U` | +| Cut | `CTRL + X` | +| Copy | `CTRL + C` | +| Paste | `CTRL + V` | +| Rename | `F2` | +| Download | `CTRL + D` | +| Delete | `DEL` | +| Select All Files | `CTRL + A` | +| Jump to First File in the List | `Home` | +| Jump to Last File in the List | `End` | +| Switch to List Layout | `CTRL + Shift + 1` | +| Switch to Grid Layout | `CTRL + Shift + 2` | +| Refresh File List | `F5` | +| Clear Selection | `Esc` | + ## 🤝 Contributing Contributions are welcome! To contribute: diff --git a/frontend/src/FileManager/Actions/Actions.jsx b/frontend/src/FileManager/Actions/Actions.jsx index 161f8e2..2009d95 100644 --- a/frontend/src/FileManager/Actions/Actions.jsx +++ b/frontend/src/FileManager/Actions/Actions.jsx @@ -4,12 +4,14 @@ import DeleteAction from "./Delete/Delete.action"; import UploadFileAction from "./UploadFile/UploadFile.action"; import PreviewFileAction from "./PreviewFile/PreviewFile.action"; import { useSelection } from "../../contexts/SelectionContext"; +import { useShortcutHandler } from "../../hooks/useShortcutHandler"; const Actions = ({ fileUploadConfig, onFileUploading, onFileUploaded, onDelete, + onRefresh, maxFileSize, filePreviewPath, acceptedFileTypes, @@ -18,6 +20,9 @@ const Actions = ({ const [activeAction, setActiveAction] = useState(null); const { selectedFiles } = useSelection(); + // Triggers all the keyboard shortcuts based actions + useShortcutHandler(triggerAction, onRefresh); + const actionTypes = { uploadFile: { title: "Upload", diff --git a/frontend/src/FileManager/Actions/CreateFolder/CreateFolder.action.jsx b/frontend/src/FileManager/Actions/CreateFolder/CreateFolder.action.jsx index 690fe35..e2b9e21 100644 --- a/frontend/src/FileManager/Actions/CreateFolder/CreateFolder.action.jsx +++ b/frontend/src/FileManager/Actions/CreateFolder/CreateFolder.action.jsx @@ -30,9 +30,9 @@ const CreateFolderAction = ({ filesViewRef, file, onCreateFolder, triggerAction // Validate folder name and call "onCreateFolder" function const handleValidateFolderName = (e) => { + e.stopPropagation(); if (e.key === "Enter") { e.preventDefault(); - e.stopPropagation(); handleFolderCreating(); return; } diff --git a/frontend/src/FileManager/Actions/Rename/Rename.action.jsx b/frontend/src/FileManager/Actions/Rename/Rename.action.jsx index c52a94a..3f9ff2a 100644 --- a/frontend/src/FileManager/Actions/Rename/Rename.action.jsx +++ b/frontend/src/FileManager/Actions/Rename/Rename.action.jsx @@ -30,9 +30,9 @@ const RenameAction = ({ filesViewRef, file, onRename, triggerAction }) => { }); const handleValidateFolderRename = (e) => { + e.stopPropagation(); if (e.key === "Enter") { e.preventDefault(); - e.stopPropagation(); outsideClick.setIsClicked(true); return; } diff --git a/frontend/src/FileManager/Actions/UploadFile/UploadFile.action.jsx b/frontend/src/FileManager/Actions/UploadFile/UploadFile.action.jsx index c9810cb..23f5e0d 100644 --- a/frontend/src/FileManager/Actions/UploadFile/UploadFile.action.jsx +++ b/frontend/src/FileManager/Actions/UploadFile/UploadFile.action.jsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useRef, useState } from "react"; import Button from "../../../components/Button/Button"; import { AiOutlineCloudUpload } from "react-icons/ai"; import UploadItem from "./UploadItem"; @@ -21,6 +21,14 @@ const UploadFileAction = ({ const [isUploading, setIsUploading] = useState({}); const { currentFolder, currentPathFiles } = useFileNavigation(); const { onError } = useFiles(); + const fileInputRef = useRef(null); + + // To open choose file if the "Choose File" button is focused and Enter key is pressed + const handleChooseFileKeyDown = (e) => { + if (e.key === "Enter") { + fileInputRef.current.click(); + } + }; const checkFileError = (file) => { const extError = !acceptedFileTypes.includes(getFileExtension(file.name)); @@ -104,9 +112,10 @@ const UploadFileAction = ({