Merge branch 'main' into 19302-left-side-panel-changes
[arvados-workbench2.git] / src / components / data-table-multiselect-popover / data-table-multiselect-popover.tsx
index 74b083a93a089f614b8d8da668d23b15a30fb539..0248c8267d9201455d7c558d3815ac5a4b2eaf81 100644 (file)
@@ -2,58 +2,33 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import React, { useEffect } from 'react';
-import {
-    WithStyles,
-    withStyles,
-    ButtonBase,
-    StyleRulesCallback,
-    Theme,
-    Popover,
-    Button,
-    Card,
-    CardActions,
-    Typography,
-    CardContent,
-    Tooltip,
-    IconButton,
-} from '@material-ui/core';
-import classnames from 'classnames';
-import { DefaultTransformOrigin } from 'components/popover/helpers';
-import { createTree } from 'models/tree';
-import { DataTableFilters, DataTableFiltersTree } from './data-table-multiselect-tree';
-import { getNodeDescendants } from 'models/tree';
-import debounce from 'lodash/debounce';
-import { green, grey } from '@material-ui/core/colors';
+import React from "react";
+import { WithStyles, withStyles, ButtonBase, StyleRulesCallback, Theme, Popover, Card, Tooltip, IconButton } from "@material-ui/core";
+import classnames from "classnames";
+import { DefaultTransformOrigin } from "components/popover/helpers";
+import { grey } from "@material-ui/core/colors";
+import { TCheckedList } from "components/data-table/data-table";
 
