X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/47e0dc87fa82bac593c53518e556ba7c55410288..dd89200ad6fdbfa337fdbab5f54def8712c6746c:/src/components/tree/tree.tsx diff --git a/src/components/tree/tree.tsx b/src/components/tree/tree.tsx index e4d8c72c..3e8cf904 100644 --- a/src/components/tree/tree.tsx +++ b/src/components/tree/tree.tsx @@ -3,50 +3,71 @@ // SPDX-License-Identifier: AGPL-3.0 import * as React from 'react'; -import List from "@material-ui/core/List/List"; -import ListItem from "@material-ui/core/ListItem/ListItem"; +import { List, ListItem, ListItemIcon, Collapse, Checkbox } from "@material-ui/core"; import { StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core/styles'; import { ReactElement } from "react"; -import Collapse from "@material-ui/core/Collapse/Collapse"; import CircularProgress from '@material-ui/core/CircularProgress'; -import { ArvadosTheme } from '../../common/custom-theme'; +import * as classnames from "classnames"; -type CssRules = 'list' | 'activeArrow' | 'inactiveArrow' | 'arrowRotate' | 'arrowTransition' | 'loader' | 'arrowVisibility'; +import { ArvadosTheme } from '~/common/custom-theme'; +import { SidePanelRightArrowIcon } from '../icon/icon'; + +type CssRules = 'list' + | 'listItem' + | 'active' + | 'loader' + | 'toggableIconContainer' + | 'iconClose' + | 'renderContainer' + | 'iconOpen' + | 'toggableIcon' + | 'checkbox'; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ list: { - paddingBottom: '3px', - paddingTop: '3px', + padding: '3px 0px' }, - activeArrow: { - color: theme.palette.primary.main, + listItem: { + padding: '3px 0px', + }, + loader: { position: 'absolute', + transform: 'translate(0px)', + top: '3px' }, - inactiveArrow: { + toggableIconContainer: { color: theme.palette.grey["700"], - position: 'absolute', + height: '14px', + width: '14px', }, - arrowTransition: { - transition: 'all 0.1s ease', + toggableIcon: { + fontSize: '14px' + }, + renderContainer: { + flex: 1 }, - arrowRotate: { + active: { + color: theme.palette.primary.main, + }, + iconClose: { transition: 'all 0.1s ease', - transform: 'rotate(-90deg)', }, - arrowVisibility: { - opacity: 0, + iconOpen: { + transition: 'all 0.1s ease', + transform: 'rotate(90deg)', }, - loader: { - position: 'absolute', - transform: 'translate(0px)', - top: '3px' + checkbox: { + width: theme.spacing.unit * 3, + height: theme.spacing.unit * 3, + margin: `0 ${theme.spacing.unit}px`, + color: theme.palette.grey["500"] } }); export enum TreeItemStatus { - Initial, - Pending, - Loaded + INITIAL, + PENDING, + LOADED } export interface TreeItem { @@ -54,62 +75,91 @@ export interface TreeItem { id: string; open: boolean; active: boolean; + selected?: boolean; status: TreeItemStatus; - toggled?: boolean; items?: Array>; } -interface TreeProps { +export interface TreeProps { items?: Array>; render: (item: TreeItem, level?: number) => ReactElement<{}>; toggleItemOpen: (id: string, status: TreeItemStatus) => void; toggleItemActive: (id: string, status: TreeItemStatus) => void; level?: number; onContextMenu: (event: React.MouseEvent, item: TreeItem) => void; + showSelection?: boolean; + onSelectionChange?: (event: React.MouseEvent, item: TreeItem) => void; + disableRipple?: boolean; } export const Tree = withStyles(styles)( class Component extends React.Component & WithStyles, {}> { render(): ReactElement { const level = this.props.level ? this.props.level : 0; - const { classes, render, toggleItemOpen, items, toggleItemActive, onContextMenu } = this.props; - const { list, inactiveArrow, activeArrow, loader } = classes; + const { classes, render, toggleItemOpen, items, toggleItemActive, onContextMenu, disableRipple } = this.props; + const { list, listItem, loader, toggableIconContainer, renderContainer } = classes; return {items && items.map((it: TreeItem, idx: number) =>
- toggleItemActive(it.id, it.status)} - onContextMenu={this.handleRowContextMenu(it)}> - {it.status === TreeItemStatus.Pending ? - : null} - {it.toggled && it.items && it.items.length === 0 ? null : this.renderArrow(it.status, it.active ? activeArrow : inactiveArrow, it.open, it.id)} - {render(it, level)} + toggleItemActive(it.id, it.status)} + onContextMenu={this.handleRowContextMenu(it)}> + {it.status === TreeItemStatus.PENDING ? + : null} + this.props.toggleItemOpen(it.id, it.status)} + className={toggableIconContainer}> + + {it.status !== TreeItemStatus.INITIAL && it.items && it.items.length === 0 ? : } + + + {this.props.showSelection && + } +
+ {render(it, level)} +
{it.items && it.items.length > 0 && - - - } + + + }
)}
; } - renderArrow(status: TreeItemStatus, arrowClass: string, open: boolean, id: string) { - const { arrowTransition, arrowVisibility, arrowRotate } = this.props.classes; - return this.props.toggleItemOpen(id, status)} - className={` - ${arrowClass} - ${status === TreeItemStatus.Pending ? arrowVisibility : ''} - ${open ? `fas fa-caret-down ${arrowTransition}` : `fas fa-caret-down ${arrowRotate}`}`}/>; + getToggableIconClassNames = (isOpen?: boolean, isActive?: boolean) => { + const { iconOpen, iconClose, active, toggableIcon } = this.props.classes; + return classnames(toggableIcon, { + [iconOpen]: isOpen, + [iconClose]: !isOpen, + [active]: isActive + }); } handleRowContextMenu = (item: TreeItem) => (event: React.MouseEvent) => this.props.onContextMenu(event, item) + + handleCheckboxChange = (item: TreeItem) => { + const { onSelectionChange } = this.props; + return onSelectionChange + ? (event: React.MouseEvent) => { + onSelectionChange(event, item); + } + : undefined; + } } );