Skip to content

Commit

Permalink
[2883] Store in node layout data if a user has resized the node
Browse files Browse the repository at this point in the history
Bug: #2883
Signed-off-by: Guillaume Coutable <guillaume.coutable@obeo.fr>
  • Loading branch information
gcoutable authored and sbegaudeau committed Jan 11, 2024
1 parent 8faec27 commit 36f8f13
Show file tree
Hide file tree
Showing 24 changed files with 119 additions and 52 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ Additional changes to this interface will propably occur in the near future in o
The default implementation of the editing context for Sirius Web, which had Sirius Web specific code already, will be owned by `sirius-web-services`.
This default implementation will now also contain all the view models properly loaded which should be considered during the lifecycle of the editing context.
- https://github.com/eclipse-sirius/sirius-web/issues/2796[#2796] [sirius-web] All Flow related configurations have been moved to `sirius-components-flow-starter` module.
- https://github.com/eclipse-sirius/sirius-web/issues/2883[#2883] [diagram] Add the attribute `resizedByUser` to reactflow nodes and graphql nodes.


=== Dependency update

Expand All @@ -103,6 +105,7 @@ Now, for a multi-valued feature, the values are properly displayed and the delet
- https://github.com/eclipse-sirius/sirius-web/issues/2755[#2755] [diagram] Ensure that the unfolded tool section is always visible above the rest of the palette

=== New Features

- https://github.com/eclipse-sirius/sirius-web/issues/2413[#2413] Add a debug mode for React Flow diagrams

- https://github.com/eclipse-sirius/sirius-web/issues/2735[#2735] [gantt] Contribute the first version of the Gantt representation.
Expand All @@ -113,8 +116,14 @@ Now, for a multi-valued feature, the values are properly displayed and the delet
- https://github.com/eclipse-sirius/sirius-web/issues/2765[#2765] [portal] Contribute the first version of the Portal representation.
- https://github.com/eclipse-sirius/sirius-web/issues/2737[#2737] [gantt] Support create, edit and delete task in gantt.
- https://github.com/eclipse-sirius/sirius-web/issues/2793[#2793] [diagram] Add the possibility to pin / unpin nodes.
<<<<<<< HEAD
- https://github.com/eclipse-sirius/sirius-web/issues/2844[#2844] [Deck] Handle the card selection and direct edit in the Deck representation.
- https://github.com/eclipse-sirius/sirius-web/issues/2871[#2871] [Deck] Handle Edit, Add and Remove Card operations.
=======
- https://github.com/eclipse-sirius/sirius-web/issues/2883[#2883] [diagram] The size of manually resized node does not change anymore, unless it needs to.
+
The size of nodes that do not have been resized manually tries to fit the node default size.
>>>>>>> 5a88f3b1d ([2883] Store in node layout data if a user has resized the node)
=== Improvements
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2023 Obeo.
* Copyright (c) 2019, 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 @@ -132,7 +132,7 @@ public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDesc
var nodeLayoutData = layoutDiagramInput.diagramLayoutData().nodeLayoutData().stream()
.collect(Collectors.toMap(
NodeLayoutDataInput::id,
nodeLayoutDataInput -> new NodeLayoutData(nodeLayoutDataInput.id(), nodeLayoutDataInput.position(), nodeLayoutDataInput.size())
nodeLayoutDataInput -> new NodeLayoutData(nodeLayoutDataInput.id(), nodeLayoutDataInput.position(), nodeLayoutDataInput.size(), nodeLayoutDataInput.resizedByUser())
));

