Move common operations to tree model
[arvados-workbench2.git] / src / views-components / tree-picker / tree-picker.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { connect } from "react-redux";
6 import { Tree, TreeProps, TreeItem, TreeItemStatus } from "~/components/tree/tree";
7 import { RootState } from "~/store/store";
8 import { getNodeValue, getNodeChildrenIds, Tree as Ttree, createTree, getNode, TreeNodeStatus } from '~/models/tree';
9 import { Dispatch } from "redux";
10 import { initTreeNode } from '../../models/tree';
11
12 export interface TreePickerProps {
13     pickerId: string;
14     onContextMenu: (event: React.MouseEvent<HTMLElement>, nodeId: string, pickerId: string) => void;
15     toggleItemOpen: (nodeId: string, status: TreeItemStatus, pickerId: string) => void;
16     toggleItemActive: (nodeId: string, status: TreeItemStatus, pickerId: string) => void;
17     toggleItemSelection: (nodeId: string, pickerId: string) => void;
18 }
19
20 const memoizedMapStateToProps = () => {
21     let prevTree: Ttree<any>;
22     let mappedProps: Pick<TreeProps<any>, 'items'>;
23     return (state: RootState, props: TreePickerProps): Pick<TreeProps<any>, 'items'> => {
24         const tree = state.treePicker[props.pickerId] || createTree();
25         if (tree !== prevTree) {
26             prevTree = tree;
27             mappedProps = {
28                 items: getNodeChildrenIds('')(tree)
29                     .map(treePickerToTreeItems(tree))
30             };
31         }
32         return mappedProps;
33     };
34 };
35
36 const mapDispatchToProps = (dispatch: Dispatch, props: TreePickerProps): Pick<TreeProps<any>, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive' | 'toggleItemSelection'> => ({
37     onContextMenu: (event, item) => props.onContextMenu(event, item.id, props.pickerId),
38     toggleItemActive: (id, status) => props.toggleItemActive(id, status, props.pickerId),
39     toggleItemOpen: (id, status) => props.toggleItemOpen(id, status, props.pickerId),
40     toggleItemSelection: (_, item) => props.toggleItemSelection(item.id, props.pickerId),
41 });
42
43 export const TreePicker = connect(memoizedMapStateToProps(), mapDispatchToProps)(Tree);
44
45 const treePickerToTreeItems = (tree: Ttree<any>) =>
46     (id: string): TreeItem<any> => {
47         const node = getNode(id)(tree) || initTreeNode({ id: '', value: 'InvalidNode' });
48         const items = getNodeChildrenIds(node.id)(tree)
49             .map(treePickerToTreeItems(tree));
50         return {
51             active: node.active,
52             data: node.value,
53             id: node.id,
54             items: items.length > 0 ? items : undefined,
55             open: node.expanded,
56             selected: node.selected,
57             status: treeNodeStatusToTreeItem(node.status),
58         };
59     };
60
61 export const treeNodeStatusToTreeItem = (status: TreeNodeStatus) => {
62     switch (status) {
63         case TreeNodeStatus.INITIAL:
64             return TreeItemStatus.INITIAL;
65         case TreeNodeStatus.PENDING:
66             return TreeItemStatus.PENDING;
67         case TreeNodeStatus.LOADED:
68             return TreeItemStatus.LOADED;
69     }
70 };
71