-export type CssRules = 'root' | 'icon' | 'iconButton' | 'active' | 'checkbox';
+export type CssRules = "root" | "icon" | "iconButton" | "disabled" | "optionsContainer" | "option";
 
 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
     root: {
-        // border: '1px dashed green',
-        margin: 0,
-        borderRadius: '7px',
-        '&:hover': {
+        borderRadius: "7px",
+        "&:hover": {
             backgroundColor: grey[200],
         },
-        '&:focus': {
+        "&:focus": {
             color: theme.palette.text.primary,
         },
     },
-    active: {
-        color: theme.palette.text.primary,
-        '& $iconButton': {
-            opacity: 1,
-        },
-    },
     icon: {
-        // border: '1px solid red',
-        cursor: 'pointer',
+        cursor: "pointer",
         fontSize: 20,
-        userSelect: 'none',
-        '&:hover': {
+        userSelect: "none",
+        "&:hover": {
             color: theme.palette.text.primary,
         },
+        paddingBottom: "5px",
     },
     iconButton: {
         color: theme.palette.text.primary,
@@ -61,59 +36,73 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
         padding: 1,
         paddingBottom: 5,
     },
-    checkbox: {
-        width: 24,
-        height: 24,
+    disabled: {
+        color: grey[500],
+    },
+    optionsContainer: {
+        padding: "1rem 0",
+        flex: 1,
+    },
+    option: {
+        cursor: "pointer",
+        display: "flex",
+        padding: "3px 2rem",
+        fontSize: "0.9rem",
+        alignItems: "center",
+        "&:hover": {
+            backgroundColor: "rgba(0, 0, 0, 0.08)",
+        },
     },
 });
 
-enum SelectionMode {
-    ALL = 'all',
-    NONE = 'none',
-}
-
-export interface DataTableFilterProps {
+export type DataTableMultiselectOption = {
     name: string;
-    filters: DataTableFilters;
-    onChange?: (filters: DataTableFilters) => void;
-
-    /**
-     * When set to true, only one filter can be selected at a time.
-     */
-    mutuallyExclusive?: boolean;
+    fn: (checkedList) => void;
+};
 
-    /**
-     * 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;
+export interface DataTableMultiselectProps {
+    name: string;
+    disabled: boolean;
+    options: DataTableMultiselectOption[];
+    checkedList: TCheckedList;
 }
 
-interface DataTableFilterState {
+interface DataTableFMultiselectPopState {
     anchorEl?: HTMLElement;
-    filters: DataTableFilters;
-    prevFilters: DataTableFilters;
 }
 
 export const DataTableMultiselectPopover = withStyles(styles)(
-    class extends React.Component<DataTableFilterProps & WithStyles<CssRules>, DataTableFilterState> {
-        state: DataTableFilterState = {
+    class extends React.Component<DataTableMultiselectProps & WithStyles<CssRules>, DataTableFMultiselectPopState> {
+        state: DataTableFMultiselectPopState = {
             anchorEl: undefined,
-            filters: createTree(),
-            prevFilters: createTree(),
         };
         icon = React.createRef<HTMLElement>();
 
         render() {
-            const { name, classes, defaultSelection = SelectionMode.ALL, children } = this.props;
-            const isActive = getNodeDescendants('')(this.state.filters).some((f) => (defaultSelection === SelectionMode.ALL ? !f.selected : f.selected));
+            const { classes, children, options, checkedList, disabled } = this.props;
             return (
                 <>
-                    <Tooltip disableFocusListener title='Multiselect Actions'>
-                        <ButtonBase className={classnames([classes.root, { [classes.active]: isActive }])} component='span' onClick={this.open} disableRipple>
+                    <Tooltip
+                        disableFocusListener
+                        title="Select Options"
+                    >
+                        <ButtonBase
+                            className={classnames(classes.root)}
+                            component="span"
+                            onClick={disabled ? () => {} : this.open}
+                            disableRipple
+                        >
                             {children}
-                            <IconButton component='span' classes={{ root: classes.iconButton }} tabIndex={-1}>
-                                <i className={classnames(['fas fa-sort-down', classes.icon])} data-fa-transform='shrink-3' ref={this.icon} />
+                            <IconButton
+                                component="span"
+                                classes={{ root: classes.iconButton }}
+                                tabIndex={-1}
+                            >
+                                <i
+                                    className={`${classnames(["fas fa-sort-down", classes.icon])}${disabled ? ` ${classes.disabled}` : ""}`}
+                                    data-fa-transform="shrink-3"
+                                    ref={this.icon}
+                                />
                             </IconButton>
                         </ButtonBase>
                     </Tooltip>
@@ -125,65 +114,33 @@ export const DataTableMultiselectPopover = withStyles(styles)(
                         onClose={this.close}
                     >
                         <Card>
-                            <CardContent>
-                                <Typography variant='caption'>{'foo'}</Typography>
-                            </CardContent>
-                            <DataTableFiltersTree filters={this.state.filters} mutuallyExclusive={this.props.mutuallyExclusive} onChange={this.onChange} />
-                            {this.props.mutuallyExclusive || (
-                                <CardActions>
-                                    <Button color='primary' variant='outlined' size='small' onClick={this.close}>
-                                        Close
-                                    </Button>
-                                </CardActions>
-                            )}
+                            <div className={classes.optionsContainer}>
+                                {options.length &&
+                                    options.map((option, i) => (
+                                        <div
+                                            key={i}
+                                            className={classes.option}
+                                            onClick={() => {
+                                                option.fn(checkedList);
+                                                this.close();
+                                            }}
+                                        >
+                                            {option.name}
+                                        </div>
+                                    ))}
+                            </div>
                         </Card>
                     </Popover>
-                    <this.MountHandler />
                 </>
             );
         }
 
-        static getDerivedStateFromProps(props: DataTableFilterProps, state: DataTableFilterState): DataTableFilterState {
-            return props.filters !== state.prevFilters ? { ...state, filters: props.filters, prevFilters: props.filters } : state;
-        }
-
         open = () => {
             this.setState({ anchorEl: this.icon.current || undefined });
         };
 
-        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);
-            }
-        }, 1000);
-
-        MountHandler = () => {
-            useEffect(() => {
-                return () => {
-                    this.submit.cancel();
-                };
-            }, []);
-            return null;
-        };
-
         close = () => {
-            this.setState((prev) => ({
+            this.setState(prev => ({
                 ...prev,
                 anchorEl: undefined,
             }));