toolbar accurately selects for appropriate buttons Arvados-DCO-1.1-Signed-off-by...
[arvados-workbench2.git] / src / components / multiselectToolbar / MultiselectToolbar.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React, { ReactElement } from 'react';
6 import { connect } from 'react-redux';
7 import { StyleRulesCallback, withStyles, WithStyles, Toolbar, Button } from '@material-ui/core';
8 import { ArvadosTheme } from 'common/custom-theme';
9 import { RootState } from 'store/store';
10 import { Dispatch } from 'redux';
11 import { CopyToClipboardSnackbar } from 'components/copy-to-clipboard-snackbar/copy-to-clipboard-snackbar';
12 import { TCheckedList } from 'components/data-table/data-table';
13 import { openRemoveProcessDialog, openRemoveManyProcessesDialog } from 'store/processes/processes-actions';
14 import { processResourceActionSet } from '../../views-components/context-menu/action-sets/process-resource-action-set';
15 import { ContextMenuResource } from 'store/context-menu/context-menu-actions';
16 import { toggleTrashed } from 'store/trash/trash-actions';
17 import { ResourceKind, extractUuidKind } from 'models/resource';
18
19 type CssRules = 'root' | 'expanded' | 'button';
20
21 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
22     root: {
23         display: 'flex',
24         flexDirection: 'row',
25         width: 0,
26         padding: 0,
27         margin: '1rem auto auto 0.5rem',
28         overflow: 'hidden',
29     },
30     expanded: {
31         transition: 'width 150ms',
32     },
33     button: {
34         backgroundColor: '#017ead',
35         color: 'white',
36         fontSize: '0.75rem',
37         width: 'auto',
38         margin: 'auto',
39         padding: '1px',
40     },
41 });
42
43 type MultiselectToolbarAction = {
44     name: string;
45     action: string;
46     relevantKinds: Array<ResourceKind>;
47 };
48
49 export const defaultActions: Array<MultiselectToolbarAction> = [
50     {
51         name: 'copy',
52         action: 'copySelected',
53         relevantKinds: [ResourceKind.COLLECTION],
54     },
55     {
56         name: 'move',
57         action: 'moveSelected',
58         relevantKinds: [ResourceKind.COLLECTION, ResourceKind.PROCESS],
59     },
60     {
61         name: 'remove',
62         action: 'removeSelected',
63         relevantKinds: [ResourceKind.COLLECTION, ResourceKind.PROCESS, ResourceKind.PROJECT],
64     },
65     {
66         name: 'foo',
67         action: 'barSelected',
68         relevantKinds: [ResourceKind.COLLECTION, ResourceKind.PROJECT],
69     },
70 ];
71
72 export type MultiselectToolbarProps = {
73     actions: Array<MultiselectToolbarAction>;
74     isVisible: boolean;
75     checkedList: TCheckedList;
76     copySelected: () => void;
77     moveSelected: () => void;
78     barSelected: () => void;
79     removeSelected: (selectedList: TCheckedList) => void;
80 };
81
82 export const MultiselectToolbar = connect(
83     mapStateToProps,
84     mapDispatchToProps
85 )(
86     withStyles(styles)((props: MultiselectToolbarProps & WithStyles<CssRules>) => {
87         // console.log(props);
88         const { classes, actions, isVisible, checkedList } = props;
89
90         const currentResourceKinds = selectedToArray(checkedList).map((element) => extractUuidKind(element));
91
92         const buttons = actions.filter(
93             (action) => currentResourceKinds.length && currentResourceKinds.every((kind) => action.relevantKinds.includes(kind as ResourceKind))
94         );
95
96         return (
97             <Toolbar className={isVisible && buttons.length ? `${classes.root} ${classes.expanded}` : classes.root} style={{ width: `${buttons.length * 5.5}rem` }}>
98                 {buttons.length ? (
99                     buttons.map((btn) => (
100                         <Button key={btn.name} className={`${classes.button} ${classes.expanded}`} onClick={() => props[btn.action](checkedList)}>
101                             {btn.name}
102                         </Button>
103                     ))
104                 ) : (
105                     <></>
106                 )}
107             </Toolbar>
108         );
109     })
110 );
111
112 function selectedToString(checkedList: TCheckedList) {
113     let stringifiedSelectedList: string = '';
114     for (const [key, value] of Object.entries(checkedList)) {
115         if (value === true) {
116             stringifiedSelectedList += key + ',';
117         }
118     }
119     return stringifiedSelectedList.slice(0, -1);
120 }
121
122 function selectedToArray<T>(checkedList: TCheckedList): Array<string> {
123     const arrayifiedSelectedList: Array<string> = [];
124     for (const [key, value] of Object.entries(checkedList)) {
125         if (value === true) {
126             arrayifiedSelectedList.push(key);
127         }
128     }
129     return arrayifiedSelectedList;
130 }
131
132 function mapStateToProps(state: RootState) {
133     const { isVisible, checkedList } = state.multiselect;
134     return {
135         isVisible: isVisible,
136         checkedList: checkedList as TCheckedList,
137     };
138 }
139
140 function mapDispatchToProps(dispatch: Dispatch) {
141     return {
142         copySelected: () => {},
143         moveSelected: () => {},
144         barSelected: () => {},
145         removeSelected: (checkedList: TCheckedList) => removeMulti(dispatch, checkedList),
146     };
147 }
148
149 function removeMulti(dispatch: Dispatch, checkedList: TCheckedList): void {
150     const list: Array<string> = selectedToArray(checkedList);
151     dispatch<any>(list.length === 1 ? openRemoveProcessDialog(list[0]) : openRemoveManyProcessesDialog(list));
152 }