Skip to content

Commit 78e4e3f

Browse files
bexsoftBenjamin Perez
andauthored
First set of redesign objects listing (#1602)
- Removed table action buttons & global actions - Changed Upload Files label - Added reload, new path & rewind buttons in top bars - Added multi-select objects panel & table behaviors for single selection & multiselection - Fixed disabled action button styles Signed-off-by: Benjamin Perez <[email protected]> Co-authored-by: Benjamin Perez <[email protected]>
1 parent 1cdc719 commit 78e4e3f

File tree

8 files changed

+187
-374
lines changed

8 files changed

+187
-374
lines changed

portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx

Lines changed: 113 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ import {
9999
import UploadFilesButton from "../../UploadFilesButton";
100100
import DetailsListPanel from "./DetailsListPanel";
101101
import ObjectDetailPanel from "./ObjectDetailPanel";
102+
import RBIconButton from "../../../BucketDetails/SummaryItems/RBIconButton";
103+
import MultiSelectionPanel from "./MultiSelectionPanel";
102104

103105
const AddFolderIcon = React.lazy(
104106
() => import("../../../../../../icons/AddFolderIcon")
@@ -346,6 +348,17 @@ const ListObjects = ({
346348
}
347349
}, [quota, bucketName]);
348350

351+
useEffect(() => {
352+
if (selectedObjects.length > 0) {
353+
setDetailsOpen(true);
354+
return;
355+
}
356+
357+
if (selectedObjects.length === 0 && selectedInternalPaths === null) {
358+
setDetailsOpen(false);
359+
}
360+
}, [selectedObjects, selectedInternalPaths]);
361+
349362
const displayDeleteObject = hasPermission(bucketName, [
350363
IAM_SCOPES.S3_DELETE_OBJECT,
351364
]);
@@ -713,6 +726,7 @@ const ListObjects = ({
713726
};
714727

715728
const openPath = (idElement: string) => {
729+
setSelectedObjects([]);
716730
if (idElement.endsWith("/")) {
717731
const newPath = `/buckets/${bucketName}/browse${
718732
idElement ? `/${encodeFileName(idElement)}` : ``
@@ -995,6 +1009,7 @@ const ListObjects = ({
9951009
elements = elements.filter((element) => element !== value);
9961010
}
9971011
setSelectedObjects(elements);
1012+
setSelectedInternalPaths(null);
9981013

9991014
return elements;
10001015
};
@@ -1115,6 +1130,42 @@ const ListObjects = ({
11151130
uploadPath = uploadPath.concat(currentPath);
11161131
}
11171132

1133+
const multiActionButtons = [
1134+
{
1135+
action: downloadSelected,
1136+
label: "Download",
1137+
disabled: selectedObjects.length === 0,
1138+
icon: <DownloadIcon />,
1139+
tooltip: "Download Selected",
1140+
},
1141+
{
1142+
action: openShare,
1143+
label: "Share",
1144+
disabled: selectedObjects.length !== 1 || !canShareFile,
1145+
icon: <ShareIcon />,
1146+
tooltip: "Share Selected File",
1147+
},
1148+
{
1149+
action: openPreview,
1150+
label: "Preview",
1151+
disabled: selectedObjects.length !== 1 || !canPreviewFile,
1152+
icon: <PreviewIcon />,
1153+
tooltip: "Preview Selected File",
1154+
},
1155+
{
1156+
action: () => {
1157+
setDeleteMultipleOpen(true);
1158+
},
1159+
label: "Delete",
1160+
icon: <DeleteIcon />,
1161+
disabled:
1162+
!hasPermission(bucketName, [IAM_SCOPES.S3_DELETE_OBJECT]) ||
1163+
selectedObjects.length === 0 ||
1164+
!displayDeleteObject,
1165+
tooltip: "Delete Selected Files",
1166+
},
1167+
];
1168+
11181169
return (
11191170
<React.Fragment>
11201171
{shareFileModalOpen && selectedPreview && (
@@ -1215,6 +1266,61 @@ const ListObjects = ({
12151266
}
12161267
actions={
12171268
<Fragment>
1269+
<RBIconButton
1270+
id={"new-path"}
1271+
tooltip={"Choose or create a new path"}
1272+
text={"New Path"}
1273+
icon={<AddFolderIcon />}
1274+
color="primary"
1275+
variant={"outlined"}
1276+
onClick={() => {
1277+
setCreateFolderOpen(true);
1278+
}}
1279+
disabled={
1280+
rewindEnabled ||
1281+
!hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT])
1282+
}
1283+
/>
1284+
<RBIconButton
1285+
id={"rewind-objects-list"}
1286+
tooltip={"Rewind Bucket"}
1287+
text={"Rewind"}
1288+
icon={
1289+
<Badge
1290+
badgeContent=" "
1291+
color="secondary"
1292+
variant="dot"
1293+
invisible={!rewindEnabled}
1294+
className={classes.badgeOverlap}
1295+
>
1296+
<HistoryIcon />
1297+
</Badge>
1298+
}
1299+
color="primary"
1300+
variant={"outlined"}
1301+
onClick={() => {
1302+
setRewindSelect(true);
1303+
}}
1304+
disabled={
1305+
!isVersioned ||
1306+
!hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT])
1307+
}
1308+
/>
1309+
<RBIconButton
1310+
id={"refresh-objects-list"}
1311+
tooltip={"Reload List"}
1312+
text={"Reload"}
1313+
icon={<RefreshIcon />}
1314+
color="primary"
1315+
variant={"outlined"}
1316+
onClick={() => {
1317+
setLoading(true);
1318+
}}
1319+
disabled={
1320+
!hasPermission(bucketName, [IAM_SCOPES.S3_LIST_BUCKET]) ||
1321+
rewindEnabled
1322+
}
1323+
/>
12181324
<input
12191325
type="file"
12201326
multiple
@@ -1282,96 +1388,21 @@ const ListObjects = ({
12821388
triggerSort: sortChange,
12831389
}}
12841390
onSelectAll={selectAllItems}
1285-
actionButtons={[
1286-
{
1287-
action: downloadSelected,
1288-
label: "Download",
1289-
disabled: selectedObjects.length === 0,
1290-
icon: <DownloadIcon />,
1291-
tooltip: "Download Selected",
1292-
},
1293-
{
1294-
action: openShare,
1295-
label: "Share",
1296-
disabled: selectedObjects.length !== 1 || !canShareFile,
1297-
icon: <ShareIcon />,
1298-
tooltip: "Share Selected File",
1299-
},
1300-
{
1301-
action: openPreview,
1302-
label: "Preview",
1303-
disabled: selectedObjects.length !== 1 || !canPreviewFile,
1304-
icon: <PreviewIcon />,
1305-
tooltip: "Preview Selected File",
1306-
},
1307-
{
1308-
action: () => {
1309-
setDeleteMultipleOpen(true);
1310-
},
1311-
label: "Delete",
1312-
icon: <DeleteIcon />,
1313-
disabled:
1314-
!hasPermission(bucketName, [
1315-
IAM_SCOPES.S3_DELETE_OBJECT,
1316-
]) ||
1317-
selectedObjects.length === 0 ||
1318-
!displayDeleteObject,
1319-
tooltip: "Delete Selected Files",
1320-
},
1321-
{
1322-
action: () => {
1323-
setRewindSelect(true);
1324-
},
1325-
label: "Rewind",
1326-
disabled:
1327-
!isVersioned ||
1328-
!hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT]),
1329-
icon: (
1330-
<Badge
1331-
badgeContent=" "
1332-
color="secondary"
1333-
variant="dot"
1334-
invisible={!rewindEnabled}
1335-
className={classes.badgeOverlap}
1336-
>
1337-
<HistoryIcon />
1338-
</Badge>
1339-
),
1340-
tooltip: "Rewind Bucket",
1341-
},
1342-
{
1343-
action: () => {
1344-
setCreateFolderOpen(true);
1345-
},
1346-
label: "New Path",
1347-
icon: <AddFolderIcon />,
1348-
disabled:
1349-
rewindEnabled ||
1350-
!hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT]),
1351-
tooltip: "Choose or create a new path",
1352-
},
1353-
]}
1354-
globalActions={[
1355-
{
1356-
action: () => {
1357-
setLoading(true);
1358-
},
1359-
label: "Reload",
1360-
icon: <RefreshIcon />,
1361-
disabled:
1362-
!hasPermission(bucketName, [IAM_SCOPES.S3_LIST_BUCKET]) ||
1363-
rewindEnabled,
1364-
tooltip: "Reload List",
1365-
},
1366-
]}
13671391
/>
13681392
<DetailsListPanel
13691393
open={detailsOpen}
13701394
closePanel={() => {
13711395
setDetailsOpen(false);
13721396
setSelectedInternalPaths(null);
1397+
setSelectedObjects([]);
13731398
}}
13741399
>
1400+
{selectedObjects.length > 0 && (
1401+
<MultiSelectionPanel
1402+
items={multiActionButtons}
1403+
title={"Selected Objects:"}
1404+
/>
1405+
)}
13751406
{selectedInternalPaths !== null && (
13761407
<ObjectDetailPanel
13771408
internalPaths={selectedInternalPaths}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2022 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import React, { Fragment } from "react";
18+
import ObjectActionButton from "./ObjectActionButton";
19+
import { withStyles } from "@mui/styles";
20+
import createStyles from "@mui/styles/createStyles";
21+
import { detailsPanel } from "../../../../Common/FormComponents/common/styleLibrary";
22+
23+
const styles = () =>
24+
createStyles({
25+
...detailsPanel,
26+
});
27+
28+
export interface MultiSelectionItem {
29+
action: () => void;
30+
label: string;
31+
disabled: boolean;
32+
icon: React.ReactNode;
33+
tooltip: string;
34+
}
35+
36+
interface IMultiSelectionPanelProps {
37+
items: MultiSelectionItem[];
38+
title: string;
39+
classes: any;
40+
}
41+
42+
const MultiSelectionPanel = ({
43+
items,
44+
classes,
45+
title,
46+
}: IMultiSelectionPanelProps) => {
47+
return (
48+
<Fragment>
49+
<div className={classes.titleLabel}>{title}</div>
50+
<ul className={classes.objectActions}>
51+
<li>Actions:</li>
52+
{items.map((actionItem) => {
53+
return (
54+
<li>
55+
<ObjectActionButton
56+
label={actionItem.label}
57+
icon={actionItem.icon}
58+
onClick={actionItem.action}
59+
disabled={actionItem.disabled}
60+
/>
61+
</li>
62+
);
63+
})}
64+
</ul>
65+
</Fragment>
66+
);
67+
};
68+
69+
export default withStyles(styles)(MultiSelectionPanel);

portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectActionButton.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const ObjectActionButton = ({
6565
return (
6666
<Button
6767
{...restProps}
68+
disabled={disabled}
6869
onClick={onClick}
6970
className={classes.root}
7071
startIcon={icon}

portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ const ObjectDetailPanel = ({
435435
</div>
436436

437437
<ul className={classes.objectActions}>
438-
<li>Object Actions:</li>
438+
<li>Actions:</li>
439439
<li>
440440
<ObjectActionButton
441441
label={"Download"}

portal-ui/src/screens/Console/Buckets/ListBuckets/UploadFilesButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ const UploadFilesButton = ({
8484
aria-haspopup="true"
8585
aria-expanded={openUploadMenu ? "true" : undefined}
8686
onClick={handleClick}
87-
text={"Upload Files"}
87+
text={"Upload"}
8888
icon={<UploadIcon />}
8989
color="primary"
9090
variant={"contained"}

portal-ui/src/screens/Console/Common/ModalWrapper/ModalWrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const styles = (theme: Theme) =>
4949
...deleteDialogStyles,
5050
root: {
5151
"& .MuiPaper-root": {
52-
padding: "1rem 2rem 2rem 1rem",
52+
padding: "0 2rem 2rem 1rem",
5353
},
5454
},
5555
content: {

0 commit comments

Comments
 (0)