From b79348f1be6a2dce8ceede8bd4c2906fc1098e2b Mon Sep 17 00:00:00 2001 From: Michal Klobukowski Date: Mon, 30 Jul 2018 16:11:56 +0200 Subject: [PATCH] Implement reducer and state mapping functions Feature #13855 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- .../collection-panel-files-state.ts | 4 +- .../collections-panel-files-reducer.ts | 98 +++++++++++++++++-- src/store/store.ts | 4 + .../collection-panel-files.ts | 73 ++++++-------- src/views/workbench/workbench.tsx | 2 - 5 files changed, 128 insertions(+), 53 deletions(-) diff --git a/src/store/collection-panel/collection-panel-files/collection-panel-files-state.ts b/src/store/collection-panel/collection-panel-files/collection-panel-files-state.ts index 8869fd30..953f4c01 100644 --- a/src/store/collection-panel/collection-panel-files/collection-panel-files-state.ts +++ b/src/store/collection-panel/collection-panel-files/collection-panel-files-state.ts @@ -5,10 +5,10 @@ export type CollectionPanelFilesState = Array; export interface CollectionPanelFile { - parentId: string; + parentId?: string; id: string; name: string; - size: number; + size?: number; collapsed: boolean; selected: boolean; type: string; diff --git a/src/store/collection-panel/collection-panel-files/collections-panel-files-reducer.ts b/src/store/collection-panel/collection-panel-files/collections-panel-files-reducer.ts index 8a294e78..1e62bad6 100644 --- a/src/store/collection-panel/collection-panel-files/collections-panel-files-reducer.ts +++ b/src/store/collection-panel/collection-panel-files/collections-panel-files-reducer.ts @@ -2,19 +2,103 @@ // // SPDX-License-Identifier: AGPL-3.0 -import { CollectionPanelFilesState } from "./collection-panel-files-state"; +import { CollectionPanelFilesState, CollectionPanelFile } from "./collection-panel-files-state"; import { CollectionPanelFilesAction, collectionPanelFilesAction } from "./collection-panel-files-actions"; +import { stat } from "fs"; -export const collectionPanelFilesReducer = (state: CollectionPanelFilesState = [], action: CollectionPanelFilesAction) => { +const initialState: CollectionPanelFilesState = [{ + collapsed: true, + id: 'Directory 1', + name: 'Directory 1', + selected: false, + type: 'directory', +}, { + parentId: 'Directory 1', + collapsed: true, + id: 'Directory 1.1', + name: 'Directory 1.1', + selected: false, + type: 'directory', +}, { + parentId: 'Directory 1', + collapsed: true, + id: 'File 1.1', + name: 'File 1.1', + selected: false, + type: 'file', +}, { + collapsed: true, + id: 'Directory 2', + name: 'Directory 2', + selected: false, + type: 'directory', +}, { + parentId: 'Directory 2', + collapsed: true, + id: 'Directory 2.1', + name: 'Directory 2.1', + selected: false, + type: 'directory', +}, { + parentId: 'Directory 2.1', + collapsed: true, + id: 'Directory 2.1.1', + name: 'Directory 2.1.1', + selected: false, + type: 'directory', +}, { + parentId: 'Directory 2.1.1', + collapsed: true, + id: 'Directory 2.1.1.1', + name: 'Directory 2.1.1.1', + selected: false, + type: 'directory', +}]; + +export const collectionPanelFilesReducer = (state: CollectionPanelFilesState = initialState, action: CollectionPanelFilesAction) => { return collectionPanelFilesAction.match(action, { SET_COLLECTION_FILES: data => data.files, - TOGGLE_COLLECTION_FILE_COLLAPSE: data => toggle(state, data.id, "collapsed"), - TOGGLE_COLLECTION_FILE_SELECTION: data => toggle(state, data.id, "selected"), + TOGGLE_COLLECTION_FILE_COLLAPSE: data => toggleCollapsed(state, data.id), + TOGGLE_COLLECTION_FILE_SELECTION: data => toggleSelected(state, data.id), default: () => state }); }; -const toggle = (state: CollectionPanelFilesState, id: string, key: "collapsed" | "selected") => +const toggleCollapsed = (state: CollectionPanelFilesState, id: string) => state.map(file => file.id === id - ? { ...file, [key]: !file[key] } - : file); \ No newline at end of file + ? { ...file, collapsed: !file.collapsed } + : file); + +const toggleSelected = (state: CollectionPanelFilesState, id: string) => + toggleAncestors(toggleDescendants(state, id), id); + +const toggleDescendants = (state: CollectionPanelFilesState, id: string) => { + const ids = getDescendants(state)({ id }).map(file => file.id); + if (ids.length > 0) { + const selected = !state.find(f => f.id === ids[0])!.selected; + return state.map(file => ids.some(id => file.id === id) ? { ...file, selected } : file); + } + return state; +}; + +const toggleAncestors = (state: CollectionPanelFilesState, id: string): CollectionPanelFile[] => { + const file = state.find(f => f.id === id); + if (file) { + const selected = state + .filter(f => f.parentId === file.parentId) + .every(f => f.selected); + if (!selected) { + const newState = state.map(f => f.id === file.parentId ? { ...f, selected } : f); + return toggleAncestors(newState, file.parentId || ""); + } + } + return state; +}; + +const getDescendants = (state: CollectionPanelFilesState) => ({ id }: { id: string }): CollectionPanelFile[] => { + const root = state.find(f => f.id === id); + if (root) { + return [root].concat(...state.filter(f => f.parentId === id).map(getDescendants(state))); + } else { return []; } +}; + diff --git a/src/store/store.ts b/src/store/store.ts index ae077442..33e537c0 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -18,6 +18,8 @@ import { favoritePanelMiddleware } from "./favorite-panel/favorite-panel-middlew import { reducer as formReducer } from 'redux-form'; import { FavoritesState, favoritesReducer } from './favorites/favorites-reducer'; import { snackbarReducer, SnackbarState } from './snackbar/snackbar-reducer'; +import { CollectionPanelFilesState } from './collection-panel/collection-panel-files/collection-panel-files-state'; +import { collectionPanelFilesReducer } from './collection-panel/collection-panel-files/collections-panel-files-reducer'; const composeEnhancers = (process.env.NODE_ENV === 'development' && @@ -34,6 +36,7 @@ export interface RootState { contextMenu: ContextMenuState; favorites: FavoritesState; snackbar: SnackbarState; + collectionPanelFiles: CollectionPanelFilesState; } const rootReducer = combineReducers({ @@ -47,6 +50,7 @@ const rootReducer = combineReducers({ form: formReducer, favorites: favoritesReducer, snackbar: snackbarReducer, + collectionPanelFiles: collectionPanelFilesReducer, }); diff --git a/src/views-components/collection-panel-files/collection-panel-files.ts b/src/views-components/collection-panel-files/collection-panel-files.ts index e61007a4..a95bcac3 100644 --- a/src/views-components/collection-panel-files/collection-panel-files.ts +++ b/src/views-components/collection-panel-files/collection-panel-files.ts @@ -5,51 +5,40 @@ import { connect } from "react-redux"; import { CollectionPanelFiles as Component, CollectionPanelFilesProps } from "../../components/collection-panel-files/collection-panel-files"; import { RootState } from "../../store/store"; -import { TreeItemStatus } from "../../components/tree/tree"; +import { TreeItemStatus, TreeItem } from "../../components/tree/tree"; +import { CollectionPanelFile } from "../../store/collection-panel/collection-panel-files/collection-panel-files-state"; +import { FileTreeData } from "../../components/file-tree/file-tree-data"; +import { Dispatch } from "redux"; +import { collectionPanelFilesAction } from "../../store/collection-panel/collection-panel-files/collection-panel-files-actions"; const mapStateToProps = (state: RootState): Pick => ({ - items: [{ - active: false, - data: { - name: "Directory 1", - type: "directory" - }, - id: "Directory 1", - open: true, - status: TreeItemStatus.LOADED, - items: [{ - active: false, - data: { - name: "Directory 1.1", - type: "directory" - }, - id: "Directory 1.1", - open: false, - status: TreeItemStatus.LOADED, - items: [] - }, { - active: false, - data: { - name: "File 1.1", - type: "file", - size: 20033 - }, - id: "File 1.1", - open: false, - status: TreeItemStatus.LOADED, - items: [] - }] - }, { + items: state.collectionPanelFiles + .filter(f => f.parentId === undefined) + .map(fileToTreeItem(state.collectionPanelFiles)) +}); + +const mapDispatchToProps = (dispatch: Dispatch): Pick => ({ + onCollapseToggle: (id) => dispatch(collectionPanelFilesAction.TOGGLE_COLLECTION_FILE_COLLAPSE({ id })), + onSelectionToggle: (event, item) => dispatch(collectionPanelFilesAction.TOGGLE_COLLECTION_FILE_SELECTION({id: item.id})), +}); + + +export const CollectionPanelFiles = connect(mapStateToProps, mapDispatchToProps)(Component); + +const fileToTreeItem = (files: CollectionPanelFile[]) => (file: CollectionPanelFile): TreeItem => { + return { active: false, data: { - name: "Directory 2", - type: "directory" + name: file.name, + size: file.size, + type: file.type }, - id: "Directory 2", - open: false, + id: file.id, + items: files + .filter(f => f.parentId === file.id) + .map(fileToTreeItem(files)), + open: !file.collapsed, + selected: file.selected, status: TreeItemStatus.LOADED - }] -}); - - -export const CollectionPanelFiles = connect(mapStateToProps)(Component); + }; +}; diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index 75e92659..ac9b06cc 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -215,8 +215,6 @@ export const Workbench = withStyles(styles)( } /> -- 2.30.2