// Copyright (C) The Arvados Authors. All rights reserved. // // SPDX-License-Identifier: AGPL-3.0 import React from 'react'; import classnames from "classnames"; import { StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core/styles'; import { ReactElement } from "react"; import { FixedSizeList, ListChildComponentProps } from "react-window"; import AutoSizer from "react-virtualized-auto-sizer"; import { ArvadosTheme } from 'common/custom-theme'; import { TreeItem, TreeProps, TreeItemStatus } from './tree'; import { ListItem, Radio, Checkbox, CircularProgress, ListItemIcon } from '@material-ui/core'; import { SidePanelRightArrowIcon } from '../icon/icon'; type CssRules = 'list' | 'listItem' | 'active' | 'loader' | 'toggableIconContainer' | 'iconClose' | 'renderContainer' | 'iconOpen' | 'toggableIcon' | 'checkbox' | 'virtualFileTree' | 'virtualizedList'; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ list: { padding: '3px 0px', }, virtualFileTree: { "&:last-child": { paddingBottom: 20 } }, virtualizedList: { height: '200px', }, listItem: { padding: '3px 0px', }, loader: { position: 'absolute', transform: 'translate(0px)', top: '3px' }, toggableIconContainer: { color: theme.palette.grey["700"], height: '14px', width: '14px', }, toggableIcon: { fontSize: '14px' }, renderContainer: { flex: 1 }, active: { color: theme.palette.primary.main, }, iconClose: { transition: 'all 0.1s ease', }, iconOpen: { transition: 'all 0.1s ease', transform: 'rotate(90deg)', }, checkbox: { width: theme.spacing.unit * 3, height: theme.spacing.unit * 3, margin: `0 ${theme.spacing.unit}px`, padding: 0, color: theme.palette.grey["500"], } }); export interface VirtualTreeItem extends TreeItem { itemCount?: number; level?: number; } // For some reason, on TSX files it isn't accepted just one generic param, so // I'm using as a workaround. // eslint-disable-next-line export const Row = (itemList: VirtualTreeItem[], render: any, treeProps: TreeProps) => withStyles(styles)( (props: React.PropsWithChildren & WithStyles) => { const { index, style, classes } = props; const it = itemList[index]; const level = it.level || 0; const { toggleItemActive, disableRipple, currentItemUuid, useRadioButtons } = treeProps; const { listItem, loader, toggableIconContainer, renderContainer, virtualFileTree } = classes; const { levelIndentation = 20, itemRightPadding = 20 } = treeProps; const showSelection = typeof treeProps.showSelection === 'function' ? treeProps.showSelection : () => treeProps.showSelection ? true : false; const handleRowContextMenu = (item: VirtualTreeItem) => (event: React.MouseEvent) => { treeProps.onContextMenu(event, item); }; const handleToggleItemOpen = (item: VirtualTreeItem) => (event: React.MouseEvent) => { event.stopPropagation(); treeProps.toggleItemOpen(event, item); }; const getToggableIconClassNames = (isOpen?: boolean, isActive?: boolean) => { const { iconOpen, iconClose, active, toggableIcon } = props.classes; return classnames(toggableIcon, { [iconOpen]: isOpen, [iconClose]: !isOpen, [active]: isActive }); }; const isSidePanelIconNotNeeded = (status: string, itemCount: number) => { return status === TreeItemStatus.PENDING || (status === TreeItemStatus.LOADED && itemCount === 0); }; const getProperArrowAnimation = (status: string, itemCount: number) => { return isSidePanelIconNotNeeded(status, itemCount) ? : ; }; const handleCheckboxChange = (item: VirtualTreeItem) => { const { toggleItemSelection } = treeProps; return toggleItemSelection ? (event: React.MouseEvent) => { event.stopPropagation(); toggleItemSelection(event, item); } : undefined; }; return
toggleItemActive(event, it)} selected={showSelection(it) && it.id === currentItemUuid} onContextMenu={handleRowContextMenu(it)}> {it.status === TreeItemStatus.PENDING ? : null} {getProperArrowAnimation(it.status, it.itemCount!)} {showSelection(it) && !useRadioButtons && } {showSelection(it) && useRadioButtons && }
{render(it, level)}
; }); const itemSize = 30; // eslint-disable-next-line export const VirtualList = (height: number, width: number, items: VirtualTreeItem[], render: any, treeProps: TreeProps) => {Row(items, render, treeProps)} ; export const VirtualTree = withStyles(styles)( class Component extends React.Component & WithStyles, {}> { render(): ReactElement { const { items, render } = this.props; return {({ height, width }) => { return VirtualList(height, width, items || [], render, this.props); }} ; } } );