[ARVADOS-WORKBENCH2] created: cf035c0589f045849ba635a82b6857fdd6f618ed
Git user
git at public.curoverse.com
Thu May 31 03:18:50 EDT 2018
at cf035c0589f045849ba635a82b6857fdd6f618ed (commit)
commit cf035c0589f045849ba635a82b6857fdd6f618ed
Author: Daniel Kos <daniel.kos at contractors.roche.com>
Date: Thu May 31 09:16:32 2018 +0200
Add tree structure rendering
Feature #13535
Arvados-DCO-1.1-Signed-off-by: Daniel Kos <daniel.kos at contractors.roche.com>:
diff --git a/package.json b/package.json
index b213bb5..13f88a4 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@material-ui/core": "1.0.0",
+ "lodash": "4.17.10",
"react": "16.3.2",
"react-dom": "16.3.2",
"react-redux": "5.0.7",
@@ -32,6 +33,7 @@
"@types/react-router-dom": "4.2.6",
"@types/react-router-redux": "5.0.14",
"@types/redux-devtools": "3.0.44",
+ "@types/lodash": "4.14.109",
"typescript": "2.8.3"
},
"moduleNameMapper": {
diff --git a/src/components/tree/tree.tsx b/src/components/tree/tree.tsx
index 0c99db8..e8ba710 100644
--- a/src/components/tree/tree.tsx
+++ b/src/components/tree/tree.tsx
@@ -6,20 +6,39 @@ import * as React from 'react';
import List from "@material-ui/core/List/List";
import ListItem from "@material-ui/core/ListItem/ListItem";
import { ReactElement } from "react";
+import Collapse from "@material-ui/core/Collapse/Collapse";
+
+export interface TreeItem<T> {
+ data: T;
+ id: string;
+ open: boolean;
+ items?: Array<TreeItem<T>>;
+}
interface TreeProps<T> {
- items: T[],
- render: (item: T) => ReactElement<{}>
+ items?: Array<TreeItem<T>>;
+ render: (item: T) => ReactElement<{}>;
+ toggleItem: (id: string) => any;
+ level?: number;
}
class Tree<T> extends React.Component<TreeProps<T>, {}> {
- render() {
- return <List>
- {this.props.items.map((it: T, idx: number) =>
- <ListItem key={`item/${idx}`} button>
- {this.props.render(it)}
+ render(): ReactElement<any> {
+ const level = this.props.level ? this.props.level : 0;
+ return <List component="div">
+ {this.props.items && this.props.items.map((it: TreeItem<T>, idx: number) =>
+ <div key={`item/${level}/${idx}`}>
+ <ListItem button onClick={() => this.props.toggleItem(it.id)} style={{paddingLeft: (level + 1) * 30}}>
+ {this.props.render(it.data)}
</ListItem>
- )}
+ {it.items && it.items.length > 0 &&
+ <Collapse in={it.open} timeout="auto" unmountOnExit>
+ <Tree items={it.items}
+ render={this.props.render}
+ toggleItem={this.props.toggleItem}
+ level={level + 1}/>
+ </Collapse>}
+ </div>)}
</List>
}
}
diff --git a/src/index.tsx b/src/index.tsx
index dacada2..ae16dce 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -12,14 +12,44 @@ import { Route, Router } from "react-router";
import createBrowserHistory from "history/createBrowserHistory";
import configureStore from "./store/store";
import { ConnectedRouter } from "react-router-redux";
+import { TreeItem } from "./components/tree/tree";
+import { Project } from "./models/project";
+
+const sampleProjects = [
+ [
+ 'Project 1', [
+ ['Project 1.1', [['Project 1.1.1'], ['Project 1.1.2']]],
+ ['Project 1.2', [['Project 1.2.1'], ['Project 1.2.2'], ['Project 1.2.3']]]
+ ]
+ ],
+ [
+ 'Project 2'
+ ],
+ [
+ 'Project 3', [['Project 3.1'], ['Project 3.2']]
+ ]
+];
+
+
+function buildProjectTree(tree: any[], level = 0): Array<TreeItem<Project>> {
+ const projects = tree.map((t, idx) => ({
+ id: `l${level}i${idx}`,
+ open: false,
+ data: {
+ name: t[0],
+ createdAt: '2018-05-05',
+ },
+ items: t.length > 1 ? buildProjectTree(t[1], level + 1) : []
+ }));
+ return projects;
+}
+
const history = createBrowserHistory();
+const projects = buildProjectTree(sampleProjects);
+
const store = configureStore({
- projects: [
- { name: 'Mouse genome', createdAt: '2018-05-01' },
- { name: 'Human body', createdAt: '2018-05-01' },
- { name: 'Secret operation', createdAt: '2018-05-01' }
- ],
+ projects,
router: {
location: null
}
diff --git a/src/store/project-action.ts b/src/store/project-action.ts
index 784adb3..7cd3bac 100644
--- a/src/store/project-action.ts
+++ b/src/store/project-action.ts
@@ -7,7 +7,8 @@ import { Project } from "../models/project";
export const actions = {
createProject: createStandardAction('@@project/create')<Project>(),
- removeProject: createStandardAction('@@project/remove')<string>()
+ removeProject: createStandardAction('@@project/remove')<string>(),
+ toggleProjectTreeItem: createStandardAction('@@project/toggleTreeItem')<string>()
};
export type ProjectAction = ActionType<typeof actions>;
diff --git a/src/store/project-reducer.ts b/src/store/project-reducer.ts
index 5e2d192..f1056f3 100644
--- a/src/store/project-reducer.ts
+++ b/src/store/project-reducer.ts
@@ -5,14 +5,38 @@
import { getType } from "typesafe-actions";
import { Project } from "../models/project";
import { actions, ProjectAction } from "./project-action";
+import { TreeItem } from "../components/tree/tree";
+import * as _ from 'lodash';
-type ProjectState = Project[];
+type ProjectState = Array<TreeItem<Project>>;
+
+function findTreeItem<T>(tree: Array<TreeItem<T>>, itemId: string): TreeItem<T> | undefined {
+ let item;
+ for (const t of tree) {
+ item = t.id === itemId
+ ? t
+ : findTreeItem(t.items ? t.items : [], itemId);
+ if (item) {
+ break;
+ }
+ }
+ return item;
+}
const projectsReducer = (state: ProjectState = [], action: ProjectAction) => {
switch (action.type) {
case getType(actions.createProject): {
return [...state, action.payload];
}
+ case getType(actions.toggleProjectTreeItem): {
+ const tree = _.cloneDeep(state);
+ const itemId = action.payload;
+ const item = findTreeItem(tree, itemId);
+ if (item) {
+ item.open = !item.open;
+ }
+ return tree;
+ }
default:
return state;
}
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 365835a..7265380 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -10,13 +10,16 @@ import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import { connect } from "react-redux";
-import Tree from "../../components/tree/tree";
+import Tree, { TreeItem } from "../../components/tree/tree";
import { Project } from "../../models/project";
import { RootState } from "../../store/root-reducer";
import ProjectList from "../../components/project-list/project-list";
import { Route, Switch } from "react-router";
import { Link } from "react-router-dom";
+import { actions as projectActions } from "../../store/project-action";
+import ListItemText from "@material-ui/core/ListItemText/ListItemText";
+
const drawerWidth = 240;
type CssRules = 'root' | 'appBar' | 'drawerPaper' | 'content' | 'toolbar';
@@ -49,7 +52,8 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
});
interface WorkbenchProps {
- projects: Project[]
+ projects: Array<TreeItem<Project>>;
+ toggleProjectTreeItem: (id: string) => any;
}
interface WorkbenchState {
@@ -58,7 +62,6 @@ interface WorkbenchState {
class Workbench extends React.Component<WorkbenchProps & WithStyles<CssRules>, WorkbenchState> {
render() {
const {classes} = this.props;
-
return (
<div className={classes.root}>
<AppBar position="absolute" className={classes.appBar}>
@@ -74,9 +77,10 @@ class Workbench extends React.Component<WorkbenchProps & WithStyles<CssRules>, W
paper: classes.drawerPaper,
}}>
<div className={classes.toolbar}/>
- <Tree items={this.props.projects} render={(p: Project) =>
- <Link to={`/project/${p.name}`}>{p.name}</Link>
- }/>
+ <Tree items={this.props.projects}
+ toggleItem={this.props.toggleProjectTreeItem}
+ render={(p: Project) => <ListItemText primary={p.name}/>}
+ />
</Drawer>
<main className={classes.content}>
<div className={classes.toolbar}/>
@@ -92,10 +96,12 @@ class Workbench extends React.Component<WorkbenchProps & WithStyles<CssRules>, W
}
}
-export default connect<WorkbenchProps>(
+export default connect(
(state: RootState) => ({
projects: state.projects
- })
+ }), {
+ toggleProjectTreeItem: (id: string) => projectActions.toggleProjectTreeItem(id)
+ }
)(
withStyles(styles)(Workbench)
);
diff --git a/tslint.json b/tslint.json
index 18630ab..1b26ab5 100644
--- a/tslint.json
+++ b/tslint.json
@@ -9,7 +9,9 @@
"member-access": false,
"jsx-boolean-value": false,
"jsx-no-lambda": false,
- "no-debugger": false
+ "no-debugger": false,
+ "no-console": false,
+ "no-shadowed-variable": false
},
"linterOptions": {
"exclude": [
diff --git a/yarn.lock b/yarn.lock
index 93702cd..6d5ec0d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -70,6 +70,10 @@
csstype "^2.0.0"
indefinite-observable "^1.0.1"
+"@types/lodash@^4.14.109":
+ version "4.14.109"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.109.tgz#b1c4442239730bf35cabaf493c772b18c045886d"
+
"@types/node@*", "@types/node at 10.1.2":
version "10.1.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.1.2.tgz#1b928a0baa408fc8ae3ac012cc81375addc147c6"
@@ -5914,7 +5918,7 @@ react-redux at 5.0.7:
loose-envify "^1.1.0"
prop-types "^15.6.0"
-react-router-dom@^4.2.2:
+react-router-dom at 4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d"
dependencies:
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list