]> git.arvados.org - arvados.git/blob - src/components/project-explorer/project-explorer.tsx
Update project explorer layout
[arvados.git] / src / components / project-explorer / project-explorer.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import DataExplorer, { DataExplorerProps } from "../../components/data-explorer/data-explorer";
7 import ColumnsConfigurator from "../../components/data-explorer/columns-configurator/columns-configurator";
8 import { Typography, Grid, ListItem, Divider, List, ListItemIcon, ListItemText } from '@material-ui/core';
9 import { Column } from '../../components/data-explorer/column';
10 import IconButton, { IconButtonProps } from '@material-ui/core/IconButton';
11 import MoreVertIcon from "@material-ui/icons/MoreVert";
12 import Popover from '../popover/popover';
13
14 export interface ProjectItem {
15     name: string;
16     type: string;
17     owner: string;
18     lastModified: string;
19     fileSize?: number;
20     status?: string;
21 }
22
23 interface ProjectExplorerProps {
24     items: ProjectItem[];
25     onItemClick: (item: ProjectItem) => void;
26 }
27
28 type ProjectExplorerState = Pick<DataExplorerProps<ProjectItem>, "columns">;
29
30 class ProjectExplorer extends React.Component<ProjectExplorerProps, ProjectExplorerState> {
31
32     state: ProjectExplorerState = {
33         columns: [
34             {
35                 header: "Name",
36                 selected: true,
37                 render: item => (
38                     <Grid
39                         container
40                         alignItems="center"
41                         wrap="nowrap"
42                         spacing={16}
43                         onClick={() => this.props.onItemClick(item)}
44                     >
45                         <Grid item>
46                             {renderIcon(item)}
47                         </Grid>
48                         <Grid item>
49                             <Typography color="primary">
50                                 {item.name}
51                             </Typography>
52                         </Grid>
53                     </Grid>
54                 )
55             },
56             {
57                 header: "Status",
58                 selected: true,
59                 render: item => (
60                     <Typography noWrap align="center">
61                         {item.status || "-"}
62                     </Typography>
63                 )
64             },
65             {
66                 header: "Type",
67                 selected: true,
68                 render: item => (
69                     <Typography noWrap>
70                         {item.type}
71                     </Typography>
72                 )
73             },
74             {
75                 header: "Owner",
76                 selected: true,
77                 render: item => (
78                     <Typography noWrap color="primary">
79                         {item.owner}
80                     </Typography>
81                 )
82             },
83             {
84                 header: "File size",
85                 selected: true,
86                 render: ({ fileSize }) => (
87                     <Typography noWrap>
88                         {typeof fileSize === "number" ? formatFileSize(fileSize) : "-"}
89                     </Typography>
90                 )
91             },
92             {
93                 header: "Last modified",
94                 selected: true,
95                 render: item => (
96                     <Typography noWrap>
97                         {formatDate(item.lastModified)}
98                     </Typography>
99                 )
100             },
101             {
102                 header: "Actions",
103                 key: "Actions",
104                 selected: true,
105                 configurable: false,
106                 renderHeader: () => (
107                     <Grid container justify="flex-end">
108                         <ColumnsConfigurator
109                             columns={this.state.columns}
110                             onColumnToggle={this.toggleColumn}
111                         />
112                     </Grid>
113                 ),
114                 render: item => (
115                     <Grid container justify="flex-end">
116                         <Popover triggerComponent={ItemActionsTrigger}>
117                             <List dense>
118                                 {[
119                                     {
120                                         icon: "fas fa-users",
121                                         label: "Share"
122                                     },
123                                     {
124                                         icon: "fas fa-sign-out-alt",
125                                         label: "Move to"
126                                     },
127                                     {
128                                         icon: "fas fa-star",
129                                         label: "Add to favourite"
130                                     },
131                                     {
132                                         icon: "fas fa-edit",
133                                         label: "Rename"
134                                     },
135                                     {
136                                         icon: "fas fa-copy",
137                                         label: "Make a copy"
138                                     },
139                                     {
140                                         icon: "fas fa-download",
141                                         label: "Download"
142                                     }].map(renderAction)
143                                 }
144                                 < Divider />
145                                 {
146                                     renderAction({ icon: "fas fa-trash-alt", label: "Remove" })
147                                 }
148                             </List>
149                         </Popover>
150                     </Grid>
151                 )
152             }
153         ]
154     };
155
156     render() {
157         return (
158             <DataExplorer
159                 columns={this.state.columns}
160                 items={this.props.items}
161                 onColumnToggle={this.toggleColumn}
162             />
163         );
164     }
165
166     toggleColumn = (column: Column<ProjectItem>) => {
167         const index = this.state.columns.indexOf(column);
168         const columns = this.state.columns.slice(0);
169         columns.splice(index, 1, { ...column, selected: !column.selected });
170         this.setState({ columns });
171     }
172 }
173
174 const formatDate = (isoDate: string) => {
175     const date = new Date(isoDate);
176     return date.toLocaleString();
177 };
178
179 const formatFileSize = (size: number) => {
180     switch (true) {
181         case size > 1000000000000:
182             return `${size / 1000000000000} TB`;
183         case size > 1000000000:
184             return `${size / 1000000000} GB`;
185         case size > 1000000:
186             return `${size / 1000000} MB`;
187         case size > 1000:
188             return `${size / 1000} KB`;
189         default:
190             return `${size} B`;
191     }
192 };
193
194 const renderIcon = (projectItem: ProjectItem) => {
195     switch (projectItem.type) {
196         case "arvados#group":
197             return <i className="fas fa-folder fa-lg" />;
198         case "arvados#groupList":
199             return <i className="fas fa-th fa-lg"/>;
200         default:
201             return <i />;
202     }
203 };
204
205 const renderAction = (action: { label: string, icon: string }, index?: number) => (
206     <ListItem button key={index}>
207         <ListItemIcon>
208             <i className={action.icon} />
209         </ListItemIcon>
210         <ListItemText>
211             {action.label}
212         </ListItemText>
213     </ListItem>
214 );
215
216 const ItemActionsTrigger: React.SFC<IconButtonProps> = (props) => (
217     <IconButton {...props}>
218         <MoreVertIcon />
219     </IconButton>
220 );
221
222 export default ProjectExplorer;