Extract tree data structure
[arvados.git] / src / store / collection-panel / collection-panel-files / collections-panel-files-reducer.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { CollectionPanelFilesState, CollectionPanelFile, CollectionPanelDirectory, mapCollectionFileToCollectionPanelFile } from "./collection-panel-files-state";
6 import { CollectionPanelFilesAction, collectionPanelFilesAction } from "./collection-panel-files-actions";
7 import { createTree, mapTree, TreeNode, mapNodes, getNode, setNode, getNodeAncestors, getNodeDescendants, mapNodeValue } from "../../../models/tree";
8 import { CollectionFileType } from "../../../models/collection-file";
9
10 export const collectionPanelFilesReducer = (state: CollectionPanelFilesState = createTree(), action: CollectionPanelFilesAction) => {
11     return collectionPanelFilesAction.match(action, {
12         SET_COLLECTION_FILES: ({ files }) =>
13             mapTree(mapCollectionFileToCollectionPanelFile)(files),
14
15         TOGGLE_COLLECTION_FILE_COLLAPSE: data =>
16             toggleCollapse(data.id)(state),
17
18         TOGGLE_COLLECTION_FILE_SELECTION: data => [state]
19             .map(toggleSelected(data.id))
20             .map(toggleAncestors(data.id))
21             .map(toggleDescendants(data.id))[0],
22
23         SELECT_ALL_COLLECTION_FILES: () =>
24             mapTree(mapNodeValue(v => ({ ...v, selected: true })))(state),
25
26         UNSELECT_ALL_COLLECTION_FILES: () =>
27             mapTree(mapNodeValue(v => ({ ...v, selected: false })))(state),
28             
29         default: () => state
30     });
31 };
32
33 const toggleCollapse = (id: string) => (tree: CollectionPanelFilesState) =>
34     mapNodes
35         ([id])
36         (mapNodeValue((v: CollectionPanelDirectory | CollectionPanelFile) =>
37             v.type === CollectionFileType.DIRECTORY
38                 ? { ...v, collapsed: !v.collapsed }
39                 : v))
40         (tree);
41
42 const toggleSelected = (id: string) => (tree: CollectionPanelFilesState) =>
43     mapNodes
44         ([id])
45         (mapNodeValue((v: CollectionPanelDirectory | CollectionPanelFile) => ({ ...v, selected: !v.selected })))
46         (tree);
47
48
49 const toggleDescendants = (id: string) => (tree: CollectionPanelFilesState) => {
50     const node = getNode(id)(tree);
51     if (node && node.value.type === CollectionFileType.DIRECTORY) {
52         return mapNodes(getNodeDescendants(id)(tree))(mapNodeValue(v => ({ ...v, selected: node.value.selected })))(tree);
53     }
54     return tree;
55 };
56
57 const toggleAncestors = (id: string) => (tree: CollectionPanelFilesState) => {
58     const ancestors = getNodeAncestors(id)(tree)
59         .map(id => getNode(id)(tree))
60         .reverse();
61     return ancestors.reduce((newTree, parent) => parent !== undefined ? toggleParentNode(parent)(newTree) : newTree, tree);
62 };
63
64 const toggleParentNode = (node: TreeNode<CollectionPanelDirectory | CollectionPanelFile>) => (tree: CollectionPanelFilesState) => {
65     const parentNode = getNode(node.id)(tree);
66     if (parentNode) {
67         const selected = parentNode.children
68             .map(id => getNode(id)(tree))
69             .every(node => node !== undefined && node.value.selected);
70         return setNode(mapNodeValue(v => ({ ...v, selected }))(parentNode))(tree);
71     }
72     return setNode(node)(tree);
73 };
74
75