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