Skip to content

Commit

Permalink
[2480] Merge Rectangle and Image nodes into FreeFormNode
Browse files Browse the repository at this point in the history
Bug: #2480
Signed-off-by: Guillaume Coutable <guillaume.coutable@obeo.fr>
Signed-off-by: Stéphane Bégaudeau <stephane.begaudeau@obeo.fr>
Signed-off-by: Florian ROUËNÉ <florian.rouene@obeosoft.com>
  • Loading branch information
gcoutable authored and sbegaudeau committed Feb 13, 2024
1 parent c4a2e23 commit a5e6450
Show file tree
Hide file tree
Showing 21 changed files with 126 additions and 369 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.adoc
Expand Up @@ -104,6 +104,7 @@ In read-only mode, the palette can not be displayed, some of the panel action ar
- https://github.com/eclipse-sirius/sirius-web/issues/3020[#3020] [sirius-web] Simplify the contribution of views in the workbench
- https://github.com/eclipse-sirius/sirius-web/issues/3066[#3066] [core] The core object-related services now can also find/resolve representations.
- [portal] Use MUI tooltips for Portal toolbar elements
- https://github.com/eclipse-sirius/sirius-web/issues/2480[#2480] [diagram] Merge the rectangle node and the image node into FreeFormNode.

== v2024.1.0

