X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/6ff11e5f5dba8e02a176bbe9455ba916e8990028..3e3eaa213219ebcac9b52c8fbe3ec3ef7c39c863:/src/views-components/tree-picker/tree-picker.ts diff --git a/src/views-components/tree-picker/tree-picker.ts b/src/views-components/tree-picker/tree-picker.ts index b90f2e42..86c76e08 100644 --- a/src/views-components/tree-picker/tree-picker.ts +++ b/src/views-components/tree-picker/tree-picker.ts @@ -3,46 +3,95 @@ // 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/tree-picker/tree-picker"; -import { getNodeValue, getNodeChildrenIds, Tree as Ttree, createTree } 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'; -export interface TreePickerProps { +type Callback = (event: React.MouseEvent, item: TreeItem, pickerId: string) => void; +export interface TreePickerProps { pickerId: string; - toggleItemOpen: (nodeId: string, status: TreeItemStatus, pickerId: string) => void; - toggleItemActive: (nodeId: string, status: TreeItemStatus, pickerId: string) => void; + onContextMenu: Callback; + toggleItemOpen: Callback; + toggleItemActive: Callback; + toggleItemSelection: Callback; } -const mapStateToProps = (state: RootState, props: TreePickerProps): Pick, 'items'> => { - const tree = state.treePicker[props.pickerId] || createTree(); - return { - items: getNodeChildrenIds('')(tree) - .map(treePickerToTreeItems(tree)) +const flatTree = (itemsIdMap: Map, depth: number, items?: any): [] => { + return items ? items + .map((item: any) => addToItemsIdMap(item, itemsIdMap)) + .reduce((prev: Array, next: any) => { + const { items } = next; + prev.push({ ...next, depth }); + prev.push(...(next.open ? flatTree(itemsIdMap, depth + 1, items) : [])); + return prev; + }, []) : []; +}; + +const addToItemsIdMap = (item: TreeItem, itemsIdMap: Map>) => { + itemsIdMap[item.id] = item; + return item; +}; + +const memoizedMapStateToProps = () => { + let prevTree: Ttree; + let mappedProps: Pick, 'items' | 'disableRipple' | 'itemsMap'>; + return (state: RootState, props: TreePickerProps): Pick, 'items' | 'disableRipple' | 'itemsMap'> => { + const itemsIdMap: Map> = new Map(); + const tree = state.treePicker[props.pickerId] || createTree(); + if (tree !== prevTree) { + prevTree = tree; + mappedProps = { + disableRipple: true, + items: getNodeChildrenIds('')(tree) + .map(treePickerToTreeItems(tree)) + .map(item => addToItemsIdMap(item, itemsIdMap)) + .map(parentItem => ({ + ...parentItem, + flatTree: true, + items: flatTree(itemsIdMap, 2, parentItem.items || []), + })), + itemsMap: itemsIdMap, + }; + } + return mappedProps; }; }; -const mapDispatchToProps = (dispatch: Dispatch, props: TreePickerProps): Pick, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive'> => ({ - onContextMenu: () => { return; }, - toggleItemActive: (id, status) => props.toggleItemActive(id, status, props.pickerId), - toggleItemOpen: (id, status) => props.toggleItemOpen(id, status, props.pickerId) +const mapDispatchToProps = (_: Dispatch, props: TreePickerProps): Pick, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive' | 'toggleItemSelection'> => ({ + onContextMenu: (event, item) => props.onContextMenu(event, item, props.pickerId), + toggleItemActive: (event, item) => props.toggleItemActive(event, item, props.pickerId), + toggleItemOpen: (event, item) => props.toggleItemOpen(event, item, props.pickerId), + toggleItemSelection: (event, item) => props.toggleItemSelection(event, item, props.pickerId), }); -export const TreePicker = connect(mapStateToProps, mapDispatchToProps)(Tree); +export const TreePicker = connect(memoizedMapStateToProps(), mapDispatchToProps)(Tree); -const treePickerToTreeItems = (tree: Ttree) => +const treePickerToTreeItems = (tree: Ttree) => (id: string): TreeItem => { - 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; + } +}; +