var layoutData = new DiagramLayoutData(nodeLayoutData, Map.of(), Map.of());
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 @@ -20,5 +20,5 @@
*
* @author sbegaudeau
*/
public record NodeLayoutDataInput(String id, Position position, Size size) {
public record NodeLayoutDataInput(String id, Position position, Size size, boolean resizedByUser) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type NodeLayoutData {
id: ID!
position: Position!
size: Size!
resizedByUser: Boolean!
}

enum ViewModifier {
Expand Down Expand Up @@ -590,6 +591,7 @@ input NodeLayoutDataInput {
id: ID!
position: PositionInput!
size: SizeInput!
resizedByUser: Boolean!
}

input SizeInput {
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 @@ -67,7 +67,7 @@ public DiagramLayoutData layout(DiagramLayoutConfiguration diagramLayoutConfigur
}

Map<String, NodeLayoutData> newNodeLayoutData = new HashMap<>();
diagramBoxModel.nodeBoxModels().forEach((nodeId, nodeBoxModel) -> newNodeLayoutData.put(nodeId, new NodeLayoutData(nodeId, nodeBoxModel.bounds().topLeft(), nodeBoxModel.bounds().size())));
diagramBoxModel.nodeBoxModels().forEach((nodeId, nodeBoxModel) -> newNodeLayoutData.put(nodeId, new NodeLayoutData(nodeId, nodeBoxModel.bounds().topLeft(), nodeBoxModel.bounds().size(), false)));

return new DiagramLayoutData(newNodeLayoutData, Map.of(), Map.of());
}
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 @@ -20,6 +20,7 @@
public record NodeLayoutData(
String id,
Position position,
Size size
Size size,
boolean resizedByUser
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*******************************************************************************/
import { Node, XYPosition } from 'reactflow';
import { GQLNodeDescription } from '../graphql/query/nodeDescriptionFragment.types';
import { GQLDiagram } from '../graphql/subscription/diagramFragment.types';
import { GQLDiagram, GQLNodeLayoutData } from '../graphql/subscription/diagramFragment.types';
import { GQLEdge } from '../graphql/subscription/edgeFragment.types';
import {
GQLIconLabelNodeStyle,
Expand Down Expand Up @@ -51,7 +51,11 @@ const toIconLabelNode = (
} = gqlNode;

const connectionHandles: ConnectionHandle[] = [];
const isNew = gqlDiagram.layoutData.nodeLayoutData.find((nodeLayoutData) => nodeLayoutData.id === id) === undefined;
const gqlNodeLayoutData: GQLNodeLayoutData | undefined = gqlDiagram.layoutData.nodeLayoutData.find(
(nodeLayoutData) => nodeLayoutData.id === id
);
const isNew = gqlNodeLayoutData === undefined;
const resizedByUser = gqlNodeLayoutData?.resizedByUser ?? false;

const data: IconLabelNodeData = {
targetObjectId,
Expand All @@ -74,6 +78,7 @@ const toIconLabelNode = (
labelEditable: labelEditable,
connectionHandles,
isNew,
resizedByUser,
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*******************************************************************************/
import { Node, XYPosition } from 'reactflow';
import { GQLNodeDescription } from '../graphql/query/nodeDescriptionFragment.types';
import { GQLDiagram } from '../graphql/subscription/diagramFragment.types';
import { GQLDiagram, GQLNodeLayoutData } from '../graphql/subscription/diagramFragment.types';
import { GQLEdge } from '../graphql/subscription/edgeFragment.types';
import { GQLImageNodeStyle, GQLNode, GQLNodeStyle, GQLViewModifier } from '../graphql/subscription/nodeFragment.types';
import { BorderNodePosition } from '../renderer/DiagramRenderer.types';
Expand Down Expand Up @@ -49,7 +49,11 @@ const toImageNode = (
} = gqlNode;

const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);
const isNew = gqlDiagram.layoutData.nodeLayoutData.find((nodeLayoutData) => nodeLayoutData.id === id) === undefined;
const gqlNodeLayoutData: GQLNodeLayoutData | undefined = gqlDiagram.layoutData.nodeLayoutData.find(
(nodeLayoutData) => nodeLayoutData.id === id
);
const isNew = gqlNodeLayoutData === undefined;
const resizedByUser = gqlNodeLayoutData?.resizedByUser ?? false;

const data: ImageNodeData = {
targetObjectId,
Expand All @@ -71,6 +75,7 @@ const toImageNode = (
positionDependentRotation: style.positionDependentRotation,
connectionHandles,
isNew,
resizedByUser,
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*******************************************************************************/
import { Node, XYPosition } from 'reactflow';
import { GQLNodeDescription } from '../graphql/query/nodeDescriptionFragment.types';
import { GQLDiagram } from '../graphql/subscription/diagramFragment.types';
import { GQLDiagram, GQLNodeLayoutData } from '../graphql/subscription/diagramFragment.types';
import { GQLEdge } from '../graphql/subscription/edgeFragment.types';
import {
GQLNode,
Expand Down Expand Up @@ -59,7 +59,11 @@ const toListNode = (
} = gqlNode;

const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);
const isNew = gqlDiagram.layoutData.nodeLayoutData.find((nodeLayoutData) => nodeLayoutData.id === id) === undefined;
const gqlNodeLayoutData: GQLNodeLayoutData | undefined = gqlDiagram.layoutData.nodeLayoutData.find(
(nodeLayoutData) => nodeLayoutData.id === id
);
const isNew = gqlNodeLayoutData === undefined;
const resizedByUser = gqlNodeLayoutData?.resizedByUser ?? false;

const data: ListNodeData = {
targetObjectId,
Expand Down Expand Up @@ -94,6 +98,7 @@ const toListNode = (
areChildNodesDraggable: isListLayoutStrategy(gqlNode.childrenLayoutStrategy)
? gqlNode.childrenLayoutStrategy.areChildNodesDraggable
: true,
resizedByUser,
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*******************************************************************************/
import { Node, XYPosition } from 'reactflow';
import { GQLNodeDescription } from '../graphql/query/nodeDescriptionFragment.types';
import { GQLDiagram } from '../graphql/subscription/diagramFragment.types';
import { GQLDiagram, GQLNodeLayoutData } from '../graphql/subscription/diagramFragment.types';
import { GQLEdge } from '../graphql/subscription/edgeFragment.types';
import {
GQLNode,
Expand Down Expand Up @@ -55,7 +55,11 @@ const toRectangularNode = (
} = gqlNode;

const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);
const isNew = gqlDiagram.layoutData.nodeLayoutData.find((nodeLayoutData) => nodeLayoutData.id === id) === undefined;
const gqlNodeLayoutData: GQLNodeLayoutData | undefined = gqlDiagram.layoutData.nodeLayoutData.find(
(nodeLayoutData) => nodeLayoutData.id === id
);
const isNew = gqlNodeLayoutData === undefined;
const resizedByUser = gqlNodeLayoutData?.resizedByUser ?? false;

const data: RectangularNodeData = {
targetObjectId,
Expand All @@ -82,6 +86,7 @@ const toRectangularNode = (
labelEditable,
connectionHandles,
isNew,
resizedByUser,
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ fragment diagramFragment on Diagram {
id
position { x y }
size { width height }
resizedByUser
}
}
nodes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface GQLNodeLayoutData {
id: string;
position: GQLPosition;
size: GQLSize;
resizedByUser: boolean;
}

export interface GQLRepresentationMetadata {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export { AlignmentMap } from './converter/convertDiagram.types';
export { convertHandles } from './converter/convertHandles';
export { convertLabelStyle, convertOutsideLabels } from './converter/convertLabel';
export type { GQLNodeDescription } from './graphql/query/nodeDescriptionFragment.types';
export type { GQLDiagram } from './graphql/subscription/diagramFragment.types';
export type { GQLDiagram, GQLNodeLayoutData } from './graphql/subscription/diagramFragment.types';
export type { GQLEdge } from './graphql/subscription/edgeFragment.types';
export { GQLViewModifier } from './graphql/subscription/nodeFragment.types';
export type { GQLNode, GQLNodeStyle, GraphQLNodeStyleFragment } from './graphql/subscription/nodeFragment.types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export interface NodeData {
style: React.CSSProperties;
connectionHandles: ConnectionHandle[];
isNew: boolean;
resizedByUser: boolean;
}

export enum BorderNodePosition {
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 @@ -11,7 +11,7 @@
* Obeo - initial API and implementation
*******************************************************************************/

import { Node, NodeChange, useReactFlow } from 'reactflow';
import { Node, NodeChange, NodeDimensionChange, NodePositionChange, useReactFlow } from 'reactflow';
import { EdgeData, NodeData } from '../DiagramRenderer.types';
import { useDropNode } from '../dropNode/useDropNode';
import { RawDiagram } from '../layout/layout.types';
Expand All @@ -26,31 +26,51 @@ export const useLayoutOnBoundsChange = (refreshEventPayloadId: string): UseLayou
const { hasDroppedNodeParentChanged } = useDropNode();
const { synchronizeLayoutData } = useSynchronizeLayoutData();

const isMoveFinished = (change: NodeChange): boolean => {
const isMoveFinished = (change: NodeChange): change is NodePositionChange => {
return (
change.type === 'position' &&
typeof change.dragging === 'boolean' &&
!change.dragging &&
!hasDroppedNodeParentChanged()
);
};
const isResizeFinished = (change: NodeChange): boolean =>
const isResizeFinished = (change: NodeChange): change is NodeDimensionChange =>
change.type === 'dimensions' && typeof change.resizing === 'boolean' && !change.resizing;

const isBoundsChangeFinished = (changes: NodeChange[]): NodeChange | undefined => {
return changes.find((change) => isMoveFinished(change) || isResizeFinished(change));
};

const updateNodeResizeByUserState = (
changes: NodeChange[],
nodes: Node<NodeData, DiagramNodeType>[]
): Node<NodeData, DiagramNodeType>[] => {
return nodes.map((node) => {
if (changes.filter(isResizeFinished).find((dimensionChange) => dimensionChange.id === node.id)) {
return {
...node,
data: {
...node.data,
resizedByUser: true,
},
};
}
return node;
});
};

const layoutOnBoundsChange = (changes: NodeChange[], nodes: Node<NodeData, DiagramNodeType>[]): void => {
const change = isBoundsChangeFinished(changes);
if (change) {
const updatedNodes = updateNodeResizeByUserState(changes, nodes);

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

layout(diagramToLayout, diagramToLayout, null, (laidOutDiagram) => {
nodes.map((node) => {
updatedNodes.map((node) => {
const existingNode = laidOutDiagram.nodes.find((laidoutNode) => laidoutNode.id === node.id);
if (existingNode) {
return {
Expand All @@ -67,10 +87,10 @@ export const useLayoutOnBoundsChange = (refreshEventPayloadId: string): UseLayou
}
return node;
});
setNodes(nodes);
setNodes(updatedNodes);
setEdges(laidOutDiagram.edges);
const finalDiagram: RawDiagram = {
nodes: nodes,
nodes: updatedNodes,
edges: laidOutDiagram.edges,
};

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 @@ -124,7 +124,7 @@ export class ImageNodeLayoutHandler implements INodeLayoutHandler<ImageNodeData>
const previousNode = (previousDiagram?.nodes ?? []).find((previous) => previous.id === node.id);
const previousDimensions = computePreviousSize(previousNode, node);

if (node.data.nodeDescription?.userResizable) {
if (node.data.resizedByUser) {
if (minNodeWith > previousDimensions.width) {
node.width = minNodeWith;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class ListNodeLayoutHandler implements INodeLayoutHandler<ListNodeData> {
const previousNode = (previousDiagram?.nodes ?? []).find((previouseNode) => previouseNode.id === node.id);
const previousDimensions = computePreviousSize(previousNode, node);

if (node.data.nodeDescription?.userResizable) {
if (node.data.resizedByUser) {
if (minNodeWith > previousDimensions.width) {
node.width = minNodeWith;
} else {
Expand Down Expand Up @@ -127,7 +127,7 @@ export class ListNodeLayoutHandler implements INodeLayoutHandler<ListNodeData> {

if (!forceWidth) {
let previousChildrenContentBoxWidthToConsider: number = 0;
if (node.data.nodeDescription?.userResizable) {
if (node.data.resizedByUser) {
previousChildrenContentBoxWidthToConsider = (previousNode?.width ?? 0) - borderWidth * 2;
}
const widerWidth = Math.max(
Expand Down Expand Up @@ -173,8 +173,7 @@ export class ListNodeLayoutHandler implements INodeLayoutHandler<ListNodeData> {
node.width = forceWidth ?? getNodeOrMinWidth(nodeWidth, node);

const minNodeheight = getNodeOrMinHeight(nodeHeight, node);
// TODO: rework this.
if (node.data.nodeDescription?.userResizable && previousNode) {
if (node.data.resizedByUser && previousNode) {
if (minNodeheight > (previousNode.height ?? 0)) {
node.height = minNodeheight;
} else {
Expand Down
Loading

0 comments on commit 36f8f13

Please sign in to comment.