github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/FileVersionItem.tsx (about) 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 from "react"; 18 import { DateTime } from "luxon"; 19 import styled from "styled-components"; 20 import get from "lodash/get"; 21 import { displayFileIconName } from "../ListObjects/utils"; 22 import { 23 DownloadIcon, 24 PreviewIcon, 25 RecoverIcon, 26 ShareIcon, 27 IconButton, 28 Tooltip, 29 Grid, 30 Checkbox, 31 } from "mds"; 32 import { niceBytes } from "../../../../../../common/utils"; 33 import SpecificVersionPill from "./SpecificVersionPill"; 34 import { BucketObject } from "api/consoleApi"; 35 36 interface IFileVersionItem { 37 fileName: string; 38 versionInfo: BucketObject; 39 index: number; 40 isSelected?: boolean; 41 checkable: boolean; 42 isChecked: boolean; 43 onCheck: (versionID: string) => void; 44 onShare: (versionInfo: BucketObject) => void; 45 onDownload: (versionInfo: BucketObject) => void; 46 onRestore: (versionInfo: BucketObject) => void; 47 onPreview: (versionInfo: BucketObject) => void; 48 globalClick: (versionInfo: BucketObject) => void; 49 key: any; 50 style: any; 51 } 52 53 const FileVersionStyled = styled.div(({ theme }) => { 54 return { 55 "&:before": { 56 content: "' '", 57 display: "block", 58 position: "absolute", 59 width: "2px", 60 height: "calc(100% + 2px)", 61 backgroundColor: get(theme, "borderColor", "#F8F8F8"), 62 left: "24px", 63 }, 64 "& .mainFileVersionItem": { 65 borderBottom: `${get(theme, "borderColor", "#F8F8F8")} 1px solid`, 66 padding: "1rem 0", 67 margin: "0 0.5rem 0 2.5rem", 68 cursor: "pointer", 69 "&.deleted": { 70 color: "#868686", 71 }, 72 }, 73 "& .intermediateLayer": { 74 margin: "0 1.5rem 0 1.5rem", 75 "&:hover, &.selected": { 76 backgroundColor: get(theme, "boxBackground", "#F8F8F8"), 77 "& > div": { 78 borderBottomColor: get(theme, "boxBackground", "#F8F8F8"), 79 }, 80 }, 81 }, 82 "& .versionContainer": { 83 fontSize: 16, 84 fontWeight: "bold", 85 display: "flex", 86 alignItems: "center", 87 "& svg.min-icon": { 88 width: 18, 89 height: 18, 90 minWidth: 18, 91 minHeight: 18, 92 marginRight: 10, 93 }, 94 }, 95 "& .buttonContainer": { 96 textAlign: "right", 97 "& button": { 98 marginLeft: "1.5rem", 99 }, 100 }, 101 "& .versionID": { 102 fontSize: "12px", 103 margin: "2px 0", 104 whiteSpace: "nowrap", 105 textOverflow: "ellipsis", 106 maxWidth: "95%", 107 overflow: "hidden", 108 }, 109 "& .versionData": { 110 marginRight: "10px", 111 fontSize: 12, 112 color: "#868686", 113 }, 114 "@media (max-width: 600px)": { 115 "& .buttonContainer": { 116 "& button": { 117 marginLeft: "5px", 118 }, 119 }, 120 }, 121 "@media (max-width: 799px)": { 122 "&:before": { 123 display: "none", 124 }, 125 "& .mainFileVersionItem": { 126 padding: "5px 0px", 127 margin: 0, 128 }, 129 "& .intermediateLayer": { 130 margin: 0, 131 "&:hover, &.selected": { 132 backgroundColor: "transparent", 133 "& > div": { 134 borderBottomColor: get(theme, "boxBackground", "#F8F8F8"), 135 }, 136 }, 137 }, 138 "& .versionContainer": { 139 fontSize: 14, 140 "& svg.min-icon": { 141 display: "none", 142 }, 143 }, 144 "& .versionData": { 145 textOverflow: "ellipsis", 146 maxWidth: "95%", 147 overflow: "hidden", 148 whiteSpace: "nowrap", 149 }, 150 "& .collapsableInfo": { 151 display: "flex", 152 flexDirection: "column", 153 }, 154 "& .versionItem": { 155 display: "none", 156 }, 157 }, 158 }; 159 }); 160 161 const FileVersionItem = ({ 162 fileName, 163 versionInfo, 164 isSelected, 165 checkable, 166 isChecked, 167 onCheck, 168 onShare, 169 onDownload, 170 onRestore, 171 onPreview, 172 globalClick, 173 index, 174 key, 175 style, 176 }: IFileVersionItem) => { 177 const disableButtons = versionInfo.is_delete_marker; 178 179 const versionItemButtons = [ 180 { 181 icon: <PreviewIcon />, 182 action: onPreview, 183 tooltip: "Preview", 184 }, 185 { 186 icon: <DownloadIcon />, 187 action: onDownload, 188 tooltip: "Download this version", 189 }, 190 { 191 icon: <ShareIcon />, 192 action: onShare, 193 tooltip: "Share this version", 194 }, 195 { 196 icon: <RecoverIcon />, 197 action: onRestore, 198 tooltip: "Restore this version", 199 }, 200 ]; 201 202 let pill: "deleted" | "current" | "null" | null = null; 203 204 if (versionInfo.is_delete_marker) { 205 pill = "deleted"; 206 } else if (versionInfo.is_latest) { 207 pill = "current"; 208 } else if (versionInfo.version_id === "null") { 209 pill = "null"; 210 } 211 212 let lastModified = DateTime.now(); 213 214 if (versionInfo.last_modified) { 215 lastModified = DateTime.fromISO( 216 versionInfo.last_modified, 217 ) as DateTime<true>; 218 } 219 220 return ( 221 <FileVersionStyled> 222 <Grid 223 container 224 className={"ctrItem"} 225 onClick={() => { 226 globalClick(versionInfo); 227 }} 228 key={key} 229 style={style} 230 > 231 <Grid 232 item 233 xs={12} 234 className={`${"intermediateLayer"} ${isSelected ? "selected" : ""}`} 235 > 236 <Grid 237 item 238 xs 239 className={`mainFileVersionItem ${ 240 versionInfo.is_delete_marker ? "deleted" : "" 241 }`} 242 > 243 <Grid item xs={12}> 244 <Grid container> 245 <Grid item xs md={4} className={"versionContainer"}> 246 {checkable && ( 247 <Checkbox 248 checked={isChecked} 249 id={`select-${versionInfo.version_id}`} 250 name={`select-${versionInfo.version_id}`} 251 onChange={(e) => { 252 e.stopPropagation(); 253 onCheck(versionInfo.version_id || ""); 254 }} 255 value={versionInfo.version_id || ""} 256 disabled={versionInfo.is_delete_marker} 257 sx={{ 258 width: "initial", 259 }} 260 /> 261 )} 262 {displayFileIconName(fileName, true)} v{index.toString()} 263 <span className={"versionItem"}> 264 {pill && <SpecificVersionPill type={pill} />} 265 </span> 266 </Grid> 267 <Grid item xs={10} md={8} className={"buttonContainer"}> 268 {versionItemButtons.map((button, index) => { 269 return ( 270 <Tooltip 271 tooltip={button.tooltip} 272 key={`version-action-${ 273 button.tooltip 274 }-${index.toString()}`} 275 > 276 <IconButton 277 size={"small"} 278 id={`version-action-${ 279 button.tooltip 280 }-${index.toString()}`} 281 className={`${"spacing"} ${ 282 disableButtons ? "buttonDisabled" : "" 283 }`} 284 disabled={disableButtons} 285 onClick={(e) => { 286 e.stopPropagation(); 287 if (!disableButtons) { 288 button.action(versionInfo); 289 } else { 290 e.preventDefault(); 291 } 292 }} 293 sx={{ 294 backgroundColor: "#F8F8F8", 295 borderRadius: "100%", 296 width: "28px", 297 height: "28px", 298 padding: "5px", 299 "& .min-icon": { 300 width: "14px", 301 height: "14px", 302 }, 303 }} 304 > 305 {button.icon} 306 </IconButton> 307 </Tooltip> 308 ); 309 })} 310 </Grid> 311 </Grid> 312 </Grid> 313 <Grid item xs={12} className={"versionID"}> 314 {versionInfo.version_id !== "null" ? versionInfo.version_id : "-"} 315 </Grid> 316 <Grid item xs={12} className={"collapsableInfo"}> 317 <span className={"versionData"}> 318 <strong>Last modified:</strong>{" "} 319 {lastModified.toFormat("ccc, LLL dd yyyy HH:mm:ss (ZZZZ)")} 320 </span> 321 <span className={"versionData"}> 322 <strong>Size:</strong> {niceBytes(`${versionInfo.size || "0"}`)} 323 </span> 324 </Grid> 325 </Grid> 326 </Grid> 327 </Grid> 328 </FileVersionStyled> 329 ); 330 }; 331 332 export default FileVersionItem;