Merge branch 'master' into 14452-my-account
[arvados-workbench2.git] / src / components / data-table-filters / data-table-filters-tree.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from "react";
6 import { Tree, toggleNodeSelection, getNode, initTreeNode, getNodeChildrenIds } from '~/models/tree';
7 import { Tree as TreeComponent, TreeItem, TreeItemStatus } from '~/components/tree/tree';
8 import { noop, map } from "lodash/fp";
9 import { toggleNodeCollapse } from '~/models/tree';
10 import { countNodes, countChildren } from '~/models/tree';
11
12 export interface DataTableFilterItem {
13     name: string;
14 }
15
16 export type DataTableFilters = Tree<DataTableFilterItem>;
17
18 export interface DataTableFilterProps {
19     filters: DataTableFilters;
20     onChange?: (filters: DataTableFilters) => void;
21 }
22
23 export class DataTableFiltersTree extends React.Component<DataTableFilterProps> {
24
25     render() {
26         const { filters } = this.props;
27         const hasSubfilters = countNodes(filters) !== countChildren('')(filters);
28         return <TreeComponent
29             levelIndentation={hasSubfilters ? 20 : 0}
30             itemRightPadding={20}
31             items={filtersToTree(filters)}
32             render={renderItem}
33             showSelection
34             disableRipple
35             onContextMenu={noop}
36             toggleItemActive={noop}
37             toggleItemOpen={this.toggleOpen}
38             toggleItemSelection={this.toggleFilter}
39         />;
40     }
41
42     toggleFilter = (_: React.MouseEvent, item: TreeItem<DataTableFilterItem>) => {
43         const { onChange = noop } = this.props;
44         onChange(toggleNodeSelection(item.id)(this.props.filters));
45     }
46
47     toggleOpen = (_: React.MouseEvent, item: TreeItem<DataTableFilterItem>) => {
48         const { onChange = noop } = this.props;
49         onChange(toggleNodeCollapse(item.id)(this.props.filters));
50     }
51 }
52
53 const renderItem = (item: TreeItem<DataTableFilterItem>) =>
54     <span>{item.data.name}</span>;
55
56 const filterToTreeItem = (filters: DataTableFilters) =>
57     (id: string): TreeItem<any> => {
58         const node = getNode(id)(filters) || initTreeNode({ id: '', value: 'InvalidNode' });
59         const items = getNodeChildrenIds(node.id)(filters)
60             .map(filterToTreeItem(filters));
61
62         return {
63             active: node.active,
64             data: node.value,
65             id: node.id,
66             items: items.length > 0 ? items : undefined,
67             open: node.expanded,
68             selected: node.selected,
69             status: TreeItemStatus.LOADED,
70         };
71     };
72
73 const filtersToTree = (filters: DataTableFilters): TreeItem<DataTableFilterItem>[] =>
74     map(filterToTreeItem(filters), getNodeChildrenIds('')(filters));