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