diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index cda706ee6c..7af86ca881 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -166,6 +166,7 @@ Thanks to this capability, a specifier can easily retrieve a variable overriden image:doc/screenshots/borderNodeCreationArea.png[BorderNode creation area,50%] + In other scenarios, the default creation position border remains the eastern one. +- https://github.com/eclipse-sirius/sirius-web/issues/2785[#2785] [gantt] Add cypress tests == v2024.1.0 diff --git a/integration-tests/cypress/e2e/project/gantt/gantt-task.cy.ts b/integration-tests/cypress/e2e/project/gantt/gantt-task.cy.ts new file mode 100644 index 0000000000..a19d9e8b84 --- /dev/null +++ b/integration-tests/cypress/e2e/project/gantt/gantt-task.cy.ts @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { Details } from '../../../workbench/Details'; +import { GanttTestHelper } from '../../../workbench/Gantt'; + +describe('Verify the Gantt Task actions', () => { + let studioProjectId: string = ''; + let taskProjectId: string = ''; + before(() => { + // We create the Gantt View from the stereotype before executing the tests + new GanttTestHelper().initGanttView().then((projectId) => { + studioProjectId = projectId; + }); + + new GanttTestHelper() + .createTaskProjectAndGanttRepresentation('Project Dev', 'New Gantt Representation') + .then((projectId) => { + taskProjectId = projectId; + }); + }); + after(() => { + cy.deleteProject(studioProjectId); + cy.deleteProject(taskProjectId); + }); + + beforeEach(() => { + new GanttTestHelper().openGanttRepresentation(taskProjectId, 'Project Dev', 'New Gantt Representation'); + }); + + it('can select a task and see its attributes in Details', () => { + const ganttHelper = new GanttTestHelper(); + ganttHelper.getTask('Specification').click(); + const details = new Details(); + details.getTextField('Name').should('have.value', 'Specification'); + details.getTextField('Description').should('have.value', 'Description of the Specification'); + details.getTextField('Progress').should('have.value', '50'); + details.getDetailsView().findByTestId('Compute Start/End Dynamically').should('not.exist'); + // details.getCheckBox('Compute Start/End Dynamically').should('not.exist'); + + ganttHelper.getProjectTask('Development').click(); + details.getCheckBox('Compute Start/End Dynamically').should('exist'); + }); + + it('can change task characteristics from detail with effect in gantt', () => { + const ganttHelper = new GanttTestHelper(); + const details = new Details(); + ganttHelper.getTask('Specification').click(); + + details.getTextField('Name').type('Renamed{Enter}'); + ganttHelper.getTask('Specification').should('not.exist'); + ganttHelper.getTask('SpecificationRenamed').should('exist'); + + ganttHelper.checkSVGAttributeInTask(ganttHelper.getTask('SpecificationRenamed'), 'rect', 'width', '82.5'); + details.getTextField('End Time').type('{selectall}{backspace}2023-12-13T17:30:00Z{Enter}'); + ganttHelper.checkSVGAttributeInTask(ganttHelper.getTask('SpecificationRenamed'), 'rect', 'width', '142.5'); + }); + + it('can create and delete a new task', () => { + const ganttHelper = new GanttTestHelper(); + ganttHelper.getTask('Front').click(); + ganttHelper.getGanttRepresentation().getByTestId('create-task').click(); + ganttHelper.getTask('New Task').should('exist'); + + ganttHelper.getTask('New Task').click(); + ganttHelper.getGanttRepresentation().getByTestId('delete-task').click(); + ganttHelper.getTask('New Task').should('not.exist'); + }); +}); diff --git a/integration-tests/cypress/e2e/project/gantt/gantt.cy.ts b/integration-tests/cypress/e2e/project/gantt/gantt.cy.ts new file mode 100644 index 0000000000..0b344ca05c --- /dev/null +++ b/integration-tests/cypress/e2e/project/gantt/gantt.cy.ts @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { Explorer } from '../../../workbench/Explorer'; +import { GanttTestHelper } from '../../../workbench/Gantt'; + +describe('Verify the Gantt Representation', () => { + let studioProjectId: string = ''; + before(() => { + // We create the Gantt View from the stereotype before executing the tests + new GanttTestHelper().initGanttView().then((projectId) => { + studioProjectId = projectId; + }); + }); + after(() => { + // We delete the created studio once all tests have been executed + cy.deleteProject(studioProjectId); + }); + + context('We new verify the Gantt Creation, Deletion and renaming', () => { + let taskProjectId: string; + before(() => { + new GanttTestHelper() + .createTaskProjectAndGanttRepresentation('Project Dev', 'New Gantt Representation') + .then((projectId) => { + taskProjectId = projectId; + }); + }); + + beforeEach(() => { + new GanttTestHelper().openGanttRepresentation(taskProjectId, 'Project Dev', 'New Gantt Representation'); + }); + + after(() => { + cy.deleteProject(taskProjectId); + }); + + it('can create the Gantt Representation', () => { + new Explorer().getSelectedTreeItems().contains('New Gantt Representation').should('exist'); + new GanttTestHelper().getGanttRepresentation().should('exist'); + }); + + it('can verify the Gantt Representation on the Dev project', () => { + new Explorer().getSelectedTreeItems().contains('New Gantt Representation').should('exist'); + + const gantt = new GanttTestHelper(); + //We verify that all tasks are present. + const taskTitles: string[] = ['Idea', 'Specification', 'Front', 'Back', 'Review']; + taskTitles.forEach((title) => { + gantt.getTask(title).should('exist'); + }); + const projectTitles: string[] = ['Development', 'Code Development']; + projectTitles.forEach((title) => { + gantt.getProjectTask(title).should('exist'); + }); + gantt.getMilestone('Release').should('exist'); + }); + + it('can rename the Gantt Representation', () => { + const explorer = new Explorer(); + explorer.getTreeItemByLabel('New Gantt Representation').should('exist'); + new GanttTestHelper().getGanttRepresentation().should('exist'); + explorer.rename('New Gantt Representation', 'renamed'); + explorer.getTreeItemByLabel('New Gantt Representation').should('not.exist'); + explorer.getTreeItemByLabel('renamed').should('exist'); + + cy.getByTestId('representation-tab-renamed').should('exist'); + explorer.rename('renamed', 'New Gantt Representation'); + }); + + it('can remove the Gantt Representation', () => { + const explorer = new Explorer(); + explorer.getTreeItemByLabel('New Gantt Representation').should('exist'); + new GanttTestHelper().getGanttRepresentation().should('exist'); + explorer.delete('New Gantt Representation'); + explorer.getTreeItemByLabel('New Gantt Representation').should('not.exist'); + cy.getByTestId('representation-area').find('h5').should('have.text', 'The Gantt does not exist anymore'); + }); + }); + + context('We verify the Gantt table columns', () => { + let taskProjectId: string; + before(() => { + new GanttTestHelper() + .createTaskProjectAndGanttRepresentation('Project Dev', 'New Gantt Representation') + .then((projectId) => { + taskProjectId = projectId; + }); + }); + + beforeEach(() => { + new GanttTestHelper().openGanttRepresentation(taskProjectId, 'Project Dev', 'New Gantt Representation'); + }); + + after(() => { + console.log('cy.deleteProject(taskProjectId)'); + cy.deleteProject(taskProjectId); + }); + it('can display the table columns', () => { + const ganttHelper = new GanttTestHelper(); + const columnTitles = ['Name', 'Date of start', 'Date of start', 'Progress']; + columnTitles.forEach((title) => { + ganttHelper.getColumnHeader(title).should('exist'); + }); + ganttHelper.getGanttRepresentation().findByTestId('display-task-list-columns').click(); + columnTitles.forEach((title) => { + ganttHelper.getColumnHeader(title).should('not.exist'); + }); + ganttHelper.getGanttRepresentation().findByTestId('display-task-list-columns').click(); + columnTitles.forEach((title) => { + ganttHelper.getColumnHeader(title).should('exist'); + }); + + //hide one column + ganttHelper.getGanttRepresentation().findByTestId('columns-select').click(); + cy.getByTestId('columnType-From').click(); + ganttHelper.getColumnHeader('Date of start').should('not.exist'); + cy.getByTestId('columnType-From').click(); + ganttHelper.getColumnHeader('Date of start').should('exist'); + cy.get('body').click(0, 0); + + // hide all columns + ganttHelper.getGanttRepresentation().findByTestId('display-task-list-columns').click(); + columnTitles.forEach((title) => { + ganttHelper.getColumnHeader(title).should('not.exist'); + }); + ganttHelper.getGanttRepresentation().findByTestId('display-task-list-columns').click(); + columnTitles.forEach((title) => { + ganttHelper.getColumnHeader(title).should('exist'); + }); + ganttHelper.getGanttRepresentation().findByTestId('columns-select').click(); + cy.getByTestId('columnType-From').click(); + cy.get('body').click(0, 0); + ganttHelper.getGanttRepresentation().findByTestId('display-task-list-columns').click(); + ganttHelper.getColumnHeader('Date of start').should('not.exist'); + }); + + it('can change the columns width', () => { + const ganttHelper = new GanttTestHelper(); + + ganttHelper.getColumnHeader('Name').then((header) => { + expect(header.width()).eq(200); + }); + ganttHelper + .getGanttRepresentation() + .findByTestId('table-column-header-resize-handle-Name') + .trigger('mousedown') + .trigger('mousemove', { clientX: 450 }) + .trigger('mouseup'); + + ganttHelper.getColumnHeader('Name').then((header) => { + expect(header.width()).eq(140); + }); + ganttHelper.getColumnHeader('Date of start').then((header) => { + expect(header.width()).eq(140); + }); + }); + }); + + context('We verify the Gantt toolbar', () => { + let taskProjectId: string; + before(() => { + new GanttTestHelper() + .createTaskProjectAndGanttRepresentation('Project Dev', 'New Gantt Representation') + .then((projectId) => { + taskProjectId = projectId; + }); + }); + + beforeEach(() => { + new GanttTestHelper().openGanttRepresentation(taskProjectId, 'Project Dev', 'New Gantt Representation'); + }); + + after(() => { + cy.deleteProject(taskProjectId); + }); + + it('can change the zoom level', () => { + cy.getByTestId('zoom-level').get('input').should('have.value', 'Day'); + cy.getByTestId('representation-area').findByTestId('zoom-level').click(); + cy.getByTestId('zoom-level-Hour').click(); + cy.getByTestId('zoom-level-Hour').should('exist'); + cy.getByTestId('fit-to-screen').click(); + cy.getByTestId('zoom-level').get('input').should('have.value', 'Day'); + }); + + it('can share the representation', () => { + cy.getByTestId('representation-area').findByTestId('share').click(); + + cy.url().then((url) => { + cy.window().then((win) => { + win.navigator.clipboard.readText().then((text) => { + expect(text).to.eq(url); + }); + }); + }); + }); + }); +}); diff --git a/integration-tests/cypress/workbench/Gantt.ts b/integration-tests/cypress/workbench/Gantt.ts new file mode 100644 index 0000000000..2f43753c60 --- /dev/null +++ b/integration-tests/cypress/workbench/Gantt.ts @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { Project } from '../pages/Project'; +import { Projects } from '../pages/Projects'; +import { isCreateProjectFromTemplateSuccessPayload } from '../support/server/createProjectFromTemplateCommand'; +import { Explorer } from './Explorer'; + +export class GanttTestHelper { + checkSVGAttributeInTask( + task: Cypress.Chainable>, + tagInsideSVG: string, + attributeName: string, + expectedValue: string + ) { + task.within(() => { + cy.get(tagInsideSVG).then((element) => { + expect(element.attr(attributeName)).eq(expectedValue); + }); + }); + } + + public getGanttRepresentation(): Cypress.Chainable> { + return cy.getByTestId('gantt-representation'); + } + + public getProjectTask(taskName: string): Cypress.Chainable> { + return this.getGanttRepresentation().findByTestId(`task-project-${taskName}`); + } + public getMilestone(taskName: string): Cypress.Chainable> { + return this.getGanttRepresentation().findByTestId(`task-milestone-${taskName}`); + } + public getTask(taskName: string): Cypress.Chainable> { + return this.getGanttRepresentation().findByTestId(`task-bar-${taskName}`); + } + public getColumnHeader(title: string): Cypress.Chainable> { + return this.getGanttRepresentation().findByTestId(`table-column-header-${title}`); + } + + public createGanttRepresentation(rootElementName: string, GanttRepresentationName) { + const explorer = new Explorer(); + explorer.getTreeItemByLabel('Task Model').should('exist'); + explorer.expand('Task Model'); + explorer.expand('Company'); + explorer.createRepresentation(rootElementName, 'Gantt Representation', GanttRepresentationName); + } + + public deleteCurrentProject() { + cy.url().then(($url) => { + const prefix = Cypress.config().baseUrl + '/projects/'; + const projectId = $url.substring(prefix.length, $url.indexOf('/', prefix.length + 1)); + cy.deleteProject(projectId); + }); + } + + public openGanttRepresentation(taskProjectId: string, projectName: string, representationName: string) { + new Projects().visit(); + const project = new Project(); + project.visit(taskProjectId); + project.disableDeletionConfirmationDialog(); + const explorer = new Explorer(); + explorer.expand('Task Model'); + explorer.expand('Company'); + explorer.expand(projectName); + new Explorer().getTreeItemByLabel(representationName).click(); + } + + public closeGanttRepresentation(representationName: string) { + cy.getByTestId(`close-representation-tab-${representationName}`).click(); + } + + /** + * Creates the Deck Task Sample view from the stereotype. + * @returns the created studio project id. + */ + public initGanttView(): Cypress.Chainable { + new Projects().visit(); + return cy.createProjectFromTemplate('blank-studio-template').then((res) => { + const payload = res.body.data.createProjectFromTemplate; + if (isCreateProjectFromTemplateSuccessPayload(payload)) { + const studioProjectId = payload.project.id; + new Project().visit(studioProjectId); + cy.getByTestId('new-model').click(); + cy.getByTestId('name-input').type('Gantt Task Sample View'); + cy.getByTestId('stereotype').click(); + cy.get('li').filter(':contains("Gantt Task Sample View")').click(); + cy.getByTestId('create-document').click(); + return cy.wrap(studioProjectId); + } + return cy.wrap(''); + }); + } + + /** + * Creates the Task Project and a Gantt representation on a project. + * @returns the created task project id. + */ + public createTaskProjectAndGanttRepresentation( + rootElementName: string, + GanttRepresentationName: string + ): Cypress.Chainable { + new Projects().visit(); + return cy + .createProjectFromTemplate('task-template') + .then((res) => { + const payload = res.body.data.createProjectFromTemplate; + if (isCreateProjectFromTemplateSuccessPayload(payload)) { + const taskProjectId = payload.project.id; + new Project().visit(taskProjectId); + new GanttTestHelper().createGanttRepresentation(rootElementName, GanttRepresentationName); + } + }) + .then(() => { + return cy.url().then(($url) => { + const prefix = Cypress.config().baseUrl + '/projects/'; + const taskProjectId = $url.substring(prefix.length, $url.indexOf('/', prefix.length + 1)); + return cy.wrap(taskProjectId); + }); + }); + } +} diff --git a/package-lock.json b/package-lock.json index 30e74a54f9..82479784a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -980,6 +980,62 @@ "node": ">=12" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.1.tgz", + "integrity": "sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==", + "dependencies": { + "@floating-ui/core": "^1.1.0" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.19.0.tgz", + "integrity": "sha512-fgYvN4ksCi5OvmPXkyOT8o5a8PSKHMzPHt+9mR6KYWdF16IAjWRLZPAAziI2sznaWT23drRFrYw64wdvYqqaQw==", + "dependencies": { + "@floating-ui/react-dom": "^1.2.2", + "aria-hidden": "^1.1.3", + "tabbable": "^6.0.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.3.0.tgz", + "integrity": "sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==", + "dependencies": { + "@floating-ui/dom": "^1.2.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom/node_modules/@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", @@ -1511,16 +1567,18 @@ } }, "node_modules/@ObeoNetwork/gantt-task-react": { - "version": "0.3.13", - "resolved": "https://npm.pkg.github.com/download/@ObeoNetwork/gantt-task-react/0.3.13/0e8a00a6c1c0b8d4e229d208bef8d28ecdad0885", - "integrity": "sha512-OP7aVcv6s/DSPvgqyzyuZ5RERalGM/X32oOGRE0jYmkGvdWqORfuTagXXOYeAE8dGSJJzIJXsoYBXA62i5QnJw==", + "version": "0.4.5", + "resolved": "https://npm.pkg.github.com/download/@ObeoNetwork/gantt-task-react/0.4.5/fd8dcda99be3da5a3f3630ab786a0d9a137b7729", + "integrity": "sha512-h2I6UTn6cncDS/t1DXpOh2R2h501ejdAvM1FJXt5Bcvaj/q1nhvj+TyGkS+QQb0at/5oHJsiaH8CSKyD15zSNQ==", "license": "MIT", - "engines": { - "node": ">=10" + "dependencies": { + "@floating-ui/dom": "1.1.1", + "@floating-ui/react": "0.19.0", + "date-fns": "2.29.3" }, "peerDependencies": { - "@material-ui/core": "^4.12.4", - "@material-ui/icons": "^4.11.3", + "@material-ui/core": "4.12.4", + "@material-ui/icons": "4.11.3", "react": "^17.0.2", "react-dom": "^17.0.2" } @@ -1555,6 +1613,45 @@ "styled-components": ">= 4.0.3" } }, + "node_modules/@ObeoNetwork/react-trello/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/@ObeoNetwork/react-trello/node_modules/react-redux": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.2.tgz", + "integrity": "sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4", + "loose-envify": "^1.1.0", + "prop-types": "^15.6.1", + "react-is": "^16.6.0", + "react-lifecycles-compat": "^3.0.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0-0 || ^16.0.0-0", + "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0" + } + }, + "node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", + "peer": true + }, "node_modules/@reactflow/background": { "version": "11.3.6", "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.6.tgz", @@ -2072,6 +2169,8 @@ "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "optional": true, + "peer": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -2087,7 +2186,7 @@ "version": "15.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==", - "dev": true + "devOptional": true }, "node_modules/@types/prop-types": { "version": "15.7.11", @@ -2113,25 +2212,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-redux": { - "version": "7.1.33", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz", - "integrity": "sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==", - "dependencies": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - } - }, - "node_modules/@types/react-redux/node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, "node_modules/@types/react-router": { "version": "5.1.20", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", @@ -2490,6 +2570,17 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/aria-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", + "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", @@ -3216,6 +3307,18 @@ "node": ">=10" } }, + "node_modules/date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3345,6 +3448,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -3536,6 +3649,12 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "peer": true + }, "node_modules/fast-equals": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", @@ -4990,6 +5109,45 @@ "node": ">=0.10.0" } }, + "node_modules/react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "peer": true, + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dev": true, + "dependencies": { + "dnd-core": "^16.0.1" + } + }, "node_modules/react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -5083,30 +5241,6 @@ "styled-components": ">= 4.0" } }, - "node_modules/react-redux": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", - "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", - "dependencies": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", - "hoist-non-react-statics": "^3.3.2", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^17.0.2" - }, - "peerDependencies": { - "react": "^16.8.3 || ^17 || ^18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -5561,6 +5695,11 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/terser": { "version": "5.25.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.25.0.tgz", @@ -6833,13 +6972,14 @@ "@eclipse-sirius/sirius-components-tsconfig": "*", "@material-ui/core": "4.12.4", "@material-ui/icons": "4.11.3", - "@ObeoNetwork/gantt-task-react": "0.3.13", + "@ObeoNetwork/gantt-task-react": "0.4.5", "@types/react": "17.0.37", "@vitejs/plugin-react": "4.0.4", "@xstate/react": "1.6.3", "graphql": "16.8.0", "prettier": "2.7.1", "react": "17.0.2", + "react-dnd-html5-backend": "^16.0.1", "rollup-plugin-peer-deps-external": "2.2.4", "typescript": "5.1.6", "vite": "4.4.9", @@ -6851,10 +6991,13 @@ "@eclipse-sirius/sirius-components-core": "*", "@material-ui/core": "4.12.4", "@material-ui/icons": "4.11.3", - "@ObeoNetwork/gantt-task-react": "0.3.13", + "@ObeoNetwork/gantt-task-react": "0.4.5", "@xstate/react": "1.6.3", "graphql": "16.8.0", "react": "17.0.2", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "17.0.2", "xstate": "4.32.1" } }, @@ -6957,7 +7100,7 @@ "@material-ui/core": "4.12.4", "@material-ui/icons": "4.11.3", "@material-ui/lab": "4.0.0-alpha.61", - "@ObeoNetwork/gantt-task-react": "0.3.13", + "@ObeoNetwork/gantt-task-react": "0.4.5", "@ObeoNetwork/react-trello": "2.4.11", "@types/react": "17.0.37", "@types/react-router-dom": "5.3.3", @@ -7036,7 +7179,6 @@ "@material-ui/core": "4.12.4", "@material-ui/icons": "4.11.3", "@material-ui/lab": "4.0.0-alpha.61", - "@ObeoNetwork/gantt-task-react": "0.3.13", "@types/react": "17.0.37", "@types/react-router-dom": "5.3.3", "@xstate/react": "1.6.3", @@ -7837,13 +7979,14 @@ "@eclipse-sirius/sirius-components-tsconfig": "*", "@material-ui/core": "4.12.4", "@material-ui/icons": "4.11.3", - "@ObeoNetwork/gantt-task-react": "0.3.13", + "@ObeoNetwork/gantt-task-react": "0.4.5", "@types/react": "17.0.37", "@vitejs/plugin-react": "4.0.4", "@xstate/react": "1.6.3", "graphql": "16.8.0", "prettier": "2.7.1", "react": "17.0.2", + "react-dnd-html5-backend": "^16.0.1", "rollup-plugin-peer-deps-external": "2.2.4", "typescript": "5.1.6", "vite": "4.4.9", @@ -8009,7 +8152,7 @@ "@material-ui/core": "4.12.4", "@material-ui/icons": "4.11.3", "@material-ui/lab": "4.0.0-alpha.61", - "@ObeoNetwork/gantt-task-react": "0.3.13", + "@ObeoNetwork/gantt-task-react": "0.4.5", "@ObeoNetwork/react-trello": "2.4.11", "@testing-library/jest-dom": "5.14.1", "@testing-library/react": "12.1.2", @@ -8366,6 +8509,56 @@ "dev": true, "optional": true }, + "@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "requires": { + "@floating-ui/utils": "^0.2.1" + } + }, + "@floating-ui/dom": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.1.tgz", + "integrity": "sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==", + "requires": { + "@floating-ui/core": "^1.1.0" + } + }, + "@floating-ui/react": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.19.0.tgz", + "integrity": "sha512-fgYvN4ksCi5OvmPXkyOT8o5a8PSKHMzPHt+9mR6KYWdF16IAjWRLZPAAziI2sznaWT23drRFrYw64wdvYqqaQw==", + "requires": { + "@floating-ui/react-dom": "^1.2.2", + "aria-hidden": "^1.1.3", + "tabbable": "^6.0.1" + } + }, + "@floating-ui/react-dom": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.3.0.tgz", + "integrity": "sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==", + "requires": { + "@floating-ui/dom": "^1.2.1" + }, + "dependencies": { + "@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "requires": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + } + } + }, + "@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, "@graphql-typed-document-node/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", @@ -8722,10 +8915,14 @@ } }, "@ObeoNetwork/gantt-task-react": { - "version": "0.3.13", - "resolved": "https://npm.pkg.github.com/download/@ObeoNetwork/gantt-task-react/0.3.13/0e8a00a6c1c0b8d4e229d208bef8d28ecdad0885", - "integrity": "sha512-OP7aVcv6s/DSPvgqyzyuZ5RERalGM/X32oOGRE0jYmkGvdWqORfuTagXXOYeAE8dGSJJzIJXsoYBXA62i5QnJw==", - "requires": {} + "version": "0.4.5", + "resolved": "https://npm.pkg.github.com/download/@ObeoNetwork/gantt-task-react/0.4.5/fd8dcda99be3da5a3f3630ab786a0d9a137b7729", + "integrity": "sha512-h2I6UTn6cncDS/t1DXpOh2R2h501ejdAvM1FJXt5Bcvaj/q1nhvj+TyGkS+QQb0at/5oHJsiaH8CSKyD15zSNQ==", + "requires": { + "@floating-ui/dom": "1.1.1", + "@floating-ui/react": "0.19.0", + "date-fns": "2.29.3" + } }, "@ObeoNetwork/react-trello": { "version": "2.4.11", @@ -8744,8 +8941,44 @@ "redux-logger": "^3.0.6", "trello-smooth-dnd": "1.0.0", "uuid": "^3.3.2" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-redux": { + "version": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.2.tgz", + "integrity": "sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q==", + "requires": { + "@babel/runtime": "^7.1.2", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4", + "loose-envify": "^1.1.0", + "prop-types": "^15.6.1", + "react-is": "^16.6.0", + "react-lifecycles-compat": "^3.0.0" + } + } } }, + "@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", + "peer": true + }, "@reactflow/background": { "version": "11.3.6", "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.6.tgz", @@ -9208,6 +9441,8 @@ "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "optional": true, + "peer": true, "requires": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -9223,7 +9458,7 @@ "version": "15.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==", - "dev": true + "devOptional": true }, "@types/prop-types": { "version": "15.7.11", @@ -9256,27 +9491,6 @@ "@types/react": "*" } }, - "@types/react-redux": { - "version": "7.1.33", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz", - "integrity": "sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==", - "requires": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - }, - "dependencies": { - "redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "requires": { - "@babel/runtime": "^7.9.2" - } - } - } - }, "@types/react-router": { "version": "5.1.20", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", @@ -9541,6 +9755,14 @@ "color-convert": "^2.0.1" } }, + "aria-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", + "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", + "requires": { + "tslib": "^2.0.0" + } + }, "aria-query": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", @@ -10074,6 +10296,11 @@ "whatwg-url": "^8.0.0" } }, + "date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -10171,6 +10398,16 @@ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, + "dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "requires": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, "dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -10321,6 +10558,12 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "peer": true + }, "fast-equals": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", @@ -11409,6 +11652,28 @@ "object-assign": "^4.1.1" } }, + "react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "peer": true, + "requires": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + } + }, + "react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dev": true, + "requires": { + "dnd-core": "^16.0.1" + } + }, "react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -11477,19 +11742,6 @@ "styled-components": ">= 4.0" } }, - "react-redux": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", - "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", - "requires": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", - "hoist-non-react-statics": "^3.3.2", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^17.0.2" - } - }, "react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -11862,6 +12114,11 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "terser": { "version": "5.25.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.25.0.tgz", diff --git a/packages/gantt/backend/sirius-components-collaborative-gantt/src/main/java/org/eclipse/sirius/components/collaborative/gantt/GanttEventProcessor.java b/packages/gantt/backend/sirius-components-collaborative-gantt/src/main/java/org/eclipse/sirius/components/collaborative/gantt/GanttEventProcessor.java index 0db7501772..c1a0760be4 100644 --- a/packages/gantt/backend/sirius-components-collaborative-gantt/src/main/java/org/eclipse/sirius/components/collaborative/gantt/GanttEventProcessor.java +++ b/packages/gantt/backend/sirius-components-collaborative-gantt/src/main/java/org/eclipse/sirius/components/collaborative/gantt/GanttEventProcessor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023 Obeo. + * Copyright (c) 2023, 2024 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 @@ -36,8 +36,6 @@ import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; -import reactor.core.publisher.Sinks; -import reactor.core.publisher.Sinks.EmitResult; import reactor.core.publisher.Sinks.Many; import reactor.core.publisher.Sinks.One; @@ -57,8 +55,6 @@ public class GanttEventProcessor implements IGanttEventProcessor { private final GanttCreationService ganttCreationService; - private final Many sink = Sinks.many().multicast().directBestEffort(); - private final AtomicReference currentGantt = new AtomicReference<>(); private final GanttEventFlux ganttEventFlux; @@ -134,24 +130,22 @@ private boolean shouldRefresh(ChangeDescription changeDescription) { @Override public Flux getOutputEvents(IInput input) { - return Flux.merge(this.ganttEventFlux.getFlux(input), this.subscriptionManager.getFlux(input)); + return Flux.merge( + this.ganttEventFlux.getFlux(input), + this.subscriptionManager.getFlux(input) + ); } @Override public void dispose() { - String id = null; - if (this.currentGantt.get() != null) { - id = this.currentGantt.get().getId(); - } + String id = Optional.ofNullable(this.currentGantt.get()) + .map(Gantt::id) + .orElse(null); + this.logger.trace("Disposing the gantt event processor {}", id); this.subscriptionManager.dispose(); - EmitResult emitResult = this.sink.tryEmitComplete(); - if (emitResult.isFailure()) { - String pattern = "An error has occurred while marking the publisher as complete: {}"; - this.logger.warn(pattern, emitResult); - } + this.ganttEventFlux.dispose(); } - } diff --git a/packages/gantt/frontend/sirius-components-gantt/package.json b/packages/gantt/frontend/sirius-components-gantt/package.json index 9eb055c4bf..d7042915d1 100644 --- a/packages/gantt/frontend/sirius-components-gantt/package.json +++ b/packages/gantt/frontend/sirius-components-gantt/package.json @@ -31,13 +31,16 @@ }, "peerDependencies": { "@apollo/client": "3.8.1", - "@ObeoNetwork/gantt-task-react": "0.3.13", + "@ObeoNetwork/gantt-task-react": "0.4.5", "@eclipse-sirius/sirius-components-core": "*", "@material-ui/core": "4.12.4", "@material-ui/icons": "4.11.3", "@xstate/react": "1.6.3", "graphql": "16.8.0", "react": "17.0.2", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "17.0.2", "xstate": "4.32.1" }, "devDependencies": { @@ -46,13 +49,14 @@ "@eclipse-sirius/sirius-components-tsconfig": "*", "@material-ui/core": "4.12.4", "@material-ui/icons": "4.11.3", - "@ObeoNetwork/gantt-task-react": "0.3.13", + "@ObeoNetwork/gantt-task-react": "0.4.5", "@types/react": "17.0.37", "@vitejs/plugin-react": "4.0.4", "@xstate/react": "1.6.3", "graphql": "16.8.0", "prettier": "2.7.1", "react": "17.0.2", + "react-dnd-html5-backend": "^16.0.1", "rollup-plugin-peer-deps-external": "2.2.4", "typescript": "5.1.6", "vite": "4.4.9", diff --git a/packages/gantt/frontend/sirius-components-gantt/src/helper/helper.tsx b/packages/gantt/frontend/sirius-components-gantt/src/helper/helper.tsx index e489bf6cfa..dc51a4295f 100644 --- a/packages/gantt/frontend/sirius-components-gantt/src/helper/helper.tsx +++ b/packages/gantt/frontend/sirius-components-gantt/src/helper/helper.tsx @@ -10,12 +10,22 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { Task } from '@ObeoNetwork/gantt-task-react'; -import { TaskType } from '@ObeoNetwork/gantt-task-react/dist/types/public-types'; +import { + Column, + ColumnProps, + DateEndColumn, + DateStartColumn, + Dependency, + EmptyTask, + TaskOrEmpty, + TaskType, + TitleColumn, +} from '@ObeoNetwork/gantt-task-react'; import { GQLGantt, GQLTask, GQLTaskDetail, SelectableTask } from '../graphql/subscription/GanttSubscription.types'; +import { TaskListColumnEnum } from '../representation/Gantt.types'; -export function getTaskFromGQLTask(gQLTasks: GQLTask[], parentId: string): Task[] { - const tasks: Task[] = []; +export function getTaskFromGQLTask(gQLTasks: GQLTask[], parentId: string): TaskOrEmpty[] { + const tasks: TaskOrEmpty[] = []; gQLTasks.forEach((gQLTask: GQLTask) => { let type: TaskType = 'task'; const isProject = gQLTask.subTasks && gQLTask.subTasks.length > 0; @@ -24,23 +34,36 @@ export function getTaskFromGQLTask(gQLTasks: GQLTask[], parentId: string): Task[ } else if (gQLTask.detail.startTime === gQLTask.detail.endTime) { type = 'milestone'; } - const task: SelectableTask = { - id: gQLTask.id, - name: gQLTask.detail.name, - progress: gQLTask.detail.progress, - type, - dependencies: gQLTask.dependencyTaskIds, - project: parentId, - hideChildren: false, - targetObjectId: gQLTask.targetObjectId, - targetObjectKind: gQLTask.targetObjectKind, - targetObjectLabel: gQLTask.targetObjectLabel, - }; - if (!!gQLTask.detail.startTime) { - task.start = new Date(gQLTask.detail.startTime); - } - if (!!gQLTask.detail.endTime) { - task.end = new Date(gQLTask.detail.endTime); + const dependencies: Dependency[] = gQLTask.dependencyTaskIds.map((dependencyTaskId) => { + return { + sourceId: dependencyTaskId, + sourceTarget: 'endOfTask', + ownTarget: 'startOfTask', + }; + }); + let task: SelectableTask | EmptyTask; + if (gQLTask.detail.startTime && gQLTask.detail.endTime) { + task = { + id: gQLTask.id, + name: gQLTask.detail.name, + start: new Date(gQLTask.detail.startTime), + end: new Date(gQLTask.detail.endTime), + progress: gQLTask.detail.progress, + type, + dependencies, + parent: parentId, + hideChildren: false, + targetObjectId: gQLTask.targetObjectId, + targetObjectKind: gQLTask.targetObjectKind, + targetObjectLabel: gQLTask.targetObjectLabel, + }; + } else { + task = { + id: gQLTask.id, + name: gQLTask.detail.name, + parent: gQLTask.id, + type: 'empty', + }; } tasks.push(task); @@ -48,7 +71,7 @@ export function getTaskFromGQLTask(gQLTasks: GQLTask[], parentId: string): Task[ if (gQLTask.detail.computeStartEndDynamically) { task.isDisabled = true; } - const children: Task[] = getTaskFromGQLTask(gQLTask.subTasks, gQLTask.id); + const children: TaskOrEmpty[] = getTaskFromGQLTask(gQLTask.subTasks, gQLTask.id); tasks.push(...children); } }); @@ -76,3 +99,54 @@ const findTask = (tasks: GQLTask[], taskId: string): GQLTask | undefined => { }); return foundTask; }; + +const ProgressColumn: React.FC = ({ data: { task } }) => { + if (task.type === 'project' || task.type === 'task') { + return <>{task.progress}%; + } + + return null; +}; + +export const getAllColumns = () => { + const columnEnums = [ + TaskListColumnEnum.NAME, + TaskListColumnEnum.FROM, + TaskListColumnEnum.TO, + TaskListColumnEnum.PROGRESS, + ]; + const columns: Column[] = []; + columnEnums.forEach((columnType) => { + if (columnType === TaskListColumnEnum.NAME) { + columns.push({ + component: TitleColumn, + width: 210, + title: 'Name', + id: TaskListColumnEnum.NAME, + }); + } else if (columnType === TaskListColumnEnum.FROM) { + columns.push({ + component: DateStartColumn, + width: 150, + title: 'Date of start', + id: TaskListColumnEnum.FROM, + }); + } else if (columnType === TaskListColumnEnum.TO) { + columns.push({ + component: DateEndColumn, + width: 150, + title: 'Date of end', + id: TaskListColumnEnum.TO, + }); + } else if (columnType === TaskListColumnEnum.PROGRESS) { + columns.push({ + component: ProgressColumn, + width: 40, + title: 'Progress', + id: TaskListColumnEnum.PROGRESS, + }); + } + }); + + return columns; +}; diff --git a/packages/gantt/frontend/sirius-components-gantt/src/icons/TaskIcon.tsx b/packages/gantt/frontend/sirius-components-gantt/src/icons/TaskIcon.tsx new file mode 100644 index 0000000000..02e6ab6c6e --- /dev/null +++ b/packages/gantt/frontend/sirius-components-gantt/src/icons/TaskIcon.tsx @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import SvgIcon, { SvgIconProps } from '@material-ui/core/SvgIcon'; + +export const TaskIcon = (props: SvgIconProps) => { + return ( + + + + + ); +}; diff --git a/packages/gantt/frontend/sirius-components-gantt/src/palette/ContextualPalette.tsx b/packages/gantt/frontend/sirius-components-gantt/src/palette/ContextualPalette.tsx index 70ab9643a7..d96c162b77 100644 --- a/packages/gantt/frontend/sirius-components-gantt/src/palette/ContextualPalette.tsx +++ b/packages/gantt/frontend/sirius-components-gantt/src/palette/ContextualPalette.tsx @@ -12,10 +12,8 @@ *******************************************************************************/ import IconButton from '@material-ui/core/IconButton'; import { makeStyles } from '@material-ui/core/styles'; -import AddIcon from '@material-ui/icons/Add'; -import CloseIcon from '@material-ui/icons/Close'; import DeleteForeverIcon from '@material-ui/icons/DeleteForever'; -import React from 'react'; +import { TaskIcon } from '../icons/TaskIcon'; import { TaskContextualPaletteProps } from '@ObeoNetwork/gantt-task-react'; import { ContextualPaletteProps } from './ContextualPalette.types'; @@ -23,7 +21,7 @@ import { ContextualPaletteProps } from './ContextualPalette.types'; const useContextualPaletteStyle = makeStyles((theme) => ({ buttonEntries: { display: 'grid', - gridTemplateColumns: `repeat(3, 36px)`, + gridTemplateColumns: `repeat(2, 28px)`, gridTemplateRows: '28px', gridAutoRows: '28px', placeItems: 'center', @@ -40,7 +38,7 @@ const useContextualPaletteStyle = makeStyles((theme) => ({ })); export const getContextalPalette = ({ onCreateTask, onDeleteTask }: ContextualPaletteProps) => { - const ContextualPalette: React.FC = ({ selectedTask, onClose }) => { + const ContextualPalette: React.FC = ({ selectedTask }) => { const classes = useContextualPaletteStyle(); return (
@@ -51,7 +49,7 @@ export const getContextalPalette = ({ onCreateTask, onDeleteTask }: ContextualPa title="Create task" onClick={() => onCreateTask(selectedTask)} data-testid="create-task"> - + - - -
); }; diff --git a/packages/gantt/frontend/sirius-components-gantt/src/representation/Gantt.tsx b/packages/gantt/frontend/sirius-components-gantt/src/representation/Gantt.tsx index a645ea36ea..b0601a9e40 100644 --- a/packages/gantt/frontend/sirius-components-gantt/src/representation/Gantt.tsx +++ b/packages/gantt/frontend/sirius-components-gantt/src/representation/Gantt.tsx @@ -10,20 +10,28 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { - Gantt as GanttDiagram, - Task, - TaskListColumn, - TaskListColumnEnum, - ViewMode, -} from '@ObeoNetwork/gantt-task-react'; +import '@ObeoNetwork/gantt-task-react'; +import { ColorStyles, Column, Gantt as GanttDiagram, Task, TaskOrEmpty, ViewMode } from '@ObeoNetwork/gantt-task-react'; import '@ObeoNetwork/gantt-task-react/dist/style.css'; import { Selection } from '@eclipse-sirius/sirius-components-core'; -import { useState } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import { useRef, useState } from 'react'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import { SelectableTask } from '../graphql/subscription/GanttSubscription.types'; +import { getAllColumns } from '../helper/helper'; import { getContextalPalette } from '../palette/ContextualPalette'; import { Toolbar } from '../toolbar/Toolbar'; -import { GanttProps, GanttState } from './Gantt.types'; +import { GanttProps, GanttState, TaskListColumnEnum } from './Gantt.types'; + +const useGanttStyle = makeStyles((theme) => ({ + dragOver: { + color: theme.palette.primary.main, + }, + ganttContainer: { + backgroundColor: theme.palette.background.default, + }, +})); export const Gantt = ({ editingContextId, @@ -35,17 +43,20 @@ export const Gantt = ({ onExpandCollapse, onDeleteTask, }: GanttProps) => { - const [{ zoomLevel, columns, displayColumns }, setState] = useState({ + const [{ zoomLevel, selectedColumns, columns, displayColumns }, setState] = useState({ zoomLevel: ViewMode.Day, - columns: [TaskListColumnEnum.NAME, TaskListColumnEnum.FROM, TaskListColumnEnum.TO, TaskListColumnEnum.ASSIGNEE], + selectedColumns: [ + TaskListColumnEnum.NAME, + TaskListColumnEnum.FROM, + TaskListColumnEnum.TO, + TaskListColumnEnum.PROGRESS, + ], + columns: getAllColumns(), displayColumns: true, }); - let columnWidth = 65; - if (zoomLevel === ViewMode.Month) { - columnWidth = 300; - } else if (zoomLevel === ViewMode.Week) { - columnWidth = 250; - } + const ganttContainerRef = useRef(null); + + const ganttClasses = useGanttStyle(); const onwheel = (wheelEvent: WheelEvent) => { const deltaY = wheelEvent.deltaY; @@ -69,7 +80,7 @@ export const Gantt = ({ } }; - const handleSelection = (task: Task) => { + const handleSelection = (task: TaskOrEmpty) => { const selectableTask = task as SelectableTask; const newSelection: Selection = { entries: [ @@ -84,16 +95,6 @@ export const Gantt = ({ setSelection(newSelection); }; - const allColumns: TaskListColumn[] = [ - { columntype: TaskListColumnEnum.NAME, columnWidth: '120px' }, - { columntype: TaskListColumnEnum.FROM, columnWidth: '155px' }, - { columntype: TaskListColumnEnum.TO, columnWidth: '155px' }, - { columntype: TaskListColumnEnum.ASSIGNEE, columnWidth: '80px' }, - ]; - const columnsToDisplay = allColumns.filter((column) => { - return columns.includes(column.columntype); - }); - const onChangeZoomLevel = (zoomLevel: ViewMode) => { setState((prevState) => { return { ...prevState, zoomLevel: zoomLevel }; @@ -106,38 +107,63 @@ export const Gantt = ({ }; const onChangeColumns = (columnTypes: TaskListColumnEnum[]) => { setState((prevState) => { - return { ...prevState, columns: columnTypes }; + return { ...prevState, selectedColumns: columnTypes }; }); }; + const handleDeleteTaskOnContextualPalette = (task: Task) => { + onDeleteTask([task]); + }; + + let tableColumns: Column[] = []; + if (displayColumns) { + tableColumns = columns.filter((col) => { + if (col.id != undefined) { + return selectedColumns.map((colEnum) => colEnum as string).includes(col.id); + } + return false; + }); + } + + const colors: Partial = { + taskDragColor: ganttClasses.dragOver, + }; + return ( -
+
- + + date} + roundStartDate={(date: Date) => date} + onWheel={onwheel} + ContextualPalette={getContextalPalette({ + onCreateTask, + onDeleteTask: handleDeleteTaskOnContextualPalette, + onEditTask, + })} + isMoveChildsWithParent={false} + /> +
); }; diff --git a/packages/gantt/frontend/sirius-components-gantt/src/representation/Gantt.types.ts b/packages/gantt/frontend/sirius-components-gantt/src/representation/Gantt.types.ts index 431e2c6134..948b2a49ea 100644 --- a/packages/gantt/frontend/sirius-components-gantt/src/representation/Gantt.types.ts +++ b/packages/gantt/frontend/sirius-components-gantt/src/representation/Gantt.types.ts @@ -11,21 +11,30 @@ * Obeo - initial API and implementation *******************************************************************************/ import { Selection } from '@eclipse-sirius/sirius-components-core'; -import { Task, TaskListColumnEnum, ViewMode } from '@ObeoNetwork/gantt-task-react'; +import { Column, Task, TaskOrEmpty, ViewMode } from '@ObeoNetwork/gantt-task-react'; + +export enum TaskListColumnEnum { + NAME = 'Name', + FROM = 'From', + TO = 'To', + PROGRESS = 'Progress', + ASSIGNEE = 'Assignee', +} export interface GanttState { zoomLevel: ViewMode; - columns: TaskListColumnEnum[]; + selectedColumns: TaskListColumnEnum[]; + columns: Column[]; displayColumns: boolean; } export interface GanttProps { editingContextId: string; representationId: string; - tasks: Task[]; + tasks: TaskOrEmpty[]; setSelection: (selection: Selection) => void; onCreateTask: (Task: Task) => void; - onEditTask: (Task: Task) => void; - onDeleteTask: (Task: Task) => void; + onEditTask: (Task: TaskOrEmpty) => void; + onDeleteTask: (tasks: readonly TaskOrEmpty[]) => void; onExpandCollapse: (Task: Task) => void; } diff --git a/packages/gantt/frontend/sirius-components-gantt/src/representation/GanttRepresentation.tsx b/packages/gantt/frontend/sirius-components-gantt/src/representation/GanttRepresentation.tsx index 66b0e8822e..df5ad931f6 100644 --- a/packages/gantt/frontend/sirius-components-gantt/src/representation/GanttRepresentation.tsx +++ b/packages/gantt/frontend/sirius-components-gantt/src/representation/GanttRepresentation.tsx @@ -10,7 +10,7 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { Task } from '@ObeoNetwork/gantt-task-react'; +import { Task, TaskOrEmpty } from '@ObeoNetwork/gantt-task-react'; import { ApolloError, useMutation, useSubscription } from '@apollo/client'; import { RepresentationComponentProps, useMultiToast, useSelection } from '@eclipse-sirius/sirius-components-core'; import Typography from '@material-ui/core/Typography'; @@ -167,13 +167,13 @@ export const GanttRepresentation = ({ editingContextId, representationId }: Repr handleError(createTaskLoading, createTaskData, createTaskError); }, [createTaskLoading, createTaskData, createTaskError, handleError]); - const handleEditTask = (task: Task) => { + const handleEditTask = (task: TaskOrEmpty) => { const newDetail: GQLTaskDetail = { name: task.name, description: '', - startTime: task.start?.toISOString(), - endTime: task.end?.toISOString(), - progress: task.progress, + startTime: (task as Task)?.start?.toISOString(), + endTime: (task as Task)?.end?.toISOString(), + progress: (task as Task)?.progress, computeStartEndDynamically: task.isDisabled, }; const input: GQLEditGanttTaskInput = { @@ -188,14 +188,17 @@ export const GanttRepresentation = ({ editingContextId, representationId }: Repr updateTask(gantt, task.id, newDetail); editGanttTask({ variables: { input } }); }; - const handleDeleteTask = (task: Task) => { - const input: GQLDeleteGanttTaskInput = { - id: crypto.randomUUID(), - editingContextId, - representationId, - taskId: task.id, - }; - deleteGanttTask({ variables: { input } }); + const handleDeleteTask = (tasks: readonly TaskOrEmpty[]) => { + const taskId = tasks?.at(0)?.id; + if (taskId) { + const input: GQLDeleteGanttTaskInput = { + id: crypto.randomUUID(), + editingContextId, + representationId, + taskId, + }; + deleteGanttTask({ variables: { input } }); + } }; const handleCreateTask = (task: Task) => { const input: GQLCreateGanttTaskInput = { diff --git a/packages/gantt/frontend/sirius-components-gantt/src/toolbar/Toolbar.tsx b/packages/gantt/frontend/sirius-components-gantt/src/toolbar/Toolbar.tsx index 7855f444c7..d97e5fd321 100644 --- a/packages/gantt/frontend/sirius-components-gantt/src/toolbar/Toolbar.tsx +++ b/packages/gantt/frontend/sirius-components-gantt/src/toolbar/Toolbar.tsx @@ -10,7 +10,7 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { TaskListColumnEnum, ViewMode } from '@ObeoNetwork/gantt-task-react'; +import { Task, TaskOrEmpty, ViewMode } from '@ObeoNetwork/gantt-task-react'; import { ShareRepresentationModal } from '@eclipse-sirius/sirius-components-core'; import Checkbox from '@material-ui/core/Checkbox'; import FormControl from '@material-ui/core/FormControl'; @@ -18,14 +18,19 @@ import IconButton from '@material-ui/core/IconButton'; import ListItemText from '@material-ui/core/ListItemText'; import MenuItem from '@material-ui/core/MenuItem'; import Select from '@material-ui/core/Select'; +import Tooltip from '@material-ui/core/Tooltip/Tooltip'; import { makeStyles } from '@material-ui/core/styles'; import { ViewColumn } from '@material-ui/icons'; import AspectRatioIcon from '@material-ui/icons/AspectRatio'; +import FullscreenIcon from '@material-ui/icons/Fullscreen'; +import FullscreenExitIcon from '@material-ui/icons/FullscreenExit'; import ShareIcon from '@material-ui/icons/Share'; import ZoomInIcon from '@material-ui/icons/ZoomIn'; import ZoomOutIcon from '@material-ui/icons/ZoomOut'; import React, { useEffect, useState } from 'react'; +import { TaskListColumnEnum } from '../representation/Gantt.types'; import { ToolbarProps, ToolbarState } from './Toolbar.types'; +import { useFullscreen } from './useFullScreen'; const useToolbarStyles = makeStyles((theme) => ({ toolbar: { @@ -56,22 +61,35 @@ export const Toolbar = ({ onChangeZoomLevel, onChangeDisplayColumns, onChangeColumns, + fullscreenNode, }: ToolbarProps) => { const [state, setState] = useState({ modal: null }); + const { fullscreen, setFullscreen } = useFullscreen(fullscreenNode); const classes = useToolbarStyles(); const onShare = () => setState((prevState) => ({ ...prevState, modal: 'share' })); const closeModal = () => setState((prevState) => ({ ...prevState, modal: null })); - + const isTask = (task: TaskOrEmpty): task is Task => { + return task.type !== 'empty'; + }; const onFitToScreen = () => { const minTime = Math.min.apply( null, - tasks.filter((task) => Boolean(task.start)).map((task) => (task.start ? task.start.getTime() : 0)) + tasks + .filter((t) => isTask(t)) + .map((t) => t as Task) + .filter((task) => Boolean(task.start)) + .map((task) => (task.start ? task.start.getTime() : 0)) ); const maxTime = Math.max.apply( null, - tasks.filter((task) => Boolean(task.end)).map((task) => (task.end ? task.end.getTime() : 0)) + + tasks + .filter((t) => isTask(t)) + .map((t) => t as Task) + .filter((task) => Boolean(task.end)) + .map((task) => (task.end ? task.end.getTime() : 0)) ); const fullTime: number = (maxTime - minTime) / 1000 / 3600; @@ -144,12 +162,33 @@ export const Toolbar = ({ { type: TaskListColumnEnum.NAME, name: 'Name' }, { type: TaskListColumnEnum.FROM, name: 'From' }, { type: TaskListColumnEnum.TO, name: 'To' }, - { type: TaskListColumnEnum.ASSIGNEE, name: 'Assignee' }, + { type: TaskListColumnEnum.PROGRESS, name: 'Progress' }, ]; return ( <>
+ {fullscreen ? ( + + setFullscreen(false)}> + + + + ) : ( + + setFullscreen(true)}> + + + + )}