Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / views-components / tree-picker / tree-picker.ts
index cb829059a5889f5e2872b9a95b602fa03c429cf9..a6fdfefec9d21d013f72a6e87ffe3ecbc6ca83fe 100644 (file)
@@ -3,11 +3,12 @@
 // 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 { getNodeChildrenIds, Tree as Ttree, createTree, getNode, TreeNodeStatus } from '~/models/tree';
+import { Tree, TreeProps, TreeItem, TreeItemStatus } from "components/tree/tree";
+import { RootState } from "store/store";
+import { getNodeChildrenIds, Tree as Ttree, createTree, getNode, TreeNodeStatus } from 'models/tree';
 import { Dispatch } from "redux";
 import { initTreeNode } from '../../models/tree';
+import { ResourcesState } from "store/resources/resources";
 
 type Callback<T> = (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>, pickerId: string) => void;
 export interface TreePickerProps<T> {
@@ -18,22 +19,39 @@ export interface TreePickerProps<T> {
     toggleItemSelection: Callback<T>;
 }
 
-const memoizedMapStateToProps = () => {
-    let prevTree: Ttree<any>;
-    let mappedProps: Pick<TreeProps<any>, 'items' | 'disableRipple'>;
-    return <T>(state: RootState, props: TreePickerProps<T>): Pick<TreeProps<T>, 'items' | 'disableRipple'> => {
+const flatTree = (itemsIdMap: Map<string, any>, depth: number, items?: any): [] => {
+    return items ? items
+        .map((item: any) => addToItemsIdMap(item, itemsIdMap))
+        .reduce((prev: Array<any>, next: any) => {
+            const { items } = next;
+            prev.push({ ...next, depth });
+            prev.push(...(next.open ? flatTree(itemsIdMap, depth + 1, items) : []));
+            return prev;
+        }, []) : [];
+};
+
+const addToItemsIdMap = <T>(item: TreeItem<T>, itemsIdMap: Map<string, TreeItem<T>>) => {
+    itemsIdMap[item.id] = item;
+    return item;
+};
+
+const mapStateToProps =
+    <T>(state: RootState, props: TreePickerProps<T>): Pick<TreeProps<T>, 'items' | 'disableRipple' | 'itemsMap'> => {
+        const itemsIdMap: Map<string, TreeItem<T>> = new Map();
         const tree = state.treePicker[props.pickerId] || createTree();
-        if (tree !== prevTree) {
-            prevTree = tree;
-            mappedProps = {
-                disableRipple: true,
-                items: getNodeChildrenIds('')(tree)
-                    .map(treePickerToTreeItems(tree))
-            };
-        }
-        return mappedProps;
+        return {
+            disableRipple: true,
+            items: getNodeChildrenIds('')(tree)
+                .map(treePickerToTreeItems(tree, state.resources))
+                .map(item => addToItemsIdMap(item, itemsIdMap))
+                .map(parentItem => ({
+                    ...parentItem,
+                    flatTree: true,
+                    items: flatTree(itemsIdMap, 2, parentItem.items || []),
+                })),
+            itemsMap: itemsIdMap,
+        };
     };
-};
 
 const mapDispatchToProps = (_: Dispatch, props: TreePickerProps<any>): Pick<TreeProps<any>, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive' | 'toggleItemSelection'> => ({
     onContextMenu: (event, item) => props.onContextMenu(event, item, props.pickerId),
@@ -42,16 +60,17 @@ const mapDispatchToProps = (_: Dispatch, props: TreePickerProps<any>): Pick<Tree
     toggleItemSelection: (event, item) => props.toggleItemSelection(event, item, props.pickerId),
 });
 
-export const TreePicker = connect(memoizedMapStateToProps(), mapDispatchToProps)(Tree);
+export const TreePicker = connect(mapStateToProps, mapDispatchToProps)(Tree);
 
-const treePickerToTreeItems = (tree: Ttree<any>) =>
+const treePickerToTreeItems = (tree: Ttree<any>, resources: ResourcesState) =>
     (id: string): TreeItem<any> => {
         const node = getNode(id)(tree) || initTreeNode({ id: '', value: 'InvalidNode' });
         const items = getNodeChildrenIds(node.id)(tree)
-            .map(treePickerToTreeItems(tree));
+            .map(treePickerToTreeItems(tree, resources));
+        const resource = resources[node.id];
         return {
             active: node.active,
-            data: node.value,
+            data: resource ? { ...resource, name: node.value.name || node.value } : undefined || node.value,
             id: node.id,
             items: items.length > 0 ? items : undefined,
             open: node.expanded,