From 7f1f89ef5dcb3de2df327c2942aec925b53b3a33 Mon Sep 17 00:00:00 2001 From: Jerome Gout Date: Fri, 10 Jan 2025 18:31:55 +0100 Subject: [PATCH] [4403] Add support of actions in table rows context menu Bug: https://github.com/eclipse-sirius/sirius-web/issues/4403 Signed-off-by: Jerome Gout --- .../DeleteRowContextMenuEntryExecutor.java | 58 ++++++++ .../PackageTableRowContextMenuProvider.java | 48 +++++++ ...ayaTableRowControllerIntegrationTests.java | 135 +++++++++++++++++- .../tables/TableQueryService.java | 9 +- .../api/IRowContextMenuEntryExecutor.java | 31 ++++ .../api/IRowContextMenuEntryProvider.java | 35 +++++ .../tables/api/ITableQueryService.java | 6 +- .../dto/InvokeRowContextMenuEntryInput.java | 26 ++++ .../tables/dto/RowContextMenuEntry.java | 23 +++ .../tables/dto/RowContextMenuInput.java | 25 ++++ .../dto/RowContextMenuSuccessPayload.java | 27 ++++ ...InvokeRowContextMenuEntryEventHandler.java | 103 +++++++++++++ .../ResetTableRowsHeightEventHandler.java | 5 +- .../ResizeTableColumnEventHandler.java | 5 +- .../handlers/RowContextMenuEventHandler.java | 101 +++++++++++++ .../CollaborativeTablesMessageService.java | 7 +- .../ICollaborativeTableMessageService.java | 9 +- .../tables/messages/MessageConstants.java | 3 +- ...components-collaborative-tables.properties | 3 +- .../src/main/resources/schema/table.graphqls | 19 +++ ...owContextMenuEntryIconURLsDataFetcher.java | 48 +++++++ ...ableDescriptionContextMenuDataFetcher.java | 66 +++++++++ ...nInvokeRowContextMenuEntryDataFetcher.java | 61 ++++++++ ...vokeRowContextMenuEntryMutationRunner.java | 60 ++++++++ .../graphql/RowContextMenuQueryRunner.java | 63 ++++++++ .../representation/TableRepresentation.tsx | 7 +- .../src/rows/RowContextMenu.tsx | 134 +++++++++++++++++ .../RowContextMenu.types.ts} | 23 +-- .../src/rows/useInvokeRowContextMenuEntry.ts | 73 ++++++++++ .../useInvokeRowContextMenuEntry.types.ts | 36 +++++ .../src/rows/useRowContextMenuEntries.ts | 64 +++++++++ .../rows/useRowContextMenuEntries.types.ts | 56 ++++++++ .../src/table/TableContent.tsx | 48 +++---- .../src/table/row/ResizeRowHandler.tsx | 73 ---------- .../src/table/useTableColumns.tsx | 77 ++++++---- 35 files changed, 1414 insertions(+), 153 deletions(-) create mode 100644 packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/DeleteRowContextMenuEntryExecutor.java create mode 100644 packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/PackageTableRowContextMenuProvider.java create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/IRowContextMenuEntryExecutor.java create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/IRowContextMenuEntryProvider.java create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/InvokeRowContextMenuEntryInput.java create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuEntry.java create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuInput.java create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuSuccessPayload.java create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/InvokeRowContextMenuEntryEventHandler.java create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/RowContextMenuEventHandler.java create mode 100644 packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/RowContextMenuEntryIconURLsDataFetcher.java create mode 100644 packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/TableDescriptionContextMenuDataFetcher.java create mode 100644 packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/mutation/MutationInvokeRowContextMenuEntryDataFetcher.java create mode 100644 packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/InvokeRowContextMenuEntryMutationRunner.java create mode 100644 packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/RowContextMenuQueryRunner.java create mode 100644 packages/tables/frontend/sirius-components-tables/src/rows/RowContextMenu.tsx rename packages/tables/frontend/sirius-components-tables/src/{table/row/ResizeRowHandler.types.ts => rows/RowContextMenu.types.ts} (59%) create mode 100644 packages/tables/frontend/sirius-components-tables/src/rows/useInvokeRowContextMenuEntry.ts create mode 100644 packages/tables/frontend/sirius-components-tables/src/rows/useInvokeRowContextMenuEntry.types.ts create mode 100644 packages/tables/frontend/sirius-components-tables/src/rows/useRowContextMenuEntries.ts create mode 100644 packages/tables/frontend/sirius-components-tables/src/rows/useRowContextMenuEntries.types.ts delete mode 100644 packages/tables/frontend/sirius-components-tables/src/table/row/ResizeRowHandler.tsx diff --git a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/DeleteRowContextMenuEntryExecutor.java b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/DeleteRowContextMenuEntryExecutor.java new file mode 100644 index 0000000000..6b50ad7a0a --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/DeleteRowContextMenuEntryExecutor.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.web.papaya.representations.table; + +import java.util.Map; +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.api.ChangeKind; +import org.eclipse.sirius.components.collaborative.tables.api.IRowContextMenuEntryExecutor; +import org.eclipse.sirius.components.core.api.IEditService; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.representations.IStatus; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.tables.Line; +import org.eclipse.sirius.components.tables.Table; +import org.eclipse.sirius.components.tables.descriptions.TableDescription; +import org.springframework.stereotype.Service; + +/** + * This class is the implementation of {@link IRowContextMenuEntryExecutor} for the example Delete row action. + * + * @author Jerome Gout + */ +@Service +public class DeleteRowContextMenuEntryExecutor implements IRowContextMenuEntryExecutor { + + private final IEditService editService; + + private final IObjectService objectService; + + public DeleteRowContextMenuEntryExecutor(IEditService editService, IObjectService objectService) { + this.editService = Objects.requireNonNull(editService); + this.objectService = Objects.requireNonNull(objectService); + } + + @Override + public boolean canExecute(TableDescription tableDescription, String tableId, String rowId, String rowMenuContextEntryId) { + return PackageTableRowContextMenuProvider.DELETE_ID.equals(rowMenuContextEntryId); + } + + @Override + public IStatus execute(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row, String rowMenuContextEntryId) { + this.objectService.getObject(editingContext, row.getTargetObjectId()).ifPresent(this.editService::delete); + return new Success(ChangeKind.SEMANTIC_CHANGE, Map.of()); + } +} diff --git a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/PackageTableRowContextMenuProvider.java b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/PackageTableRowContextMenuProvider.java new file mode 100644 index 0000000000..daff23e0bf --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/PackageTableRowContextMenuProvider.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.web.papaya.representations.table; + +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.tables.api.IRowContextMenuEntryProvider; +import org.eclipse.sirius.components.collaborative.tables.dto.RowContextMenuEntry; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.tables.Line; +import org.eclipse.sirius.components.tables.Table; +import org.eclipse.sirius.components.tables.descriptions.TableDescription; +import org.springframework.stereotype.Service; + +/** + * Example of row context menu entries used inside the papaya package table. + * + * @author Jerome Gout + */ +@Service +public class PackageTableRowContextMenuProvider implements IRowContextMenuEntryProvider { + + public static final String DELETE_ID = "papaya-package-table-delete-row"; + + public static final String DELETE_LABEL = "Delete row"; + + @Override + public boolean canHandle(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row) { + return Objects.equals(tableDescription.getId(), PackageTableRepresentationDescriptionProvider.TABLE_DESCRIPTION_ID); + } + + @Override + public List getRowContextMenuEntries(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row) { + return List.of(new RowContextMenuEntry(DELETE_ID, DELETE_LABEL, List.of("/icons/full/obj16/DeleteTool.svg"))); + } +} diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableRowControllerIntegrationTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableRowControllerIntegrationTests.java index a6084eb099..46b85d56d4 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableRowControllerIntegrationTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableRowControllerIntegrationTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 CEA LIST. + * Copyright (c) 2024, 2025 CEA LIST. * 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 @@ -15,9 +15,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; +import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.JsonPath; import java.time.Duration; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; @@ -26,15 +29,19 @@ import org.eclipse.sirius.components.collaborative.dto.CreateRepresentationInput; import org.eclipse.sirius.components.collaborative.tables.TableEventInput; import org.eclipse.sirius.components.collaborative.tables.TableRefreshedEventPayload; +import org.eclipse.sirius.components.collaborative.tables.dto.InvokeRowContextMenuEntryInput; import org.eclipse.sirius.components.collaborative.tables.dto.ResetTableRowsHeightInput; import org.eclipse.sirius.components.collaborative.tables.dto.ResizeTableRowInput; import org.eclipse.sirius.components.core.api.SuccessPayload; import org.eclipse.sirius.components.tables.Line; +import org.eclipse.sirius.components.tables.tests.graphql.InvokeRowContextMenuEntryMutationRunner; import org.eclipse.sirius.components.tables.tests.graphql.ResetTableRowsHeightMutationRunner; import org.eclipse.sirius.components.tables.tests.graphql.ResizeTableRowMutationRunner; +import org.eclipse.sirius.components.tables.tests.graphql.RowContextMenuQueryRunner; import org.eclipse.sirius.components.tables.tests.graphql.TableEventSubscriptionRunner; import org.eclipse.sirius.web.AbstractIntegrationTests; import org.eclipse.sirius.web.data.PapayaIdentifiers; +import org.eclipse.sirius.web.papaya.representations.table.PackageTableRowContextMenuProvider; import org.eclipse.sirius.web.tests.services.api.IGivenCommittedTransaction; import org.eclipse.sirius.web.tests.services.api.IGivenCreatedTableSubscription; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; @@ -76,6 +83,12 @@ public class PapayaTableRowControllerIntegrationTests extends AbstractIntegratio @Autowired private ResetTableRowsHeightMutationRunner resetTableRowsHeightMutationRunner; + @Autowired + private InvokeRowContextMenuEntryMutationRunner invokeRowContextMenuEntryMutationRunner; + + @Autowired + private RowContextMenuQueryRunner rowContextMenuQueryRunner; + @Autowired private TableEventSubscriptionRunner tableEventSubscriptionRunner; @@ -211,4 +224,124 @@ public void givenTableWithAResizedRowWhenRowAResetRowsHeightMutationIsTriggeredT .thenCancel() .verify(Duration.ofSeconds(10)); } + + @Test + @DisplayName("Given a table, when row context menu entries are queried, then the correct entries are returned") + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void giveATableWhenRowContextMenuEntriesAreQueriedThenTheCorrectEntriesAreReturned() { + this.givenCommittedTransaction.commit(); + + var tableEventInput = new TableEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), PapayaIdentifiers.PAPAYA_PACKAGE_TABLE_REPRESENTATION.toString()); + var flux = this.tableEventSubscriptionRunner.run(tableEventInput); + + TestTransaction.flagForCommit(); + TestTransaction.end(); + TestTransaction.start(); + + var tableId = new AtomicReference(); + var rowId = new AtomicReference(); + + Consumer initialTableContentConsumer = payload -> Optional.of(payload) + .filter(DataFetcherResult.class::isInstance) + .map(DataFetcherResult.class::cast) + .map(DataFetcherResult::getData) + .filter(TableRefreshedEventPayload.class::isInstance) + .map(TableRefreshedEventPayload.class::cast) + .map(TableRefreshedEventPayload::table) + .ifPresentOrElse(table -> { + assertThat(table).isNotNull(); + assertThat(table.getLines()).hasSize(2); + tableId.set(table.getId()); + rowId.set(table.getLines().get(0).getId()); + }, () -> fail(MISSING_TABLE)); + + Runnable getContextMenuActions = () -> { + Map variables = Map.of( + "editingContextId", PapayaIdentifiers.PAPAYA_PROJECT.toString(), + "representationId", tableId.get(), + "tableId", tableId.get(), + "rowId", rowId.get().toString() + ); + var result = this.rowContextMenuQueryRunner.run(variables); + Object document = Configuration.defaultConfiguration().jsonProvider().parse(result); + + List actionLabels = JsonPath.read(document, "$.data.viewer.editingContext.representation.description.contextMenu[*].label"); + assertThat(actionLabels).isNotEmpty().hasSize(1); + assertThat(actionLabels.get(0)).isEqualTo("Delete row"); + }; + + StepVerifier.create(flux) + .consumeNextWith(initialTableContentConsumer) + .then(getContextMenuActions) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + + @Test + @DisplayName("Given a table, when a row context menu entry is triggered, then the entry is correctly invoked ") + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void giveATableWhenARowContextMenuEntryIsTriggeredThenTheEntryIsCorrectlyInvoked() { + this.givenCommittedTransaction.commit(); + + var tableEventInput = new TableEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), PapayaIdentifiers.PAPAYA_PACKAGE_TABLE_REPRESENTATION.toString()); + var flux = this.tableEventSubscriptionRunner.run(tableEventInput); + + TestTransaction.flagForCommit(); + TestTransaction.end(); + TestTransaction.start(); + + var tableId = new AtomicReference(); + var rowId = new AtomicReference(); + + Consumer initialTableContentConsumer = payload -> Optional.of(payload) + .filter(DataFetcherResult.class::isInstance) + .map(DataFetcherResult.class::cast) + .map(DataFetcherResult::getData) + .filter(TableRefreshedEventPayload.class::isInstance) + .map(TableRefreshedEventPayload.class::cast) + .map(TableRefreshedEventPayload::table) + .ifPresentOrElse(table -> { + assertThat(table).isNotNull(); + assertThat(table.getLines()).hasSize(2); + tableId.set(table.getId()); + rowId.set(table.getLines().get(0).getId()); + }, () -> fail(MISSING_TABLE)); + + Runnable invokeDeleteRowAction = () -> { + var invokeRowContextMenuEntryInput = new InvokeRowContextMenuEntryInput( + UUID.randomUUID(), + PapayaIdentifiers.PAPAYA_PROJECT.toString(), + tableId.get(), + tableId.get(), + rowId.get(), + PackageTableRowContextMenuProvider.DELETE_ID + ); + var result = this.invokeRowContextMenuEntryMutationRunner.run(invokeRowContextMenuEntryInput); + + String typename = JsonPath.read(result, "$.data.invokeRowContextMenuEntry.__typename"); + assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); + }; + + Consumer updatedTableContentConsumer = payload -> Optional.of(payload) + .filter(DataFetcherResult.class::isInstance) + .map(DataFetcherResult.class::cast) + .map(DataFetcherResult::getData) + .filter(TableRefreshedEventPayload.class::isInstance) + .map(TableRefreshedEventPayload.class::cast) + .map(TableRefreshedEventPayload::table) + .ifPresentOrElse(table -> { + assertThat(table).isNotNull(); + assertThat(table.getLines()).hasSize(1); + }, () -> fail(MISSING_TABLE)); + + StepVerifier.create(flux) + .consumeNextWith(initialTableContentConsumer) + .then(invokeDeleteRowAction) + .consumeNextWith(updatedTableContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } } diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/TableQueryService.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/TableQueryService.java index 1096c91a8f..53c3270756 100644 --- a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/TableQueryService.java +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/TableQueryService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -43,6 +43,13 @@ public Optional findLineByCellId(Table table, UUID cellId) { .findFirst(); } + @Override + public Optional findLineById(Table table, UUID rowId) { + return table.getLines().stream() + .filter(row -> Objects.equals(rowId, row.getId())) + .findFirst(); + } + @Override public Optional findColumnById(Table table, UUID columnId) { return table.getColumns().stream() diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/IRowContextMenuEntryExecutor.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/IRowContextMenuEntryExecutor.java new file mode 100644 index 0000000000..31a3b01842 --- /dev/null +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/IRowContextMenuEntryExecutor.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.components.collaborative.tables.api; + +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.representations.IStatus; +import org.eclipse.sirius.components.tables.Line; +import org.eclipse.sirius.components.tables.Table; +import org.eclipse.sirius.components.tables.descriptions.TableDescription; + +/** + * Interface allowing to perform row context menu entries. + * + * @author Jerome Gout + */ +public interface IRowContextMenuEntryExecutor { + boolean canExecute(TableDescription tableDescription, String tableId, String rowId, String rowMenuContextEntryId); + + IStatus execute(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row, String rowMenuContextEntryId); +} diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/IRowContextMenuEntryProvider.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/IRowContextMenuEntryProvider.java new file mode 100644 index 0000000000..fb80fa873c --- /dev/null +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/IRowContextMenuEntryProvider.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.components.collaborative.tables.api; + +import java.util.List; + +import org.eclipse.sirius.components.collaborative.tables.dto.RowContextMenuEntry; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.tables.Line; +import org.eclipse.sirius.components.tables.Table; +import org.eclipse.sirius.components.tables.descriptions.TableDescription; + +/** + * Interface allowing to provide context menu entries in a table row. + * + * @author Jerome Gout + */ +public interface IRowContextMenuEntryProvider { + + boolean canHandle(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row); + + List getRowContextMenuEntries(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row); + +} diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/ITableQueryService.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/ITableQueryService.java index bb2eb84fba..322b4a8d1c 100644 --- a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/ITableQueryService.java +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/api/ITableQueryService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -29,7 +29,9 @@ public interface ITableQueryService { Optional findCellById(Table table, UUID cellId); - Optional findLineByCellId(Table table, UUID lineId); + Optional findLineByCellId(Table table, UUID cellId); + + Optional findLineById(Table table, UUID lineId); Optional findColumnById(Table table, UUID columnId); diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/InvokeRowContextMenuEntryInput.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/InvokeRowContextMenuEntryInput.java new file mode 100644 index 0000000000..f8b58310cb --- /dev/null +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/InvokeRowContextMenuEntryInput.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.components.collaborative.tables.dto; + +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.tables.api.ITableInput; + +/** + * The input object for the row context menu entry invocation mutation. + * + * @author Jerome Gout + */ +public record InvokeRowContextMenuEntryInput(UUID id, String editingContextId, String representationId, String tableId, UUID rowId, String menuEntryId) implements ITableInput { +} \ No newline at end of file diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuEntry.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuEntry.java new file mode 100644 index 0000000000..8e9a44642e --- /dev/null +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuEntry.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.components.collaborative.tables.dto; + +import java.util.List; + +/** + * Entry in the table row context menu. + * + * @author Jerome Gout + */ +public record RowContextMenuEntry(String id, String label, List iconURLs) { } diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuInput.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuInput.java new file mode 100644 index 0000000000..72913204f1 --- /dev/null +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuInput.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.components.collaborative.tables.dto; + +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.tables.api.ITableInput; + +/** + * The input of the table row context menu query. + * + * @author Jerome Gout + */ +public record RowContextMenuInput(UUID id, String editingContextId, String representationId, String tableId, UUID rowId) implements ITableInput { } \ No newline at end of file diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuSuccessPayload.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuSuccessPayload.java new file mode 100644 index 0000000000..04e6bd7472 --- /dev/null +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/RowContextMenuSuccessPayload.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.components.collaborative.tables.dto; + +import java.util.List; +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IPayload; + +/** + * Payload used to tell the frontend the list of context menu action. + * + * @author Jerome Gout + */ +public record RowContextMenuSuccessPayload(UUID id, List entries) implements IPayload { +} diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/InvokeRowContextMenuEntryEventHandler.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/InvokeRowContextMenuEntryEventHandler.java new file mode 100644 index 0000000000..a8b7c60c03 --- /dev/null +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/InvokeRowContextMenuEntryEventHandler.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ +package org.eclipse.sirius.components.collaborative.tables.handlers; + +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.api.ChangeDescription; +import org.eclipse.sirius.components.collaborative.api.ChangeKind; +import org.eclipse.sirius.components.collaborative.api.Monitoring; +import org.eclipse.sirius.components.collaborative.tables.api.IRowContextMenuEntryExecutor; +import org.eclipse.sirius.components.collaborative.tables.api.ITableContext; +import org.eclipse.sirius.components.collaborative.tables.api.ITableEventHandler; +import org.eclipse.sirius.components.collaborative.tables.api.ITableInput; +import org.eclipse.sirius.components.collaborative.tables.api.ITableQueryService; +import org.eclipse.sirius.components.collaborative.tables.dto.InvokeRowContextMenuEntryInput; +import org.eclipse.sirius.components.collaborative.tables.messages.ICollaborativeTableMessageService; +import org.eclipse.sirius.components.core.api.ErrorPayload; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.core.api.SuccessPayload; +import org.eclipse.sirius.components.representations.Failure; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.tables.Line; +import org.eclipse.sirius.components.tables.descriptions.TableDescription; +import org.springframework.stereotype.Service; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import reactor.core.publisher.Sinks; + +/** + * Handle row context menu entry invocation event. + * + * @author Jerome Gout + */ +@Service +public class InvokeRowContextMenuEntryEventHandler implements ITableEventHandler { + + private final ICollaborativeTableMessageService messageService; + + private final ITableQueryService tableQueryService; + + private final Counter counter; + + private final List rowContextMenuEntryExecutors; + + public InvokeRowContextMenuEntryEventHandler(ICollaborativeTableMessageService messageService, ITableQueryService tableQueryService, MeterRegistry meterRegistry, List rowContextMenuEntryExecutors) { + this.messageService = messageService; + this.tableQueryService = Objects.requireNonNull(tableQueryService); + this.rowContextMenuEntryExecutors = Objects.requireNonNull(rowContextMenuEntryExecutors); + this.counter = Counter.builder(Monitoring.EVENT_HANDLER) + .tag(Monitoring.NAME, this.getClass().getSimpleName()) + .register(meterRegistry); + } + + @Override + public boolean canHandle(ITableInput tableInput) { + return tableInput instanceof InvokeRowContextMenuEntryInput; + } + + @Override + public void handle(Sinks.One payloadSink, Sinks.Many changeDescriptionSink, IEditingContext editingContext, ITableContext tableContext, TableDescription tableDescription, ITableInput tableInput) { + this.counter.increment(); + + ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, tableInput.representationId(), tableInput); + String message = this.messageService.invalidInput(tableInput.getClass().getSimpleName(), InvokeRowContextMenuEntryInput.class.getSimpleName()); + IPayload payload = new ErrorPayload(tableInput.id(), message); + + if (tableInput instanceof InvokeRowContextMenuEntryInput invokeRowContextMenuEntryInput) { + var optionalRow = this.tableQueryService.findLineById(tableContext.getTable(), invokeRowContextMenuEntryInput.rowId()); + if (optionalRow.isPresent()) { + Line row = optionalRow.get(); + var status = this.rowContextMenuEntryExecutors.stream() + .filter(executor -> executor.canExecute(tableDescription, tableContext.getTable().getId(), invokeRowContextMenuEntryInput.rowId().toString(), + invokeRowContextMenuEntryInput.menuEntryId())) + .findFirst() + .map(executor -> executor.execute(editingContext, tableDescription, tableContext.getTable(), row, invokeRowContextMenuEntryInput.menuEntryId())) + .orElseGet(() -> new Failure(this.messageService.noRowContextMenuEntryExecutor())); + + if (status instanceof Success success) { + changeDescription = new ChangeDescription(success.getChangeKind(), tableInput.representationId(), tableInput, success.getParameters()); + payload = new SuccessPayload(tableInput.id()); + } else if (status instanceof Failure failure) { + payload = new ErrorPayload(tableInput.id(), failure.getMessages()); + } + } + } + + payloadSink.tryEmitValue(payload); + changeDescriptionSink.tryEmitNext(changeDescription); + } +} diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/ResetTableRowsHeightEventHandler.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/ResetTableRowsHeightEventHandler.java index 83462200da..be1408a225 100644 --- a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/ResetTableRowsHeightEventHandler.java +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/ResetTableRowsHeightEventHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 CEA LIST. + * Copyright (c) 2024, 2025 CEA LIST. * 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 @@ -19,7 +19,6 @@ import org.eclipse.sirius.components.collaborative.tables.api.ITableContext; import org.eclipse.sirius.components.collaborative.tables.api.ITableEventHandler; import org.eclipse.sirius.components.collaborative.tables.api.ITableInput; -import org.eclipse.sirius.components.collaborative.tables.dto.EditTextfieldCellInput; import org.eclipse.sirius.components.collaborative.tables.dto.ResetTableRowsHeightInput; import org.eclipse.sirius.components.collaborative.tables.messages.ICollaborativeTableMessageService; import org.eclipse.sirius.components.core.api.ErrorPayload; @@ -63,7 +62,7 @@ public void handle(Sinks.One payloadSink, Sinks.Many payloadSink, Sinks.Many contextMenuEntryProviders; + + public RowContextMenuEventHandler(ICollaborativeTableMessageService messageService, ITableQueryService tableQueryService, MeterRegistry meterRegistry, List contextMenuEntryProviders) { + this.messageService = Objects.requireNonNull(messageService); + this.tableQueryService = Objects.requireNonNull(tableQueryService); + this.contextMenuEntryProviders = Objects.requireNonNull(contextMenuEntryProviders); + + this.counter = Counter.builder(Monitoring.EVENT_HANDLER) + .tag(Monitoring.NAME, this.getClass().getSimpleName()) + .register(meterRegistry); + } + + @Override + public boolean canHandle(ITableInput tableInput) { + return tableInput instanceof RowContextMenuInput; + } + + @Override + public void handle(Sinks.One payloadSink, Sinks.Many changeDescriptionSink, IEditingContext editingContext, ITableContext tableContext, TableDescription tableDescription, ITableInput tableInput) { + this.counter.increment(); + + String message = this.messageService.invalidInput(tableInput.getClass().getSimpleName(), RowContextMenuInput.class.getSimpleName()); + IPayload payload = new ErrorPayload(tableInput.id(), message); + ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, tableInput.representationId(), tableInput); + + if (tableInput instanceof RowContextMenuInput input) { + var optionalRow = this.tableQueryService.findLineById(tableContext.getTable(), input.rowId()); + if (optionalRow.isPresent()) { + Line row = optionalRow.get(); + + var entries = this.contextMenuEntryProviders.stream() + .filter(provider -> provider.canHandle(editingContext, tableDescription, tableContext.getTable(), row)) + .flatMap(provider -> provider.getRowContextMenuEntries(editingContext, tableDescription, tableContext.getTable(), row).stream()) + .filter(Objects::nonNull) + .sorted(Comparator.comparing(RowContextMenuEntry::label)) + .toList(); + + payload = new RowContextMenuSuccessPayload(tableInput.id(), entries); + } + } + + changeDescriptionSink.tryEmitNext(changeDescription); + payloadSink.tryEmitValue(payload); + } +} diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/CollaborativeTablesMessageService.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/CollaborativeTablesMessageService.java index 0e5a1c30e2..832e12c780 100644 --- a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/CollaborativeTablesMessageService.java +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/CollaborativeTablesMessageService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -37,6 +37,11 @@ public String invalidInput(String expectedInputTypeName, String receivedInputTyp return this.messageSourceAccessor.getMessage(MessageConstants.INVALID_INPUT, new Object[] {expectedInputTypeName, receivedInputTypeName}); } + @Override + public String noRowContextMenuEntryExecutor() { + return this.messageSourceAccessor.getMessage(MessageConstants.NO_ROW_CONTEXT_MENU_ENTRY_FOUND); + } + @Override public String noHandlerFound() { return this.messageSourceAccessor.getMessage(MessageConstants.NO_HANDLER_FOUND); diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/ICollaborativeTableMessageService.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/ICollaborativeTableMessageService.java index 74940f6d62..1995bdb923 100644 --- a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/ICollaborativeTableMessageService.java +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/ICollaborativeTableMessageService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -21,6 +21,8 @@ public interface ICollaborativeTableMessageService { String invalidInput(String expectedInputTypeName, String receivedInputTypeName); + String noRowContextMenuEntryExecutor(); + String noHandlerFound(); /** @@ -35,6 +37,11 @@ public String invalidInput(String expectedInputTypeName, String receivedInputTyp return "invalidInput"; } + @Override + public String noRowContextMenuEntryExecutor() { + return "noRowContextMenuEntryFound"; + } + @Override public String noHandlerFound() { return "noHandlerFound"; diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/MessageConstants.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/MessageConstants.java index 20ce98cbd9..748008b32b 100644 --- a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/MessageConstants.java +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/messages/MessageConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -21,6 +21,7 @@ public final class MessageConstants { public static final String INVALID_INPUT = "INVALID_INPUT"; public static final String NO_HANDLER_FOUND = "NO_HANDLER_FOUND"; + public static final String NO_ROW_CONTEXT_MENU_ENTRY_FOUND = "NO_ROW_CONTEXT_MENU_ENTRY_FOUND"; private MessageConstants() { // Prevent instantiation diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/messages/sirius-components-collaborative-tables.properties b/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/messages/sirius-components-collaborative-tables.properties index ac5dc3fb21..3099ffb5dc 100644 --- a/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/messages/sirius-components-collaborative-tables.properties +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/messages/sirius-components-collaborative-tables.properties @@ -1,5 +1,5 @@ ################################################################################################ -# Copyright (c) 2021 Obeo. All Rights Reserved. +# Copyright (c) 2021, 2025 Obeo. All Rights Reserved. # This software and the attached documentation are the exclusive ownership # of its authors and was conceded to the profit of Obeo SARL. # This software and the attached documentation are protected under the rights @@ -15,3 +15,4 @@ ################################################################################################ INVALID_INPUT=Invalid input type, "{0}" has been received while "{1}" was expected NO_HANDLER_FOUND=No handler found +NO_ROW_CONTEXT_MENU_ENTRY_FOUND=No existing action for this entry menu diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/schema/table.graphqls b/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/schema/table.graphqls index 31b5f52384..8260b8379b 100644 --- a/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/schema/table.graphqls +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/schema/table.graphqls @@ -132,6 +132,13 @@ type IconLabelCell implements Cell { type TableDescription implements RepresentationDescription { id: ID! label: String! + contextMenu(tableId: ID!, rowId: ID!): [RowContextMenuEntry!]! +} + +type RowContextMenuEntry { + id: ID! + label: String! + iconURLs: [String!]! } extend type Mutation { @@ -145,6 +152,7 @@ extend type Mutation { resetTableRowsHeight(input: ResetTableRowsHeightInput!): ResetTableRowsHeightPayload! changeGlobalFilterValue(input: ChangeGlobalFilterValueInput!): ChangeGlobalFilterValuePayload! changeColumnFilter(input: ChangeColumnFilterInput!): ChangeColumnFilterPayload! + invokeRowContextMenuEntry(input: InvokeRowContextMenuEntryInput!): InvokeRowContextMenuEntryPayload! } input EditCheckboxCellInput { @@ -261,3 +269,14 @@ input ResetTableRowsHeightInput { } union ResetTableRowsHeightPayload = ErrorPayload | SuccessPayload + +input InvokeRowContextMenuEntryInput { + id: ID! + editingContextId: ID! + representationId: ID! + tableId: ID! + rowId: ID! + menuEntryId: ID! +} + +union InvokeRowContextMenuEntryPayload = ErrorPayload | SuccessPayload diff --git a/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/RowContextMenuEntryIconURLsDataFetcher.java b/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/RowContextMenuEntryIconURLsDataFetcher.java new file mode 100644 index 0000000000..bc3ce8d383 --- /dev/null +++ b/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/RowContextMenuEntryIconURLsDataFetcher.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ +package org.eclipse.sirius.components.tables.graphql.datafetchers; + +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher; +import org.eclipse.sirius.components.collaborative.tables.dto.RowContextMenuEntry; +import org.eclipse.sirius.components.core.api.IImageURLSanitizer; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.URLConstants; + +import graphql.schema.DataFetchingEnvironment; + +/** + * The data fetcher used to concatenate the server image URL to the icon of context menu entry of row. + * + * @author Jerome Gout + */ +@QueryDataFetcher(type = "RowContextMenuEntry", field = "iconURLs") +public class RowContextMenuEntryIconURLsDataFetcher implements IDataFetcherWithFieldCoordinates> { + + private final IImageURLSanitizer imageURLSanitizer; + + public RowContextMenuEntryIconURLsDataFetcher(IImageURLSanitizer imageURLSanitizer) { + this.imageURLSanitizer = Objects.requireNonNull(imageURLSanitizer); + } + + @Override + public List get(DataFetchingEnvironment environment) throws Exception { + RowContextMenuEntry source = environment.getSource(); + + return source.iconURLs().stream() + .map(url -> this.imageURLSanitizer.sanitize(URLConstants.IMAGE_BASE_PATH, url)) + .toList(); + } +} diff --git a/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/TableDescriptionContextMenuDataFetcher.java b/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/TableDescriptionContextMenuDataFetcher.java new file mode 100644 index 0000000000..bea848379e --- /dev/null +++ b/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/TableDescriptionContextMenuDataFetcher.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.components.tables.graphql.datafetchers; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorRegistry; +import org.eclipse.sirius.components.collaborative.tables.dto.RowContextMenuEntry; +import org.eclipse.sirius.components.collaborative.tables.dto.RowContextMenuInput; +import org.eclipse.sirius.components.collaborative.tables.dto.RowContextMenuSuccessPayload; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.LocalContextConstants; + +import graphql.schema.DataFetchingEnvironment; + +/** + * Used to retrieve the table description context menu. + * + * @author Jerome Gout + */ +@QueryDataFetcher(type = "TableDescription", field = "contextMenu") +public class TableDescriptionContextMenuDataFetcher implements IDataFetcherWithFieldCoordinates>> { + + private static final String TABLE_ID_ARGUMENT = "tableId"; + private static final String ROW_ID_ARGUMENT = "rowId"; + + private final IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry; + + public TableDescriptionContextMenuDataFetcher(IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry) { + this.editingContextEventProcessorRegistry = Objects.requireNonNull(editingContextEventProcessorRegistry); + } + + @Override + public CompletableFuture> get(DataFetchingEnvironment environment) throws Exception { + Map localContext = environment.getLocalContext(); + String editingContextId = Optional.ofNullable(localContext.get(LocalContextConstants.EDITING_CONTEXT_ID)).map(Object::toString).orElse(null); + String representationId = Optional.ofNullable(localContext.get(LocalContextConstants.REPRESENTATION_ID)).map(Object::toString).orElse(null); + String tableId = environment.getArgument(TABLE_ID_ARGUMENT); + String rowId = environment.getArgument(ROW_ID_ARGUMENT); + + assert rowId != null; + var input = new RowContextMenuInput(UUID.randomUUID(), editingContextId, representationId, tableId, UUID.fromString(rowId)); + return this.editingContextEventProcessorRegistry.dispatchEvent(editingContextId, input) + .filter(RowContextMenuSuccessPayload.class::isInstance) + .map(RowContextMenuSuccessPayload.class::cast) + .map(RowContextMenuSuccessPayload::entries) + .toFuture(); + } +} diff --git a/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/mutation/MutationInvokeRowContextMenuEntryDataFetcher.java b/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/mutation/MutationInvokeRowContextMenuEntryDataFetcher.java new file mode 100644 index 0000000000..f44f84a74d --- /dev/null +++ b/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/mutation/MutationInvokeRowContextMenuEntryDataFetcher.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.components.tables.graphql.datafetchers.mutation; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.sirius.components.annotations.spring.graphql.MutationDataFetcher; +import org.eclipse.sirius.components.collaborative.tables.dto.InvokeRowContextMenuEntryInput; +import org.eclipse.sirius.components.collaborative.tables.dto.ResizeTableRowInput; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.IEditingContextDispatcher; +import org.eclipse.sirius.components.graphql.api.IExceptionWrapper; + +import graphql.schema.DataFetchingEnvironment; + +/** + * Data fetcher used to invoke an entry of the context menu of a table row. + * + * @author Jerome Gout + */ +@MutationDataFetcher(type = "Mutation", field = "invokeRowContextMenuEntry") +public class MutationInvokeRowContextMenuEntryDataFetcher implements IDataFetcherWithFieldCoordinates> { + + private static final String INPUT_ARGUMENT = "input"; + + private final ObjectMapper objectMapper; + + private final IExceptionWrapper exceptionWrapper; + + private final IEditingContextDispatcher editingContextDispatcher; + + public MutationInvokeRowContextMenuEntryDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEditingContextDispatcher editingContextDispatcher) { + this.objectMapper = Objects.requireNonNull(objectMapper); + this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper); + this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher); + } + + @Override + public CompletableFuture get(DataFetchingEnvironment environment) throws Exception { + Object argument = environment.getArgument(INPUT_ARGUMENT); + var input = this.objectMapper.convertValue(argument, InvokeRowContextMenuEntryInput.class); + + return this.exceptionWrapper.wrapMono(() -> this.editingContextDispatcher.dispatchMutation(input.editingContextId(), input), input).toFuture(); + } + +} diff --git a/packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/InvokeRowContextMenuEntryMutationRunner.java b/packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/InvokeRowContextMenuEntryMutationRunner.java new file mode 100644 index 0000000000..4bd35783e6 --- /dev/null +++ b/packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/InvokeRowContextMenuEntryMutationRunner.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ +package org.eclipse.sirius.components.tables.tests.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.tables.dto.InvokeRowContextMenuEntryInput; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.springframework.stereotype.Service; + +/** + * Used to invoke a row context menu entry with the GraphQL API. + * + * @author Jerome Gout + */ +@Service +public class InvokeRowContextMenuEntryMutationRunner implements IMutationRunner { + + private static final String INVOKE_ROW_CONTEXT_MENU_ENTRY_MUTATION = """ + mutation invokeRowContextMenuEntry($input: InvokeRowContextMenuEntryInput!) { + invokeRowContextMenuEntry(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on SuccessPayload { + messages { + body + level + } + } + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public InvokeRowContextMenuEntryMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public String run(InvokeRowContextMenuEntryInput input) { + return this.graphQLRequestor.execute(INVOKE_ROW_CONTEXT_MENU_ENTRY_MUTATION, input); + } +} diff --git a/packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/RowContextMenuQueryRunner.java b/packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/RowContextMenuQueryRunner.java new file mode 100644 index 0000000000..2d643f1aec --- /dev/null +++ b/packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/RowContextMenuQueryRunner.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 + *******************************************************************************/ + +package org.eclipse.sirius.components.tables.tests.graphql; + +import java.util.Map; +import java.util.Objects; + +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IQueryRunner; +import org.springframework.stereotype.Service; + +/** + * Used to call a row context menu graphQL query for testing purpose. + * + * @author Jerome Gout + */ +@Service +public class RowContextMenuQueryRunner implements IQueryRunner { + + private static final String ROW_CONTEXT_MENU_QUERY = """ + query rowContextMenuQuery($editingContextId: ID!, $representationId: ID!, $tableId: ID!, $rowId: ID!) { + viewer { + editingContext(editingContextId: $editingContextId) { + representation(representationId: $representationId) { + description { + ... on TableDescription { + contextMenu(tableId: $tableId, rowId: $rowId) { + __typename + id + label + iconURLs + } + } + } + } + } + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public RowContextMenuQueryRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public String run(Map variables) { + return this.graphQLRequestor.execute(ROW_CONTEXT_MENU_QUERY, variables); + + } +} diff --git a/packages/tables/frontend/sirius-components-tables/src/representation/TableRepresentation.tsx b/packages/tables/frontend/sirius-components-tables/src/representation/TableRepresentation.tsx index 3a297085c9..6f53bce75f 100644 --- a/packages/tables/frontend/sirius-components-tables/src/representation/TableRepresentation.tsx +++ b/packages/tables/frontend/sirius-components-tables/src/representation/TableRepresentation.tsx @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 CEA List. + * Copyright (c) 2024, 2025 CEA List. * 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 @@ -27,6 +27,9 @@ const useTableRepresentationStyles = makeStyles()((theme) => ({ justifyContent: 'center', paddingTop: theme.spacing(8), }, + representation: { + overflowX: 'auto', + }, })); export const TableRepresentation = ({ editingContextId, representationId, readOnly }: RepresentationComponentProps) => { @@ -73,7 +76,7 @@ export const TableRepresentation = ({ editingContextId, representationId, readOn } return ( -
+
{table !== null && !complete ? ( { + const [anchorEl, setAnchorEl] = useState(null); + const [contextMenuEntries, setContextMenuEntries] = useState([]); + const { callRowContextMenuEntriesQuery, loading, data } = useRowContextMenuEntries(); + const { invokeRowContextMenuEntry } = useInvokeRowContextMenuEntry( + editingContextId, + representationId, + tableId, + row.original.id + ); + + const convertMetadata = (menuEntry: GQLRowContextMenuEntry): RowContextMenuEntry => { + return { + __typename: menuEntry.__typename, + id: menuEntry.id, + label: menuEntry.label, + iconURLs: menuEntry.iconURLs, + }; + }; + + useEffect(() => { + if (!loading && data) { + const allContextEntries = data.viewer.editingContext.representation?.description.contextMenu.map(convertMetadata); + setContextMenuEntries(allContextEntries); + } + }, [loading, data]); + + const handleOpenRowActionMenu = (event: MouseEvent) => { + event.stopPropagation(); + event.preventDefault(); + setAnchorEl(event.currentTarget); + + const variables = { + variables: { + editingContextId, + representationId: tableId, + tableId, + rowId: row.original.id, + }, + }; + callRowContextMenuEntriesQuery(variables); + }; + + const handleClickContextMenuEntry = (menuEntry: RowContextMenuEntry) => { + invokeRowContextMenuEntry(menuEntry.id); + setAnchorEl(null); + }; + + return ( + <> + + + + + + event.stopPropagation()} + onClose={() => setAnchorEl(null)} + open={!!anchorEl} + data-testid={`row-context_menu-${row.original.headerLabel}`}> + {contextMenuEntries.map((entry) => ( + handleClickContextMenuEntry(entry)} + data-testid={`context-menu-entry-${entry.label}`} + disabled={readOnly} + aria-disabled> + + {entry.iconURLs.length > 0 ? ( + + ) : ( +
+ )} + + + + ))} +
+ + ); +}; diff --git a/packages/tables/frontend/sirius-components-tables/src/table/row/ResizeRowHandler.types.ts b/packages/tables/frontend/sirius-components-tables/src/rows/RowContextMenu.types.ts similarity index 59% rename from packages/tables/frontend/sirius-components-tables/src/table/row/ResizeRowHandler.types.ts rename to packages/tables/frontend/sirius-components-tables/src/rows/RowContextMenu.types.ts index 7647205d50..0e9b7e4390 100644 --- a/packages/tables/frontend/sirius-components-tables/src/table/row/ResizeRowHandler.types.ts +++ b/packages/tables/frontend/sirius-components-tables/src/rows/RowContextMenu.types.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 CEA LIST. + * Copyright (c) 2025 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,19 +10,22 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { GQLLine, GQLTable } from '../TableContent.types'; -export interface ResizeRowHandlerProps { +import { MRT_Row, MRT_TableInstance } from 'material-react-table'; +import { GQLLine } from '../table/TableContent.types'; + +export interface RowContextMenuProps { editingContextId: string; representationId: string; - table: GQLTable; + tableId: string; + row: MRT_Row; + table: MRT_TableInstance; readOnly: boolean; - row: GQLLine; - onRowHeightChanged: (rowId: string, height: number) => void; } -export interface DragState { - isDragging: boolean; - height: number; - trElement: HTMLElement | undefined; +export interface RowContextMenuEntry { + __typename: string; + id: string; + label: string; + iconURLs: string[]; } diff --git a/packages/tables/frontend/sirius-components-tables/src/rows/useInvokeRowContextMenuEntry.ts b/packages/tables/frontend/sirius-components-tables/src/rows/useInvokeRowContextMenuEntry.ts new file mode 100644 index 0000000000..d6df8a99b1 --- /dev/null +++ b/packages/tables/frontend/sirius-components-tables/src/rows/useInvokeRowContextMenuEntry.ts @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 { gql, useMutation } from '@apollo/client'; +import { useReporting } from '@eclipse-sirius/sirius-components-core'; +import { + GQLInvokeRowContextMenuEntryData, + GQLInvokeRowContextMenuEntryInput, + GQLInvokeRowContextMenuEntryVariables, + UseInvokeRowContextMenuEntryMutationValue, +} from './useInvokeRowContextMenuEntry.types'; + +export const invokeRowContextMenuEntryMutation = gql` + mutation invokeRowContextMenuEntry($input: InvokeRowContextMenuEntryInput!) { + invokeRowContextMenuEntry(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on SuccessPayload { + messages { + body + level + } + } + } + } +`; + +export const useInvokeRowContextMenuEntry = ( + editingContextId: string, + representationId: string, + tableId: string, + rowId: string +): UseInvokeRowContextMenuEntryMutationValue => { + const [mutationInvokeRowContextMenuEntry, mutationInvokeRowContextMenuEntryResult] = useMutation< + GQLInvokeRowContextMenuEntryData, + GQLInvokeRowContextMenuEntryVariables + >(invokeRowContextMenuEntryMutation); + useReporting( + mutationInvokeRowContextMenuEntryResult, + (data: GQLInvokeRowContextMenuEntryData) => data.resetTableRowsHeight + ); + + const invokeRowContextMenuEntry = (menuEntryId: string) => { + const input: GQLInvokeRowContextMenuEntryInput = { + id: crypto.randomUUID(), + editingContextId, + representationId, + tableId, + rowId, + menuEntryId, + }; + + mutationInvokeRowContextMenuEntry({ variables: { input } }); + }; + + return { + invokeRowContextMenuEntry, + }; +}; diff --git a/packages/tables/frontend/sirius-components-tables/src/rows/useInvokeRowContextMenuEntry.types.ts b/packages/tables/frontend/sirius-components-tables/src/rows/useInvokeRowContextMenuEntry.types.ts new file mode 100644 index 0000000000..5447949831 --- /dev/null +++ b/packages/tables/frontend/sirius-components-tables/src/rows/useInvokeRowContextMenuEntry.types.ts @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2024 CEA LIST. + * 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 { GQLErrorPayload, GQLSuccessPayload } from '@eclipse-sirius/sirius-components-core'; + +export interface UseInvokeRowContextMenuEntryMutationValue { + invokeRowContextMenuEntry: (menuEntryId: string) => void; +} + +export interface GQLInvokeRowContextMenuEntryInput { + id: string; + editingContextId: string; + representationId: string; + tableId: string; + rowId: string; + menuEntryId: string; +} + +export interface GQLInvokeRowContextMenuEntryVariables { + input: GQLInvokeRowContextMenuEntryInput; +} + +export interface GQLInvokeRowContextMenuEntryData { + resetTableRowsHeight: GQLInvokeRowContextMenuEntryPayload; +} + +export type GQLInvokeRowContextMenuEntryPayload = GQLErrorPayload | GQLSuccessPayload; diff --git a/packages/tables/frontend/sirius-components-tables/src/rows/useRowContextMenuEntries.ts b/packages/tables/frontend/sirius-components-tables/src/rows/useRowContextMenuEntries.ts new file mode 100644 index 0000000000..aaeee6f7ec --- /dev/null +++ b/packages/tables/frontend/sirius-components-tables/src/rows/useRowContextMenuEntries.ts @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2025 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 { gql, useLazyQuery } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { useEffect } from 'react'; +import { + GQLGetAllRowContextMenuEntriesData, + GQLGetAllRowContextMenuEntriesVariables, + UseRowContextMenuEntriesValue, +} from './useRowContextMenuEntries.types'; + +const getRowContextMenuEntriesQuery = gql` + query getAllRowContextMenuEntries($editingContextId: ID!, $representationId: ID!, $tableId: ID!, $rowId: ID!) { + viewer { + editingContext(editingContextId: $editingContextId) { + representation(representationId: $representationId) { + description { + ... on TableDescription { + contextMenu(tableId: $tableId, rowId: $rowId) { + __typename + id + label + iconURLs + } + } + } + } + } + } + } +`; + +export const useRowContextMenuEntries = (): UseRowContextMenuEntriesValue => { + const [callRowContextMenuEntriesQuery, { loading, data, error }] = useLazyQuery< + GQLGetAllRowContextMenuEntriesData, + GQLGetAllRowContextMenuEntriesVariables + >(getRowContextMenuEntriesQuery); + + const { addErrorMessage } = useMultiToast(); + + useEffect(() => { + if (error) { + const { message } = error; + addErrorMessage(message); + } + }, [error]); + + return { + callRowContextMenuEntriesQuery, + loading, + data: data ?? null, + }; +}; diff --git a/packages/tables/frontend/sirius-components-tables/src/rows/useRowContextMenuEntries.types.ts b/packages/tables/frontend/sirius-components-tables/src/rows/useRowContextMenuEntries.types.ts new file mode 100644 index 0000000000..8bb2f50e28 --- /dev/null +++ b/packages/tables/frontend/sirius-components-tables/src/rows/useRowContextMenuEntries.types.ts @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2025 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 { LazyQueryExecFunction } from '@apollo/client'; + +export interface UseRowContextMenuEntriesValue { + callRowContextMenuEntriesQuery: LazyQueryExecFunction< + GQLGetAllRowContextMenuEntriesData, + GQLGetAllRowContextMenuEntriesVariables + >; + loading: boolean; + data: GQLGetAllRowContextMenuEntriesData | null; +} + +export interface GQLGetAllRowContextMenuEntriesVariables { + editingContextId: string; + representationId: string; + tableId: string; + rowId: string; +} + +export interface GQLGetAllRowContextMenuEntriesData { + viewer: GQLGetAllRowContextMenuEntriesViewer; +} + +export interface GQLGetAllRowContextMenuEntriesViewer { + editingContext: GQLGetAllRowContextMenuEntriesEditingContext; +} + +export interface GQLGetAllRowContextMenuEntriesEditingContext { + representation: GQLGetAllRowContextMenuEntriesRepresentationMetadata; +} + +export interface GQLGetAllRowContextMenuEntriesRepresentationMetadata { + description: GQLGetAllRowContextMenuEntriesRepresentationDescription; +} + +export interface GQLGetAllRowContextMenuEntriesRepresentationDescription { + contextMenu: GQLRowContextMenuEntry[]; +} + +export interface GQLRowContextMenuEntry { + __typename: string; + id: string; + label: string; + iconURLs: string[]; +} diff --git a/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx b/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx index c85bf29492..f4fa6235d6 100644 --- a/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx +++ b/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -18,8 +18,7 @@ import { SettingsButton } from '../actions/SettingsButton'; import { useTableColumnFiltering } from '../columns/useTableColumnFiltering'; import { useTableColumnSizing } from '../columns/useTableColumnSizing'; import { useTableColumnVisibility } from '../columns/useTableColumnVisibility'; -import { ResizeRowHandler } from '../rows/ResizeRowHandler'; -import { RowHeader } from '../rows/RowHeader'; +import { RowContextMenu } from '../rows/RowContextMenu'; import { useResetRowsMutation } from '../rows/useResetRows'; import { CursorBasedPagination } from './CursorBasedPagination'; import { GQLLine, TablePaginationState, TableProps } from './TableContent.types'; @@ -51,7 +50,9 @@ export const TableContent = memo( readOnly, enableColumnVisibility, enableColumnResizing, - enableColumnFilters + enableColumnFilters, + enableRowSizing, + handleRowHeightChange ); const { columnSizing, setColumnSizing } = useTableColumnSizing( editingContextId, @@ -126,9 +127,9 @@ export const TableContent = memo( onPaginationChange(pagination.cursor, pagination.direction, pagination.size); }, [pagination.cursor, pagination.size, pagination.direction]); - const handleRowHeightChange = (rowId, height) => { + function handleRowHeightChange(rowId, height) { setLinesState((prev) => prev.map((line) => (line.id === rowId ? { ...line, height } : line))); - }; + } useEffect(() => { setLinesState([...table.lines]); @@ -158,7 +159,11 @@ export const TableContent = memo( enableGlobalFilter, manualFiltering: true, onGlobalFilterChange: setGlobalFilter, - initialState: { showGlobalFilter: enableGlobalFilter }, + enableColumnPinning: true, + initialState: { + showGlobalFilter: enableGlobalFilter, + columnPinning: { left: ['mrt-row-header'], right: ['mrt-row-actions'] }, + }, onColumnSizingChange: setColumnSizing, onColumnVisibilityChange: setColumnVisibility, onDensityChange: setDensity, @@ -193,26 +198,15 @@ export const TableContent = memo( onPageSizeChange={handlePageSize} /> ), - displayColumnDefOptions: { - 'mrt-row-actions': { - header: '', - size: 120, - }, - }, - renderRowActions: ({ row }) => ( - <> - - {enableRowSizing ? ( - - ) : null} - + renderRowActions: ({ row, table: MRT_table }) => ( + ), }; diff --git a/packages/tables/frontend/sirius-components-tables/src/table/row/ResizeRowHandler.tsx b/packages/tables/frontend/sirius-components-tables/src/table/row/ResizeRowHandler.tsx deleted file mode 100644 index 1e8866fc68..0000000000 --- a/packages/tables/frontend/sirius-components-tables/src/table/row/ResizeRowHandler.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 CEA LIST. - * 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 { memo, useRef } from 'react'; -import { makeStyles } from 'tss-react/mui'; -import { useTableMutations } from '../../graphql/mutation/useTableMutation'; -import { DragState, ResizeRowHandlerProps } from './ResizeRowHandler.types'; - -const useStyles = makeStyles()(() => ({ - handler: { - position: 'absolute', - margin: 0, - backgroundColor: '#B3BFC5', - borderColor: '#B3BFC5', - borderRadius: '2px', - width: '24px', - height: '4px', - bottom: 0, - left: '15px', - cursor: 'row-resize', - }, -})); - -export const ResizeRowHandler = memo( - ({ editingContextId, representationId, table, readOnly, row, onRowHeightChanged }: ResizeRowHandlerProps) => { - const { classes } = useStyles(); - const { resizeRow } = useTableMutations(editingContextId, representationId, table.id); - - const dragState = useRef({ - isDragging: false, - height: 0, - trElement: undefined, - }); - - const handleMouseDown = (e) => { - e.preventDefault(); - - dragState.current = { - isDragging: true, - height: parseInt(window.getComputedStyle(e.target.parentElement.parentElement).height, 10), - trElement: e.target.parentElement.parentElement, - }; - - const handleMouseMove = (e: MouseEvent) => { - if (dragState.current.isDragging) { - dragState.current.height += e.movementY; - onRowHeightChanged(row.id, dragState.current.height); - } - }; - - const handleMouseUp = (_) => { - dragState.current.isDragging = false; - resizeRow(row.id, dragState.current.height); - document.removeEventListener('mousemove', handleMouseMove); - document.removeEventListener('mouseup', handleMouseUp); - }; - - document.addEventListener('mousemove', handleMouseMove); - document.addEventListener('mouseup', handleMouseUp); - }; - - return !readOnly && row.isResizable ?
: null; - } -); diff --git a/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx b/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx index f1b2e09a1e..a69cedc728 100644 --- a/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx +++ b/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -14,6 +14,8 @@ import { MRT_ColumnDef } from 'material-react-table'; import { useMemo } from 'react'; import { Cell } from '../cells/Cell'; import { ColumnHeader } from '../columns/ColumnHeader'; +import { ResizeRowHandler } from '../rows/ResizeRowHandler'; +import { RowHeader } from '../rows/RowHeader'; import { GQLCell, GQLLine, GQLTable } from './TableContent.types'; import { UseTableColumnsValue } from './useTableColumns.types'; @@ -24,37 +26,58 @@ export const useTableColumns = ( readOnly: boolean, enableColumnVisibility: boolean, enableColumnSizing: boolean, - enableColumnFilters: boolean + enableColumnFilters: boolean, + enableRowSizing: boolean, + handleRowHeightChange: (rowId: string, height: number) => void ): UseTableColumnsValue => { const columns = useMemo[]>(() => { - const columnDefs: MRT_ColumnDef[] = table.columns.map((column) => { - return { - id: column.id, - accessorKey: column.id, - header: column.headerLabel, - Header: ({}) => { - return ; - }, - filterVariant: enableColumnFilters ? column.filterVariant : undefined, - size: enableColumnSizing && column.width > 0 ? column.width : undefined, - enableResizing: enableColumnSizing && column.isResizable, - visibleInShowHideMenu: enableColumnVisibility, - Cell: ({ row }) => { - const cell: GQLCell | null = row.original.cells.find((cell) => column.id === cell.columnId) ?? null; - return ( - [] = table.columns.map((column) => ({ + id: column.id, + accessorKey: column.id, + header: column.headerLabel, + Header: ({}) => { + return ; + }, + filterVariant: enableColumnFilters ? column.filterVariant : undefined, + size: enableColumnSizing && column.width > 0 ? column.width : undefined, + enableResizing: enableColumnSizing && column.isResizable, + visibleInShowHideMenu: enableColumnVisibility, + Cell: ({ row }) => { + const cell: GQLCell | null = row.original.cells.find((cell) => column.id === cell.columnId) ?? null; + return ( + + ); + }, + })); + + const rowHeaderColumn: MRT_ColumnDef = { + id: 'mrt-row-header', + header: '', + size: 200, + columnDefType: 'display', + Cell: ({ row }) => ( + <> + + {enableRowSizing ? ( + - ); - }, - }; - }); - - return columnDefs; + ) : null} + + ), + }; + return [rowHeaderColumn, ...columnDefs]; }, [table]); return {