Implement better pattern for hanling actions in context menu
[arvados.git] / src / components / side-panel / side-panel.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import { ReactElement } from 'react';
7 import { StyleRulesCallback, Theme, WithStyles, withStyles } from '@material-ui/core/styles';
8 import List from "@material-ui/core/List/List";
9 import ListItem from "@material-ui/core/ListItem/ListItem";
10 import ListItemText from "@material-ui/core/ListItemText/ListItemText";
11 import ListItemIcon from '@material-ui/core/ListItemIcon';
12 import Collapse from "@material-ui/core/Collapse/Collapse";
13
14 import { Typography } from '@material-ui/core';
15
16 export interface SidePanelItem {
17     id: string;
18     name: string;
19     icon: string;
20     active?: boolean;
21     open?: boolean;
22     margin?: boolean;
23     openAble?: boolean;
24 }
25
26 interface SidePanelProps {
27     toggleOpen: (id: string) => void;
28     toggleActive: (id: string) => void;
29     sidePanelItems: SidePanelItem[];
30     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: SidePanelItem) => void;
31 }
32
33 class SidePanel extends React.Component<SidePanelProps & WithStyles<CssRules>> {
34     render(): ReactElement<any> {
35         const { classes, toggleOpen, toggleActive, sidePanelItems, children } = this.props;
36         const { listItemText, leftSidePanelContainer, row, list, icon, projectIconMargin, active, activeArrow, inactiveArrow, arrowTransition, arrowRotate } = classes;
37         return (
38             <div className={leftSidePanelContainer}>
39                 <List>
40                     {sidePanelItems.map(it => (
41                         <span key={it.name}>
42                             <ListItem button className={list} onClick={() => toggleActive(it.id)} onContextMenu={this.handleRowContextMenu(it)}>
43                                 <span className={row}>
44                                     {it.openAble ? <i onClick={() => toggleOpen(it.id)} className={`${it.active ? activeArrow : inactiveArrow} 
45                                         ${it.open ? `fas fa-caret-down ${arrowTransition}` : `fas fa-caret-down ${arrowRotate}`}`} /> : null}
46                                     <ListItemIcon className={it.active ? active : ''}>
47                                         <i className={`${it.icon} ${icon} ${it.margin ? projectIconMargin : ''}`} />
48                                     </ListItemIcon>
49                                     <ListItemText className={listItemText} primary={<Typography className={it.active ? active : ''}>{it.name}</Typography>} />
50                                 </span>
51                             </ListItem>
52                             {it.openAble ? (
53                                 <Collapse in={it.open} timeout="auto" unmountOnExit>
54                                     {children}
55                                 </Collapse>) : null}
56                         </span>
57                     ))}
58                 </List>
59             </div>
60         );
61     }
62
63     handleRowContextMenu = (item: SidePanelItem) =>
64         (event: React.MouseEvent<HTMLElement>) =>
65             item.openAble ? this.props.onContextMenu(event, item) : null
66
67 }
68
69 type CssRules = 'active' | 'listItemText' | 'row' | 'leftSidePanelContainer' | 'list' | 'icon' | 'projectIconMargin' |
70     'activeArrow' | 'inactiveArrow' | 'arrowRotate' | 'arrowTransition';
71
72 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
73     active: {
74         color: '#4285F6',
75     },
76     listItemText: {
77         padding: '0px',
78     },
79     row: {
80         display: 'flex',
81         alignItems: 'center',
82     },
83     activeArrow: {
84         color: '#4285F6',
85         position: 'absolute',
86     },
87     inactiveArrow: {
88         position: 'absolute',
89     },
90     arrowTransition: {
91         transition: 'all 0.1s ease',
92     },
93     arrowRotate: {
94         transition: 'all 0.1s ease',
95         transform: 'rotate(-90deg)',
96     },
97     leftSidePanelContainer: {
98         overflowY: 'auto',
99         minWidth: '240px',
100         whiteSpace: 'nowrap',
101         marginTop: '52px',
102         display: 'flex',
103         flexGrow: 1,
104     },
105     list: {
106         paddingBottom: '5px',
107         paddingTop: '5px',
108         paddingLeft: '14px',
109         minWidth: '240px',
110     },
111     icon: {
112         minWidth: '20px',
113     },
114     projectIconMargin: {
115         marginLeft: '17px',
116     }
117 });
118
119 export default withStyles(styles)(SidePanel);