diff --git a/templates/frontend/src/components/shared/file-browser/file-browser.jsx b/templates/frontend/src/components/shared/file-browser/file-browser.jsx index c5387c0a..e1657e41 100644 --- a/templates/frontend/src/components/shared/file-browser/file-browser.jsx +++ b/templates/frontend/src/components/shared/file-browser/file-browser.jsx @@ -149,10 +149,10 @@ const FileBrowser = ({ readOnly = false, onCreateManually, className }) => { }); }; - const handleRenameFolder = (newName, folderId) => { + const handleRenameItem = (newName, itemId) => { filesDispatch({ - type: 'renameFolder', - folderId, + type: 'renameItem', + itemId, newName, }); }; @@ -207,7 +207,7 @@ const FileBrowser = ({ readOnly = false, onCreateManually, className }) => { onAddFile={!readOnly ? handleAddFile : noop} onAddFolder={!readOnly ? handleAddFolder : noop} onDropFile={!readOnly ? handleDropFile : noop} - onRenameFolder={!readOnly ? handleRenameFolder : noop} + onRenameItem={!readOnly ? handleRenameItem : noop} onItemDelete={!readOnly ? handleDeleteItem : noop} onOpenFile={handleOpenFile} onDragStart={!readOnly ? handleDragStart : noop} diff --git a/templates/frontend/src/components/shared/file-browser/file/file.jsx b/templates/frontend/src/components/shared/file-browser/file/file.jsx index 2f6039e8..9ad24332 100644 --- a/templates/frontend/src/components/shared/file-browser/file/file.jsx +++ b/templates/frontend/src/components/shared/file-browser/file/file.jsx @@ -1,8 +1,10 @@ import classNames from 'classnames/bind'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState, useCallback } from 'react'; +import Dropdown from 'components/shared/dropdown'; +import RenameItemModal from 'components/shared/file-browser/rename-item-modal'; import { useFiles } from 'contexts/files-provider'; -import CloseSvg from 'icons/close.inline.svg'; +import DotsIcon from 'icons/dots.inline.svg'; import { getIconByFilename } from 'utils/language'; import DeleteFileModal from '../delete-file-modal'; @@ -21,6 +23,7 @@ const File = ({ onDelete, onDragStart, onDragEnd, + onRenameItem, level, readOnly, }) => { @@ -31,6 +34,7 @@ const File = ({ } = useFiles(); const [isDeleteFileModalOpen, setIsDeleteFileModalOpen] = useState(false); + const [isRenameItemModalOpen, setIsRenameItemModalOpen] = useState(false); useEffect(() => { const fileElem = fileRef.current; @@ -54,12 +58,6 @@ const File = ({ onDelete(file.id); }; - const handleDeleteClick = (evt) => { - evt.preventDefault(); - evt.stopPropagation(); - setIsDeleteFileModalOpen(true); - }; - const handleDragStart = (evt) => { onOpen(null); evt.preventDefault(); @@ -68,6 +66,20 @@ const File = ({ }; const Icon = getIconByFilename(file.data.name); + const menuItems = [ + { + text: 'Rename', + onClick: () => setIsRenameItemModalOpen(true), + }, + { + text: 'Delete', + onClick: () => setIsDeleteFileModalOpen(true), + theme: 'danger', + }, + ]; + + const handleCloseDeleteFileModalClick = useCallback(() => setIsDeleteFileModalOpen(false), []); + const handleCloseRenameFileModalClick = useCallback(() => setIsRenameItemModalOpen(false), []); return (
setIsDeleteFileModalOpen(false)} + onClose={handleCloseDeleteFileModalClick} onSave={handleDelete} /> )} + {isRenameItemModalOpen && ( + onRenameItem(newName, file.id)} + /> + )}
{file.data.name} {!readOnly && ( - + + + )}
); diff --git a/templates/frontend/src/components/shared/file-browser/file/file.module.scss b/templates/frontend/src/components/shared/file-browser/file/file.module.scss index ebc9784e..e6b27fb6 100644 --- a/templates/frontend/src/components/shared/file-browser/file/file.module.scss +++ b/templates/frontend/src/components/shared/file-browser/file/file.module.scss @@ -15,9 +15,11 @@ &:hover { background-color: $color-secondary-black; + border-radius: 2px; - & .button-delete { + .options { visibility: visible; + opacity: 1; } } @@ -72,3 +74,38 @@ } } } + +.options { + position: absolute; + top: 50%; + right: 5px; + z-index: 1; + display: flex; + align-items: center; + justify-content: flex-end; + height: 12px; + padding-top: 7px; + padding-right: 10px; + padding-bottom: 7px; + padding-left: 10px; + margin-left: auto; + cursor: pointer; + visibility: hidden; + opacity: 0; + transform: translateY(-50%); + + &:hover { + circle { + fill: $color-white; + } + } +} + +.options-icon { + height: 15px; + fill: $color-grey; +} + +.options-menu { + min-width: 100px; +} diff --git a/templates/frontend/src/components/shared/file-browser/folder/folder.jsx b/templates/frontend/src/components/shared/file-browser/folder/folder.jsx index 0a04e749..392a7657 100644 --- a/templates/frontend/src/components/shared/file-browser/folder/folder.jsx +++ b/templates/frontend/src/components/shared/file-browser/folder/folder.jsx @@ -5,7 +5,7 @@ import Dropdown from 'components/shared/dropdown'; import AddFileModal from 'components/shared/file-browser/add-file-modal'; import AddFolderModal from 'components/shared/file-browser/add-folder-modal'; import DeleteFolderModal from 'components/shared/file-browser/delete-folder-modal'; -import RenameFolderModal from 'components/shared/file-browser/rename-folder-modal'; +import RenameItemModal from 'components/shared/file-browser/rename-item-modal'; import TreeRecursive from 'components/shared/file-browser/tree-recursive'; import ArrowSvg from 'icons/arrow-down.inline.svg'; import DotsIcon from 'icons/dots.inline.svg'; @@ -21,7 +21,7 @@ const Folder = ({ onOpenFile, onAddFile, onAddFolder, - onRenameFolder, + onRenameItem, onDragStart, onDragEnd, level, @@ -73,7 +73,7 @@ const Folder = ({ const [isAddFileModalOpen, setIsAddFileModalOpen] = useState(false); const [isAddFolderModalOpen, setIsAddFolderModalOpen] = useState(false); - const [isRenameFolderModalOpen, setIsRenameFolderModalOpen] = useState(false); + const [isRenameItemModalOpen, setIsRenameItemModalOpen] = useState(false); const [isDeleteFolderModalOpen, setIsDeleteFolderModalOpen] = useState(false); const menuItems = [ @@ -87,7 +87,7 @@ const Folder = ({ }, { text: 'Rename', - onClick: () => setIsRenameFolderModalOpen(true), + onClick: () => setIsRenameItemModalOpen(true), }, { text: 'Delete', @@ -96,6 +96,22 @@ const Folder = ({ }, ]; + const handleCloseRenameItemModalClick = useCallback(() => { + setIsRenameItemModalOpen(false); + }, []); + + const handleCloseAddFolderModalClick = useCallback(() => { + setIsAddFolderModalOpen(false); + }, []); + + const handleCloseDeleteFolderModalClick = useCallback(() => { + setIsDeleteFolderModalOpen(false); + }, []); + + const handleCloseAddFileModalClick = useCallback(() => { + setIsAddFileModalOpen(false); + }, []); + return (
setIsAddFileModalOpen(false)} + onClose={handleCloseAddFileModalClick} onSave={(fileName) => onAddFile(fileName, folder.id)} /> )} {isAddFolderModalOpen && ( setIsAddFolderModalOpen(false)} + onClose={handleCloseAddFolderModalClick} onSave={(folderName) => onAddFolder(folderName, folder.id)} /> )} - {isRenameFolderModalOpen && ( - setIsRenameFolderModalOpen(false)} - onSave={(newName) => onRenameFolder(newName, folder.id)} + isOpen={isRenameItemModalOpen} + onClose={handleCloseRenameItemModalClick} + onSave={(newName) => onRenameItem(newName, folder.id)} /> )} {isDeleteFolderModalOpen && ( setIsDeleteFolderModalOpen(false)} + onClose={handleCloseDeleteFolderModalClick} onSave={() => onDelete(folder.id)} /> )} @@ -174,7 +191,7 @@ const Folder = ({ onAddFile={onAddFile} onAddFolder={onAddFolder} onOpenFile={onOpenFile} - onRenameFolder={onRenameFolder} + onRenameItem={onRenameItem} onDragStart={onDragStart} onDragEnd={onDragEnd} /> diff --git a/templates/frontend/src/components/shared/file-browser/rename-folder-modal/index.js b/templates/frontend/src/components/shared/file-browser/rename-folder-modal/index.js deleted file mode 100644 index 440e8e10..00000000 --- a/templates/frontend/src/components/shared/file-browser/rename-folder-modal/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './rename-folder-modal'; diff --git a/templates/frontend/src/components/shared/file-browser/rename-item-modal/index.js b/templates/frontend/src/components/shared/file-browser/rename-item-modal/index.js new file mode 100644 index 00000000..5cfba6a7 --- /dev/null +++ b/templates/frontend/src/components/shared/file-browser/rename-item-modal/index.js @@ -0,0 +1 @@ +export { default } from './rename-item-modal'; diff --git a/templates/frontend/src/components/shared/file-browser/rename-folder-modal/rename-folder-modal.jsx b/templates/frontend/src/components/shared/file-browser/rename-item-modal/rename-item-modal.jsx similarity index 81% rename from templates/frontend/src/components/shared/file-browser/rename-folder-modal/rename-folder-modal.jsx rename to templates/frontend/src/components/shared/file-browser/rename-item-modal/rename-item-modal.jsx index 09ef9541..228fcbc9 100644 --- a/templates/frontend/src/components/shared/file-browser/rename-folder-modal/rename-folder-modal.jsx +++ b/templates/frontend/src/components/shared/file-browser/rename-item-modal/rename-item-modal.jsx @@ -10,7 +10,7 @@ import Input from 'components/shared/input'; import Modal from 'components/shared/modal'; import ModalPortal from 'components/shared/modal-portal'; -import styles from './rename-folder-modal.module.scss'; +import styles from './rename-item-modal.module.scss'; const schema = yup.object().shape({ newName: yup @@ -23,8 +23,8 @@ const schema = yup.object().shape({ const cx = classNames.bind(styles); -const RenameFolderModal = (props) => { - const { name, isOpen, onClose, onSave } = props; +const RenameItemModal = (props) => { + const { name, isOpen, onClose, onSave, label } = props; const { register, handleSubmit, clearErrors, errors } = useForm({ defaultValues: { newName: name }, @@ -40,10 +40,10 @@ const RenameFolderModal = (props) => { return ( - +
{ ); }; -RenameFolderModal.propTypes = { +RenameItemModal.propTypes = { name: PropTypes.string.isRequired, isOpen: PropTypes.bool, onClose: PropTypes.func, onSave: PropTypes.func, }; -RenameFolderModal.defaultProps = { +RenameItemModal.defaultProps = { isOpen: false, onClose: () => {}, onSave: () => {}, }; -export default RenameFolderModal; +export default RenameItemModal; diff --git a/templates/frontend/src/components/shared/file-browser/rename-folder-modal/rename-folder-modal.module.scss b/templates/frontend/src/components/shared/file-browser/rename-item-modal/rename-item-modal.module.scss similarity index 100% rename from templates/frontend/src/components/shared/file-browser/rename-folder-modal/rename-folder-modal.module.scss rename to templates/frontend/src/components/shared/file-browser/rename-item-modal/rename-item-modal.module.scss diff --git a/templates/frontend/src/components/shared/file-browser/tree-recursive/tree-recursive.jsx b/templates/frontend/src/components/shared/file-browser/tree-recursive/tree-recursive.jsx index d46777c2..3ee912db 100644 --- a/templates/frontend/src/components/shared/file-browser/tree-recursive/tree-recursive.jsx +++ b/templates/frontend/src/components/shared/file-browser/tree-recursive/tree-recursive.jsx @@ -15,7 +15,7 @@ const TreeRecursive = ({ onOpenFile, onAddFile, onAddFolder, - onRenameFolder, + onRenameItem, onDragStart, onDragEnd, level, @@ -37,6 +37,7 @@ const TreeRecursive = ({ onOpen={onOpenFile} onDragStart={onDragStart} onDragEnd={onDragEnd} + onRenameItem={onRenameItem} /> ); } @@ -51,7 +52,7 @@ const TreeRecursive = ({ onAddFile={onAddFile} onAddFolder={onAddFolder} onOpenFile={onOpenFile} - onRenameFolder={onRenameFolder} + onRenameItem={onRenameItem} onDragStart={onDragStart} onDragEnd={onDragEnd} /> diff --git a/templates/frontend/src/contexts/files-provider.jsx b/templates/frontend/src/contexts/files-provider.jsx index 96b2feaf..0158fae0 100644 --- a/templates/frontend/src/contexts/files-provider.jsx +++ b/templates/frontend/src/contexts/files-provider.jsx @@ -3,7 +3,7 @@ import React, { useContext, useReducer } from 'react'; import { addItem, moveItem, - renameFolder, + renameItem, deleteItem, changeFileContent, changeFileLanguage, @@ -37,11 +37,11 @@ export const filesReducer = (state, action) => { hasChangedFiles: true, ...moveItem({ files: state.files, item: action.item, newFolderId: action.newFolderId }), }; - case 'renameFolder': + case 'renameItem': return { ...state, hasChangedFiles: true, - ...renameFolder({ files: state.files, folderId: action.folderId, newName: action.newName }), + ...renameItem({ files: state.files, itemId: action.itemId, newName: action.newName }), }; case 'deleteItem': return { diff --git a/templates/frontend/src/utils/files-provider-helpers.js b/templates/frontend/src/utils/files-provider-helpers.js index 807dbe9e..d4ddd666 100644 --- a/templates/frontend/src/utils/files-provider-helpers.js +++ b/templates/frontend/src/utils/files-provider-helpers.js @@ -33,7 +33,7 @@ const addFile = (files, fileData, parentFolderId = null) => { newFiles.push(file); sortFiles(newFiles); } else { - const folderPath = findFolderPathByKey(newFiles, parentFolderId); + const folderPath = findItemPathByKey(newFiles, parentFolderId); const lastFolder = folderPath[folderPath.length - 1]; lastFolder.data.files.push(file); sortFiles(lastFolder.data.files); @@ -55,7 +55,7 @@ const addFolder = (files, folderData, parentFolderId = null) => { newFiles.push(folder); sortFiles(newFiles); } else { - const folderPath = findFolderPathByKey(newFiles, parentFolderId); + const folderPath = findItemPathByKey(newFiles, parentFolderId); const lastFolder = folderPath[folderPath.length - 1]; lastFolder.data.files.push(folder); sortFiles(lastFolder.data.files); @@ -75,19 +75,19 @@ export const addItem = ({ files, data, parentFolderId = null }) => { }; /** Changes folder name */ -export const renameFolder = ({ files, folderId, newName }) => { +export const renameItem = ({ files, itemId, newName }) => { let newData = cloneDeep(files); - const filePath = findFolderPathByKey(newData, folderId); + const itemPath = findItemPathByKey(newData, itemId); // if file/folder in root directory - if (filePath.length === 1) { + if (itemPath.length === 1) { newData = newData.map((item) => - item.id === folderId ? { ...item, data: { ...item.data, name: newName } } : item + item.id === itemId ? { ...item, data: { ...item.data, name: newName } } : item ); // if has parent folder } else { - const parentFolder = filePath[filePath.length - 2]; + const parentFolder = itemPath[itemPath.length - 2]; parentFolder.data.files = parentFolder.data.files.map((item) => - item.id === folderId ? { ...item, data: { ...item.data, name: newName } } : item + item.id === itemId ? { ...item, data: { ...item.data, name: newName } } : item ); } return { files: newData }; @@ -95,7 +95,7 @@ export const renameFolder = ({ files, folderId, newName }) => { export const deleteItem = ({ files, itemId, isFileOpen = false }) => { let newData = cloneDeep(files); - const filePath = findFolderPathByKey(newData, itemId); + const filePath = findItemPathByKey(newData, itemId); // if file/folder in root directory if (filePath.length === 1) { newData = newData.filter((item) => item.id !== itemId); @@ -121,7 +121,7 @@ export const moveItem = ({ files, item, newFolderId = null }) => { newFiles.push(item); sortFiles(newFiles); } else { - const folderPath = findFolderPathByKey(newFiles, newFolderId); + const folderPath = findItemPathByKey(newFiles, newFolderId); const lastFolder = folderPath[folderPath.length - 1]; lastFolder.data.files.push(item); sortFiles(lastFolder.data.files); @@ -152,13 +152,13 @@ export const createFolder = (data) => ({ }); /** Find path to folder with id=folderId */ -export const findFolderPathByKey = (data, folderId, path = []) => { +export const findItemPathByKey = (data, folderId, path = []) => { for (const item of data) { if (item.id === folderId) { return [...path, item]; } if (item.data.files) { - const result = findFolderPathByKey(item.data.files, folderId, [...path, item]); + const result = findItemPathByKey(item.data.files, folderId, [...path, item]); if (result) { return result; } @@ -203,7 +203,7 @@ export const getFilePath = (files, fileId) => { return null; } - const path = findFolderPathByKey(files, fileId); + const path = findItemPathByKey(files, fileId); return path ? path.map((item) => item.data.name).join('/') : null; };