15768: project copy-to-clipboard works Arvados-DCO-1.1-Signed-off-by: Lisa Knox ...
[arvados.git] / src / components / tree / tree.tsx
index 2089d1cb54d1e86b0deead0297b5f9f6aa852111..e37086213eae94fc0a997537c810520ee5aaea25 100644 (file)
@@ -2,16 +2,18 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import * as React from 'react';
+import React from 'react';
 import { List, ListItem, ListItemIcon, Checkbox, Radio, Collapse } from "@material-ui/core";
 import { StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core/styles';
-import { ProjectIcon } from '~/components/icon/icon';
+import { CollectionIcon, DefaultIcon, DirectoryIcon, FileIcon, ProjectIcon, FilterGroupIcon, FreezeIcon } from 'components/icon/icon';
 import { ReactElement } from "react";
 import CircularProgress from '@material-ui/core/CircularProgress';
 import classnames from "classnames";
 
-import { ArvadosTheme } from '~/common/custom-theme';
+import { ArvadosTheme } from 'common/custom-theme';
 import { SidePanelRightArrowIcon } from '../icon/icon';
+import { ResourceKind } from 'models/resource';
+import { GroupClass } from 'models/group';
 
 type CssRules = 'list'
     | 'listItem'
@@ -24,7 +26,8 @@ type CssRules = 'list'
     | 'toggableIcon'
     | 'checkbox'
     | 'childItem'
-    | 'childItemIcon';
+    | 'childItemIcon'
+    | 'frozenIcon';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     list: {
@@ -81,6 +84,11 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     active: {
         color: theme.palette.primary.main,
     },
+    frozenIcon: {
+        fontSize: 20,
+        color: theme.palette.grey["600"],
+        marginLeft: '10px',
+    },
 });
 
 export enum TreeItemStatus {
@@ -95,9 +103,12 @@ export interface TreeItem<T> {
     open: boolean;
     active: boolean;
     selected?: boolean;
+    initialState?: boolean;
+    indeterminate?: boolean;
     flatTree?: boolean;
     status: TreeItemStatus;
     items?: Array<TreeItem<T>>;
+    isFrozen?: boolean;
 }
 
 export interface TreeProps<T> {
@@ -153,6 +164,9 @@ interface FlatTreeProps {
     getProperArrowAnimation: Function;
     itemsMap?: Map<string, TreeItem<any>>;
     classes: any;
+    showSelection: any;
+    useRadioButtons?: boolean;
+    handleCheckboxChange: Function;
 }
 
 const FLAT_TREE_ACTIONS = {
@@ -161,10 +175,43 @@ const FLAT_TREE_ACTIONS = {
     toggleActive: 'TOGGLE_ACTIVE',
 };
 
+const ItemIcon = React.memo(({ type, kind, active, groupClass, classes }: any) => {
+    let Icon = ProjectIcon;
+
+    if (groupClass === GroupClass.FILTER) {
+        Icon = FilterGroupIcon;
+    }
+
+    if (type) {
+        switch (type) {
+            case 'directory':
+                Icon = DirectoryIcon;
+                break;
+            case 'file':
+                Icon = FileIcon;
+                break;
+            default:
+                Icon = DefaultIcon;
+        }
+    }
+
+    if (kind) {
+        switch (kind) {
+            case ResourceKind.COLLECTION:
+                Icon = CollectionIcon;
+                break;
+            default:
+                break;
+        }
+    }
+
+    return <Icon className={classnames({ [classes.active]: active }, classes.childItemIcon)} />;
+});
+
 const FlatTree = (props: FlatTreeProps) =>
     <div
         onContextMenu={(event) => {
-            const [action, id] = getActionAndId(event, FLAT_TREE_ACTIONS.contextMenu);
+            const id = getActionAndId(event, FLAT_TREE_ACTIONS.contextMenu)[1];
             props.onContextMenu(event, { id } as any);
         }}
         onClick={(event) => {
@@ -196,12 +243,26 @@ const FlatTree = (props: FlatTreeProps) =>
                             {props.getProperArrowAnimation(item.status, item.items!)}
                         </ListItemIcon>
                     </i>
+                    {props.showSelection(item) && !props.useRadioButtons &&
+                        <Checkbox
+                            checked={item.selected}
+                            className={props.classes.checkbox}
+                            color="primary"
+                            onClick={props.handleCheckboxChange(item)} />}
+                    {props.showSelection(item) && props.useRadioButtons &&
+                        <Radio
+                            checked={item.selected}
+                            className={props.classes.checkbox}
+                            color="primary" />}
                     <div data-action={FLAT_TREE_ACTIONS.toggleActive} className={props.classes.renderContainer}>
                         <span style={{ display: 'flex', alignItems: 'center' }}>
-                            <ProjectIcon className={classnames({ [props.classes.active]: item.active }, props.classes.childItemIcon)} />
+                            <ItemIcon type={item.data.type} active={item.active} kind={item.data.kind} groupClass={item.data.kind === ResourceKind.GROUP ? item.data.groupClass : ''} classes={props.classes} />
                             <span style={{ fontSize: '0.875rem' }}>
                                 {item.data.name}
                             </span>
+                            {
+                                !!item.data.frozenByUuid ? <FreezeIcon className={props.classes.frozenIcon} /> : null
+                            }
                         </span>
                     </div>
                 </div>)
@@ -219,7 +280,6 @@ export const Tree = withStyles(styles)(
                 : () => this.props.showSelection ? true : false;
 
             const { levelIndentation = 20, itemRightPadding = 20 } = this.props;
-
             return <List className={list}>
                 {items && items.map((it: TreeItem<T>, idx: number) =>
                     <div key={`item/${level}/${it.id}`}>
@@ -243,6 +303,7 @@ export const Tree = withStyles(styles)(
                             {showSelection(it) && !useRadioButtons &&
                                 <Checkbox
                                     checked={it.selected}
+                                    indeterminate={!it.selected && it.indeterminate}
                                     className={classes.checkbox}
                                     color="primary"
                                     onClick={this.handleCheckboxChange(it)} />}
@@ -258,29 +319,32 @@ export const Tree = withStyles(styles)(
                         {
                             it.open && it.items && it.items.length > 0 &&
                                 it.flatTree ?
-                                    <FlatTree
-                                        it={it}
-                                        itemsMap={itemsMap}
-                                        classes={this.props.classes}
-                                        levelIndentation={levelIndentation}
+                                <FlatTree
+                                    it={it}
+                                    itemsMap={itemsMap}
+                                    showSelection={showSelection}
+                                    classes={this.props.classes}
+                                    useRadioButtons={useRadioButtons}
+                                    levelIndentation={levelIndentation}
+                                    handleCheckboxChange={this.handleCheckboxChange}
+                                    onContextMenu={this.props.onContextMenu}
+                                    handleToggleItemOpen={this.handleToggleItemOpen}
+                                    toggleItemActive={this.props.toggleItemActive}
+                                    getToggableIconClassNames={this.getToggableIconClassNames}
+                                    getProperArrowAnimation={this.getProperArrowAnimation}
+                                /> :
+                                <Collapse in={it.open} timeout="auto" unmountOnExit>
+                                    <Tree
+                                        showSelection={this.props.showSelection}
+                                        items={it.items}
+                                        render={render}
+                                        disableRipple={disableRipple}
+                                        toggleItemOpen={toggleItemOpen}
+                                        toggleItemActive={toggleItemActive}
+                                        level={level + 1}
                                         onContextMenu={this.props.onContextMenu}
-                                        handleToggleItemOpen={this.handleToggleItemOpen}
-                                        toggleItemActive={this.props.toggleItemActive}
-                                        getToggableIconClassNames={this.getToggableIconClassNames}
-                                        getProperArrowAnimation={this.getProperArrowAnimation}
-                                    /> :
-                                    <Collapse in={it.open} timeout="auto" unmountOnExit>
-                                        <Tree
-                                            showSelection={this.props.showSelection}
-                                            items={it.items}
-                                            render={render}
-                                            disableRipple={disableRipple}
-                                            toggleItemOpen={toggleItemOpen}
-                                            toggleItemActive={toggleItemActive}
-                                            level={level + 1}
-                                            onContextMenu={this.props.onContextMenu}
-                                            toggleItemSelection={this.props.toggleItemSelection} />
-                                    </Collapse>
+                                        toggleItemSelection={this.props.toggleItemSelection} />
+                                </Collapse>
                         }
                     </div>)}
             </List>;