Expand Down Expand Up @@ -258,7 +259,7 @@ The size of nodes that do not have been resized manually tries to fit the node d
- https://github.com/eclipse-sirius/sirius-web/issues/2644[#2644] [diagram] Highlight nodes on hover in React Flow diagrams
- https://github.com/eclipse-sirius/sirius-web/issues/2766[#2766] [tree] Tree representations (including the explorer) now support dragging any kind of element (not just semantic elements).
It is the responsibility of the drop targets (e.g. a diagram) to validate the dropped element(s) type and ignore the one it does not support.
- https://github.com/eclipse-sirius/sirius-web/issues/2772[#2772] Improve performance of the Palette
- https://github.com/eclipse-sirius/sirius-web/issues/2772[#2772] [diagram] Improve the Palette performance.
- https://github.com/eclipse-sirius/sirius-web/issues/2679[#2679] [diagram] When editing a label, place input element closer to the label's location (centered).
- https://github.com/eclipse-sirius/sirius-web/issues/2804[#2804] [sirius-web] Add the ability to declare custom nodes while using the `sirius-web-application` frontend package.
- https://github.com/eclipse-sirius/sirius-web/issues/2718[#2718] Replacing useNodes and useEdges by useReactFlow to improve performances.
Expand Down
Expand Up @@ -38,8 +38,7 @@ describe('/projects/:projectId/edit - Diagram', () => {
cy.getByTestId('rf__wrapper').should('exist');
cy.get('.react-flow__edgelabel-renderer').children().should('have.length', 7);
cy.get('.react-flow__nodes').children().should('have.length', 18);
cy.get('.react-flow__node-rectangularNode').should('have.length', 2);
cy.get('.react-flow__node-imageNode').should('have.length', 10);
cy.get('.react-flow__node-freeFormNode').should('have.length', 12);
cy.get('.react-flow__node-listNode').should('have.length', 2);
cy.get('.react-flow__node-iconLabelNode').should('have.length', 4);
});
Expand Down
Expand Up @@ -33,7 +33,7 @@ const toIconLabelNode = (
gqlDiagram: GQLDiagram,
gqlNode: GQLNode<GQLIconLabelNodeStyle>,
gqlParentNode: GQLNode<GQLNodeStyle> | null,
nodeDescription: GQLNodeDescription | undefined,
nodeDescription: GQLNodeDescription,
isBorderNode: boolean
): Node<IconLabelNodeData> => {
const {
Expand Down Expand Up @@ -144,6 +144,8 @@ export class IconLabelNodeConverter implements INodeConverter {
nodeDescriptions: GQLNodeDescription[]
) {
const nodeDescription = nodeDescriptions.find((description) => description.id === gqlNode.descriptionId);
nodes.push(toIconLabelNode(gqlDiagram, gqlNode, parentNode, nodeDescription, isBorderNode));
if (nodeDescription) {
nodes.push(toIconLabelNode(gqlDiagram, gqlNode, parentNode, nodeDescription, isBorderNode));
}
}
}
Expand Up @@ -17,10 +17,10 @@ import { GQLEdge } from '../graphql/subscription/edgeFragment.types';
import { GQLImageNodeStyle, GQLNode, GQLNodeStyle, GQLViewModifier } from '../graphql/subscription/nodeFragment.types';
import { BorderNodePosition } from '../renderer/DiagramRenderer.types';
import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types';
import { ImageNodeData } from '../renderer/node/ImageNode.types';
import { FreeFormNodeData } from '../renderer/node/FreeFormNode.types';
import { GQLDiagramDescription } from '../representation/DiagramRepresentation.types';
import { IConvertEngine, INodeConverter } from './ConvertEngine.types';
import { AlignmentMap } from './convertDiagram.types';
import { convertLineStyle } from './convertDiagram';
import { convertHandles } from './convertHandles';
import { convertLabelStyle, convertOutsideLabels } from './convertLabel';

Expand All @@ -30,10 +30,10 @@ const toImageNode = (
gqlDiagram: GQLDiagram,
gqlNode: GQLNode<GQLImageNodeStyle>,
gqlParentNode: GQLNode<GQLNodeStyle> | null,
nodeDescription: GQLNodeDescription | undefined,
nodeDescription: GQLNodeDescription,
isBorderNode: boolean,
gqlEdges: GQLEdge[]
): Node<ImageNodeData> => {
): Node<FreeFormNodeData> => {
const {
targetObjectId,
targetObjectLabel,
Expand All @@ -55,15 +55,20 @@ const toImageNode = (
const isNew = gqlNodeLayoutData === undefined;
const resizedByUser = gqlNodeLayoutData?.resizedByUser ?? false;

const data: ImageNodeData = {
const data: FreeFormNodeData = {
targetObjectId,
targetObjectLabel,
targetObjectKind,
descriptionId,
insideLabel: null,
outsideLabels: convertOutsideLabels(outsideLabels),
imageURL: style.imageURL,
style: {},
style: {
borderColor: style.borderColor,
borderRadius: style.borderRadius,
borderWidth: style.borderSize,
borderStyle: convertLineStyle(style.borderStyle),
},
faded: state === GQLViewModifier.Faded,
pinned,
nodeDescription,
Expand All @@ -79,39 +84,29 @@ const toImageNode = (
};

if (insideLabel) {
const labelStyle = insideLabel.style;
data.insideLabel = {
id: insideLabel.id,
text: insideLabel.text,
iconURL: labelStyle.iconURL,
style: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
padding: '8px 16px',
textAlign: 'center',
...convertLabelStyle(labelStyle),
const {
id,
text,
style: labelStyle,
style: { iconURL },
} = insideLabel;
data.outsideLabels = {
BOTTOM_MIDDLE: {
id,
text,
iconURL,
style: {
...convertLabelStyle(labelStyle),
justifyContent: 'center',
padding: '8px 16px',
},
},
isHeader: insideLabel.isHeader,
displayHeaderSeparator: insideLabel.displayHeaderSeparator,
};

const alignement = AlignmentMap[insideLabel.insideLabelLocation];
if (alignement.isPrimaryVerticalAlignment) {
if (alignement.primaryAlignment === 'TOP') {
data.style = { ...data.style, display: 'flex', flexDirection: 'column', justifyContent: 'flex-start' };
}
if (alignement.secondaryAlignment === 'CENTER') {
data.style = { ...data.style, alignItems: 'stretch' };
data.insideLabel.style = { ...data.insideLabel.style, justifyContent: 'center' };
}
}
}

const node: Node<ImageNodeData> = {
const node: Node<FreeFormNodeData> = {
id,
type: 'imageNode',
type: 'freeFormNode',
data,
position: defaultPosition,
hidden: state === GQLViewModifier.Hidden,
Expand Down Expand Up @@ -157,7 +152,9 @@ export class ImageNodeConverter implements INodeConverter {
nodeDescriptions: GQLNodeDescription[]
) {
const nodeDescription = nodeDescriptions.find((description) => description.id === gqlNode.descriptionId);
nodes.push(toImageNode(gqlDiagram, gqlNode, parentNode, nodeDescription, isBorderNode, gqlEdges));
if (nodeDescription) {
nodes.push(toImageNode(gqlDiagram, gqlNode, parentNode, nodeDescription, isBorderNode, gqlEdges));
}

const borderNodeDescriptions: GQLNodeDescription[] = (nodeDescription?.borderNodeDescriptionIds ?? []).flatMap(
(nodeDescriptionId) =>
Expand Down
Expand Up @@ -40,7 +40,7 @@ const toListNode = (
gqlDiagram: GQLDiagram,
gqlNode: GQLNode<GQLRectangularNodeStyle>,
gqlParentNode: GQLNode<GQLNodeStyle> | null,
nodeDescription: GQLNodeDescription | undefined,
nodeDescription: GQLNodeDescription,
isBorderNode: boolean,
gqlEdges: GQLEdge[]
): Node<ListNodeData> => {
Expand Down Expand Up @@ -208,7 +208,9 @@ export class ListNodeConverter implements INodeConverter {
nodeDescriptions: GQLNodeDescription[]
) {
const nodeDescription = nodeDescriptions.find((description) => description.id === gqlNode.descriptionId);
nodes.push(toListNode(gqlDiagram, gqlNode, parentNode, nodeDescription, isBorderNode, gqlEdges));
if (nodeDescription) {
nodes.push(toListNode(gqlDiagram, gqlNode, parentNode, nodeDescription, isBorderNode, gqlEdges));
}

const borderNodeDescriptions: GQLNodeDescription[] = (nodeDescription?.borderNodeDescriptionIds ?? []).flatMap(
(nodeDescriptionId) =>
Expand Down
Expand Up @@ -22,7 +22,7 @@ import {
} from '../graphql/subscription/nodeFragment.types';
import { BorderNodePosition } from '../renderer/DiagramRenderer.types';
import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types';
import { RectangularNodeData } from '../renderer/node/RectangularNode.types';
import { FreeFormNodeData } from '../renderer/node/FreeFormNode.types';
import { GQLDiagramDescription } from '../representation/DiagramRepresentation.types';
import { IConvertEngine, INodeConverter } from './ConvertEngine.types';
import { convertLineStyle } from './convertDiagram';
Expand All @@ -36,10 +36,10 @@ const toRectangularNode = (
gqlDiagram: GQLDiagram,
gqlNode: GQLNode<GQLRectangularNodeStyle>,
gqlParentNode: GQLNode<GQLNodeStyle> | null,
nodeDescription: GQLNodeDescription | undefined,
nodeDescription: GQLNodeDescription,
isBorderNode: boolean,
gqlEdges: GQLEdge[]
): Node<RectangularNodeData> => {
): Node<FreeFormNodeData> => {
const {
targetObjectId,
targetObjectLabel,
Expand All @@ -61,7 +61,7 @@ const toRectangularNode = (
const isNew = gqlNodeLayoutData === undefined;
const resizedByUser = gqlNodeLayoutData?.resizedByUser ?? false;

const data: RectangularNodeData = {
const data: FreeFormNodeData = {
targetObjectId,
targetObjectLabel,
targetObjectKind,
Expand All @@ -76,6 +76,7 @@ const toRectangularNode = (
},
insideLabel: null,
outsideLabels: convertOutsideLabels(outsideLabels),
imageURL: null,
faded: state === GQLViewModifier.Faded,
pinned,
nodeDescription,
Expand All @@ -84,6 +85,7 @@ const toRectangularNode = (
isBorderNode: isBorderNode,
borderNodePosition: isBorderNode ? BorderNodePosition.EAST : null,
labelEditable,
positionDependentRotation: false,
connectionHandles,
isNew,
resizedByUser,
Expand Down Expand Up @@ -123,9 +125,9 @@ const toRectangularNode = (
}
}

const node: Node<RectangularNodeData> = {
const node: Node<FreeFormNodeData> = {
id,
type: 'rectangularNode',
type: 'freeFormNode',
data,
position: defaultPosition,
hidden: state === GQLViewModifier.Hidden,
Expand Down Expand Up @@ -171,7 +173,9 @@ export class RectangleNodeConverter implements INodeConverter {
nodeDescriptions: GQLNodeDescription[]
) {
const nodeDescription = nodeDescriptions.find((description) => description.id === gqlNode.descriptionId);
nodes.push(toRectangularNode(gqlDiagram, gqlNode, parentNode, nodeDescription, isBorderNode, gqlEdges));
if (nodeDescription) {
nodes.push(toRectangularNode(gqlDiagram, gqlNode, parentNode, nodeDescription, isBorderNode, gqlEdges));
}

const borderNodeDescriptions: GQLNodeDescription[] = (nodeDescription?.borderNodeDescriptionIds ?? []).flatMap(
(nodeDescriptionId) =>
Expand Down
Expand Up @@ -80,6 +80,10 @@ export interface GQLRectangularNodeStyle extends GQLNodeStyle {

export interface GQLImageNodeStyle extends GQLNodeStyle {
imageURL: string;
borderColor: string;
borderStyle: string;
borderSize: string;
borderRadius: number;
positionDependentRotation: boolean;
}

Expand Down
Expand Up @@ -54,7 +54,7 @@ export interface NodeData {
outsideLabels: OutsideLabels;
faded: boolean;
pinned: boolean;
nodeDescription: GQLNodeDescription | undefined;
nodeDescription: GQLNodeDescription;
defaultWidth: number | null;
defaultHeight: number | null;
isBorderNode: boolean;
Expand Down
Expand Up @@ -34,7 +34,7 @@ const labelStyle = (
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
whiteSpace: 'pre-line',
whiteSpace: 'nowrap',
...style,
color: style.color ? getCSSColor(String(style.color), theme) : undefined,
};
Expand Down
Expand Up @@ -13,8 +13,8 @@

import { Node } from 'reactflow';
import { NodeData } from '../DiagramRenderer.types';
import { FreeFormNodeData } from '../node/FreeFormNode.types';
import { DiagramNodeType } from '../node/NodeTypes.types';
import { RectangularNodeData } from '../node/RectangularNode.types';
import { ILayoutEngine, INodeLayoutHandler } from './LayoutEngine.types';
import { computePreviousPosition, computePreviousSize } from './bounds';
import { RawDiagram } from './layout.types';
Expand All @@ -35,22 +35,22 @@ import {
} from './layoutNode';
import { rectangularNodePadding } from './layoutParams';

export class RectangleNodeLayoutHandler implements INodeLayoutHandler<RectangularNodeData> {
export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<FreeFormNodeData> {
public canHandle(node: Node<NodeData, DiagramNodeType>) {
return node.type === 'rectangularNode';
return node.type === 'freeFormNode';
}

public handle(
layoutEngine: ILayoutEngine,
previousDiagram: RawDiagram | null,
node: Node<RectangularNodeData, 'rectangularNode'>,
node: Node<FreeFormNodeData, 'freeFormNode'>,
visibleNodes: Node<NodeData, DiagramNodeType>[],
directChildren: Node<NodeData, DiagramNodeType>[],
newlyAddedNode: Node<NodeData, DiagramNodeType> | undefined,
forceWidth?: number
) {
const nodeIndex = findNodeIndex(visibleNodes, node.id);
const nodeElement = document.getElementById(`${node.id}-rectangularNode-${nodeIndex}`)?.children[0];
const nodeElement = document.getElementById(`${node.id}-freeFormNode-${nodeIndex}`)?.children[0];
const borderWidth = nodeElement ? parseFloat(window.getComputedStyle(nodeElement).borderWidth) : 0;

if (directChildren.length > 0) {
Expand All @@ -72,7 +72,7 @@ export class RectangleNodeLayoutHandler implements INodeLayoutHandler<Rectangula
private handleParentNode(
layoutEngine: ILayoutEngine,
previousDiagram: RawDiagram | null,
node: Node<RectangularNodeData, 'rectangularNode'>,
node: Node<FreeFormNodeData, 'freeFormNode'>,
visibleNodes: Node<NodeData, DiagramNodeType>[],
directChildren: Node<NodeData, DiagramNodeType>[],
newlyAddedNode: Node<NodeData, DiagramNodeType> | undefined,
Expand Down Expand Up @@ -201,7 +201,7 @@ export class RectangleNodeLayoutHandler implements INodeLayoutHandler<Rectangula

private handleLeafNode(
previousDiagram: RawDiagram | null,
node: Node<RectangularNodeData, 'rectangularNode'>,
node: Node<FreeFormNodeData, 'freeFormNode'>,
visibleNodes: Node<NodeData, DiagramNodeType>[],
borderWidth: number,
forceWidth?: number
Expand Down

0 comments on commit a5e6450

Please sign in to comment.