From f4e9dc3b240a4844bec02438622f24e742f73d46 Mon Sep 17 00:00:00 2001 From: Guillaume Coutable Date: Wed, 13 Dec 2023 09:50:15 +0100 Subject: [PATCH] [2480] Merge Rectangle and Image layout handling together Bug: https://github.com/eclipse-sirius/sirius-web/issues/2480 --- .../converter/ImageNodeConverterHandler.ts | 41 ++--- .../src/renderer/Label.tsx | 2 +- ...andler.ts => FreeFormNodeLayoutHandler.ts} | 11 +- .../renderer/layout/ImageNodeLayoutHandler.ts | 147 ------------------ .../src/renderer/layout/LayoutEngine.ts | 6 +- .../src/renderer/node/ImageNode.tsx | 55 +++++-- 6 files changed, 65 insertions(+), 197 deletions(-) rename packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/{RectangleNodeLayoutHandler.ts => FreeFormNodeLayoutHandler.ts} (95%) delete mode 100644 packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/ImageNodeLayoutHandler.ts diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/converter/ImageNodeConverterHandler.ts b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/converter/ImageNodeConverterHandler.ts index f303fd3a62..85a1d44255 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/converter/ImageNodeConverterHandler.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/converter/ImageNodeConverterHandler.ts @@ -19,7 +19,6 @@ import { BorderNodePosition } from '../renderer/DiagramRenderer.types'; import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types'; import { ImageNodeData } from '../renderer/node/ImageNode.types'; import { IConvertEngine, INodeConverterHandler } from './ConvertEngine.types'; -import { AlignmentMap } from './convertDiagram.types'; import { convertHandles } from './convertHandles'; import { convertLabelStyle, convertOutsideLabels } from './convertLabel'; @@ -71,34 +70,22 @@ 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), + }, }, - 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 = { diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/Label.tsx b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/Label.tsx index 63ad74d8ba..8539803087 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/Label.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/Label.tsx @@ -35,7 +35,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, }; diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/RectangleNodeLayoutHandler.ts b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/FreeFormNodeLayoutHandler.ts similarity index 95% rename from packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/RectangleNodeLayoutHandler.ts rename to packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/FreeFormNodeLayoutHandler.ts index 89cbce0d9c..cf44c026ae 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/RectangleNodeLayoutHandler.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/FreeFormNodeLayoutHandler.ts @@ -13,6 +13,7 @@ import { Node } from 'reactflow'; import { NodeData } from '../DiagramRenderer.types'; +import { ImageNodeData } from '../node/ImageNode.types'; import { DiagramNodeType } from '../node/NodeTypes.types'; import { RectangularNodeData } from '../node/RectangularNode.types'; import { ILayoutEngine, INodeLayoutHandler } from './LayoutEngine.types'; @@ -35,15 +36,15 @@ import { } from './layoutNode'; import { rectangularNodePadding } from './layoutParams'; -export class RectangleNodeLayoutHandler implements INodeLayoutHandler { +export class FreeFormNodeLayoutHandler implements INodeLayoutHandler { public canHandle(node: Node) { - return node.type === 'rectangularNode'; + return node.type === 'rectangularNode' || node.type === 'imageNode'; } public handle( layoutEngine: ILayoutEngine, previousDiagram: RawDiagram | null, - node: Node, + node: Node, visibleNodes: Node[], directChildren: Node[], newlyAddedNode: Node | undefined, @@ -72,7 +73,7 @@ export class RectangleNodeLayoutHandler implements INodeLayoutHandler, + node: Node, visibleNodes: Node[], directChildren: Node[], newlyAddedNode: Node | undefined, @@ -201,7 +202,7 @@ export class RectangleNodeLayoutHandler implements INodeLayoutHandler, + node: Node, visibleNodes: Node[], borderWidth: number, forceWidth?: number diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/ImageNodeLayoutHandler.ts b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/ImageNodeLayoutHandler.ts deleted file mode 100644 index ae30271214..0000000000 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/ImageNodeLayoutHandler.ts +++ /dev/null @@ -1,147 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2023 Obeo. - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Obeo - initial API and implementation - *******************************************************************************/ - -import { Node } from 'reactflow'; -import { NodeData } from '../DiagramRenderer.types'; -import { ImageNodeData } from '../node/ImageNode.types'; -import { DiagramNodeType } from '../node/NodeTypes.types'; -import { ILayoutEngine, INodeLayoutHandler } from './LayoutEngine.types'; -import { computePreviousSize } from './bounds'; -import { RawDiagram } from './layout.types'; -import { getBorderNodeExtent } from './layoutBorderNodes'; -import { - applyRatioOnNewNodeSizeValue, - computeNodesBox, - getEastBorderNodeFootprintHeight, - getNodeOrMinHeight, - getNodeOrMinWidth, - getNorthBorderNodeFootprintWidth, - getSouthBorderNodeFootprintWidth, - getWestBorderNodeFootprintHeight, - setBorderNodesPosition, -} from './layoutNode'; -import { borderLeftAndRight, borderTopAndBottom, rectangularNodePadding } from './layoutParams'; - -export class ImageNodeLayoutHandler implements INodeLayoutHandler { - public canHandle(node: Node) { - return node.type === 'imageNode'; - } - - public handle( - layoutEngine: ILayoutEngine, - previousDiagram: RawDiagram | null, - node: Node, - visibleNodes: Node[], - directChildren: Node[], - newlyAddedNode: Node | undefined, - forceWidth?: number - ) { - if (directChildren.length > 0) { - this.handleParentNode(layoutEngine, previousDiagram, node, visibleNodes, directChildren, newlyAddedNode); - } else { - this.handleLeafNode(previousDiagram, node, forceWidth); - } - } - - private handleParentNode( - layoutEngine: ILayoutEngine, - previousDiagram: RawDiagram | null, - node: Node, - visibleNodes: Node[], - directChildren: Node[], - newlyAddedNode: Node | undefined - ) { - layoutEngine.layoutNodes(previousDiagram, visibleNodes, directChildren, newlyAddedNode); - - const previousNode = (previousDiagram?.nodes ?? []).find((previousNode) => previousNode.id === node.id); - const previousDimensions = computePreviousSize(previousNode, node); - if (previousDimensions) { - node.width = getNodeOrMinWidth(previousDimensions.width, node); - node.height = getNodeOrMinHeight(previousDimensions.height, node); - } else { - node.width = getNodeOrMinWidth(undefined, node); - node.height = getNodeOrMinHeight(undefined, node); - } - - const borderNodes = directChildren.filter((node) => node.data.isBorderNode); - const directNodesChildren = directChildren.filter((child) => !child.data.isBorderNode); - - const childrenContentBox = computeNodesBox(visibleNodes, directNodesChildren); // WARN: The current content box algorithm does not take the margin of direct children (it should) - - const directChildrenAwareNodeWidth = childrenContentBox.x + childrenContentBox.width + rectangularNodePadding; - const northBorderNodeFootprintWidth = getNorthBorderNodeFootprintWidth(visibleNodes, borderNodes, previousDiagram); - const southBorderNodeFootprintWidth = getSouthBorderNodeFootprintWidth(visibleNodes, borderNodes, previousDiagram); - const nodeWidth = Math.max( - directChildrenAwareNodeWidth, - node.width, - northBorderNodeFootprintWidth, - southBorderNodeFootprintWidth - ); - - // WARN: the label is not used for the height because children are already position under the label - const directChildrenAwareNodeHeight = childrenContentBox.y + childrenContentBox.height + rectangularNodePadding; - const eastBorderNodeFootprintHeight = getEastBorderNodeFootprintHeight(visibleNodes, borderNodes, previousDiagram); - const westBorderNodeFootprintHeight = getWestBorderNodeFootprintHeight(visibleNodes, borderNodes, previousDiagram); - const nodeHeight = Math.max( - directChildrenAwareNodeHeight, - node.height, - eastBorderNodeFootprintHeight, - westBorderNodeFootprintHeight - ); - - node.width = getNodeOrMinWidth(nodeWidth + borderLeftAndRight, node); - node.height = getNodeOrMinHeight(nodeHeight + borderTopAndBottom, node); - - if (node.data.nodeDescription?.keepAspectRatio) { - applyRatioOnNewNodeSizeValue(node); - } - - // Update border nodes positions - borderNodes.forEach((borderNode) => { - borderNode.extent = getBorderNodeExtent(node, borderNode); - }); - setBorderNodesPosition(borderNodes, node, previousDiagram); - } - - private handleLeafNode( - previousDiagram: RawDiagram | null, - node: Node, - forceWidth?: number - ) { - const minNodeWith = forceWidth ?? getNodeOrMinWidth(undefined, node); - const minNodeHeight = getNodeOrMinHeight(undefined, node); - - const previousNode = (previousDiagram?.nodes ?? []).find((previous) => previous.id === node.id); - const previousDimensions = computePreviousSize(previousNode, node); - - if (node.data.nodeDescription?.userResizable) { - if (minNodeWith > previousDimensions.width) { - node.width = minNodeWith; - } else { - node.width = previousDimensions.width; - } - if (minNodeHeight > previousDimensions.height) { - node.height = minNodeHeight; - } else { - node.height = previousDimensions.height; - } - } else { - node.width = minNodeWith; - node.height = minNodeHeight; - } - - if (node.data.nodeDescription?.keepAspectRatio) { - applyRatioOnNewNodeSizeValue(node); - } - } -} diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/LayoutEngine.ts b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/LayoutEngine.ts index 4d387b4ef4..3abcba7465 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/LayoutEngine.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/LayoutEngine.ts @@ -14,18 +14,16 @@ import { Node } from 'reactflow'; import { NodeData } from '../DiagramRenderer.types'; import { DiagramNodeType } from '../node/NodeTypes.types'; +import { FreeFormNodeLayoutHandler } from './FreeFormNodeLayoutHandler'; import { IconLabelNodeLayoutHandler } from './IconLabelNodeLayoutHandler'; -import { ImageNodeLayoutHandler } from './ImageNodeLayoutHandler'; import { ILayoutEngine, INodeLayoutHandler } from './LayoutEngine.types'; import { ListNodeLayoutHandler } from './ListNodeLayoutHandler'; -import { RectangleNodeLayoutHandler } from './RectangleNodeLayoutHandler'; import { RawDiagram } from './layout.types'; export class LayoutEngine implements ILayoutEngine { nodeLayoutHandlers: INodeLayoutHandler[] = [ - new RectangleNodeLayoutHandler(), + new FreeFormNodeLayoutHandler(), new ListNodeLayoutHandler(), - new ImageNodeLayoutHandler(), new IconLabelNodeLayoutHandler(), ]; diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/node/ImageNode.tsx b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/node/ImageNode.tsx index fe6220ec30..38bac866e6 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/node/ImageNode.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/node/ImageNode.tsx @@ -66,6 +66,17 @@ const computeBorderRotation = (data: ImageNodeData): string | undefined => { return undefined; }; +const outsideBottomLabelAreaStyle = (): React.CSSProperties => { + return { + display: 'grid', + gridTemplateColumns: `1fr 1fr 1fr`, + gridTemplateRows: `min-content`, + gridTemplateAreas: ` + 'BOTTOM_BEGIN BOTTOM_MIDDLE BOTTOM_END' + `, + }; +}; + export const ImageNode = memo(({ data, id, selected }: NodeProps) => { const { httpOrigin } = useContext(ServerContext); const theme = useTheme(); @@ -75,6 +86,21 @@ export const ImageNode = memo(({ data, id, selected }: NodeProps) const rotation = computeBorderRotation(data); useRefreshConnectionHandles(id, data.connectionHandles); + + const outsideBottomlabels: JSX.Element[] = []; + if (data.outsideLabels.BOTTOM_MIDDLE) { + const outsideLabel = data.outsideLabels.BOTTOM_MIDDLE; + outsideBottomlabels.push( +