diff --git a/README.md b/README.md index d56ea60..0bf5751 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ type File = { | Name | Type | Description | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `acceptedFileTypes` | string | A comma-separated list of allowed file extensions for uploading files. (e.g.,`.txt, .png, .pdf`). | -| `enableFilePreview` | boolean | A boolean flag indicating whether to use the default file previewer in the file manager. | +| `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" } }` | | `files` | Array<[File](#-file-structure)> | An array of file and folder objects representing the current directory structure. Each object includes `name`, `isDirectory`, and `path` properties. | @@ -124,10 +124,12 @@ type File = { | 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` | +| Select Multiple Files | `CTRL + Click` | +| Select Range of Files | `Shift + Click` | | Switch to List Layout | `CTRL + Shift + 1` | | Switch to Grid Layout | `CTRL + Shift + 2` | +| Jump to First File in the List | `Home` | +| Jump to Last File in the List | `End` | | Refresh File List | `F5` | | Clear Selection | `Esc` | diff --git a/frontend/README.md b/frontend/README.md index d56ea60..0bf5751 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -90,7 +90,7 @@ type File = { | Name | Type | Description | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `acceptedFileTypes` | string | A comma-separated list of allowed file extensions for uploading files. (e.g.,`.txt, .png, .pdf`). | -| `enableFilePreview` | boolean | A boolean flag indicating whether to use the default file previewer in the file manager. | +| `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" } }` | | `files` | Array<[File](#-file-structure)> | An array of file and folder objects representing the current directory structure. Each object includes `name`, `isDirectory`, and `path` properties. | @@ -124,10 +124,12 @@ type File = { | 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` | +| Select Multiple Files | `CTRL + Click` | +| Select Range of Files | `Shift + Click` | | Switch to List Layout | `CTRL + Shift + 1` | | Switch to Grid Layout | `CTRL + Shift + 2` | +| Jump to First File in the List | `Home` | +| Jump to Last File in the List | `End` | | Refresh File List | `F5` | | Clear Selection | `Esc` | diff --git a/frontend/src/FileManager/FileList/FileItem.jsx b/frontend/src/FileManager/FileList/FileItem.jsx index c1579a2..d589157 100644 --- a/frontend/src/FileManager/FileList/FileItem.jsx +++ b/frontend/src/FileManager/FileList/FileItem.jsx @@ -49,11 +49,42 @@ const FileItem = ({ } }; + const handleFileRangeSelection = (shiftKey, ctrlKey) => { + if (selectedFileIndexes.length > 0 && shiftKey) { + let reverseSelection = false; + let startRange = selectedFileIndexes[0]; + let endRange = index; + + // Reverse Selection + if (startRange >= endRange) { + const temp = startRange; + startRange = endRange; + endRange = temp; + reverseSelection = true; + } + + const filesRange = currentPathFiles.slice(startRange, endRange + 1); + setSelectedFiles(reverseSelection ? filesRange.reverse() : filesRange); + } else if (selectedFileIndexes.length > 0 && ctrlKey) { + // Remove file from selected files if it already exists on CTRL + Click, other push it in selectedFiles + setSelectedFiles((prev) => { + const filteredFiles = prev.filter((f) => f.path !== file.path); + if (prev.length === filteredFiles.length) { + return [...prev, file]; + } + return filteredFiles; + }); + } else { + setSelectedFiles([file]); + } + }; + const handleFileSelection = (e) => { e.stopPropagation(); if (file.isEditing) return; - setSelectedFiles([file]); + handleFileRangeSelection(e.shiftKey, e.ctrlKey); + const currentTime = new Date().getTime(); if (currentTime - lastClickTime < 300) { handleFileAccess(); @@ -175,3 +206,15 @@ const FileItem = ({ }; export default FileItem; + +// CTRL Shortcut +// On clicking a file, we'll check if CTRL key was pressed at the time of clicking. +// Once confirmed, we'll check if the clicked file is already present in the selection context -> We'll remove it from there. +// If it's not present in the selection context, we'll push it into the selection context. +// + +// Shift Shortcut Algo +// On clicking a file, if shift key is pressed -> we'll select in range of (lastSelectedFile -- currentClickedFile) +// + +// Note: If shift key or ctrl key is pressed, clicking outside should not deselect files diff --git a/frontend/src/utils/shortcuts.js b/frontend/src/utils/shortcuts.js index eb67ba9..dbc56b4 100644 --- a/frontend/src/utils/shortcuts.js +++ b/frontend/src/utils/shortcuts.js @@ -8,8 +8,6 @@ export const shortcuts = { download: ["Control", "D"], delete: ["Delete"], selectAll: ["Control", "A"], - selectIndividuals: ["Control", "Click"], // (pending) - selectRange: ["Shift", "Click"], // (pending) selectArrows: ["Shift", "Arrows"], // (pending) navigation: ["Arrows"], // (pending) jumpToFirst: ["Home"],