A few Makefile changes for the CI build pipeline.
[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, DataColumn, ColumnSelector } from "../../components/data-table";
7 import { Typography, Grid, Paper, Toolbar } from '@material-ui/core';
8 import IconButton from '@material-ui/core/IconButton';
9 import MoreVertIcon from "@material-ui/icons/MoreVert";
10 import { formatFileSize, formatDate } from '../../common/formatters';
11 import { DataItem } from './data-item';
12 import { mockAnchorFromMouseEvent } from '../popover/helpers';
13 import ContextMenu from '../context-menu/context-menu';
14
15 export interface DataExplorerContextActions {
16     onAddToFavourite: (dataIitem: DataItem) => void;
17     onCopy: (dataIitem: DataItem) => void;
18     onDownload: (dataIitem: DataItem) => void;
19     onMoveTo: (dataIitem: DataItem) => void;
20     onRemove: (dataIitem: DataItem) => void;
21     onRename: (dataIitem: DataItem) => void;
22     onShare: (dataIitem: DataItem) => void;
23 }
24 interface DataExplorerProps {
25     items: DataItem[];
26     onItemClick: (item: DataItem) => void;
27     contextActions: DataExplorerContextActions;
28 }
29
30 interface DataExplorerState {
31     columns: Array<DataColumn<DataItem>>;
32     contextMenu: {
33         anchorEl?: HTMLElement;
34         item?: DataItem;
35     };
36 }
37
38 class DataExplorer extends React.Component<DataExplorerProps, DataExplorerState> {
39     state: DataExplorerState = {
40         contextMenu: {},
41         columns: [{
42             name: "Name",
43             selected: true,
44             render: item => this.renderName(item)
45         }, {
46             name: "Status",
47             selected: true,
48             render: item => renderStatus(item.status)
49         }, {
50             name: "Type",
51             selected: true,
52             render: item => renderType(item.type)
53         }, {
54             name: "Owner",
55             selected: true,
56             render: item => renderOwner(item.owner)
57         }, {
58             name: "File size",
59             selected: true,
60             render: item => renderFileSize(item.fileSize)
61         }, {
62             name: "Last modified",
63             selected: true,
64             render: item => renderDate(item.lastModified)
65         }, {
66             name: "Actions",
67             selected: true,
68             configurable: false,
69             renderHeader: () => null,
70             render: item => this.renderActions(item)
71         }]
72     };
73
74     contextMenuActions = [[{
75         icon: "fas fa-users fa-fw",
76         name: "Share",
77         onClick: this.handleContextAction("onShare")
78     }, {
79         icon: "fas fa-sign-out-alt fa-fw",
80         name: "Move to",
81         onClick: this.handleContextAction("onMoveTo")
82     }, {
83         icon: "fas fa-star fa-fw",
84         name: "Add to favourite",
85         onClick: this.handleContextAction("onAddToFavourite")
86     }, {
87         icon: "fas fa-edit fa-fw",
88         name: "Rename",
89         onClick: this.handleContextAction("onRename")
90     }, {
91         icon: "fas fa-copy fa-fw",
92         name: "Make a copy",
93         onClick: this.handleContextAction("onCopy")
94     }, {
95         icon: "fas fa-download fa-fw",
96         name: "Download",
97         onClick: this.handleContextAction("onDownload")
98     }], [{
99         icon: "fas fa-trash-alt fa-fw",
100         name: "Remove",
101         onClick: this.handleContextAction("onRemove")
102     }
103     ]];
104
105     render() {
106         return <Paper>
107             <ContextMenu
108                 {...this.state.contextMenu}
109                 actions={this.contextMenuActions}
110                 onClose={this.closeContextMenu} />
111             <Toolbar>
112                 <Grid container justify="flex-end">
113                     <ColumnSelector
114                         columns={this.state.columns}
115                         onColumnToggle={this.toggleColumn} />
116                 </Grid>
117             </Toolbar>
118             <DataTable
119                 columns={this.state.columns}
120                 items={this.props.items}
121                 onRowContextMenu={this.openItemMenuOnRowClick} />
122             <Toolbar />
123         </Paper>;
124     }
125
126     toggleColumn = (column: DataColumn<DataItem>) => {
127         const index = this.state.columns.indexOf(column);
128         const columns = this.state.columns.slice(0);
129         columns.splice(index, 1, { ...column, selected: !column.selected });
130         this.setState({ columns });
131     }
132
133     renderName = (item: DataItem) =>
134         <Grid
135             container
136             alignItems="center"
137             wrap="nowrap"
138             spacing={16}
139             onClick={() => this.props.onItemClick(item)}>
140             <Grid item>
141                 {renderIcon(item)}
142             </Grid>
143             <Grid item>
144                 <Typography color="primary">
145                     {item.name}
146                 </Typography>
147             </Grid>
148         </Grid>
149
150     renderActions = (item: DataItem) =>
151         <Grid container justify="flex-end">
152             <IconButton onClick={event => this.openItemMenuOnActionsClick(event, item)}>
153                 <MoreVertIcon />
154             </IconButton>
155         </Grid>
156
157     openItemMenuOnRowClick = (event: React.MouseEvent<HTMLElement>, item: DataItem) => {
158         event.preventDefault();
159         this.setState({
160             contextMenu: {
161                 anchorEl: mockAnchorFromMouseEvent(event),
162                 item
163             }
164         });
165     }
166
167     openItemMenuOnActionsClick = (event: React.MouseEvent<HTMLElement>, item: DataItem) => {
168         this.setState({
169             contextMenu: {
170                 anchorEl: event.currentTarget,
171                 item
172             }
173         });
174     }
175
176     closeContextMenu = () => {
177         this.setState({ contextMenu: {} });
178     }
179
180     handleContextAction(action: keyof DataExplorerContextActions) {
181         return (item: DataItem) => {
182             this.closeContextMenu();
183             this.props.contextActions[action](item);
184         };
185     }
186
187 }
188
189 const renderIcon = (dataItem: DataItem) => {
190     switch (dataItem.type) {
191         case "arvados#group":
192             return <i className="fas fa-folder fa-lg" />;
193         case "arvados#groupList":
194             return <i className="fas fa-th fa-lg" />;
195         default:
196             return <i />;
197     }
198 };
199
200 const renderDate = (date: string) =>
201     <Typography noWrap>
202         {formatDate(date)}
203     </Typography>;
204
205 const renderFileSize = (fileSize?: number) =>
206     <Typography noWrap>
207         {formatFileSize(fileSize)}
208     </Typography>;
209
210 const renderOwner = (owner: string) =>
211     <Typography noWrap color="primary">
212         {owner}
213     </Typography>;
214
215 const renderType = (type: string) =>
216     <Typography noWrap>
217         {type}
218     </Typography>;
219
220 const renderStatus = (status?: string) =>
221     <Typography noWrap align="center">
222         {status || "-"}
223     </Typography>;
224
225 export default DataExplorer;