1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import * as React from "react";
20 } from "@material-ui/core";
21 import * as classnames from "classnames";
22 import { DefaultTransformOrigin } from "~/components/popover/helpers";
23 import { createTree } from '~/models/tree';
24 import { DataTableFilters, DataTableFiltersTree } from "./data-table-filters-tree";
25 import { getNodeDescendants } from '~/models/tree';
27 export type CssRules = "root" | "icon" | "iconButton" | "active" | "checkbox";
29 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
32 display: "inline-flex",
33 justifyContent: "flex-start",
34 flexDirection: "inherit",
37 color: theme.palette.text.primary,
40 color: theme.palette.text.primary,
44 color: theme.palette.text.primary,
57 color: theme.palette.text.primary,
71 export interface DataTableFilterProps {
73 filters: DataTableFilters;
74 onChange?: (filters: DataTableFilters) => void;
77 * When set to true, only one filter can be selected at a time.
79 mutuallyExclusive?: boolean;
82 * By default `all` filters selection means that label should be grayed out.
83 * Use `none` when label is supposed to be grayed out when no filter is selected.
85 defaultSelection?: SelectionMode;
88 interface DataTableFilterState {
89 anchorEl?: HTMLElement;
90 filters: DataTableFilters;
91 prevFilters: DataTableFilters;
94 export const DataTableFiltersPopover = withStyles(styles)(
95 class extends React.Component<DataTableFilterProps & WithStyles<CssRules>, DataTableFilterState> {
96 state: DataTableFilterState = {
98 filters: createTree(),
99 prevFilters: createTree(),
101 icon = React.createRef<HTMLElement>();
104 const { name, classes, defaultSelection = SelectionMode.ALL, children } = this.props;
105 const isActive = getNodeDescendants('')(this.state.filters)
106 .some(f => defaultSelection === SelectionMode.ALL
111 <Tooltip title='Filters'>
113 className={classnames([classes.root, { [classes.active]: isActive }])}
118 <IconButton component='span' classes={{ root: classes.iconButton }} tabIndex={-1}>
119 <i className={classnames(["fas fa-filter", classes.icon])}
120 data-fa-transform="shrink-3"
126 anchorEl={this.state.anchorEl}
127 open={!!this.state.anchorEl}
128 anchorOrigin={DefaultTransformOrigin}
129 transformOrigin={DefaultTransformOrigin}
130 onClose={this.cancel}>
133 <Typography variant="caption">
137 <DataTableFiltersTree
138 filters={this.state.filters}
139 mutuallyExclusive={this.props.mutuallyExclusive}
140 onChange={filters => {
141 this.setState({ filters });
142 if (this.props.mutuallyExclusive) {
143 const { onChange } = this.props;
147 this.setState({ anchorEl: undefined });
150 {this.props.mutuallyExclusive ||
156 onClick={this.submit}>
163 onClick={this.cancel}>
173 static getDerivedStateFromProps(props: DataTableFilterProps, state: DataTableFilterState): DataTableFilterState {
174 return props.filters !== state.prevFilters
175 ? { ...state, filters: props.filters, prevFilters: props.filters }
180 this.setState({ anchorEl: this.icon.current || undefined });
184 const { onChange } = this.props;
186 onChange(this.state.filters);
188 this.setState({ anchorEl: undefined });
192 this.setState(prev => ({
194 filters: prev.prevFilters,
199 setFilters = (filters: DataTableFilters) => {
200 this.setState({ filters });