diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 85c9fc8c28..677ac8ae75 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -14,6 +14,10 @@ === Breaking changes +- https://github.com/eclipse-sirius/sirius-components/issues/2080[#2080] [tree] EditingContextRepresentationDataFetcher will filter on IRepresentation unstead of ISemanticRepresentation to be able to support TreeDescription. ++ +As such targetObjectId is removed from RepresentationMetadata. + === Dependency update - [releng] Switch to Jacoco 0.8.10 @@ -28,6 +32,7 @@ === New Features - https://github.com/eclipse-sirius/sirius-components/issues/2039[#2039] [project] A new "Blank Studio" project template is available to create a project with the "studio" nature but no initial content +- https://github.com/eclipse-sirius/sirius-components/issues/2080[#2080] [tree] Add an inital label value when editing tree items label. === Improvements diff --git a/integration-tests/cypress/e2e/project/edit/explorer.cy.js b/integration-tests/cypress/e2e/project/edit/explorer.cy.js index 8f35ffb2a4..fe1707b1af 100644 --- a/integration-tests/cypress/e2e/project/edit/explorer.cy.js +++ b/integration-tests/cypress/e2e/project/edit/explorer.cy.js @@ -87,6 +87,7 @@ describe('/projects/:projectId/edit - Explorer', () => { cy.getByTestId('selected').contains('robot'); cy.getByTestId('robot').click(); cy.getByTestId('robot').type('renamed-robot{esc}'); + cy.getByTestId('name-edit').should('not.exist'); cy.getByTestId('robot').should('exist'); }); diff --git a/packages/compatibility/backend/sirius-components-compatibility-emf/src/test/java/org/eclipse/sirius/components/compatibility/emf/compatibility/operations/NavigationOperationHandlerTests.java b/packages/compatibility/backend/sirius-components-compatibility-emf/src/test/java/org/eclipse/sirius/components/compatibility/emf/compatibility/operations/NavigationOperationHandlerTests.java index f89f2d5893..9dcfa71e5b 100644 --- a/packages/compatibility/backend/sirius-components-compatibility-emf/src/test/java/org/eclipse/sirius/components/compatibility/emf/compatibility/operations/NavigationOperationHandlerTests.java +++ b/packages/compatibility/backend/sirius-components-compatibility-emf/src/test/java/org/eclipse/sirius/components/compatibility/emf/compatibility/operations/NavigationOperationHandlerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 Obeo. + * Copyright (c) 2022, 2023 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 @@ -62,9 +62,9 @@ public class NavigationOperationHandlerTests { private final IRepresentationMetadataSearchService representationMetadataSearchService = new IRepresentationMetadataSearchService.NoOp() { @Override public List findAllByTargetObjectId(IEditingContext editingContext, String targetObjectId) { - var firstRepresentationMetadata = new RepresentationMetadata(FIRST_DIAGRAM_ID, Diagram.KIND, FIRST_DIAGRAM_LABEL, FIRST_DIAGRAM_DESCRIPTION_ID, targetObjectId); - var secondRepresentationMetadata = new RepresentationMetadata(SECOND_DIAGRAM_ID, Diagram.KIND, SECOND_DIAGRAM_LABEL, FIRST_DIAGRAM_DESCRIPTION_ID, targetObjectId); - var thirdRepresentationMetadata = new RepresentationMetadata(THIRD_DIAGRAM_ID, Diagram.KIND, THIRD_DIAGRAM_LABEL, SECOND_DIAGRAM_DESCRIPTION_ID, targetObjectId); + var firstRepresentationMetadata = new RepresentationMetadata(FIRST_DIAGRAM_ID, Diagram.KIND, FIRST_DIAGRAM_LABEL, FIRST_DIAGRAM_DESCRIPTION_ID); + var secondRepresentationMetadata = new RepresentationMetadata(SECOND_DIAGRAM_ID, Diagram.KIND, SECOND_DIAGRAM_LABEL, FIRST_DIAGRAM_DESCRIPTION_ID); + var thirdRepresentationMetadata = new RepresentationMetadata(THIRD_DIAGRAM_ID, Diagram.KIND, THIRD_DIAGRAM_LABEL, SECOND_DIAGRAM_DESCRIPTION_ID); return List.of(firstRepresentationMetadata, secondRepresentationMetadata, thirdRepresentationMetadata); } }; diff --git a/packages/core/backend/sirius-components-collaborative/src/main/java/org/eclipse/sirius/components/collaborative/editingcontext/EditingContextEventProcessorRegistry.java b/packages/core/backend/sirius-components-collaborative/src/main/java/org/eclipse/sirius/components/collaborative/editingcontext/EditingContextEventProcessorRegistry.java index d71f66a6f7..3f4b9afd1d 100644 --- a/packages/core/backend/sirius-components-collaborative/src/main/java/org/eclipse/sirius/components/collaborative/editingcontext/EditingContextEventProcessorRegistry.java +++ b/packages/core/backend/sirius-components-collaborative/src/main/java/org/eclipse/sirius/components/collaborative/editingcontext/EditingContextEventProcessorRegistry.java @@ -20,8 +20,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import jakarta.annotation.PreDestroy; - import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessor; import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorFactory; import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorRegistry; @@ -34,6 +32,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import jakarta.annotation.PreDestroy; import reactor.core.Disposable; import reactor.core.publisher.Mono; diff --git a/packages/core/backend/sirius-components-core/src/main/java/org/eclipse/sirius/components/core/RepresentationMetadata.java b/packages/core/backend/sirius-components-core/src/main/java/org/eclipse/sirius/components/core/RepresentationMetadata.java index c75d392ac3..d4e660ec28 100644 --- a/packages/core/backend/sirius-components-core/src/main/java/org/eclipse/sirius/components/core/RepresentationMetadata.java +++ b/packages/core/backend/sirius-components-core/src/main/java/org/eclipse/sirius/components/core/RepresentationMetadata.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 Obeo. + * Copyright (c) 2022, 2023 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,14 +29,11 @@ public class RepresentationMetadata { private final String descriptionId; - private final String targetObjectId; - - public RepresentationMetadata(String id, String kind, String label, String descriptionId, String targetObjectId) { + public RepresentationMetadata(String id, String kind, String label, String descriptionId) { this.id = Objects.requireNonNull(id); this.kind = Objects.requireNonNull(kind); this.label = Objects.requireNonNull(label); this.descriptionId = Objects.requireNonNull(descriptionId); - this.targetObjectId = targetObjectId; } public String getId() { @@ -55,13 +52,9 @@ public String getDescriptionId() { return this.descriptionId; } - public String getTargetObjectId() { - return this.targetObjectId; - } - @Override public String toString() { - String pattern = "{0} '{'id: {1}, kind: {2}, label: {3}, descriptionId: {4}, targetObjectId: {5}'}'"; - return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.kind, this.label, this.descriptionId, this.targetObjectId); + String pattern = "{0} '{'id: {1}, kind: {2}, label: {3}, descriptionId: {4}'}'"; + return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.kind, this.label, this.descriptionId); } } diff --git a/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/editingcontext/EditingContextRepresentationDataFetcher.java b/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/editingcontext/EditingContextRepresentationDataFetcher.java index 93523f0d62..5cb2afba90 100644 --- a/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/editingcontext/EditingContextRepresentationDataFetcher.java +++ b/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/editingcontext/EditingContextRepresentationDataFetcher.java @@ -15,7 +15,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.Optional; import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher; import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorRegistry; @@ -23,8 +22,6 @@ import org.eclipse.sirius.components.core.RepresentationMetadata; import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; import org.eclipse.sirius.components.graphql.api.LocalContextConstants; -import org.eclipse.sirius.components.representations.IRepresentation; -import org.eclipse.sirius.components.representations.ISemanticRepresentation; import org.eclipse.sirius.web.services.api.representations.IRepresentationService; import org.eclipse.sirius.web.services.api.representations.RepresentationDescriptor; @@ -66,7 +63,6 @@ public DataFetcherResult get(DataFetchingEnvironment env Map localContext = new HashMap<>(environment.getLocalContext()); localContext.put(LocalContextConstants.REPRESENTATION_ID, representationId); - // Search among the active representations first. They are already loaded in memory and include transient // representations. // @formatter:off @@ -74,14 +70,11 @@ public DataFetcherResult get(DataFetchingEnvironment env .flatMap(editingContextEventProcessor -> editingContextEventProcessor.getRepresentationEventProcessors().stream()) .filter(editingContextEventProcessor -> editingContextEventProcessor.getRepresentation().getId().equals(representationId)) .map(IRepresentationEventProcessor::getRepresentation) - .filter(ISemanticRepresentation.class::isInstance) - .map(ISemanticRepresentation.class::cast) - .map((ISemanticRepresentation representation) -> { + .map(representation -> { return new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), - representation.getDescriptionId(), - representation.getTargetObjectId()); + representation.getDescriptionId()); }) .findFirst(); // @formatter:on @@ -90,7 +83,10 @@ public DataFetcherResult get(DataFetchingEnvironment env if (representationMetadata.isEmpty()) { representationMetadata = this.representationService.getRepresentationDescriptorForProjectId(editingContextId, representationId) .map(RepresentationDescriptor::getRepresentation) - .map(this::toRepresentationMetadata); + .map(representation -> new RepresentationMetadata(representation.getId(), + representation.getKind(), + representation.getLabel(), + representation.getDescriptionId())); } return DataFetcherResult.newResult() @@ -99,16 +95,4 @@ public DataFetcherResult get(DataFetchingEnvironment env .build(); // @formatter:on } - - private RepresentationMetadata toRepresentationMetadata(IRepresentation representation) { - // @formatter:off - String targetObjectId = Optional.of(representation) - .filter(ISemanticRepresentation.class::isInstance) - .map(ISemanticRepresentation.class::cast) - .map(ISemanticRepresentation::getTargetObjectId) - .orElse(null); - // @formatter:on - return new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId(), targetObjectId); - } - } diff --git a/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/editingcontext/EditingContextRepresentationsDataFetcher.java b/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/editingcontext/EditingContextRepresentationsDataFetcher.java index 90d5318d1d..516ed3543c 100644 --- a/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/editingcontext/EditingContextRepresentationsDataFetcher.java +++ b/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/editingcontext/EditingContextRepresentationsDataFetcher.java @@ -15,13 +15,11 @@ import java.util.Base64; import java.util.List; import java.util.Objects; -import java.util.Optional; import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher; import org.eclipse.sirius.components.core.RepresentationMetadata; import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; import org.eclipse.sirius.components.representations.IRepresentation; -import org.eclipse.sirius.components.representations.ISemanticRepresentation; import org.eclipse.sirius.web.services.api.representations.IRepresentationService; import org.eclipse.sirius.web.services.api.representations.RepresentationDescriptor; @@ -90,14 +88,7 @@ public Connection get(DataFetchingEnvironment environmen } private RepresentationMetadata toRepresentationMetadata(IRepresentation representation) { - // @formatter:off - String targetObjectId = Optional.of(representation) - .filter(ISemanticRepresentation.class::isInstance) - .map(ISemanticRepresentation.class::cast) - .map(ISemanticRepresentation::getTargetObjectId) - .orElse(null); - // @formatter:on - return new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId(), targetObjectId); + return new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId()); } } diff --git a/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/representation/RepresentationMetadataDescriptionDataFetcher.java b/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/representation/RepresentationMetadataDescriptionDataFetcher.java index d1051fe8d3..514d0cb48b 100644 --- a/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/representation/RepresentationMetadataDescriptionDataFetcher.java +++ b/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/datafetchers/representation/RepresentationMetadataDescriptionDataFetcher.java @@ -24,12 +24,15 @@ import org.eclipse.sirius.components.collaborative.dto.GetRepresentationDescriptionInput; import org.eclipse.sirius.components.collaborative.dto.GetRepresentationDescriptionPayload; import org.eclipse.sirius.components.collaborative.forms.PropertiesEventProcessorFactory; +import org.eclipse.sirius.components.collaborative.trees.TreeEventProcessorFactory; import org.eclipse.sirius.components.core.RepresentationMetadata; import org.eclipse.sirius.components.forms.description.FormDescription; import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; import org.eclipse.sirius.components.graphql.api.LocalContextConstants; +import org.eclipse.sirius.components.representations.Failure; import org.eclipse.sirius.components.representations.GetOrCreateRandomIdProvider; import org.eclipse.sirius.components.representations.IRepresentationDescription; +import org.eclipse.sirius.components.trees.description.TreeDescription; import graphql.schema.DataFetchingEnvironment; import reactor.core.publisher.Mono; @@ -53,8 +56,28 @@ public class RepresentationMetadataDescriptionDataFetcher implements IDataFetche .build(); // @formatter:on + // @formatter:off + private static final IRepresentationDescription FAKE_DETAILS_TREE = TreeDescription.newTreeDescription(TreeEventProcessorFactory.TREE_ID) + .label("Explorer") + .idProvider(new GetOrCreateRandomIdProvider()) + .treeItemIdProvider(variableManager -> TreeEventProcessorFactory.TREE_ID) + .kindProvider(variableManager -> TreeEventProcessorFactory.TREE_ID) + .labelProvider(variableManager -> TreeEventProcessorFactory.TREE_ID) + .imageURLProvider(variableManager -> TreeEventProcessorFactory.TREE_ID) + .editableProvider(variableManager -> null) + .deletableProvider(variableManager -> null) + .elementsProvider(variableManager -> null) + .hasChildrenProvider(variableManager -> null) + .childrenProvider(variableManager -> null) + .canCreatePredicate(variableManager -> true) + .deleteHandler(variableManager -> null) + .renameHandler((variableManager, newValue) -> new Failure("")) + .build(); + // @formatter:on + private final IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry; + public RepresentationMetadataDescriptionDataFetcher(IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry) { this.editingContextEventProcessorRegistry = Objects.requireNonNull(editingContextEventProcessorRegistry); } @@ -64,15 +87,20 @@ public CompletableFuture get(DataFetchingEnvironment CompletableFuture result = Mono.empty().toFuture(); RepresentationMetadata representationMetadata = environment.getSource(); - if (Objects.equals(PropertiesEventProcessorFactory.DETAILS_VIEW_ID, representationMetadata.getDescriptionId())) { + if (Objects.equals(PropertiesEventProcessorFactory.DETAILS_VIEW_ID, representationMetadata.getDescriptionId()) || (Objects.equals(TreeEventProcessorFactory.TREE_ID, representationMetadata.getDescriptionId()))) { /* - * The FormDescription used for the details view can not be found by - * IRepresentationDescriptionSearchService, but we can get away by returning a fake one with the same id as - * no GraphQL query actually needs to see its content. We need to return *something* with the correct id + * The FormDescription used for the details view and the TreeDescription used for the explorer can not be found by + * IRepresentationDescriptionSearchService. We can get away by returning fake ones with the same ids as + * no GraphQL query actually needs to see their content. We need to return *something* with the correct ids * only to allow GraphQL resolution to continue on queries like "completionProposals" defined on * FormDescription. */ - result = Mono.just(FAKE_DETAILS_DESCRIPTION).toFuture(); + if (Objects.equals(PropertiesEventProcessorFactory.DETAILS_VIEW_ID, representationMetadata.getDescriptionId())) { + result = Mono.just(FAKE_DETAILS_DESCRIPTION).toFuture(); + } + if (Objects.equals(TreeEventProcessorFactory.TREE_ID, representationMetadata.getDescriptionId())) { + result = Mono.just(FAKE_DETAILS_TREE).toFuture(); + } } else { Map localContext = environment.getLocalContext(); @@ -80,7 +108,6 @@ public CompletableFuture get(DataFetchingEnvironment String representationId = Optional.ofNullable(localContext.get(LocalContextConstants.REPRESENTATION_ID)).map(Object::toString).orElse(null); if (editingContextId != null && representationId != null) { GetRepresentationDescriptionInput input = new GetRepresentationDescriptionInput(UUID.randomUUID(), editingContextId, representationId); - // @formatter:off result = this.editingContextEventProcessorRegistry.dispatchEvent(input.editingContextId(), input) .filter(GetRepresentationDescriptionPayload.class::isInstance) diff --git a/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/configuration/FlowProjectTemplatesInitializer.java b/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/configuration/FlowProjectTemplatesInitializer.java index d891604d08..671806e5a5 100644 --- a/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/configuration/FlowProjectTemplatesInitializer.java +++ b/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/configuration/FlowProjectTemplatesInitializer.java @@ -19,12 +19,14 @@ import fr.obeo.dsl.designer.sample.flow.FlowFactory; import fr.obeo.dsl.designer.sample.flow.Processor; import fr.obeo.dsl.designer.sample.flow.System; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; + import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.sirius.components.collaborative.api.IRepresentationPersistenceService; @@ -133,7 +135,7 @@ private Optional initializeFlowProject(IEditingContext e Diagram diagram = this.diagramCreationService.create(topographyDiagram.getLabel(), semanticTarget, topographyDiagram, editingContext); this.representationPersistenceService.save(editingContext, diagram); - result = Optional.of(new RepresentationMetadata(diagram.getId(), diagram.getKind(), diagram.getLabel(), diagram.getDescriptionId(), diagram.getTargetObjectId())); + result = Optional.of(new RepresentationMetadata(diagram.getId(), diagram.getKind(), diagram.getLabel(), diagram.getDescriptionId())); } } catch (IOException exception) { this.logger.warn(exception.getMessage(), exception); diff --git a/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/configuration/StudioProjectTemplatesInitializer.java b/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/configuration/StudioProjectTemplatesInitializer.java index b9354efed2..a77d015d00 100644 --- a/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/configuration/StudioProjectTemplatesInitializer.java +++ b/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/configuration/StudioProjectTemplatesInitializer.java @@ -159,7 +159,7 @@ private Optional initializeStudioProject(IEditingContext Diagram diagram = this.diagramCreationService.create(topographyDiagram.getLabel(), semanticTarget, topographyDiagram, editingContext); this.representationPersistenceService.save(editingContext, diagram); - result = Optional.of(new RepresentationMetadata(diagram.getId(), diagram.getKind(), diagram.getLabel(), diagram.getDescriptionId(), diagram.getTargetObjectId())); + result = Optional.of(new RepresentationMetadata(diagram.getId(), diagram.getKind(), diagram.getLabel(), diagram.getDescriptionId())); } } catch (IOException exception) { this.logger.warn(exception.getMessage(), exception); diff --git a/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/papaya/PapayaStudioTemplatesInitializer.java b/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/papaya/PapayaStudioTemplatesInitializer.java index fe7d6fa653..fbe270c284 100644 --- a/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/papaya/PapayaStudioTemplatesInitializer.java +++ b/packages/sirius-web/backend/sirius-web-sample-application/src/main/java/org/eclipse/sirius/web/sample/papaya/PapayaStudioTemplatesInitializer.java @@ -133,7 +133,7 @@ private Optional initializeStudioProject(IEditingContext Diagram diagram = this.diagramCreationService.create(topographyDiagram.getLabel(), semanticTarget, topographyDiagram, editingContext); this.representationPersistenceService.save(editingContext, diagram); - result = Optional.of(new RepresentationMetadata(diagram.getId(), diagram.getKind(), diagram.getLabel(), diagram.getDescriptionId(), diagram.getTargetObjectId())); + result = Optional.of(new RepresentationMetadata(diagram.getId(), diagram.getKind(), diagram.getLabel(), diagram.getDescriptionId())); } } catch (IOException exception) { this.logger.warn(exception.getMessage(), exception); diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/explorer/ExplorerInitialDirectEditTreeItemLabelProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/explorer/ExplorerInitialDirectEditTreeItemLabelProvider.java new file mode 100644 index 0000000000..901bbc1966 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/explorer/ExplorerInitialDirectEditTreeItemLabelProvider.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + *******************************************************************************/ +package org.eclipse.sirius.web.services.explorer; + +import java.util.Optional; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.trees.api.IInitialDirectEditTreeItemLabelProvider; +import org.eclipse.sirius.components.collaborative.trees.dto.InitialDirectEditElementLabelInput; +import org.eclipse.sirius.components.collaborative.trees.dto.InitialDirectEditElementLabelSuccessPayload; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.trees.Tree; +import org.eclipse.sirius.components.trees.TreeItem; +import org.springframework.stereotype.Service; + +/** + * Used to compute the initial label to display when the direct edit of a tree item of the explorer is triggered on the frontend. + * + * @author mcharfadi + */ +@Service +public class ExplorerInitialDirectEditTreeItemLabelProvider implements IInitialDirectEditTreeItemLabelProvider { + + public static final String EXPLORER_DESCRIPTION_ID = UUID.nameUUIDFromBytes("explorer_tree_description".getBytes()).toString(); + + public static final String EXPLORER_DOCUMENT_KIND = "siriusComponents://representation?type=Tree"; + + public static final String EXPLORER_NAME = "Explorer"; + + @Override + public boolean canHandle(Tree tree) { + return tree.getId().startsWith("explorer://"); + } + + @Override + public IPayload handle(IEditingContext editingContext, Tree tree, InitialDirectEditElementLabelInput input) { + String initialLabel = tree.getChildren().stream() + .map(treeItems -> this.searchById(treeItems, input.treeItemId())) + .filter(Optional::isPresent) + .map(Optional::get) + .map(treeItem -> treeItem.getLabel()) + .findFirst() + .orElse(""); + + return new InitialDirectEditElementLabelSuccessPayload(input.id(), initialLabel); + } + + private Optional searchById(TreeItem treeItem, String id) { + Optional optionalTreeItem = Optional.empty(); + if (treeItem.getId().equals(id)) { + optionalTreeItem = Optional.of(treeItem); + } + if (optionalTreeItem.isEmpty() && treeItem.isHasChildren()) { + optionalTreeItem = treeItem.getChildren().stream() + .map(treeItems -> this.searchById(treeItems, id)) + .filter(Optional::isPresent) + .map(Optional::get).findFirst(); + } + return optionalTreeItem; + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/representations/RepresentationService.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/representations/RepresentationService.java index bf4d8f4859..4b9c5be2b3 100644 --- a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/representations/RepresentationService.java +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/representations/RepresentationService.java @@ -14,6 +14,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; + import java.util.List; import java.util.Objects; import java.util.Optional; @@ -35,6 +36,7 @@ import org.eclipse.sirius.web.services.api.id.IDParser; import org.eclipse.sirius.web.services.api.representations.IRepresentationService; import org.eclipse.sirius.web.services.api.representations.RepresentationDescriptor; +import org.eclipse.sirius.web.services.explorer.ExplorerInitialDirectEditTreeItemLabelProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -182,25 +184,21 @@ public void deleteDanglingRepresentations(String editingContextId) { @Override public Optional findByRepresentationId(String representationId) { + if (representationId.startsWith("explorer://")) { + return Optional.of(new RepresentationMetadata(ExplorerInitialDirectEditTreeItemLabelProvider.EXPLORER_DESCRIPTION_ID, ExplorerInitialDirectEditTreeItemLabelProvider.EXPLORER_DOCUMENT_KIND, ExplorerInitialDirectEditTreeItemLabelProvider.EXPLORER_NAME, representationId)); + } return new IDParser().parse(representationId) .flatMap(this.representationRepository::findById) .map(new RepresentationMapper(this.objectMapper)::toDTO) .map(RepresentationDescriptor::getRepresentation) - .filter(ISemanticRepresentation.class::isInstance) - .map(ISemanticRepresentation.class::cast) - .map(representation -> new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId(), representation.getTargetObjectId())); + .filter(IRepresentation.class::isInstance) + .map(IRepresentation.class::cast) + .map(representation -> new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId())); } @Override public Optional findByRepresentation(IRepresentation representation) { - // @formatter:off - String targetObjectId = Optional.of(representation) - .filter(ISemanticRepresentation.class::isInstance) - .map(ISemanticRepresentation.class::cast) - .map(ISemanticRepresentation::getTargetObjectId) - .orElse(null); - // @formatter:on - return Optional.of(new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId(), targetObjectId)); + return Optional.of(new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId())); } @Override diff --git a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/TreeEventProcessorFactory.java b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/TreeEventProcessorFactory.java index 3e52b61ab4..7a1ecda20f 100644 --- a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/TreeEventProcessorFactory.java +++ b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/TreeEventProcessorFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2022 Obeo. + * Copyright (c) 2019, 2023 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 @@ -15,6 +15,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.UUID; import org.eclipse.sirius.components.collaborative.api.IRepresentationConfiguration; import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; @@ -41,6 +42,8 @@ @Service public class TreeEventProcessorFactory implements IRepresentationEventProcessorFactory { + public static final String TREE_ID = UUID.nameUUIDFromBytes("explorer_tree_description".getBytes()).toString(); + private final IExplorerDescriptionProvider explorerDescriptionProvider; private final ITreeService treeService; diff --git a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/api/IInitialDirectEditTreeItemLabelProvider.java b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/api/IInitialDirectEditTreeItemLabelProvider.java new file mode 100644 index 0000000000..da8267aae2 --- /dev/null +++ b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/api/IInitialDirectEditTreeItemLabelProvider.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + *******************************************************************************/ +package org.eclipse.sirius.components.collaborative.trees.api; + +import org.eclipse.sirius.components.collaborative.trees.dto.InitialDirectEditElementLabelInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.trees.Tree; + +/** + * IInitialDirectEditTreeItemLabelProvider. + * + * @author mcharfadi + */ +public interface IInitialDirectEditTreeItemLabelProvider { + + boolean canHandle(Tree treeDescription); + + IPayload handle(IEditingContext editingContext, Tree tree, InitialDirectEditElementLabelInput input); +} diff --git a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/api/TreeConfiguration.java b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/api/TreeConfiguration.java index b2cedd79ef..312ebba71f 100644 --- a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/api/TreeConfiguration.java +++ b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/api/TreeConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2021 Obeo. + * Copyright (c) 2019, 2023 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 @@ -31,7 +31,7 @@ public class TreeConfiguration implements IRepresentationConfiguration { public TreeConfiguration(String editingContextId, List expanded) { String uniqueId = editingContextId + expanded.toString(); - this.treeId = UUID.nameUUIDFromBytes(uniqueId.getBytes()).toString(); + this.treeId = "explorer://" + UUID.nameUUIDFromBytes(uniqueId.getBytes()).toString(); this.expanded = Objects.requireNonNull(expanded); } diff --git a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/InitialDirectEditElementLabelInput.java b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/InitialDirectEditElementLabelInput.java new file mode 100644 index 0000000000..9d3f0ec83b --- /dev/null +++ b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/InitialDirectEditElementLabelInput.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + *******************************************************************************/ +package org.eclipse.sirius.components.collaborative.trees.dto; + +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.trees.api.ITreeInput; + +/** + * The "initial direct edit element label" query input. + * + * @author mcharfadi + */ +public record InitialDirectEditElementLabelInput(UUID id, String editingContextId, String representationId, String treeItemId, String initialLabel) implements ITreeInput { +} diff --git a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/InitialDirectEditElementLabelSuccessPayload.java b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/InitialDirectEditElementLabelSuccessPayload.java new file mode 100644 index 0000000000..7df4971be2 --- /dev/null +++ b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/InitialDirectEditElementLabelSuccessPayload.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + *******************************************************************************/ +package org.eclipse.sirius.components.collaborative.trees.dto; + +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IPayload; + +/** + * The "initial direct edit element label" success payload. + * + * @author mcharfadi + */ +public record InitialDirectEditElementLabelSuccessPayload(UUID id, String initialDirectEditElementLabel) implements IPayload { + public InitialDirectEditElementLabelSuccessPayload { + Objects.requireNonNull(id); + Objects.requireNonNull(initialDirectEditElementLabel); + } +} diff --git a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/TreeEventInput.java b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/TreeEventInput.java index 7e8bbbf24e..f225b2522a 100644 --- a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/TreeEventInput.java +++ b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/dto/TreeEventInput.java @@ -22,5 +22,5 @@ * * @author sbegaudeau */ -public record TreeEventInput(UUID id, String editingContextId, List expanded) implements IInput { +public record TreeEventInput(UUID id, String editingContextId, String treeId, List expanded) implements IInput { } diff --git a/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/handlers/InitialDirectEditTreeItemLabelEventHandler.java b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/handlers/InitialDirectEditTreeItemLabelEventHandler.java new file mode 100644 index 0000000000..73a053650a --- /dev/null +++ b/packages/trees/backend/sirius-components-collaborative-trees/src/main/java/org/eclipse/sirius/components/collaborative/trees/handlers/InitialDirectEditTreeItemLabelEventHandler.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + *******************************************************************************/ +package org.eclipse.sirius.components.collaborative.trees.handlers; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.api.ChangeDescription; +import org.eclipse.sirius.components.collaborative.api.ChangeKind; +import org.eclipse.sirius.components.collaborative.trees.api.IInitialDirectEditTreeItemLabelProvider; +import org.eclipse.sirius.components.collaborative.trees.api.ITreeEventHandler; +import org.eclipse.sirius.components.collaborative.trees.api.ITreeInput; +import org.eclipse.sirius.components.collaborative.trees.dto.InitialDirectEditElementLabelInput; +import org.eclipse.sirius.components.collaborative.trees.dto.InitialDirectEditElementLabelSuccessPayload; +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.trees.Tree; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import reactor.core.publisher.Sinks.Many; +import reactor.core.publisher.Sinks.One; + +/** + * Handler for InitialDirectEditElementLabelInput which simply forwards to the first available IInitialDirectEditTreeItemLabelProvider which can handle the + * request. + * + * @author mcharfadi + */ +@Service +public class InitialDirectEditTreeItemLabelEventHandler implements ITreeEventHandler { + + private final Logger logger = LoggerFactory.getLogger(InitialDirectEditTreeItemLabelEventHandler.class); + + private final List initialDirectEditTreeItemLabelProvider; + + public InitialDirectEditTreeItemLabelEventHandler(List initialDirectEditTreeItemLabelProvider) { + this.initialDirectEditTreeItemLabelProvider = Objects.requireNonNull(initialDirectEditTreeItemLabelProvider); + } + + @Override + public boolean canHandle(ITreeInput treeInput) { + return treeInput instanceof InitialDirectEditElementLabelInput; + } + + @Override + public void handle(One payloadSink, Many changeDescriptionSink, IEditingContext editingContext, Tree tree, ITreeInput treeInput) { + IPayload payload = new InitialDirectEditElementLabelSuccessPayload(treeInput.id(), ""); + ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, treeInput.representationId(), treeInput); + + if (treeInput instanceof InitialDirectEditElementLabelInput input) { + Optional optionalPathProvider = this.initialDirectEditTreeItemLabelProvider.stream().filter(treePathProvider -> treePathProvider.canHandle(tree)).findFirst(); + if (optionalPathProvider.isPresent()) { + IPayload resultPayload = optionalPathProvider.get().handle(editingContext, tree, input); + if (resultPayload instanceof InitialDirectEditElementLabelSuccessPayload) { + payload = resultPayload; + } else if (resultPayload instanceof ErrorPayload errorPayload) { + this.logger.warn(errorPayload.message()); + } + } + } + changeDescriptionSink.tryEmitNext(changeDescription); + payloadSink.tryEmitValue(payload); + } + +} \ No newline at end of file diff --git a/packages/trees/backend/sirius-components-collaborative-trees/src/main/resources/schema/tree.graphqls b/packages/trees/backend/sirius-components-collaborative-trees/src/main/resources/schema/tree.graphqls index 5c2bf3c915..ec4848e6b3 100644 --- a/packages/trees/backend/sirius-components-collaborative-trees/src/main/resources/schema/tree.graphqls +++ b/packages/trees/backend/sirius-components-collaborative-trees/src/main/resources/schema/tree.graphqls @@ -14,6 +14,7 @@ type TreePath { input TreeEventInput { id: ID! + treeId: String! editingContextId: ID! expanded: [String!]! } @@ -46,6 +47,7 @@ type TreeItem { type TreeDescription implements RepresentationDescription { id: ID! label: String! + initialDirectEditTreeItemLabel(treeItemId: ID!): String! } extend type Mutation { @@ -63,11 +65,11 @@ input DeleteTreeItemInput { union DeleteTreeItemPayload = SuccessPayload | ErrorPayload input RenameTreeItemInput { - id: ID! - editingContextId: ID! - representationId: ID! - treeItemId: ID! - newLabel: String! + id: ID! + editingContextId: ID! + representationId: ID! + treeItemId: ID! + newLabel: String! } union RenameTreeItemPayload = SuccessPayload | ErrorPayload diff --git a/packages/trees/backend/sirius-components-trees-graphql/src/main/java/org/eclipse/sirius/components/trees/graphql/datafetchers/tree/TreeItemInitialDirectEditLabelDataFetcher.java b/packages/trees/backend/sirius-components-trees-graphql/src/main/java/org/eclipse/sirius/components/trees/graphql/datafetchers/tree/TreeItemInitialDirectEditLabelDataFetcher.java new file mode 100644 index 0000000000..a9639be1cc --- /dev/null +++ b/packages/trees/backend/sirius-components-trees-graphql/src/main/java/org/eclipse/sirius/components/trees/graphql/datafetchers/tree/TreeItemInitialDirectEditLabelDataFetcher.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + *******************************************************************************/ +package org.eclipse.sirius.components.trees.graphql.datafetchers.tree; + +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.trees.dto.InitialDirectEditElementLabelInput; +import org.eclipse.sirius.components.collaborative.trees.dto.InitialDirectEditElementLabelSuccessPayload; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.LocalContextConstants; + +import graphql.schema.DataFetchingEnvironment; +import reactor.core.publisher.Mono; + +/** + * The "initial direct edit element label" query input. + * + * @author mcharfadi + */ +@QueryDataFetcher(type = "TreeDescription", field = "initialDirectEditTreeItemLabel") +public class TreeItemInitialDirectEditLabelDataFetcher implements IDataFetcherWithFieldCoordinates> { + + private static final String INPUT_ARGUMENT = "treeItemId"; + + private final IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry; + + public TreeItemInitialDirectEditLabelDataFetcher(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 treeItemId = environment.getArgument(INPUT_ARGUMENT); + + if (editingContextId != null && representationId != null && treeItemId != null) { + var input = new InitialDirectEditElementLabelInput(UUID.randomUUID(), editingContextId, representationId, treeItemId, ""); + // @formatter:off + return this.editingContextEventProcessorRegistry.dispatchEvent(editingContextId, input) + .filter(InitialDirectEditElementLabelSuccessPayload.class::isInstance) + .map(InitialDirectEditElementLabelSuccessPayload.class::cast) + .map(InitialDirectEditElementLabelSuccessPayload::initialDirectEditElementLabel) + .toFuture(); + // @formatter:on + } + return Mono. empty().toFuture(); + } +} diff --git a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItem.tsx b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItem.tsx index ce6cd3839a..a987adcdeb 100644 --- a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItem.tsx +++ b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItem.tsx @@ -10,10 +10,8 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { gql, useMutation } from '@apollo/client'; import { DRAG_SOURCES_TYPE, Selection, SelectionEntry, ServerContext } from '@eclipse-sirius/sirius-components-core'; import IconButton from '@material-ui/core/IconButton'; -import TextField from '@material-ui/core/TextField'; import Typography from '@material-ui/core/Typography'; import { makeStyles } from '@material-ui/core/styles'; import CropDinIcon from '@material-ui/icons/CropDin'; @@ -22,19 +20,9 @@ import React, { useContext, useEffect, useRef, useState } from 'react'; import { TreeItemProps } from './TreeItem.types'; import { TreeItemArrow } from './TreeItemArrow'; import { TreeItemContextMenu, TreeItemContextMenuContext } from './TreeItemContextMenu'; +import { TreeItemDirectEditInput } from './TreeItemDirectEditInput'; import { isFilterCandidate } from './filterTreeItem'; -const renameTreeItemMutation = gql` - mutation renameTreeItem($input: RenameTreeItemInput!) { - renameTreeItem(input: $input) { - __typename - ... on ErrorPayload { - message - } - } - } -`; - const useTreeItemStyle = makeStyles((theme) => ({ treeItem: { display: 'flex', @@ -137,41 +125,14 @@ export const TreeItem = ({ editingMode: false, label: item.label, prevSelectionId: null, + editingkey: '', }; + const [state, setState] = useState(initialState); - const { showContextMenu, menuAnchor, editingMode, label } = state; + const { showContextMenu, menuAnchor, editingMode } = state; const refDom = useRef() as any; - const [renameTreeItem, { loading: renameTreeItemLoading, data: renameTreeItemData, error: renameTreeItemError }] = - useMutation(renameTreeItemMutation); - useEffect(() => { - if (!renameTreeItemLoading && !renameTreeItemError && renameTreeItemData?.renameTreeItem) { - const { renameTreeItem } = renameTreeItemData; - if (renameTreeItem.__typename === 'SuccessPayload') { - setState((prevState) => { - return { ...prevState, editingMode: false }; - }); - } - } - }, [renameTreeItemData, renameTreeItemError, renameTreeItemLoading]); - - // custom hook for getting previous value - const usePrevious = (value) => { - const ref = useRef(); - useEffect(() => { - ref.current = value; - }); - return ref.current; - }; - - const prevEditingMode = usePrevious(editingMode); - useEffect(() => { - if (prevEditingMode && !editingMode) { - refDom.current.focus(); - } - }, [editingMode, prevEditingMode]); - // Context menu handling const openContextMenu = (event) => { if (!showContextMenu) { @@ -181,6 +142,7 @@ export const TreeItem = ({ showContextMenu: true, menuAnchor: currentTarget, editingMode: false, + editingkey: prevState.editingkey, label: item.label, prevSelectionId: prevState.prevSelectionId, }; @@ -197,6 +159,7 @@ export const TreeItem = ({ showContextMenu: false, menuAnchor: null, editingMode: false, + editingkey: prevState.editingkey, label: item.label, prevSelectionId: prevState.prevSelectionId, }; @@ -209,6 +172,7 @@ export const TreeItem = ({ showContextMenu: false, menuAnchor: null, editingMode: true, + editingkey: prevState.editingkey, label: item.label, prevSelectionId: prevState.prevSelectionId, }; @@ -286,61 +250,20 @@ export const TreeItem = ({ } const highlightRegExp = new RegExp(`(${textToHighlight})`, 'gi'); let text; + const onCloseEditingMode = () => { + setState((prevState) => { + return { ...prevState, editingMode: false }; + }); + refDom.current.focus(); + }; if (editingMode) { - const handleChange = (event) => { - const newLabel = event.target.value; - setState((prevState) => { - return { ...prevState, editingMode: true, label: newLabel }; - }); - }; - - const doRename = () => { - const isNameValid = label.length >= 1; - if (isNameValid && item) { - renameTreeItem({ - variables: { - input: { - id: crypto.randomUUID(), - editingContextId, - representationId: treeId, - treeItemId: item.id, - newLabel: label, - }, - }, - }); - } else { - setState((prevState) => { - return { ...prevState, editingMode: false, label: item.label }; - }); - } - }; - const onFinishEditing = (event) => { - const { key } = event; - if (key === 'Enter') { - doRename(); - } else if (key === 'Escape') { - setState((prevState) => { - return { ...prevState, editingMode: false, label: item.label }; - }); - } - }; - const onFocusIn = (event) => { - event.target.select(); - }; - const onFocusOut = () => doRename(); text = ( - + ); } else { let itemLabel: JSX.Element; @@ -373,23 +296,25 @@ export const TreeItem = ({ } const onClick: React.MouseEventHandler = (event) => { - refDom.current.focus(); + if (!state.editingMode) { + refDom.current.focus(); - if (event.ctrlKey || event.metaKey) { - event.stopPropagation(); - const isItemInSelection = selection.entries.find((entry) => entry.id === item.id); - if (isItemInSelection) { - const newSelection: Selection = { entries: selection.entries.filter((entry) => entry.id !== item.id) }; - setSelection(newSelection); + if (event.ctrlKey || event.metaKey) { + event.stopPropagation(); + const isItemInSelection = selection.entries.find((entry) => entry.id === item.id); + if (isItemInSelection) { + const newSelection: Selection = { entries: selection.entries.filter((entry) => entry.id !== item.id) }; + setSelection(newSelection); + } else { + const { id, label, kind } = item; + const newEntry = { id, label, kind }; + const newSelection: Selection = { entries: [...selection.entries, newEntry] }; + setSelection(newSelection); + } } else { const { id, label, kind } = item; - const newEntry = { id, label, kind }; - const newSelection: Selection = { entries: [...selection.entries, newEntry] }; - setSelection(newSelection); + setSelection({ entries: [{ id, label, kind }] }); } - } else { - const { id, label, kind } = item; - setSelection({ entries: [{ id, label, kind }] }); } }; @@ -406,7 +331,7 @@ export const TreeItem = ({ !event.metaKey && !event.ctrlKey && key.length === 1 && directEditActivationValidCharacters.test(key); if (validFirstInputChar) { setState((prevState) => { - return { ...prevState, editingMode: true, label: key }; + return { ...prevState, editingMode: true, editingkey: key }; }); } }; diff --git a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemDirectEditInput.tsx b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemDirectEditInput.tsx new file mode 100644 index 0000000000..469b73f7dd --- /dev/null +++ b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemDirectEditInput.tsx @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2023 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, useMutation, useQuery } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import TextField from '@material-ui/core/TextField'; +import { useEffect, useRef, useState } from 'react'; +import { + GQLErrorPayload, + GQLInitialDirectEditElementLabelData, + GQLInitialDirectEditElementLabelInput, + GQLRenameTreeItemPayload, + GQLSuccessPayload, + TreeItemDirectEditInputProps, + TreeItemDirectEditInputState, +} from './TreeItemDirectEditInput.types'; + +const renameTreeItemMutation = gql` + mutation renameTreeItem($input: RenameTreeItemInput!) { + renameTreeItem(input: $input) { + __typename + ... on ErrorPayload { + message + } + } + } +`; + +const initialDirectEditElementLabeQuery = gql` + query initialDirectEditElementLabel($editingContextId: ID!, $representationId: ID!, $treeItemId: ID!) { + viewer { + editingContext(editingContextId: $editingContextId) { + representation(representationId: $representationId) { + description { + ... on TreeDescription { + initialDirectEditTreeItemLabel(treeItemId: $treeItemId) + } + } + } + } + } + } +`; + +export const TreeItemDirectEditInput = ({ + editingContextId, + treeId, + treeItemId, + editingkey, + onClose, +}: TreeItemDirectEditInputProps) => { + const [state, setState] = useState({ + newLabel: editingkey, + }); + + const { addErrorMessage } = useMultiToast(); + + const isErrorPayload = (payload: GQLRenameTreeItemPayload): payload is GQLErrorPayload => + payload.__typename === 'ErrorPayload'; + const isSuccessPayload = (payload: GQLRenameTreeItemPayload): payload is GQLSuccessPayload => + payload.__typename === 'SuccessPayload'; + + const textInput = useRef(null); + + const { data: initialLabelTreeItemItemData, error: initialLabelTreeItemItemError } = useQuery< + GQLInitialDirectEditElementLabelData, + GQLInitialDirectEditElementLabelInput + >(initialDirectEditElementLabeQuery, { + variables: { + editingContextId: editingContextId, + representationId: treeId, + treeItemId: treeItemId, + }, + }); + + useEffect(() => { + if (initialLabelTreeItemItemError) { + addErrorMessage('An unexpected error has occurred, please refresh the page'); + } + if (initialLabelTreeItemItemData?.viewer.editingContext.representation.description.initialDirectEditTreeItemLabel) { + const initialLabel = + initialLabelTreeItemItemData?.viewer.editingContext.representation.description.initialDirectEditTreeItemLabel; + if (!editingkey) { + setState((prevState) => { + return { ...prevState, newLabel: initialLabel }; + }); + setTimeout(() => { + textInput.current.select(); + }, 0); + } + } + }, [initialLabelTreeItemItemError, initialLabelTreeItemItemData]); + + const [renameTreeItem, { data: renameTreeItemData, error: renameTreeItemError }] = + useMutation(renameTreeItemMutation); + + useEffect(() => { + if (renameTreeItemError) { + addErrorMessage('An unexpected error has occurred, please refresh the page'); + } + if (renameTreeItemData) { + const { renameTreeItem } = renameTreeItemData; + if (isErrorPayload(renameTreeItem)) { + addErrorMessage(renameTreeItem.messages); + } else if (isSuccessPayload(renameTreeItem)) { + if (renameTreeItem.__typename === 'SuccessPayload') { + onClose(); + } + } + } + }, [renameTreeItemData, renameTreeItemError]); + + const doRename = () => { + const isNameValid = state.newLabel.length >= 1; + if (isNameValid) { + renameTreeItem({ + variables: { + input: { + id: crypto.randomUUID(), + editingContextId: editingContextId, + representationId: treeId, + treeItemId: treeItemId, + newLabel: state.newLabel, + }, + }, + }); + } else { + onClose(); + } + }; + + const handleChange = (event) => { + const newLabel = event.target.value; + setState((prevState) => { + return { ...prevState, newLabel: newLabel }; + }); + }; + + const onFinishEditing = (event) => { + const { key } = event; + if (key === 'Enter') { + doRename(); + } else if (key === 'Escape') { + onClose(); + } + }; + + const onFocusIn = (event) => event.target.select(); + + useEffect(() => { + document.addEventListener('mousedown', doRename); + return () => document.removeEventListener('mousedown', doRename); + }); + + return ( + <> + + + ); +}; diff --git a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemDirectEditInput.types.ts b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemDirectEditInput.types.ts new file mode 100644 index 0000000000..630e8f36a3 --- /dev/null +++ b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemDirectEditInput.types.ts @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2023 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 + *******************************************************************************/ +export interface TreeItemDirectEditInputProps { + editingContextId: string; + treeId: string; + treeItemId: string; + editingkey: string; + onClose: () => void; +} + +export interface TreeItemDirectEditInputState { + newLabel: string; +} + +export interface GQLInitialDirectEditElementLabelInput { + editingContextId: string; + representationId: string; + treeItemId: string; +} + +export interface GQLInitialDirectEditElementLabelData { + viewer: GQLViewer; +} + +export interface GQLViewer { + editingContext: GQLEditingContext; +} + +export interface GQLEditingContext { + representation: GQLRepresentation; +} + +export interface GQLRepresentation { + description: GQLRepresentationDescription; +} + +export interface GQLRepresentationDescription { + __typename: string; + initialDirectEditTreeItemLabel: string; +} + +export interface GQLRenameTreeItemPayload { + __typename: string; +} + +export interface GQLSuccessPayload extends GQLRenameTreeItemPayload { + messages: string; +} + +export interface GQLErrorPayload extends GQLRenameTreeItemPayload { + messages: string; +} diff --git a/packages/trees/frontend/sirius-components-trees/src/views/ExplorerView.tsx b/packages/trees/frontend/sirius-components-trees/src/views/ExplorerView.tsx index bbf36acf2f..0267dd9877 100644 --- a/packages/trees/frontend/sirius-components-trees/src/views/ExplorerView.tsx +++ b/packages/trees/frontend/sirius-components-trees/src/views/ExplorerView.tsx @@ -170,6 +170,7 @@ export const ExplorerView = ({ editingContextId, selection, setSelection, readOn variables: { input: { id, + treeId: 'explorer', editingContextId, expanded, }, diff --git a/packages/trees/frontend/sirius-components-trees/src/views/ExplorerView.types.ts b/packages/trees/frontend/sirius-components-trees/src/views/ExplorerView.types.ts index aa40b0d5eb..509560b24b 100644 --- a/packages/trees/frontend/sirius-components-trees/src/views/ExplorerView.types.ts +++ b/packages/trees/frontend/sirius-components-trees/src/views/ExplorerView.types.ts @@ -17,6 +17,7 @@ export interface GQLExplorerEventVariables { export interface GQLExplorerEventInput { id: string; + treeId: string; editingContextId: string; expanded: string[]; }