17585: New collection viewer layout
[arvados-workbench2.git] / src / components / collection-panel-files / collection-panel-files2.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import 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     }, [onSearchChange, 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                         label='Search files'
88                         onSearch={setSearchValue} />
89                 </div>
90             }
91             className={classes.cardSubheader}
92             classes={{ action: classes.button }}
93             action={<>
94                 {isWritable &&
95                     <Button
96                         data-cy='upload-button'
97                         onClick={onUploadDataClick}
98                         variant='contained'
99                         color='primary'
100                         size='small'>
101                         <DownloadIcon className={classes.uploadIcon} />
102                     Upload data
103                 </Button>}
104                 {!tooManyFiles &&
105                     <Tooltip title="More options" disableFocusListener>
106                         <IconButton
107                             data-cy='collection-files-panel-options-btn'
108                             onClick={(ev) => onOptionsMenuOpen(ev, isWritable)}>
109                             <CustomizeTableIcon />
110                         </IconButton>
111                     </Tooltip>}
112             </>
113             } />
114         {tooManyFiles
115             ? <div className={classes.centeredLabel}>
116                 File listing may take some time, please click to browse: <Button onClick={loadFilesFunc}><DownloadIcon />Show files</Button>
117             </div>
118             : <>
119                 <Grid container justify="space-between">
120                     <Typography variant="caption" className={classes.nameHeader}>
121                         Name
122                     </Typography>
123                     <Typography variant="caption" className={classes.fileSizeHeader}>
124                         File size
125                     </Typography>
126                 </Grid>
127                 {isLoading
128                     ? <div className={classes.centeredLabel}><CircularProgress /></div>
129                     : <div style={{ height: 'calc(100% - 60px)' }}>
130                         <FileTree
131                             onMenuOpen={(ev, item) => onItemMenuOpen(ev, item, isWritable)}
132                             {...treeProps} /></div>}
133             </>
134         }
135     </Card>);
136 };
137
138 export const CollectionPanelFiles = withStyles(styles)(CollectionPanelFilesComponent);