[ARVADOS-WORKBENCH2] created: 1.2.0-541-gd1af457
Git user
git at public.curoverse.com
Thu Oct 4 16:11:31 EDT 2018
at d1af457494186adb375ce2c012cb58685e0556e3 (commit)
commit d1af457494186adb375ce2c012cb58685e0556e3
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date: Thu Oct 4 22:11:21 2018 +0200
Move common operations to tree model
Feature #13862
Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
diff --git a/src/components/file-tree/file-tree.tsx b/src/components/file-tree/file-tree.tsx
index 06fc8b7..165b4bf 100644
--- a/src/components/file-tree/file-tree.tsx
+++ b/src/components/file-tree/file-tree.tsx
@@ -24,7 +24,7 @@ export class FileTree extends React.Component<FileTreeProps> {
onContextMenu={this.handleContextMenu}
toggleItemActive={this.handleToggleActive}
toggleItemOpen={this.handleToggle}
- onSelectionChange={this.handleSelectionChange} />;
+ toggleItemSelection={this.handleSelectionChange} />;
}
handleContextMenu = (event: React.MouseEvent<any>, item: TreeItem<FileTreeData>) => {
diff --git a/src/components/tree/tree.tsx b/src/components/tree/tree.tsx
index c892d7d..247661f 100644
--- a/src/components/tree/tree.tsx
+++ b/src/components/tree/tree.tsx
@@ -60,7 +60,8 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
width: theme.spacing.unit * 3,
height: theme.spacing.unit * 3,
margin: `0 ${theme.spacing.unit}px`,
- color: theme.palette.grey["500"]
+ padding: 0,
+ color: theme.palette.grey["500"],
}
});
@@ -88,7 +89,7 @@ export interface TreeProps<T> {
level?: number;
onContextMenu: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
showSelection?: boolean;
- onSelectionChange?: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
+ toggleItemSelection?: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
disableRipple?: boolean;
}
@@ -134,7 +135,7 @@ export const Tree = withStyles(styles)(
toggleItemActive={toggleItemActive}
level={level + 1}
onContextMenu={onContextMenu}
- onSelectionChange={this.props.onSelectionChange} />
+ toggleItemSelection={this.props.toggleItemSelection} />
</Collapse>}
</div>)}
</List>;
@@ -164,10 +165,11 @@ export const Tree = withStyles(styles)(
this.props.onContextMenu(event, item)
handleCheckboxChange = (item: TreeItem<T>) => {
- const { onSelectionChange } = this.props;
- return onSelectionChange
+ const { toggleItemSelection } = this.props;
+ return toggleItemSelection
? (event: React.MouseEvent<HTMLElement>) => {
- onSelectionChange(event, item);
+ event.stopPropagation();
+ toggleItemSelection(event, item);
}
: undefined;
}
diff --git a/src/models/collection-file.ts b/src/models/collection-file.ts
index d74ada6..37e18cf 100644
--- a/src/models/collection-file.ts
+++ b/src/models/collection-file.ts
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0
-import { Tree, createTree, setNode } from './tree';
+import { Tree, createTree, setNode, TreeNodeStatus } from './tree';
export type CollectionFilesTree = Tree<CollectionDirectory | CollectionFile>;
@@ -60,6 +60,11 @@ export const createCollectionFilesTree = (data: Array<CollectionDirectory | Coll
children: [],
id: item.id,
parent: item.path,
- value: item
+ value: item,
+ active: false,
+ selected: false,
+ expanded: false,
+ status: TreeNodeStatus.INITIAL
+
})(tree), createTree<CollectionDirectory | CollectionFile>());
};
\ No newline at end of file
diff --git a/src/models/tree.test.ts b/src/models/tree.test.ts
index 375a012..b36cfad 100644
--- a/src/models/tree.test.ts
+++ b/src/models/tree.test.ts
@@ -12,77 +12,88 @@ describe('Tree', () => {
});
it('sets new node', () => {
- const newTree = Tree.setNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' })(tree);
+ const newTree = Tree.setNode(mockTreeNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' }))(tree);
expect(Tree.getNode('Node 1')(newTree)).toEqual({ children: [], id: 'Node 1', parent: '', value: 'Value 1' });
});
it('adds new node reference to parent children', () => {
const [newTree] = [tree]
- .map(Tree.setNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' }))
- .map(Tree.setNode({ children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 2' }));
+ .map(Tree.setNode(mockTreeNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' })))
+ .map(Tree.setNode(mockTreeNode({ children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 2' })));
expect(Tree.getNode('Node 1')(newTree)).toEqual({ children: ['Node 2'], id: 'Node 1', parent: '', value: 'Value 1' });
});
it('gets node ancestors', () => {
const newTree = [
- { children: [], id: 'Node 1', parent: '', value: 'Value 1' },
- { children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' },
- { children: [], id: 'Node 3', parent: 'Node 2', value: 'Value 1' }
+ mockTreeNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 3', parent: 'Node 2', value: 'Value 1' }),
].reduce((tree, node) => Tree.setNode(node)(tree), tree);
expect(Tree.getNodeAncestorsIds('Node 3')(newTree)).toEqual(['Node 1', 'Node 2']);
});
it('gets node descendants', () => {
const newTree = [
- { children: [], id: 'Node 1', parent: '', value: 'Value 1' },
- { children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' },
- { children: [], id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' },
- { children: [], id: 'Node 3', parent: 'Node 1', value: 'Value 1' },
- { children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }
+ mockTreeNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 3', parent: 'Node 1', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }),
].reduce((tree, node) => Tree.setNode(node)(tree), tree);
expect(Tree.getNodeDescendantsIds('Node 1')(newTree)).toEqual(['Node 2', 'Node 3', 'Node 2.1', 'Node 3.1']);
});
it('gets root descendants', () => {
const newTree = [
- { children: [], id: 'Node 1', parent: '', value: 'Value 1' },
- { children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' },
- { children: [], id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' },
- { children: [], id: 'Node 3', parent: 'Node 1', value: 'Value 1' },
- { children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }
+ mockTreeNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 3', parent: 'Node 1', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }),
].reduce((tree, node) => Tree.setNode(node)(tree), tree);
expect(Tree.getNodeDescendantsIds('')(newTree)).toEqual(['Node 1', 'Node 2', 'Node 3', 'Node 2.1', 'Node 3.1']);
});
it('gets node children', () => {
const newTree = [
- { children: [], id: 'Node 1', parent: '', value: 'Value 1' },
- { children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' },
- { children: [], id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' },
- { children: [], id: 'Node 3', parent: 'Node 1', value: 'Value 1' },
- { children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }
+ mockTreeNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 3', parent: 'Node 1', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }),
].reduce((tree, node) => Tree.setNode(node)(tree), tree);
expect(Tree.getNodeChildrenIds('Node 1')(newTree)).toEqual(['Node 2', 'Node 3']);
});
it('gets root children', () => {
const newTree = [
- { children: [], id: 'Node 1', parent: '', value: 'Value 1' },
- { children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' },
- { children: [], id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' },
- { children: [], id: 'Node 3', parent: '', value: 'Value 1' },
- { children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }
+ mockTreeNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 3', parent: '', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }),
].reduce((tree, node) => Tree.setNode(node)(tree), tree);
expect(Tree.getNodeChildrenIds('')(newTree)).toEqual(['Node 1', 'Node 3']);
});
it('maps tree', () => {
const newTree = [
- { children: [], id: 'Node 1', parent: '', value: 'Value 1' },
- { children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 2' },
+ mockTreeNode({ children: [], id: 'Node 1', parent: '', value: 'Value 1' }),
+ mockTreeNode({ children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 2' }),
].reduce((tree, node) => Tree.setNode(node)(tree), tree);
const mappedTree = Tree.mapTreeValues<string, number>(value => parseInt(value.split(' ')[1], 10))(newTree);
- expect(Tree.getNode('Node 2')(mappedTree)).toEqual({ children: [], id: 'Node 2', parent: 'Node 1', value: 2 }, );
+ expect(Tree.getNode('Node 2')(mappedTree)).toEqual({ children: [], id: 'Node 2', parent: 'Node 1', value: 2 });
});
+});
+
+const mockTreeNode = <T>(node: Partial<Tree.TreeNode<T | string>>): Tree.TreeNode<T | string> => ({
+ children: [],
+ id: '',
+ parent: '',
+ value: '',
+ active: false,
+ selected: false,
+ expanded: false,
+ status: Tree.TreeNodeStatus.INITIAL,
});
\ No newline at end of file
diff --git a/src/models/tree.ts b/src/models/tree.ts
index a5fb49c..b649a40 100644
--- a/src/models/tree.ts
+++ b/src/models/tree.ts
@@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0
+import { pipe } from 'lodash/fp';
export type Tree<T> = Record<string, TreeNode<T>>;
export const TREE_ROOT_ID = '';
@@ -11,6 +12,16 @@ export interface TreeNode<T = any> {
value: T;
id: string;
parent: string;
+ active: boolean;
+ selected: boolean;
+ expanded: boolean;
+ status: TreeNodeStatus;
+}
+
+export enum TreeNodeStatus {
+ INITIAL = 'INITIAL',
+ PENDING = 'PENDING',
+ LOADED = 'LOADED',
}
export const createTree = <T>(): Tree<T> => ({});
@@ -18,6 +29,7 @@ export const createTree = <T>(): Tree<T> => ({});
export const getNode = (id: string) => <T>(tree: Tree<T>): TreeNode<T> | undefined => tree[id];
export const setNode = <T>(node: TreeNode<T>) => (tree: Tree<T>): Tree<T> => {
+ console.log(node);
const [newTree] = [tree]
.map(tree => getNode(node.id)(tree) === node
? tree
@@ -95,6 +107,79 @@ export const getNodeChildrenIds = (id: string) => <T>(tree: Tree<T>): string[] =
export const mapIdsToNodes = (ids: string[]) => <T>(tree: Tree<T>) =>
ids.map(id => getNode(id)(tree)).filter((node): node is TreeNode<T> => node !== undefined);
+export const activateNode = (id: string) => <T>(tree: Tree<T>) =>
+ mapTree(node => node.id === id ? { ...node, active: true } : { ...node, active: false })(tree);
+
+
+export const expandNode = (...ids: string[]) => <T>(tree: Tree<T>) =>
+ mapTree(node => ids.some(id => id === node.id) ? { ...node, expanded: true } : node)(tree);
+
+export const collapseNode = (...ids: string[]) => <T>(tree: Tree<T>) =>
+ mapTree(node => ids.some(id => id === node.id) ? { ...node, expanded: false } : node)(tree);
+
+export const toggleNodeCollapse = (...ids: string[]) => <T>(tree: Tree<T>) =>
+ mapTree(node => ids.some(id => id === node.id) ? { ...node, expanded: !node.expanded } : node)(tree);
+
+export const setNodeStatus = (id: string) => (status: TreeNodeStatus) => <T>(tree: Tree<T>) => {
+ const node = getNode(id)(tree);
+ return node
+ ? setNode({ ...node, status })(tree)
+ : tree;
+};
+
+export const toggleNodeSelection = (id: string) => <T>(tree: Tree<T>) => {
+ const node = getNode(id)(tree);
+ return node
+ ? pipe(
+ setNode({ ...node, selected: !node.selected }),
+ toggleAncestorsSelection(id),
+ toggleDescendantsSelection(id))(tree)
+ : tree;
+
+};
+
+export const initTreeNode = <T>(data: Pick<TreeNode<T>, 'id' | 'value'>): TreeNode<T> => ({
+ children: [],
+ active: false,
+ selected: false,
+ expanded: false,
+ status: TreeNodeStatus.INITIAL,
+ parent: '',
+ ...data,
+});
+
+const toggleDescendantsSelection = (id: string) => <T>(tree: Tree<T>) => {
+ const node = getNode(id)(tree);
+ if (node) {
+ return getNodeDescendants(id)(tree)
+ .reduce((newTree, subNode) =>
+ setNode({ ...subNode, selected: node.selected })(newTree),
+ tree);
+ }
+ return tree;
+};
+
+const toggleAncestorsSelection = (id: string) => <T>(tree: Tree<T>) => {
+ const ancestors = getNodeAncestorsIds(id)(tree).reverse();
+ return ancestors.reduce((newTree, parent) => parent ? toggleParentNodeSelection(parent)(newTree) : newTree, tree);
+};
+
+const toggleParentNodeSelection = (id: string) => <T>(tree: Tree<T>) => {
+ const node = getNode(id)(tree);
+ if (node) {
+ const parentNode = getNode(node.id)(tree);
+ if (parentNode) {
+ const selected = parentNode.children
+ .map(id => getNode(id)(tree))
+ .every(node => node !== undefined && node.selected);
+ return setNode({ ...parentNode, selected })(tree);
+ }
+ return setNode(node)(tree);
+ }
+ return tree;
+};
+
+
const mapNodeValue = <T, R>(mapFn: (value: T) => R) => (node: TreeNode<T>): TreeNode<R> =>
({ ...node, value: mapFn(node.value) });
diff --git a/src/services/collection-files-service/collection-manifest-mapper.ts b/src/services/collection-files-service/collection-manifest-mapper.ts
index 0c7e91d..6e64f83 100644
--- a/src/services/collection-files-service/collection-manifest-mapper.ts
+++ b/src/services/collection-files-service/collection-manifest-mapper.ts
@@ -4,7 +4,7 @@
import { uniqBy, groupBy } from 'lodash';
import { KeepManifestStream, KeepManifestStreamFile, KeepManifest } from "~/models/keep-manifest";
-import { TreeNode, setNode, createTree, getNodeDescendantsIds, getNodeValue } from '~/models/tree';
+import { TreeNode, setNode, createTree, getNodeDescendantsIds, getNodeValue, TreeNodeStatus } from '~/models/tree';
import { CollectionFilesTree, CollectionFile, CollectionDirectory, createCollectionDirectory, createCollectionFile, CollectionFileType } from '../../models/collection-file';
export const mapCollectionFilesTreeToManifest = (tree: CollectionFilesTree): KeepManifest => {
@@ -30,7 +30,11 @@ export const mapCollectionFileToTreeNode = (file: CollectionFile): TreeNode<Coll
children: [],
id: file.id,
parent: file.path,
- value: file
+ value: file,
+ active: false,
+ selected: false,
+ expanded: false,
+ status: TreeNodeStatus.INITIAL,
});
export const manifestToCollectionFiles = (manifest: KeepManifest): Array<CollectionDirectory | CollectionFile> => ([
diff --git a/src/store/breadcrumbs/breadcrumbs-actions.ts b/src/store/breadcrumbs/breadcrumbs-actions.ts
index a5ded34..8b1eb2b 100644
--- a/src/store/breadcrumbs/breadcrumbs-actions.ts
+++ b/src/store/breadcrumbs/breadcrumbs-actions.ts
@@ -28,7 +28,7 @@ const getSidePanelTreeBreadcrumbs = (uuid: string) => (treePicker: TreePicker):
const nodes = getSidePanelTreeBranch(uuid)(treePicker);
return nodes.map(node =>
typeof node.value === 'string'
- ? { label: node.value, uuid: node.nodeId }
+ ? { label: node.value, uuid: node.id }
: { label: node.value.name, uuid: node.value.uuid });
};
diff --git a/src/store/collection-panel/collection-panel-files/collection-panel-files-reducer.test.ts b/src/store/collection-panel/collection-panel-files/collection-panel-files-reducer.test.ts
index 90dedaa..3964ee4 100644
--- a/src/store/collection-panel/collection-panel-files/collection-panel-files-reducer.test.ts
+++ b/src/store/collection-panel/collection-panel-files/collection-panel-files-reducer.test.ts
@@ -5,7 +5,7 @@
import { collectionPanelFilesReducer } from "./collection-panel-files-reducer";
import { collectionPanelFilesAction } from "./collection-panel-files-actions";
import { CollectionFile, CollectionDirectory, createCollectionFile, createCollectionDirectory } from "~/models/collection-file";
-import { createTree, setNode, getNodeValue, mapTreeValues } from "~/models/tree";
+import { createTree, setNode, getNodeValue, mapTreeValues, TreeNodeStatus } from "~/models/tree";
import { CollectionPanelFile, CollectionPanelDirectory } from "./collection-panel-files-state";
describe('CollectionPanelFilesReducer', () => {
@@ -26,7 +26,11 @@ describe('CollectionPanelFilesReducer', () => {
children: [],
id: file.id,
parent: file.path,
- value: file
+ value: file,
+ active: false,
+ selected: false,
+ expanded: false,
+ status: TreeNodeStatus.INITIAL,
})(tree), createTree<CollectionFile | CollectionDirectory>());
const collectionPanelFilesTree = collectionPanelFilesReducer(
diff --git a/src/store/file-tree-picker/file-tree-picker-actions.ts b/src/store/file-tree-picker/file-tree-picker-actions.ts
deleted file mode 100644
index 18d0f5f..0000000
--- a/src/store/file-tree-picker/file-tree-picker-actions.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { unionize, ofType, UnionOf } from "~/common/unionize";
-
-import { TreePickerNode } from "./file-tree-picker";
-
-export const fileTreePickerActions = unionize({
- LOAD_TREE_PICKER_NODE: ofType<{ nodeId: string, pickerId: string }>(),
- LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ nodeId: string, nodes: Array<TreePickerNode>, pickerId: string }>(),
- TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ nodeId: string, pickerId: string }>(),
- TOGGLE_TREE_PICKER_NODE_SELECT: ofType<{ nodeId: string, pickerId: string }>(),
- EXPAND_TREE_PICKER_NODES: ofType<{ nodeIds: string[], pickerId: string }>(),
- RESET_TREE_PICKER: ofType<{ pickerId: string }>()
-});
-
-export type FileTreePickerAction = UnionOf<typeof fileTreePickerActions>;
diff --git a/src/store/file-tree-picker/file-tree-picker-reducer.ts b/src/store/file-tree-picker/file-tree-picker-reducer.ts
deleted file mode 100644
index 59e33a9..0000000
--- a/src/store/file-tree-picker/file-tree-picker-reducer.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { createTree, setNodeValueWith, TreeNode, setNode, mapTreeValues, Tree } from "~/models/tree";
-import { TreePicker, TreePickerNode } from "./file-tree-picker";
-import { fileTreePickerActions, FileTreePickerAction } from "./file-tree-picker-actions";
-import { TreeItemStatus } from "~/components/tree/tree";
-import { compose } from "redux";
-import { getNode } from '~/models/tree';
-
-export const fileTreePickerReducer = (state: TreePicker = {}, action: FileTreePickerAction) =>
- fileTreePickerActions.match(action, {
- LOAD_TREE_PICKER_NODE: ({ nodeId, pickerId }) =>
- updateOrCreatePicker(state, pickerId, setNodeValueWith(setPending)(nodeId)),
- LOAD_TREE_PICKER_NODE_SUCCESS: ({ nodeId, nodes, pickerId }) =>
- updateOrCreatePicker(state, pickerId, compose(receiveNodes(nodes)(nodeId), setNodeValueWith(setLoaded)(nodeId))),
- TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ nodeId, pickerId }) =>
- updateOrCreatePicker(state, pickerId, setNodeValueWith(toggleCollapse)(nodeId)),
- TOGGLE_TREE_PICKER_NODE_SELECT: ({ nodeId, pickerId }) =>
- updateOrCreatePicker(state, pickerId, mapTreeValues(toggleSelect(nodeId))),
- RESET_TREE_PICKER: ({ pickerId }) =>
- updateOrCreatePicker(state, pickerId, createTree),
- EXPAND_TREE_PICKER_NODES: ({ pickerId, nodeIds }) =>
- updateOrCreatePicker(state, pickerId, mapTreeValues(expand(nodeIds))),
- default: () => state
- });
-
-const updateOrCreatePicker = (state: TreePicker, pickerId: string, func: (value: Tree<TreePickerNode>) => Tree<TreePickerNode>) => {
- const picker = state[pickerId] || createTree();
- const updatedPicker = func(picker);
- return { ...state, [pickerId]: updatedPicker };
-};
-
-const expand = (ids: string[]) => (node: TreePickerNode): TreePickerNode =>
- ids.some(id => id === node.nodeId)
- ? { ...node, collapsed: false }
- : node;
-
-const setPending = (value: TreePickerNode): TreePickerNode =>
- ({ ...value, status: TreeItemStatus.PENDING });
-
-const setLoaded = (value: TreePickerNode): TreePickerNode =>
- ({ ...value, status: TreeItemStatus.LOADED });
-
-const toggleCollapse = (value: TreePickerNode): TreePickerNode =>
- ({ ...value, collapsed: !value.collapsed });
-
-const toggleSelect = (nodeId: string) => (value: TreePickerNode): TreePickerNode =>
- value.nodeId === nodeId
- ? ({ ...value, selected: !value.selected })
- : ({ ...value, selected: false });
-
-const receiveNodes = (nodes: Array<TreePickerNode>) => (parent: string) => (state: Tree<TreePickerNode>) => {
- const parentNode = getNode(parent)(state);
- let newState = state;
- if (parentNode) {
- newState = setNode({ ...parentNode, children: [] })(state);
- }
- return nodes.reduce((tree, node) => {
- const oldNode = getNode(node.nodeId)(state) || { value: {} };
- const newNode = createTreeNode(parent)(node);
- const value = { ...oldNode.value, ...newNode.value };
- return setNode({ ...newNode, value })(tree);
- }, newState);
-};
-
-const createTreeNode = (parent: string) => (node: TreePickerNode): TreeNode<TreePickerNode> => ({
- children: [],
- id: node.nodeId,
- parent,
- value: node
-});
diff --git a/src/store/file-tree-picker/file-tree-picker.ts b/src/store/file-tree-picker/file-tree-picker.ts
deleted file mode 100644
index 259a4b8..0000000
--- a/src/store/file-tree-picker/file-tree-picker.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { Tree } from "~/models/tree";
-import { TreeItemStatus } from "~/components/tree/tree";
-
-export type TreePicker = { [key: string]: Tree<TreePickerNode> };
-
-export interface TreePickerNode<Value = any> {
- nodeId: string;
- value: Value;
- selected: boolean;
- collapsed: boolean;
- status: TreeItemStatus;
-}
-
-export const createTreePickerNode = (data: { nodeId: string, value: any }) => ({
- ...data,
- selected: false,
- collapsed: true,
- status: TreeItemStatus.INITIAL
-});
-
-export const getTreePicker = <Value = {}>(id: string) => (state: TreePicker): Tree<TreePickerNode<Value>> | undefined => state[id];
\ No newline at end of file
diff --git a/src/store/side-panel-tree/side-panel-tree-actions.ts b/src/store/side-panel-tree/side-panel-tree-actions.ts
index 22a83dd..ec593b9 100644
--- a/src/store/side-panel-tree/side-panel-tree-actions.ts
+++ b/src/store/side-panel-tree/side-panel-tree-actions.ts
@@ -4,14 +4,12 @@
import { Dispatch } from 'redux';
import { treePickerActions } from "~/store/tree-picker/tree-picker-actions";
-import { createTreePickerNode, TreePickerNode } from '~/store/tree-picker/tree-picker';
import { RootState } from '../store';
import { ServiceRepository } from '~/services/services';
import { FilterBuilder } from '~/services/api/filter-builder';
import { resourcesActions } from '../resources/resources-actions';
import { getTreePicker, TreePicker } from '../tree-picker/tree-picker';
-import { TreeItemStatus } from "~/components/tree/tree";
-import { getNodeAncestors, getNodeValue, getNodeAncestorsIds, getNode } from '~/models/tree';
+import { getNodeAncestors, getNodeAncestorsIds, getNode, TreeNode, initTreeNode, TreeNodeStatus } from '~/models/tree';
import { ProjectResource } from '~/models/project';
import { OrderBuilder } from '../../services/api/order-builder';
@@ -29,11 +27,11 @@ export const SIDE_PANEL_TREE = 'sidePanelTree';
export const getSidePanelTree = (treePicker: TreePicker) =>
getTreePicker<ProjectResource | string>(SIDE_PANEL_TREE)(treePicker);
-export const getSidePanelTreeBranch = (uuid: string) => (treePicker: TreePicker): Array<TreePickerNode<ProjectResource | string>> => {
+export const getSidePanelTreeBranch = (uuid: string) => (treePicker: TreePicker): Array<TreeNode<ProjectResource | string>> => {
const tree = getSidePanelTree(treePicker);
if (tree) {
- const ancestors = getNodeAncestors(uuid)(tree).map(node => node.value);
- const node = getNodeValue(uuid)(tree);
+ const ancestors = getNodeAncestors(uuid)(tree);
+ const node = getNode(uuid)(tree);
if (node) {
return [...ancestors, node];
}
@@ -54,16 +52,16 @@ export const isSidePanelTreeCategory = (id: string) => SIDE_PANEL_CATEGORIES.som
export const initSidePanelTree = () =>
(dispatch: Dispatch, getState: () => RootState, { authService }: ServiceRepository) => {
const rootProjectUuid = authService.getUuid() || '';
- const nodes = SIDE_PANEL_CATEGORIES.map(nodeId => createTreePickerNode({ nodeId, value: nodeId }));
- const projectsNode = createTreePickerNode({ nodeId: rootProjectUuid, value: SidePanelTreeCategory.PROJECTS });
+ const nodes = SIDE_PANEL_CATEGORIES.map(id => initTreeNode({ id, value: id }));
+ const projectsNode = initTreeNode({ id: rootProjectUuid, value: SidePanelTreeCategory.PROJECTS });
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
- nodeId: '',
+ id: '',
pickerId: SIDE_PANEL_TREE,
nodes: [projectsNode, ...nodes]
}));
SIDE_PANEL_CATEGORIES.forEach(category => {
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
- nodeId: category,
+ id: category,
pickerId: SIDE_PANEL_TREE,
nodes: []
}));
@@ -75,7 +73,7 @@ export const loadSidePanelTreeProjects = (projectUuid: string) =>
const treePicker = getTreePicker(SIDE_PANEL_TREE)(getState().treePicker);
const node = treePicker ? getNode(projectUuid)(treePicker) : undefined;
if (node || projectUuid === '') {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ nodeId: projectUuid, pickerId: SIDE_PANEL_TREE }));
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: projectUuid, pickerId: SIDE_PANEL_TREE }));
const params = {
filters: new FilterBuilder()
.addEqual('ownerUuid', projectUuid)
@@ -86,81 +84,81 @@ export const loadSidePanelTreeProjects = (projectUuid: string) =>
};
const { items } = await services.projectService.list(params);
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
- nodeId: projectUuid,
+ id: projectUuid,
pickerId: SIDE_PANEL_TREE,
- nodes: items.map(item => createTreePickerNode({ nodeId: item.uuid, value: item })),
+ nodes: items.map(item => initTreeNode({ id: item.uuid, value: item })),
}));
dispatch(resourcesActions.SET_RESOURCES(items));
}
};
-export const activateSidePanelTreeItem = (nodeId: string) =>
+export const activateSidePanelTreeItem = (id: string) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- const node = getSidePanelTreeNode(nodeId)(getState().treePicker);
- if (node && !node.selected) {
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId, pickerId: SIDE_PANEL_TREE }));
+ const node = getSidePanelTreeNode(id)(getState().treePicker);
+ if (node && !node.active) {
+ dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id, pickerId: SIDE_PANEL_TREE }));
}
- if (!isSidePanelTreeCategory(nodeId)) {
- await dispatch<any>(activateSidePanelTreeProject(nodeId));
+ if (!isSidePanelTreeCategory(id)) {
+ await dispatch<any>(activateSidePanelTreeProject(id));
}
};
-export const activateSidePanelTreeProject = (nodeId: string) =>
+export const activateSidePanelTreeProject = (id: string) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
const { treePicker } = getState();
- const node = getSidePanelTreeNode(nodeId)(treePicker);
- if (node && node.status !== TreeItemStatus.LOADED) {
- await dispatch<any>(loadSidePanelTreeProjects(nodeId));
+ const node = getSidePanelTreeNode(id)(treePicker);
+ if (node && node.status !== TreeNodeStatus.LOADED) {
+ await dispatch<any>(loadSidePanelTreeProjects(id));
} else if (node === undefined) {
- await dispatch<any>(activateSidePanelTreeBranch(nodeId));
+ await dispatch<any>(activateSidePanelTreeBranch(id));
}
dispatch(treePickerActions.EXPAND_TREE_PICKER_NODES({
- nodeIds: getSidePanelTreeNodeAncestorsIds(nodeId)(treePicker),
+ ids: getSidePanelTreeNodeAncestorsIds(id)(treePicker),
pickerId: SIDE_PANEL_TREE
}));
- dispatch<any>(expandSidePanelTreeItem(nodeId));
+ dispatch<any>(expandSidePanelTreeItem(id));
};
-export const activateSidePanelTreeBranch = (nodeId: string) =>
+export const activateSidePanelTreeBranch = (id: string) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- const ancestors = await services.ancestorsService.ancestors(nodeId, services.authService.getUuid() || '');
+ const ancestors = await services.ancestorsService.ancestors(id, services.authService.getUuid() || '');
for (const ancestor of ancestors) {
await dispatch<any>(loadSidePanelTreeProjects(ancestor.uuid));
}
dispatch(treePickerActions.EXPAND_TREE_PICKER_NODES({
- nodeIds: ancestors.map(ancestor => ancestor.uuid),
+ ids: ancestors.map(ancestor => ancestor.uuid),
pickerId: SIDE_PANEL_TREE
}));
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId, pickerId: SIDE_PANEL_TREE }));
+ dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id, pickerId: SIDE_PANEL_TREE }));
};
-export const toggleSidePanelTreeItemCollapse = (nodeId: string) =>
+export const toggleSidePanelTreeItemCollapse = (id: string) =>
async (dispatch: Dispatch, getState: () => RootState) => {
- const node = getSidePanelTreeNode(nodeId)(getState().treePicker);
- if (node && node.status === TreeItemStatus.INITIAL) {
- await dispatch<any>(loadSidePanelTreeProjects(node.nodeId));
+ const node = getSidePanelTreeNode(id)(getState().treePicker);
+ if (node && node.status === TreeNodeStatus.INITIAL) {
+ await dispatch<any>(loadSidePanelTreeProjects(node.id));
}
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId, pickerId: SIDE_PANEL_TREE }));
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId: SIDE_PANEL_TREE }));
};
-export const expandSidePanelTreeItem = (nodeId: string) =>
+export const expandSidePanelTreeItem = (id: string) =>
async (dispatch: Dispatch, getState: () => RootState) => {
- const node = getSidePanelTreeNode(nodeId)(getState().treePicker);
- if (node && node.collapsed) {
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId, pickerId: SIDE_PANEL_TREE }));
+ const node = getSidePanelTreeNode(id)(getState().treePicker);
+ if (node && !node.expanded) {
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId: SIDE_PANEL_TREE }));
}
};
-export const getSidePanelTreeNode = (nodeId: string) => (treePicker: TreePicker) => {
+export const getSidePanelTreeNode = (id: string) => (treePicker: TreePicker) => {
const sidePanelTree = getTreePicker(SIDE_PANEL_TREE)(treePicker);
return sidePanelTree
- ? getNodeValue(nodeId)(sidePanelTree)
+ ? getNode(id)(sidePanelTree)
: undefined;
};
-export const getSidePanelTreeNodeAncestorsIds = (nodeId: string) => (treePicker: TreePicker) => {
+export const getSidePanelTreeNodeAncestorsIds = (id: string) => (treePicker: TreePicker) => {
const sidePanelTree = getTreePicker(SIDE_PANEL_TREE)(treePicker);
return sidePanelTree
- ? getNodeAncestorsIds(nodeId)(sidePanelTree)
+ ? getNodeAncestorsIds(id)(sidePanelTree)
: [];
};
diff --git a/src/store/store.ts b/src/store/store.ts
index 4c08a39..21e5029 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -38,7 +38,6 @@ import { progressIndicatorReducer } from './progress-indicator/progress-indicato
import { runProcessPanelReducer } from '~/store/run-process-panel/run-process-panel-reducer';
import { WorkflowMiddlewareService } from './workflow-panel/workflow-middleware-service';
import { WORKFLOW_PANEL_ID } from './workflow-panel/workflow-panel-actions';
-import { fileTreePickerReducer } from './file-tree-picker/file-tree-picker-reducer';
const composeEnhancers =
(process.env.NODE_ENV === 'development' &&
@@ -100,6 +99,5 @@ const createRootReducer = (services: ServiceRepository) => combineReducers({
fileUploader: fileUploaderReducer,
processPanel: processPanelReducer,
progressIndicator: progressIndicatorReducer,
- fileTreePicker: fileTreePickerReducer,
runProcessPanel: runProcessPanelReducer
});
diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index 5b04389..0385766 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -3,15 +3,16 @@
// SPDX-License-Identifier: AGPL-3.0
import { unionize, ofType, UnionOf } from "~/common/unionize";
+import { TreeNode } from '~/models/tree';
-import { TreePickerNode } from "./tree-picker";
export const treePickerActions = unionize({
- LOAD_TREE_PICKER_NODE: ofType<{ nodeId: string, pickerId: string }>(),
- LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ nodeId: string, nodes: Array<TreePickerNode>, pickerId: string }>(),
- TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ nodeId: string, pickerId: string }>(),
- TOGGLE_TREE_PICKER_NODE_SELECT: ofType<{ nodeId: string, pickerId: string }>(),
- EXPAND_TREE_PICKER_NODES: ofType<{ nodeIds: string[], pickerId: string }>(),
+ LOAD_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(),
+ LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ id: string, nodes: Array<TreeNode<any>>, pickerId: string }>(),
+ TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ id: string, pickerId: string }>(),
+ ACTIVATE_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(),
+ TOGGLE_TREE_PICKER_NODE_SELECTION: ofType<{ id: string, pickerId: string }>(),
+ EXPAND_TREE_PICKER_NODES: ofType<{ ids: string[], pickerId: string }>(),
RESET_TREE_PICKER: ofType<{ pickerId: string }>()
});
diff --git a/src/store/tree-picker/tree-picker-reducer.test.ts b/src/store/tree-picker/tree-picker-reducer.test.ts
index e09d12d..56a3ba5 100644
--- a/src/store/tree-picker/tree-picker-reducer.test.ts
+++ b/src/store/tree-picker/tree-picker-reducer.test.ts
@@ -3,103 +3,103 @@
// SPDX-License-Identifier: AGPL-3.0
import { createTree, getNodeValue, getNodeChildrenIds } from "~/models/tree";
-import { TreePickerNode, createTreePickerNode } from "./tree-picker";
import { treePickerReducer } from "./tree-picker-reducer";
import { treePickerActions } from "./tree-picker-actions";
import { TreeItemStatus } from "~/components/tree/tree";
+import { initTreeNode } from '~/models/tree';
describe('TreePickerReducer', () => {
it('LOAD_TREE_PICKER_NODE - initial state', () => {
- const tree = createTree<TreePickerNode>();
- const newState = treePickerReducer({}, treePickerActions.LOAD_TREE_PICKER_NODE({ nodeId: '1', pickerId: "projects" }));
+ const tree = createTree<{}>();
+ const newState = treePickerReducer({}, treePickerActions.LOAD_TREE_PICKER_NODE({ id: '1', pickerId: "projects" }));
expect(newState).toEqual({ 'projects': tree });
});
it('LOAD_TREE_PICKER_NODE', () => {
- const node = createTreePickerNode({ nodeId: '1', value: '1' });
+ const node = initTreeNode({ id: '1', value: '1' });
const [newState] = [{
- projects: createTree<TreePickerNode>()
+ projects: createTree<{}>()
}]
- .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ nodeId: '', nodes: [node], pickerId: "projects" })))
- .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE({ nodeId: '1', pickerId: "projects" })));
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE({ id: '1', pickerId: "projects" })));
expect(getNodeValue('1')(newState.projects)).toEqual({
- ...createTreePickerNode({ nodeId: '1', value: '1' }),
+ ...initTreeNode({ id: '1', value: '1' }),
status: TreeItemStatus.PENDING
});
});
it('LOAD_TREE_PICKER_NODE_SUCCESS - initial state', () => {
- const subNode = createTreePickerNode({ nodeId: '1.1', value: '1.1' });
- const newState = treePickerReducer({}, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ nodeId: '', nodes: [subNode], pickerId: "projects" }));
+ const subNode = initTreeNode({ id: '1.1', value: '1.1' });
+ const newState = treePickerReducer({}, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [subNode], pickerId: "projects" }));
expect(getNodeChildrenIds('')(newState.projects)).toEqual(['1.1']);
});
it('LOAD_TREE_PICKER_NODE_SUCCESS', () => {
- const node = createTreePickerNode({ nodeId: '1', value: '1' });
- const subNode = createTreePickerNode({ nodeId: '1.1', value: '1.1' });
+ const node = initTreeNode({ id: '1', value: '1' });
+ const subNode = initTreeNode({ id: '1.1', value: '1.1' });
const [newState] = [{
- projects: createTree<TreePickerNode>()
+ projects: createTree<{}>()
}]
- .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ nodeId: '', nodes: [node], pickerId: "projects" })))
- .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ nodeId: '1', nodes: [subNode], pickerId: "projects" })));
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '1', nodes: [subNode], pickerId: "projects" })));
expect(getNodeChildrenIds('1')(newState.projects)).toEqual(['1.1']);
expect(getNodeValue('1')(newState.projects)).toEqual({
- ...createTreePickerNode({ nodeId: '1', value: '1' }),
+ ...initTreeNode({ id: '1', value: '1' }),
status: TreeItemStatus.LOADED
});
});
it('TOGGLE_TREE_PICKER_NODE_COLLAPSE - collapsed', () => {
- const node = createTreePickerNode({ nodeId: '1', value: '1' });
+ const node = initTreeNode({ id: '1', value: '1' });
const [newState] = [{
- projects: createTree<TreePickerNode>()
+ projects: createTree<{}>()
}]
- .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ nodeId: '', nodes: [node], pickerId: "projects" })))
- .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId: '1', pickerId: "projects" })));
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id: '1', pickerId: "projects" })));
expect(getNodeValue('1')(newState.projects)).toEqual({
- ...createTreePickerNode({ nodeId: '1', value: '1' }),
+ ...initTreeNode({ id: '1', value: '1' }),
collapsed: false
});
});
it('TOGGLE_TREE_PICKER_NODE_COLLAPSE - expanded', () => {
- const node = createTreePickerNode({ nodeId: '1', value: '1' });
+ const node = initTreeNode({ id: '1', value: '1' });
const [newState] = [{
- projects: createTree<TreePickerNode>()
+ projects: createTree<{}>()
}]
- .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ nodeId: '', nodes: [node], pickerId: "projects" })))
- .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId: '1', pickerId: "projects" })))
- .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId: '1', pickerId: "projects" })));
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id: '1', pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id: '1', pickerId: "projects" })));
expect(getNodeValue('1')(newState.projects)).toEqual({
- ...createTreePickerNode({ nodeId: '1', value: '1' }),
+ ...initTreeNode({ id: '1', value: '1' }),
collapsed: true
});
});
it('TOGGLE_TREE_PICKER_NODE_SELECT - selected', () => {
- const node = createTreePickerNode({ nodeId: '1', value: '1' });
+ const node = initTreeNode({ id: '1', value: '1' });
const [newState] = [{
- projects: createTree<TreePickerNode>()
+ projects: createTree<{}>()
}]
- .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ nodeId: '', nodes: [node], pickerId: "projects" })))
- .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId: '1', pickerId: "projects" })));
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id: '1', pickerId: "projects" })));
expect(getNodeValue('1')(newState.projects)).toEqual({
- ...createTreePickerNode({ nodeId: '1', value: '1' }),
+ ...initTreeNode({ id: '1', value: '1' }),
selected: true
});
});
it('TOGGLE_TREE_PICKER_NODE_SELECT - not selected', () => {
- const node = createTreePickerNode({ nodeId: '1', value: '1' });
+ const node = initTreeNode({ id: '1', value: '1' });
const [newState] = [{
- projects: createTree<TreePickerNode>()
+ projects: createTree<{}>()
}]
- .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ nodeId: '', nodes: [node], pickerId: "projects" })))
- .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId: '1', pickerId: "projects" })))
- .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId: '1', pickerId: "projects" })));
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id: '1', pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id: '1', pickerId: "projects" })));
expect(getNodeValue('1')(newState.projects)).toEqual({
- ...createTreePickerNode({ nodeId: '1', value: '1' }),
+ ...initTreeNode({ id: '1', value: '1' }),
selected: false
});
});
diff --git a/src/store/tree-picker/tree-picker-reducer.ts b/src/store/tree-picker/tree-picker-reducer.ts
index b0d9bc9..054cb44 100644
--- a/src/store/tree-picker/tree-picker-reducer.ts
+++ b/src/store/tree-picker/tree-picker-reducer.ts
@@ -2,72 +2,45 @@
//
// SPDX-License-Identifier: AGPL-3.0
-import { createTree, setNodeValueWith, TreeNode, setNode, mapTreeValues, Tree } from "~/models/tree";
-import { TreePicker, TreePickerNode } from "./tree-picker";
+import { createTree, TreeNode, setNode, Tree, TreeNodeStatus, setNodeStatus, expandNode } from '~/models/tree';
+import { TreePicker } from "./tree-picker";
import { treePickerActions, TreePickerAction } from "./tree-picker-actions";
-import { TreeItemStatus } from "~/components/tree/tree";
import { compose } from "redux";
-import { getNode } from '../../models/tree';
+import { getNode, toggleNodeCollapse, toggleNodeSelection } from '~/models/tree';
+import { activateNode } from '../../models/tree';
export const treePickerReducer = (state: TreePicker = {}, action: TreePickerAction) =>
treePickerActions.match(action, {
- LOAD_TREE_PICKER_NODE: ({ nodeId, pickerId }) =>
- updateOrCreatePicker(state, pickerId, setNodeValueWith(setPending)(nodeId)),
- LOAD_TREE_PICKER_NODE_SUCCESS: ({ nodeId, nodes, pickerId }) =>
- updateOrCreatePicker(state, pickerId, compose(receiveNodes(nodes)(nodeId), setNodeValueWith(setLoaded)(nodeId))),
- TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ nodeId, pickerId }) =>
- updateOrCreatePicker(state, pickerId, setNodeValueWith(toggleCollapse)(nodeId)),
- TOGGLE_TREE_PICKER_NODE_SELECT: ({ nodeId, pickerId }) =>
- updateOrCreatePicker(state, pickerId, mapTreeValues(toggleSelect(nodeId))),
+ LOAD_TREE_PICKER_NODE: ({ id, pickerId }) =>
+ updateOrCreatePicker(state, pickerId, setNodeStatus(id)(TreeNodeStatus.PENDING)),
+ LOAD_TREE_PICKER_NODE_SUCCESS: ({ id, nodes, pickerId }) =>
+ updateOrCreatePicker(state, pickerId, compose(receiveNodes(nodes)(id), setNodeStatus(id)(TreeNodeStatus.LOADED))),
+ TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ id, pickerId }) =>
+ updateOrCreatePicker(state, pickerId, toggleNodeCollapse(id)),
+ ACTIVATE_TREE_PICKER_NODE: ({ id, pickerId }) =>
+ updateOrCreatePicker(state, pickerId, activateNode(id)),
+ TOGGLE_TREE_PICKER_NODE_SELECTION: ({ id, pickerId }) =>
+ updateOrCreatePicker(state, pickerId, toggleNodeSelection(id)),
RESET_TREE_PICKER: ({ pickerId }) =>
updateOrCreatePicker(state, pickerId, createTree),
- EXPAND_TREE_PICKER_NODES: ({ pickerId, nodeIds }) =>
- updateOrCreatePicker(state, pickerId, mapTreeValues(expand(nodeIds))),
+ EXPAND_TREE_PICKER_NODES: ({ pickerId, ids }) =>
+ updateOrCreatePicker(state, pickerId, expandNode(...ids)),
default: () => state
});
-const updateOrCreatePicker = (state: TreePicker, pickerId: string, func: (value: Tree<TreePickerNode>) => Tree<TreePickerNode>) => {
+const updateOrCreatePicker = <V>(state: TreePicker, pickerId: string, func: (value: Tree<V>) => Tree<V>) => {
const picker = state[pickerId] || createTree();
const updatedPicker = func(picker);
return { ...state, [pickerId]: updatedPicker };
};
-const expand = (ids: string[]) => (node: TreePickerNode): TreePickerNode =>
- ids.some(id => id === node.nodeId)
- ? { ...node, collapsed: false }
- : node;
-
-const setPending = (value: TreePickerNode): TreePickerNode =>
- ({ ...value, status: TreeItemStatus.PENDING });
-
-const setLoaded = (value: TreePickerNode): TreePickerNode =>
- ({ ...value, status: TreeItemStatus.LOADED });
-
-const toggleCollapse = (value: TreePickerNode): TreePickerNode =>
- ({ ...value, collapsed: !value.collapsed });
-
-const toggleSelect = (nodeId: string) => (value: TreePickerNode): TreePickerNode =>
- value.nodeId === nodeId
- ? ({ ...value, selected: !value.selected })
- : ({ ...value, selected: false });
-
-const receiveNodes = (nodes: Array<TreePickerNode>) => (parent: string) => (state: Tree<TreePickerNode>) => {
+const receiveNodes = <V>(nodes: Array<TreeNode<V>>) => (parent: string) => (state: Tree<V>) => {
const parentNode = getNode(parent)(state);
let newState = state;
if (parentNode) {
newState = setNode({ ...parentNode, children: [] })(state);
}
return nodes.reduce((tree, node) => {
- const oldNode = getNode(node.nodeId)(state) || { value: {} };
- const newNode = createTreeNode(parent)(node);
- const value = { ...oldNode.value, ...newNode.value };
- return setNode({ ...newNode, value })(tree);
+ return setNode({ ...node, parent })(tree);
}, newState);
};
-
-const createTreeNode = (parent: string) => (node: TreePickerNode): TreeNode<TreePickerNode> => ({
- children: [],
- id: node.nodeId,
- parent,
- value: node
-});
diff --git a/src/store/tree-picker/tree-picker.ts b/src/store/tree-picker/tree-picker.ts
index 259a4b8..a4127fa 100644
--- a/src/store/tree-picker/tree-picker.ts
+++ b/src/store/tree-picker/tree-picker.ts
@@ -3,17 +3,10 @@
// SPDX-License-Identifier: AGPL-3.0
import { Tree } from "~/models/tree";
-import { TreeItemStatus } from "~/components/tree/tree";
+import { TreeItemStatus } from '~/components/tree/tree';
+export type TreePicker = { [key: string]: Tree<any> };
-export type TreePicker = { [key: string]: Tree<TreePickerNode> };
-
-export interface TreePickerNode<Value = any> {
- nodeId: string;
- value: Value;
- selected: boolean;
- collapsed: boolean;
- status: TreeItemStatus;
-}
+export const getTreePicker = <Value = {}>(id: string) => (state: TreePicker): Tree<Value> | undefined => state[id];
export const createTreePickerNode = (data: { nodeId: string, value: any }) => ({
...data,
@@ -21,5 +14,3 @@ export const createTreePickerNode = (data: { nodeId: string, value: any }) => ({
collapsed: true,
status: TreeItemStatus.INITIAL
});
-
-export const getTreePicker = <Value = {}>(id: string) => (state: TreePicker): Tree<TreePickerNode<Value>> | undefined => state[id];
\ No newline at end of file
diff --git a/src/views-components/dialog-file-selection/dialog-file-selection.tsx b/src/views-components/dialog-file-selection/dialog-file-selection.tsx
deleted file mode 100644
index e7185c0..0000000
--- a/src/views-components/dialog-file-selection/dialog-file-selection.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import * as React from 'react';
-import { InjectedFormProps, Field } from 'redux-form';
-import { WithDialogProps } from '~/store/dialog/with-dialog';
-import { CollectionCreateFormDialogData } from '~/store/collections/collection-create-actions';
-import { FormDialog } from '~/components/form-dialog/form-dialog';
-import { require } from '~/validators/require';
-import { FileTreePickerField } from '~/views-components/file-tree-picker/file-tree-picker';
-
-type FileSelectionProps = WithDialogProps<{}> & InjectedFormProps<CollectionCreateFormDialogData>;
-
-export const DialogFileSelection = (props: FileSelectionProps) =>
- <FormDialog
- dialogTitle='Choose a file'
- formFields={FileSelectionFields}
- submitLabel='Ok'
- {...props}
- />;
-
-const FileSelectionFields = () =>
- <Field
- name='tree'
- validate={FILES_FIELD_VALIDATION}
- component={FileTreePickerField} />;
-
-const FILES_FIELD_VALIDATION = [require];
\ No newline at end of file
diff --git a/src/views-components/dialog-forms/file-selection-dialog.ts b/src/views-components/dialog-forms/file-selection-dialog.ts
deleted file mode 100644
index 4a3f2af..0000000
--- a/src/views-components/dialog-forms/file-selection-dialog.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { compose } from "redux";
-import { reduxForm } from 'redux-form';
-import { withDialog } from "~/store/dialog/with-dialog";
-import { FILE_SELECTION } from '~/store/file-selection/file-selection-actions';
-import { DialogFileSelection } from '~/views-components/dialog-file-selection/dialog-file-selection';
-import { dialogActions } from '~/store/dialog/dialog-actions';
-
-export const FileSelectionDialog = compose(
- withDialog(FILE_SELECTION),
- reduxForm({
- form: FILE_SELECTION,
- onSubmit: (data, dispatch) => {
- console.log(data);
- dispatch(dialogActions.CLOSE_DIALOG({ id: FILE_SELECTION }));
- return data;
- }
- })
-)(DialogFileSelection);
\ No newline at end of file
diff --git a/src/views-components/file-tree-picker/file-tree-picker.tsx b/src/views-components/file-tree-picker/file-tree-picker.tsx
deleted file mode 100644
index 129cb75..0000000
--- a/src/views-components/file-tree-picker/file-tree-picker.tsx
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import * as React from "react";
-import { Dispatch } from "redux";
-import { connect } from "react-redux";
-import { Typography } from "@material-ui/core";
-import { MainFileTreePicker, MainFileTreePickerProps } from "./main-file-tree-picker";
-import { TreeItem, TreeItemStatus } from "~/components/tree/tree";
-import { ProjectResource } from "~/models/project";
-import { fileTreePickerActions } from "~/store/file-tree-picker/file-tree-picker-actions";
-import { ListItemTextIcon } from "~/components/list-item-text-icon/list-item-text-icon";
-import { ProjectIcon, FavoriteIcon, ProjectsIcon, ShareMeIcon, CollectionIcon } from '~/components/icon/icon';
-import { createTreePickerNode } from "~/store/tree-picker/tree-picker";
-import { RootState } from "~/store/store";
-import { ServiceRepository } from "~/services/services";
-import { FilterBuilder } from "~/services/api/filter-builder";
-import { WrappedFieldProps } from 'redux-form';
-import { ResourceKind, extractUuidKind } from '~/models/resource';
-import { GroupContentsResource } from '~/services/groups-service/groups-service';
-import { loadCollectionFiles } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
-
-type FileTreePickerProps = Pick<MainFileTreePickerProps, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen'>;
-
-const mapDispatchToProps = (dispatch: Dispatch, props: { onChange: (projectUuid: string) => void }): FileTreePickerProps => ({
- onContextMenu: () => { return; },
- toggleItemActive: (nodeId, status, pickerId) => {
- getNotSelectedTreePickerKind(pickerId)
- .forEach(pickerId => dispatch(fileTreePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId: '', pickerId })));
- dispatch(fileTreePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId, pickerId }));
-
- props.onChange(nodeId);
- },
- toggleItemOpen: (nodeId, status, pickerId) => {
- dispatch<any>(toggleItemOpen(nodeId, status, pickerId));
- }
-});
-
-const toggleItemOpen = (nodeId: string, status: TreeItemStatus, pickerId: string) =>
- (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- if (status === TreeItemStatus.INITIAL) {
- if (pickerId === TreePickerId.PROJECTS) {
- dispatch<any>(loadProjectTreePicker(nodeId));
- } else if (pickerId === TreePickerId.FAVORITES) {
- dispatch<any>(loadFavoriteTreePicker(nodeId === services.authService.getUuid() ? '' : nodeId));
- } else {
- // TODO: load sharedWithMe
- }
- } else {
- dispatch(fileTreePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId, pickerId }));
- }
- };
-
-const getNotSelectedTreePickerKind = (pickerId: string) => {
- return [TreePickerId.PROJECTS, TreePickerId.FAVORITES, TreePickerId.SHARED_WITH_ME].filter(nodeId => nodeId !== pickerId);
-};
-
-enum TreePickerId {
- PROJECTS = 'Projects',
- SHARED_WITH_ME = 'Shared with me',
- FAVORITES = 'Favorites'
-}
-
-export const FileTreePicker = connect(undefined, mapDispatchToProps)((props: FileTreePickerProps) =>
- <div style={{ display: 'flex', flexDirection: 'column' }}>
- <div style={{ flexGrow: 1, overflow: 'auto' }}>
- <MainFileTreePicker {...props} render={renderTreeItem} pickerId={TreePickerId.PROJECTS} />
- <MainFileTreePicker {...props} render={renderTreeItem} pickerId={TreePickerId.SHARED_WITH_ME} />
- <MainFileTreePicker {...props} render={renderTreeItem} pickerId={TreePickerId.FAVORITES} />
- </div>
- </div>);
-
-export const loadProjectTreePicker = (nodeId: string) =>
- async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- dispatch(fileTreePickerActions.LOAD_TREE_PICKER_NODE({ nodeId, pickerId: TreePickerId.PROJECTS }));
-
- const ownerUuid = nodeId.length === 0 ? services.authService.getUuid() || '' : nodeId;
-
- const filters = new FilterBuilder()
- .addIsA("uuid", [ResourceKind.PROJECT, ResourceKind.COLLECTION])
- .addEqual('ownerUuid', ownerUuid)
- .getFilters();
-
- // TODO: loadfiles from collections
- const { items } = (extractUuidKind(nodeId) === ResourceKind.COLLECTION)
- ? dispatch<any>(loadCollectionFiles(nodeId))
- : await services.groupsService.contents(ownerUuid, { filters });
-
- await dispatch<any>(receiveTreePickerData(nodeId, items, TreePickerId.PROJECTS));
- };
-
-export const loadFavoriteTreePicker = (nodeId: string) =>
- async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- const parentId = services.authService.getUuid() || '';
-
- if (nodeId === '') {
- dispatch(fileTreePickerActions.LOAD_TREE_PICKER_NODE({ nodeId: parentId, pickerId: TreePickerId.FAVORITES }));
- const { items } = await services.favoriteService.list(parentId);
-
- dispatch<any>(receiveTreePickerData(parentId, items as ProjectResource[], TreePickerId.FAVORITES));
- } else {
- dispatch(fileTreePickerActions.LOAD_TREE_PICKER_NODE({ nodeId, pickerId: TreePickerId.FAVORITES }));
- const filters = new FilterBuilder()
- .addEqual('ownerUuid', nodeId)
- .getFilters();
-
- const { items } = (extractUuidKind(nodeId) === ResourceKind.COLLECTION)
- ? dispatch<any>(loadCollectionFiles(nodeId))
- : await services.groupsService.contents(parentId, { filters });
-
-
- dispatch<any>(receiveTreePickerData(nodeId, items, TreePickerId.FAVORITES));
- }
- };
-
-const getProjectPickerIcon = (item: TreeItem<ProjectResource>) => {
- switch (item.data.name) {
- case TreePickerId.FAVORITES:
- return FavoriteIcon;
- case TreePickerId.PROJECTS:
- return ProjectsIcon;
- case TreePickerId.SHARED_WITH_ME:
- return ShareMeIcon;
- default:
- return getResourceIcon(item);
- }
-};
-
-const getResourceIcon = (item: TreeItem<GroupContentsResource>) => {
- switch (item.data.kind) {
- case ResourceKind.COLLECTION:
- return CollectionIcon;
- case ResourceKind.PROJECT:
- return ProjectIcon;
- default:
- return ProjectIcon;
- }
-};
-
-const renderTreeItem = (item: TreeItem<ProjectResource>) =>
- <ListItemTextIcon
- icon={getProjectPickerIcon(item)}
- name={item.data.name}
- isActive={item.active}
- hasMargin={true} />;
-
-
-export const receiveTreePickerData = (nodeId: string, items: GroupContentsResource[] = [], pickerId: string) =>
- (dispatch: Dispatch) => {
- dispatch(fileTreePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
- nodeId,
- nodes: items.map(item => createTreePickerNode({ nodeId: item.uuid, value: item })),
- pickerId,
- }));
-
- dispatch(fileTreePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId, pickerId }));
- };
-
-export const FileTreePickerField = (props: WrappedFieldProps) =>
- <div style={{ height: '200px', display: 'flex', flexDirection: 'column' }}>
- <FileTreePicker onChange={handleChange(props)} />
- {props.meta.dirty && props.meta.error &&
- <Typography variant='caption' color='error'>
- {props.meta.error}
- </Typography>}
- </div>;
-
-const handleChange = (props: WrappedFieldProps) => (value: string) =>
- props.input.value === value
- ? props.input.onChange('')
- : props.input.onChange(value);
-
diff --git a/src/views-components/file-tree-picker/main-file-tree-picker.ts b/src/views-components/file-tree-picker/main-file-tree-picker.ts
deleted file mode 100644
index dc52ea6..0000000
--- a/src/views-components/file-tree-picker/main-file-tree-picker.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { connect } from "react-redux";
-import { Tree, TreeProps, TreeItem, TreeItemStatus } from "~/components/tree/tree";
-import { RootState } from "~/store/store";
-import { createTreePickerNode, TreePickerNode } from "~/store/file-tree-picker/file-tree-picker";
-import { getNodeValue, getNodeChildrenIds, Tree as Ttree, createTree } from "~/models/tree";
-import { Dispatch } from "redux";
-
-export interface MainFileTreePickerProps {
- pickerId: string;
- onContextMenu: (event: React.MouseEvent<HTMLElement>, nodeId: string, pickerId: string) => void;
- toggleItemOpen: (nodeId: string, status: TreeItemStatus, pickerId: string) => void;
- toggleItemActive: (nodeId: string, status: TreeItemStatus, pickerId: string) => void;
-}
-
-const memoizedMapStateToProps = () => {
- let prevTree: Ttree<TreePickerNode>;
- let mappedProps: Pick<TreeProps<any>, 'items'>;
- return (state: RootState, props: MainFileTreePickerProps): Pick<TreeProps<any>, 'items'> => {
- const tree = state.treePicker[props.pickerId] || createTree();
- if(tree !== prevTree){
- prevTree = tree;
- mappedProps = {
- items: getNodeChildrenIds('')(tree)
- .map(treePickerToTreeItems(tree))
- };
- }
- return mappedProps;
- };
-};
-
-const mapDispatchToProps = (dispatch: Dispatch, props: MainFileTreePickerProps): Pick<TreeProps<any>, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive'> => ({
- onContextMenu: (event, item) => props.onContextMenu(event, item.id, props.pickerId),
- toggleItemActive: (id, status) => props.toggleItemActive(id, status, props.pickerId),
- toggleItemOpen: (id, status) => props.toggleItemOpen(id, status, props.pickerId)
-});
-
-export const MainFileTreePicker = connect(memoizedMapStateToProps(), mapDispatchToProps)(Tree);
-
-const treePickerToTreeItems = (tree: Ttree<TreePickerNode>) =>
- (id: string): TreeItem<any> => {
- const node: TreePickerNode = getNodeValue(id)(tree) || createTreePickerNode({ nodeId: '', value: 'InvalidNode' });
- const items = getNodeChildrenIds(node.nodeId)(tree)
- .map(treePickerToTreeItems(tree));
- return {
- active: node.selected,
- data: node.value,
- id: node.nodeId,
- items: items.length > 0 ? items : undefined,
- open: !node.collapsed,
- status: node.status
- };
- };
-
diff --git a/src/views-components/project-tree-picker/project-tree-picker.tsx b/src/views-components/project-tree-picker/project-tree-picker.tsx
index 9139ee7..16ab122 100644
--- a/src/views-components/project-tree-picker/project-tree-picker.tsx
+++ b/src/views-components/project-tree-picker/project-tree-picker.tsx
@@ -12,40 +12,43 @@ import { ProjectResource } from "~/models/project";
import { treePickerActions } from "~/store/tree-picker/tree-picker-actions";
import { ListItemTextIcon } from "~/components/list-item-text-icon/list-item-text-icon";
import { ProjectIcon, FavoriteIcon, ProjectsIcon, ShareMeIcon } from "~/components/icon/icon";
-import { createTreePickerNode } from "~/store/tree-picker/tree-picker";
import { RootState } from "~/store/store";
import { ServiceRepository } from "~/services/services";
import { FilterBuilder } from "~/services/api/filter-builder";
import { WrappedFieldProps } from 'redux-form';
+import { initTreeNode } from '~/models/tree';
-type ProjectTreePickerProps = Pick<TreePickerProps, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen'>;
+type ProjectTreePickerProps = Pick<TreePickerProps, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
const mapDispatchToProps = (dispatch: Dispatch, props: { onChange: (projectUuid: string) => void }): ProjectTreePickerProps => ({
onContextMenu: () => { return; },
- toggleItemActive: (nodeId, status, pickerId) => {
+ toggleItemActive: (id, status, pickerId) => {
getNotSelectedTreePickerKind(pickerId)
- .forEach(pickerId => dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId: '', pickerId })));
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId, pickerId }));
+ .forEach(pickerId => dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id: '', pickerId })));
+ dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id, pickerId }));
- props.onChange(nodeId);
+ props.onChange(id);
+ },
+ toggleItemOpen: (id, status, pickerId) => {
+ dispatch<any>(toggleItemOpen(id, status, pickerId));
+ },
+ toggleItemSelection: (id, pickerId) => {
+ dispatch<any>(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECTION({ id, pickerId }));
},
- toggleItemOpen: (nodeId, status, pickerId) => {
- dispatch<any>(toggleItemOpen(nodeId, status, pickerId));
- }
});
-const toggleItemOpen = (nodeId: string, status: TreeItemStatus, pickerId: string) =>
+const toggleItemOpen = (id: string, status: TreeItemStatus, pickerId: string) =>
(dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
if (status === TreeItemStatus.INITIAL) {
if (pickerId === TreePickerId.PROJECTS) {
- dispatch<any>(loadProjectTreePickerProjects(nodeId));
+ dispatch<any>(loadProjectTreePickerProjects(id));
} else if (pickerId === TreePickerId.FAVORITES) {
- dispatch<any>(loadFavoriteTreePickerProjects(nodeId === services.authService.getUuid() ? '' : nodeId));
+ dispatch<any>(loadFavoriteTreePickerProjects(id === services.authService.getUuid() ? '' : id));
} else {
// TODO: load sharedWithMe
}
} else {
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId, pickerId }));
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
}
};
@@ -73,11 +76,11 @@ export const ProjectTreePicker = connect(undefined, mapDispatchToProps)((props:
// TODO: move action creator to store directory
-export const loadProjectTreePickerProjects = (nodeId: string) =>
+export const loadProjectTreePickerProjects = (id: string) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ nodeId, pickerId: TreePickerId.PROJECTS }));
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerId.PROJECTS }));
- const ownerUuid = nodeId.length === 0 ? services.authService.getUuid() || '' : nodeId;
+ const ownerUuid = id.length === 0 ? services.authService.getUuid() || '' : id;
const filters = new FilterBuilder()
.addEqual('ownerUuid', ownerUuid)
@@ -85,27 +88,27 @@ export const loadProjectTreePickerProjects = (nodeId: string) =>
const { items } = await services.projectService.list({ filters });
- dispatch<any>(receiveTreePickerData(nodeId, items, TreePickerId.PROJECTS));
+ dispatch<any>(receiveTreePickerData(id, items, TreePickerId.PROJECTS));
};
-export const loadFavoriteTreePickerProjects = (nodeId: string) =>
+export const loadFavoriteTreePickerProjects = (id: string) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
const parentId = services.authService.getUuid() || '';
- if (nodeId === '') {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ nodeId: parentId, pickerId: TreePickerId.FAVORITES }));
+ if (id === '') {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: parentId, pickerId: TreePickerId.FAVORITES }));
const { items } = await services.favoriteService.list(parentId);
dispatch<any>(receiveTreePickerData(parentId, items as ProjectResource[], TreePickerId.FAVORITES));
} else {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ nodeId, pickerId: TreePickerId.FAVORITES }));
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerId.FAVORITES }));
const filters = new FilterBuilder()
- .addEqual('ownerUuid', nodeId)
+ .addEqual('ownerUuid', id)
.getFilters();
const { items } = await services.projectService.list({ filters });
- dispatch<any>(receiveTreePickerData(nodeId, items, TreePickerId.FAVORITES));
+ dispatch<any>(receiveTreePickerData(id, items, TreePickerId.FAVORITES));
}
};
@@ -132,15 +135,15 @@ const renderTreeItem = (item: TreeItem<ProjectResource>) =>
// TODO: move action creator to store directory
-export const receiveTreePickerData = (nodeId: string, projects: ProjectResource[], pickerId: string) =>
+export const receiveTreePickerData = (id: string, projects: ProjectResource[], pickerId: string) =>
(dispatch: Dispatch) => {
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
- nodeId,
- nodes: projects.map(project => createTreePickerNode({ nodeId: project.uuid, value: project })),
+ id,
+ nodes: projects.map(project => initTreeNode({ id: project.uuid, value: project })),
pickerId,
}));
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId, pickerId }));
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
};
export const ProjectTreePickerField = (props: WrappedFieldProps) =>
diff --git a/src/views-components/side-panel-tree/side-panel-tree.tsx b/src/views-components/side-panel-tree/side-panel-tree.tsx
index 4d4760f..96224cf 100644
--- a/src/views-components/side-panel-tree/side-panel-tree.tsx
+++ b/src/views-components/side-panel-tree/side-panel-tree.tsx
@@ -13,13 +13,13 @@ import { ProjectIcon, FavoriteIcon, ProjectsIcon, ShareMeIcon, TrashIcon } from
import { RecentIcon, WorkflowIcon } from '~/components/icon/icon';
import { activateSidePanelTreeItem, toggleSidePanelTreeItemCollapse, SIDE_PANEL_TREE, SidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
import { openSidePanelContextMenu } from '~/store/context-menu/context-menu-actions';
-
+import { noop } from 'lodash';
export interface SidePanelTreeProps {
onItemActivation: (id: string) => void;
sidePanelProgress?: boolean;
}
-type SidePanelTreeActionProps = Pick<TreePickerProps, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen'>;
+type SidePanelTreeActionProps = Pick<TreePickerProps, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
const mapDispatchToProps = (dispatch: Dispatch, props: SidePanelTreeProps): SidePanelTreeActionProps => ({
onContextMenu: (event, id) => {
@@ -31,7 +31,8 @@ const mapDispatchToProps = (dispatch: Dispatch, props: SidePanelTreeProps): Side
},
toggleItemOpen: (nodeId) => {
dispatch<any>(toggleSidePanelTreeItemCollapse(nodeId));
- }
+ },
+ toggleItemSelection: noop,
});
export const SidePanelTree = connect(undefined, mapDispatchToProps)(
diff --git a/src/views-components/tree-picker/tree-picker.ts b/src/views-components/tree-picker/tree-picker.ts
index 8b7630a..ff11913 100644
--- a/src/views-components/tree-picker/tree-picker.ts
+++ b/src/views-components/tree-picker/tree-picker.ts
@@ -5,23 +5,24 @@
import { connect } from "react-redux";
import { Tree, TreeProps, TreeItem, TreeItemStatus } from "~/components/tree/tree";
import { RootState } from "~/store/store";
-import { createTreePickerNode, TreePickerNode } from "~/store/tree-picker/tree-picker";
-import { getNodeValue, getNodeChildrenIds, Tree as Ttree, createTree } from "~/models/tree";
+import { getNodeValue, getNodeChildrenIds, Tree as Ttree, createTree, getNode, TreeNodeStatus } from '~/models/tree';
import { Dispatch } from "redux";
+import { initTreeNode } from '../../models/tree';
export interface TreePickerProps {
pickerId: string;
onContextMenu: (event: React.MouseEvent<HTMLElement>, nodeId: string, pickerId: string) => void;
toggleItemOpen: (nodeId: string, status: TreeItemStatus, pickerId: string) => void;
toggleItemActive: (nodeId: string, status: TreeItemStatus, pickerId: string) => void;
+ toggleItemSelection: (nodeId: string, pickerId: string) => void;
}
const memoizedMapStateToProps = () => {
- let prevTree: Ttree<TreePickerNode>;
+ let prevTree: Ttree<any>;
let mappedProps: Pick<TreeProps<any>, 'items'>;
return (state: RootState, props: TreePickerProps): Pick<TreeProps<any>, 'items'> => {
const tree = state.treePicker[props.pickerId] || createTree();
- if(tree !== prevTree){
+ if (tree !== prevTree) {
prevTree = tree;
mappedProps = {
items: getNodeChildrenIds('')(tree)
@@ -32,26 +33,39 @@ const memoizedMapStateToProps = () => {
};
};
-const mapDispatchToProps = (dispatch: Dispatch, props: TreePickerProps): Pick<TreeProps<any>, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive'> => ({
+const mapDispatchToProps = (dispatch: Dispatch, props: TreePickerProps): Pick<TreeProps<any>, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive' | 'toggleItemSelection'> => ({
onContextMenu: (event, item) => props.onContextMenu(event, item.id, props.pickerId),
toggleItemActive: (id, status) => props.toggleItemActive(id, status, props.pickerId),
- toggleItemOpen: (id, status) => props.toggleItemOpen(id, status, props.pickerId)
+ toggleItemOpen: (id, status) => props.toggleItemOpen(id, status, props.pickerId),
+ toggleItemSelection: (_, item) => props.toggleItemSelection(item.id, props.pickerId),
});
export const TreePicker = connect(memoizedMapStateToProps(), mapDispatchToProps)(Tree);
-const treePickerToTreeItems = (tree: Ttree<TreePickerNode>) =>
+const treePickerToTreeItems = (tree: Ttree<any>) =>
(id: string): TreeItem<any> => {
- const node: TreePickerNode = getNodeValue(id)(tree) || createTreePickerNode({ nodeId: '', value: 'InvalidNode' });
- const items = getNodeChildrenIds(node.nodeId)(tree)
+ const node = getNode(id)(tree) || initTreeNode({ id: '', value: 'InvalidNode' });
+ const items = getNodeChildrenIds(node.id)(tree)
.map(treePickerToTreeItems(tree));
return {
- active: node.selected,
+ active: node.active,
data: node.value,
- id: node.nodeId,
+ id: node.id,
items: items.length > 0 ? items : undefined,
- open: !node.collapsed,
- status: node.status
+ open: node.expanded,
+ selected: node.selected,
+ status: treeNodeStatusToTreeItem(node.status),
};
};
+export const treeNodeStatusToTreeItem = (status: TreeNodeStatus) => {
+ switch (status) {
+ case TreeNodeStatus.INITIAL:
+ return TreeItemStatus.INITIAL;
+ case TreeNodeStatus.PENDING:
+ return TreeItemStatus.PENDING;
+ case TreeNodeStatus.LOADED:
+ return TreeItemStatus.LOADED;
+ }
+};
+
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 553afa4..49ce472 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -41,7 +41,6 @@ import { SharedWithMePanel } from '~/views/shared-with-me-panel/shared-with-me-p
import { RunProcessPanel } from '~/views/run-process-panel/run-process-panel';
import SplitterLayout from 'react-splitter-layout';
import { WorkflowPanel } from '~/views/workflow-panel/workflow-panel';
-import { FileSelectionDialog } from '~/views-components/dialog-forms/file-selection-dialog';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
@@ -114,8 +113,6 @@ export const WorkbenchPanel =
<CreateProjectDialog />
<CurrentTokenDialog />
<FileRemoveDialog />
- <FileRemoveDialog />
- <FileSelectionDialog />
<FilesUploadCollectionDialog />
<MoveCollectionDialog />
<MoveProcessDialog />
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list