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