From 61d4d8947f73e1321a6b4057bf16952648179207 Mon Sep 17 00:00:00 2001 From: Saifullah-dev Date: Thu, 17 Oct 2024 23:04:18 +0500 Subject: [PATCH 1/5] Add useKeyPress hook for key press detection and shortcuts utils --- frontend/src/FileManager/FileManager.jsx | 14 +++++++++++ frontend/src/hooks/useKeyPress.js | 32 ++++++++++++++++++++++++ frontend/src/utils/shortcuts.js | 21 ++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 frontend/src/hooks/useKeyPress.js create mode 100644 frontend/src/utils/shortcuts.js diff --git a/frontend/src/FileManager/FileManager.jsx b/frontend/src/FileManager/FileManager.jsx index 7d265df..22037a8 100644 --- a/frontend/src/FileManager/FileManager.jsx +++ b/frontend/src/FileManager/FileManager.jsx @@ -14,6 +14,8 @@ import { useColumnResize } from "../hooks/useColumnResize"; import PropTypes from "prop-types"; import { dateStringValidator, urlValidator } from "../validators/propValidators"; import "./FileManager.scss"; +import { useKeyPress } from "../hooks/useKeyPress"; +import { shortcuts } from "../utils/shortcuts"; const FileManager = ({ files, @@ -42,6 +44,10 @@ const FileManager = ({ const { containerRef, colSizes, isDragging, handleMouseMove, handleMouseUp, handleMouseDown } = useColumnResize(20, 80); + useKeyPress(shortcuts.uploadFiles, (e) => { + console.log("File Upload!"); + }); + return (
{ + const lastKeyPressed = useRef(new Set([])); + + const handleKeyDown = (e) => { + if (e.repeat) return; // To prevent this function from triggering on key hold e.g. Ctrl hold + + const keysSet = new Set(keys); + lastKeyPressed.current.add(e.key); + + if (keysSet.isSubsetOf(lastKeyPressed.current)) { + e.preventDefault(); + callback(e); + return; + } + }; + + const handleKeyUp = (e) => { + lastKeyPressed.current.delete(e.key); + }; + + useEffect(() => { + window.addEventListener("keydown", handleKeyDown); + window.addEventListener("keyup", handleKeyUp); + + return () => { + window.removeEventListener("keydown", handleKeyDown); + window.removeEventListener("keyup", handleKeyUp); + }; + }, [keys, callback]); +}; diff --git a/frontend/src/utils/shortcuts.js b/frontend/src/utils/shortcuts.js new file mode 100644 index 0000000..c7f7b9b --- /dev/null +++ b/frontend/src/utils/shortcuts.js @@ -0,0 +1,21 @@ +export const shortcuts = { + createFolder: ["Control", "Shift", "N"], + uploadFiles: ["Control", "U"], + cut: ["Control", "X"], + copy: ["Control", "C"], + paste: ["Control", "V"], + rename: ["F2"], + download: ["Control", "D"], + delete: ["Delete"], + selectAll: ["Control", "A"], + selectIndividuals: ["Control", "Click"], + selectRange: ["Shift", "Click"], + selectArrows: ["Shift", "Arrows"], + navigation: ["Arrows"], + jumpToFirst: ["Home"], + jumpToLast: ["End"], + listLayout: ["Control", "Shift", "1"], + gridLayout: ["Control", "Shift", "2"], + refresh: ["F5"], + clearSelection: ["Escape"], +}; From 23048440453180c1b4725b334a19b67873f27dd3 Mon Sep 17 00:00:00 2001 From: Saifullah-dev Date: Sat, 19 Oct 2024 01:47:10 +0500 Subject: [PATCH 2/5] Implemented most of the file manager shortcuts with improved key press detection --- frontend/src/FileManager/Actions/Actions.jsx | 5 + .../CreateFolder/CreateFolder.action.jsx | 2 +- .../Actions/Rename/Rename.action.jsx | 2 +- .../Actions/UploadFile/UploadFile.action.jsx | 13 ++- .../src/FileManager/FileList/FileItem.jsx | 9 +- .../src/FileManager/FileList/FileList.jsx | 60 +++++------- frontend/src/FileManager/FileManager.jsx | 23 +---- frontend/src/FileManager/Toolbar/Toolbar.jsx | 37 ++------ frontend/src/components/Button/Button.jsx | 3 +- frontend/src/components/Modal/Modal.jsx | 13 ++- frontend/src/contexts/ClipboardContext.jsx | 26 ++++- frontend/src/contexts/SelectionContext.jsx | 8 +- frontend/src/hooks/useKeyPress.js | 26 +++-- frontend/src/hooks/useShortcutHandler.js | 94 +++++++++++++++++++ frontend/src/utils/shortcuts.js | 14 +-- 15 files changed, 217 insertions(+), 118 deletions(-) create mode 100644 frontend/src/hooks/useShortcutHandler.js 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 = ({
- )} {!selectedFiles.isDirectory && ( - diff --git a/frontend/src/components/Button/Button.jsx b/frontend/src/components/Button/Button.jsx index 91bddf7..d3c6ec6 100644 --- a/frontend/src/components/Button/Button.jsx +++ b/frontend/src/components/Button/Button.jsx @@ -1,9 +1,10 @@ import "./Button.scss"; -const Button = ({ onClick, type = "primary", padding = "0.4rem 0.8rem", children }) => { +const Button = ({ onClick, onKeyDown, type = "primary", padding = "0.4rem 0.8rem", children }) => { return (
- )} {!selectedFiles.isDirectory && ( - diff --git a/frontend/src/components/Button/Button.jsx b/frontend/src/components/Button/Button.jsx index 91bddf7..d3c6ec6 100644 --- a/frontend/src/components/Button/Button.jsx +++ b/frontend/src/components/Button/Button.jsx @@ -1,9 +1,10 @@ import "./Button.scss"; -const Button = ({ onClick, type = "primary", padding = "0.4rem 0.8rem", children }) => { +const Button = ({ onClick, onKeyDown, type = "primary", padding = "0.4rem 0.8rem", children }) => { return (