diff --git a/frontend/src/views/edit-project/EditProjectView.tsx b/frontend/src/views/edit-project/EditProjectView.tsx
index 97ed5fd057..edff7a16af 100644
--- a/frontend/src/views/edit-project/EditProjectView.tsx
+++ b/frontend/src/views/edit-project/EditProjectView.tsx
@@ -107,19 +107,20 @@ export const EditProjectView = () => {
if (representation && representation.id !== representationId) {
const pathname = generatePath(routeMatch.path, { projectId, representationId: representation.id });
history.push({ pathname });
+ } else if (editProjectView === 'loaded' && representation === null && representationId) {
+ const pathname = generatePath(routeMatch.path, { projectId, representationId: null });
+ history.push({ pathname });
}
- }, [projectId, routeMatch, history, representation, representationId]);
+ }, [editProjectView, projectId, routeMatch, history, representation, representationId]);
let main = null;
if (editProjectView === 'loaded') {
const onRepresentationSelected = (representationSelected: Representation) => {
- if (representationSelected.id !== representationId) {
- const selectRepresentationEvent: SelectRepresentationEvent = {
- type: 'SELECT_REPRESENTATION',
- representation: representationSelected,
- };
- dispatch(selectRepresentationEvent);
- }
+ const selectRepresentationEvent: SelectRepresentationEvent = {
+ type: 'SELECT_REPRESENTATION',
+ representation: representationSelected,
+ };
+ dispatch(selectRepresentationEvent);
};
main = (
diff --git a/frontend/src/workbench/RepresentationArea.tsx b/frontend/src/workbench/RepresentationArea.tsx
index 2126b22a0a..edb5b014cf 100644
--- a/frontend/src/workbench/RepresentationArea.tsx
+++ b/frontend/src/workbench/RepresentationArea.tsx
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2019, 2020 Obeo.
+ * Copyright (c) 2019, 2021 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
@@ -24,6 +24,7 @@ const propTypes = {
representations: PropTypes.array.isRequired,
selection: PropTypes.object,
displayedRepresentation: PropTypes.object,
+ onRepresentationClick: PropTypes.func.isRequired,
setSelection: PropTypes.func.isRequired,
};
export const RepresentationArea = ({
@@ -33,6 +34,8 @@ export const RepresentationArea = ({
selection,
displayedRepresentation,
setSelection,
+ onRepresentationClick,
+ onClose,
}) => {
let content;
if (displayedRepresentation.kind === 'Diagram') {
@@ -56,7 +59,8 @@ export const RepresentationArea = ({
{content}
diff --git a/frontend/src/workbench/RepresentationNavigation.tsx b/frontend/src/workbench/RepresentationNavigation.tsx
index 11e7a7301c..14165f6a6c 100644
--- a/frontend/src/workbench/RepresentationNavigation.tsx
+++ b/frontend/src/workbench/RepresentationNavigation.tsx
@@ -10,39 +10,90 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-import Typography from '@material-ui/core/Typography';
-import PropTypes from 'prop-types';
+import { makeStyles } from '@material-ui/core/styles';
+import Tab from '@material-ui/core/Tab';
+import Tabs from '@material-ui/core/Tabs';
+import CloseIcon from '@material-ui/icons/Close';
import React from 'react';
-import styles from './RepresentationNavigation.module.css';
+import { RepresentationNavigationProps } from 'workbench/RepresentationNavigation.types';
+import { Representation, Selection } from 'workbench/Workbench.types';
-const propTypes = {
- representations: PropTypes.array.isRequired,
- displayedRepresentation: PropTypes.object,
- setSelection: PropTypes.func.isRequired,
+const useRepresentationNavigationStyles = makeStyles((theme) => ({
+ tabsRoot: {
+ minHeight: theme.spacing(4),
+ borderBottomColor: theme.palette.divider,
+ borderBottomWidth: '1px',
+ borderBottomStyle: 'solid',
+ },
+ tabRoot: {
+ minHeight: theme.spacing(4),
+ textTransform: 'none',
+ },
+ tabLabel: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ '& > *:nth-child(2)': {
+ marginLeft: theme.spacing(1),
+ },
+ },
+}));
+
+const a11yProps = (id: string) => {
+ return {
+ id: `simple-tab-${id}`,
+ 'aria-controls': `simple-tabpanel-${id}`,
+ };
};
-export const RepresentationNavigation = ({ representations, displayedRepresentation, setSelection }) => {
+export const RepresentationNavigation = ({
+ representations,
+ displayedRepresentation,
+ onRepresentationClick,
+ onClose,
+}: RepresentationNavigationProps) => {
+ const classes = useRepresentationNavigationStyles();
+
+ const onChange = (_, value) => {
+ const representationSelected = representations.find((representation) => representation.id === value);
+ const selection: Selection = {
+ id: representationSelected.id,
+ label: representationSelected.label,
+ kind: representationSelected.kind,
+ };
+ onRepresentationClick(selection);
+ };
+ const onRepresentationClose = (event, representation: Representation) => {
+ event.stopPropagation();
+ onClose(representation);
+ };
return (
-
+
{representations.map((representation) => {
- let labelClassName = styles.label;
- const isSelected = representation.id === displayedRepresentation.id;
- if (isSelected) {
- labelClassName = `${labelClassName} ${styles.selected}`;
- }
- const { id, label, kind } = representation;
return (
- -
+ {representation.label}
+ onRepresentationClose(event, representation)} />
+
+ }
key={representation.id}
- className={styles.item}
- onClick={() => setSelection({ id, label, kind })}
- data-testid={`representation-tab-${label}`}
- data-testselected={isSelected}>
- {label}
-
+ data-testid={`representation-tab-${representation.label}`}
+ data-testselected={representation.id === displayedRepresentation.id}
+ />
);
})}
-
+
);
};
-RepresentationNavigation.propTypes = propTypes;
diff --git a/frontend/src/workbench/RepresentationNavigation.module.css b/frontend/src/workbench/RepresentationNavigation.types.ts
similarity index 51%
rename from frontend/src/workbench/RepresentationNavigation.module.css
rename to frontend/src/workbench/RepresentationNavigation.types.ts
index 27ebbe798a..a7302198b9 100644
--- a/frontend/src/workbench/RepresentationNavigation.module.css
+++ b/frontend/src/workbench/RepresentationNavigation.types.ts
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2019, 2020 Obeo.
+ * Copyright (c) 2021 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
@@ -10,25 +10,11 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-.representationNavigation {
- display: flex;
- flex-direction: row;
- border-bottom: 1px solid var(--blue-lagoon);
-}
+import { Representation } from 'workbench/Workbench.types';
-.item {
- padding: 8px;
- border-right: 1px solid var(--blue-lagoon);
-}
-
-.label {
- font-size: var(--font-size-5);
- font-weight: var(--font-weight-bold);
- color: var(--blue-lagoon-lighten-60);
-}
-.label:hover {
- color: var(--blue-lagoon-lighten-10);
-}
-.selected {
- color: var(--blue-lagoon-lighten-10);
-}
+export type RepresentationNavigationProps = {
+ representations: Representation[];
+ displayedRepresentation: Representation;
+ onRepresentationClick: (representation: Representation) => void;
+ onClose: (representation: Representation) => void;
+};
diff --git a/frontend/src/workbench/Workbench.tsx b/frontend/src/workbench/Workbench.tsx
index 01a4d819ac..e5209479a6 100644
--- a/frontend/src/workbench/Workbench.tsx
+++ b/frontend/src/workbench/Workbench.tsx
@@ -18,8 +18,15 @@ import { PropertiesWebSocketContainer } from 'properties/PropertiesWebSocketCont
import React, { useEffect } from 'react';
import { OnboardArea } from 'workbench/OnboardArea';
import { RepresentationArea } from 'workbench/RepresentationArea';
-import { Selection, WorkbenchProps } from 'workbench/Workbench.types';
-import { UpdateSelectionEvent, WorkbenchContext, WorkbenchEvent, workbenchMachine } from 'workbench/WorkbenchMachine';
+import { Representation, Selection, WorkbenchProps } from 'workbench/Workbench.types';
+import {
+ HideRepresentationEvent,
+ ShowRepresentationEvent,
+ UpdateSelectionEvent,
+ WorkbenchContext,
+ WorkbenchEvent,
+ workbenchMachine,
+} from 'workbench/WorkbenchMachine';
const useWorkbenchStyles = makeStyles((theme) => ({
main: {
@@ -49,9 +56,21 @@ export const Workbench = ({
dispatch(updateSelectionEvent);
};
+ const onRepresentationClick = (representation: Representation) => {
+ const showRepresentationEvent: ShowRepresentationEvent = { type: 'SHOW_REPRESENTATION', representation };
+ dispatch(showRepresentationEvent);
+ };
+
+ const onClose = (representation: Representation) => {
+ const hideRepresentationEvent: HideRepresentationEvent = { type: 'HIDE_REPRESENTATION', representation };
+ dispatch(hideRepresentationEvent);
+ };
+
useEffect(() => {
- if (displayedRepresentation !== null && displayedRepresentation.id !== initialRepresentationSelected?.id) {
+ if (displayedRepresentation && displayedRepresentation.id !== initialRepresentationSelected?.id) {
onRepresentationSelected(displayedRepresentation);
+ } else if (displayedRepresentation === null && initialRepresentationSelected) {
+ onRepresentationSelected(null);
}
}, [onRepresentationSelected, initialRepresentationSelected, displayedRepresentation]);
@@ -69,6 +88,8 @@ export const Workbench = ({
displayedRepresentation={displayedRepresentation}
selection={selection}
setSelection={setSelection}
+ onRepresentationClick={onRepresentationClick}
+ onClose={onClose}
readOnly={readOnly}
/>
);
diff --git a/frontend/src/workbench/WorkbenchMachine.ts b/frontend/src/workbench/WorkbenchMachine.ts
index 7db5941a54..a56505629c 100644
--- a/frontend/src/workbench/WorkbenchMachine.ts
+++ b/frontend/src/workbench/WorkbenchMachine.ts
@@ -26,8 +26,9 @@ export interface WorkbenchContext {
}
export type ShowRepresentationEvent = { type: 'SHOW_REPRESENTATION'; representation: Representation };
+export type HideRepresentationEvent = { type: 'HIDE_REPRESENTATION'; representation: Representation };
export type UpdateSelectionEvent = { type: 'UPDATE_SELECTION'; selection: Selection };
-export type WorkbenchEvent = UpdateSelectionEvent | ShowRepresentationEvent;
+export type WorkbenchEvent = UpdateSelectionEvent | ShowRepresentationEvent | HideRepresentationEvent;
export const workbenchMachine = Machine(
{
@@ -48,6 +49,10 @@ export const workbenchMachine = Machine {
+ const { representation: representationToHide } = event as HideRepresentationEvent;
+
+ const previousIndex = context.representations.findIndex(
+ (representation) => representation.id === context.displayedRepresentation.id
+ );
+ const newRepresentations = context.representations.filter(
+ (representation) => representation.id !== representationToHide.id
+ );
+
+ if (newRepresentations.length === 0) {
+ // There are no representations anymore
+ return { displayedRepresentation: null, representations: [] };
+ } else {
+ const newIndex = newRepresentations.findIndex(
+ (representation) => representation.id === context.displayedRepresentation.id
+ );
+
+ if (newIndex !== -1) {
+ // The previously displayed representation has not been closed
+ return { representations: newRepresentations };
+ } else if (newRepresentations.length === previousIndex) {
+ // The previous representation has been closed and it was the last one
+ const displayedRepresentation = newRepresentations[previousIndex - 1];
+ return { displayedRepresentation, representations: newRepresentations };
+ } else {
+ const displayedRepresentation = newRepresentations[previousIndex];
+ return { displayedRepresentation, representations: newRepresentations };
+ }
+ }
+ }),
},
}
);