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