Merge branch 'master' into 13857-workflow-view
[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 { Grid, Paper, Toolbar, StyleRulesCallback, withStyles, WithStyles, TablePagination, IconButton, Tooltip } from '@material-ui/core';
7 import MoreVertIcon from "@material-ui/icons/MoreVert";
8 import { ColumnSelector } from "../column-selector/column-selector";
9 import { DataTable, DataColumns } from "../data-table/data-table";
10 import { DataColumn, SortDirection } from "../data-table/data-column";
11 import { DataTableFilterItem } from '../data-table-filters/data-table-filters';
12 import { SearchInput } from '../search-input/search-input';
13 import { ArvadosTheme } from "~/common/custom-theme";
14
15 type CssRules = 'searchBox' | "toolbar" | "root";
16
17 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
18     searchBox: {
19         paddingBottom: theme.spacing.unit * 2
20     },
21     toolbar: {
22         paddingTop: theme.spacing.unit * 2
23     },
24     root: {
25         height: '100%'
26     }
27 });
28
29 interface DataExplorerDataProps<T> {
30     items: T[];
31     itemsAvailable: number;
32     columns: DataColumns<T>;
33     searchValue: string;
34     rowsPerPage: number;
35     rowsPerPageOptions: number[];
36     page: number;
37     contextMenuColumn: boolean;
38     dataTableDefaultView?: React.ReactNode;
39     working?: boolean;
40 }
41
42 interface DataExplorerActionProps<T> {
43     onSetColumns: (columns: DataColumns<T>) => void;
44     onSearch: (value: string) => void;
45     onRowClick: (item: T) => void;
46     onRowDoubleClick: (item: T) => void;
47     onColumnToggle: (column: DataColumn<T>) => void;
48     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: T) => void;
49     onSortToggle: (column: DataColumn<T>) => void;
50     onFiltersChange: (filters: DataTableFilterItem[], column: DataColumn<T>) => void;
51     onChangePage: (page: number) => void;
52     onChangeRowsPerPage: (rowsPerPage: number) => void;
53     extractKey?: (item: T) => React.Key;
54 }
55
56 type DataExplorerProps<T> = DataExplorerDataProps<T> & DataExplorerActionProps<T> & WithStyles<CssRules>;
57
58 export const DataExplorer = withStyles(styles)(
59     class DataExplorerGeneric<T> extends React.Component<DataExplorerProps<T>> {
60         componentDidMount() {
61             if (this.props.onSetColumns) {
62                 this.props.onSetColumns(this.props.columns);
63             }
64         }
65         render() {
66             const {
67                 columns, onContextMenu, onFiltersChange, onSortToggle, working, extractKey,
68                 rowsPerPage, rowsPerPageOptions, onColumnToggle, searchValue, onSearch,
69                 items, itemsAvailable, onRowClick, onRowDoubleClick, classes,
70                 dataTableDefaultView
71             } = this.props;
72             return <Paper className={classes.root}>
73                 <Toolbar className={classes.toolbar}>
74                     <Grid container justify="space-between" wrap="nowrap" alignItems="center">
75                         <div className={classes.searchBox}>
76                             <SearchInput
77                                 value={searchValue}
78                                 onSearch={onSearch} />
79                         </div>
80                         <ColumnSelector
81                             columns={columns}
82                             onColumnToggle={onColumnToggle} />
83                     </Grid>
84                 </Toolbar>
85                 <DataTable
86                     columns={this.props.contextMenuColumn ? [...columns, this.contextMenuColumn] : columns}
87                     items={items}
88                     onRowClick={(_, item: T) => onRowClick(item)}
89                     onContextMenu={onContextMenu}
90                     onRowDoubleClick={(_, item: T) => onRowDoubleClick(item)}
91                     onFiltersChange={onFiltersChange}
92                     onSortToggle={onSortToggle}
93                     extractKey={extractKey}
94                     working={working}
95                     defaultView={dataTableDefaultView}
96                 />
97                 <Toolbar>
98                     <Grid container justify="flex-end">
99                         <TablePagination
100                             count={itemsAvailable}
101                             rowsPerPage={rowsPerPage}
102                             rowsPerPageOptions={rowsPerPageOptions}
103                             page={this.props.page}
104                             onChangePage={this.changePage}
105                             onChangeRowsPerPage={this.changeRowsPerPage}
106                             component="div" />
107                     </Grid>
108                 </Toolbar>
109             </Paper>;
110         }
111
112         changePage = (event: React.MouseEvent<HTMLButtonElement>, page: number) => {
113             this.props.onChangePage(page);
114         }
115
116         changeRowsPerPage: React.ChangeEventHandler<HTMLTextAreaElement | HTMLInputElement> = (event) => {
117             this.props.onChangeRowsPerPage(parseInt(event.target.value, 10));
118         }
119
120         renderContextMenuTrigger = (item: T) =>
121             <Grid container justify="flex-end">
122                 <Tooltip title="More options">
123                     <IconButton onClick={event => this.props.onContextMenu(event, item)}>
124                         <MoreVertIcon />
125                     </IconButton>
126                 </Tooltip>
127             </Grid>
128
129         contextMenuColumn: DataColumn<any> = {
130             name: "Actions",
131             selected: true,
132             configurable: false,
133             sortDirection: SortDirection.NONE,
134             filters: [],
135             key: "context-actions",
136             render: this.renderContextMenuTrigger
137         };
138     }
139 );