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