Merge branch '13703-data-explorer-and-contents-api'
[arvados-workbench2.git] / src / components / data-table / data-table.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 { Table, TableBody, TableRow, TableCell, TableHead, TableSortLabel, StyleRulesCallback, Theme, WithStyles, withStyles, Typography } from '@material-ui/core';
7 import { DataColumn, SortDirection } from './data-column';
8 import DataTableFilters, { DataTableFilterItem } from "../data-table-filters/data-table-filters";
9
10 export type DataColumns<T, F extends DataTableFilterItem = DataTableFilterItem> = Array<DataColumn<T, F>>;
11 export interface DataItem {
12     key: React.Key;
13 }
14 export interface DataTableProps<T> {
15     items: T[];
16     columns: DataColumns<T>;
17     onRowClick: (event: React.MouseEvent<HTMLTableRowElement>, item: T) => void;
18     onRowContextMenu: (event: React.MouseEvent<HTMLTableRowElement>, item: T) => void;
19     onSortToggle: (column: DataColumn<T>) => void;
20     onFiltersChange: (filters: DataTableFilterItem[], column: DataColumn<T>) => void;
21 }
22
23 class DataTable<T extends DataItem> extends React.Component<DataTableProps<T> & WithStyles<CssRules>> {
24     render() {
25         const { items, classes } = this.props;
26         return <div className={classes.tableContainer}>
27             <Table>
28                 <TableHead>
29                     <TableRow>
30                         {this.mapVisibleColumns(this.renderHeadCell)}
31                     </TableRow>
32                 </TableHead>
33                 <TableBody className={classes.tableBody}>
34                     {items.map(this.renderBodyRow)}
35                 </TableBody>
36             </Table>
37         </div>;
38     }
39
40     renderHeadCell = (column: DataColumn<T>, index: number) => {
41         const { name, key, renderHeader, filters, sortDirection } = column;
42         const { onSortToggle, onFiltersChange } = this.props;
43         return <TableCell key={key || index} style={{width: column.width, minWidth: column.width}}>
44             {renderHeader ?
45                 renderHeader() :
46                 filters
47                     ? <DataTableFilters
48                         name={`${name} filters`}
49                         onChange={filters =>
50                             onFiltersChange &&
51                             onFiltersChange(filters, column)}
52                         filters={filters}>
53                         {name}
54                     </DataTableFilters>
55                     : sortDirection
56                         ? <TableSortLabel
57                             active={sortDirection !== SortDirection.None}
58                             direction={sortDirection !== SortDirection.None ? sortDirection : undefined}
59                             onClick={() =>
60                                 onSortToggle &&
61                                 onSortToggle(column)}>
62                             {name}
63                         </TableSortLabel>
64                         : <span>
65                             {name}
66                         </span>}
67         </TableCell>;
68     }
69
70     renderBodyRow = (item: T, index: number) => {
71         const { onRowClick, onRowContextMenu } = this.props;
72         return <TableRow
73             hover
74             key={item.key}
75             onClick={event => onRowClick && onRowClick(event, item)}
76             onContextMenu={event => onRowContextMenu && onRowContextMenu(event, item)}>
77             {this.mapVisibleColumns((column, index) => (
78                 <TableCell key={column.key || index}>
79                     {column.render(item)}
80                 </TableCell>
81             ))}
82         </TableRow>;
83     }
84
85     mapVisibleColumns = (fn: (column: DataColumn<T>, index: number) => React.ReactElement<any>) => {
86         return this.props.columns.filter(column => column.selected).map(fn);
87     }
88
89 }
90
91 type CssRules = "tableBody" | "tableContainer" | "noItemsInfo";
92
93 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
94     tableContainer: {
95         overflowX: 'auto',
96         overflowY: 'hidden'
97     },
98     tableBody: {
99         background: theme.palette.background.paper
100     },
101     noItemsInfo: {
102         textAlign: "center",
103         padding: theme.spacing.unit
104     }
105 });
106
107 export default withStyles(styles)(DataTable);