Remove columns configurator usage to project-explorer, use provided keys when mapping...
[arvados-workbench2.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 container onClick={() => this.props.onItemClick(item)}>
39                         {renderIcon(item)}
40                         <Typography style={{ marginLeft: 8 }}>
41                             {item.name}
42                         </Typography>
43                     </Grid>
44                 )
45             },
46             {
47                 header: "Status",
48                 selected: true,
49                 render: item => (
50                     <Typography noWrap align="center">
51                         {item.status || "-"}
52                     </Typography>
53                 )
54             },
55             {
56                 header: "Type",
57                 selected: true,
58                 render: item => (
59                     <Typography noWrap>
60                         {item.type}
61                     </Typography>
62                 )
63             },
64             {
65                 header: "Owner",
66                 selected: true,
67                 render: item => (
68                     <Typography noWrap>
69                         {item.owner}
70                     </Typography>
71                 )
72             },
73             {
74                 header: "File size",
75                 selected: true,
76                 render: ({ fileSize }) => (
77                     <Typography noWrap>
78                         {typeof fileSize === "number" ? formatFileSize(fileSize) : "-"}
79                     </Typography>
80                 )
81             },
82             {
83                 header: "Last modified",
84                 selected: true,
85                 render: item => (
86                     <Typography noWrap>
87                         {formatDate(item.lastModified)}
88                     </Typography>
89                 )
90             },
91             {
92                 header: "Actions",
93                 key: "Actions",
94                 selected: true,
95                 configurable: false,
96                 renderHeader: () => (
97                     <Grid container justify="flex-end">
98                         <ColumnsConfigurator
99                             columns={this.state.columns}
100                             onColumnToggle={this.toggleColumn}
101                         />
102                     </Grid>
103                 ),
104                 render: item => (
105                     <Grid container justify="flex-end">
106                         <Popover triggerComponent={ItemActionsTrigger}>
107                             <List dense>
108                                 {[
109                                     {
110                                         icon: "fas fa-users",
111                                         label: "Share"
112                                     },
113                                     {
114                                         icon: "fas fa-sign-out-alt",
115                                         label: "Move to"
116                                     },
117                                     {
118                                         icon: "fas fa-star",
119                                         label: "Add to favourite"
120                                     },
121                                     {
122                                         icon: "fas fa-edit",
123                                         label: "Rename"
124                                     },
125                                     {
126                                         icon: "fas fa-copy",
127                                         label: "Make a copy"
128                                     },
129                                     {
130                                         icon: "fas fa-download",
131                                         label: "Download"
132                                     }].map(renderAction)
133                                 }
134                                 < Divider />
135                                 {
136                                     renderAction({ icon: "fas fa-trash-alt", label: "Remove" })
137                                 }
138                             </List>
139                         </Popover>
140                     </Grid>
141                 )
142             }
143         ]
144     };
145
146     render() {
147         return (
148             <DataExplorer
149                 columns={this.state.columns}
150                 items={this.props.items}
151                 onColumnToggle={this.toggleColumn}
152             />
153         );
154     }
155
156     toggleColumn = (column: Column<ProjectItem>) => {
157         const index = this.state.columns.indexOf(column);
158         const columns = this.state.columns.slice(0);
159         columns.splice(index, 1, { ...column, selected: !column.selected });
160         this.setState({ columns });
161     }
162 }
163
164 const formatDate = (isoDate: string) => {
165     const date = new Date(isoDate);
166     return date.toLocaleString();
167 };
168
169 const formatFileSize = (size: number) => {
170     switch (true) {
171         case size > 1000000000000:
172             return `${size / 1000000000000} TB`;
173         case size > 1000000000:
174             return `${size / 1000000000} GB`;
175         case size > 1000000:
176             return `${size / 1000000} MB`;
177         case size > 1000:
178             return `${size / 1000} KB`;
179         default:
180             return `${size} B`;
181     }
182 };
183
184 const renderIcon = (projectItem: ProjectItem) => {
185     switch (projectItem.type) {
186         case "arvados#group":
187             return <i className="fas fa-folder" />;
188         case "arvados#groupList":
189             return <i className="fas fa-th" />;
190         default:
191             return <i />;
192     }
193 };
194
195 const renderAction = (action: { label: string, icon: string }, index?: number) => (
196     <ListItem button key={index}>
197         <ListItemIcon>
198             <i className={action.icon} />
199         </ListItemIcon>
200         <ListItemText>
201             {action.label}
202         </ListItemText>
203     </ListItem>
204 );
205
206 const ItemActionsTrigger: React.SFC<IconButtonProps> = (props) => (
207     <IconButton {...props}>
208         <MoreVertIcon />
209     </IconButton>
210 );
211
212 export default ProjectExplorer;