From 258a4526f02e4f4980e575212c8ea744d2cf0a46 Mon Sep 17 00:00:00 2001 From: Leandro Beretta Date: Tue, 24 Oct 2023 13:17:18 -0300 Subject: [PATCH] added property to set whether the outline is hull or rect --- .../demo-app-ts/src/components/StyleGroup.tsx | 1 + .../src/components/groups/DefaultGroup.tsx | 2 + .../groups/DefaultGroupExpanded.tsx | 79 +++++++++++-------- 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/packages/demo-app-ts/src/components/StyleGroup.tsx b/packages/demo-app-ts/src/components/StyleGroup.tsx index a566ac86..6e8e7333 100644 --- a/packages/demo-app-ts/src/components/StyleGroup.tsx +++ b/packages/demo-app-ts/src/components/StyleGroup.tsx @@ -84,6 +84,7 @@ const StyleGroup: React.FunctionComponent = ({ collapsedWidth={collapsedWidth} collapsedHeight={collapsedHeight} showLabel={detailsLevel === ScaleDetailsLevel.high} + hulledOutline={true} {...rest} {...passedData} > diff --git a/packages/module/src/components/groups/DefaultGroup.tsx b/packages/module/src/components/groups/DefaultGroup.tsx index a188d6ab..1beab616 100644 --- a/packages/module/src/components/groups/DefaultGroup.tsx +++ b/packages/module/src/components/groups/DefaultGroup.tsx @@ -77,6 +77,8 @@ interface DefaultGroupProps { onContextMenu?: (e: React.MouseEvent) => void; /** Flag indicating that the context menu for the node is currently open */ contextMenuOpen?: boolean; + /** Flag indicating whether to use hull layout or rect layout for expanded groups. Defaults to hull (true) */ + hulledOutline?: boolean; } type DefaultGroupInnerProps = Omit & { element: Node }; diff --git a/packages/module/src/components/groups/DefaultGroupExpanded.tsx b/packages/module/src/components/groups/DefaultGroupExpanded.tsx index 9087d741..84778c6a 100644 --- a/packages/module/src/components/groups/DefaultGroupExpanded.tsx +++ b/packages/module/src/components/groups/DefaultGroupExpanded.tsx @@ -19,6 +19,7 @@ import { WithSelectionProps } from '../../behavior'; import { CollapsibleGroupProps } from './types'; +import Rect from '../../geom/Rect'; type DefaultGroupExpandedProps = { className?: string; @@ -41,6 +42,7 @@ type DefaultGroupExpandedProps = { labelIconClass?: string; // Icon to show in label labelIcon?: string; labelIconPadding?: number; + hulledOutline?: boolean; } & CollapsibleGroupProps & WithDragNodeProps & WithSelectionProps & WithDndDropProps & WithContextMenuProps; type PointWithSize = [number, number, number]; @@ -96,7 +98,8 @@ const DefaultGroupExpanded: React.FunctionComponent = labelIconClass, labelIcon, labelIconPadding, - onCollapseChange + onCollapseChange, + hulledOutline, }) => { const [hovered, hoverRef] = useHover(); const [labelHover, labelHoverRef] = useHover(); @@ -107,6 +110,8 @@ const DefaultGroupExpanded: React.FunctionComponent = const outlineRef = useCombineRefs(dndDropRef, anchorRef); const labelLocation = React.useRef(); const pathRef = React.useRef(); + const boxRef = React.useRef(null); + const nodeElement = element as Node; let parent = element.getParent(); let altGroup = false; @@ -118,40 +123,47 @@ const DefaultGroupExpanded: React.FunctionComponent = // cast to number and coerce const padding = maxPadding(element.getStyle().padding ?? 17); const hullPadding = (point: PointWithSize | PointTuple) => (point[2] || 0) + padding; - - if (!droppable || !pathRef.current || !labelLocation.current) { - const children = element.getNodes().filter(c => c.isVisible()); - if (children.length === 0) { - return null; - } - const points: (PointWithSize | PointTuple)[] = []; - _.forEach(children, c => { - if (c.getNodeShape() === NodeShape.circle) { - const bounds = c.getBounds(); - const { width, height } = bounds; - const { x, y } = bounds.getCenter(); - const radius = Math.max(width, height) / 2; - points.push([x, y, radius] as PointWithSize); - } else { - // add all 4 corners - const { width, height, x, y } = c.getBounds(); - points.push([x, y, 0] as PointWithSize); - points.push([x + width, y, 0] as PointWithSize); - points.push([x, y + height, 0] as PointWithSize); - points.push([x + width, y + height, 0] as PointWithSize); + + if (hulledOutline) { + if (!droppable || !pathRef.current || !labelLocation.current) { + const children = element.getNodes().filter(c => c.isVisible()); + if (children.length === 0) { + return null; + } + const points: (PointWithSize | PointTuple)[] = []; + _.forEach(children, c => { + if (c.getNodeShape() === NodeShape.circle) { + const bounds = c.getBounds(); + const { width, height } = bounds; + const { x, y } = bounds.getCenter(); + const radius = Math.max(width, height) / 2; + points.push([x, y, radius] as PointWithSize); + } else { + // add all 4 corners + const { width, height, x, y } = c.getBounds(); + points.push([x, y, 0] as PointWithSize); + points.push([x + width, y, 0] as PointWithSize); + points.push([x, y + height, 0] as PointWithSize); + points.push([x + width, y + height, 0] as PointWithSize); + } + }); + const hullPoints: (PointWithSize | PointTuple)[] = + points.length > 2 ? polygonHull(points as PointTuple[]) : (points as PointTuple[]); + if (!hullPoints) { + return null; } - }); - const hullPoints: (PointWithSize | PointTuple)[] = - points.length > 2 ? polygonHull(points as PointTuple[]) : (points as PointTuple[]); - if (!hullPoints) { - return null; - } - // change the box only when not dragging - pathRef.current = hullPath(hullPoints as PointTuple[], hullPadding); + // change the box only when not dragging + pathRef.current = hullPath(hullPoints as PointTuple[], hullPadding); - // Compute the location of the group label. - labelLocation.current = computeLabelLocation(hullPoints as PointWithSize[]); + // Compute the location of the group label. + labelLocation.current = computeLabelLocation(hullPoints as PointWithSize[]); + } + } else { + if (!droppable || !boxRef.current) { + // change the box only when not dragging + boxRef.current = nodeElement.getBounds(); + } } const groupClassName = css( @@ -177,7 +189,8 @@ const DefaultGroupExpanded: React.FunctionComponent = - + {hulledOutline && } + {!hulledOutline && } {showLabel && (label || element.getLabel()) && (