// Copyright (C) The Arvados Authors. All rights reserved. // // SPDX-License-Identifier: AGPL-3.0 import * as React from 'react'; 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 {FixedSizeTree as Tree} from 'react-vtree'; import { ArvadosTheme } from '~/common/custom-theme'; import { TreeItem } from './tree'; // import { FileTreeData } from '../file-tree/file-tree-data'; type CssRules = 'list' | 'listItem' | 'active' | 'loader' | 'toggableIconContainer' | 'iconClose' | 'renderContainer' | 'iconOpen' | 'toggableIcon' | 'checkbox' | 'virtualizedList'; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ list: { padding: '3px 0px', }, 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 TreeProps { disableRipple?: boolean; currentItemUuid?: string; items?: Array>; level?: number; onContextMenu: (event: React.MouseEvent, item: TreeItem) => void; render: (item: TreeItem, level?: number) => ReactElement<{}>; showSelection?: boolean | ((item: TreeItem) => boolean); levelIndentation?: number; itemRightPadding?: number; toggleItemActive: (event: React.MouseEvent, item: TreeItem) => void; toggleItemOpen: (event: React.MouseEvent, item: TreeItem) => void; toggleItemSelection?: (event: React.MouseEvent, item: TreeItem) => void; /** * When set to true use radio buttons instead of checkboxes for item selection. * This does not guarantee radio group behavior (i.e item mutual exclusivity). * Any item selection logic must be done in the toggleItemActive callback prop. */ useRadioButtons?: boolean; } // export const RowA = (items: TreeItem[], render:any) => (index: number) => { // return
// {render(items[index])} //
; // }; // For some reason, on TSX files it isn't accepted just one generic param, so // I'm using as a workaround. export const Row = (items: TreeItem[], render: any) => (props: React.PropsWithChildren) => { const { index, style } = props; const level = items[index].level || 0; const levelIndentation = 20; return
{typeof render === 'function' ? items[index] && render(items[index]) || '' : 'whoops'}
; //
// toggleItemActive(event, it)} // selected={showSelection(it) && it.id === currentItemUuid} // onContextMenu={this.handleRowContextMenu(it)}> // {it.status === TreeItemStatus.PENDING ? // : null} // // // {this.getProperArrowAnimation(it.status, it.items!)} // // // {showSelection(it) && !useRadioButtons && // } // {showSelection(it) && useRadioButtons && // } //
// {render(it, level)} //
//
// {it.items && it.items.length > 0 && // // // } //
}; export const VirtualList = (height: number, width: number, items: TreeItem[], render: any) => {Row(items, render)} ; 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); }}
; } } ); // const treeWalkerWithTree = (tree: Array>) => function* treeWalker(refresh: any) { // const stack = []; // // Remember all the necessary data of the first node in the stack. // stack.push({ // nestingLevel: 0, // node: tree, // }); // // Walk through the tree until we have no nodes available. // while (stack.length !== 0) { // const { // node: {items = [], id, name}, // nestingLevel, // } = stack.pop()!; // // Here we are sending the information about the node to the Tree component // // and receive an information about the openness state from it. The // // `refresh` parameter tells us if the full update of the tree is requested; // // basing on it we decide to return the full node data or only the node // // id to update the nodes order. // const isOpened = yield refresh // ? { // id, // isLeaf: items.length === 0, // isOpenByDefault: true, // name, // nestingLevel, // } // : id; // // Basing on the node openness state we are deciding if we need to render // // the child nodes (if they exist). // if (children.length !== 0 && isOpened) { // // Since it is a stack structure, we need to put nodes we want to render // // first to the end of the stack. // for (let i = children.length - 1; i >= 0; i--) { // stack.push({ // nestingLevel: nestingLevel + 1, // node: children[i], // }); // } // } // } // }; // // Node component receives all the data we created in the `treeWalker` + // // internal openness state (`isOpen`), function to change internal openness // // state (`toggle`) and `style` parameter that should be added to the root div. // const Node = ({data: {isLeaf, name}, isOpen, style, toggle}) => ( //
// {!isLeaf && ( // // )} //
{name}
//
// ); // export const Example = () => ( // // {Node} // // );