Skip to content

Commit

Permalink
[2713] Enhances resize support for list nodes
Browse files Browse the repository at this point in the history
Bug: #2713
Signed-off-by: Florian ROUËNÉ <florian.rouene@obeosoft.com>
  • Loading branch information
frouene authored and AxelRICHARD committed Feb 27, 2024
1 parent 62f1440 commit d02067c
Show file tree
Hide file tree
Showing 41 changed files with 792 additions and 110 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The EMF validation support will now be provided by `sirius-web-services`.
- https://github.com/eclipse-sirius/sirius-web/issues/3156[#3156] [core] In our GraphQL schema, `Viewer` is now a type instead of an interface.
Having `Viewer` as a type did not bring any additional value and it created some overhead by having to create and keep updated an additional type without any benefit.
- [sirius-web] The field `ChangeKind.PROJECT_RENAMING` has been deleted since it does not make any sense in Sirius Components.

- https://github.com/eclipse-sirius/sirius-web/issues/2713[#2713] [diagram] Add the attribute `growableNodeIds` to graphql `ListLayoutStrategy`.

=== Dependency update

Expand Down Expand Up @@ -140,6 +140,7 @@ image:doc/screenshots/showDeletionConfirmation.png[Deletion Confirmation Dialog,
- https://github.com/eclipse-sirius/sirius-web/issues/3079[#3079] [deck] Add a name attribute on CardDescription and LaneDescription
- https://github.com/eclipse-sirius/sirius-web/issues/3019[#3019] [sirius-web] Improve the new architecture to let product builders reuse Sirius Web
- https://github.com/eclipse-sirius/sirius-web/issues/3141[#3141] [portal] Make the header of embedded representations sticky so that it (and its icon) are always visible.
- https://github.com/eclipse-sirius/sirius-web/issues/2713[#2713] [diagram] Enhances resize support for list nodes

== v2024.1.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type ListLayoutStrategy {
areChildNodesDraggable: Boolean!
topGap: Int!
bottomGap: Int!
growableNodeIds: [ID!]!
}

union ILayoutStrategy = FreeFormLayoutStrategy | ListLayoutStrategy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
*******************************************************************************/
package org.eclipse.sirius.components.diagrams;

import java.util.List;
import java.util.Objects;

import org.eclipse.sirius.components.annotations.Immutable;

/**
Expand All @@ -29,6 +32,8 @@ public final class ListLayoutStrategy implements ILayoutStrategy {

private int bottomGap;

private List<String> growableNodeIds;

private ListLayoutStrategy() {
// Prevent instantiation
}
Expand All @@ -54,6 +59,10 @@ public int getBottomGap() {
return this.bottomGap;
}

public List<String> getGrowableNodeIds() {
return this.growableNodeIds;
}

/**
* The builder used to create a listLayoutStrategy.
*
Expand All @@ -68,6 +77,8 @@ public static final class Builder {

private int bottomGap;

private List<String> growableNodeIds = List.of();

private Builder() {
// Prevent instantiation
}
Expand All @@ -87,11 +98,17 @@ public Builder bottomGap(int bottomGap) {
return this;
}

public Builder growableNodeIds(List<String> growableNodeIds) {
this.growableNodeIds = growableNodeIds;
return this;
}

public ListLayoutStrategy build() {
ListLayoutStrategy listLayoutStrategy = new ListLayoutStrategy();
listLayoutStrategy.areChildNodesDraggable = this.areChildNodesDraggable;
listLayoutStrategy.topGap = this.topGap;
listLayoutStrategy.bottomGap = this.bottomGap;
listLayoutStrategy.growableNodeIds = Objects.requireNonNull(this.growableNodeIds);
return listLayoutStrategy;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { IconLabelNodeData } from '../renderer/node/IconsLabelNode.types';
import { GQLDiagramDescription } from '../representation/DiagramRepresentation.types';
import { IConvertEngine, INodeConverter } from './ConvertEngine.types';
import { convertLabelStyle, convertOutsideLabels } from './convertLabel';
import { isListLayoutStrategy } from './convertDiagram';

const defaultPosition: XYPosition = { x: 0, y: 0 };

Expand Down Expand Up @@ -79,6 +80,7 @@ const toIconLabelNode = (
connectionHandles,
isNew,
resizedByUser,
isListChild: isListLayoutStrategy(gqlParentNode?.childrenLayoutStrategy),
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { IConvertEngine, INodeConverter } from './ConvertEngine.types';
import { convertLineStyle } from './convertDiagram';
import { convertHandles } from './convertHandles';
import { convertLabelStyle, convertOutsideLabels } from './convertLabel';
import { isListLayoutStrategy } from './convertDiagram';

const defaultPosition: XYPosition = { x: 0, y: 0 };

Expand Down Expand Up @@ -81,6 +82,7 @@ const toImageNode = (
connectionHandles,
isNew,
resizedByUser,
isListChild: isListLayoutStrategy(gqlParentNode?.childrenLayoutStrategy),
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import {
GQLNodeStyle,
GQLRectangularNodeStyle,
GQLViewModifier,
ILayoutStrategy,
ListLayoutStrategy,
} from '../graphql/subscription/nodeFragment.types';
import { BorderNodePosition } from '../renderer/DiagramRenderer.types';
import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types';
Expand All @@ -30,12 +28,10 @@ import { IConvertEngine, INodeConverter } from './ConvertEngine.types';
import { AlignmentMap } from './convertDiagram.types';
import { convertHandles } from './convertHandles';
import { convertLabelStyle, convertOutsideLabels } from './convertLabel';
import { isListLayoutStrategy } from './convertDiagram';

const defaultPosition: XYPosition = { x: 0, y: 0 };

const isListLayoutStrategy = (strategy: ILayoutStrategy | undefined): strategy is ListLayoutStrategy =>
strategy?.kind === 'List';

const toListNode = (
gqlDiagram: GQLDiagram,
gqlNode: GQLNode<GQLRectangularNodeStyle>,
Expand Down Expand Up @@ -100,7 +96,11 @@ const toListNode = (
: true,
topGap: isListLayoutStrategy(gqlNode.childrenLayoutStrategy) ? gqlNode.childrenLayoutStrategy.topGap : 0,
bottomGap: isListLayoutStrategy(gqlNode.childrenLayoutStrategy) ? gqlNode.childrenLayoutStrategy.bottomGap : 0,
isListChild: isListLayoutStrategy(gqlParentNode?.childrenLayoutStrategy),
resizedByUser,
growableNodeIds: isListLayoutStrategy(gqlNode.childrenLayoutStrategy)
? gqlNode.childrenLayoutStrategy.growableNodeIds
: [],
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types';
import { FreeFormNodeData } from '../renderer/node/FreeFormNode.types';
import { GQLDiagramDescription } from '../representation/DiagramRepresentation.types';
import { IConvertEngine, INodeConverter } from './ConvertEngine.types';
import { convertLineStyle } from './convertDiagram';
import { convertLineStyle, isListLayoutStrategy } from './convertDiagram';
import { AlignmentMap } from './convertDiagram.types';
import { convertHandles } from './convertHandles';
import { convertLabelStyle, convertOutsideLabels } from './convertLabel';
Expand Down Expand Up @@ -89,6 +89,7 @@ const toRectangularNode = (
connectionHandles,
isNew,
resizedByUser,
isListChild: isListLayoutStrategy(gqlParentNode?.childrenLayoutStrategy),
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import { Edge, Node } from 'reactflow';
import { GQLNodeDescription } from '../graphql/query/nodeDescriptionFragment.types';
import { GQLDiagram } from '../graphql/subscription/diagramFragment.types';
import { GQLLabel } from '../graphql/subscription/labelFragment.types';
import { GQLNode, GQLNodeStyle, GQLViewModifier } from '../graphql/subscription/nodeFragment.types';
import {
GQLNode,
GQLNodeStyle,
GQLViewModifier,
ILayoutStrategy,
ListLayoutStrategy,
} from '../graphql/subscription/nodeFragment.types';
import { Diagram, EdgeLabel, NodeData } from '../renderer/DiagramRenderer.types';
import { MultiLabelEdgeData } from '../renderer/edge/MultiLabelEdge.types';
import { RawDiagram } from '../renderer/layout/layout.types';
Expand Down Expand Up @@ -69,6 +75,9 @@ export const convertLineStyle = (lineStyle: string): string => {
return 'solid';
};

export const isListLayoutStrategy = (strategy: ILayoutStrategy | undefined): strategy is ListLayoutStrategy =>
strategy?.kind === 'List';

const defaultNodeConverters: INodeConverter[] = [
new RectangleNodeConverter(),
new ImageNodeConverter(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ fragment nodeFragment on Node {
areChildNodesDraggable
topGap
bottomGap
growableNodeIds
}
... on FreeFormLayoutStrategy {
kind
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface ListLayoutStrategy extends ILayoutStrategy {
areChildNodesDraggable: boolean;
topGap: number;
bottomGap: number;
growableNodeIds: string[];
}

export interface FreeFormLayoutStrategy extends ILayoutStrategy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type { DiagramContextValue } from './contexts/DiagramContext.types';
export { NodeTypeContext } from './contexts/NodeContext';
export type { NodeTypeContextValue, NodeTypeContributionElement } from './contexts/NodeContext.types';
export type { IConvertEngine, INodeConverter } from './converter/ConvertEngine.types';
export { convertLineStyle } from './converter/convertDiagram';
export { convertLineStyle, isListLayoutStrategy } from './converter/convertDiagram';
export { AlignmentMap } from './converter/convertDiagram.types';
export { convertHandles } from './converter/convertHandles';
export { convertLabelStyle, convertOutsideLabels } from './converter/convertLabel';
Expand All @@ -39,6 +39,7 @@ export { ConnectionTargetHandle } from './renderer/handles/ConnectionTargetHandl
export { useRefreshConnectionHandles } from './renderer/handles/useRefreshConnectionHandles';
export type { ILayoutEngine, INodeLayoutHandler } from './renderer/layout/LayoutEngine.types';
export { computePreviousPosition, computePreviousSize } from './renderer/layout/bounds';
export type { ForcedDimensions } from './renderer/layout/layout.types';
export * from './renderer/layout/layoutBorderNodes';
export * from './renderer/layout/layoutNode';
export { useLayout } from './renderer/layout/useLayout';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { useDiagramSelection } from './selection/useDiagramSelection';
import { useSnapToGrid } from './snap-to-grid/useSnapToGrid';

import 'reactflow/dist/style.css';
import { useResizeChange } from './resize/useResizeChange';

const GRID_STEP: number = 10;

Expand Down Expand Up @@ -139,6 +140,7 @@ export const DiagramRenderer = ({ diagramRefreshedEventPayload }: DiagramRendere
const { updateSelectionOnNodesChange, updateSelectionOnEdgesChange } = useDiagramSelection();
const { transformBorderNodeChanges } = useBorderChange();
const { transformUndraggableListNodeChanges, applyMoveChange } = useMoveChange();
const { transformResizeListNodeChanges } = useResizeChange();
const { applyHandleChange } = useHandleChange();
const { layoutOnBoundsChange } = useLayoutOnBoundsChange(diagramRefreshedEventPayload.id);
const { filterReadOnlyChanges } = useFilterReadOnlyChanges();
Expand All @@ -156,6 +158,7 @@ export const DiagramRenderer = ({ diagramRefreshedEventPayload }: DiagramRendere
setNodes((oldNodes) => {
let transformedNodeChanges = transformBorderNodeChanges(noReadOnlyChanges);
transformedNodeChanges = transformUndraggableListNodeChanges(transformedNodeChanges);
transformedNodeChanges = transformResizeListNodeChanges(transformedNodeChanges);

if (transformedNodeChanges.some((change) => change.type === 'position')) {
hideDiagramElementPalette();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export interface NodeData {
connectionHandles: ConnectionHandle[];
isNew: boolean;
resizedByUser: boolean;
isListChild: boolean;
}

export enum BorderNodePosition {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { FreeFormNodeData } from '../node/FreeFormNode.types';
import { DiagramNodeType } from '../node/NodeTypes.types';
import { ILayoutEngine, INodeLayoutHandler } from './LayoutEngine.types';
import { computePreviousPosition, computePreviousSize } from './bounds';
import { RawDiagram } from './layout.types';
import { RawDiagram, ForcedDimensions } from './layout.types';
import { getBorderNodeExtent } from './layoutBorderNodes';
import {
applyRatioOnNewNodeSizeValue,
Expand Down Expand Up @@ -47,7 +47,7 @@ export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<FreeFormNod
visibleNodes: Node<NodeData, DiagramNodeType>[],
directChildren: Node<NodeData, DiagramNodeType>[],
newlyAddedNode: Node<NodeData, DiagramNodeType> | undefined,
forceWidth?: number
forceDimensions?: ForcedDimensions
) {
const nodeIndex = findNodeIndex(visibleNodes, node.id);
const nodeElement = document.getElementById(`${node.id}-freeFormNode-${nodeIndex}`)?.children[0];
Expand All @@ -62,10 +62,10 @@ export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<FreeFormNod
directChildren,
newlyAddedNode,
borderWidth,
forceWidth
forceDimensions
);
} else {
this.handleLeafNode(previousDiagram, node, visibleNodes, borderWidth, forceWidth);
this.handleLeafNode(previousDiagram, node, visibleNodes, borderWidth, forceDimensions);
}
}

Expand All @@ -77,7 +77,7 @@ export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<FreeFormNod
directChildren: Node<NodeData, DiagramNodeType>[],
newlyAddedNode: Node<NodeData, DiagramNodeType> | undefined,
borderWidth: number,
forceWidth?: number
forceDimensions?: ForcedDimensions
) {
layoutEngine.layoutNodes(previousDiagram, visibleNodes, directChildren, newlyAddedNode);

Expand Down Expand Up @@ -167,8 +167,8 @@ export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<FreeFormNod
Math.max(directChildrenAwareNodeHeight, eastBorderNodeFootprintHeight, westBorderNodeFootprintHeight) +
borderWidth * 2;

const nodeWith = forceWidth ?? getDefaultOrMinWidth(nodeMinComputeWidth, node); // WARN: not sure yet for the forceWidth to be here.
const nodeHeight = getDefaultOrMinHeight(nodeMinComputeHeight, node);
const nodeWidth = forceDimensions?.width ?? getDefaultOrMinWidth(nodeMinComputeWidth, node);
const nodeHeight = forceDimensions?.height ?? getDefaultOrMinHeight(nodeMinComputeHeight, node);

const previousNode = (previousDiagram?.nodes ?? []).find((previouseNode) => previouseNode.id === node.id);
const previousDimensions = computePreviousSize(previousNode, node);
Expand All @@ -184,7 +184,7 @@ export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<FreeFormNod
node.height = previousDimensions.height;
}
} else {
node.width = nodeWith;
node.width = nodeWidth;
node.height = nodeHeight;
}

Expand All @@ -204,7 +204,7 @@ export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<FreeFormNod
node: Node<FreeFormNodeData, 'freeFormNode'>,
visibleNodes: Node<NodeData, DiagramNodeType>[],
borderWidth: number,
forceWidth?: number
forceDimensions?: ForcedDimensions
) {
const nodeIndex = findNodeIndex(visibleNodes, node.id);
const labelElement = document.getElementById(`${node.id}-label-${nodeIndex}`);
Expand All @@ -217,8 +217,8 @@ export class FreeFormNodeLayoutHandler implements INodeLayoutHandler<FreeFormNod
const nodeMinComputeHeight =
rectangularNodePadding + (labelElement?.getBoundingClientRect().height ?? 0) + rectangularNodePadding;

const nodeWith = forceWidth ?? getDefaultOrMinWidth(nodeMinComputeWidth, node);
const nodeHeight = getDefaultOrMinHeight(nodeMinComputeHeight, node);
const nodeWith = forceDimensions?.width ?? getDefaultOrMinWidth(nodeMinComputeWidth, node);
const nodeHeight = forceDimensions?.height ?? getDefaultOrMinHeight(nodeMinComputeHeight, node);

const previousNode = (previousDiagram?.nodes ?? []).find((previouseNode) => previouseNode.id === node.id);
const previousDimensions = computePreviousSize(previousNode, node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,29 @@ import { NodeData } from '../DiagramRenderer.types';
import { IconLabelNodeData } from '../node/IconsLabelNode.types';
import { DiagramNodeType } from '../node/NodeTypes.types';
import { ILayoutEngine, INodeLayoutHandler } from './LayoutEngine.types';
import { RawDiagram } from './layout.types';
import { RawDiagram, ForcedDimensions } from './layout.types';

const rectangularNodePadding = 8;

export class IconLabelNodeLayoutHandler implements INodeLayoutHandler<IconLabelNodeData> {
canHandle(node: Node<NodeData, DiagramNodeType>) {
return node.type === 'iconLabelNode';
}

handle(
_layoutEngine: ILayoutEngine,
_previousDiagram: RawDiagram | null,
node: Node<IconLabelNodeData>,
visibleNodes: Node<NodeData, DiagramNodeType>[],
_directChildren: Node<NodeData, DiagramNodeType>[],
_newlyAddedNode: Node<NodeData, DiagramNodeType> | undefined,
forceWidth?: number
forceDimensions?: ForcedDimensions
) {
const nodeIndex = this.findNodeIndex(visibleNodes, node.id);
const labelElement = document.getElementById(`${node.id}-label-${nodeIndex}`);

node.width =
forceWidth ??
forceDimensions?.width ??
rectangularNodePadding + (labelElement?.getBoundingClientRect().width ?? 0) + rectangularNodePadding;
node.height = labelElement?.getBoundingClientRect().height;
}
Expand Down

0 comments on commit d02067c

Please sign in to comment.