diff --git a/portal-ui/src/icons/BackCaretIcon.tsx b/portal-ui/src/icons/BackCaretIcon.tsx new file mode 100644 index 000000000..96b8f3f2a --- /dev/null +++ b/portal-ui/src/icons/BackCaretIcon.tsx @@ -0,0 +1,37 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import * as React from "react"; +import { SVGProps } from "react"; + +const BackCaretIcon = (props: SVGProps) => ( + + + + + +); + +export default BackCaretIcon; diff --git a/portal-ui/src/icons/BucketsIcon.tsx b/portal-ui/src/icons/BucketsIcon.tsx index cad0528ab..2790ee938 100644 --- a/portal-ui/src/icons/BucketsIcon.tsx +++ b/portal-ui/src/icons/BucketsIcon.tsx @@ -25,19 +25,12 @@ const BucketsIcon = (props: SVGProps) => ( viewBox="0 0 256 256" {...props} > - - - - - - - - + diff --git a/portal-ui/src/icons/FolderIcon.tsx b/portal-ui/src/icons/FolderIcon.tsx index c5aaf7156..39ae74b98 100644 --- a/portal-ui/src/icons/FolderIcon.tsx +++ b/portal-ui/src/icons/FolderIcon.tsx @@ -1,5 +1,5 @@ // This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. +// Copyright (c) 2022 MinIO, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -25,17 +25,11 @@ const FolderIcon = (props: SVGProps) => ( viewBox="0 0 256 256" {...props} > - - - - - - - - - - - + + ); diff --git a/portal-ui/src/icons/index.ts b/portal-ui/src/icons/index.ts index 23357c87c..d93aba041 100644 --- a/portal-ui/src/icons/index.ts +++ b/portal-ui/src/icons/index.ts @@ -166,3 +166,4 @@ export { default as ConsoleIcon } from "./ConsoleIcon"; export { default as FileVideoIcon } from "./FileVideoIcon"; export { default as ChangePasswordIcon } from "./ChangePasswordIcon"; export { default as LockIcon } from "./LockIcon"; +export { default as BackCaretIcon } from "./BackCaretIcon"; diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx index 29ad3212c..e4a054edd 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx @@ -102,6 +102,16 @@ const CreateFolderModal = ({ setIsFormValid(valid); }, [pathUrl]); + const inputChange = (e: React.ChangeEvent) => { + setPathUrl(e.target.value); + }; + + const keyPressed = (e: any) => { + if(e.code === "Enter" && pathUrl !== "") { + createProcess(); + } + }; + return ( { - setPathUrl(e.target.value); - }} + onChange={inputChange} + onKeyPress={keyPressed} required /> diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx index a53c2ac99..8f43fa43c 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx @@ -102,9 +102,6 @@ import ObjectDetailPanel from "./ObjectDetailPanel"; import RBIconButton from "../../../BucketDetails/SummaryItems/RBIconButton"; import MultiSelectionPanel from "./MultiSelectionPanel"; -const AddFolderIcon = React.lazy( - () => import("../../../../../../icons/AddFolderIcon") -); const HistoryIcon = React.lazy( () => import("../../../../../../icons/HistoryIcon") ); @@ -115,9 +112,7 @@ const RefreshIcon = React.lazy( const DeleteIcon = React.lazy( () => import("../../../../../../icons/DeleteIcon") ); -const CreateFolderModal = withSuspense( - React.lazy(() => import("./CreateFolderModal")) -); + const DeleteMultipleObjects = withSuspense( React.lazy(() => import("./DeleteMultipleObjects")) ); @@ -169,6 +164,22 @@ const styles = (theme: Theme) => ...searchField.searchField, maxWidth: 380, }, + screenTitleContainer: { + border: "#EAEDEE 1px solid", + borderBottom: 0, + padding: "0.8rem 15px 0", + }, + titleSpacer: { + marginLeft: 10, + }, + listIcon: { + display: "block", + marginTop: "-10px", + "& .min-icon": { + width: 20, + height: 20, + } + }, ...objectBrowserCommon, ...containerForHeader(theme.spacing(4)), }); @@ -269,7 +280,6 @@ const ListObjects = ({ const [rewind, setRewind] = useState([]); const [loadingRewind, setLoadingRewind] = useState(false); const [deleteMultipleOpen, setDeleteMultipleOpen] = useState(false); - const [createFolderOpen, setCreateFolderOpen] = useState(false); const [loadingStartTime, setLoadingStartTime] = useState(0); const [loadingMessage, setLoadingMessage] = useState(defLoading); @@ -372,12 +382,12 @@ const ListObjects = ({ if (timeDelta / 1000 >= 6) { setLoadingMessage( - + This operation is taking longer than expected... ( {Math.ceil(timeDelta / 1000)}s) - + ); } else if (timeDelta / 1000 >= 3) { setLoadingMessage( @@ -656,9 +666,7 @@ const ListObjects = ({ } }; - const closeAddFolderModal = () => { - setCreateFolderOpen(false); - }; + const handleUploadButton = (e: any) => { if ( @@ -1167,7 +1175,7 @@ const ListObjects = ({ ]; return ( - + {shareFileModalOpen && selectedPreview && ( )} - {createFolderOpen && ( - - )} {rewindSelect && ( )} - + - - - } - title={ - + + + } + title={{bucketName}} subTitle={ @@ -1266,21 +1259,6 @@ const ListObjects = ({ } actions={ - } - color="primary" - variant={"outlined"} - onClick={() => { - setCreateFolderOpen(true); - }} - disabled={ - rewindEnabled || - !hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT]) - } - /> @@ -1355,6 +1334,13 @@ const ListObjects = ({ } /> + + +
-
+ ); }; diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails.tsx index e51fc58ed..feccf7756 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails.tsx @@ -48,7 +48,6 @@ import { import { decodeFileName, encodeFileName } from "../../../../../../common/utils"; import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions"; import SetRetention from "./SetRetention"; -import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs"; import DeleteObject from "../ListObjects/DeleteObject"; import AddTagModal from "./AddTagModal"; import DeleteTagModal from "./DeleteTagModal"; @@ -518,14 +517,6 @@ const ObjectDetails = ({ ? objectNameArray[objectNameArray.length - 1] : actualInfo.name } - subTitle={ - - - - } actions={ ) => void; + onKeyPress?: (e: any) => void; value: string | boolean; id: string; name: string; @@ -133,6 +134,7 @@ const InputBoxWrapper = ({ autoFocus = false, classes, className = "", + onKeyPress, }: InputBoxProps) => { let inputProps: any = { "data-index": index, ...extraInputProps }; @@ -197,6 +199,7 @@ const InputBoxWrapper = ({ helperText={error} placeholder={placeholder} className={classes.inputRebase} + onKeyPress={onKeyPress} /> {overlayIcon && (
. -import React from "react"; +import React, { Fragment, useState } from "react"; import get from "lodash/get"; import Grid from "@mui/material/Grid"; -import Moment from "react-moment"; import { connect } from "react-redux"; import withStyles from "@mui/styles/withStyles"; import { Theme } from "@mui/material/styles"; @@ -26,6 +25,19 @@ import { ObjectBrowserState } from "./reducers"; import { objectBrowserCommon } from "../Common/FormComponents/common/styleLibrary"; import { Link } from "react-router-dom"; import { encodeFileName } from "../../../common/utils"; +import { BackCaretIcon, FolderIcon } from "../../../icons"; +import { IconButton, Tooltip } from "@mui/material"; +import history from "../../../history"; +import { hasPermission } from "../../../common/SecureComponent"; +import { IAM_SCOPES } from "../../../common/SecureComponent/permissions"; +import withSuspense from "../Common/Components/withSuspense"; +import { BucketObject } from "../Buckets/ListBuckets/Objects/ListObjects/types"; + +const CreateFolderModal = withSuspense( + React.lazy( + () => import("../Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal") + ) +); interface ObjectBrowserReducer { objectBrowser: ObjectBrowserState; @@ -37,7 +49,7 @@ interface IObjectBrowser { internalPaths: string; rewindEnabled?: boolean; rewindDate?: any; - fullSizeBreadcrumbs?: boolean; + existingFiles: BucketObject[]; } const styles = (theme: Theme) => @@ -51,8 +63,10 @@ const BrowserBreadcrumbs = ({ internalPaths, rewindEnabled, rewindDate, - fullSizeBreadcrumbs, + existingFiles, }: IObjectBrowser) => { + const [createFolderOpen, setCreateFolderOpen] = useState(false); + let paths = internalPaths; if (internalPaths !== "") { @@ -60,52 +74,83 @@ const BrowserBreadcrumbs = ({ } const splitPaths = paths.split("/").filter((path) => path !== ""); - const listBreadcrumbs = splitPaths.map( - (objectItem: string, index: number) => { - const subSplit = splitPaths.slice(0, index + 1).join("/"); - const route = `/buckets/${bucketName}/browse/${ - subSplit ? `${encodeFileName(subSplit)}` : `` - }`; - return ( - - {objectItem} - {index < splitPaths.length - 1 && / } - - ); - } - ); + let breadcrumbsMap = splitPaths.map((objectItem: string, index: number) => { + const subSplit = splitPaths.slice(0, index + 1).join("/"); + const route = `/buckets/${bucketName}/browse/${ + subSplit ? `${encodeFileName(subSplit)}` : `` + }`; + return ( + + / + {objectItem} + + ); + }); + + const listBreadcrumbs: any[] = [ + + {bucketName} + , + ...breadcrumbsMap, + ]; + + const closeAddFolderModal = () => { + setCreateFolderOpen(false); + }; - const title = false; return ( - {title && ( - -
- {splitPaths && splitPaths.length > 0 - ? splitPaths[splitPaths.length - 1] - : ""} - {rewindEnabled && splitPaths.length > 1 && ( - -  (Rewind:{" "} - ) - - )} -
-
+ {createFolderOpen && ( + )} - - - - {bucketName} - {listBreadcrumbs.length > 0 && / } - - {listBreadcrumbs} + + { + history.goBack(); + }} + sx={{ + border: "#EAEDEE 1px solid", + backgroundColor: "#fff", + borderLeft: 0, + borderBottom: 0, + borderRadius: 0, + width: 39, + height: 39, + marginRight: "10px", + }} + > + + + + { + setCreateFolderOpen(true); + }} + disabled={ + rewindEnabled || + !hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT]) + } + disableTouchRipple + disableRipple + focusRipple={false} + sx={{ + padding: 0, + paddingLeft: "6px", + }} + > + + + +
+ {listBreadcrumbs} +
);