From f1a197a42c3134c34af2bb7f6c06ec246407160c Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 22 Jan 2026 18:58:49 +0100 Subject: [PATCH 1/2] Actions in context menu and toolbar : Add possibility to set custom actions on the files or folder --- frontend/src/App.jsx | 16 +++ .../src/FileManager/FileList/useFileList.jsx | 126 +++++++++++------- frontend/src/FileManager/Toolbar/Toolbar.jsx | 29 +++- 3 files changed, 121 insertions(+), 50 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index d017f263..51861b74 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -7,6 +7,17 @@ import { getAllFilesAPI } from "./api/getAllFilesAPI"; import { renameAPI } from "./api/renameAPI"; import "./App.scss"; import FileManager from "./FileManager/FileManager"; +import { BiAperture } from "react-icons/bi"; + +const customButtonExample = { + inContextMenu : true, + inToolbarMenu : true, + enableMultiItems : true, + hideContextMenuOnClick : false, + title : "sync", + icon : , + onClick : (a) => {console.log("click on custom button", a)} +} function App() { const fileUploadConfig = { @@ -22,6 +33,11 @@ function App() { setIsLoading(true); const response = await getAllFilesAPI(); if (response.status === 200 && response.data) { + response.data.forEach(x=> { + if(x.name.startsWith("custom button")) { + x.customActions = [customButtonExample]; + } + }) setFiles(response.data); } else { console.error(response); diff --git a/frontend/src/FileManager/FileList/useFileList.jsx b/frontend/src/FileManager/FileList/useFileList.jsx index f32dc0bc..ab149533 100644 --- a/frontend/src/FileManager/FileList/useFileList.jsx +++ b/frontend/src/FileManager/FileList/useFileList.jsx @@ -1,4 +1,4 @@ -import { BiRename, BiSelectMultiple } from "react-icons/bi"; +import { BiRename, BiSelectMultiple, BiQuestionMark } from "react-icons/bi"; import { BsCopy, BsFolderPlus, BsGrid, BsScissors } from "react-icons/bs"; import { FaListUl, FaRegFile, FaRegPaste } from "react-icons/fa6"; import { FiRefreshCw } from "react-icons/fi"; @@ -140,54 +140,82 @@ const useFileList = (onRefresh, enableFilePreview, triggerAction, permissions, o }, ]; - const selecCtxItems = [ - { - title: t("open"), - icon: lastSelectedFile?.isDirectory ? : , - onClick: handleFileOpen, - divider: true, - }, - { - title: t("cut"), - icon: , - onClick: () => handleMoveOrCopyItems(true), - divider: !lastSelectedFile?.isDirectory && !permissions.copy, - hidden: !permissions.move, - }, - { - title: t("copy"), - icon: , - onClick: () => handleMoveOrCopyItems(false), - divider: !lastSelectedFile?.isDirectory, - hidden: !permissions.copy, - }, - { - title: t("paste"), - icon: , - onClick: handleFilePasting, - className: `${clipBoard ? "" : "disable-paste"}`, - hidden: !lastSelectedFile?.isDirectory || (!permissions.move && !permissions.copy), - divider: true, - }, - { - title: t("rename"), - icon: , - onClick: handleRenaming, - hidden: selectedFiles.length > 1 || !permissions.rename, - }, - { - title: t("download"), - icon: , - onClick: handleDownloadItems, - hidden: !permissions.download, - }, - { - title: t("delete"), - icon: , - onClick: handleDelete, - hidden: !permissions.delete, - }, - ]; + let customButtons = []; + + if(lastSelectedFile?.customActions) { + // Handle custom actions, that are tagged 'inContextMenu' + customButtons = lastSelectedFile.customActions.filter(x => x.inContextMenu).map(customButton => { + let hidden = (selectedFiles.length == 1) ? false : true; + if(selectedFiles.length > 1 && customButton.enableMultiItems) { + // Display button only if all the selected items have the same button + hidden = !selectedFiles.every(x=>x.customActions && x.customActions.includes(customButton)) + } + return { + title: customButton.title ?? "??", + icon: customButton.icon ? customButton.icon : , + hidden, + onClick: () => { + if(customButton.onClick) { + // Send selected files as argument, to make it work the same for multi and single selection + customButton.onClick(selectedFiles); + } else { + console.warn("no onClick specified for this custom button"); + } + setVisible(customButton.hideContextMenuOnClick === undefined ? false : !customButton.hideContextMenuOnClick); + }, + divider: customButton.divider ? customButton.divider : false, + }; + }) + } + + const selecCtxItems = customButtons.concat([ + { + title: t("open"), + icon: lastSelectedFile?.isDirectory ? : , + onClick: handleFileOpen, + divider: true, + }, + { + title: t("cut"), + icon: , + onClick: () => handleMoveOrCopyItems(true), + divider: !lastSelectedFile?.isDirectory && !permissions.copy, + hidden: !permissions.move, + }, + { + title: t("copy"), + icon: , + onClick: () => handleMoveOrCopyItems(false), + divider: !lastSelectedFile?.isDirectory, + hidden: !permissions.copy, + }, + { + title: t("paste"), + icon: , + onClick: handleFilePasting, + className: `${clipBoard ? "" : "disable-paste"}`, + hidden: !lastSelectedFile?.isDirectory || (!permissions.move && !permissions.copy), + divider: true, + }, + { + title: t("rename"), + icon: , + onClick: handleRenaming, + hidden: selectedFiles.length > 1 || !permissions.rename, + }, + { + title: t("download"), + icon: , + onClick: handleDownloadItems, + hidden: !permissions.download, + }, + { + title: t("delete"), + icon: , + onClick: handleDelete, + hidden: !permissions.delete, + }, + ]); // const handleFolderCreating = () => { diff --git a/frontend/src/FileManager/Toolbar/Toolbar.jsx b/frontend/src/FileManager/Toolbar/Toolbar.jsx index 41475788..be5d5960 100644 --- a/frontend/src/FileManager/Toolbar/Toolbar.jsx +++ b/frontend/src/FileManager/Toolbar/Toolbar.jsx @@ -7,7 +7,7 @@ import { MdOutlineFileDownload, MdOutlineFileUpload, } from "react-icons/md"; -import { BiRename } from "react-icons/bi"; +import { BiRename, BiQuestionMark } from "react-icons/bi"; import { FaListUl, FaRegPaste } from "react-icons/fa6"; import LayoutToggler from "./LayoutToggler"; import { useFileNavigation } from "../../contexts/FileNavigationContext"; @@ -75,6 +75,7 @@ const Toolbar = ({ onLayoutChange, onRefresh, triggerAction, permissions }) => { // Selected File/Folder Actions if (selectedFiles.length > 0) { + let customButtonId = 0; return (
@@ -125,6 +126,32 @@ const Toolbar = ({ onLayoutChange, onRefresh, triggerAction, permissions }) => { {t("delete")} )} + {selectedFiles.length > 0 && selectedFiles[0].customActions && + selectedFiles[0].customActions.filter(x => x.inToolbarMenu) + .filter(customButton => { + let hidden = (selectedFiles.length == 1) ? false : true; + if(selectedFiles.length > 1 && customButton.enableMultiItems) { + // Display button only if all the selected items have the same button + hidden = !selectedFiles.every(x=>x.customActions && x.customActions.includes(customButton)) + } + return !hidden; + }) + .map(customButton => ( + + ) + )}