[ARVADOS-WORKBENCH2] created: 1.1.4-291-g99d2472
Git user
git at public.curoverse.com
Fri Jul 13 07:40:09 EDT 2018
at 99d2472afc89ca78a4fef46ac08ce520b3bcf17b (commit)
commit 99d2472afc89ca78a4fef46ac08ce520b3bcf17b
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date: Fri Jul 13 13:39:58 2018 +0200
Extract context-menu view component
Feature #13805
Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
diff --git a/src/components/context-menu/context-menu.tsx b/src/components/context-menu/context-menu.tsx
index c892ba2..5626181 100644
--- a/src/components/context-menu/context-menu.tsx
+++ b/src/components/context-menu/context-menu.tsx
@@ -13,14 +13,14 @@ export interface ContextMenuAction {
export type ContextMenuActionGroup = ContextMenuAction[];
-export interface ContextMenuProps<T> {
+export interface ContextMenuProps {
anchorEl?: HTMLElement;
actions: ContextMenuActionGroup[];
onActionClick: (action: ContextMenuAction) => void;
onClose: () => void;
}
-export default class ContextMenu<T> extends React.PureComponent<ContextMenuProps<T>> {
+export default class ContextMenu extends React.PureComponent<ContextMenuProps> {
render() {
const { anchorEl, actions, onClose, onActionClick } = this.props;
return <Popover
diff --git a/src/components/popover/helpers.ts b/src/components/popover/helpers.ts
index 13f74a6..f2be98c 100644
--- a/src/components/popover/helpers.ts
+++ b/src/components/popover/helpers.ts
@@ -4,13 +4,13 @@
import { PopoverOrigin } from "@material-ui/core/Popover";
-export const mockAnchorFromMouseEvent = (event: React.MouseEvent<HTMLElement>) => {
+export const createAnchorAt = (position: {x: number, y: number}) => {
const el = document.createElement('div');
const clientRect = {
- left: event.clientX,
- right: event.clientX,
- top: event.clientY,
- bottom: event.clientY,
+ left: position.x,
+ right: position.x,
+ top: position.y,
+ bottom: position.y,
width: 0,
height: 0
};
diff --git a/src/store/context-menu/context-menu-actions.ts b/src/store/context-menu/context-menu-actions.ts
index 89d6524..8630f9c 100644
--- a/src/store/context-menu/context-menu-actions.ts
+++ b/src/store/context-menu/context-menu-actions.ts
@@ -2,14 +2,16 @@
//
// SPDX-License-Identifier: AGPL-3.0
-// import { default as unionize, ofType, UnionOf } from "unionize";
+import { default as unionize, ofType, UnionOf } from "unionize";
+import { ContextMenuPosition, ContextMenuResource } from "./context-menu-reducer";
-// const actions = unionize({
-// OPEN_CONTEXT_MENU: ofType<{position: {x: number, y: number}}>()
-// }, {
-// tag: 'type',
-// value: 'payload'
-// });
+const actions = unionize({
+ OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(),
+ CLOSE_CONTEXT_MENU: ofType<{}>()
+}, {
+ tag: 'type',
+ value: 'payload'
+ });
-// export type ContextMenuAction = UnionOf<typeof actions>;
-// export default actions;
\ No newline at end of file
+export type ContextMenuAction = UnionOf<typeof actions>;
+export default actions;
\ No newline at end of file
diff --git a/src/store/context-menu/context-menu-reducer.ts b/src/store/context-menu/context-menu-reducer.ts
index 9a825a5..69f9c9a 100644
--- a/src/store/context-menu/context-menu-reducer.ts
+++ b/src/store/context-menu/context-menu-reducer.ts
@@ -2,31 +2,33 @@
//
// SPDX-License-Identifier: AGPL-3.0
-// import actions, { DetailsPanelAction } from "./details-panel-action";
-// import { Resource, ResourceKind } from "../../models/resource";
+import { ResourceKind } from "../../models/resource";
+import actions, { ContextMenuAction } from "./context-menu-actions";
-// export interface ContextMenuState {
-// position: {
-// x: number;
-// y: number;
-// },
-// resource: {
-// uuid: string;
-// kind: ResourceKind.
-// }
-// }
+export interface ContextMenuState {
+ position: ContextMenuPosition;
+ resource?: ContextMenuResource;
+}
-// const initialState = {
-// item: null,
-// isOpened: false
-// };
+export interface ContextMenuPosition {
+ x: number;
+ y: number;
+}
-// const reducer = (state: DetailsPanelState = initialState, action: DetailsPanelAction) =>
-// actions.match(action, {
-// default: () => state,
-// LOAD_DETAILS: () => state,
-// LOAD_DETAILS_SUCCESS: ({ item }) => ({ ...state, item }),
-// TOGGLE_DETAILS_PANEL: () => ({ ...state, isOpened: !state.isOpened })
-// });
+export interface ContextMenuResource {
+ uuid: string;
+ kind: ResourceKind;
+}
-// export default reducer;
+const initialState = {
+ position: { x: 0, y: 0 }
+};
+
+const reducer = (state: ContextMenuState = initialState, action: ContextMenuAction) =>
+ actions.match(action, {
+ default: () => state,
+ OPEN_CONTEXT_MENU: ({resource, position}) => ({ resource, position }),
+ CLOSE_CONTEXT_MENU: () => ({ position: state.position })
+ });
+
+export default reducer;
diff --git a/src/store/store.ts b/src/store/store.ts
index 36f9203..130028a 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -13,6 +13,7 @@ import authReducer, { AuthState } from "./auth/auth-reducer";
import dataExplorerReducer, { DataExplorerState } from './data-explorer/data-explorer-reducer';
import { projectPanelMiddleware } from '../store/project-panel/project-panel-middleware';
import detailsPanelReducer, { DetailsPanelState } from './details-panel/details-panel-reducer';
+import contextMenuReducer, { ContextMenuState } from './context-menu/context-menu-reducer';
const composeEnhancers =
(process.env.NODE_ENV === 'development' &&
@@ -26,6 +27,7 @@ export interface RootState {
dataExplorer: DataExplorerState;
sidePanel: SidePanelState;
detailsPanel: DetailsPanelState;
+ contextMenu: ContextMenuState;
}
const rootReducer = combineReducers({
@@ -34,7 +36,8 @@ const rootReducer = combineReducers({
router: routerReducer,
dataExplorer: dataExplorerReducer,
sidePanel: sidePanelReducer,
- detailsPanel: detailsPanelReducer
+ detailsPanel: detailsPanelReducer,
+ contextMenu: contextMenuReducer
});
diff --git a/src/views-components/context-menu/context-menu.tsx b/src/views-components/context-menu/context-menu.tsx
new file mode 100644
index 0000000..21d851d
--- /dev/null
+++ b/src/views-components/context-menu/context-menu.tsx
@@ -0,0 +1,74 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { connect, Dispatch, DispatchProp } from "react-redux";
+import { RootState } from "../../store/store";
+import actions from "../../store/context-menu/context-menu-actions";
+import ContextMenu, { ContextMenuAction, ContextMenuProps } from "../../components/context-menu/context-menu";
+import { createAnchorAt } from "../../components/popover/helpers";
+import projectActions from "../../store/project/project-action";
+import { ContextMenuResource } from "../../store/context-menu/context-menu-reducer";
+
+
+type DataProps = Pick<ContextMenuProps, "anchorEl" | "actions"> & { resource?: ContextMenuResource };
+const mapStateToProps = (state: RootState): DataProps => {
+ const { position, resource } = state.contextMenu;
+ return {
+ anchorEl: resource ? createAnchorAt(position) : undefined,
+ actions: contextMenuActions,
+ resource
+ };
+};
+
+type ActionProps = Pick<ContextMenuProps, "onClose"> & {onActionClick: (action: ContextMenuAction, resource?: ContextMenuResource) => void};
+const mapDispatchToProps = (dispatch: Dispatch): ActionProps => ({
+ onClose: () => {
+ dispatch(actions.CLOSE_CONTEXT_MENU());
+ },
+ onActionClick: (action: ContextMenuAction, resource?: ContextMenuResource) => {
+ dispatch(actions.CLOSE_CONTEXT_MENU());
+ if (resource) {
+ if (action.name === "New project") {
+ dispatch(projectActions.OPEN_PROJECT_CREATOR({ ownerUuid: resource.uuid }));
+ }
+ }
+ }
+});
+
+const mergeProps = ({ resource, ...dataProps }: DataProps, actionProps: ActionProps): ContextMenuProps => ({
+ ...dataProps,
+ ...actionProps,
+ onActionClick: (action: ContextMenuAction) => {
+ actionProps.onActionClick(action, resource);
+ }
+});
+
+export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(ContextMenu);
+
+const contextMenuActions = [[{
+ icon: "fas fa-plus fa-fw",
+ name: "New project"
+}, {
+ icon: "fas fa-users fa-fw",
+ name: "Share"
+}, {
+ icon: "fas fa-sign-out-alt fa-fw",
+ name: "Move to"
+}, {
+ icon: "fas fa-star fa-fw",
+ name: "Add to favourite"
+}, {
+ icon: "fas fa-edit fa-fw",
+ name: "Rename"
+}, {
+ icon: "fas fa-copy fa-fw",
+ name: "Make a copy"
+}, {
+ icon: "fas fa-download fa-fw",
+ name: "Download"
+}], [{
+ icon: "fas fa-trash-alt fa-fw",
+ name: "Remove"
+}
+]];
\ No newline at end of file
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 6972b2f..afd9dfd 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -26,12 +26,12 @@ import projectActions from "../../store/project/project-action";
import ProjectPanel from "../project-panel/project-panel";
import DetailsPanel from '../../views-components/details-panel/details-panel';
import { ArvadosTheme } from '../../common/custom-theme';
-import ContextMenu, { ContextMenuAction } from '../../components/context-menu/context-menu';
-import { mockAnchorFromMouseEvent } from '../../components/popover/helpers';
+import ContextMenu from "../../views-components/context-menu/context-menu";
import CreateProjectDialog from "../../views-components/create-project-dialog/create-project-dialog";
import { authService } from '../../services/services';
import detailsPanelActions, { loadDetails } from "../../store/details-panel/details-panel-action";
+import contextMenuActions from "../../store/context-menu/context-menu-actions";
import { SidePanelIdentifiers } from '../../store/side-panel/side-panel-reducer';
import { ProjectResource } from '../../models/project';
import { ResourceKind } from '../../models/resource';
@@ -98,10 +98,6 @@ interface NavMenuItem extends MainAppBarMenuItem {
}
interface WorkbenchState {
- contextMenu: {
- anchorEl?: HTMLElement;
- itemUuid?: string;
- };
anchorEl: any;
searchText: string;
menuItems: {
@@ -114,10 +110,6 @@ interface WorkbenchState {
class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
state = {
- contextMenu: {
- anchorEl: undefined,
- itemUuid: undefined
- },
isCreationDialogOpen: false,
anchorEl: null,
searchText: "",
@@ -177,30 +169,18 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
}
handleCreationDialogOpen = (itemUuid: string) => {
- this.closeContextMenu();
this.props.dispatch(projectActions.OPEN_PROJECT_CREATOR({ ownerUuid: itemUuid }));
}
openContextMenu = (event: React.MouseEvent<HTMLElement>, itemUuid: string) => {
event.preventDefault();
- this.setState({
- contextMenu: {
- anchorEl: mockAnchorFromMouseEvent(event),
- itemUuid
- }
- });
- }
-
- closeContextMenu = () => {
- this.setState({ contextMenu: {} });
- }
-
- openCreateDialog = (item: ContextMenuAction) => {
- const { itemUuid } = this.state.contextMenu;
- if (item.openCreateDialog && itemUuid) {
- this.handleCreationDialogOpen(itemUuid);
- }
+ this.props.dispatch(
+ contextMenuActions.OPEN_CONTEXT_MENU({
+ position: { x: event.clientX, y: event.clientY },
+ resource: { uuid: itemUuid, kind: ResourceKind.Project }
+ })
+ );
}
render() {
@@ -253,11 +233,7 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
</div>
<DetailsPanel />
</main>
- <ContextMenu
- anchorEl={this.state.contextMenu.anchorEl}
- actions={contextMenuActions}
- onActionClick={this.openCreateDialog}
- onClose={this.closeContextMenu} />
+ <ContextMenu />
<CreateProjectDialog />
</div>
);
@@ -277,34 +253,6 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
{...props} />
}
-const contextMenuActions = [[{
- icon: "fas fa-plus fa-fw",
- name: "New project",
- openCreateDialog: true
-}, {
- icon: "fas fa-users fa-fw",
- name: "Share"
-}, {
- icon: "fas fa-sign-out-alt fa-fw",
- name: "Move to"
-}, {
- icon: "fas fa-star fa-fw",
- name: "Add to favourite"
-}, {
- icon: "fas fa-edit fa-fw",
- name: "Rename"
-}, {
- icon: "fas fa-copy fa-fw",
- name: "Make a copy"
-}, {
- icon: "fas fa-download fa-fw",
- name: "Download"
-}], [{
- icon: "fas fa-trash-alt fa-fw",
- name: "Remove"
-}
-]];
-
export default connect<WorkbenchDataProps>(
(state: RootState) => ({
projects: state.projects.items,
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list