Skip to content

Commit

Permalink
[2288] Make IconLabel a react flow node
Browse files Browse the repository at this point in the history
Bug: #2288
Signed-off-by: Guillaume Coutable <guillaume.coutable@obeo.fr>
  • Loading branch information
gcoutable committed Sep 18, 2023
1 parent 03588bb commit 2fdc6c0
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 108 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.adoc
Expand Up @@ -118,10 +118,10 @@ To illustrate this new feature, we contribute a new tool on the _Papaya Diagram_
- https://github.com/eclipse-sirius/sirius-web/issues/2261[#2261] [diagram] Change the handle used by edges depending on the position of the source and target
- https://github.com/eclipse-sirius/sirius-web/issues/2265[#2265] [diagram] Update the viewport to show the current state of the selection
- https://github.com/eclipse-sirius/sirius-web/issues/2368[#2368] [form] Remove useless reference to the GraphQL version of Google Guava

- https://github.com/eclipse-sirius/sirius-web/issues/2343[#2343] [emf] Add icon for type selection in new object modal.
- https://github.com/eclipse-sirius/sirius-web/issues/2365[#2365] [diagram] Support key down and F2 to enable direct edit on edges on react-flow diagrams.
- https://github.com/eclipse-sirius/sirius-web/issues/2358[#2358] [form] Adds diagnostic messages to the reference widget.
- https://github.com/eclipse-sirius/sirius-web/issues/2288[#2288] [diagram] Make IconLabel a react flow node

== v2023.8.0

Expand Down
Expand Up @@ -37,9 +37,11 @@ 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', 14);
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-listNode').should('have.length', 2);
cy.get('.react-flow__node-iconLabelNode').should('have.length', 4);
});

it('can share the representation', () => {
Expand Down
Expand Up @@ -15,15 +15,17 @@ import { Edge, Node, XYPosition } from 'reactflow';
import { GQLDiagram } from '../graphql/subscription/diagramFragment.types';
import { GQLLabel, GQLLabelStyle } from '../graphql/subscription/labelFragment.types';
import {
GQLIconLabelNodeStyle,
GQLImageNodeStyle,
GQLNode,
GQLRectangularNodeStyle,
GQLViewModifier,
} from '../graphql/subscription/nodeFragment.types';
import { Diagram, Label } from '../renderer/DiagramRenderer.types';
import { MultiLabelEdgeData } from '../renderer/edge/MultiLabelEdge.types';
import { IconLabelNodeData } from '../renderer/node/IconsLabel.types';
import { ImageNodeData } from '../renderer/node/ImageNode.types';
import { ListItemData, ListNodeData } from '../renderer/node/ListNode.types';
import { ListNodeData } from '../renderer/node/ListNode.types';
import { RectangularNodeData } from '../renderer/node/RectangularNode.types';

const defaultPosition: XYPosition = { x: 0, y: 0 };
Expand Down Expand Up @@ -104,34 +106,50 @@ const toRectangularNode = (gqlNode: GQLNode, gqlParentNode: GQLNode | null): Nod
return node;
};

const toListNode = (gqlNode: GQLNode, gqlParentNode: GQLNode | null): Node<ListNodeData> => {
const style = gqlNode.style as GQLRectangularNodeStyle;
const labelStyle = gqlNode.label.style;
const toIconLabelNode = (gqlNode: GQLNode, gqlParentNode: GQLNode | null): Node<IconLabelNodeData> => {
const { targetObjectId, targetObjectLabel, targetObjectKind } = gqlNode;
const style = gqlNode.style as GQLIconLabelNodeStyle;
const { id, label } = gqlNode;
const labelStyle = label.style;

const listItems: ListItemData[] = (gqlNode.childNodes ?? []).map((gqlChildNode) => {
const { id, label } = gqlChildNode;
return {
id,
label: {
id: label.id,
text: label.text,
iconURL: null,
style: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
gap: '8px',
padding: '4px 8px',
},
},
const data: IconLabelNodeData = {
targetObjectId,
targetObjectLabel,
targetObjectKind,
style: {
textAlign: 'left',
backgroundColor: style.backgroundColor,
},
label: {
id: label.id,
text: label.text,
style: {
textAlign: 'left',
...convertLabelStyle(label.style),
...convertLabelStyle(labelStyle),
},
hidden: gqlChildNode.state === GQLViewModifier.Hidden,
};
});
iconURL: labelStyle.iconURL,
},
faded: gqlNode.state === GQLViewModifier.Faded,
};

const node: Node<IconLabelNodeData> = {
id,
type: 'iconLabelNode',
data,
position: defaultPosition,
hidden: gqlNode.state === GQLViewModifier.Hidden,
};

if (gqlParentNode) {
node.parentNode = gqlParentNode.id;
node.extent = 'parent';
}

return node;
};

const toListNode = (gqlNode: GQLNode, gqlParentNode: GQLNode | null): Node<ListNodeData> => {
const style = gqlNode.style as GQLRectangularNodeStyle;
const labelStyle = gqlNode.label.style;

const { targetObjectId, targetObjectLabel, targetObjectKind } = gqlNode;
const data: ListNodeData = {
Expand All @@ -154,14 +172,12 @@ const toListNode = (gqlNode: GQLNode, gqlParentNode: GQLNode | null): Node<ListN
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: '8px',
padding: '8px 16px',
textAlign: 'center',
...convertLabelStyle(labelStyle),
},
},
faded: gqlNode.state === GQLViewModifier.Faded,
listItems,
};

