X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/95b86eb6b3184b787b570a906347ccaac32195c6..HEAD:/src/models/tree.ts?ds=sidebyside diff --git a/src/models/tree.ts b/src/models/tree.ts index bec2f758..aeb41541 100644 --- a/src/models/tree.ts +++ b/src/models/tree.ts @@ -14,6 +14,7 @@ export interface TreeNode { parent: string; active: boolean; selected: boolean; + initialState?: boolean; expanded: boolean; status: TreeNodeStatus; } @@ -27,7 +28,8 @@ export enum TreeNodeStatus { export enum TreePickerId { PROJECTS = 'Projects', SHARED_WITH_ME = 'Shared with me', - FAVORITES = 'Favorites' + FAVORITES = 'Favorites', + PUBLIC_FAVORITES = 'Public Favorites' } export const createTree = (): Tree => ({}); @@ -42,12 +44,13 @@ export const appendSubtree = (id: string, subtree: Tree) => (tree: Tree )(subtree) as Tree; export const setNode = (node: TreeNode) => (tree: Tree): Tree => { - return pipe( - (tree: Tree) => getNode(node.id)(tree) === node - ? tree - : { ...tree, [node.id]: node }, - addChild(node.parent, node.id) - )(tree); + if (tree[node.id] && tree[node.id] === node) { return tree; } + + tree[node.id] = node; + if (tree[node.parent]) { + tree[node.parent].children = Array.from(new Set([...tree[node.parent].children, node.id])); + } + return tree; }; export const getNodeValue = (id: string) => (tree: Tree) => { @@ -72,6 +75,7 @@ export const setNodeValueWith = (mapFn: (value: T) => T) => (id: string) => ( export const mapTreeValues = (mapFn: (value: T) => R) => (tree: Tree): Tree => getNodeDescendantsIds('')(tree) .map(id => getNode(id)(tree)) + .filter(node => !!node) .map(mapNodeValue(mapFn)) .reduce((newTree, node) => setNode(node)(newTree), createTree()); @@ -134,6 +138,11 @@ export const deactivateNode = (tree: Tree) => export const expandNode = (...ids: string[]) => (tree: Tree) => mapTree((node: TreeNode) => ids.some(id => id === node.id) ? { ...node, expanded: true } : node)(tree); +export const expandNodeAncestors = (...ids: string[]) => (tree: Tree) => { + const ancestors = ids.reduce((acc, id): string[] => ([...acc, ...getNodeAncestorsIds(id)(tree)]), [] as string[]); + return mapTree((node: TreeNode) => ancestors.some(id => id === node.id) ? { ...node, expanded: true } : node)(tree); +} + export const collapseNode = (...ids: string[]) => (tree: Tree) => mapTree((node: TreeNode) => ids.some(id => id === node.id) ? { ...node, expanded: false } : node)(tree); @@ -147,38 +156,40 @@ export const setNodeStatus = (id: string) => (status: TreeNodeStatus) => (tre : tree; }; -export const toggleNodeSelection = (id: string) => (tree: Tree) => { +export const toggleNodeSelection = (id: string, cascade: boolean) => (tree: Tree) => { const node = getNode(id)(tree); + return node - ? pipe( - setNode({ ...node, selected: !node.selected }), - toggleAncestorsSelection(id), - toggleDescendantsSelection(id))(tree) + ? cascade + ? pipe( + setNode({ ...node, selected: !node.selected }), + toggleAncestorsSelection(id), + toggleDescendantsSelection(id))(tree) + : setNode({ ...node, selected: !node.selected })(tree) : tree; - }; -export const selectNode = (id: string) => (tree: Tree) => { +export const selectNode = (id: string, cascade: boolean) => (tree: Tree) => { const node = getNode(id)(tree); return node && node.selected ? tree - : toggleNodeSelection(id)(tree); + : toggleNodeSelection(id, cascade)(tree); }; -export const selectNodes = (id: string | string[]) => (tree: Tree) => { +export const selectNodes = (id: string | string[], cascade: boolean) => (tree: Tree) => { const ids = typeof id === 'string' ? [id] : id; - return ids.reduce((tree, id) => selectNode(id)(tree), tree); + return ids.reduce((tree, id) => selectNode(id, cascade)(tree), tree); }; -export const deselectNode = (id: string) => (tree: Tree) => { +export const deselectNode = (id: string, cascade: boolean) => (tree: Tree) => { const node = getNode(id)(tree); return node && node.selected - ? toggleNodeSelection(id)(tree) + ? toggleNodeSelection(id, cascade)(tree) : tree; }; -export const deselectNodes = (id: string | string[]) => (tree: Tree) => { +export const deselectNodes = (id: string | string[], cascade: boolean) => (tree: Tree) => { const ids = typeof id === 'string' ? [id] : id; - return ids.reduce((tree, id) => deselectNode(id)(tree), tree); + return ids.reduce((tree, id) => deselectNode(id, cascade)(tree), tree); }; export const getSelectedNodes = (tree: Tree) => @@ -195,6 +206,19 @@ export const initTreeNode = (data: Pick, 'id' | 'value'> & { pare ...data, }); +export const getTreeDirty = (id: string) => (tree: Tree): boolean => { + const node = getNode(id)(tree); + const children = getNodeDescendants(id)(tree); + return (node + && node.initialState !== undefined + && node.selected !== node.initialState + ) + || children.some(child => + child.initialState !== undefined + && child.selected !== child.initialState + ); +} + const toggleDescendantsSelection = (id: string) => (tree: Tree) => { const node = getNode(id)(tree); if (node) { @@ -226,7 +250,6 @@ const toggleParentNodeSelection = (id: string) => (tree: Tree) => { return tree; }; - const mapNodeValue = (mapFn: (value: T) => R) => (node: TreeNode): TreeNode => ({ ...node, value: mapFn(node.value) }); @@ -234,20 +257,3 @@ const getRootNodeChildrenIds = (tree: Tree) => Object .keys(tree) .filter(id => getNode(id)(tree)!.parent === TREE_ROOT_ID); - - -const addChild = (parentId: string, childId: string) => (tree: Tree): Tree => { - const node = getNode(parentId)(tree); - if (node) { - const children = node.children.some(id => id === childId) - ? node.children - : [...node.children, childId]; - - const newNode = children === node.children - ? node - : { ...node, children }; - - return setNode(newNode)(tree); - } - return tree; -};