From 2824db735219eba3be1e14a3b61a350429a54593 Mon Sep 17 00:00:00 2001 From: DaneEveritt Date: Mon, 20 Jun 2022 14:16:42 -0400 Subject: [PATCH] Update file manager design a bit --- resources/lang/en/activity.php | 2 +- .../scripts/components/elements/Portal.tsx | 8 ++ .../components/elements/dialog/Dialog.tsx | 6 +- .../server/files/FileDropdownMenu.tsx | 17 ++-- .../server/files/FileManagerContainer.tsx | 44 ++++----- .../server/files/MassActionsBar.tsx | 84 ++++++++-------- .../server/files/NewDirectoryButton.tsx | 97 +++++++++++-------- .../components/server/files/UploadButton.tsx | 63 ++++++------ .../components/server/files/style.module.css | 15 +++ 9 files changed, 184 insertions(+), 152 deletions(-) create mode 100644 resources/scripts/components/elements/Portal.tsx create mode 100644 resources/scripts/components/server/files/style.module.css diff --git a/resources/lang/en/activity.php b/resources/lang/en/activity.php index 64e0bb760..203115afa 100644 --- a/resources/lang/en/activity.php +++ b/resources/lang/en/activity.php @@ -61,7 +61,7 @@ return [ 'copy' => 'Created a copy of :file', 'create-directory' => 'Created a new directory :name in :directory', 'decompress' => 'Decompressed :files in :directory', - 'delete_one' => 'Deleted :directory:files', + 'delete_one' => 'Deleted :directory:files.0', 'delete_other' => 'Deleted :count files in :directory', 'download' => 'Downloaded :file', 'pull' => 'Downloaded a remote file from :url to :directory', diff --git a/resources/scripts/components/elements/Portal.tsx b/resources/scripts/components/elements/Portal.tsx new file mode 100644 index 000000000..e0be75778 --- /dev/null +++ b/resources/scripts/components/elements/Portal.tsx @@ -0,0 +1,8 @@ +import React, { useRef } from 'react'; +import { createPortal } from 'react-dom'; + +export default ({ children }: { children: React.ReactNode }) => { + const element = useRef(document.getElementById('modal-portal')); + + return createPortal(children, element!.current!); +}; diff --git a/resources/scripts/components/elements/dialog/Dialog.tsx b/resources/scripts/components/elements/dialog/Dialog.tsx index d15920973..d6dc2d4f0 100644 --- a/resources/scripts/components/elements/dialog/Dialog.tsx +++ b/resources/scripts/components/elements/dialog/Dialog.tsx @@ -44,8 +44,8 @@ const Dialog = ({ open, title, description, onClose, hideCloseIcon, children }: open={open} onClose={onClose} > -
-
+
+
-
+
{icon &&
{icon}
}
{title && diff --git a/resources/scripts/components/server/files/FileDropdownMenu.tsx b/resources/scripts/components/server/files/FileDropdownMenu.tsx index 603ed80ea..1b5bd04ee 100644 --- a/resources/scripts/components/server/files/FileDropdownMenu.tsx +++ b/resources/scripts/components/server/files/FileDropdownMenu.tsx @@ -30,8 +30,8 @@ import useEventListener from '@/plugins/useEventListener'; import compressFiles from '@/api/server/files/compressFiles'; import decompressFiles from '@/api/server/files/decompressFiles'; import isEqual from 'react-fast-compare'; -import ConfirmationModal from '@/components/elements/ConfirmationModal'; import ChmodFileModal from '@/components/server/files/ChmodFileModal'; +import { Dialog } from '@/components/elements/dialog'; type ModalType = 'rename' | 'move' | 'chmod'; @@ -128,15 +128,16 @@ const FileDropdownMenu = ({ file }: { file: FileObject }) => { return ( <> - setShowConfirmation(false)} + title={`Delete ${file.isFile ? 'File' : 'Directory'}`} + confirm={'Delete'} onConfirmed={doDeletion} - onModalDismissed={() => setShowConfirmation(false)} > - Deleting files is a permanent operation, you cannot undo this action. - + You will not be able to recover the contents of  + {file.name} once deleted. + ( diff --git a/resources/scripts/components/server/files/FileManagerContainer.tsx b/resources/scripts/components/server/files/FileManagerContainer.tsx index 47910b8c3..1b5e547fd 100644 --- a/resources/scripts/components/server/files/FileManagerContainer.tsx +++ b/resources/scripts/components/server/files/FileManagerContainer.tsx @@ -10,7 +10,7 @@ import { NavLink, useLocation } from 'react-router-dom'; import Can from '@/components/elements/Can'; import { ServerError } from '@/components/elements/ScreenBlock'; import tw from 'twin.macro'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button/index'; import { ServerContext } from '@/state/server'; import useFileManagerSwr from '@/plugins/useFileManagerSwr'; import MassActionsBar from '@/components/server/files/MassActionsBar'; @@ -20,6 +20,7 @@ import { useStoreActions } from '@/state/hooks'; import ErrorBoundary from '@/components/elements/ErrorBoundary'; import { FileActionCheckbox } from '@/components/server/files/SelectFileCheckbox'; import { hashToPath } from '@/helpers'; +import style from './style.module.css'; const sortFiles = (files: FileObject[]): FileObject[] => { const sortedFiles: FileObject[] = files.sort((a, b) => a.name.localeCompare(b.name)).sort((a, b) => a.isFile === b.isFile ? 0 : (a.isFile ? 1 : -1)); @@ -59,8 +60,8 @@ export default () => { return ( -
- + +
{ /> } /> - - - -
- - - - + +
+ + + +
- -
-
+
+
+
{ !files ? @@ -102,12 +96,12 @@ export default () => {
{files.length > 250 && -
-

- This directory is too large to display in the browser, - limiting the output to the first 250 files. -

-
+
+

+ This directory is too large to display in the browser, + limiting the output to the first 250 files. +

+
} { sortFiles(files.slice(0, 250)).map(file => ( diff --git a/resources/scripts/components/server/files/MassActionsBar.tsx b/resources/scripts/components/server/files/MassActionsBar.tsx index 50779a7b3..e3ff2d6b1 100644 --- a/resources/scripts/components/server/files/MassActionsBar.tsx +++ b/resources/scripts/components/server/files/MassActionsBar.tsx @@ -1,17 +1,16 @@ import React, { useEffect, useState } from 'react'; import tw from 'twin.macro'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button/index'; import Fade from '@/components/elements/Fade'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faFileArchive, faLevelUpAlt, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import useFileManagerSwr from '@/plugins/useFileManagerSwr'; import useFlash from '@/plugins/useFlash'; import compressFiles from '@/api/server/files/compressFiles'; import { ServerContext } from '@/state/server'; -import ConfirmationModal from '@/components/elements/ConfirmationModal'; import deleteFiles from '@/api/server/files/deleteFiles'; import RenameFileModal from '@/components/server/files/RenameFileModal'; +import Portal from '@/components/elements/Portal'; +import { Dialog } from '@/components/elements/dialog'; const MassActionsBar = () => { const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); @@ -62,53 +61,54 @@ const MassActionsBar = () => { }; return ( - 0} unmountOnExit> + <>
{loadingMessage} - setShowConfirm(false)} onConfirmed={onClickConfirmDeletion} - onModalDismissed={() => setShowConfirm(false)} > - Are you sure you want to delete {selectedFiles.length} file(s)? -
- Deleting the file(s) listed below is a permanent operation, you cannot undo this action. -
- - { selectedFiles.slice(0, 15).map(file => ( -
  • {file}
  • )) - } - { selectedFiles.length > 15 && -
  • + {selectedFiles.length - 15} other(s)
  • - } -
    -
    +

    + Are you sure you want to delete  + {selectedFiles.length} files? This is + a permanent action and the files cannot be recovered. +

    + {selectedFiles.slice(0, 15).map(file => ( +
  • {file}
  • )) + } + {selectedFiles.length > 15 && +
  • and {selectedFiles.length - 15} others
  • + } + {showMove && - setShowMove(false)} - /> + setShowMove(false)} + /> } -
    - - - -
    + +
    + 0} unmountOnExit> +
    + + + setShowConfirm(true)}> + Delete + +
    +
    +
    +
    -
    + ); }; diff --git a/resources/scripts/components/server/files/NewDirectoryButton.tsx b/resources/scripts/components/server/files/NewDirectoryButton.tsx index 56b58e17e..1b24767f0 100644 --- a/resources/scripts/components/server/files/NewDirectoryButton.tsx +++ b/resources/scripts/components/server/files/NewDirectoryButton.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useState } from 'react'; -import Modal from '@/components/elements/Modal'; import { ServerContext } from '@/state/server'; import { Form, Formik, FormikHelpers } from 'formik'; import Field from '@/components/elements/Field'; @@ -7,12 +6,15 @@ import { join } from 'path'; import { object, string } from 'yup'; import createDirectory from '@/api/server/files/createDirectory'; import tw from 'twin.macro'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button/index'; import { FileObject } from '@/api/server/files/loadDirectory'; import useFlash from '@/plugins/useFlash'; import useFileManagerSwr from '@/plugins/useFileManagerSwr'; import { WithClassname } from '@/components/types'; import FlashMessageRender from '@/components/FlashMessageRender'; +import { Dialog } from '@/components/elements/dialog'; +import Portal from '@/components/elements/Portal'; +import Code from '@/components/elements/Code'; interface Values { directoryName: string; @@ -66,48 +68,57 @@ export default ({ className }: WithClassname) => { return ( <> - - {({ resetForm, isSubmitting, values }) => ( - { - setVisible(false); - resetForm(); - }} - > - -
    - -

    - This directory will be created as -  /home/container/ - - {join(directory, values.directoryName).replace(/^(\.\.\/|\/)+/, '')} - -

    -
    - -
    - -
    - )} -
    - + + + )} + + + setVisible(true)} className={className}> Create Directory - + ); }; diff --git a/resources/scripts/components/server/files/UploadButton.tsx b/resources/scripts/components/server/files/UploadButton.tsx index a2e218bb7..454025921 100644 --- a/resources/scripts/components/server/files/UploadButton.tsx +++ b/resources/scripts/components/server/files/UploadButton.tsx @@ -1,7 +1,7 @@ import axios from 'axios'; import getFileUploadUrl from '@/api/server/files/getFileUploadUrl'; import tw from 'twin.macro'; -import Button from '@/components/elements/Button'; +import { Button } from '@/components/elements/button/index'; import React, { useEffect, useRef, useState } from 'react'; import styled from 'styled-components/macro'; import { ModalMask } from '@/components/elements/Modal'; @@ -12,6 +12,7 @@ import useFlash from '@/plugins/useFlash'; import useFileManagerSwr from '@/plugins/useFileManagerSwr'; import { ServerContext } from '@/state/server'; import { WithClassname } from '@/components/types'; +import Portal from '@/components/elements/Portal'; const InnerContainer = styled.div` max-width: 600px; @@ -71,36 +72,38 @@ export default ({ className }: WithClassname) => { return ( <> - - setVisible(false)} - onDragOver={e => e.preventDefault()} - onDrop={e => { - e.preventDefault(); - e.stopPropagation(); - - setVisible(false); - if (!e.dataTransfer?.files.length) return; - - onFileSubmission(e.dataTransfer.files); - }} + + -
    - -

    - Drag and drop files to upload. -

    -
    -
    -
    -
    - + setVisible(false)} + onDragOver={e => e.preventDefault()} + onDrop={e => { + e.preventDefault(); + e.stopPropagation(); + + setVisible(false); + if (!e.dataTransfer?.files.length) return; + + onFileSubmission(e.dataTransfer.files); + }} + > +
    + +

    + Drag and drop files to upload. +

    +
    +
    +
    + + +