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