Skip to content

Commit

Permalink
[2792] Add support to label for arrangeAll
Browse files Browse the repository at this point in the history
Call layout after arangeAll

Bug: #2792
Signed-off-by: Florian ROUËNÉ <florian.rouene@obeosoft.com>
  • Loading branch information
frouene committed Jan 17, 2024
1 parent 62243b6 commit 1d1e6c4
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ This change makes the editing context the single source of truth for the state o
- https://github.com/eclipse-sirius/sirius-web/issues/2904[#2904] [sirius-web] Order candidates in Representations sections in the Onboard Area.
- https://github.com/eclipse-sirius/sirius-web/issues/2903[#2903] [sirius-web] Order New Representation modal candidates.
- https://github.com/eclipse-sirius/sirius-web/issues/2796[#2796] [sirius-web] Flow diagram description has been converted to the view DSL.
- https://github.com/eclipse-sirius/sirius-web/issues/2792[#2792] [diagram] Add support to label and call layout after arrangeAll.

== v2023.12.0

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* Copyright (c) 2023, 2024 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
Expand Down Expand Up @@ -45,7 +45,7 @@ export const IconOverlay = ({
return (
<>
{iconURL?.length > 0 && (
<div className={classes.iconContainer}>
<div className={classes.iconContainer} style={{ ...customIconStyle }}>
{iconURL.map((url: string, index) => (
<img
height={iconHeight}
Expand All @@ -55,7 +55,7 @@ export const IconOverlay = ({
title={title}
src={httpOrigin + url}
className={classes.icon}
style={{ ...customIconStyle, zIndex: index }}
style={{ zIndex: index }}
/>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { Selection, useSelection } from '@eclipse-sirius/sirius-components-core';
import React, { useContext, useEffect, useRef } from 'react';
import {
applyNodeChanges,
Background,
BackgroundVariant,
ConnectionLineType,
Expand All @@ -25,7 +26,6 @@ import {
OnEdgesChange,
OnNodesChange,
ReactFlow,
applyNodeChanges,
useEdgesState,
useNodesState,
} from 'reactflow';
Expand Down Expand Up @@ -254,7 +254,11 @@ export const DiagramRenderer = ({ diagramRefreshedEventPayload }: DiagramRendere
) : (
<Background style={{ backgroundColor }} color={backgroundColor} />
)}
<DiagramPanel snapToGrid={snapToGrid} onSnapToGrid={onSnapToGrid} />
<DiagramPanel
snapToGrid={snapToGrid}
onSnapToGrid={onSnapToGrid}
refreshEventPayloadId={diagramRefreshedEventPayload.id}
/>
<DiagramPalette diagramElementId={diagramRefreshedEventPayload.diagram.id} />
{diagramDescription.debug ? <DebugPanel reactFlowWrapper={ref} /> : null}
<ConnectorContextualMenu />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* Copyright (c) 2023, 2024 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
Expand All @@ -22,8 +22,7 @@ const labelStyle = (
theme: Theme,
style: React.CSSProperties,
faded: Boolean,
transform: string,
hasIcon: boolean
transform: string
): React.CSSProperties => {
const labelStyle: React.CSSProperties = {
transform,
Expand All @@ -38,10 +37,6 @@ const labelStyle = (
color: style.color ? getCSSColor(String(style.color), theme) : undefined,
};

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

return labelStyle;
};

Expand All @@ -61,16 +56,16 @@ export const Label = memo(({ diagramElementId, label, faded, transform }: LabelP
label.id === currentlyEditedLabelId ? (
<DiagramDirectEditInput editingKey={editingKey} onClose={handleClose} labelId={label.id} />
) : (
<>
<IconOverlay iconURL={label.iconURL} alt={label.text} />
<div data-id={`${label.id}-content`} style={{ display: 'flex', alignItems: 'center' }}>
<IconOverlay iconURL={label.iconURL} alt={label.text} customIconStyle={{ marginRight: theme.spacing(1) }} />
{label.text}
</>
</div>
);
return (
<div
data-id={label.id}
data-testid={`Label - ${label.text}`}
style={labelStyle(theme, label.style, faded, transform, !!label.iconURL)}
style={labelStyle(theme, label.style, faded, transform)}
className="nopan">
{content}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { UseArrangeAllValue } from './useArrangeAll.types';
import { Edge, Node, useReactFlow } from 'reactflow';
import { Edge, Node, useReactFlow, useViewport } from 'reactflow';
import { EdgeData, NodeData } from '../DiagramRenderer.types';
import { DiagramNodeType } from '../node/NodeTypes.types';
import { ListNodeData } from '../node/ListNode.types';
import ELK, { ElkNode } from 'elkjs/lib/elk.bundled';
import ELK, { ElkLabel, ElkNode } from 'elkjs/lib/elk.bundled';
import { LayoutOptions } from 'elkjs/lib/elk-api';
import { headerVerticalOffset } from './layoutParams';
import { RawDiagram } from './layout.types';
import { useLayout } from './useLayout';
import { useSynchronizeLayoutData } from './useSynchronizeLayoutData';

const isListData = (node: Node): node is Node<ListNodeData> => node.type === 'listNode';

Expand Down Expand Up @@ -47,13 +50,34 @@ const elkOptions = {
'elk.layered.spacing.edgeNodeBetweenLayers': '40',
};

export const useArrangeAll = (): UseArrangeAllValue => {
const { getNodes, getEdges, setNodes } = useReactFlow<NodeData, EdgeData>();
const computeContainerLabel = (parentNode, viewportZoom): ElkLabel[] => {
const labels: ElkLabel[] = [];
if (parentNode && parentNode.data.insideLabel) {
const label = document.querySelector<HTMLDivElement>(`[data-id="${parentNode.data.insideLabel.id}-content"]`);
if (label) {
const elkLabel: ElkLabel = {
width: label.getBoundingClientRect().width / viewportZoom,
height: label.getBoundingClientRect().height / viewportZoom,
text: parentNode.data.insideLabel.text,
};
labels.push(elkLabel);
}
}
return labels;
};

export const useArrangeAll = (refreshEventPayloadId: string): UseArrangeAllValue => {
const { getNodes, getEdges, setNodes, setEdges } = useReactFlow<NodeData, EdgeData>();
const viewport = useViewport();
const { layout } = useLayout();
const { synchronizeLayoutData } = useSynchronizeLayoutData();

const elk = new ELK();

const getELKLayout = async (
nodes,
edges,
labels: ElkLabel[],
options: LayoutOptions = {},
parentNodeId: string,
withHeader: boolean
Expand All @@ -62,6 +86,7 @@ export const useArrangeAll = (): UseArrangeAllValue => {
id: parentNodeId,
layoutOptions: options,
children: nodes.map((node) => ({
labels,
...node,
})),
edges,
Expand Down Expand Up @@ -115,22 +140,27 @@ export const useArrangeAll = (): UseArrangeAllValue => {
edge.source = parentNodeId;
}
});
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),
];
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);
}
);
layoutedAllNodes = [
...layoutedAllNodes,
...layoutedSubNodes,
...nodes.filter((node) => node.data.isBorderNode),
];
});
}
return layoutedAllNodes;
};
Expand All @@ -139,8 +169,39 @@ export const useArrangeAll = (): UseArrangeAllValue => {
const nodes: Node<NodeData, string>[] = [...getNodes()] as Node<NodeData, DiagramNodeType>[];
const subNodes: Map<string, Node<NodeData, string>[]> = reverseOrdreMap(getSubNodes(nodes));
applyElkOnSubNodes(subNodes, nodes).then((nodes: Node<NodeData, string>[]) => {
const reversedOrder: Node<NodeData, string>[] = nodes.reverse();
setNodes(reversedOrder);
const laidOutNodesWithElk: Node<NodeData, string>[] = nodes.reverse();

const diagramToLayout: RawDiagram = {
nodes: laidOutNodesWithElk,
edges: getEdges(),
};

layout(diagramToLayout, diagramToLayout, null, (laidOutDiagram) => {
laidOutNodesWithElk.map((node) => {
const existingNode = laidOutDiagram.nodes.find((laidOutNode) => laidOutNode.id === node.id);
if (existingNode) {
return {
...node,
position: existingNode.position,
width: existingNode.width,
height: existingNode.height,
style: {
...node.style,
width: `${existingNode.width}px`,
height: `${existingNode.height}px`,
},
};
}
return node;
});
setNodes(laidOutNodesWithElk);
setEdges(laidOutDiagram.edges);
const finalDiagram: RawDiagram = {
nodes: laidOutNodesWithElk,
edges: laidOutDiagram.edges,
};
synchronizeLayoutData(refreshEventPayloadId, finalDiagram);
});
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { DiagramPanelProps, DiagramPanelState } from './DiagramPanel.types';
import { useExportToImage } from './useExportToImage';
import { useArrangeAll } from '../layout/useArrangeAll';

export const DiagramPanel = memo(({ snapToGrid, onSnapToGrid }: DiagramPanelProps) => {
export const DiagramPanel = memo(({ snapToGrid, onSnapToGrid, refreshEventPayloadId }: DiagramPanelProps) => {
const [state, setState] = useState<DiagramPanelState>({
dialogOpen: null,
});
Expand All @@ -50,7 +50,7 @@ export const DiagramPanel = memo(({ snapToGrid, onSnapToGrid }: DiagramPanelProp
const getSelectedNodes = () => getNodes().filter((node) => node.selected);

const { fullscreen, onFullscreen } = useFullscreen();
const { arrangeAll } = useArrangeAll();
const { arrangeAll } = useArrangeAll(refreshEventPayloadId);

const handleFitToScreen = () => reactFlow.fitView({ duration: 200, nodes: getSelectedNodes() });
const handleZoomIn = () => reactFlow.zoomIn({ duration: 200 });
Expand Down Expand Up @@ -136,7 +136,12 @@ export const DiagramPanel = memo(({ snapToGrid, onSnapToGrid }: DiagramPanelProp
<GridOnIcon />
</IconButton>
)}
<IconButton size="small" onClick={() => arrangeAll()}>
<IconButton
size="small"
aria-label="arrange all elements"
title="Arrange all elements"
onClick={() => arrangeAll()}
data-testid={'arrange-all'}>
<AccountTreeIcon />
</IconButton>
<IconButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* Copyright (c) 2023, 2024 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
Expand All @@ -14,6 +14,7 @@
export interface DiagramPanelProps {
snapToGrid: boolean;
onSnapToGrid: (snapToGrid: boolean) => void;
refreshEventPayloadId: string;
}

export interface DiagramPanelState {
Expand Down

0 comments on commit 1d1e6c4

Please sign in to comment.