if (style.withHeader && data.label) {
Expand Down Expand Up @@ -234,7 +250,6 @@ const toImageNode = (gqlNode: GQLNode, gqlParentNode: GQLNode | null): Node<Imag
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: '8px',
padding: '8px 16px',
textAlign: 'center',
...convertLabelStyle(labelStyle),
Expand Down Expand Up @@ -282,6 +297,8 @@ const convertNode = (gqlNode: GQLNode, parentNode: GQLNode | null, nodes: Node[]

(gqlNode.borderNodes ?? []).forEach((gqlBorderNode) => convertNode(gqlBorderNode, gqlNode, nodes));
(gqlNode.childNodes ?? []).forEach((gqlChildNode) => convertNode(gqlChildNode, gqlNode, nodes));
} else if (gqlNode.style.__typename === 'IconLabelNodeStyle') {
nodes.push(toIconLabelNode(gqlNode, parentNode));
}
};

Expand Down
Expand Up @@ -61,4 +61,6 @@ export interface GQLImageNodeStyle extends GQLNodeStyle {
imageURL: string;
}

export interface GQLIconLabelNodeStyle extends GQLNodeStyle {}
export interface GQLIconLabelNodeStyle extends GQLNodeStyle {
backgroundColor: string;
}
Expand Up @@ -39,6 +39,7 @@ import { useDiagramDirectEdit } from './direct-edit/useDiagramDirectEdit';
import { useDrop } from './drop/useDrop';
import { MultiLabelEdge } from './edge/MultiLabelEdge';
import { MultiLabelEdgeData } from './edge/MultiLabelEdge.types';
import { IconLabelNode } from './node/IconLabel';
import { ImageNode } from './node/ImageNode';
import { ListNode } from './node/ListNode';
import { RectangularNode } from './node/RectangularNode';
Expand All @@ -54,6 +55,7 @@ const nodeTypes: NodeTypes = {
rectangularNode: RectangularNode,
imageNode: ImageNode,
listNode: ListNode,
iconLabelNode: IconLabelNode,
};

const edgeTypes: EdgeTypes = {
Expand Down
Expand Up @@ -17,8 +17,13 @@ import { LabelProps } from './Label.types';
import { DiagramDirectEditInput } from './direct-edit/DiagramDirectEditInput';
import { useDiagramDirectEdit } from './direct-edit/useDiagramDirectEdit';

const labelStyle = (style: React.CSSProperties, faded: Boolean, transform: string): React.CSSProperties => {
return {
const labelStyle = (
style: React.CSSProperties,
faded: boolean,
transform: string,
hasIcon: boolean
): React.CSSProperties => {
const labelStyle: React.CSSProperties = {
transform,
opacity: faded ? '0.4' : '',
pointerEvents: 'all',
Expand All @@ -28,6 +33,12 @@ const labelStyle = (style: React.CSSProperties, faded: Boolean, transform: strin
justifyContent: 'flex-start',
...style,
};

if (hasIcon) {
labelStyle.gap = '8px';
}

return labelStyle;
};

export const Label = memo(({ diagramElementId, label, faded, transform }: LabelProps) => {
Expand All @@ -51,12 +62,13 @@ export const Label = memo(({ diagramElementId, label, faded, transform }: LabelP
<DiagramDirectEditInput editingKey={editingKey} onClose={handleClose} labelId={label.id} transform={transform} />
);
}

return (
<div
data-id={label.id}
data-testid={`Label - ${label.text}`}
onDoubleClick={handleDoubleClick}
style={labelStyle(label.style, faded, transform)}
style={labelStyle(label.style, faded, transform, !!label.iconURL)}
className="nopan">
{label.iconURL ? <img src={httpOrigin + label.iconURL} /> : ''}
{label.text}
Expand Down

0 comments on commit 2fdc6c0

Please sign in to comment.