import { favoriteActionSet } from "./views-components/context-menu/action-sets/favorite-action-set";
import { collectionFilesActionSet } from './views-components/context-menu/action-sets/collection-files-action-set';
import { collectionFilesItemActionSet } from './views-components/context-menu/action-sets/collection-files-item-action-set';
+import { collectionFilesNotSelectedActionSet } from './views-components/context-menu/action-sets/collection-files-not-selected-action-set';
import { collectionActionSet } from './views-components/context-menu/action-sets/collection-action-set';
import { collectionResourceActionSet } from './views-components/context-menu/action-sets/collection-resource-action-set';
import { processActionSet } from './views-components/context-menu/action-sets/process-action-set';
addMenuActionSet(ContextMenuKind.RESOURCE, resourceActionSet);
addMenuActionSet(ContextMenuKind.FAVORITE, favoriteActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION_FILES, collectionFilesActionSet);
+addMenuActionSet(ContextMenuKind.COLLECTION_FILES_NOT_SELECTED, collectionFilesNotSelectedActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION_FILES_ITEM, collectionFilesItemActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION, collectionActionSet);
addMenuActionSet(ContextMenuKind.COLLECTION_RESOURCE, collectionResourceActionSet);
LOADED = 'LOADED',
}
+export enum TreePickerId {
+ PROJECTS = 'Projects',
+ SHARED_WITH_ME = 'Shared with me',
+ FAVORITES = 'Favorites'
+}
+
export const createTree = <T>(): Tree<T> => ({});
export const getNode = (id: string) => <T>(tree: Tree<T>): TreeNode<T> | undefined => tree[id];
);
};
+export const openCollectionFilesContextMenu = (event: React.MouseEvent<HTMLElement>) =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ const isCollectionFileSelected = JSON.stringify(getState().collectionPanelFiles).includes('"selected":true');
+ dispatch<any>(openContextMenu(event, {
+ name: '',
+ uuid: '',
+ ownerUuid: '',
+ kind: ResourceKind.COLLECTION,
+ menuKind: isCollectionFileSelected ? ContextMenuKind.COLLECTION_FILES : ContextMenuKind.COLLECTION_FILES_NOT_SELECTED
+ }));
+ };
+
export const openRootProjectContextMenu = (event: React.MouseEvent<HTMLElement>, projectUuid: string) =>
(dispatch: Dispatch, getState: () => RootState) => {
const res = getResource<UserResource>(projectUuid)(getState().resources);
SET_ROWS_PER_PAGE: ofType<{ id: string, rowsPerPage: number }>(),
TOGGLE_COLUMN: ofType<{ id: string, columnName: string }>(),
TOGGLE_SORT: ofType<{ id: string, columnName: string }>(),
- SET_SEARCH_VALUE: ofType<{ id: string, searchValue: string }>(),
+ SET_EXPLORER_SEARCH_VALUE: ofType<{ id: string, searchValue: string }>(),
});
export type DataExplorerAction = UnionOf<typeof dataExplorerActions>;
dataExplorerActions.TOGGLE_COLUMN({ ...payload, id }),
TOGGLE_SORT: (payload: { columnName: string }) =>
dataExplorerActions.TOGGLE_SORT({ ...payload, id }),
- SET_SEARCH_VALUE: (payload: { searchValue: string }) =>
- dataExplorerActions.SET_SEARCH_VALUE({ ...payload, id }),
+ SET_EXPLORER_SEARCH_VALUE: (payload: { searchValue: string }) =>
+ dataExplorerActions.SET_EXPLORER_SEARCH_VALUE({ ...payload, id }),
});
};
const next = jest.fn();
const middleware = dataExplorerMiddleware(service)(api)(next);
- middleware(dataExplorerActions.SET_SEARCH_VALUE({ id: service.getId(), searchValue: "" }));
+ middleware(dataExplorerActions.SET_EXPLORER_SEARCH_VALUE({ id: service.getId(), searchValue: "" }));
expect(api.dispatch).toHaveBeenCalledTimes(2);
});
TOGGLE_SORT: handleAction(() => {
api.dispatch(actions.REQUEST_ITEMS());
}),
- SET_SEARCH_VALUE: handleAction(() => {
+ SET_EXPLORER_SEARCH_VALUE: handleAction(() => {
api.dispatch(actions.RESET_PAGINATION());
api.dispatch(actions.REQUEST_ITEMS());
}),
SET_ROWS_PER_PAGE: ({ id, rowsPerPage }) =>
update(state, id, explorer => ({ ...explorer, rowsPerPage })),
- SET_SEARCH_VALUE: ({ id, searchValue }) =>
+ SET_EXPLORER_SEARCH_VALUE: ({ id, searchValue }) =>
update(state, id, explorer => ({ ...explorer, searchValue })),
TOGGLE_SORT: ({ id, columnName }) =>
import { Dispatch } from "redux";
import { RootState } from "~/store/store";
import { ServiceRepository } from "~/services/services";
-import { TreePickerId, receiveTreePickerData } from "~/views-components/project-tree-picker/project-tree-picker";
import { mockProjectResource } from "~/models/test-utils";
-import { treePickerActions } from "~/store/tree-picker/tree-picker-actions";
+import { treePickerActions, receiveTreePickerProjectsData } from "~/store/tree-picker/tree-picker-actions";
+import { TreePickerId } from '~/models/tree';
export const resetPickerProjectTree = () => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
dispatch<any>(treePickerActions.RESET_TREE_PICKER({pickerId: TreePickerId.PROJECTS}));
};
const getProjectsPickerTree = (uuid: string, kind: string) => {
- return receiveTreePickerData(
+ return receiveTreePickerProjectsData(
'',
[mockProjectResource({ uuid, name: kind })],
kind
// SPDX-License-Identifier: AGPL-3.0
import { unionize, ofType, UnionOf } from "~/common/unionize";
-import { TreeNode, initTreeNode, getNodeDescendants, getNodeDescendantsIds, getNodeValue, TreeNodeStatus, getNode } from '~/models/tree';
+import { TreeNode, initTreeNode, getNodeDescendants, TreeNodeStatus, getNode, TreePickerId } from '~/models/tree';
import { Dispatch } from 'redux';
import { RootState } from '~/store/store';
import { ServiceRepository } from '~/services/services';
import { FilterBuilder } from '~/services/api/filter-builder';
-import { pipe, map, values, mapValues } from 'lodash/fp';
+import { pipe, values } from 'lodash/fp';
import { ResourceKind } from '~/models/resource';
-import { GroupContentsResource } from '../../services/groups-service/groups-service';
-import { CollectionDirectory, CollectionFile } from '../../models/collection-file';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
+import { CollectionDirectory, CollectionFile } from '~/models/collection-file';
import { getTreePicker, TreePicker } from './tree-picker';
import { ProjectsTreePickerItem } from '~/views-components/projects-tree-picker/generic-projects-tree-picker';
+import { OrderBuilder } from '~/services/api/order-builder';
+import { ProjectResource } from '~/models/project';
export const treePickerActions = unionize({
LOAD_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(),
id: string;
pickerId: string;
}
+
export const receiveTreePickerData = <T>(params: ReceiveTreePickerDataParams<T>) =>
(dispatch: Dispatch) => {
const { data, extractNodeData, id, pickerId, } = params;
includeCollections?: boolean;
includeFiles?: boolean;
}
+
export const loadFavoritesProject = (params: LoadFavoritesProjectParams) =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
const { pickerId, includeCollections = false, includeFiles = false } = params;
}));
}
};
+
+export const receiveTreePickerProjectsData = (id: string, projects: ProjectResource[], pickerId: string) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
+ id,
+ nodes: projects.map(project => initTreeNode({ id: project.uuid, value: project })),
+ pickerId,
+ }));
+
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
+ };
+
+export const loadProjectTreePickerProjects = (id: string) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerId.PROJECTS }));
+
+ const ownerUuid = id.length === 0 ? services.authService.getUuid() || '' : id;
+ const { items } = await services.projectService.list(buildParams(ownerUuid));
+
+ dispatch<any>(receiveTreePickerProjectsData(id, items, TreePickerId.PROJECTS));
+ };
+
+export const loadFavoriteTreePickerProjects = (id: string) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const parentId = services.authService.getUuid() || '';
+
+ if (id === '') {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: parentId, pickerId: TreePickerId.FAVORITES }));
+ const { items } = await services.favoriteService.list(parentId);
+ dispatch<any>(receiveTreePickerProjectsData(parentId, items as ProjectResource[], TreePickerId.FAVORITES));
+ } else {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerId.FAVORITES }));
+ const { items } = await services.projectService.list(buildParams(id));
+ dispatch<any>(receiveTreePickerProjectsData(id, items, TreePickerId.FAVORITES));
+ }
+
+ };
+
+const buildParams = (ownerUuid: string) => {
+ return {
+ filters: new FilterBuilder()
+ .addEqual('ownerUuid', ownerUuid)
+ .getFilters(),
+ order: new OrderBuilder<ProjectResource>()
+ .addAsc('name')
+ .getOrder()
+ };
+};
\ No newline at end of file
import { ContextMenuKind } from "../context-menu/context-menu";
import { getNode, getNodeChildrenIds, Tree } from "~/models/tree";
import { CollectionFileType } from "~/models/collection-file";
-import { openContextMenu } from '~/store/context-menu/context-menu-actions';
+import { openContextMenu, openCollectionFilesContextMenu } from '~/store/context-menu/context-menu-actions';
import { openUploadCollectionFilesDialog } from '~/store/collections/collection-upload-actions';
import { ResourceKind } from "~/models/resource";
dispatch<any>(openContextMenu(event, { menuKind: ContextMenuKind.COLLECTION_FILES_ITEM, kind: ResourceKind.COLLECTION, name: item.data.name, uuid: item.id, ownerUuid: '' }));
},
onOptionsMenuOpen: (event) => {
- dispatch<any>(openContextMenu(event, { menuKind: ContextMenuKind.COLLECTION_FILES, kind: ResourceKind.COLLECTION, name: '', uuid: '', ownerUuid: '' }));
+ dispatch<any>(openCollectionFilesContextMenu(event));
},
});
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ContextMenuActionSet } from "~/views-components/context-menu/context-menu-action-set";
+import { collectionPanelFilesAction } from "~/store/collection-panel/collection-panel-files/collection-panel-files-actions";
+
+export const collectionFilesNotSelectedActionSet: ContextMenuActionSet = [[{
+ name: "Select all",
+ execute: dispatch => {
+ dispatch(collectionPanelFilesAction.SELECT_ALL_COLLECTION_FILES());
+ }
+}]];
\ No newline at end of file
TRASH = "Trash",
COLLECTION_FILES = "CollectionFiles",
COLLECTION_FILES_ITEM = "CollectionFilesItem",
+ COLLECTION_FILES_NOT_SELECTED = "CollectionFilesNotSelected",
COLLECTION = 'Collection',
COLLECTION_RESOURCE = 'CollectionResource',
TRASHED_COLLECTION = 'TrashedCollection',
},
onSearch: (searchValue: string) => {
- dispatch(dataExplorerActions.SET_SEARCH_VALUE({ id, searchValue }));
+ dispatch(dataExplorerActions.SET_EXPLORER_SEARCH_VALUE({ id, searchValue }));
},
onColumnToggle: (column: DataColumn<any>) => {
import { TreePicker, TreePickerProps } from "../tree-picker/tree-picker";
import { TreeItem, TreeItemStatus } from "~/components/tree/tree";
import { ProjectResource } from "~/models/project";
-import { treePickerActions } from "~/store/tree-picker/tree-picker-actions";
+import { treePickerActions, loadProjectTreePickerProjects, loadFavoriteTreePickerProjects } from "~/store/tree-picker/tree-picker-actions";
import { ListItemTextIcon } from "~/components/list-item-text-icon/list-item-text-icon";
import { ProjectIcon, FavoriteIcon, ProjectsIcon, ShareMeIcon } from "~/components/icon/icon";
import { RootState } from "~/store/store";
import { ServiceRepository } from "~/services/services";
-import { FilterBuilder } from "~/services/api/filter-builder";
import { WrappedFieldProps } from 'redux-form';
-import { initTreeNode } from '~/models/tree';
+import { TreePickerId } from '~/models/tree';
type ProjectTreePickerProps = Pick<TreePickerProps<ProjectResource>, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
return [TreePickerId.PROJECTS, TreePickerId.FAVORITES, TreePickerId.SHARED_WITH_ME].filter(nodeId => nodeId !== pickerId);
};
-export enum TreePickerId {
- PROJECTS = 'Projects',
- SHARED_WITH_ME = 'Shared with me',
- FAVORITES = 'Favorites'
-}
-
export const ProjectTreePicker = connect(undefined, mapDispatchToProps)((props: ProjectTreePickerProps) =>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Typography variant='caption' style={{ flexShrink: 0 }}>
</div>
</div>);
-
-// TODO: move action creator to store directory
-export const loadProjectTreePickerProjects = (id: string) =>
- async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerId.PROJECTS }));
-
- const ownerUuid = id.length === 0 ? services.authService.getUuid() || '' : id;
-
- const filters = new FilterBuilder()
- .addEqual('ownerUuid', ownerUuid)
- .getFilters();
-
- const { items } = await services.projectService.list({ filters });
-
- dispatch<any>(receiveTreePickerData(id, items, TreePickerId.PROJECTS));
- };
-
-export const loadFavoriteTreePickerProjects = (id: string) =>
- async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- const parentId = services.authService.getUuid() || '';
-
- if (id === '') {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: parentId, pickerId: TreePickerId.FAVORITES }));
- const { items } = await services.favoriteService.list(parentId);
-
- dispatch<any>(receiveTreePickerData(parentId, items as ProjectResource[], TreePickerId.FAVORITES));
- } else {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerId.FAVORITES }));
- const filters = new FilterBuilder()
- .addEqual('ownerUuid', id)
- .getFilters();
-
- const { items } = await services.projectService.list({ filters });
-
- dispatch<any>(receiveTreePickerData(id, items, TreePickerId.FAVORITES));
- }
-
- };
-
const getProjectPickerIcon = (item: TreeItem<ProjectResource>) => {
switch (item.data.name) {
case TreePickerId.FAVORITES:
const renderTreeItem = (item: TreeItem<ProjectResource>) =>
<ListItemTextIcon
icon={getProjectPickerIcon(item)}
- name={item.data.name}
+ name={typeof item.data === 'string' ? item.data : item.data.name}
isActive={item.active}
hasMargin={true} />;
-
-// TODO: move action creator to store directory
-export const receiveTreePickerData = (id: string, projects: ProjectResource[], pickerId: string) =>
- (dispatch: Dispatch) => {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
- id,
- nodes: projects.map(project => initTreeNode({ id: project.uuid, value: project })),
- pickerId,
- }));
-
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
- };
-
export const ProjectTreePickerField = (props: WrappedFieldProps) =>
<div style={{ height: '200px', display: 'flex', flexDirection: 'column' }}>
<ProjectTreePicker onChange={handleChange(props)} />
export const SearchBarAutocompleteView = withStyles(styles)(
({ classes, searchResults, searchValue, navigateTo, selectedItem }: SearchBarAutocompleteViewProps) => {
- console.log(searchValue, selectedItem);
return <Paper className={classes.searchView}>
<List component="nav" className={classes.list}>
<ListItem button className={classes.listItem} selected={!selectedItem || searchValue === selectedItem.id}>
backgroundColor: theme.palette.error.dark
},
info: {
- backgroundColor: theme.palette.primary.dark
+ backgroundColor: theme.palette.primary.main
},
warning: {
backgroundColor: amber[700]
menuKind: kind
}));
}
+ dispatch<any>(loadDetailsPanel(resourceUuid));
},
onDialogOpen: (ownerUuid: string) => { return; },
onItemClick: (resourceUuid: string) => {
export const ProcessInformationCard = withStyles(styles, { withTheme: true })(
({ classes, process, onContextMenu, theme }: ProcessInformationCardProps) =>
<Card className={classes.card}>
+ {console.log(process)}
<CardHeader
classes={{
content: classes.title,
menuKind
}));
}
+ this.props.dispatch<any>(loadDetailsPanel(resourceUuid));
}
handleRowDoubleClick = (uuid: string) => {
menuKind: kind
}));
}
+ dispatch<any>(loadDetailsPanel(resourceUuid));
},
onDialogOpen: (ownerUuid: string) => { return; },
onItemClick: (resourceUuid: string) => {
menuKind
}));
}
+ this.props.dispatch<any>(loadDetailsPanel(resourceUuid));
}
handleRowDoubleClick = (uuid: string) => {
menuKind: ContextMenuKind.TRASH
}));
}
+ this.props.dispatch<any>(loadDetailsPanel(resourceUuid));
}
handleRowDoubleClick = (uuid: string) => {
type WorkbenchPanelProps = WithStyles<CssRules>;
+const defaultSplitterSize = 90;
+
+const getSplitterInitialSize = () => {
+ const splitterSize = localStorage.getItem('splitterSize');
+ return splitterSize ? Number(splitterSize) : defaultSplitterSize;
+};
+
+const saveSplitterSize = (size: number) => localStorage.setItem('splitterSize', size.toString());
+
export const WorkbenchPanel =
withStyles(styles)(({ classes }: WorkbenchPanelProps) =>
<Grid container item xs className={classes.root}>
<Grid container item xs className={classes.container}>
<SplitterLayout customClassName={classes.splitter} percentage={true}
- primaryIndex={0} primaryMinSize={10} secondaryInitialSize={90} secondaryMinSize={40}>
+ primaryIndex={0} primaryMinSize={10}
+ secondaryInitialSize={getSplitterInitialSize()} secondaryMinSize={40}
+ onSecondaryPaneSizeChange={saveSplitterSize}>
<Grid container item xs component='aside' direction='column' className={classes.asidePanel}>
<SidePanel />
</Grid>
<UpdateProcessDialog />
<UpdateProjectDialog />
</Grid>
- );
+ );
\ No newline at end of file