Bucket objects listing menu redesign (#1467)

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2022-01-27 01:28:08 -07:00
committed by GitHub
parent 5b6e5786ea
commit c129eae6a7
7 changed files with 462 additions and 217 deletions

View File

@@ -25,22 +25,18 @@ const DeleteIcon = (props: SVGProps<SVGSVGElement>) => (
viewBox="0 0 256 256"
{...props}
>
<defs>
<clipPath id="prefix__a">
<path d="M0 0h256v256H0z" />
</clipPath>
</defs>
<g clipPath="url(#prefix__a)">
<path fill="none" d="M0 0h256v256H0z" />
<g data-name="Grupo 1557">
<path data-name="Rect\xE1ngulo 826" fill="none" d="M0 0h256v256H0z" />
<path
data-name="Uni\xF3n 10"
d="M71.113 256a37.94 37.94 0 0 1-37.889-37.9V60.906a15.426 15.426 0 0 1-14.227-15.353V29.621a15.423 15.423 0 0 1 15.4-15.4h41.541A15.378 15.378 0 0 1 91.258.003h72.871a15.393 15.393 0 0 1 15.334 14.218h41.531a15.423 15.423 0 0 1 15.4 15.4v15.932a15.426 15.426 0 0 1-14.227 15.353V218.1a37.942 37.942 0 0 1-37.9 37.9Zm-19.605-37.9a19.634 19.634 0 0 0 19.605 19.614h113.164A19.637 19.637 0 0 0 203.89 218.1V60.951H51.507ZM218.117 38.6v-6.1h-56.893V18.278H94.177V32.5H37.286v6.1Z"
/>
</g>
<g id="trash-icn" transform="translate(0 0)">
<path
fill={"currentcolor"}
d="M219.6,16.2h-49.7V8.4c0-3.4-2.7-6.1-6.1-6.1H92.2c-3.4,0-6.1,2.7-6.1,6.1v7.8H36.3
c-3.4,0-6.1,2.8-6.1,6.2V38c0,3.4,2.7,6.1,6.1,6.1h183.3c3.4,0,6.1-2.7,6.1-6.1V22.4C225.8,19,223.1,16.2,219.6,16.2
C219.7,16.2,219.6,16.2,219.6,16.2z"
/>
<path
fill={"currentcolor"}
d="M44.2,225.5c0,15.6,12.7,28.2,28.2,28.2h111.2c15.6-0.1,28.2-12.7,28.2-28.2V58.1H44.2V225.5z"
/>
</g>
</svg>
);
export default DeleteIcon;

View File

@@ -25,22 +25,20 @@ const HistoryIcon = (props: SVGProps<SVGSVGElement>) => (
viewBox="0 0 256 256"
{...props}
>
<defs>
<clipPath id="prefix__a">
<path d="M0 0h256v256H0z" />
</clipPath>
</defs>
<g clipPath="url(#prefix__a)">
<path fill="none" d="M0 0h256v256H0z" />
<path data-name="Rect\xE1ngulo 872" fill="none" d="M0 0h256v256H0z" />
<g>
<path
data-name="Trazado 453"
d="m81.488 242.942-22.543-48.346a13.317 13.317 0 0 1 6.49-17.833l48.346-22.538c15.859-7.4 27.115 16.725 11.252 24.122l-18.053 8.418a80.439 80.439 0 0 0 102.75-48.094 80.444 80.444 0 0 0-48.021-102.986A80.441 80.441 0 0 0 58.723 83.71a14.88 14.88 0 0 1-19.068 8.89 14.882 14.882 0 0 1-8.9-19.072 110.238 110.238 0 0 1 141.131-65.81 110.23 110.23 0 0 1 65.809 141.131 110.193 110.193 0 0 1-139.842 66.2l7.762 16.637c4.881 10.461-3.945 18.912-12.646 18.913-4.497.005-8.96-2.252-11.481-7.657Zm75.682-93.834a3.229 3.229 0 0 1-1.561-1.045q-13.8-13.794-27.611-27.593v-.646c.063-12.9.033-25.8.039-38.708a3.459 3.459 0 0 1 .8-2.727 3.477 3.477 0 0 1 2.256-.563c1.682-.02 3.363-.044 5.055-.063a4.066 4.066 0 0 1 3.014.773 4.046 4.046 0 0 1 .811 2.911q.2 13.947-.086 27.9a14.025 14.025 0 0 0 .713 5.9 13.918 13.918 0 0 0 3.219 4.131q10.119 10.053 20.238 20.117a3.339 3.339 0 0 1 1.084 1.623 3.122 3.122 0 0 1-.855 2.3 24.648 24.648 0 0 1-4.822 4.832 3.49 3.49 0 0 1-1.975.881 1.689 1.689 0 0 1-.319-.023Z"
fill={"currentcolor"}
d="M145.4,20C86.3,20.1,38.3,67.6,37.5,126.6L24.8,114c-5.2-5-13.4-4.9-18.5,0.2
c-4.9,5.1-4.9,13.2,0,18.2l37,37c5.1,5.1,13.3,5.2,18.5,0.1c0,0,0.1-0.1,0.1-0.1l37-37c4.9-5.3,4.6-13.5-0.7-18.5
c-5-4.7-12.8-4.7-17.8,0l-13.8,13.8c0.2-43.4,35.4-78.5,78.8-78.5c43.5,0,78.8,35.3,78.8,78.8c0,43.5-35.3,78.8-78.8,78.8
c-8.1,0-14.6,6.5-14.6,14.6s6.5,14.6,14.6,14.6c59.6-0.1,107.8-48.4,107.9-107.9C253.4,68.5,205.1,20.1,145.4,20z"
/>
<path
data-name="Uni\xF3n 13 - Contorno"
d="M92.968 251.103a12.73 12.73 0 0 1-6.744-1.879 13.836 13.836 0 0 1-5.189-6.07l-22.543-48.346a14.085 14.085 0 0 1-1.307-5.259 13.856 13.856 0 0 1 .744-5.241 13.776 13.776 0 0 1 7.295-8l48.346-22.538a14.467 14.467 0 0 1 6.129-1.44 12.56 12.56 0 0 1 8.6 3.438 15.406 15.406 0 0 1 4.545 7.953 13.39 13.39 0 0 1-.756 8.441 12.4 12.4 0 0 1-2.592 3.727 14.978 14.978 0 0 1-4.252 2.91l-16.941 7.9a79.364 79.364 0 0 0 25.85 4.331 79.721 79.721 0 0 0 23.842-3.653 80.4 80.4 0 0 0 21.525-10.468 79.546 79.546 0 0 0 17.619-16.539 79.522 79.522 0 0 0 12.121-21.871 79.941 79.941 0 0 0-47.723-102.346 79.383 79.383 0 0 0-27.23-4.821 79.7 79.7 0 0 0-23.844 3.656 80.393 80.393 0 0 0-21.527 10.471 79.581 79.581 0 0 0-17.621 16.544 79.559 79.559 0 0 0-12.121 21.876 15.272 15.272 0 0 1-5.725 7.4 15.345 15.345 0 0 1-8.729 2.719 15.342 15.342 0 0 1-5.258-.931 15.29 15.29 0 0 1-8.682-7.954 15.289 15.289 0 0 1-.516-11.759 110.23 110.23 0 0 1 16.793-30.3 110.251 110.251 0 0 1 24.412-22.917 111.367 111.367 0 0 1 29.82-14.5A110.409 110.409 0 0 1 134.336.574a110 110 0 0 1 37.721 6.677 110.98 110.98 0 0 1 20.15 9.731A109.94 109.94 0 0 1 209.5 30.145a110.736 110.736 0 0 1 14.162 16.02 111.715 111.715 0 0 1 10.766 18.3 111.8 111.8 0 0 1 7.1 20.011 110.787 110.787 0 0 1 3.168 21.146 109.933 109.933 0 0 1-1.031 21.707 111.107 111.107 0 0 1-5.5 21.691 110.2 110.2 0 0 1-16.783 30.288 110.134 110.134 0 0 1-24.4 22.9 111.325 111.325 0 0 1-29.807 14.487 110.443 110.443 0 0 1-33.014 5.056 110.035 110.035 0 0 1-35.361-5.84l7.264 15.569a12.959 12.959 0 0 1-.525 12.821 15.194 15.194 0 0 1-12.571 6.802Zm26.73-97.771a13.491 13.491 0 0 0-5.707 1.346l-48.346 22.538a12.787 12.787 0 0 0-6.771 7.418 12.867 12.867 0 0 0-.691 4.864 13.1 13.1 0 0 0 1.215 4.887l22.543 48.346a12.851 12.851 0 0 0 4.811 5.644 11.731 11.731 0 0 0 6.217 1.729 14.154 14.154 0 0 0 6.668-1.717 13.6 13.6 0 0 0 5.063-4.624 11.334 11.334 0 0 0 1.787-5.406 13.174 13.174 0 0 0-1.324-6.455l-8.264-17.713 1.121.393a109.092 109.092 0 0 0 36.146 6.168 109.439 109.439 0 0 0 32.715-5.01 110.312 110.312 0 0 0 29.539-14.357 109.081 109.081 0 0 0 24.174-22.69 109.052 109.052 0 0 0 16.631-30.013 109.874 109.874 0 0 0 5.451-21.5 108.833 108.833 0 0 0 1.023-21.509 109.579 109.579 0 0 0-3.141-20.955 110.678 110.678 0 0 0-7.035-19.832 110.7 110.7 0 0 0-10.67-18.138 109.785 109.785 0 0 0-14.033-15.875 108.977 108.977 0 0 0-17.135-13.043 110 110 0 0 0-19.971-9.643 108.984 108.984 0 0 0-37.377-6.616 109.405 109.405 0 0 0-32.729 5.018A110.407 110.407 0 0 0 72.053 20.96a109.231 109.231 0 0 0-24.187 22.709 109.145 109.145 0 0 0-16.641 30.027 14.281 14.281 0 0 0 .48 10.995 14.3 14.3 0 0 0 8.119 7.437 14.322 14.322 0 0 0 4.914.871 14.349 14.349 0 0 0 8.162-2.542 14.256 14.256 0 0 0 5.35-6.919 80.6 80.6 0 0 1 12.275-22.15 80.562 80.562 0 0 1 17.842-16.752 81.393 81.393 0 0 1 21.795-10.6 80.7 80.7 0 0 1 24.143-3.7 80.4 80.4 0 0 1 27.574 4.881 80.946 80.946 0 0 1 48.32 103.627 80.516 80.516 0 0 1-12.275 22.145 80.54 80.54 0 0 1-17.84 16.747 81.4 81.4 0 0 1-21.793 10.6 80.725 80.725 0 0 1-24.141 3.7 80.414 80.414 0 0 1-27.342-4.8l-1.141-.411 1.1-.512 18.053-8.418a13.988 13.988 0 0 0 3.969-2.713 11.409 11.409 0 0 0 2.385-3.426 12.405 12.405 0 0 0 .693-7.815 14.4 14.4 0 0 0-4.248-7.433 11.568 11.568 0 0 0-7.92-3.176Zm37.789-3.694a2.222 2.222 0 0 1-.412-.039 3.621 3.621 0 0 1-1.822-1.184l-11.055-11.048-16.555-16.543-.146-.146v-.853c.045-9.332.043-18.821.039-28V81.114a3.867 3.867 0 0 1 .977-3.107 3.924 3.924 0 0 1 2.57-.683l2.494-.031q1.28-.017 2.564-.032h.344a4.118 4.118 0 0 1 3.029.923 4.436 4.436 0 0 1 .957 3.258 800.73 800.73 0 0 1-.086 27.921 13.773 13.773 0 0 0 .672 5.692 13.532 13.532 0 0 0 3.113 3.975c6.664 6.621 13.439 13.358 19.992 19.874l.244.243a3.8 3.8 0 0 1 1.223 1.882v.006a3.536 3.536 0 0 1-.949 2.694 25.081 25.081 0 0 1-4.92 4.929 3.911 3.911 0 0 1-2.272.98Zm-28.99-29.375 16.406 16.4 11.059 11.05a2.806 2.806 0 0 0 1.3.908 1.17 1.17 0 0 0 .223.021 3.078 3.078 0 0 0 1.672-.779 24.124 24.124 0 0 0 4.725-4.734 2.72 2.72 0 0 0 .766-1.909 2.945 2.945 0 0 0-.945-1.362l-.246-.244c-6.553-6.516-13.328-13.253-19.992-19.874a14.293 14.293 0 0 1-3.326-4.288 14.5 14.5 0 0 1-.754-6.109 798.65 798.65 0 0 0 .086-27.885 3.61 3.61 0 0 0-.662-2.562 3.688 3.688 0 0 0-2.652-.629l-2.566.032-2.492.031a3.051 3.051 0 0 0-1.936.442 3.053 3.053 0 0 0-.627 2.348v10.71c0 9.178.006 18.668-.039 28Z"
fill="rgba(0,0,0,0)"
fill={"currentcolor"}
d="M150.7,81.1c0.2-1.5-0.3-3-1.2-4.2c-1.3-0.9-2.9-1.3-4.4-1.1h-7.4c-1.2-0.1-2.3,0.2-3.3,0.8
c-0.9,1.1-1.4,2.5-1.2,4c0,18.9,0,37.8,0,56.6v0.9l40.4,40.4c0.6,0.7,1.4,1.3,2.3,1.5c1.2,0.1,2.5-0.4,3.4-1.2c2.7-2,5-4.4,7-7.1
c0.9-0.9,1.3-2.1,1.2-3.4c-0.3-0.9-0.8-1.8-1.6-2.4l-29.6-29.4c-1.9-1.7-3.5-3.7-4.7-6c-1-2.8-1.3-5.7-1-8.6
C150.9,108.3,150.9,94.7,150.7,81.1z"
/>
</g>
</svg>

View File

@@ -19,28 +19,30 @@ import { SVGProps } from "react";
const PreviewIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="Layer_1"
className={`min-icon`}
fill={"currentcolor"}
viewBox="0 0 256 256"
{...props}
>
<defs>
<clipPath id="prefix__a">
<path d="M0 0h256v256H0z" />
</clipPath>
</defs>
<g clipPath="url(#prefix__a)">
<path fill="none" d="M0 0h256v256H0z" />
<path
data-name="Trazado 408"
d="M251.052 112.324A147.3 147.3 0 0 0 128 46.272 147.3 147.3 0 0 0 4.949 112.324a29.583 29.583 0 0 0 0 32.76A147.3 147.3 0 0 0 128 211.136a147.305 147.305 0 0 0 123.052-66.052 29.585 29.585 0 0 0 0-32.76Zm-17.68 21.08A126.144 126.144 0 0 1 128 189.974a126.144 126.144 0 0 1-105.371-56.57 8.487 8.487 0 0 1 0-9.4A126.144 126.144 0 0 1 128 67.434a126.145 126.145 0 0 1 105.372 56.569 8.488 8.488 0 0 1 0 9.401Z"
/>
<path
data-name="Trazado 409"
d="M128 74.274a54.492 54.492 0 0 0-54.431 54.43A54.492 54.492 0 0 0 128 183.135a54.493 54.493 0 0 0 54.431-54.431A54.493 54.493 0 0 0 128 74.274Zm0 87.7a33.305 33.305 0 0 1-33.268-33.268A33.305 33.305 0 0 1 128 95.439a33.305 33.305 0 0 1 33.263 33.265A33.305 33.305 0 0 1 128 161.972Z"
/>
<path data-name="Rect\xE1ngulo 859" fill="none" d="M0 0h256v256H0z" />
<g>
<defs>
<rect id="SVGID_1_" x="2.6" y="47.4" width="250.4" height="161.2" />
</defs>
<g>
<path
d="M127.8,95.5c-18,0-32.5,14.6-32.5,32.5c0,18,14.6,32.5,32.5,32.5l0,0
c18,0,32.5-14.6,32.5-32.5C160.3,110,145.8,95.5,127.8,95.5"
fill={"currentcolor"}
/>
<path
d="M248.2,112C204.1,45.5,114.5,27.4,48,71.4C31.9,82.1,18.1,95.9,7.5,112
c-6.5,9.7-6.5,22.3,0,32c44.1,66.5,133.7,84.6,200.1,40.5c16.1-10.7,29.9-24.5,40.5-40.5C254.6,134.3,254.6,121.7,248.2,112
M127.8,181.2c-29.4,0-53.2-23.8-53.2-53.2s23.8-53.2,53.2-53.2S181,98.6,181,128l0,0C181,157.4,157.2,181.2,127.8,181.2"
fill={"currentcolor"}
/>
</g>
</g>
</svg>
);

View File

@@ -89,8 +89,7 @@ import SearchBox from "../../../../Common/SearchBox";
import withSuspense from "../../../../Common/Components/withSuspense";
import { displayName } from "./utils";
import { DownloadIcon } from "../../../../../../icons";
import RBIconButton from "../../../BucketDetails/SummaryItems/RBIconButton";
import { DownloadIcon, PreviewIcon, ShareIcon } from "../../../../../../icons";
import UploadFilesButton from "../../UploadFilesButton";
const AddFolderIcon = React.lazy(
@@ -119,7 +118,6 @@ const ShareFile = withSuspense(
React.lazy(() => import("../ObjectDetails/ShareFile"))
);
const RewindEnable = withSuspense(React.lazy(() => import("./RewindEnable")));
const DeleteObject = withSuspense(React.lazy(() => import("./DeleteObject")));
const PreviewFileModal = withSuspense(
React.lazy(() => import("../Preview/PreviewFileModal"))
);
@@ -140,8 +138,11 @@ const styles = (theme: Theme) =>
badgeOverlap: {
"& .MuiBadge-badge": {
top: 35,
right: 10,
top: 10,
right: 1,
width: 5,
height: 5,
minWidth: 5
},
},
screenTitle: {
@@ -233,7 +234,6 @@ const ListObjects = ({
classes,
match,
history,
downloadingFiles,
rewindEnabled,
rewindDate,
bucketToRewind,
@@ -254,10 +254,8 @@ const ListObjects = ({
const [loading, setLoading] = useState<boolean>(true);
const [rewind, setRewind] = useState<RewindObject[]>([]);
const [loadingRewind, setLoadingRewind] = useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [deleteMultipleOpen, setDeleteMultipleOpen] = useState<boolean>(false);
const [createFolderOpen, setCreateFolderOpen] = useState<boolean>(false);
const [selectedObject, setSelectedObject] = useState<string>("");
const [filterObjects, setFilterObjects] = useState<string>("");
const [loadingStartTime, setLoadingStartTime] = useState<number>(0);
const [loadingMessage, setLoadingMessage] =
@@ -276,6 +274,8 @@ const ListObjects = ({
>("ASC");
const [currentSortField, setCurrentSortField] = useState<string>("name");
const [iniLoad, setIniLoad] = useState<boolean>(false);
const [canShareFile, setCanShareFile] = useState<boolean>(false);
const [canPreviewFile, setCanPreviewFile] = useState<boolean>(false);
const internalPaths = get(match.params, "subpaths", "");
const bucketName = match.params["bucketName"];
@@ -290,6 +290,27 @@ const ListObjects = ({
}
}, [folderUpload]);
useEffect(() => {
if (selectedObjects.length === 1) {
const objectName = selectedObjects[0];
if (extensionPreview(objectName) !== "none") {
setCanPreviewFile(true);
} else {
setCanPreviewFile(false);
}
if (objectName.endsWith("/")) {
setCanShareFile(false);
} else {
setCanShareFile(true);
}
} else {
setCanShareFile(false);
setCanPreviewFile(false);
}
}, [selectedObjects]);
const displayDeleteObject = hasPermission(bucketName, [
IAM_SCOPES.S3_DELETE_OBJECT,
]);
@@ -575,15 +596,6 @@ const ListObjects = ({
setErrorSnackMessage,
]);
const closeDeleteModalAndRefresh = (refresh: boolean) => {
setDeleteOpen(false);
if (refresh) {
setSnackBarMessage(`Object '${selectedObject}' deleted successfully.`);
setLoading(true);
}
};
const closeDeleteMultipleModalAndRefresh = (refresh: boolean) => {
setDeleteMultipleOpen(false);
@@ -630,11 +642,6 @@ const ListObjects = ({
return niceBytes(String(object.size));
};
const confirmDeleteObject = (object: BucketObject) => {
setDeleteOpen(true);
setSelectedObject(object.name);
};
const displayDeleteFlag = (state: boolean) => {
return state ? "Yes" : "No";
};
@@ -856,14 +863,36 @@ const ListObjects = ({
[isDragActive, isDragAccept]
);
const openPreview = (fileObject: BucketObject) => {
setSelectedPreview(fileObject);
setPreviewOpen(true);
const openPreview = () => {
if (selectedObjects.length === 1) {
let fileObject: BucketObject | undefined;
const findFunction = (currValue: BucketObject | RewindObject) =>
selectedObjects.includes(currValue.name);
fileObject = filteredRecords.find(findFunction);
if (fileObject) {
setSelectedPreview(fileObject);
setPreviewOpen(true);
}
}
};
const openShare = (fileObject: BucketObject) => {
setSelectedPreview(fileObject);
setShareFileModalOpen(true);
const openShare = () => {
if (selectedObjects.length === 1) {
let fileObject: BucketObject | undefined;
const findFunction = (currValue: BucketObject | RewindObject) =>
selectedObjects.includes(currValue.name);
fileObject = filteredRecords.find(findFunction);
if (fileObject) {
setSelectedPreview(fileObject);
setShareFileModalOpen(true);
}
}
};
const closeShareModal = () => {
@@ -878,50 +907,8 @@ const ListObjects = ({
onClick: openPath,
sendOnlyId: true,
},
{
type: "preview",
label: "Preview",
onClick: openPreview,
disableButtonFunction: (item: string) =>
extensionPreview(item) === "none",
},
{
type: "share",
label: "Share",
onClick: openShare,
disableButtonFunction: (item: string) => item.endsWith("/"),
},
{
type: "download",
label: "Download",
onClick: downloadObject,
showLoaderFunction: (item: string) =>
downloadingFiles.includes(`${match.params["bucket"]}/${item}`),
disableButtonFunction: (item: string) => {
if (rewindEnabled) {
const element = rewind.find((elm) => elm.name === item);
if (element && element.delete_flag) {
return true;
}
}
return false;
},
sendOnlyId: false,
},
];
if (displayDeleteObject) {
tableActions.push({
type: "delete",
label: "Delete",
onClick: confirmDeleteObject,
disableButtonFunction: () => {
return rewindEnabled;
},
});
}
const filteredRecords = records.filter((b: BucketObject) => {
if (filterObjects === "") {
return true;
@@ -1089,15 +1076,6 @@ const ListObjects = ({
}}
/>
)}
{deleteOpen && (
<DeleteObject
deleteOpen={deleteOpen}
selectedBucket={bucketName}
selectedObject={encodeFileName(selectedObject)}
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
versioning={isVersioned}
/>
)}
{deleteMultipleOpen && (
<DeleteMultipleObjects
deleteOpen={deleteMultipleOpen}
@@ -1173,7 +1151,6 @@ const ListObjects = ({
closeMenu();
}}
/>
<input
type="file"
multiple
@@ -1206,97 +1183,6 @@ const ListObjects = ({
overrideClass={classes.searchField}
/>
</SecureComponent>
<div>
<SecureComponent
scopes={[IAM_SCOPES.S3_DELETE_OBJECT]}
resource={bucketName}
errorProps={{ disabled: true }}
>
<RBIconButton
tooltip={"Delete Selected"}
onClick={() => {
setDeleteMultipleOpen(true);
}}
text={"Delete Selected"}
icon={<DeleteIcon />}
color="secondary"
disabled={selectedObjects.length === 0}
variant={"outlined"}
/>
</SecureComponent>
<RBIconButton
tooltip={"Download Selected"}
onClick={downloadSelected}
text={"Download Selected"}
icon={<DownloadIcon />}
color="primary"
disabled={selectedObjects.length === 0}
variant={"contained"}
/>
</div>
</Grid>
<Grid item xs={12}>
<Fragment>
<SecureComponent
resource={bucketName}
scopes={[IAM_SCOPES.S3_PUT_OBJECT]}
errorProps={{ disabled: true }}
>
<RBIconButton
tooltip={"Choose or create a new path"}
onClick={() => {
setCreateFolderOpen(true);
}}
text={""}
icon={<AddFolderIcon />}
color="primary"
disabled={rewindEnabled}
variant={"outlined"}
/>
</SecureComponent>
<SecureComponent
resource={bucketName}
scopes={[IAM_SCOPES.S3_PUT_OBJECT]}
errorProps={{ disabled: true }}
>
<Badge
badgeContent=" "
color="secondary"
variant="dot"
invisible={!rewindEnabled}
className={classes.badgeOverlap}
>
<RBIconButton
tooltip={"Rewind"}
onClick={() => {
setRewindSelect(true);
}}
text={""}
icon={<HistoryIcon />}
color="primary"
disabled={!isVersioned}
variant={"outlined"}
/>
</Badge>
</SecureComponent>
<SecureComponent
scopes={[IAM_SCOPES.S3_LIST_BUCKET]}
resource={bucketName}
errorProps={{ disabled: true }}
>
<RBIconButton
tooltip={"Refresh list"}
onClick={() => {
setLoading(true);
}}
text={""}
icon={<RefreshIcon />}
color="primary"
disabled={rewindEnabled}
variant={"contained"}
/>
</SecureComponent>
</Fragment>
</Grid>
<Grid item xs={12}>
<br />
@@ -1332,6 +1218,88 @@ const ListObjects = ({
triggerSort: sortChange,
}}
onSelectAll={selectAllItems}
actionButtons={[
{
action: downloadSelected,
label: "Download",
disabled: selectedObjects.length === 0,
icon: <DownloadIcon />,
tooltip: "Download Selected",
},
{
action: openShare,
label: "Share",
disabled: selectedObjects.length !== 1 || !canShareFile,
icon: <ShareIcon />,
tooltip: "Share Selected File",
},
{
action: openPreview,
label: "Preview",
disabled: selectedObjects.length !== 1 || !canPreviewFile,
icon: <PreviewIcon />,
tooltip: "Preview Selected File",
},
{
action: () => {
setDeleteMultipleOpen(true);
},
label: "Delete",
icon: <DeleteIcon />,
disabled:
!hasPermission(bucketName, [
IAM_SCOPES.S3_DELETE_OBJECT,
]) ||
selectedObjects.length === 0 ||
!displayDeleteObject,
tooltip: "Delete Selected Files",
},
{
action: () => {
setRewindSelect(true);
},
label: "Rewind",
disabled:
!isVersioned ||
!hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT]),
icon: (
<Badge
badgeContent=" "
color="secondary"
variant="dot"
invisible={!rewindEnabled}
className={classes.badgeOverlap}
>
<HistoryIcon />
</Badge>
),
tooltip: "Rewind Bucket",
},
{
action: () => {
setCreateFolderOpen(true);
},
label: "New Path",
icon: <AddFolderIcon />,
disabled:
rewindEnabled ||
!hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT]),
tooltip: "Choose or create a new path",
},
]}
globalActions={[
{
action: () => {
setLoading(true);
},
label: "Reload",
icon: <RefreshIcon />,
disabled:
!hasPermission(bucketName, [IAM_SCOPES.S3_LIST_BUCKET]) ||
rewindEnabled,
tooltip: "Reload List",
},
]}
/>
</SecureComponent>
</Grid>

View File

@@ -34,6 +34,7 @@ interface IUploadFilesButton {
const styles = (theme: Theme) =>
createStyles({
listUploadIcons: {
height: 20,
"& .min-icon": {
width: 18,
fill: "rgba(0,0,0,0.87)",

View File

@@ -34,6 +34,7 @@ import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
import TableActionButton from "./TableActionButton";
import CheckboxWrapper from "../FormComponents/CheckboxWrapper/CheckboxWrapper";
import TopActionButton from "./TopActionButton";
import history from "../../../../history";
import {
checkboxIcons,
@@ -81,6 +82,14 @@ interface ISortConfig {
currentDirection: "ASC" | "DESC" | undefined;
}
interface IActionButton {
label: string;
icon?: React.ReactNode;
action: () => void;
disabled: boolean;
tooltip?: string;
}
interface TableWrapperProps {
itemActions?: ItemActions[] | null;
columns: IColumns[];
@@ -110,6 +119,10 @@ interface TableWrapperProps {
}: {
index: number;
}) => "deleted" | "" | React.CSSProperties;
generalTableActions?: () => void;
actionButtons?: IActionButton[];
subActions?: React.ReactNode;
globalActions?: IActionButton[];
}
const borderColor = "#9c9c9c80";
@@ -128,9 +141,19 @@ const styles = () =>
overflowY: "scroll",
position: "relative",
"&::-webkit-scrollbar": {
width: 3,
width: 0,
height: 3,
},
"&.actionsBar": {
padding: "45px 16px 8px",
},
},
topHelpers: {
position: "absolute",
top: 0,
left: 0,
backgroundColor: "#F8F8F8",
borderBottom: "#EAEDEE 1px solid",
},
noBackground: {
backgroundColor: "transparent",
@@ -167,6 +190,59 @@ const styles = () =>
checkAllWrapper: {
marginTop: -16,
},
actionsScrollable: {
display: "flex",
overflowX: "auto",
overflowY: "hidden",
height: 36,
alignItems: "center",
"&::-webkit-scrollbar": {
height: 2,
minHeight: 2,
borderRadius: 0,
},
"&::-webkit-scrollbar-track": {
background: "#F0F0F0",
borderRadius: 0,
boxShadow: "inset 0px 0px 0px 0px transparent",
height: 2,
},
"&::-webkit-scrollbar-thumb": {
background: "#5E5E5E",
borderRadius: 0,
},
"&::-webkit-scrollbar-thumb:hover": {
background: "#4C4C4C",
},
},
objectsSelected: {
display: "flex",
flexGrow: 0,
whiteSpace: "nowrap",
backgroundColor: "#F4F2F2",
color: "#000",
fontWeight: "bold",
fontSize: 14,
height: 37,
maxHeight: 37,
padding: "0 25px",
alignItems: "center",
position: "relative",
"&::after": {
content: "' '",
borderRight: "#eaeaea 1px solid",
width: 1,
height: 22,
display: "block",
position: "absolute",
right: 0,
},
},
globalActions: {
display: "flex",
flexGrow: 0,
justifyContent: "flex-end",
},
"@global": {
".rowLine": {
borderBottom: `1px solid ${borderColor}`,
@@ -465,6 +541,9 @@ const TableWrapper = ({
disabled = false,
onSelectAll,
rowStyle,
actionButtons,
subActions,
globalActions,
}: TableWrapperProps) => {
const [columnSelectorOpen, setColumnSelectorOpen] = useState<boolean>(false);
const [anchorEl, setAnchorEl] = React.useState<any>(null);
@@ -563,6 +642,10 @@ const TableWrapper = ({
customPaperHeight !== ""
? customPaperHeight
: classes.defaultPaperHeight
} ${
onSelect || actionButtons || subActions || globalActions
? "actionsBar"
: ""
}`}
>
{isLoading && (
@@ -575,6 +658,71 @@ const TableWrapper = ({
</Grid>
</Grid>
)}
{!isLoading && (
<Fragment>
<Grid
container
direction={"row"}
alignItems={"center"}
flexGrow={"1"}
className={classes.topHelpers}
>
{onSelect && (
<Grid item xs className={classes.objectsSelected}>
{selectedItems?.length || 0} Objects selected
</Grid>
)}
{actionButtons && (
<Grid
item
xs
className={classes.actionsScrollable}
flexWrap={"nowrap"}
>
{actionButtons.map((button, index) => {
return (
<TopActionButton
variant={"text"}
onClick={button.action}
disabled={button.disabled}
id={`button-option-${button.label}`}
key={`tableActon-${index.toString()}`}
startIcon={button.icon}
tooltip={button.tooltip}
>
{button.label}
</TopActionButton>
);
})}
</Grid>
)}
{subActions && (
<Grid item xs>
{subActions}
</Grid>
)}
{globalActions && (
<Grid item xs className={classes.globalActions}>
{globalActions.map((button, index) => {
return (
<TopActionButton
variant={"contained"}
onClick={button.action}
disabled={button.disabled}
id={`button-option-${button.label}`}
key={`tableActon-${index.toString()}`}
endIcon={button.icon}
tooltip={button.tooltip}
>
{button.label}
</TopActionButton>
);
})}
</Grid>
)}
</Grid>
</Fragment>
)}
{columnsSelector && !isLoading && records.length > 0 && (
<div className={classes.overlayColumnSelection}>
{columnsSelection(columns)}

View File

@@ -0,0 +1,132 @@
// 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 <http://www.gnu.org/licenses/>.
import React from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Button, ButtonProps, Tooltip } from "@mui/material";
import clsx from "clsx";
const styles = (theme: Theme) =>
createStyles({
root: {
padding: "0 15px",
height: 22,
margin: 0,
color: "#5E5E5E",
fontWeight: "normal",
fontSize: 14,
borderRight: "#E5E5E5 1px solid",
borderStyle: "solid",
borderRadius: 0,
"&:hover": {
backgroundColor: "transparent",
color: "#000",
},
"& .min-icon": {
width: 11,
},
"&:disabled": {
color: "#EBEBEB",
borderColor: "#EBEBEB",
},
"&": {
"@media (max-width: 1279px)": {
padding: 0,
display: "flex",
justifyContent: "center",
"& .min-icon": {
width: 15,
},
},
},
},
contained: {
borderColor: "#5E5E5E",
background: "#5E5E5E",
color: "white",
borderRadius: 0,
height: 37,
fontWeight: "bold",
padding: "15px 25px",
"& .MuiTouchRipple-root span": {
backgroundColor: "#4c4c4c",
borderRadius: 3,
opacity: 0.3,
},
"&:hover": {
backgroundColor: "#4c4c4c",
color: "#FFF",
},
"& .min-icon": {
width: 12,
marginTop: -3,
},
"&": {
"@media (max-width: 1279px)": {
padding: 0,
display: "flex",
justifyContent: "center",
"& .min-icon": {
width: 15,
},
},
},
},
});
interface ITopActionButton extends ButtonProps {
classes: any;
children: any;
variant?: "text" | "contained";
tooltip?: string;
}
const TopActionButton = ({
classes,
children,
variant = "text",
tooltip,
...rest
}: ITopActionButton) => {
return (
<Tooltip title={tooltip || ""}>
<Button
{...rest}
className={clsx(classes.root, {
[classes.contained]: variant === "contained",
})}
sx={{
"& span.buttonItem": {
"@media (max-width: 1279px)": {
display: "none",
},
},
"& span": {
"@media (max-width: 1279px)": {
margin: 0,
},
},
}}
>
<span className={"buttonItem"}>{children}</span>
</Button>
</Tooltip>
);
};
export default withStyles(styles)(TopActionButton);