import { ContainerRequestState } from '~/models/container-request';
import { MountKind } from '~/models/mount-types';
import { receiveTreePickerData, loadUserProject } from '~/store/tree-picker/tree-picker-actions';
-import { loadProject, loadCollection, initUserProject, initSharedProject, initFavoritesProject } from './store/tree-picker/tree-picker-actions';
+import { loadProject, loadCollection, initUserProject, initSharedProject, initFavoritesProject, initProjectsTreePicker } from './store/tree-picker/tree-picker-actions';
import { ResourceKind } from '~/models/resource';
const getBuildNumber = () => "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev");
await store.dispatch(loadWorkbench());
addRouteChangeHandlers(history, store);
// createEnumCollectorWorkflow(services);
- store.dispatch(initUserProject('testPicker1'));
- store.dispatch(initUserProject('testPicker2'));
- store.dispatch(initUserProject('testPicker3'));
- store.dispatch(initSharedProject('testPicker4'));
- store.dispatch(initSharedProject('testPicker5'));
- store.dispatch(initSharedProject('testPicker6'));
- store.dispatch(initFavoritesProject('testPicker7'));
- store.dispatch(initFavoritesProject('testPicker8'));
- store.dispatch(initFavoritesProject('testPicker9'));
+ store.dispatch(initProjectsTreePicker('testPicker1'));
+ store.dispatch(initProjectsTreePicker('testPicker2'));
+ store.dispatch(initProjectsTreePicker('testPicker3'));
+
// await store.dispatch(loadCollection(
// 'c97qk-4zz18-9sn8ygaf62chkkd',
// 'testPicker',
export const activateNode = (id: string) => <T>(tree: Tree<T>) =>
mapTree(node => node.id === id ? { ...node, active: true } : { ...node, active: false })(tree);
+export const deactivateNode = <T>(tree: Tree<T>) =>
+ mapTree(node => node.active ? { ...node, active: false } : node)(tree);
export const expandNode = (...ids: string[]) => <T>(tree: Tree<T>) =>
mapTree(node => ids.some(id => id === node.id) ? { ...node, expanded: true } : node)(tree);
LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ id: string, nodes: Array<TreeNode<any>>, pickerId: string }>(),
TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ id: string, pickerId: string }>(),
ACTIVATE_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(),
+ DEACTIVATE_TREE_PICKER_NODE: ofType<{ pickerId: string }>(),
TOGGLE_TREE_PICKER_NODE_SELECTION: ofType<{ id: string, pickerId: string }>(),
EXPAND_TREE_PICKER_NODES: ofType<{ ids: string[], pickerId: string }>(),
RESET_TREE_PICKER: ofType<{ pickerId: string }>()
export type TreePickerAction = UnionOf<typeof treePickerActions>;
+export const getProjectsTreePickerIds = (pickerId: string) => ({
+ home: `${pickerId}_home`,
+ shared: `${pickerId}_shared`,
+ favorites: `${pickerId}_favorites`,
+});
+export const initProjectsTreePicker = (pickerId: string) =>
+async (dispatch: Dispatch, _: () => RootState, services: ServiceRepository) => {
+ const {home, shared, favorites} = getProjectsTreePickerIds(pickerId);
+ dispatch<any>(initUserProject(home));
+ dispatch<any>(initSharedProject(shared));
+ dispatch<any>(initFavoritesProject(favorites));
+};
+
interface ReceiveTreePickerDataParams<T> {
data: T[];
extractNodeData: (value: T) => { id: string, value: T, status?: TreeNodeStatus };
//
// SPDX-License-Identifier: AGPL-3.0
-import { createTree, TreeNode, setNode, Tree, TreeNodeStatus, setNodeStatus, expandNode } from '~/models/tree';
+import { createTree, TreeNode, setNode, Tree, TreeNodeStatus, setNodeStatus, expandNode, deactivateNode } from '~/models/tree';
import { TreePicker } from "./tree-picker";
import { treePickerActions, TreePickerAction } from "./tree-picker-actions";
import { compose } from "redux";
updateOrCreatePicker(state, pickerId, toggleNodeCollapse(id)),
ACTIVATE_TREE_PICKER_NODE: ({ id, pickerId }) =>
updateOrCreatePicker(state, pickerId, activateNode(id)),
+ DEACTIVATE_TREE_PICKER_NODE: ({ pickerId }) =>
+ updateOrCreatePicker(state, pickerId, deactivateNode),
TOGGLE_TREE_PICKER_NODE_SELECTION: ({ id, pickerId }) =>
updateOrCreatePicker(state, pickerId, toggleNodeSelection(id)),
RESET_TREE_PICKER: ({ pickerId }) =>
// SPDX-License-Identifier: AGPL-3.0
import { connect } from 'react-redux';
-import { ProjectsTreePicker, ProjectsTreePickerProps } from '~/views-components/projects-tree-picker/projects-tree-picker';
+import { ProjectsTreePicker, ProjectsTreePickerProps } from '~/views-components/projects-tree-picker/generic-projects-tree-picker';
import { Dispatch } from 'redux';
import { FavoriteIcon } from '~/components/icon/icon';
import { loadFavoritesProject } from '~/store/tree-picker/tree-picker-actions';
-export const FavoritesProjectsTreePicker = connect(() => ({
+export const FavoritesTreePicker = connect(() => ({
rootItemIcon: FavoriteIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
loadRootItem: (_, pickerId, includeCollections, includeFiles) => {
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { Dispatch } from "redux";
+import { connect } from "react-redux";
+import { TreeItem, TreeItemStatus } from '~/components/tree/tree';
+import { ProjectResource } from "~/models/project";
+import { treePickerActions } from "~/store/tree-picker/tree-picker-actions";
+import { ListItemTextIcon } from "~/components/list-item-text-icon/list-item-text-icon";
+import { ProjectIcon, InputIcon, IconType, CollectionIcon } from '~/components/icon/icon';
+import { loadProject, loadCollection } from '~/store/tree-picker/tree-picker-actions';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
+import { CollectionDirectory, CollectionFile, CollectionFileType } from '~/models/collection-file';
+import { ResourceKind } from '~/models/resource';
+import { TreePickerProps, TreePicker } from "~/views-components/tree-picker/tree-picker";
+
+export interface ProjectsTreePickerRootItem {
+ id: string;
+ name: string;
+}
+
+export type ProjectsTreePickerItem = ProjectsTreePickerRootItem | GroupContentsResource | CollectionDirectory | CollectionFile;
+type PickedTreePickerProps = Pick<TreePickerProps<ProjectsTreePickerItem>, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
+
+export interface ProjectsTreePickerDataProps {
+ pickerId: string;
+ includeCollections?: boolean;
+ includeFiles?: boolean;
+ rootItemIcon: IconType;
+ loadRootItem: (item: TreeItem<ProjectsTreePickerRootItem>, pickerId: string, includeCollections?: boolean, inlcudeFiles?: boolean) => void;
+}
+
+export type ProjectsTreePickerProps = ProjectsTreePickerDataProps & Partial<PickedTreePickerProps>;
+
+const mapStateToProps = (_: any, { pickerId, rootItemIcon }: ProjectsTreePickerProps) => ({
+ render: renderTreeItem(rootItemIcon),
+ pickerId,
+});
+
+const mapDispatchToProps = (dispatch: Dispatch, { loadRootItem, includeCollections, includeFiles, ...props }: ProjectsTreePickerProps): PickedTreePickerProps => ({
+ onContextMenu: () => { return; },
+ toggleItemActive: (event, item, pickerId) => {
+ dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id: item.id, pickerId }));
+ if (props.toggleItemActive) {
+ props.toggleItemActive(event, item, pickerId);
+ }
+ },
+ toggleItemOpen: (_, item, pickerId) => {
+ const { id, data, status } = item;
+ if (status === TreeItemStatus.INITIAL) {
+ if ('kind' in data) {
+ dispatch<any>(
+ data.kind === ResourceKind.COLLECTION
+ ? loadCollection(id, pickerId)
+ : loadProject({ id, pickerId, includeCollections, includeFiles })
+ );
+ } else if (!('type' in data) && loadRootItem) {
+ loadRootItem(item as TreeItem<ProjectsTreePickerRootItem>, pickerId, includeCollections, includeFiles);
+ }
+ } else if (status === TreeItemStatus.LOADED) {
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
+ }
+ },
+ toggleItemSelection: (_, { id }, pickerId) => {
+ dispatch<any>(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECTION({ id, pickerId }));
+ },
+});
+
+export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(TreePicker);
+
+const getProjectPickerIcon = ({ data }: TreeItem<ProjectsTreePickerItem>, rootIcon: IconType): IconType => {
+ if ('kind' in data) {
+ switch (data.kind) {
+ case ResourceKind.COLLECTION:
+ return CollectionIcon;
+ default:
+ return ProjectIcon;
+ }
+ } else if ('type' in data) {
+ switch (data.type) {
+ case CollectionFileType.FILE:
+ return InputIcon;
+ default:
+ return ProjectIcon;
+ }
+ } else {
+ return rootIcon;
+ }
+};
+
+const renderTreeItem = (rootItemIcon: IconType) => (item: TreeItem<ProjectResource>) =>
+ <ListItemTextIcon
+ icon={getProjectPickerIcon(item, rootItemIcon)}
+ name={item.data.name}
+ isActive={item.active}
+ hasMargin={true} />;
// SPDX-License-Identifier: AGPL-3.0
import { connect } from 'react-redux';
-import { ProjectsTreePicker, ProjectsTreePickerProps } from '~/views-components/projects-tree-picker/projects-tree-picker';
+import { ProjectsTreePicker, ProjectsTreePickerProps } from '~/views-components/projects-tree-picker/generic-projects-tree-picker';
import { Dispatch } from 'redux';
import { loadUserProject } from '~/store/tree-picker/tree-picker-actions';
import { ProjectIcon } from '~/components/icon/icon';
-export const UserProjectsTreePicker = connect(() => ({
+export const HomeTreePicker = connect(() => ({
rootItemIcon: ProjectIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
loadRootItem: (_, pickerId, includeCollections, includeFiles) => {
//
// SPDX-License-Identifier: AGPL-3.0
-import * as React from "react";
-import { Dispatch } from "redux";
-import { connect } from "react-redux";
-import { TreeItem, TreeItemStatus } from '~/components/tree/tree';
-import { ProjectResource } from "~/models/project";
-import { treePickerActions } from "~/store/tree-picker/tree-picker-actions";
-import { ListItemTextIcon } from "~/components/list-item-text-icon/list-item-text-icon";
-import { ProjectIcon, InputIcon, IconType, CollectionIcon } from '~/components/icon/icon';
-import { loadProject, loadCollection } from '~/store/tree-picker/tree-picker-actions';
-import { GroupContentsResource } from '~/services/groups-service/groups-service';
-import { CollectionDirectory, CollectionFile, CollectionFileType } from '~/models/collection-file';
-import { ResourceKind } from '~/models/resource';
-import { TreePickerProps, TreePicker } from "~/views-components/tree-picker/tree-picker";
-
-export interface ProjectsTreePickerRootItem {
- id: string;
- name: string;
-}
-
-type ProjectsTreePickerItem = ProjectsTreePickerRootItem | GroupContentsResource | CollectionDirectory | CollectionFile;
-type PickedTreePickerProps = Pick<TreePickerProps<ProjectsTreePickerItem>, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
-
-export interface ProjectsTreePickerDataProps {
+import * as React from 'react';
+import { Dispatch } from 'redux';
+import { connect } from 'react-redux';
+import { HomeTreePicker } from '~/views-components/projects-tree-picker/home-tree-picker';
+import { SharedTreePicker } from '~/views-components/projects-tree-picker/shared-tree-picker';
+import { FavoritesTreePicker } from '~/views-components/projects-tree-picker/favorites-tree-picker';
+import { getProjectsTreePickerIds, treePickerActions } from '~/store/tree-picker/tree-picker-actions';
+import { TreeItem } from '~/components/tree/tree';
+import { ProjectsTreePickerItem } from './generic-projects-tree-picker';
+
+export interface ProjectsTreePickerProps {
pickerId: string;
includeCollections?: boolean;
includeFiles?: boolean;
- rootItemIcon: IconType;
- loadRootItem: (item: TreeItem<ProjectsTreePickerRootItem>, pickerId: string, includeCollections?: boolean, inlcudeFiles?: boolean) => void;
-}
-
-export interface ProjectsTreePickerActionProps {
+ toggleItemActive?: (event: React.MouseEvent<HTMLElement>, item: TreeItem<ProjectsTreePickerItem>, pickerId: string) => void;
}
-export type ProjectsTreePickerProps = ProjectsTreePickerDataProps & ProjectsTreePickerActionProps;
-
-const mapStateToProps = (_: any, { pickerId, rootItemIcon }: ProjectsTreePickerProps) => ({
- render: renderTreeItem(rootItemIcon),
- pickerId,
-});
-
-const mapDispatchToProps = (dispatch: Dispatch, { loadRootItem, includeCollections, includeFiles }: ProjectsTreePickerProps): PickedTreePickerProps => ({
- onContextMenu: () => { return; },
- toggleItemActive: (_, { id }, pickerId) => {
- dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id, pickerId }));
- },
- toggleItemOpen: (_, item, pickerId) => {
- const { id, data, status } = item;
- if (status === TreeItemStatus.INITIAL) {
- if ('kind' in data) {
- dispatch<any>(
- data.kind === ResourceKind.COLLECTION
- ? loadCollection(id, pickerId)
- : loadProject({ id, pickerId, includeCollections, includeFiles })
- );
- } else if (!('type' in data) && loadRootItem) {
- loadRootItem(item as TreeItem<ProjectsTreePickerRootItem>, pickerId, includeCollections, includeFiles);
- }
- } else if (status === TreeItemStatus.LOADED) {
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
- }
- },
- toggleItemSelection: (_, { id }, pickerId) => {
- dispatch<any>(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECTION({ id, pickerId }));
- },
-});
-
-export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(TreePicker);
-
-const getProjectPickerIcon = ({ data }: TreeItem<ProjectsTreePickerItem>, rootIcon: IconType): IconType => {
- if ('kind' in data) {
- switch (data.kind) {
- case ResourceKind.COLLECTION:
- return CollectionIcon;
- default:
- return ProjectIcon;
- }
- } else if ('type' in data) {
- switch (data.type) {
- case CollectionFileType.FILE:
- return InputIcon;
- default:
- return ProjectIcon;
- }
- } else {
- return rootIcon;
- }
-};
-
-const renderTreeItem = (rootItemIcon: IconType) => (item: TreeItem<ProjectResource>) =>
- <ListItemTextIcon
- icon={getProjectPickerIcon(item, rootItemIcon)}
- name={item.data.name}
- isActive={item.active}
- hasMargin={true} />;
+export const ProjectsTreePicker = ({ pickerId, ...props }: ProjectsTreePickerProps) => {
+ const { home, shared, favorites } = getProjectsTreePickerIds(pickerId);
+ return <div>
+ <HomeTreePicker pickerId={home} {...props} />
+ <SharedTreePicker pickerId={shared} {...props} />
+ <FavoritesTreePicker pickerId={favorites} {...props} />
+ </div>;
+ };
// SPDX-License-Identifier: AGPL-3.0
import { connect } from 'react-redux';
-import { ProjectsTreePicker, ProjectsTreePickerProps } from '~/views-components/projects-tree-picker/projects-tree-picker';
+import { ProjectsTreePicker, ProjectsTreePickerProps } from '~/views-components/projects-tree-picker/generic-projects-tree-picker';
import { Dispatch } from 'redux';
import { ShareMeIcon } from '~/components/icon/icon';
-import { loadProject } from '../../store/tree-picker/tree-picker-actions';
+import { loadProject } from '~/store/tree-picker/tree-picker-actions';
-export const SharedProjectsTreePicker = connect(() => ({
+export const SharedTreePicker = connect(() => ({
rootItemIcon: ShareMeIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
loadRootItem: (_, pickerId, includeCollections, includeFiles) => {
import { RunProcessPanel } from '~/views/run-process-panel/run-process-panel';
import SplitterLayout from 'react-splitter-layout';
import { WorkflowPanel } from '~/views/workflow-panel/workflow-panel';
-import { TreePicker } from '../../views-components/tree-picker/tree-picker';
-import { noop } from 'lodash';
-import { TreeItem } from '~/components/tree/tree';
-import { GroupContentsResource } from '~/services/groups-service/groups-service';
+import { HomeTreePicker } from '~/views-components/projects-tree-picker/home-tree-picker';
+import { SharedTreePicker } from '~/views-components/projects-tree-picker/shared-tree-picker';
+import { FavoritesTreePicker } from '../../views-components/projects-tree-picker/favorites-tree-picker';
import { ProjectsTreePicker } from '~/views-components/projects-tree-picker/projects-tree-picker';
-import { UserProjectsTreePicker } from '~/views-components/projects-tree-picker/user-projects-tree-picker';
-import { SharedProjectsTreePicker } from '~/views-components/projects-tree-picker/shared-projects-tree-picker';
-import { FavoritesProjectsTreePicker } from '../../views-components/projects-tree-picker/favorites-tree-picker';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
<MainContentBar />
</Grid>
<Grid item xs className={classes.content}>
- <UserProjectsTreePicker pickerId='testPicker1'/>
- <UserProjectsTreePicker pickerId='testPicker2' includeCollections/>
- <UserProjectsTreePicker pickerId='testPicker3' includeCollections includeFiles/>
- <SharedProjectsTreePicker pickerId='testPicker4'/>
- <SharedProjectsTreePicker pickerId='testPicker5' includeCollections/>
- <SharedProjectsTreePicker pickerId='testPicker6' includeCollections includeFiles/>
- <FavoritesProjectsTreePicker pickerId='testPicker7'/>
- <FavoritesProjectsTreePicker pickerId='testPicker8' includeCollections/>
- <FavoritesProjectsTreePicker pickerId='testPicker9' includeCollections includeFiles/>
+ <p>Projects only</p>
+ <ProjectsTreePicker pickerId="testPicker1"/>
+ <p>Collections included</p>
+ <ProjectsTreePicker pickerId="testPicker2" includeCollections/>
+ <p>Files included</p>
+ <ProjectsTreePicker pickerId="testPicker3" includeCollections includeFiles toggleItemActive={(...args: any[]) => console.log(args)}/>
<Switch>
<Route path={Routes.PROJECTS} component={ProjectPanel} />
<Route path={Routes.COLLECTIONS} component={CollectionPanel} />