16243: Added filtering for collection files
[arvados-workbench2.git] / src / components / collection-panel-files / collection-panel-files.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 { TreeItem, TreeItemStatus } from '~/components/tree/tree';
7 import { FileTreeData } from '~/components/file-tree/file-tree-data';
8 import { FileTree } from '~/components/file-tree/file-tree';
9 import { CollectionFileType } from "~/models/collection-file";
10 import { IconButton, Grid, Typography, StyleRulesCallback, withStyles, WithStyles, CardHeader, Card, Button, Tooltip, CircularProgress } from '@material-ui/core';
11 import { CustomizeTableIcon } from '~/components/icon/icon';
12 import { DownloadIcon } from '~/components/icon/icon';
13 import { SearchInput } from '../search-input/search-input';
14
15 export interface CollectionPanelFilesProps {
16     items: Array<TreeItem<FileTreeData>>;
17     isWritable: boolean;
18     isLoading: boolean;
19     tooManyFiles: boolean;
20     onUploadDataClick: () => void;
21     onSearchChange: (searchValue: string) => void;
22     onItemMenuOpen: (event: React.MouseEvent<HTMLElement>, item: TreeItem<FileTreeData>, isWritable: boolean) => void;
23     onOptionsMenuOpen: (event: React.MouseEvent<HTMLElement>, isWritable: boolean) => void;
24     onSelectionToggle: (event: React.MouseEvent<HTMLElement>, item: TreeItem<FileTreeData>) => void;
25     onCollapseToggle: (id: string, status: TreeItemStatus) => void;
26     onFileClick: (id: string) => void;
27     loadFilesFunc: () => void;
28     currentItemUuid?: string;
29 }
30
31 type CssRules = 'root' | 'cardSubheader' | 'nameHeader' | 'fileSizeHeader' | 'uploadIcon' | 'button' | 'centeredLabel' | 'cardHeaderContent' | 'cardHeaderContentTitle';
32
33 const styles: StyleRulesCallback<CssRules> = theme => ({
34     root: {
35         paddingBottom: theme.spacing.unit,
36         height: '100%'
37     },
38     cardSubheader: {
39         paddingTop: 0,
40         paddingBottom: 0,
41         minHeight: 8 * theme.spacing.unit,
42     },
43     cardHeaderContent: {
44         display: 'flex',
45         paddingRight: 2 * theme.spacing.unit,
46         justifyContent: 'space-between',
47     },
48     cardHeaderContentTitle: {
49         paddingLeft: theme.spacing.unit,
50         paddingTop: 2 * theme.spacing.unit,
51         paddingRight: 2 * theme.spacing.unit,
52     },
53     nameHeader: {
54         marginLeft: '75px'
55     },
56     fileSizeHeader: {
57         marginRight: '65px'
58     },
59     uploadIcon: {
60         transform: 'rotate(180deg)'
61     },
62     button: {
63         marginRight: -theme.spacing.unit,
64         marginTop: '8px'
65     },
66     centeredLabel: {
67         fontSize: '0.875rem',
68         textAlign: 'center'
69     },
70 });
71
72
73
74 export const CollectionPanelFiles =
75     withStyles(styles)(
76         ({ onItemMenuOpen, onSearchChange, onOptionsMenuOpen, onUploadDataClick, classes,
77             isWritable, isLoading, tooManyFiles, loadFilesFunc, ...treeProps }: CollectionPanelFilesProps & WithStyles<CssRules>) => {
78             const { useState, useEffect } = React;
79             const [searchValue, setSearchValue] = useState('');
80
81             useEffect(() => {
82                 onSearchChange(searchValue);
83             }, [searchValue]);
84
85             return (<Card data-cy='collection-files-panel' className={classes.root}>
86                 <CardHeader
87                     title={
88                         <div className={classes.cardHeaderContent}>
89                             <span className={classes.cardHeaderContentTitle}>Files</span>
90                             <SearchInput
91                                 value={searchValue}
92                                 onSearch={setSearchValue} />
93                         </div>
94                     }
95                     className={classes.cardSubheader}
96                     classes={{ action: classes.button }}
97                     action={<>
98                         {isWritable &&
99                             <Button
100                                 data-cy='upload-button'
101                                 onClick={onUploadDataClick}
102                                 variant='contained'
103                                 color='primary'
104                                 size='small'>
105                                 <DownloadIcon className={classes.uploadIcon} />
106                             Upload data
107                         </Button>}
108                         {!tooManyFiles &&
109                             <Tooltip title="More options" disableFocusListener>
110                                 <IconButton
111                                     data-cy='collection-files-panel-options-btn'
112                                     onClick={(ev) => onOptionsMenuOpen(ev, isWritable)}>
113                                     <CustomizeTableIcon />
114                                 </IconButton>
115                             </Tooltip>}
116                     </>
117                     } />
118                 {tooManyFiles
119                     ? <div className={classes.centeredLabel}>
120                         File listing may take some time, please click to browse: <Button onClick={loadFilesFunc}><DownloadIcon />Show files</Button>
121                     </div>
122                     : <>
123                         <Grid container justify="space-between">
124                             <Typography variant="caption" className={classes.nameHeader}>
125                                 Name
126                         </Typography>
127                             <Typography variant="caption" className={classes.fileSizeHeader}>
128                                 File size
129                         </Typography>
130                         </Grid>
131                         {isLoading
132                             ? <div className={classes.centeredLabel}><CircularProgress /></div>
133                             : <div style={{ height: 'calc(100% - 60px)' }}>
134                                 <FileTree
135                                     onMenuOpen={(ev, item) => onItemMenuOpen(ev, item, isWritable)}
136                                     {...treeProps}
137                                     items={treeProps.items.filter((item) => {
138                                         if (item.data.type === CollectionFileType.FILE) {
139                                             return item.data.name.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
140                                         }
141
142                                         return true;
143                                     })} /></div>}
144                     </>
145                 }
146             </Card>);
147         }
148     );