From 6156e0017abfd75f41291821a062a183e1915ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20ROU=C3=8BN=C3=89?= Date: Thu, 1 Feb 2024 14:52:08 +0100 Subject: [PATCH] [2721] Support outside labels during layout and arrangeAll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: https://github.com/eclipse-sirius/sirius-web/issues/2721 Signed-off-by: Florian ROUËNÉ --- CHANGELOG.adoc | 1 + .../src/renderer/layout/layout.tsx | 19 ++++++ .../src/renderer/layout/layoutNode.ts | 25 +++---- .../src/renderer/layout/layoutParams.ts | 2 + .../src/renderer/layout/useArrangeAll.ts | 68 +++++++++++-------- 5 files changed, 70 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 8abec433bf..1ce8f2ccc4 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -38,6 +38,7 @@ As a result, all the APIs relying on this code have been deleted too such as the - https://github.com/eclipse-sirius/sirius-web/issues/3004[#3004] [deck] Fix some of the console errors - https://github.com/eclipse-sirius/sirius-web/issues/2799[#2799] [diagram] Remove the unwanted space between the header and first element in list node - https://github.com/eclipse-sirius/sirius-web/issues/2822[#2822] [diagram] Prevent direct edit to be triggered on edge if there is no edit label tool corresponding +- https://github.com/eclipse-sirius/sirius-web/issues/2721[#2721] [diagram] Prevent labels to exceed their container === New Features diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layout.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layout.tsx index 616595f56f..24633869a4 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layout.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layout.tsx @@ -98,6 +98,25 @@ export const prepareLayoutArea = ( }); labelElements.push(element); } + if (hiddenContainer && node.data.outsideLabels.BOTTOM_MIDDLE) { + const outsideLabel = node.data.outsideLabels.BOTTOM_MIDDLE; + const children: JSX.Element[] = [ + createElement(Label, { + diagramElementId: node.id, + label: outsideLabel, + faded: false, + transform: '', + key: outsideLabel.id, + }), + ]; + const element: JSX.Element = createElement('div', { + id: `${outsideLabel.id}-label`, + key: `${outsideLabel.id}-label`, + role: 'button', // role applied by react flow + children, + }); + labelElements.push(element); + } }); // The container used to render label is a flex container authorizing wrapping. diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layoutNode.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layoutNode.ts index a438ae0120..d2f9ea93c7 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layoutNode.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layoutNode.ts @@ -13,21 +13,8 @@ import { Box, Node, Rect, XYPosition, boxToRect, rectToBox } from 'reactflow'; import { NodeData } from '../DiagramRenderer.types'; import { RawDiagram } from './layout.types'; -import { - getBorderNodeExtent, - isEastBorderNode, - isNorthBorderNode, - isSouthBorderNode, - isWestBorderNode, -} from './layoutBorderNodes'; -import { - borderNodeOffset, - defaultHeight, - defaultNodeMargin, - defaultWidth, - gap, - rectangularNodePadding, -} from './layoutParams'; +import { getBorderNodeExtent, isEastBorderNode, isNorthBorderNode, isSouthBorderNode, isWestBorderNode, } from './layoutBorderNodes'; +import { borderNodeOffset, defaultHeight, defaultNodeMargin, defaultWidth, gap, rectangularNodePadding, } from './layoutParams'; /** * It requires that nodes are already positioned @@ -155,11 +142,17 @@ const getNodeFootprint = (allVisibleNodes: Node[], node: Node( (currentFootPrint, child) => { + const outsideLabel = child.data.outsideLabels.BOTTOM_MIDDLE; + let outsideLabelHeightFootPrint: number = 0; + if (outsideLabel) { + const outsideLabelElement = document.getElementById(`${outsideLabel.id}-label`); + outsideLabelHeightFootPrint = outsideLabelElement?.getBoundingClientRect().height ?? 0; + } const nodeBox = rectToBox({ x: node !== child && child.data.isBorderNode ? node.position.x + child.position.x : child.position.x, y: node !== child && child.data.isBorderNode ? node.position.y + child.position.y : child.position.y, width: child.width ?? 0, - height: child.height ?? 0, + height: (child.height ?? 0) + outsideLabelHeightFootPrint, }); return getBoundsOfBoxes(currentFootPrint, nodeBox); diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layoutParams.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layoutParams.ts index 5d7402067b..5c6ce52c03 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layoutParams.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/layoutParams.ts @@ -20,3 +20,5 @@ export const borderNodeOffset = 5; export const defaultWidth = 150; export const defaultHeight = 70; export const headerVerticalOffset = 25; +export const labelVerticalPadding = 8; +export const labelHorizontalPadding = 16; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/useArrangeAll.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/useArrangeAll.ts index 9f6b7bb8d0..67df6e890a 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/useArrangeAll.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/layout/useArrangeAll.ts @@ -17,7 +17,7 @@ import { DiagramNodeType } from '../node/NodeTypes.types'; import { ListNodeData } from '../node/ListNode.types'; import ELK, { ElkLabel, ElkNode } from 'elkjs/lib/elk.bundled'; import { LayoutOptions } from 'elkjs/lib/elk-api'; -import { headerVerticalOffset } from './layoutParams'; +import { headerVerticalOffset, labelVerticalPadding, labelHorizontalPadding } from './layoutParams'; import { RawDiagram } from './layout.types'; import { useLayout } from './useLayout'; import { useSynchronizeLayoutData } from './useSynchronizeLayoutData'; @@ -50,15 +50,30 @@ const elkOptions = { 'elk.layered.spacing.edgeNodeBetweenLayers': '40', }; -const computeContainerLabel = (parentNode, viewportZoom): ElkLabel[] => { +const computeLabels = (node, viewportZoom: number): ElkLabel[] => { const labels: ElkLabel[] = []; - if (parentNode && parentNode.data.insideLabel) { - const label = document.querySelector(`[data-id="${parentNode.data.insideLabel.id}-content"]`); + if (node && node.data.insideLabel) { + const label = document.querySelector(`[data-id="${node.data.insideLabel.id}-content"]`); if (label) { const elkLabel: ElkLabel = { - width: label.getBoundingClientRect().width / viewportZoom, - height: label.getBoundingClientRect().height / viewportZoom, - text: parentNode.data.insideLabel.text, + id: node.data.insideLabel.id, + width: label.getBoundingClientRect().width / viewportZoom + labelHorizontalPadding * 2, + height: label.getBoundingClientRect().height / viewportZoom + labelVerticalPadding * 2, + text: node.data.insideLabel.text, + }; + labels.push(elkLabel); + } + } + if (node && node.data.outsideLabels.BOTTOM_MIDDLE) { + const label = document.querySelector( + `[data-id="${node.data.outsideLabels.BOTTOM_MIDDLE.id}-content"]` + ); + if (label) { + const elkLabel: ElkLabel = { + id: node.data.outsideLabels.BOTTOM_MIDDLE.id, + width: label.getBoundingClientRect().width / viewportZoom + labelHorizontalPadding * 2, + height: label.getBoundingClientRect().height / viewportZoom + labelVerticalPadding * 2, + text: node.data.outsideLabels.BOTTOM_MIDDLE.text, }; labels.push(elkLabel); } @@ -77,7 +92,6 @@ export const useArrangeAll = (refreshEventPayloadId: string): UseArrangeAllValue const getELKLayout = async ( nodes, edges, - labels: ElkLabel[], options: LayoutOptions = {}, parentNodeId: string, withHeader: boolean @@ -86,7 +100,7 @@ export const useArrangeAll = (refreshEventPayloadId: string): UseArrangeAllValue id: parentNodeId, layoutOptions: options, children: nodes.map((node) => ({ - labels, + labels: computeLabels(node, viewport.zoom), ...node, })), edges, @@ -140,27 +154,22 @@ export const useArrangeAll = (refreshEventPayloadId: string): UseArrangeAllValue edge.source = parentNodeId; } }); - await getELKLayout( - subGroupNodes, - subGroupEdges, - computeContainerLabel(parentNode, viewport.zoom), - elkOptions, - parentNodeId, - withHeader - ).then(({ nodes: layoutedSubNodes, layoutReturn }) => { - const parentNode = allNodes.find((node) => node.id === parentNodeId); - if (parentNode) { - parentNode.width = layoutReturn.width; - parentNode.height = layoutReturn.height + (withHeader ? headerVerticalOffset : 0); - parentNode.style = { width: `${parentNode.width}px`, height: `${parentNode.height}px` }; - parentNodeWithNewSize.push(parentNode); + await getELKLayout(subGroupNodes, subGroupEdges, elkOptions, parentNodeId, withHeader).then( + ({ nodes: layoutedSubNodes, layoutReturn }) => { + const parentNode = allNodes.find((node) => node.id === parentNodeId); + if (parentNode) { + parentNode.width = layoutReturn.width; + parentNode.height = layoutReturn.height + (withHeader ? headerVerticalOffset : 0); + parentNode.style = { width: `${parentNode.width}px`, height: `${parentNode.height}px` }; + parentNodeWithNewSize.push(parentNode); + } + layoutedAllNodes = [ + ...layoutedAllNodes, + ...layoutedSubNodes, + ...nodes.filter((node) => node.data.isBorderNode), + ]; } - layoutedAllNodes = [ - ...layoutedAllNodes, - ...layoutedSubNodes, - ...nodes.filter((node) => node.data.isBorderNode), - ]; - }); + ); } return layoutedAllNodes; }; @@ -170,6 +179,7 @@ export const useArrangeAll = (refreshEventPayloadId: string): UseArrangeAllValue const subNodes: Map[]> = reverseOrdreMap(getSubNodes(nodes)); applyElkOnSubNodes(subNodes, nodes).then((nodes: Node[]) => { const laidOutNodesWithElk: Node[] = nodes.reverse(); + laidOutNodesWithElk.forEach((node) => (node.data.resizedByUser = true)); // allows elk to resize nodes during layout const diagramToLayout: RawDiagram = { nodes: laidOutNodesWithElk,