StyleRulesCallback,
Theme,
Popover,
- List,
- ListItem,
- Checkbox,
- ListItemText,
Button,
Card,
CardActions,
Typography,
CardContent,
- Tooltip
+ Tooltip,
+ IconButton
} from "@material-ui/core";
import * as classnames from "classnames";
-import { DefaultTransformOrigin } from "../popover/helpers";
-import { createTree, initTreeNode, mapTree } from '~/models/tree';
-import { DataTableFilters as DataTableFiltersModel, DataTableFiltersTree } from "./data-table-filters-tree";
-import { pipe } from 'lodash/fp';
-import { setNode } from '~/models/tree';
+import { DefaultTransformOrigin } from "~/components/popover/helpers";
+import { createTree } from '~/models/tree';
+import { DataTableFilters, DataTableFiltersTree } from "./data-table-filters-tree";
+import { getNodeDescendants } from '~/models/tree';
-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,
}
});
-export interface DataTableFilterItem {
- name: string;
- selected: boolean;
+enum SelectionMode {
+ ALL = 'all',
+ NONE = 'none'
}
export interface DataTableFilterProps {
name: string;
- filters: DataTableFilterItem[];
- onChange?: (filters: DataTableFilterItem[]) => void;
+ 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 {
anchorEl?: HTMLElement;
- filters: DataTableFilterItem[];
- prevFilters: DataTableFilterItem[];
- filtersTree: DataTableFiltersModel;
+ filters: DataTableFilters;
+ prevFilters: DataTableFilters;
}
-const filters: DataTableFiltersModel = pipe(
- createTree,
- setNode(initTreeNode({ id: 'Project', value: { name: 'Project' } })),
- setNode(initTreeNode({ id: 'Process', value: { name: 'Process' } })),
- setNode(initTreeNode({ id: 'Data collection', value: { name: 'Data collection' } })),
- setNode(initTreeNode({ id: 'General', parent: 'Data collection', value: { name: 'General' } })),
- setNode(initTreeNode({ id: 'Output', parent: 'Data collection', value: { name: 'Output' } })),
- setNode(initTreeNode({ id: 'Log', parent: 'Data collection', value: { name: 'Log' } })),
- mapTree(node => ({...node, selected: true})),
-)();
-
-export const DataTableFilters = withStyles(styles)(
+export const DataTableFiltersPopover = withStyles(styles)(
class extends React.Component<DataTableFilterProps & WithStyles<CssRules>, DataTableFilterState> {
state: DataTableFilterState = {
anchorEl: undefined,
- filters: [],
- prevFilters: [],
- filtersTree: filters,
+ filters: createTree(),
+ prevFilters: createTree(),
};
icon = React.createRef<HTMLElement>();
render() {
- const { name, classes, children } = this.props;
- const isActive = 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
{name}
</Typography>
</CardContent>
- <List dense>
- {this.state.filters.map((filter, index) =>
- <ListItem
- key={index}>
- <Checkbox
- onClick={this.toggleFilter(filter)}
- color="primary"
- checked={filter.selected}
- className={classes.checkbox} />
- <ListItemText>
- {filter.name}
- </ListItemText>
- </ListItem>
- )}
- </List>
<DataTableFiltersTree
- filters={this.state.filtersTree}
- onChange={filtersTree => this.setState({ filtersTree })} />
+ filters={this.state.filters}
+ mutuallyExclusive={this.props.mutuallyExclusive}
+ onChange={filters => {
+ this.setState({ filters });
+ if (this.props.mutuallyExclusive) {
+ const { onChange } = this.props;
+ if (onChange) {
+ onChange(filters);
+ }
+ this.setState({ anchorEl: undefined });
+ }
+ }} />
+ {this.props.mutuallyExclusive ||
<CardActions>
<Button
color="primary"
- variant="raised"
+ variant='contained'
size="small"
onClick={this.submit}>
Ok
Cancel
</Button>
</CardActions >
+ }
</Card>
</Popover>
</>;
}));
}
- toggleFilter = (toggledFilter: DataTableFilterItem) => () => {
- this.setState(prev => ({
- ...prev,
- filters: prev.filters.map(filter =>
- filter === toggledFilter
- ? { ...filter, selected: !filter.selected }
- : filter)
- }));
+ setFilters = (filters: DataTableFilters) => {
+ this.setState({ filters });
}
+
}
);