Add favorite panel
[arvados-workbench2.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, WithStyles, withStyles } from '@material-ui/core/styles';
8 import { ArvadosTheme } from '../../common/custom-theme';
9 import { List, ListItem, ListItemIcon, Collapse } from "@material-ui/core";
10 import { SidePanelRightArrowIcon, IconType } from '../icon/icon';
11 import * as classnames from "classnames";
12 import { ListItemTextIcon } from '../list-item-text-icon/list-item-text-icon';
13
14 type CssRules = 'active' | 'row' | 'root' | 'list' | 'iconClose' | 'iconOpen' | 'toggableIconContainer' | 'toggableIcon';
15
16 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
17     root: {
18         overflowY: 'auto',
19         minWidth: '240px',
20         whiteSpace: 'nowrap',
21         marginTop: '52px',
22         display: 'flex',
23         flexGrow: 1,
24     },
25     list: {
26         padding: '5px 0px 5px 14px',
27         minWidth: '240px',
28     },
29     row: {
30         display: 'flex',
31         alignItems: 'center',
32     },
33     toggableIconContainer: {
34         color: theme.palette.grey["700"],
35         height: '14px',
36         position: 'absolute'
37     },
38     toggableIcon: {
39         fontSize: '14px'
40     },
41     active: {
42         color: theme.palette.primary.main,
43     },
44     iconClose: {
45         transition: 'all 0.1s ease',
46     },
47     iconOpen: {
48         transition: 'all 0.1s ease',
49         transform: 'rotate(90deg)',
50     }
51 });
52
53 export interface SidePanelItem {
54     id: string;
55     name: string;
56     icon: IconType;
57     active?: boolean;
58     open?: boolean;
59     margin?: boolean;
60     openAble?: boolean;
61     path?: string;
62 }
63
64 interface SidePanelDataProps {
65     toggleOpen: (id: string) => void;
66     toggleActive: (id: string) => void;
67     sidePanelItems: SidePanelItem[];
68     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: SidePanelItem) => void;
69 }
70
71 type SidePanelProps = SidePanelDataProps & WithStyles<CssRules>;
72
73 export const SidePanel = withStyles(styles)(
74     class extends React.Component<SidePanelProps> {
75         render(): ReactElement<any> {
76             const { classes, toggleOpen, toggleActive, sidePanelItems, children } = this.props;
77             const { root, row, list, toggableIconContainer } = classes;
78             return (
79                 <div className={root}>
80                     <List>
81                         {sidePanelItems.map(it => (
82                             <span key={it.name}>
83                                 <ListItem button className={list} onClick={() => toggleActive(it.id)}
84                                           onContextMenu={this.handleRowContextMenu(it)}>
85                                     <span className={row}>
86                                         {it.openAble ? (
87                                             <i onClick={() => toggleOpen(it.id)} className={toggableIconContainer}>
88                                                 <ListItemIcon
89                                                     className={this.getToggableIconClassNames(it.open, it.active)}>
90                                                     < SidePanelRightArrowIcon/>
91                                                 </ListItemIcon>
92                                             </i>
93                                         ) : null}
94                                         <ListItemTextIcon icon={it.icon} name={it.name} isActive={it.active}
95                                                           hasMargin={it.margin}/>
96                                     </span>
97                                 </ListItem>
98                                 {it.openAble ? (
99                                     <Collapse in={it.open} timeout="auto" unmountOnExit>
100                                         {children}
101                                     </Collapse>
102                                 ) : null}
103                             </span>
104                         ))}
105                     </List>
106                 </div>
107             );
108         }
109
110         getToggableIconClassNames = (isOpen?: boolean, isActive ?: boolean) => {
111             const { classes } = this.props;
112             return classnames(classes.toggableIcon, {
113                 [classes.iconOpen]: isOpen,
114                 [classes.iconClose]: !isOpen,
115                 [classes.active]: isActive
116             });
117         }
118
119         handleRowContextMenu = (item: SidePanelItem) =>
120             (event: React.MouseEvent<HTMLElement>) =>
121                 item.openAble ? this.props.onContextMenu(event, item) : null
122     }
123 );