Skip to content

Commit

Permalink
[2480] Merge Rectangle and Image nodes together
Browse files Browse the repository at this point in the history
Bug: #2480
  • Loading branch information
gcoutable authored and sbegaudeau committed Jan 4, 2024
1 parent b124555 commit 3d72c3b
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 193 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.adoc
Expand Up @@ -109,7 +109,7 @@ const nodeTypeRegistry: NodeTypeRegistry = {
- 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 imporove performances.
Expand All @@ -123,6 +123,8 @@ It is the responsibility of the drop targets (e.g. a diagram) to validate the dr
image:doc/screenshots/biggerResizeHandles.png[Compartment with header without separator,70%]
- https://github.com/eclipse-sirius/sirius-web/issues/2849[#2849] [diagram] Reduce the time taken to open a diagram
- https://github.com/eclipse-sirius/sirius-web/issues/2609[#2609] [diagram] Move handles after node move to reduce edges crossing.
- https://github.com/eclipse-sirius/sirius-web/issues/2480[#2480] [diagram] Merge the rectangle node and the image node into FreeFormNode.


== v2023.12.0

Expand Down
Expand Up @@ -17,7 +17,7 @@ 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 { IConvertEngine, INodeConverterHandler } from './ConvertEngine.types';
import { convertHandles } from './convertHandles';
import { convertLabelStyle, convertOutsideLabels } from './convertLabel';
Expand All @@ -31,7 +31,7 @@ const toImageNode = (
nodeDescription: GQLNodeDescription | undefined,
isBorderNode: boolean,
gqlEdges: GQLEdge[]
): Node<ImageNodeData> => {
): Node<FreeFormNodeData> => {
const {
targetObjectId,
targetObjectLabel,
Expand All @@ -48,7 +48,7 @@ const toImageNode = (
const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);
const isNew = gqlDiagram.layoutData.nodeLayoutData.find((nodeLayoutData) => nodeLayoutData.id === id) === undefined;

const data: ImageNodeData = {
const data: FreeFormNodeData = {
targetObjectId,
targetObjectLabel,
targetObjectKind,
Expand Down Expand Up @@ -88,9 +88,9 @@ const toImageNode = (
};
}

const node: Node<ImageNodeData> = {
const node: Node<FreeFormNodeData> = {
id,
type: 'imageNode',
type: 'freeFormNode',
data,
position: defaultPosition,
hidden: state === GQLViewModifier.Hidden,
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 { IConvertEngine, INodeConverterHandler } from './ConvertEngine.types';
import { convertLineStyle } from './convertDiagram';
import { AlignmentMap } from './convertDiagram.types';
Expand All @@ -38,7 +38,7 @@ const toRectangularNode = (
nodeDescription: GQLNodeDescription | undefined,
isBorderNode: boolean,
gqlEdges: GQLEdge[]
): Node<RectangularNodeData> => {
): Node<FreeFormNodeData> => {
const {
targetObjectId,
targetObjectLabel,
Expand All @@ -55,7 +55,7 @@ const toRectangularNode = (
const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);
const isNew = gqlDiagram.layoutData.nodeLayoutData.find((nodeLayoutData) => nodeLayoutData.id === id) === undefined;

const data: RectangularNodeData = {
const data: FreeFormNodeData = {
targetObjectId,
targetObjectLabel,
targetObjectKind,
Expand All @@ -70,13 +70,15 @@ const toRectangularNode = (
},
insideLabel: null,
outsideLabels: convertOutsideLabels(outsideLabels),
imageURL: null,
faded: state === GQLViewModifier.Faded,
nodeDescription,
defaultWidth: gqlNode.defaultWidth,
defaultHeight: gqlNode.defaultHeight,
isBorderNode: isBorderNode,
borderNodePosition: isBorderNode ? BorderNodePosition.EAST : null,
labelEditable,
positionDependentRotation: false,
connectionHandles,
isNew,
};
Expand Down Expand Up @@ -115,9 +117,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 @@ -13,9 +13,8 @@

import { Node } from 'reactflow';
import { NodeData } from '../DiagramRenderer.types';
import { ImageNodeData } from '../node/ImageNode.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 @@ -36,22 +35,22 @@ import {
} from './layoutNode';
import { rectangularNodePadding } from './layoutParams';

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

public handle(
layoutEngine: ILayoutEngine,
previousDiagram: RawDiagram | null,
node: Node<RectangularNodeData | ImageNodeData, 'rectangularNode' | 'imageNode'>,
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 @@ -73,7 +72,7 @@ export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<Rectangular
private handleParentNode(
layoutEngine: ILayoutEngine,
previousDiagram: RawDiagram | null,
node: Node<RectangularNodeData | ImageNodeData, 'rectangularNode' | 'imageNode'>,
node: Node<FreeFormNodeData, 'freeFormNode'>,
visibleNodes: Node<NodeData, DiagramNodeType>[],
directChildren: Node<NodeData, DiagramNodeType>[],
newlyAddedNode: Node<NodeData, DiagramNodeType> | undefined,
Expand Down Expand Up @@ -202,7 +201,7 @@ export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<Rectangular

private handleLeafNode(
previousDiagram: RawDiagram | null,
node: Node<RectangularNodeData | ImageNodeData, 'rectangularNode' | 'imageNode'>,
node: Node<FreeFormNodeData, 'freeFormNode'>,
visibleNodes: Node<NodeData, DiagramNodeType>[],
borderWidth: number,
forceWidth?: number
Expand Down
Expand Up @@ -22,11 +22,11 @@ import { GQLReferencePosition } from '../../graphql/subscription/diagramEventSub
import { NodeData } from '../DiagramRenderer.types';
import { Label } from '../Label';
import { DiagramDirectEditContextProvider } from '../direct-edit/DiagramDirectEditContext';
import { FreeFormNode } from '../node/FreeFormNode';
import { FreeFormNodeData } from '../node/FreeFormNode.types';
import { ListNode } from '../node/ListNode';
import { ListNodeData } from '../node/ListNode.types';
import { DiagramNodeType } from '../node/NodeTypes.types';
import { RectangularNode } from '../node/RectangularNode';
import { RectangularNodeData } from '../node/RectangularNode.types';
import { LayoutEngine } from './LayoutEngine';
import { ILayoutEngine, INodeLayoutHandler } from './LayoutEngine.types';
import { computePreviousPosition } from './bounds';
Expand Down Expand Up @@ -54,7 +54,7 @@ const emptyRectangularNodeProps = {
};

const isListNode = (node: Node<NodeData>): node is Node<ListNodeData> => node.type === 'listNode';
const isRectangularNode = (node: Node<NodeData>): node is Node<RectangularNodeData> => node.type === 'rectangularNode';
const isRectangularNode = (node: Node<NodeData>): node is Node<FreeFormNodeData> => node.type === 'rectangularNode';

export const prepareLayoutArea = (
diagram: RawDiagram,
Expand Down Expand Up @@ -172,7 +172,7 @@ export const prepareLayoutArea = (
if (hiddenContainer && node) {
const children: JSX.Element[] = [];
if (isRectangularNode(node)) {
const element = createElement(RectangularNode, {
const element = createElement(FreeFormNode, {
...emptyRectangularNodeProps,
id: node.id,
data: node.data,
Expand Down
Expand Up @@ -11,10 +11,11 @@
* Obeo - initial API and implementation
*******************************************************************************/

import { getCSSColor } from '@eclipse-sirius/sirius-components-core';
import { ServerContext, ServerContextValue, getCSSColor } from '@eclipse-sirius/sirius-components-core';
import { Theme, useTheme } from '@material-ui/core/styles';
import React, { memo, useContext } from 'react';
import { NodeProps, NodeResizer } from 'reactflow';
import { BorderNodePosition } from '../DiagramRenderer.types';
import { Label } from '../Label';
import { useConnector } from '../connector/useConnector';
import { useDrop } from '../drop/useDrop';
Expand All @@ -24,28 +25,52 @@ import { ConnectionHandles } from '../handles/ConnectionHandles';
import { ConnectionTargetHandle } from '../handles/ConnectionTargetHandle';
import { useRefreshConnectionHandles } from '../handles/useRefreshConnectionHandles';
import { DiagramElementPalette } from '../palette/DiagramElementPalette';
import { FreeFormNodeData } from './FreeFormNode.types';
import { NodeContext } from './NodeContext';
import { NodeContextValue } from './NodeContext.types';
import { RectangularNodeData } from './RectangularNode.types';

const rectangularNodeStyle = (
const freeFormNodeStyle = (
theme: Theme,
style: React.CSSProperties,
selected: boolean,
hovered: boolean,
faded: boolean
faded: boolean,
rotation: string | undefined,
imageURL: string | undefined
): React.CSSProperties => {
const rectangularNodeStyle: React.CSSProperties = {
const freeFormNodeStyle: React.CSSProperties = {
width: '100%',
height: '100%',
opacity: faded ? '0.4' : '',
transform: rotation,
...style,
backgroundColor: getCSSColor(String(style.backgroundColor), theme),
};
if (selected || hovered) {
rectangularNodeStyle.outline = `${theme.palette.selected} solid 1px`;
freeFormNodeStyle.outline = `${theme.palette.selected} solid 1px`;
}
return rectangularNodeStyle;
if (imageURL) {
freeFormNodeStyle.backgroundImage = `url(${imageURL})`;
freeFormNodeStyle.backgroundRepeat = 'no-repeat';
freeFormNodeStyle.backgroundSize = '100% 100%';
}
return freeFormNodeStyle;
};

const computeBorderRotation = (data: FreeFormNodeData): string | undefined => {
if (data?.isBorderNode && data.positionDependentRotation) {
switch (data.borderNodePosition) {
case BorderNodePosition.NORTH:
return 'rotate(90deg)';
case BorderNodePosition.EAST:
return 'rotate(180deg)';
case BorderNodePosition.SOUTH:
return 'rotate(270deg)';
default:
return undefined;
}
}
return undefined;
};

const resizeHandleStyle = (theme: Theme): React.CSSProperties => {
Expand All @@ -66,12 +91,18 @@ const outsideBottomLabelAreaStyle = (): React.CSSProperties => {
};
};

export const RectangularNode = memo(({ data, id, selected }: NodeProps<RectangularNodeData>) => {
export const FreeFormNode = memo(({ data, id, selected }: NodeProps<FreeFormNodeData>) => {
const { httpOrigin } = useContext<ServerContextValue>(ServerContext);
const theme = useTheme();
const { onDrop, onDragOver } = useDrop();
const { newConnectionStyleProvider } = useConnector();
const { style: dropFeedbackStyle } = useDropNodeStyle(id);
const { hoveredNode } = useContext<NodeContextValue>(NodeContext);
const rotation = computeBorderRotation(data);
let imageURL: string | undefined = undefined;
if (data.imageURL) {
imageURL = httpOrigin + data.imageURL;
}

const handleOnDrop = (event: React.DragEvent) => {
onDrop(event, id);
Expand Down Expand Up @@ -118,13 +149,13 @@ export const RectangularNode = memo(({ data, id, selected }: NodeProps<Rectangul
)}
<div
style={{
...rectangularNodeStyle(theme, data.style, selected, hoveredNode?.id === id, data.faded),
...freeFormNodeStyle(theme, data.style, selected, hoveredNode?.id === id, data.faded, rotation, imageURL),
...newConnectionStyleProvider.getNodeStyle(id, data.descriptionId),
...dropFeedbackStyle,
}}
onDragOver={onDragOver}
onDrop={handleOnDrop}
data-testid={`Rectangle - ${data?.insideLabel?.text}`}>
data-testid={`FreeForm - ${data?.targetObjectLabel}`}>
{data.insideLabel ? (
<Label diagramElementId={id} label={data.insideLabel} faded={data.faded} transform="" />
) : null}
Expand Down
Expand Up @@ -13,7 +13,7 @@

import { NodeData } from '../DiagramRenderer.types';

export interface ImageNodeData extends NodeData {
imageURL: string;
export interface FreeFormNodeData extends NodeData {
imageURL: string | null;
positionDependentRotation: boolean;
}

0 comments on commit 3d72c3b

Please sign in to comment.