[ARVADOS-WORKBENCH2] updated: 1.4.1-370-g8edacc9b
Git user
git at public.arvados.org
Wed Jun 24 21:46:09 UTC 2020
Summary of changes:
src/components/tree/tree.tsx | 1 +
src/components/tree/virtual-tree.tsx | 240 ++++++++-------------
.../collection-panel-files.ts | 48 ++---
3 files changed, 114 insertions(+), 175 deletions(-)
via 8edacc9b2dac6520ace9a2d420979bbeb5558a6f (commit)
from e92207c912aed73a07340b5fb2a9e2cb23e1da5f (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit 8edacc9b2dac6520ace9a2d420979bbeb5558a6f
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date: Wed Jun 24 18:45:31 2020 -0300
15610: Code cleanup, styiling additions. (WIP)
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas at di-pentima.com.ar>
diff --git a/src/components/tree/tree.tsx b/src/components/tree/tree.tsx
index b5ce5ec5..28833b85 100644
--- a/src/components/tree/tree.tsx
+++ b/src/components/tree/tree.tsx
@@ -79,6 +79,7 @@ export interface TreeItem<T> {
selected?: boolean;
status: TreeItemStatus;
items?: Array<TreeItem<T>>;
+ itemCount?: number;
level?: number;
}
diff --git a/src/components/tree/virtual-tree.tsx b/src/components/tree/virtual-tree.tsx
index 4615db4f..467969fa 100644
--- a/src/components/tree/virtual-tree.tsx
+++ b/src/components/tree/virtual-tree.tsx
@@ -3,15 +3,16 @@
// SPDX-License-Identifier: AGPL-3.0
import * as React from 'react';
+import * as 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 {FixedSizeTree as Tree} from 'react-vtree';
import { ArvadosTheme } from '~/common/custom-theme';
-import { TreeItem } from './tree';
-// import { FileTreeData } from '../file-tree/file-tree-data';
+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'
@@ -70,28 +71,6 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
}
});
-export interface TreeProps<T> {
- disableRipple?: boolean;
- currentItemUuid?: string;
- items?: Array<TreeItem<T>>;
- level?: number;
- onContextMenu: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
- render: (item: TreeItem<T>, level?: number) => ReactElement<{}>;
- showSelection?: boolean | ((item: TreeItem<T>) => boolean);
- levelIndentation?: number;
- itemRightPadding?: number;
- toggleItemActive: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
- toggleItemOpen: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
- toggleItemSelection?: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => 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 = <T, _>(items: TreeItem<T>[], render:any) => (index: number) => {
// return <div>
// {render(items[index])}
@@ -100,65 +79,92 @@ export interface TreeProps<T> {
// For some reason, on TSX files it isn't accepted just one generic param, so
// I'm using <T, _> as a workaround.
-export const Row = <T, _>(items: TreeItem<T>[], render: any) => (props: React.PropsWithChildren<ListChildComponentProps>) => {
- const { index, style } = props;
- const level = items[index].level || 0;
- const levelIndentation = 20;
- return <div style={style}>
- <div style={{ paddingLeft: (level + 1) * levelIndentation,}}>
- {typeof render === 'function'
- ? items[index] && render(items[index]) || ''
- : 'whoops'}
- </div>
- </div>;
- // <div style={style} key={`item/${level}/${idx}`}>
- // <ListItem button className={listItem}
- // style={{
- // paddingLeft: (level + 1) * levelIndentation,
- // paddingRight: itemRightPadding,
- // }}
- // disableRipple={disableRipple}
- // onClick={event => toggleItemActive(event, it)}
- // selected={showSelection(it) && it.id === currentItemUuid}
- // onContextMenu={this.handleRowContextMenu(it)}>
- // {it.status === TreeItemStatus.PENDING ?
- // <CircularProgress size={10} className={loader} /> : null}
- // <i onClick={this.handleToggleItemOpen(it)}
- // className={toggableIconContainer}>
- // <ListItemIcon className={this.getToggableIconClassNames(it.open, it.active)}>
- // {this.getProperArrowAnimation(it.status, it.items!)}
- // </ListItemIcon>
- // </i>
- // {showSelection(it) && !useRadioButtons &&
- // <Checkbox
- // checked={it.selected}
- // className={classes.checkbox}
- // color="primary"
- // onClick={this.handleCheckboxChange(it)} />}
- // {showSelection(it) && useRadioButtons &&
- // <Radio
- // checked={it.selected}
- // className={classes.checkbox}
- // color="primary" />}
- // <div className={renderContainer}>
- // {render(it, level)}
- // </div>
- // </ListItem>
- // {it.items && it.items.length > 0 &&
- // <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={onContextMenu}
- // toggleItemSelection={this.props.toggleItemSelection} />
- // </Collapse>}
- // </div>
-};
+export const Row = <T, _>(itemList: TreeItem<T>[], render: any) => withStyles(styles)(
+ (props: React.PropsWithChildren<ListChildComponentProps> & TreeProps<T> & WithStyles<CssRules>) => {
+ const { index, style } = props;
+ const it = itemList[index];
+ const level = it.level || 0;
+ const { classes, toggleItemActive, disableRipple, currentItemUuid, useRadioButtons } = props;
+ const { listItem, loader, toggableIconContainer, renderContainer } = classes;
+ const { levelIndentation = 20, itemRightPadding = 20 } = props;
+
+ const showSelection = typeof props.showSelection === 'function'
+ ? props.showSelection
+ : () => props.showSelection ? true : false;
+
+ const handleRowContextMenu = (item: TreeItem<T>) =>
+ (event: React.MouseEvent<HTMLElement>) =>
+ props.onContextMenu(event, item);
+
+ const handleToggleItemOpen = (item: TreeItem<T>) =>
+ (event: React.MouseEvent<HTMLElement>) => {
+ event.stopPropagation();
+ props.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) ? <span /> : <SidePanelRightArrowIcon style={{ fontSize: '14px' }} />;
+ };
+
+ const handleCheckboxChange = (item: TreeItem<T>) => {
+ const { toggleItemSelection } = props;
+ return toggleItemSelection
+ ? (event: React.MouseEvent<HTMLElement>) => {
+ event.stopPropagation();
+ toggleItemSelection(event, item);
+ }
+ : undefined;
+ };
+
+ return <div style={style}>
+ <ListItem button className={listItem}
+ style={{
+ paddingLeft: (level + 1) * levelIndentation,
+ paddingRight: itemRightPadding,
+ }}
+ disableRipple={disableRipple}
+ onClick={event => toggleItemActive(event, it)}
+ selected={showSelection(it) && it.id === currentItemUuid}
+ onContextMenu={handleRowContextMenu(it)}>
+ {it.status === TreeItemStatus.PENDING ?
+ <CircularProgress size={10} className={loader} /> : null}
+ <i onClick={handleToggleItemOpen(it)}
+ className={toggableIconContainer}>
+ <ListItemIcon className={getToggableIconClassNames(it.open, it.active)}>
+ {getProperArrowAnimation(it.status, it.itemCount!)}
+ </ListItemIcon>
+ </i>
+ {showSelection(it) && !useRadioButtons &&
+ <Checkbox
+ checked={it.selected}
+ className={classes.checkbox}
+ color="primary"
+ onClick={handleCheckboxChange(it)} />}
+ {showSelection(it) && useRadioButtons &&
+ <Radio
+ checked={it.selected}
+ className={classes.checkbox}
+ color="primary" />}
+ <div className={renderContainer}>
+ {render(it, level)}
+ </div>
+ </ListItem>
+ </div>;
+ });
export const VirtualList = <T, _>(height: number, width: number, items: TreeItem<T>[], render: any) =>
<FixedSizeList
@@ -183,69 +189,3 @@ export const VirtualTree = withStyles(styles)(
}
}
);
-
-// const treeWalkerWithTree = (tree: Array<TreeItem<FileTreeData>>) => 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}) => (
-// <div style={style}>
-// {!isLeaf && (
-// <button type="button" onClick={toggle}>
-// {isOpen ? '-' : '+'}
-// </button>
-// )}
-// <div>{name}</div>
-// </div>
-// );
-
-// export const Example = () => (
-// <Tree treeWalker={treeWalker} itemSize={30} height={150} width={300}>
-// {Node}
-// </Tree>
-// );
\ No newline at end of file
diff --git a/src/views-components/collection-panel-files/collection-panel-files.ts b/src/views-components/collection-panel-files/collection-panel-files.ts
index e0798086..3cc3569d 100644
--- a/src/views-components/collection-panel-files/collection-panel-files.ts
+++ b/src/views-components/collection-panel-files/collection-panel-files.ts
@@ -34,10 +34,7 @@ const memoizedMapStateToProps = () => {
prevState = state.collectionPanelFiles;
prevTree = [].concat.apply(
[], getNodeChildrenIds('')(state.collectionPanelFiles)
- .map(collectionItemToList(0)(state.collectionPanelFiles)))
- .map(nodeToTreeItem);
- // prevTree = getNodeChildrenIds('')(state.collectionPanelFiles)
- // .map(collectionItemToTreeItem(state.collectionPanelFiles));
+ .map(collectionItemToList(0)(state.collectionPanelFiles)));
}
return {
items: prevTree,
@@ -92,10 +89,29 @@ export const collectionItemToList = (level: number) => (tree: Tree<CollectionPan
collapsed: true
}
});
- const childs = [].concat.apply([], node.children.map(collectionItemToList(level+1)(tree)));
+
+ const treeItem = {
+ active: false,
+ data: {
+ name: node.value.name,
+ size: node.value.type === CollectionFileType.FILE ? node.value.size : undefined,
+ type: node.value.type,
+ url: node.value.url,
+ },
+ id: node.id,
+ items: [], // Not used in this case as we're converting a tree to a list.
+ itemCount: node.children.length,
+ open: node.value.type === CollectionFileType.DIRECTORY ? !node.value.collapsed : false,
+ selected: node.value.selected,
+ status: TreeItemStatus.LOADED,
+ level,
+ };
+
+ const treeItemChilds = [].concat.apply([], node.children.map(collectionItemToList(level+1)(tree)));
+
return [
- {...node, level},
- ...childs,
+ treeItem,
+ ...treeItemChilds,
];
};
@@ -126,21 +142,3 @@ const collectionItemToTreeItem = (tree: Tree<CollectionPanelDirectory | Collecti
status: TreeItemStatus.LOADED
};
};
-
-const nodeToTreeItem = (node: TreeNode<CollectionPanelDirectory | CollectionPanelFile>): TreeItem<FileTreeData> => {
- return ({
- active: false,
- data: {
- name: node.value.name,
- size: node.value.type === CollectionFileType.FILE ? node.value.size : undefined,
- type: node.value.type,
- url: node.value.url,
- },
- id: node.id,
- items: [],
- open: node.value.type === CollectionFileType.DIRECTORY ? !node.value.collapsed : false,
- selected: node.value.selected,
- status: TreeItemStatus.LOADED,
- level: node.level,
- });
-};
\ No newline at end of file
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list