X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/4ff3ff000c1a0132d29ea4829907f484467b6275..cfe5ff035578ede95613b0c545708466da78cbea:/src/components/data-table-filters/data-table-filters-popover.tsx

diff --git a/src/components/data-table-filters/data-table-filters-popover.tsx b/src/components/data-table-filters/data-table-filters-popover.tsx
index 7033d369..3183157b 100644
--- a/src/components/data-table-filters/data-table-filters-popover.tsx
+++ b/src/components/data-table-filters/data-table-filters-popover.tsx
@@ -2,7 +2,7 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import * as React from "react";
+import React from "react";
 import {
     WithStyles,
     withStyles,
@@ -10,25 +10,21 @@ import {
     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 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';
 
-export type CssRules = "root" | "icon" | "active" | "checkbox";
+export type CssRules = "root" | "icon" | "iconButton" | "active" | "checkbox";
 
 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
     root: {
@@ -46,16 +42,20 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
     },
     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,
@@ -63,59 +63,63 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
     }
 });
 
-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
@@ -130,28 +134,24 @@ export const DataTableFilters = withStyles(styles)(
                                 {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
@@ -164,6 +164,7 @@ export const DataTableFilters = withStyles(styles)(
                                 Cancel
                             </Button>
                         </CardActions >
+                        }
                     </Card>
                 </Popover>
             </>;
@@ -195,14 +196,9 @@ export const DataTableFilters = withStyles(styles)(
             }));
         }
 
-        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 });
         }
+
     }
 );
