diff --git a/frontend/README.md b/frontend/README.md index 64905a8..072db70 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -2,7 +2,9 @@
-![NPM Downloads](https://img.shields.io/npm/d18m/%40cubone%2Freact-file-manager?style=for-the-badge) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40cubone%2Freact-file-manager?style=for-the-badge) ![NPM Version](https://img.shields.io/npm/v/%40cubone%2Freact-file-manager?style=for-the-badge&color=%23c87d32) +![NPM Downloads](https://img.shields.io/npm/d18m/%40cubone%2Freact-file-manager?style=for-the-badge) +![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40cubone%2Freact-file-manager?style=for-the-badge) +![NPM Version](https://img.shields.io/npm/v/%40cubone%2Freact-file-manager?style=for-the-badge&color=%23c87d32)
@@ -12,13 +14,19 @@ An open-source React.js package for easy integration of a file manager into appl ## ✨ Features -- **File & Folder Management**: View, upload, download, delete, copy, move, create, and rename files or folders seamlessly. +- **File & Folder Management**: View, upload, download, delete, copy, move, create, and rename files + or folders seamlessly. - **Grid & List View**: Switch between grid and list views to browse files in your preferred layout. -- **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. -- **Drag-and-Drop**: Move selected files and folders by dragging them to the desired directory, making file organization effortless. +- **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. +- **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) @@ -74,7 +82,9 @@ export default App; ## 📂 File Structure -The `files` prop accepts an array of objects, where each object represents a file or folder. You can customize the structure to meet your application needs. Each file or folder object follows the structure detailed below: +The `files` prop accepts an array of objects, where each object represents a file or folder. You can +customize the structure to meet your application needs. Each file or folder object follows the +structure detailed below: ```typescript type File = { @@ -88,30 +98,31 @@ type File = { ## ⚙️ Props -| 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. | -| `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. | -| `height` | string \| number | The height of the component `default: 600px`. Can be a string (e.g., `'100%'`, `'10rem'`) or a number (in pixels). | -| `initialPath` | string | The path of the directory to be loaded initially e.g. `/Documents`. This should be the path of a folder which is included in `files` array. Default value is `""` | -| `isLoading` | boolean | A boolean state indicating whether the application is currently performing an operation, such as creating, renaming, or deleting a file/folder. Displays a loading state if set `true`. | -| `layout` | "list" \| "grid" | Specifies the default layout style for the file manager. Can be either "list" or "grid". Default value is "grid". | -| `maxFileSize` | number | For limiting the maximum upload file size in bytes. | -| `onCreateFolder` | (name: string, parentFolder: [File](#-file-structure)) => void | A callback function triggered when a new folder is created. Use this function to update the files state to include the new folder under the specified parent folder using create folder API call to your server. | -| `onDelete` | (files: Array<[File](#-file-structure)>) => void | A callback function is triggered when one or more files or folders are deleted. | -| `onDownload` | (files: Array<[File](#-file-structure)>) => void | A callback function triggered when one or more files or folders are downloaded. | -| `onError` | (error: { type: string, message: string }, file: [File](#-file-structure)) => void | A callback function triggered whenever there is an error in the file manager. Where error is an object containing `type` ("upload", etc.) and a descriptive error `message`. | -| `onFileOpen` | (file: [File](#-file-structure)) => void | A callback function triggered when a file or folder is opened. | -| `onFileUploaded` | (response: { [key: string]: any }) => void | A callback function triggered after a file is successfully uploaded. Provides JSON `response` holding uploaded file details, use it to extract the uploaded file details and add it to the `files` state e.g. `setFiles((prev) => [...prev, JSON.parse(response)]);` | -| `onFileUploading` | (file: [File](#-file-structure), parentFolder: [File](#-file-structure)) => { [key: string]: any } | A callback function triggered during the file upload process. You can also return an object with key-value pairs that will be appended to the `FormData` along with the file being uploaded. The object can contain any number of valid properties. | -| `onLayoutChange` | (layout: "list" \| "grid") => void | A callback function triggered when the layout of the file manager is changed. | -| `onPaste` | (files: Array<[File](#-file-structure)>, destinationFolder: [File](#-file-structure), operationType: "copy" \| "move") => void | A callback function triggered when when one or more files or folders are pasted into a new location. Depending on `operationType`, use this to either copy or move the `sourceItem` to the `destinationFolder`, updating the files state accordingly. | -| `onRefresh` | () => void | A callback function triggered when the file manager is refreshed. Use this to refresh the `files` state to reflect any changes or updates. | -| `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). | +| 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. | +| `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`. | +| `filePreviewComponent` | (file: [File](#-file-structure)) => React.ReactNode | (Optional) A callback function that provides a custom file preview. It receives the selected file as its argument and must return a valid React node, JSX element, or HTML. Use this prop to override the default file preview behavior. Example: [Custom Preview Usage](#-custom-file-preview). | +| `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. | +| `height` | string \| number | The height of the component `default: 600px`. Can be a string (e.g., `'100%'`, `'10rem'`) or a number (in pixels). | +| `initialPath` | string | The path of the directory to be loaded initially e.g. `/Documents`. This should be the path of a folder which is included in `files` array. Default value is `""` | +| `isLoading` | boolean | A boolean state indicating whether the application is currently performing an operation, such as creating, renaming, or deleting a file/folder. Displays a loading state if set `true`. | +| `layout` | "list" \| "grid" | Specifies the default layout style for the file manager. Can be either "list" or "grid". Default value is "grid". | +| `maxFileSize` | number | For limiting the maximum upload file size in bytes. | +| `onCreateFolder` | (name: string, parentFolder: [File](#-file-structure)) => void | A callback function triggered when a new folder is created. Use this function to update the files state to include the new folder under the specified parent folder using create folder API call to your server. | +| `onDelete` | (files: Array<[File](#-file-structure)>) => void | A callback function is triggered when one or more files or folders are deleted. | +| `onDownload` | (files: Array<[File](#-file-structure)>) => void | A callback function triggered when one or more files or folders are downloaded. | +| `onError` | (error: { type: string, message: string }, file: [File](#-file-structure)) => void | A callback function triggered whenever there is an error in the file manager. Where error is an object containing `type` ("upload", etc.) and a descriptive error `message`. | +| `onFileOpen` | (file: [File](#-file-structure)) => void | A callback function triggered when a file or folder is opened. | +| `onFileUploaded` | (response: { [key: string]: any }) => void | A callback function triggered after a file is successfully uploaded. Provides JSON `response` holding uploaded file details, use it to extract the uploaded file details and add it to the `files` state e.g. `setFiles((prev) => [...prev, JSON.parse(response)]);` | +| `onFileUploading` | (file: [File](#-file-structure), parentFolder: [File](#-file-structure)) => { [key: string]: any } | A callback function triggered during the file upload process. You can also return an object with key-value pairs that will be appended to the `FormData` along with the file being uploaded. The object can contain any number of valid properties. | +| `onLayoutChange` | (layout: "list" \| "grid") => void | A callback function triggered when the layout of the file manager is changed. | +| `onPaste` | (files: Array<[File](#-file-structure)>, destinationFolder: [File](#-file-structure), operationType: "copy" \| "move") => void | A callback function triggered when when one or more files or folders are pasted into a new location. Depending on `operationType`, use this to either copy or move the `sourceItem` to the `destinationFolder`, updating the files state accordingly. | +| `onRefresh` | () => void | A callback function triggered when the file manager is refreshed. Use this to refresh the `files` state to reflect any changes or updates. | +| `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 @@ -135,6 +146,25 @@ type File = { | Refresh File List | `F5` | | Clear Selection | `Esc` | +## Custom File Preview + +The `FileManager` component allows you to provide a custom file preview by passing the +`filePreviewComponent` prop. This is an optional callback function that receives the selected file +as an argument and must return a valid React node, JSX element, or HTML. + +### Usage Example + +```jsx +const CustomImagePreviewer = ({ file }) => { + return {file.name}; +}; + + } +/>; +``` + ## 🤝 Contributing Contributions are welcome! To contribute: diff --git a/frontend/src/FileManager/Actions/Actions.jsx b/frontend/src/FileManager/Actions/Actions.jsx index 2009d95..ea75689 100644 --- a/frontend/src/FileManager/Actions/Actions.jsx +++ b/frontend/src/FileManager/Actions/Actions.jsx @@ -14,6 +14,7 @@ const Actions = ({ onRefresh, maxFileSize, filePreviewPath, + filePreviewComponent, acceptedFileTypes, triggerAction, }) => { @@ -44,7 +45,12 @@ const Actions = ({ }, previewFile: { title: "Preview", - component: , + component: ( + + ), width: "50%", }, }; diff --git a/frontend/src/FileManager/Actions/PreviewFile/PreviewFile.action.jsx b/frontend/src/FileManager/Actions/PreviewFile/PreviewFile.action.jsx index dcd82a1..fa8cf91 100644 --- a/frontend/src/FileManager/Actions/PreviewFile/PreviewFile.action.jsx +++ b/frontend/src/FileManager/Actions/PreviewFile/PreviewFile.action.jsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import React, { useMemo, useState } from "react"; import { getFileExtension } from "../../../utils/getFileExtension"; import Loader from "../../../components/Loader/Loader"; import { useSelection } from "../../../contexts/SelectionContext"; @@ -14,7 +14,7 @@ const videoExtensions = ["mp4", "mov", "avi"]; const audioExtensions = ["mp3", "wav", "m4a"]; const iFrameExtensions = ["txt", "pdf"]; -const PreviewFileAction = ({ filePreviewPath }) => { +const PreviewFileAction = ({ filePreviewPath, filePreviewComponent }) => { const [isLoading, setIsLoading] = useState(true); const [hasError, setHasError] = useState(false); const { selectedFiles } = useSelection(); @@ -22,6 +22,12 @@ const PreviewFileAction = ({ filePreviewPath }) => { const extension = getFileExtension(selectedFiles[0].name)?.toLowerCase(); const filePath = `${filePreviewPath}${selectedFiles[0].path}`; + // Custom file preview component + const customPreview = useMemo( + () => filePreviewComponent?.(selectedFiles[0]), + [filePreviewComponent] + ); + const handleImageLoad = () => { setIsLoading(false); // Loading is complete setHasError(false); // No error @@ -36,6 +42,10 @@ const PreviewFileAction = ({ filePreviewPath }) => { window.location.href = filePath; }; + if (React.isValidElement(customPreview)) { + return customPreview; + } + return (
{hasError || diff --git a/frontend/src/FileManager/FileManager.jsx b/frontend/src/FileManager/FileManager.jsx index d5240e3..6e367d7 100644 --- a/frontend/src/FileManager/FileManager.jsx +++ b/frontend/src/FileManager/FileManager.jsx @@ -38,6 +38,7 @@ const FileManager = ({ height = "600px", width = "100%", initialPath = "", + filePreviewComponent, }) => { const triggerAction = useTriggerAction(); const { containerRef, colSizes, isDragging, handleMouseMove, handleMouseUp, handleMouseDown } = @@ -97,6 +98,7 @@ const FileManager = ({ onRefresh={onRefresh} maxFileSize={maxFileSize} filePreviewPath={filePreviewPath} + filePreviewComponent={filePreviewComponent} acceptedFileTypes={acceptedFileTypes} triggerAction={triggerAction} /> @@ -145,6 +147,7 @@ FileManager.propTypes = { height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), initialPath: PropTypes.string, + filePreviewComponent: PropTypes.func, }; export default FileManager;