11607e4b22d108277f4f2dd8c32028ec88bcb36c
[arvados-workbench2.git] / src / components / data-table-filters / data-table-filters.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 {
7     WithStyles,
8     withStyles,
9     ButtonBase,
10     StyleRulesCallback,
11     Theme,
12     Popover,
13     List,
14     ListItem,
15     Checkbox,
16     ListItemText,
17     Button,
18     Card,
19     CardActions,
20     Typography,
21     CardContent,
22     Tooltip
23 } from "@material-ui/core";
24 import * as classnames from "classnames";
25 import { DefaultTransformOrigin } from "../popover/helpers";
26
27 export type CssRules = "root" | "icon" | "active" | "checkbox";
28
29 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
30     root: {
31         cursor: "pointer",
32         display: "inline-flex",
33         justifyContent: "flex-start",
34         flexDirection: "inherit",
35         alignItems: "center",
36         "&:hover": {
37             color: theme.palette.text.primary,
38         },
39         "&:focus": {
40             color: theme.palette.text.primary,
41         },
42     },
43     active: {
44         color: theme.palette.text.primary,
45         '& $icon': {
46             opacity: 1,
47         },
48     },
49     icon: {
50         marginRight: 4,
51         marginLeft: 4,
52         opacity: 0.7,
53         userSelect: "none",
54         width: 16
55     },
56     checkbox: {
57         width: 24,
58         height: 24
59     }
60 });
61
62 export interface DataTableFilterItem {
63     name: string;
64     selected: boolean;
65 }
66
67 export interface DataTableFilterProps {
68     name: string;
69     filters: DataTableFilterItem[];
70     onChange?: (filters: DataTableFilterItem[]) => void;
71 }
72
73 interface DataTableFilterState {
74     anchorEl?: HTMLElement;
75     filters: DataTableFilterItem[];
76     prevFilters: DataTableFilterItem[];
77 }
78
79 export const DataTableFilters = withStyles(styles)(
80     class extends React.Component<DataTableFilterProps & WithStyles<CssRules>, DataTableFilterState> {
81         state: DataTableFilterState = {
82             anchorEl: undefined,
83             filters: [],
84             prevFilters: []
85         };
86         icon = React.createRef<HTMLElement>();
87
88         render() {
89             const { name, classes, children } = this.props;
90             const isActive = this.state.filters.some(f => f.selected);
91             return <>
92                 <Tooltip title='Filters'>
93                     <ButtonBase
94                         className={classnames([classes.root, { [classes.active]: isActive }])}
95                         component="span"
96                         onClick={this.open}
97                         disableRipple>
98                         {children}
99                         <i className={classnames(["fas fa-filter", classes.icon])}
100                             data-fa-transform="shrink-3"
101                             ref={this.icon} />
102                     </ButtonBase>
103                 </Tooltip>
104                 <Popover
105                     anchorEl={this.state.anchorEl}
106                     open={!!this.state.anchorEl}
107                     anchorOrigin={DefaultTransformOrigin}
108                     transformOrigin={DefaultTransformOrigin}
109                     onClose={this.cancel}>
110                     <Card>
111                         <CardContent>
112                             <Typography variant="caption">
113                                 {name}
114                             </Typography>
115                         </CardContent>
116                         <List dense>
117                             {this.state.filters.map((filter, index) =>
118                                 <ListItem
119                                     key={index}>
120                                     <Checkbox
121                                         onClick={this.toggleFilter(filter)}
122                                         color="primary"
123                                         checked={filter.selected}
124                                         className={classes.checkbox} />
125                                     <ListItemText>
126                                         {filter.name}
127                                     </ListItemText>
128                                 </ListItem>
129                             )}
130                         </List>
131                         <CardActions>
132                             <Button
133                                 color="primary"
134                                 variant="raised"
135                                 size="small"
136                                 onClick={this.submit}>
137                                 Ok
138                             </Button>
139                             <Button
140                                 color="primary"
141                                 variant="outlined"
142                                 size="small"
143                                 onClick={this.cancel}>
144                                 Cancel
145                             </Button>
146                         </CardActions >
147                     </Card>
148                 </Popover>
149             </>;
150         }
151
152         static getDerivedStateFromProps(props: DataTableFilterProps, state: DataTableFilterState): DataTableFilterState {
153             return props.filters !== state.prevFilters
154                 ? { ...state, filters: props.filters, prevFilters: props.filters }
155                 : state;
156         }
157
158         open = () => {
159             this.setState({ anchorEl: this.icon.current || undefined });
160         }
161
162         submit = () => {
163             const { onChange } = this.props;
164             if (onChange) {
165                 onChange(this.state.filters);
166             }
167             this.setState({ anchorEl: undefined });
168         }
169
170         cancel = () => {
171             this.setState(prev => ({
172                 ...prev,
173                 filters: prev.prevFilters,
174                 anchorEl: undefined
175             }));
176         }
177
178         toggleFilter = (toggledFilter: DataTableFilterItem) => () => {
179             this.setState(prev => ({
180                 ...prev,
181                 filters: prev.filters.map(filter =>
182                     filter === toggledFilter
183                         ? { ...filter, selected: !filter.selected }
184                         : filter)
185             }));
186         }
187     }
188 );