diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 7a8bedf788..292c8305ce 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -35,6 +35,7 @@ - https://github.com/eclipse-sirius/sirius-components/issues/1248[#1248] [charts] Add support for pie-chart in Form representation - https://github.com/eclipse-sirius/sirius-components/issues/1255[#1255] [form] Add support for charts in form descriptions editor - https://github.com/eclipse-sirius/sirius-components/issues/1244[#1244] [form] Add support for flexbox containers on FormDescriptions +- https://github.com/eclipse-sirius/sirius-components/issues/1265[#1265] [form] Add support for flexbox containers on FormDescriptionEditors == v2022.5.0 diff --git a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/dto/AddWidgetInput.java b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/dto/AddWidgetInput.java index ae81a8de1c..629907a550 100644 --- a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/dto/AddWidgetInput.java +++ b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/dto/AddWidgetInput.java @@ -31,6 +31,8 @@ public class AddWidgetInput implements IFormDescriptionEditorInput { private String representationId; + private String containerId; + private String kind; private int index; @@ -39,10 +41,13 @@ public AddWidgetInput() { // Used by jackson } - public AddWidgetInput(UUID id, String editingContextId, String representationId) { + public AddWidgetInput(UUID id, String editingContextId, String representationId, String containerId, String kind, int index) { this.id = Objects.requireNonNull(id); this.editingContextId = Objects.requireNonNull(editingContextId); this.representationId = Objects.requireNonNull(representationId); + this.containerId = Objects.requireNonNull(containerId); + this.kind = Objects.requireNonNull(kind); + this.index = Objects.requireNonNull(index); } @Override @@ -59,6 +64,10 @@ public String getEditingContextId() { return this.editingContextId; } + public String getContainerId() { + return this.containerId; + } + public String getKind() { return this.kind; } @@ -69,8 +78,8 @@ public int getIndex() { @Override public String toString() { - String pattern = "{0} '{'id: {1}, editingContextId: {2}, representationId: {3}; kind: {4}, index: {5}}'"; //$NON-NLS-1$ - return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.editingContextId, this.representationId, this.kind, this.index); + String pattern = "{0} '{'id: {1}, editingContextId: {2}, representationId: {3}, containerId: {4}, kind: {5}, index: {6}}'"; //$NON-NLS-1$ + return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.editingContextId, this.representationId, this.containerId, this.kind, this.index); } } diff --git a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/dto/MoveWidgetInput.java b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/dto/MoveWidgetInput.java index 84915e5ab3..40f70d0ca9 100644 --- a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/dto/MoveWidgetInput.java +++ b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/dto/MoveWidgetInput.java @@ -31,6 +31,8 @@ public class MoveWidgetInput implements IFormDescriptionEditorInput { private String representationId; + private String containerId; + private String widgetId; private int index; @@ -39,10 +41,13 @@ public MoveWidgetInput() { // Used by jackson } - public MoveWidgetInput(UUID id, String editingContextId, String representationId) { + public MoveWidgetInput(UUID id, String editingContextId, String representationId, String containerId, String widgetId, int index) { this.id = Objects.requireNonNull(id); this.editingContextId = Objects.requireNonNull(editingContextId); this.representationId = Objects.requireNonNull(representationId); + this.containerId = Objects.requireNonNull(containerId); + this.widgetId = Objects.requireNonNull(widgetId); + this.index = Objects.requireNonNull(index); } @Override @@ -59,6 +64,10 @@ public String getEditingContextId() { return this.editingContextId; } + public String getContainerId() { + return this.containerId; + } + public String getWidgetId() { return this.widgetId; } @@ -69,8 +78,8 @@ public int getIndex() { @Override public String toString() { - String pattern = "{0} '{'id: {1}, editingContextId: {2}, representationId: {3}; widgetId: {4}, index: {5}}'"; //$NON-NLS-1$ - return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.editingContextId, this.representationId, this.widgetId, this.index); + String pattern = "{0} '{'id: {1}, editingContextId: {2}, representationId: {3}, containerId: {4}, widgetId: {5}, index: {6}}'"; //$NON-NLS-1$ + return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.editingContextId, this.representationId, this.containerId, this.widgetId, this.index); } } diff --git a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/AddWidgetEventHandler.java b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/AddWidgetEventHandler.java index e5c98ea60b..37c30a8279 100644 --- a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/AddWidgetEventHandler.java +++ b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/AddWidgetEventHandler.java @@ -13,6 +13,7 @@ package org.eclipse.sirius.components.collaborative.formdescriptioneditors.handlers; import java.util.Objects; +import java.util.Optional; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; @@ -30,6 +31,8 @@ import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IObjectService; import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.view.FlexDirection; +import org.eclipse.sirius.components.view.FlexboxContainerDescription; import org.eclipse.sirius.components.view.FormDescription; import org.eclipse.sirius.components.view.ViewFactory; import org.eclipse.sirius.components.view.WidgetDescription; @@ -81,9 +84,10 @@ public void handle(One payloadSink, Many changeDesc ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, formDescriptionEditorInput.getRepresentationId(), formDescriptionEditorInput); if (formDescriptionEditorInput instanceof AddWidgetInput) { + String containerId = ((AddWidgetInput) formDescriptionEditorInput).getContainerId(); String kind = ((AddWidgetInput) formDescriptionEditorInput).getKind(); int index = ((AddWidgetInput) formDescriptionEditorInput).getIndex(); - boolean addWidget = this.addWidget(editingContext, formDescriptionEditorContext, kind, index); + boolean addWidget = this.addWidget(editingContext, formDescriptionEditorContext, containerId, kind, index); if (addWidget) { payload = new AddWidgetSuccessPayload(formDescriptionEditorInput.getId()); changeDescription = new ChangeDescription(ChangeKind.SEMANTIC_CHANGE, formDescriptionEditorInput.getRepresentationId(), formDescriptionEditorInput); @@ -94,23 +98,35 @@ public void handle(One payloadSink, Many changeDesc changeDescriptionSink.tryEmitNext(changeDescription); } - protected boolean addWidget(IEditingContext editingContext, IFormDescriptionEditorContext formDescriptionEditorContext, String kind, int index) { - var optionalSelf = this.objectService.getObject(editingContext, formDescriptionEditorContext.getFormDescriptionEditor().getTargetObjectId()); + protected boolean addWidget(IEditingContext editingContext, IFormDescriptionEditorContext formDescriptionEditorContext, String containerId, String kind, int index) { + boolean success = false; + var optionalSelf = Optional.empty(); + if (containerId != null) { + optionalSelf = this.objectService.getObject(editingContext, containerId); + } else { + optionalSelf = this.objectService.getObject(editingContext, formDescriptionEditorContext.getFormDescriptionEditor().getTargetObjectId()); + } if (optionalSelf.isPresent()) { - Object formDescription = optionalSelf.get(); - if (formDescription instanceof FormDescription) { - EClassifier eClassifier = ViewFactory.eINSTANCE.getEPackage().getEClassifier(kind + "Description"); //$NON-NLS-1$ - if (eClassifier instanceof EClass) { - var widgetDescription = ViewFactory.eINSTANCE.create((EClass) eClassifier); - if (widgetDescription instanceof WidgetDescription) { - this.createStyle((WidgetDescription) widgetDescription); - ((FormDescription) formDescription).getWidgets().add(index, (WidgetDescription) widgetDescription); - return true; + Object container = optionalSelf.get(); + EClassifier eClassifier = ViewFactory.eINSTANCE.getEPackage().getEClassifier(kind + "Description"); //$NON-NLS-1$ + if (eClassifier instanceof EClass) { + var widgetDescription = ViewFactory.eINSTANCE.create((EClass) eClassifier); + if (widgetDescription instanceof FlexboxContainerDescription) { + ((FlexboxContainerDescription) widgetDescription).setFlexDirection(FlexDirection.get(kind)); + } + if (widgetDescription instanceof WidgetDescription) { + this.createStyle((WidgetDescription) widgetDescription); + if (container instanceof FormDescription) { + ((FormDescription) container).getWidgets().add(index, (WidgetDescription) widgetDescription); + success = true; + } else if (container instanceof FlexboxContainerDescription) { + ((FlexboxContainerDescription) container).getChildren().add(index, (WidgetDescription) widgetDescription); + success = true; } } } } - return false; + return success; } private void createStyle(WidgetDescription widgetDescription) { diff --git a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/MoveWidgetEventHandler.java b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/MoveWidgetEventHandler.java index ef35df8ee6..eb8a217c6f 100644 --- a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/MoveWidgetEventHandler.java +++ b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/MoveWidgetEventHandler.java @@ -28,6 +28,7 @@ import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IObjectService; import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.view.FlexboxContainerDescription; import org.eclipse.sirius.components.view.FormDescription; import org.eclipse.sirius.components.view.WidgetDescription; import org.springframework.stereotype.Service; @@ -77,9 +78,10 @@ public void handle(One payloadSink, Many changeDesc ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, formDescriptionEditorInput.getRepresentationId(), formDescriptionEditorInput); if (formDescriptionEditorInput instanceof MoveWidgetInput) { + String containerId = ((MoveWidgetInput) formDescriptionEditorInput).getContainerId(); String widgetId = ((MoveWidgetInput) formDescriptionEditorInput).getWidgetId(); int index = ((MoveWidgetInput) formDescriptionEditorInput).getIndex(); - boolean moveWidget = this.moveWidget(editingContext, formDescriptionEditorContext, widgetId, index); + boolean moveWidget = this.moveWidget(editingContext, formDescriptionEditorContext, containerId, widgetId, index); if (moveWidget) { payload = new MoveWidgetSuccessPayload(formDescriptionEditorInput.getId()); changeDescription = new ChangeDescription(ChangeKind.SEMANTIC_CHANGE, formDescriptionEditorInput.getRepresentationId(), formDescriptionEditorInput); @@ -90,18 +92,36 @@ public void handle(One payloadSink, Many changeDesc changeDescriptionSink.tryEmitNext(changeDescription); } - protected boolean moveWidget(IEditingContext editingContext, IFormDescriptionEditorContext formDescriptionEditorContext, String widgetId, int index) { - var optionalSelf = this.objectService.getObject(editingContext, formDescriptionEditorContext.getFormDescriptionEditor().getTargetObjectId()); + protected boolean moveWidget(IEditingContext editingContext, IFormDescriptionEditorContext formDescriptionEditorContext, String containerId, String widgetId, int index) { + boolean success = false; + var optionalSelf = Optional.empty(); + if (containerId != null) { + optionalSelf = this.objectService.getObject(editingContext, containerId); + } else { + optionalSelf = this.objectService.getObject(editingContext, formDescriptionEditorContext.getFormDescriptionEditor().getTargetObjectId()); + } if (optionalSelf.isPresent()) { - Object formDescription = optionalSelf.get(); - if (formDescription instanceof FormDescription) { - Optional widgetToMove = this.objectService.getObject(editingContext, widgetId); - if (widgetToMove.filter(WidgetDescription.class::isInstance).isPresent()) { - ((FormDescription) formDescription).getWidgets().move(index, (WidgetDescription) widgetToMove.get()); - return true; + Object container = optionalSelf.get(); + var objectToMove = this.objectService.getObject(editingContext, widgetId); + if (objectToMove.filter(WidgetDescription.class::isInstance).isPresent()) { + WidgetDescription widgetToMove = (WidgetDescription) objectToMove.get(); + if (container instanceof FormDescription) { + if (container.equals(widgetToMove.eContainer())) { + ((FormDescription) container).getWidgets().move(index, widgetToMove); + } else { + ((FormDescription) container).getWidgets().add(index, widgetToMove); + } + success = true; + } else if (container instanceof FlexboxContainerDescription) { + if (container.equals(widgetToMove.eContainer())) { + ((FlexboxContainerDescription) container).getChildren().move(index, widgetToMove); + } else { + ((FlexboxContainerDescription) container).getChildren().add(index, widgetToMove); + } + success = true; } } } - return false; + return success; } } diff --git a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/resources/schema/formdescriptioneditor.graphqls b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/resources/schema/formdescriptioneditor.graphqls index 5c0b369b7d..827ffbc982 100644 --- a/backend/sirius-components-collaborative-formdescriptioneditors/src/main/resources/schema/formdescriptioneditor.graphqls +++ b/backend/sirius-components-collaborative-formdescriptioneditors/src/main/resources/schema/formdescriptioneditor.graphqls @@ -18,7 +18,7 @@ type FormDescriptionEditorRefreshedEventPayload { type FormDescriptionEditor implements Representation { id: ID! metadata: RepresentationMetadata! - widgets: [FormDescriptionEditorWidget!]! + widgets: [AbstractFormDescriptionEditorWidget!]! } type FormDescriptionEditorDescription implements RepresentationDescription { @@ -26,10 +26,26 @@ type FormDescriptionEditorDescription implements RepresentationDescription { label: String! } -type FormDescriptionEditorWidget { +interface AbstractFormDescriptionEditorWidget { + id: ID! + kind: String! + label: String! +} + +type FormDescriptionEditorWidget implements AbstractFormDescriptionEditorWidget { + id: ID! + kind: String! + label: String! +} + +type FormDescriptionEditorFlexboxContainer implements AbstractFormDescriptionEditorWidget { id: ID! kind: String! label: String! + flexDirection: String! + flexWrap: String! + flexGrow: Int! + children: [AbstractFormDescriptionEditorWidget!]! } extend type Mutation { @@ -42,6 +58,7 @@ input AddWidgetInput { id: ID! editingContextId: ID! representationId: ID! + containerId: ID kind: String! index: Int! } @@ -69,6 +86,7 @@ input MoveWidgetInput { id: ID! editingContextId: ID! representationId: ID! + containerId: ID widgetId: String! index: Int! } diff --git a/backend/sirius-components-collaborative-formdescriptioneditors/src/test/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/AddWidgetEventHandlerTests.java b/backend/sirius-components-collaborative-formdescriptioneditors/src/test/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/AddWidgetEventHandlerTests.java index 8c4de117bb..e66b013adc 100644 --- a/backend/sirius-components-collaborative-formdescriptioneditors/src/test/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/AddWidgetEventHandlerTests.java +++ b/backend/sirius-components-collaborative-formdescriptioneditors/src/test/java/org/eclipse/sirius/components/collaborative/formdescriptioneditors/handlers/AddWidgetEventHandlerTests.java @@ -44,11 +44,11 @@ public class AddWidgetEventHandlerTests { public void testArrangeAll() { var handler = new AddWidgetEventHandler(new IObjectService.NoOp(), new ICollaborativeFormDescriptionEditorMessageService.NoOp(), new SimpleMeterRegistry()) { @Override - protected boolean addWidget(IEditingContext editingContext, IFormDescriptionEditorContext formDescriptionEditorContext, String kind, int index) { + protected boolean addWidget(IEditingContext editingContext, IFormDescriptionEditorContext formDescriptionEditorContext, String containerId, String kind, int index) { return true; } }; - var input = new AddWidgetInput(UUID.randomUUID(), "editingContextId", "representationId"); //$NON-NLS-1$//$NON-NLS-2$ + var input = new AddWidgetInput(UUID.randomUUID(), "editingContextId", "representationId", "containerId", "kind", 0); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ assertThat(handler.canHandle(input)).isTrue(); diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/AbstractFormDescriptionEditorWidget.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/AbstractFormDescriptionEditorWidget.java new file mode 100644 index 0000000000..ee6fceb099 --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/AbstractFormDescriptionEditorWidget.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors; + +/** + * Abstract class to be extended by all the widgets of the form-description-editor representation. + * + * @author arichard + */ +public class AbstractFormDescriptionEditorWidget implements IFormDescriptionEditorWidget { + + protected String id; + + protected String kind; + + protected String label; + + @Override + public String getId() { + return this.id; + } + + @Override + public String getKind() { + return this.kind; + } + + @Override + public String getLabel() { + return this.label; + } + +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditor.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditor.java index e1bf2196b3..d24c3bfa57 100644 --- a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditor.java +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditor.java @@ -42,7 +42,7 @@ public final class FormDescriptionEditor implements IRepresentation, ISemanticRe private String descriptionId; - private List widgets; + private List widgets; private FormDescriptionEditor() { // Prevent instantiation @@ -73,7 +73,7 @@ public String getDescriptionId() { return this.descriptionId; } - public List getWidgets() { + public List getWidgets() { return this.widgets; } @@ -108,7 +108,7 @@ public static final class Builder { private String descriptionId; - private List widgets; + private List widgets; private Builder(String id) { this.id = Objects.requireNonNull(id); @@ -137,7 +137,7 @@ public Builder descriptionId(String descriptionId) { return this; } - public Builder widgets(List widgets) { + public Builder widgets(List widgets) { this.widgets = Objects.requireNonNull(widgets); return this; } diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditorFlexboxContainer.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditorFlexboxContainer.java new file mode 100644 index 0000000000..b50e067d72 --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditorFlexboxContainer.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.annotations.Immutable; + +/** + * Any widget contained in the form description editor flexbox container. + * + * @author arichard + */ +@Immutable +public final class FormDescriptionEditorFlexboxContainer extends AbstractFormDescriptionEditorWidget { + + private String flexDirection; + + private String flexWrap; + + private int flexGrow; + + private List children; + + private FormDescriptionEditorFlexboxContainer() { + // Prevent instantiation + } + + public String getFlexDirection() { + return this.flexDirection; + } + + public String getFlexWrap() { + return this.flexWrap; + } + + public int getFlexGrow() { + return this.flexGrow; + } + + public List getChildren() { + return this.children; + } + + public static Builder newFormDescriptionEditorFlexboxContainer(String id) { + return new Builder(id); + } + + @Override + public String toString() { + String pattern = "{0} '{'id: {1}, kind: {2}, label: {3}, flexDirection: {4}, flexWrap: {5}, flexGrow: {6}'}'"; //$NON-NLS-1$ + return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.kind, this.label, this.flexDirection, this.flexWrap, this.flexGrow); + } + + /** + * The builder used to create the form description editor flexbox container. + * + * @author arichard + */ + @SuppressWarnings("checkstyle:HiddenField") + public static final class Builder { + private String id; + + private String kind; + + private String label; + + private String flexDirection; + + private String flexWrap; + + private int flexGrow; + + private List children; + + private Builder(String id) { + this.id = Objects.requireNonNull(id); + } + + public Builder kind(String kind) { + this.kind = Objects.requireNonNull(kind); + return this; + } + + public Builder label(String label) { + this.label = Objects.requireNonNull(label); + return this; + } + + public Builder flexDirection(String flexDirection) { + this.flexDirection = Objects.requireNonNull(flexDirection); + return this; + } + + public Builder flexWrap(String flexWrap) { + this.flexWrap = Objects.requireNonNull(flexWrap); + return this; + } + + public Builder flexGrow(int flexGrow) { + this.flexGrow = Objects.requireNonNull(flexGrow); + return this; + } + + public Builder children(List children) { + this.children = Objects.requireNonNull(children); + return this; + } + + public FormDescriptionEditorFlexboxContainer build() { + FormDescriptionEditorFlexboxContainer fdeFlexboxContainer = new FormDescriptionEditorFlexboxContainer(); + fdeFlexboxContainer.id = Objects.requireNonNull(this.id); + fdeFlexboxContainer.kind = Objects.requireNonNull(this.kind); + fdeFlexboxContainer.label = Objects.requireNonNull(this.label); + fdeFlexboxContainer.flexDirection = Objects.requireNonNull(this.flexDirection); + fdeFlexboxContainer.flexWrap = Objects.requireNonNull(this.flexWrap); + fdeFlexboxContainer.flexGrow = Objects.requireNonNull(this.flexGrow); + fdeFlexboxContainer.children = Objects.requireNonNull(this.children); + return fdeFlexboxContainer; + } + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditorWidget.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditorWidget.java index 585e396cdc..3574737596 100644 --- a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditorWidget.java +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/FormDescriptionEditorWidget.java @@ -18,38 +18,17 @@ import org.eclipse.sirius.components.annotations.Immutable; /** - * Any widget contained in the form description editor representation. + * Any widget contained in the form description editor widget. * * @author arichard */ @Immutable -public final class FormDescriptionEditorWidget implements IFormDescriptionEditorWidget { - - private String id; - - private String kind; - - private String label; +public final class FormDescriptionEditorWidget extends AbstractFormDescriptionEditorWidget { private FormDescriptionEditorWidget() { // Prevent instantiation } - @Override - public String getId() { - return this.id; - } - - @Override - public String getKind() { - return this.kind; - } - - @Override - public String getLabel() { - return this.label; - } - public static Builder newFormDescriptionEditorWidget(String id) { return new Builder(id); } diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorComponent.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorComponent.java index bd716fe5d6..83c44567c0 100644 --- a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorComponent.java +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorComponent.java @@ -15,15 +15,18 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; +import java.util.function.Function; import org.eclipse.sirius.components.formdescriptioneditors.FormDescriptionEditor; -import org.eclipse.sirius.components.formdescriptioneditors.FormDescriptionEditorWidget; +import org.eclipse.sirius.components.formdescriptioneditors.description.AbstractFormDescriptionEditorWidgetDescription; import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorDescription; +import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorFlexboxContainerDescription; +import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorWidgetDescription; import org.eclipse.sirius.components.formdescriptioneditors.elements.FormDescriptionEditorElementProps; import org.eclipse.sirius.components.representations.Element; import org.eclipse.sirius.components.representations.IComponent; import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.components.view.FlexboxContainerDescription; import org.eclipse.sirius.components.view.FormDescription; import org.eclipse.sirius.components.view.WidgetDescription; @@ -54,39 +57,100 @@ public Element render() { .orElseGet(() -> variableManager.get(FormDescriptionEditor.LABEL, String.class) .orElse("Form Description Editor")); //$NON-NLS-1$ // @formatter:on - String targetObjectId = formDescriptionEditorDescription.getTargetObjectIdProvider().apply(variableManager); - List widgets = new ArrayList<>(); - widgets = formDescription.getWidgets().stream().map(widget -> { + Function targetObjectIdProvider = formDescriptionEditorDescription.getTargetObjectIdProvider(); + String targetObjectId = targetObjectIdProvider.apply(variableManager); - VariableManager variableManager2 = variableManager.createChild(); - variableManager2.put(VariableManager.SELF, widget); - String widgetId = formDescriptionEditorDescription.getTargetObjectIdProvider().apply(variableManager2); + List childrenWidgets = new ArrayList<>(); - String name = widget.getName(); - if (name == null) { - name = this.getKind(widget); + formDescription.getWidgets().forEach(widgetDescription -> { + + VariableManager childVariableManager = variableManager.createChild(); + childVariableManager.put(VariableManager.SELF, widgetDescription); + String widgetId = targetObjectIdProvider.apply(childVariableManager); + String widgetKind = this.getKind(widgetDescription); + String widgetLabel = widgetDescription.getName(); + if (widgetLabel == null) { + widgetLabel = widgetKind; + } + + if (widgetDescription instanceof FlexboxContainerDescription) { + List childrenDescriptions = this.transformDescriptions(((FlexboxContainerDescription) widgetDescription).getChildren(), + childVariableManager, targetObjectIdProvider); + // @formatter:off + FormDescriptionEditorFlexboxContainerDescription fdeFlexboxContainerDescription = FormDescriptionEditorFlexboxContainerDescription.newFormDescriptionEditorFlexboxContainerDescription(widgetId) + .label(widgetLabel) + .kind(widgetKind) + .flexDirection(((FlexboxContainerDescription) widgetDescription).getFlexDirection()) + .children(childrenDescriptions) + .build(); + // @formatter:on + FormDescriptionEditorFlexboxContainerComponentProps fdeFlexboxContainerComponentProps = new FormDescriptionEditorFlexboxContainerComponentProps(fdeFlexboxContainerDescription); + childrenWidgets.add(new Element(FormDescriptionEditorFlexboxContainerComponent.class, fdeFlexboxContainerComponentProps)); + } else if (widgetDescription instanceof WidgetDescription) { + // @formatter:off + FormDescriptionEditorWidgetDescription fdeWidgetDescription = FormDescriptionEditorWidgetDescription.newFormDescriptionEditorWidgetDescription(widgetId) + .label(widgetLabel) + .kind(widgetKind) + .build(); + // @formatter:on + FormDescriptionEditorWidgetComponentProps fdeWidgetComponentProps = new FormDescriptionEditorWidgetComponentProps(fdeWidgetDescription); + childrenWidgets.add(new Element(FormDescriptionEditorWidgetComponent.class, fdeWidgetComponentProps)); } - // @formatter:off - return FormDescriptionEditorWidget.newFormDescriptionEditorWidget(widgetId) - .label(name) - .kind(this.getKind(widget)) - .build(); - // @formatter:on - }).collect(Collectors.toList()); + }); // @formatter:off FormDescriptionEditorElementProps formDescriptionEditorElementProps = FormDescriptionEditorElementProps.newFormDescriptionEditorElementProps(id) .label(label) .targetObjectId(targetObjectId) .descriptionId(formDescriptionEditorDescription.getId()) - .widgets(widgets) + .children(childrenWidgets) .build(); + // @formatter:on return new Element(FormDescriptionEditorElementProps.TYPE, formDescriptionEditorElementProps); - // @formatter:on } private String getKind(WidgetDescription widget) { return widget.eClass().getName().replace("Description", ""); //$NON-NLS-1$ //$NON-NLS-2$ } + + private List transformDescriptions(List widgetDescriptions, VariableManager variableManager, + Function targetObjectIdProvider) { + List fdeWidgetDescriptions = new ArrayList<>(); + + widgetDescriptions.forEach(widgetDescription -> { + + VariableManager childVariableManager = variableManager.createChild(); + childVariableManager.put(VariableManager.SELF, widgetDescription); + String widgetId = targetObjectIdProvider.apply(childVariableManager); + String widgetKind = this.getKind(widgetDescription); + String widgetLabel = widgetDescription.getName(); + if (widgetLabel == null) { + widgetLabel = widgetKind; + } + + if (widgetDescription instanceof FlexboxContainerDescription) { + List childrenDescriptions = this.transformDescriptions(((FlexboxContainerDescription) widgetDescription).getChildren(), + childVariableManager, targetObjectIdProvider); + // @formatter:off + FormDescriptionEditorFlexboxContainerDescription fdeFlexboxContainerDescription = FormDescriptionEditorFlexboxContainerDescription.newFormDescriptionEditorFlexboxContainerDescription(widgetId) + .label(widgetLabel) + .kind(widgetKind) + .flexDirection(((FlexboxContainerDescription) widgetDescription).getFlexDirection()) + .children(childrenDescriptions) + .build(); + // @formatter:on + fdeWidgetDescriptions.add(fdeFlexboxContainerDescription); + } else if (widgetDescription instanceof WidgetDescription) { + // @formatter:off + FormDescriptionEditorWidgetDescription fdeWidgetDescription = FormDescriptionEditorWidgetDescription.newFormDescriptionEditorWidgetDescription(widgetId) + .label(widgetLabel) + .kind(widgetKind) + .build(); + // @formatter:on + fdeWidgetDescriptions.add(fdeWidgetDescription); + } + }); + return fdeWidgetDescriptions; + } } diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorFlexboxContainerComponent.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorFlexboxContainerComponent.java new file mode 100644 index 0000000000..c21669c1ec --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorFlexboxContainerComponent.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors.components; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorFlexboxContainerDescription; +import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorWidgetDescription; +import org.eclipse.sirius.components.formdescriptioneditors.elements.FormDescriptionEditorFlexboxContainerElementProps; +import org.eclipse.sirius.components.representations.Element; +import org.eclipse.sirius.components.representations.Fragment; +import org.eclipse.sirius.components.representations.FragmentProps; +import org.eclipse.sirius.components.representations.IComponent; +import org.eclipse.sirius.components.view.FlexDirection; + +/** + * The component used to render the form description editor flexbox container. + * + * @author arichard + */ +public class FormDescriptionEditorFlexboxContainerComponent implements IComponent { + + private final FormDescriptionEditorFlexboxContainerComponentProps props; + + public FormDescriptionEditorFlexboxContainerComponent(FormDescriptionEditorFlexboxContainerComponentProps props) { + this.props = props; + } + + @Override + public Element render() { + FormDescriptionEditorFlexboxContainerDescription flexboxContainerDescription = this.props.getFormDescriptionEditorFlexboxContainerDescription(); + + String id = flexboxContainerDescription.getId(); + String label = flexboxContainerDescription.getLabel(); + String kind = flexboxContainerDescription.getKind(); + FlexDirection flexDirection = flexboxContainerDescription.getFlexDirection(); + + List childrenWidgets = new ArrayList<>(); + + flexboxContainerDescription.getChildren().forEach(widget -> { + if (widget instanceof FormDescriptionEditorWidgetDescription) { + childrenWidgets.add(new Element(FormDescriptionEditorWidgetComponent.class, new FormDescriptionEditorWidgetComponentProps((FormDescriptionEditorWidgetDescription) widget))); + } else if (widget instanceof FormDescriptionEditorFlexboxContainerDescription) { + childrenWidgets.add(new Element(FormDescriptionEditorFlexboxContainerComponent.class, + new FormDescriptionEditorFlexboxContainerComponentProps((FormDescriptionEditorFlexboxContainerDescription) widget))); + } + }); + + // @formatter:off + FormDescriptionEditorFlexboxContainerElementProps flexboxContainerElementProps = FormDescriptionEditorFlexboxContainerElementProps.newFormDescriptionEditorFlexboxContainerElementProps(id) + .label(label) + .kind(kind) + .flexDirection(flexDirection) + .children(childrenWidgets) + .build(); + // @formatter:on + + List children = new ArrayList<>(); + Element flexboxContainerElement = new Element(FormDescriptionEditorFlexboxContainerElementProps.TYPE, flexboxContainerElementProps); + children.add(flexboxContainerElement); + + FragmentProps fragmentProps = new FragmentProps(children); + return new Fragment(fragmentProps); + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorFlexboxContainerComponentProps.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorFlexboxContainerComponentProps.java new file mode 100644 index 0000000000..d4112d7eb1 --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorFlexboxContainerComponentProps.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors.components; + +import java.util.Objects; + +import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorFlexboxContainerDescription; +import org.eclipse.sirius.components.representations.IProps; + +/** + * * The properties of the form description editor flexbox container component. + * + * @author arichard + */ +public class FormDescriptionEditorFlexboxContainerComponentProps implements IProps { + + private final FormDescriptionEditorFlexboxContainerDescription fdeFlexboxContainerDescription; + + public FormDescriptionEditorFlexboxContainerComponentProps(FormDescriptionEditorFlexboxContainerDescription fdeFlexboxContainerDescription) { + this.fdeFlexboxContainerDescription = Objects.requireNonNull(fdeFlexboxContainerDescription); + } + + public FormDescriptionEditorFlexboxContainerDescription getFormDescriptionEditorFlexboxContainerDescription() { + return this.fdeFlexboxContainerDescription; + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorWidgetComponent.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorWidgetComponent.java new file mode 100644 index 0000000000..a5041c0e09 --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorWidgetComponent.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors.components; + +import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorWidgetDescription; +import org.eclipse.sirius.components.formdescriptioneditors.elements.FormDescriptionEditorWidgetElementProps; +import org.eclipse.sirius.components.representations.Element; +import org.eclipse.sirius.components.representations.IComponent; + +/** + * The component used to render the form description editor widget. + * + * @author arichard + */ +public class FormDescriptionEditorWidgetComponent implements IComponent { + + private final FormDescriptionEditorWidgetComponentProps props; + + public FormDescriptionEditorWidgetComponent(FormDescriptionEditorWidgetComponentProps props) { + this.props = props; + } + + @Override + public Element render() { + FormDescriptionEditorWidgetDescription formDescriptionEditorWidgetDescription = this.props.getFormDescriptionEditorWidgetDescription(); + + String id = formDescriptionEditorWidgetDescription.getId(); + String label = formDescriptionEditorWidgetDescription.getLabel(); + String kind = formDescriptionEditorWidgetDescription.getKind(); + + // @formatter:off + FormDescriptionEditorWidgetElementProps widgetPropsBuilder = FormDescriptionEditorWidgetElementProps.newFormDescriptionEditorWidgetElementProps(id) + .label(label) + .kind(kind) + .build(); + // @formatter:on + + return new Element(FormDescriptionEditorWidgetElementProps.TYPE, widgetPropsBuilder); + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorWidgetComponentProps.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorWidgetComponentProps.java new file mode 100644 index 0000000000..fac028e4a0 --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/components/FormDescriptionEditorWidgetComponentProps.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors.components; + +import java.util.Objects; + +import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorWidgetDescription; +import org.eclipse.sirius.components.representations.IProps; + +/** + * * The properties of the form description editor widget component. + * + * @author arichard + */ +public class FormDescriptionEditorWidgetComponentProps implements IProps { + + private final FormDescriptionEditorWidgetDescription formDescriptionEditorWidgetDescription; + + public FormDescriptionEditorWidgetComponentProps(FormDescriptionEditorWidgetDescription formDescriptionEditorWidgetDescription) { + this.formDescriptionEditorWidgetDescription = Objects.requireNonNull(formDescriptionEditorWidgetDescription); + } + + public FormDescriptionEditorWidgetDescription getFormDescriptionEditorWidgetDescription() { + return this.formDescriptionEditorWidgetDescription; + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/description/AbstractFormDescriptionEditorWidgetDescription.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/description/AbstractFormDescriptionEditorWidgetDescription.java new file mode 100644 index 0000000000..8b399f992b --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/description/AbstractFormDescriptionEditorWidgetDescription.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors.description; + +/** + * The root concept of the description of a form description editor widget. + * + * @author arichard + */ +public abstract class AbstractFormDescriptionEditorWidgetDescription { + + protected String id; + + protected String label; + + protected String kind; + + public String getId() { + return this.id; + } + + public String getLabel() { + return this.label; + } + + public String getKind() { + return this.kind; + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/description/FormDescriptionEditorFlexboxContainerDescription.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/description/FormDescriptionEditorFlexboxContainerDescription.java new file mode 100644 index 0000000000..9bbffd785b --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/description/FormDescriptionEditorFlexboxContainerDescription.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors.description; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.annotations.Immutable; +import org.eclipse.sirius.components.view.FlexDirection; + +/** + * The root concept of the description of a form description editor flexbox container widget. + * + * @author arichard + */ +@Immutable +public final class FormDescriptionEditorFlexboxContainerDescription extends AbstractFormDescriptionEditorWidgetDescription { + + private FlexDirection flexDirection; + + private List children; + + private FormDescriptionEditorFlexboxContainerDescription() { + // Prevent instantiation + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String getLabel() { + return this.label; + } + + @Override + public String getKind() { + return this.kind; + } + + public FlexDirection getFlexDirection() { + return this.flexDirection; + } + + public List getChildren() { + return this.children; + } + + public static Builder newFormDescriptionEditorFlexboxContainerDescription(String id) { + return new Builder(id); + } + + @Override + public String toString() { + String pattern = "{0} '{'id: {1}, label: {2}, kind: {3}, flexDirection: {4}'}'"; //$NON-NLS-1$ + return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.label, this.kind, this.flexDirection); + } + + /** + * Builder used to create the form description editor flexbox widget description. + * + * @author arichard + */ + @SuppressWarnings("checkstyle:HiddenField") + public static final class Builder { + private String id; + + private String label; + + private String kind; + + private FlexDirection flexDirection; + + private List children; + + private Builder(String id) { + this.id = Objects.requireNonNull(id); + } + + public Builder label(String label) { + this.label = Objects.requireNonNull(label); + return this; + } + + public Builder kind(String kind) { + this.kind = Objects.requireNonNull(kind); + return this; + } + + public Builder flexDirection(FlexDirection flexDirection) { + this.flexDirection = Objects.requireNonNull(flexDirection); + return this; + } + + public Builder children(List children) { + this.children = Objects.requireNonNull(children); + return this; + } + + public FormDescriptionEditorFlexboxContainerDescription build() { + FormDescriptionEditorFlexboxContainerDescription fdeFlexboxContainerDescription = new FormDescriptionEditorFlexboxContainerDescription(); + fdeFlexboxContainerDescription.id = Objects.requireNonNull(this.id); + fdeFlexboxContainerDescription.label = Objects.requireNonNull(this.label); + fdeFlexboxContainerDescription.kind = Objects.requireNonNull(this.kind); + fdeFlexboxContainerDescription.flexDirection = Objects.requireNonNull(this.flexDirection); + fdeFlexboxContainerDescription.children = Objects.requireNonNull(this.children); + return fdeFlexboxContainerDescription; + } + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/description/FormDescriptionEditorWidgetDescription.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/description/FormDescriptionEditorWidgetDescription.java new file mode 100644 index 0000000000..1a23290413 --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/description/FormDescriptionEditorWidgetDescription.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors.description; + +import java.text.MessageFormat; +import java.util.Objects; + +import org.eclipse.sirius.components.annotations.Immutable; + +/** + * The root concept of the description of a form description editor widget. + * + * @author arichard + */ +@Immutable +public final class FormDescriptionEditorWidgetDescription extends AbstractFormDescriptionEditorWidgetDescription { + + private FormDescriptionEditorWidgetDescription() { + // Prevent instantiation + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String getLabel() { + return this.label; + } + + @Override + public String getKind() { + return this.kind; + } + + public static Builder newFormDescriptionEditorWidgetDescription(String id) { + return new Builder(id); + } + + @Override + public String toString() { + String pattern = "{0} '{'id: {1}, label: {2}, kind: {3}'}'"; //$NON-NLS-1$ + return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.label, this.kind); + } + + /** + * Builder used to create the form description editor widget description. + * + * @author arichard + */ + @SuppressWarnings("checkstyle:HiddenField") + public static final class Builder { + private String id; + + private String label; + + private String kind; + + private Builder(String id) { + this.id = Objects.requireNonNull(id); + } + + public Builder label(String label) { + this.label = Objects.requireNonNull(label); + return this; + } + + public Builder kind(String kind) { + this.kind = Objects.requireNonNull(kind); + return this; + } + + public FormDescriptionEditorWidgetDescription build() { + FormDescriptionEditorWidgetDescription formDescriptionEditorDescription = new FormDescriptionEditorWidgetDescription(); + formDescriptionEditorDescription.id = Objects.requireNonNull(this.id); + formDescriptionEditorDescription.label = Objects.requireNonNull(this.label); + formDescriptionEditorDescription.kind = Objects.requireNonNull(this.kind); + return formDescriptionEditorDescription; + } + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorElementProps.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorElementProps.java index 76969eb219..1338733e22 100644 --- a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorElementProps.java +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorElementProps.java @@ -13,17 +13,15 @@ package org.eclipse.sirius.components.formdescriptioneditors.elements; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.eclipse.sirius.components.annotations.Immutable; -import org.eclipse.sirius.components.formdescriptioneditors.FormDescriptionEditorWidget; import org.eclipse.sirius.components.representations.Element; import org.eclipse.sirius.components.representations.IProps; /** - * The properties of the form description editor element. + * The properties of the form description editor widget element. * * @author arichard */ @@ -39,7 +37,7 @@ public final class FormDescriptionEditorElementProps implements IProps { private String descriptionId; - private List widgets; + private List children; private FormDescriptionEditorElementProps() { // Prevent instantiation @@ -61,13 +59,9 @@ public String getDescriptionId() { return this.descriptionId; } - public List getWidgets() { - return this.widgets; - } - @Override public List getChildren() { - return new ArrayList<>(); + return this.children; } public static Builder newFormDescriptionEditorElementProps(String id) { @@ -81,7 +75,7 @@ public String toString() { } /** - * The builder of the form description editor element props. + * The builder of the form description editor widget element props. * * @author arichard */ @@ -95,7 +89,7 @@ public static final class Builder { private String descriptionId; - private List widgets; + private List children; private Builder(String id) { this.id = Objects.requireNonNull(id); @@ -116,8 +110,8 @@ public Builder descriptionId(String descriptionId) { return this; } - public Builder widgets(List widgets) { - this.widgets = Objects.requireNonNull(widgets); + public Builder children(List children) { + this.children = Objects.requireNonNull(children); return this; } @@ -127,7 +121,7 @@ public FormDescriptionEditorElementProps build() { formDescriptionEditorElementProps.label = Objects.requireNonNull(this.label); formDescriptionEditorElementProps.targetObjectId = Objects.requireNonNull(this.targetObjectId); formDescriptionEditorElementProps.descriptionId = Objects.requireNonNull(this.descriptionId); - formDescriptionEditorElementProps.widgets = Objects.requireNonNull(this.widgets); + formDescriptionEditorElementProps.children = Objects.requireNonNull(this.children); return formDescriptionEditorElementProps; } } diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorFlexboxContainerElementProps.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorFlexboxContainerElementProps.java new file mode 100644 index 0000000000..2a767774e7 --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorFlexboxContainerElementProps.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors.elements; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.annotations.Immutable; +import org.eclipse.sirius.components.representations.Element; +import org.eclipse.sirius.components.representations.IProps; +import org.eclipse.sirius.components.view.FlexDirection; + +/** + * The properties of the form description editor flexbox container element. + * + * @author arichard + */ +@Immutable +public final class FormDescriptionEditorFlexboxContainerElementProps implements IProps { + public static final String TYPE = "FormDescriptionEditorFlexboxContainer"; //$NON-NLS-1$ + + private String id; + + private String label; + + private String kind; + + private FlexDirection flexDirection; + + private List children; + + private FormDescriptionEditorFlexboxContainerElementProps() { + // Prevent instantiation + } + + public String getId() { + return this.id; + } + + public String getLabel() { + return this.label; + } + + public String getKind() { + return this.kind; + } + + public FlexDirection getFlexDirection() { + return this.flexDirection; + } + + @Override + public List getChildren() { + return this.children; + } + + public static Builder newFormDescriptionEditorFlexboxContainerElementProps(String id) { + return new Builder(id); + } + + @Override + public String toString() { + String pattern = "{0} '{'id: {1}, label: {2}, kind: {3}, flexDirection: {4}'}'"; //$NON-NLS-1$ + return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.label, this.kind, this.flexDirection); + } + + /** + * The builder of the form description editor flexbox container element props. + * + * @author arichard + */ + @SuppressWarnings("checkstyle:HiddenField") + public static final class Builder { + private String id; + + private String label; + + private String kind; + + private FlexDirection flexDirection; + + private List children; + + private Builder(String id) { + this.id = Objects.requireNonNull(id); + } + + public Builder label(String label) { + this.label = Objects.requireNonNull(label); + return this; + } + + public Builder kind(String kind) { + this.kind = Objects.requireNonNull(kind); + return this; + } + + public Builder flexDirection(FlexDirection flexDirection) { + this.flexDirection = Objects.requireNonNull(flexDirection); + return this; + } + + public Builder children(List children) { + this.children = Objects.requireNonNull(children); + return this; + } + + public FormDescriptionEditorFlexboxContainerElementProps build() { + FormDescriptionEditorFlexboxContainerElementProps fdeFlexboxContainerElementProps = new FormDescriptionEditorFlexboxContainerElementProps(); + fdeFlexboxContainerElementProps.id = Objects.requireNonNull(this.id); + fdeFlexboxContainerElementProps.label = Objects.requireNonNull(this.label); + fdeFlexboxContainerElementProps.kind = Objects.requireNonNull(this.kind); + fdeFlexboxContainerElementProps.flexDirection = Objects.requireNonNull(this.flexDirection); + fdeFlexboxContainerElementProps.children = Objects.requireNonNull(this.children); + return fdeFlexboxContainerElementProps; + } + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorWidgetElementProps.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorWidgetElementProps.java new file mode 100644 index 0000000000..2f4ce6a610 --- /dev/null +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/elements/FormDescriptionEditorWidgetElementProps.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2022 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.formdescriptioneditors.elements; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.annotations.Immutable; +import org.eclipse.sirius.components.representations.Element; +import org.eclipse.sirius.components.representations.IProps; + +/** + * The properties of the form description editor element. + * + * @author arichard + */ +@Immutable +public final class FormDescriptionEditorWidgetElementProps implements IProps { + public static final String TYPE = "FormDescriptionEditorWidget"; //$NON-NLS-1$ + + private String id; + + private String label; + + private String kind; + + private FormDescriptionEditorWidgetElementProps() { + // Prevent instantiation + } + + public String getId() { + return this.id; + } + + public String getLabel() { + return this.label; + } + + public String getKind() { + return this.kind; + } + + @Override + public List getChildren() { + return new ArrayList<>(); + } + + public static Builder newFormDescriptionEditorWidgetElementProps(String id) { + return new Builder(id); + } + + @Override + public String toString() { + String pattern = "{0} '{'id: {1}, label: {2}'}'"; //$NON-NLS-1$ + return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.label); + } + + /** + * The builder of the form description editor element props. + * + * @author arichard + */ + @SuppressWarnings("checkstyle:HiddenField") + public static final class Builder { + private String id; + + private String label; + + private String kind; + + private Builder(String id) { + this.id = Objects.requireNonNull(id); + } + + public Builder label(String label) { + this.label = Objects.requireNonNull(label); + return this; + } + + public Builder kind(String kind) { + this.kind = Objects.requireNonNull(kind); + return this; + } + + public FormDescriptionEditorWidgetElementProps build() { + FormDescriptionEditorWidgetElementProps widgetElementProps = new FormDescriptionEditorWidgetElementProps(); + widgetElementProps.id = Objects.requireNonNull(this.id); + widgetElementProps.label = Objects.requireNonNull(this.label); + widgetElementProps.kind = Objects.requireNonNull(this.kind); + return widgetElementProps; + } + } +} diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorComponentPropsValidator.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorComponentPropsValidator.java index 07f434f79a..f0d2c5ffed 100644 --- a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorComponentPropsValidator.java +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorComponentPropsValidator.java @@ -14,6 +14,10 @@ import org.eclipse.sirius.components.formdescriptioneditors.components.FormDescriptionEditorComponent; import org.eclipse.sirius.components.formdescriptioneditors.components.FormDescriptionEditorComponentProps; +import org.eclipse.sirius.components.formdescriptioneditors.components.FormDescriptionEditorWidgetComponent; +import org.eclipse.sirius.components.formdescriptioneditors.components.FormDescriptionEditorWidgetComponentProps; +import org.eclipse.sirius.components.formdescriptioneditors.components.FormDescriptionEditorFlexboxContainerComponent; +import org.eclipse.sirius.components.formdescriptioneditors.components.FormDescriptionEditorFlexboxContainerComponentProps; import org.eclipse.sirius.components.representations.IComponentPropsValidator; import org.eclipse.sirius.components.representations.IProps; @@ -30,6 +34,10 @@ public boolean validateComponentProps(Class componentType, IProps props) { if (FormDescriptionEditorComponent.class.equals(componentType)) { checkValidProps = props instanceof FormDescriptionEditorComponentProps; + } else if (FormDescriptionEditorWidgetComponent.class.equals(componentType)) { + checkValidProps = props instanceof FormDescriptionEditorWidgetComponentProps; + } else if (FormDescriptionEditorFlexboxContainerComponent.class.equals(componentType)) { + checkValidProps = props instanceof FormDescriptionEditorFlexboxContainerComponentProps; } return checkValidProps; } diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorElementFactory.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorElementFactory.java index 19775f2a98..b51f1cc7bb 100644 --- a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorElementFactory.java +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorElementFactory.java @@ -13,9 +13,15 @@ package org.eclipse.sirius.components.formdescriptioneditors.renderer; import java.util.List; +import java.util.stream.Collectors; +import org.eclipse.sirius.components.formdescriptioneditors.AbstractFormDescriptionEditorWidget; import org.eclipse.sirius.components.formdescriptioneditors.FormDescriptionEditor; +import org.eclipse.sirius.components.formdescriptioneditors.FormDescriptionEditorFlexboxContainer; +import org.eclipse.sirius.components.formdescriptioneditors.FormDescriptionEditorWidget; import org.eclipse.sirius.components.formdescriptioneditors.elements.FormDescriptionEditorElementProps; +import org.eclipse.sirius.components.formdescriptioneditors.elements.FormDescriptionEditorFlexboxContainerElementProps; +import org.eclipse.sirius.components.formdescriptioneditors.elements.FormDescriptionEditorWidgetElementProps; import org.eclipse.sirius.components.representations.IElementFactory; import org.eclipse.sirius.components.representations.IProps; @@ -31,17 +37,53 @@ public Object instantiateElement(String type, IProps props, List childre Object object = null; if (FormDescriptionEditorElementProps.TYPE.equals(type) && props instanceof FormDescriptionEditorElementProps) { object = this.instantiateForm((FormDescriptionEditorElementProps) props, children); + } else if (FormDescriptionEditorWidgetElementProps.TYPE.equals(type) && props instanceof FormDescriptionEditorWidgetElementProps) { + object = this.instantiateWidget((FormDescriptionEditorWidgetElementProps) props, children); + } else if (FormDescriptionEditorFlexboxContainerElementProps.TYPE.equals(type) && props instanceof FormDescriptionEditorFlexboxContainerElementProps) { + object = this.instantiateFlexboxContainer((FormDescriptionEditorFlexboxContainerElementProps) props, children); } return object; } private FormDescriptionEditor instantiateForm(FormDescriptionEditorElementProps props, List children) { // @formatter:off + List widgets = children.stream() + .filter(AbstractFormDescriptionEditorWidget.class::isInstance) + .map(AbstractFormDescriptionEditorWidget.class::cast) + .collect(Collectors.toList()); + return FormDescriptionEditor.newFormDescriptionEditor(props.getId()) .label(props.getLabel()) .targetObjectId(props.getTargetObjectId()) .descriptionId(props.getDescriptionId()) - .widgets(props.getWidgets()) + .widgets(widgets) + .build(); + // @formatter:on + } + + private FormDescriptionEditorWidget instantiateWidget(FormDescriptionEditorWidgetElementProps props, List children) { + // @formatter:off + return FormDescriptionEditorWidget.newFormDescriptionEditorWidget(props.getId()) + .label(props.getLabel()) + .kind(props.getKind()) + .build(); + // @formatter:on + } + + private FormDescriptionEditorFlexboxContainer instantiateFlexboxContainer(FormDescriptionEditorFlexboxContainerElementProps props, List children) { + // @formatter:off + List widgets = children.stream() + .filter(AbstractFormDescriptionEditorWidget.class::isInstance) + .map(AbstractFormDescriptionEditorWidget.class::cast) + .collect(Collectors.toList()); + + return FormDescriptionEditorFlexboxContainer.newFormDescriptionEditorFlexboxContainer(props.getId()) + .label(props.getLabel()) + .kind(props.getKind()) + .flexDirection(props.getFlexDirection().getLiteral()) + .flexWrap("wrap") //$NON-NLS-1$ + .flexGrow(1) + .children(widgets) .build(); // @formatter:on } diff --git a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorInstancePropsValidator.java b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorInstancePropsValidator.java index 511a8f2498..a355b91da2 100644 --- a/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorInstancePropsValidator.java +++ b/backend/sirius-components-formdescriptioneditors/src/main/java/org/eclipse/sirius/components/formdescriptioneditors/renderer/FormDescriptionEditorInstancePropsValidator.java @@ -13,6 +13,8 @@ package org.eclipse.sirius.components.formdescriptioneditors.renderer; import org.eclipse.sirius.components.formdescriptioneditors.elements.FormDescriptionEditorElementProps; +import org.eclipse.sirius.components.formdescriptioneditors.elements.FormDescriptionEditorFlexboxContainerElementProps; +import org.eclipse.sirius.components.formdescriptioneditors.elements.FormDescriptionEditorWidgetElementProps; import org.eclipse.sirius.components.representations.IInstancePropsValidator; import org.eclipse.sirius.components.representations.IProps; @@ -29,6 +31,10 @@ public boolean validateInstanceProps(String type, IProps props) { if (FormDescriptionEditorElementProps.TYPE.equals(type)) { checkValidProps = props instanceof FormDescriptionEditorElementProps; + } else if (FormDescriptionEditorWidgetElementProps.TYPE.equals(type)) { + checkValidProps = props instanceof FormDescriptionEditorWidgetElementProps; + } else if (FormDescriptionEditorFlexboxContainerElementProps.TYPE.equals(type)) { + checkValidProps = props instanceof FormDescriptionEditorFlexboxContainerElementProps; } return checkValidProps; } diff --git a/frontend/src/formdescriptioneditor/FlexboxContainerWidget.tsx b/frontend/src/formdescriptioneditor/FlexboxContainerWidget.tsx new file mode 100644 index 0000000000..38f13e52be --- /dev/null +++ b/frontend/src/formdescriptioneditor/FlexboxContainerWidget.tsx @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 2022 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 { useMutation } from '@apollo/client'; +import IconButton from '@material-ui/core/IconButton'; +import Snackbar from '@material-ui/core/Snackbar'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import CloseIcon from '@material-ui/icons/Close'; +import React, { useEffect, useRef, useState } from 'react'; +import { v4 as uuid } from 'uuid'; +import { FlexboxContainerWidgetState, FlexboxContainerWidgetStyleProps } from './FlexboxContainerWidget.types'; +import { addWidgetMutation, moveWidgetMutation } from './FormDescriptionEditorEventFragment'; +import { + GQLAddWidgetInput, + GQLAddWidgetMutationData, + GQLAddWidgetMutationVariables, + GQLAddWidgetPayload, + GQLErrorPayload, + GQLMoveWidgetInput, + GQLMoveWidgetMutationData, + GQLMoveWidgetMutationVariables, + GQLMoveWidgetPayload, +} from './FormDescriptionEditorEventFragment.types'; +import { WidgetEntry } from './WidgetEntry'; +import { FlexboxContainerWidgetProps } from './WidgetEntry.types'; +import { isKind } from './WidgetOperations'; + +const isErrorPayload = (payload: GQLAddWidgetPayload | GQLMoveWidgetPayload): payload is GQLErrorPayload => + payload.__typename === 'ErrorPayload'; + +const useStyles = makeStyles((theme) => ({ + selected: { + color: theme.palette.primary.main, + }, + widget: { + border: 'solid 1px lightgrey', + padding: '0px 0px 0px 4px', + width: '100%', + }, + container: { + display: 'flex', + flexWrap: ({ flexWrap }) => flexWrap, + flexDirection: ({ flexDirection }) => flexDirection, + '& > *': { + marginBottom: theme.spacing(0), + }, + }, + bottomDropArea: { + height: '30px', + width: '100%', + }, + dragOver: { + border: 'dashed 1px red', + }, +})); + +export const FlexboxContainerWidget = ({ + editingContextId, + representationId, + widget, + selection, + setSelection, +}: FlexboxContainerWidgetProps) => { + const classes = useStyles({ + flexDirection: widget.flexDirection, + flexWrap: widget.flexWrap, + }); + + const initialState: FlexboxContainerWidgetState = { message: null, selected: false }; + const [state, setState] = useState(initialState); + const { message, selected } = state; + + const [addWidget, { loading: addWidgetLoading, data: addWidgetData, error: addWidgetError }] = useMutation< + GQLAddWidgetMutationData, + GQLAddWidgetMutationVariables + >(addWidgetMutation); + + useEffect(() => { + if (!addWidgetLoading) { + if (addWidgetError) { + setState((prevState) => { + return { ...prevState, message: addWidgetError.message }; + }); + } + if (addWidgetData) { + const { addWidget } = addWidgetData; + if (isErrorPayload(addWidget)) { + setState((prevState) => { + return { ...prevState, message: addWidget.message }; + }); + } + } + } + }, [addWidgetLoading, addWidgetData, addWidgetError]); + + const ref = useRef(null); + + useEffect(() => { + if (ref.current && selection.entries.find((entry) => entry.id === widget.id)) { + ref.current.focus(); + setState((prevState) => { + return { ...prevState, selected: true }; + }); + } else { + setState((prevState) => { + return { ...prevState, selected: false }; + }); + } + }, [selection, widget]); + + const [moveWidget, { loading: moveWidgetLoading, data: moveWidgetData, error: moveWidgetError }] = useMutation< + GQLMoveWidgetMutationData, + GQLMoveWidgetMutationVariables + >(moveWidgetMutation); + + useEffect(() => { + if (!moveWidgetLoading) { + if (moveWidgetError) { + setState((prevState) => { + return { ...prevState, message: moveWidgetError.message }; + }); + } + if (moveWidgetData) { + const { moveWidget } = moveWidgetData; + if (isErrorPayload(moveWidget)) { + setState((prevState) => { + return { ...prevState, message: moveWidget.message }; + }); + } + } + } + }, [moveWidgetLoading, moveWidgetData, moveWidgetError]); + + const handleDragEnter: React.DragEventHandler = (event) => { + event.preventDefault(); + event.currentTarget.classList.add(classes.dragOver); + }; + const handleDragOver: React.DragEventHandler = (event) => { + event.preventDefault(); + event.currentTarget.classList.add(classes.dragOver); + }; + const handleDragLeave: React.DragEventHandler = (event) => { + event.preventDefault(); + event.currentTarget.classList.remove(classes.dragOver); + }; + const handleDrop: React.DragEventHandler = (event) => { + event.preventDefault(); + event.currentTarget.classList.remove(classes.dragOver); + + const id: string = event.dataTransfer.getData('text/plain'); + + if (isKind(id)) { + const addWidgetInput: GQLAddWidgetInput = { + id: uuid(), + editingContextId, + representationId, + containerId: widget.id, + kind: id, + index: widget.children.length, + }; + const addWidgetVariables: GQLAddWidgetMutationVariables = { input: addWidgetInput }; + addWidget({ variables: addWidgetVariables }); + } else { + const moveWidgetInput: GQLMoveWidgetInput = { + id: uuid(), + editingContextId, + representationId, + containerId: widget.id, + widgetId: id, + index: widget.children.length, + }; + const moveWidgetVariables: GQLMoveWidgetMutationVariables = { input: moveWidgetInput }; + moveWidget({ variables: moveWidgetVariables }); + } + }; + + let children = widget.children.map((childWidget) => { + return ( + + ); + }); + + return ( +
+ + setState((prevState) => { + return { ...prevState, selected: true }; + }) + } + onBlur={() => + setState((prevState) => { + return { ...prevState, selected: false }; + }) + } + > + {widget.label} + +
{children}
+
+ + setState((prevState) => { + return { ...prevState, message: null }; + }) + } + message={message} + action={ + + setState((prevState) => { + return { ...prevState, message: null }; + }) + } + > + + + } + data-testid="error" + /> +
+ ); +}; diff --git a/frontend/src/formdescriptioneditor/FlexboxContainerWidget.types.tsx b/frontend/src/formdescriptioneditor/FlexboxContainerWidget.types.tsx new file mode 100644 index 0000000000..baa788271f --- /dev/null +++ b/frontend/src/formdescriptioneditor/FlexboxContainerWidget.types.tsx @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2022 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 { FlexDirection, FlexWrap } from 'form/Form.types'; + +export interface FlexboxContainerWidgetState { + message: string | null; + selected: boolean; +} + +export interface FlexboxContainerWidgetStyleProps { + flexDirection: FlexDirection; + flexWrap: FlexWrap; +} diff --git a/frontend/src/formdescriptioneditor/FormDescriptionEditorEventFragment.ts b/frontend/src/formdescriptioneditor/FormDescriptionEditorEventFragment.ts index 1939807487..81f53a3a45 100644 --- a/frontend/src/formdescriptioneditor/FormDescriptionEditorEventFragment.ts +++ b/frontend/src/formdescriptioneditor/FormDescriptionEditorEventFragment.ts @@ -41,6 +41,36 @@ export const formDescriptionEditorEventSubscription = gql` id label kind + ... on FormDescriptionEditorFlexboxContainer { + flexDirection + flexWrap + flexGrow + children { + id + label + kind + ... on FormDescriptionEditorFlexboxContainer { + flexDirection + flexWrap + flexGrow + children { + id + label + kind + ... on FormDescriptionEditorFlexboxContainer { + flexDirection + flexWrap + flexGrow + children { + id + label + kind + } + } + } + } + } + } } } } diff --git a/frontend/src/formdescriptioneditor/FormDescriptionEditorEventFragment.types.ts b/frontend/src/formdescriptioneditor/FormDescriptionEditorEventFragment.types.ts index 6665046251..3e4f9f53db 100644 --- a/frontend/src/formdescriptioneditor/FormDescriptionEditorEventFragment.types.ts +++ b/frontend/src/formdescriptioneditor/FormDescriptionEditorEventFragment.types.ts @@ -11,6 +11,8 @@ * Obeo - initial API and implementation *******************************************************************************/ +import { GQLFlexDirection, GQLFlexWrap } from 'form/FormEventFragments.types'; + export interface GQLFormDescriptionEditorEventSubscription { formDescriptionEditorEvent: GQLFormDescriptionEditorEventPayload; } @@ -69,19 +71,29 @@ export interface GQLRepresentationDescription { export interface GQLFormDescriptionEditor extends GQLRepresentation { id: string; metadata: GQLRepresentationMetadata; - widgets: GQLFormDescriptionEditorWidget[]; + widgets: GQLAbstractFormDescriptionEditorWidget[]; } -export interface GQLFormDescriptionEditorWidget { +export interface GQLAbstractFormDescriptionEditorWidget { id: string; label: string; kind: string; } +export interface GQLFormDescriptionEditorWidget extends GQLAbstractFormDescriptionEditorWidget {} + +export interface GQLFormDescriptionEditorFlexboxContainer extends GQLAbstractFormDescriptionEditorWidget { + flexDirection: GQLFlexDirection; + flexWrap: GQLFlexWrap; + flexGrow: number; + children: GQLAbstractFormDescriptionEditorWidget[]; +} + export interface GQLAddWidgetInput { id: string; editingContextId: string; representationId: string; + containerId: string | null; kind: string; index: number; } @@ -129,6 +141,7 @@ export interface GQLMoveWidgetInput { id: string; editingContextId: string; representationId: string; + containerId: string | null; widgetId: string; index: number; } diff --git a/frontend/src/formdescriptioneditor/FormDescriptionEditorWebSocketContainer.tsx b/frontend/src/formdescriptioneditor/FormDescriptionEditorWebSocketContainer.tsx index 8654ffa722..8a543d16bb 100644 --- a/frontend/src/formdescriptioneditor/FormDescriptionEditorWebSocketContainer.tsx +++ b/frontend/src/formdescriptioneditor/FormDescriptionEditorWebSocketContainer.tsx @@ -39,7 +39,6 @@ import { GQLMoveWidgetMutationVariables, GQLMoveWidgetPayload, } from 'formdescriptioneditor/FormDescriptionEditorEventFragment.types'; -import { Kind } from 'formdescriptioneditor/FormDescriptionEditorWebSocketContainer.types'; import { FormDescriptionEditorWebSocketContainerContext, FormDescriptionEditorWebSocketContainerEvent, @@ -51,6 +50,7 @@ import { ShowToastEvent, } from 'formdescriptioneditor/FormDescriptionEditorWebSocketContainerMachine'; import { WidgetEntry } from 'formdescriptioneditor/WidgetEntry'; +import { FlexboxContainer } from 'icons'; import React, { useEffect } from 'react'; import { v4 as uuid } from 'uuid'; import { RepresentationComponentProps } from 'workbench/Workbench.types'; @@ -59,6 +59,7 @@ import { formDescriptionEditorEventSubscription, moveWidgetMutation, } from './FormDescriptionEditorEventFragment'; +import { isKind } from './WidgetOperations'; const isErrorPayload = (payload: GQLAddWidgetPayload | GQLMoveWidgetPayload): payload is GQLErrorPayload => payload.__typename === 'ErrorPayload'; @@ -97,6 +98,7 @@ const useFormDescriptionEditorStyles = makeStyles((theme) => ({ preview: { width: '100%', padding: '4px 8px 4px 8px', + overflowY: 'scroll', }, body: { display: 'flex', @@ -130,21 +132,13 @@ const useFormDescriptionEditorStyles = makeStyles((theme) => ({ alignItems: 'center', justifyItems: 'center', }, + icon: { + width: '24px', + height: '24px', + fill: theme.palette.secondary.main, + }, })); -const isKind = (value: string): value is Kind => { - return ( - value === 'Textfield' || - value === 'TextArea' || - value === 'Checkbox' || - value === 'Radio' || - value === 'Select' || - value === 'MultiSelect' || - value === 'BarChart' || - value === 'PieChart' - ); -}; - export const FormDescriptionEditorWebSocketContainer = ({ editingContextId, representationId, @@ -276,6 +270,7 @@ export const FormDescriptionEditorWebSocketContainer = ({ id: uuid(), editingContextId, representationId, + containerId: null, kind: id, index: formDescriptionEditor.widgets.length, }; @@ -290,6 +285,7 @@ export const FormDescriptionEditorWebSocketContainer = ({ id: uuid(), editingContextId, representationId, + containerId: null, widgetId: id, index, }; @@ -401,6 +397,18 @@ export const FormDescriptionEditorWebSocketContainer = ({ PieChart
+
+ + + Flexbox Container + +
Preview @@ -410,10 +418,13 @@ export const FormDescriptionEditorWebSocketContainer = ({ key={widget.id} editingContextId={editingContextId} representationId={representationId} - formDescriptionEditor={formDescriptionEditor} + containerId={null} + siblings={formDescriptionEditor.widgets} widget={widget} selection={selection} setSelection={setSelection} + flexDirection={'column'} + flexGrow={1} /> ))}
({ +const useWidgetEntryStyles = makeStyles(() => ({ widget: { display: 'flex', - flexDirection: 'column', - alignItems: 'stretch', + flexDirection: ({ flexDirection }) => flexDirection, + flexGrow: ({ flexGrow }) => flexGrow, + }, + widgetElement: { + flexGrow: ({ flexGrow }) => flexGrow, }, placeholder: { - height: '8px', + height: ({ flexDirection }) => + flexDirection === 'column' || flexDirection === 'column-reverse' ? '10px' : 'inherit', + width: ({ flexDirection }) => (flexDirection === 'row' || flexDirection === 'row-reverse' ? '10px' : 'inherit'), }, dragOver: { border: 'dashed 1px red', @@ -64,28 +71,18 @@ const isErrorPayload = ( payload: GQLAddWidgetPayload | GQLDeleteWidgetPayload | GQLMoveWidgetPayload ): payload is GQLErrorPayload => payload.__typename === 'ErrorPayload'; -const isKind = (value: string): value is Kind => { - return ( - value === 'Textfield' || - value === 'TextArea' || - value === 'Checkbox' || - value === 'Radio' || - value === 'Select' || - value === 'MultiSelect' || - value === 'BarChart' || - value === 'PieChart' - ); -}; - export const WidgetEntry = ({ editingContextId, representationId, - formDescriptionEditor, + containerId, + siblings, widget, selection, setSelection, + flexDirection, + flexGrow, }: WidgetEntryProps) => { - const classes = useWidgetEntryStyles(); + const classes = useWidgetEntryStyles({ flexDirection, flexGrow }); const initialState: WidgetEntryState = { message: null }; const [state, setState] = useState(initialState); @@ -100,18 +97,14 @@ export const WidgetEntry = ({ if (!addWidgetLoading) { if (addWidgetError) { setState((prevState) => { - const newState = { ...prevState }; - newState.message = addWidgetError.message; - return newState; + return { ...prevState, message: addWidgetError.message }; }); } if (addWidgetData) { const { addWidget } = addWidgetData; if (isErrorPayload(addWidget)) { setState((prevState) => { - const newState = { ...prevState }; - newState.message = addWidget.message; - return newState; + return { ...prevState, message: addWidget.message }; }); } } @@ -125,18 +118,14 @@ export const WidgetEntry = ({ if (!deleteWidgetLoading) { if (deleteWidgetError) { setState((prevState) => { - const newState = { ...prevState }; - newState.message = deleteWidgetError.message; - return newState; + return { ...prevState, message: deleteWidgetError.message }; }); } if (deleteWidgetData) { const { deleteWidget } = deleteWidgetData; if (isErrorPayload(deleteWidget)) { setState((prevState) => { - const newState = { ...prevState }; - newState.message = deleteWidget.message; - return newState; + return { ...prevState, message: deleteWidget.message }; }); } } @@ -152,18 +141,14 @@ export const WidgetEntry = ({ if (!moveWidgetLoading) { if (moveWidgetError) { setState((prevState) => { - const newState = { ...prevState }; - newState.message = moveWidgetError.message; - return newState; + return { ...prevState, message: moveWidgetError.message }; }); } if (moveWidgetData) { const { moveWidget } = moveWidgetData; if (isErrorPayload(moveWidget)) { setState((prevState) => { - const newState = { ...prevState }; - newState.message = moveWidget.message; - return newState; + return { ...prevState, message: moveWidget.message }; }); } } @@ -181,6 +166,7 @@ export const WidgetEntry = ({ ], }; setSelection(newSelection); + event.stopPropagation(); }; const handleDelete: React.KeyboardEventHandler = (event) => { @@ -194,13 +180,14 @@ export const WidgetEntry = ({ }; const deleteWidgetVariables: GQLDeleteWidgetMutationVariables = { input: deleteWidgetInput }; deleteWidget({ variables: deleteWidgetVariables }); + event.stopPropagation(); } }; const handleDragStart: React.DragEventHandler = (event) => { event.dataTransfer.setData('text/plain', widget.id); + event.stopPropagation(); }; - const handleDragEnter: React.DragEventHandler = (event) => { event.preventDefault(); event.currentTarget.classList.add(classes.dragOver); @@ -222,8 +209,7 @@ export const WidgetEntry = ({ const onDropBefore = (event: React.DragEvent, widget: GQLFormDescriptionEditorWidget) => { const id: string = event.dataTransfer.getData('text/plain'); - const existingWidgets: GQLFormDescriptionEditorWidget[] = formDescriptionEditor.widgets; - let index: number = existingWidgets.indexOf(widget); + let index: number = siblings.indexOf(widget); if (index <= 0) { index = 0; } @@ -233,20 +219,22 @@ export const WidgetEntry = ({ id: uuid(), editingContextId, representationId, + containerId, kind: id, index, }; const addWidgetVariables: GQLAddWidgetMutationVariables = { input: addWidgetInput }; addWidget({ variables: addWidgetVariables }); } else { - const movedWidgetIndex = formDescriptionEditor.widgets.findIndex((w) => w.id === id); - if (movedWidgetIndex < index) { + const movedWidgetIndex = siblings.findIndex((w) => w.id === id); + if (movedWidgetIndex > -1 && movedWidgetIndex < index) { index = index - 1; } const moveWidgetInput: GQLMoveWidgetInput = { id: uuid(), editingContextId, representationId, + containerId, widgetId: id, index, }; @@ -336,6 +324,17 @@ export const WidgetEntry = ({ onDropBefore={onDropBefore} /> ); + } else if (widget.kind === 'FlexboxContainer') { + widgetElement = ( + + ); } return (
- {widgetElement} +
{widgetElement}
void; + flexDirection: FlexDirection; + flexGrow: number; } export interface WidgetProps { @@ -30,6 +38,19 @@ export interface WidgetProps { onDropBefore: (event: React.DragEvent, widget: GQLFormDescriptionEditorWidget) => void; } +export interface FlexboxContainerWidgetProps { + editingContextId: string; + representationId: string; + widget: GQLFormDescriptionEditorFlexboxContainer; + selection: Selection; + setSelection: (newSelection: Selection) => void; +} + export interface WidgetEntryState { message: string | null; } + +export interface WidgetEntryStyleProps { + flexDirection: FlexDirection; + flexGrow: number; +} diff --git a/frontend/src/formdescriptioneditor/WidgetOperations.tsx b/frontend/src/formdescriptioneditor/WidgetOperations.tsx new file mode 100644 index 0000000000..29900b5de8 --- /dev/null +++ b/frontend/src/formdescriptioneditor/WidgetOperations.tsx @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2022 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 { Kind } from './FormDescriptionEditorWebSocketContainer.types'; + +export const isKind = (value: string): value is Kind => { + return ( + value === 'Textfield' || + value === 'TextArea' || + value === 'Checkbox' || + value === 'Radio' || + value === 'Select' || + value === 'MultiSelect' || + value === 'BarChart' || + value === 'PieChart' || + value === 'FlexboxContainer' + ); +}; diff --git a/frontend/src/formdescriptioneditor/__tests__/WidgetEntry.test.tsx b/frontend/src/formdescriptioneditor/__tests__/WidgetEntry.test.tsx index d6ebaff58d..08e7be4880 100644 --- a/frontend/src/formdescriptioneditor/__tests__/WidgetEntry.test.tsx +++ b/frontend/src/formdescriptioneditor/__tests__/WidgetEntry.test.tsx @@ -48,6 +48,7 @@ const addWidgetVariables: GQLAddWidgetMutationVariables = { id: '48be95fc-3422-45d3-b1f9-d590e847e9e1', editingContextId: 'editingContextId', representationId: 'formDescriptionEditorId', + containerId: 'containerId', kind: 'Textfield', index: 0, }, @@ -85,6 +86,7 @@ const moveWidgetVariables: GQLMoveWidgetMutationVariables = { id: '48be95fc-3422-45d3-b1f9-d590e847e9e1', editingContextId: 'editingContextId', representationId: 'formDescriptionEditorId', + containerId: 'containerId', widgetId: 'Textfield2', index: 0, }, @@ -132,10 +134,13 @@ test('should drop the Textfield in the drop area', async () => { ); @@ -191,10 +196,13 @@ test('should delete the Textfield from the drop area', async () => { ); @@ -249,10 +257,13 @@ test('should delete the PieChart from the drop area', async () => { ); @@ -309,10 +320,13 @@ test('should move the existing Textfield from/into the drop area', async () => { ); diff --git a/frontend/src/icons/FlexboxContainer.tsx b/frontend/src/icons/FlexboxContainer.tsx new file mode 100644 index 0000000000..cb56e846a3 --- /dev/null +++ b/frontend/src/icons/FlexboxContainer.tsx @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2022 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 PropTypes from 'prop-types'; +import React from 'react'; + +const propTypes = { + title: PropTypes.string.isRequired, +}; + +export const FlexboxContainer = ({ title, ...props }) => { + return ( + + {title} + + + ); +}; +FlexboxContainer.propTypes = propTypes; diff --git a/frontend/src/icons/index.ts b/frontend/src/icons/index.ts index 7f44ed7731..48968cb2fc 100644 --- a/frontend/src/icons/index.ts +++ b/frontend/src/icons/index.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2021 Obeo. + * Copyright (c) 2019, 2022 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 @@ -17,6 +17,7 @@ export * from './Danger'; export * from './Delete'; export * from './Edit'; export * from './Exit'; +export * from './FlexboxContainer'; export * from './More'; export * from './NewDocument'; export * from './NewRepresentation'; diff --git a/frontend/src/properties/Page.tsx b/frontend/src/properties/Page.tsx index 4bbd80da83..2b0ebfffcf 100644 --- a/frontend/src/properties/Page.tsx +++ b/frontend/src/properties/Page.tsx @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2021 Obeo. + * Copyright (c) 2019, 2022 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 @@ -22,6 +22,7 @@ const usePageStyles = makeStyles((theme) => ({ '& > *': { marginBottom: theme.spacing(2), }, + overflowY: 'scroll', }, })); diff --git a/frontend/src/properties/propertysections/FlexboxContainerPropertySection.tsx b/frontend/src/properties/propertysections/FlexboxContainerPropertySection.tsx index d612cdaebc..d441a88ada 100644 --- a/frontend/src/properties/propertysections/FlexboxContainerPropertySection.tsx +++ b/frontend/src/properties/propertysections/FlexboxContainerPropertySection.tsx @@ -19,7 +19,7 @@ import { import { PropertySectionLabel } from 'properties/propertysections/PropertySectionLabel'; import React from 'react'; -const useWidgetContainerPropertySectionStyles = makeStyles( +const useFlexboxContainerPropertySectionStyles = makeStyles( (theme) => ({ container: { display: 'flex', @@ -43,7 +43,7 @@ export const FlexboxContainerPropertySection = ({ setSelection, readOnly, }: FlexboxContainerPropertySectionProps) => { - const classes = useWidgetContainerPropertySectionStyles({ + const classes = useFlexboxContainerPropertySectionStyles({ flexDirection: widget.flexDirection, flexWrap: widget.flexWrap, flexGrow: widget.flexGrow,