//
// SPDX-License-Identifier: AGPL-3.0
-import * as React from "react";
+import React, { useEffect } from "react";
import {
WithStyles,
withStyles,
CardActions,
Typography,
CardContent,
- Tooltip
+ Tooltip,
+ IconButton
} from "@material-ui/core";
-import * as classnames from "classnames";
-import { DefaultTransformOrigin } from "~/components/popover/helpers";
-import { createTree } from '~/models/tree';
+import classnames from "classnames";
+import { DefaultTransformOrigin } from "components/popover/helpers";
+import { createTree } from 'models/tree';
import { DataTableFilters, DataTableFiltersTree } from "./data-table-filters-tree";
-import { getNodeDescendants } from '~/models/tree';
+import { getNodeDescendants } from 'models/tree';
+import debounce from "lodash/debounce";
-export type CssRules = "root" | "icon" | "active" | "checkbox";
+export type CssRules = "root" | "icon" | "iconButton" | "active" | "checkbox";
const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
root: {
},
active: {
color: theme.palette.text.primary,
- '& $icon': {
+ '& $iconButton': {
opacity: 1,
},
},
icon: {
- marginRight: 4,
- marginLeft: 4,
+ fontSize: 12,
+ userSelect: 'none',
+ width: 16,
+ height: 15,
+ marginTop: 1
+ },
+ iconButton: {
+ color: theme.palette.text.primary,
opacity: 0.7,
- userSelect: "none",
- width: 16
},
checkbox: {
width: 24,
}
});
+enum SelectionMode {
+ ALL = 'all',
+ NONE = 'none'
+}
+
export interface DataTableFilterProps {
name: string;
filters: DataTableFilters;
onChange?: (filters: DataTableFilters) => void;
+
+ /**
+ * When set to true, only one filter can be selected at a time.
+ */
+ mutuallyExclusive?: boolean;
+
+ /**
+ * By default `all` filters selection means that label should be grayed out.
+ * Use `none` when label is supposed to be grayed out when no filter is selected.
+ */
+ defaultSelection?: SelectionMode;
}
interface DataTableFilterState {
icon = React.createRef<HTMLElement>();
render() {
- const { name, classes, children } = this.props;
- const isActive = getNodeDescendants('')(this.state.filters).some(f => f.selected);
+ const { name, classes, defaultSelection = SelectionMode.ALL, children } = this.props;
+ const isActive = getNodeDescendants('')(this.state.filters)
+ .some(f => defaultSelection === SelectionMode.ALL
+ ? !f.selected
+ : f.selected
+ );
return <>
- <Tooltip title='Filters'>
+ <Tooltip disableFocusListener title='Filters'>
<ButtonBase
className={classnames([classes.root, { [classes.active]: isActive }])}
component="span"
onClick={this.open}
disableRipple>
{children}
- <i className={classnames(["fas fa-filter", classes.icon])}
- data-fa-transform="shrink-3"
- ref={this.icon} />
+ <IconButton component='span' classes={{ root: classes.iconButton }} tabIndex={-1}>
+ <i className={classnames(["fas fa-filter", classes.icon])}
+ data-fa-transform="shrink-3"
+ ref={this.icon} />
+ </IconButton>
</ButtonBase>
</Tooltip>
<Popover
open={!!this.state.anchorEl}
anchorOrigin={DefaultTransformOrigin}
transformOrigin={DefaultTransformOrigin}
- onClose={this.cancel}>
+ onClose={this.close}>
<Card>
<CardContent>
<Typography variant="caption">
</CardContent>
<DataTableFiltersTree
filters={this.state.filters}
- onChange={filters => this.setState({ filters })} />
+ mutuallyExclusive={this.props.mutuallyExclusive}
+ onChange={this.onChange} />
+ {this.props.mutuallyExclusive ||
<CardActions>
- <Button
- color="primary"
- variant="raised"
- size="small"
- onClick={this.submit}>
- Ok
- </Button>
<Button
color="primary"
variant="outlined"
size="small"
- onClick={this.cancel}>
- Cancel
+ onClick={this.close}>
+ Close
</Button>
</CardActions >
+ }
</Card>
</Popover>
+ <this.MountHandler />
</>;
}
this.setState({ anchorEl: this.icon.current || undefined });
}
- submit = () => {
+ onChange = (filters) => {
+ this.setState({ filters });
+ if (this.props.mutuallyExclusive) {
+ // Mutually exclusive filters apply immediately
+ const { onChange } = this.props;
+ if (onChange) {
+ onChange(filters);
+ }
+ this.close();
+ } else {
+ // Non-mutually exclusive filters are debounced
+ this.submit();
+ }
+ }
+
+ submit = debounce (() => {
const { onChange } = this.props;
if (onChange) {
onChange(this.state.filters);
}
- this.setState({ anchorEl: undefined });
- }
+ }, 1000);
- cancel = () => {
+ MountHandler = () => {
+ useEffect(() => {
+ return () => {
+ this.submit.cancel();
+ }
+ },[]);
+ return null;
+ };
+
+ close = () => {
this.setState(prev => ({
...prev,
- filters: prev.prevFilters,
anchorEl: undefined
}));
}
- setFilters = (filters: DataTableFilters) => {
- this.setState({ filters });
- }
-
}
);