Skip to content

Commit d6479b5

Browse files
Merge pull request #216 from jeff-phillips-18/group-label-position
feat(group label): Add ability to center the group's label on the edge
2 parents 6718a46 + 57b04bd commit d6479b5

10 files changed

Lines changed: 116 additions & 69 deletions

File tree

.eslintignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ coverage
1111
.eslintcache
1212
generated
1313
*.d.ts
14-
*/css/*.js
14+
**/css/*.js
1515

1616
# package managers
1717
yarn-error.log

packages/demo-app-ts/src/demos/pipelineGroupsDemo/DemoTaskGroup.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const DemoTaskGroup: React.FunctionComponent<DemoTaskGroupProps> = ({ element, .
4343
collapsedWidth={GROUP_TASK_WIDTH}
4444
collapsedHeight={DEFAULT_TASK_HEIGHT}
4545
element={element as Node}
46+
centerLabelOnEdge
4647
recreateLayoutOnCollapseChange
4748
getEdgeCreationTypes={getEdgeCreationTypes}
4849
scaleNode={hover && detailsLevel !== ScaleDetailsLevel.high}

packages/demo-app-ts/src/demos/pipelineGroupsDemo/createDemoPipelineGroupsNodes.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export const createExecution3 = (runAfter?: string): [string, PipelineNodeModel[
155155
height: DEFAULT_TASK_HEIGHT,
156156
group: true,
157157
style: {
158-
padding: [NODE_PADDING_VERTICAL, NODE_PADDING_HORIZONTAL]
158+
padding: [GROUP_PADDING_VERTICAL, GROUP_PADDING_HORIZONTAL]
159159
},
160160
runAfterTasks: [task_3_1.id],
161161
data: {
@@ -386,7 +386,7 @@ export const createComplexDemoPipelineGroupsNodes = (): PipelineNodeModel[] => [
386386
'metadata',
387387
'training-configurator-and-validator'
388388
],
389-
style: { padding: [15, 15] },
389+
style: { padding: [GROUP_PADDING_VERTICAL, GROUP_PADDING_HORIZONTAL] },
390390
data: {}
391391
},
392392
{
@@ -420,7 +420,7 @@ export const createComplexDemoPipelineGroupsNodes = (): PipelineNodeModel[] => [
420420
'model',
421421
'model-upload'
422422
],
423-
style: { padding: [15, 15] },
423+
style: { padding: [GROUP_PADDING_VERTICAL, GROUP_PADDING_HORIZONTAL] },
424424
data: {}
425425
},
426426
{
@@ -519,7 +519,7 @@ export const createComplexDemoPipelineGroupsNodes = (): PipelineNodeModel[] => [
519519
'model-evaluation-import',
520520
'table-to-uri'
521521
],
522-
style: { padding: [15, 15] },
522+
style: { padding: [GROUP_PADDING_VERTICAL, GROUP_PADDING_HORIZONTAL] },
523523
data: {}
524524
},
525525
{
@@ -748,7 +748,7 @@ export const createComplexDemoPipelineGroupsNodes = (): PipelineNodeModel[] => [
748748
'get-prediction-image-uri-2',
749749
'model-upload-2'
750750
],
751-
style: { padding: [15, 15] },
751+
style: { padding: [GROUP_PADDING_VERTICAL, GROUP_PADDING_HORIZONTAL] },
752752
data: {}
753753
},
754754
{
@@ -797,7 +797,7 @@ export const createComplexDemoPipelineGroupsNodes = (): PipelineNodeModel[] => [
797797
'model-evaluation-import-2',
798798
'table-to-uri-2'
799799
],
800-
style: { padding: [15, 15] },
800+
style: { padding: [GROUP_PADDING_VERTICAL, GROUP_PADDING_HORIZONTAL] },
801801
data: {}
802802
},
803803
{

packages/module/src/components/ElementWrapper.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useDndManager } from '../behavior/useDndManager';
99

1010
interface ElementWrapperProps {
1111
element: Graph | Edge | Node;
12+
level?: number; // used for groups to set z-index keeping child groups drawn on top of their parent.
1213
}
1314

1415
const NodeElementComponent: React.FunctionComponent<{ element: Node }> = observer(({ element }) => {
@@ -44,7 +45,7 @@ const ElementComponent: React.FunctionComponent<ElementWrapperProps> = observer(
4445
return null;
4546
});
4647

47-
const ElementChildren: React.FunctionComponent<ElementWrapperProps> = observer(({ element }) => (
48+
const ElementChildren: React.FunctionComponent<ElementWrapperProps> = observer(({ element, level }) => (
4849
<>
4950
{element
5051
.getChildren()
@@ -56,12 +57,12 @@ const ElementChildren: React.FunctionComponent<ElementWrapperProps> = observer((
5657
.getChildren()
5758
.filter(isNode)
5859
.map((e) => (
59-
<ElementWrapper key={e.getId()} element={e} />
60+
<ElementWrapper key={e.getId()} element={e} level={level} />
6061
))}
6162
</>
6263
));
6364

64-
const ElementWrapper: React.FunctionComponent<ElementWrapperProps> = observer(({ element }) => {
65+
const ElementWrapper: React.FunctionComponent<ElementWrapperProps> = observer(({ element, level = 0 }) => {
6566
if (!element.isVisible()) {
6667
if (!isNode(element) || element.isDimensionsInitialized()) {
6768
return null;
@@ -116,9 +117,9 @@ const ElementWrapper: React.FunctionComponent<ElementWrapperProps> = observer(({
116117
);
117118
}
118119
return (
119-
<g {...commonAttrs}>
120+
<g {...commonAttrs} style={{ zIndex: level }}>
120121
<ElementComponent element={element} />
121-
<ElementChildren element={element} />
122+
<ElementChildren element={element} level={level + 1} />
122123
</g>
123124
);
124125
});

packages/module/src/components/nodes/labels/NodeLabel.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ type NodeLabelProps = {
1919
x?: number;
2020
y?: number;
2121
position?: LabelPosition;
22+
centerLabelOnEdge?: boolean;
23+
boxRef?: React.Ref<SVGRectElement>;
2224
cornerRadius?: number;
2325
status?: NodeStatus;
2426
secondaryLabel?: string;
@@ -54,6 +56,7 @@ const NodeLabel: React.FunctionComponent<NodeLabelProps> = ({
5456
x = 0,
5557
y = 0,
5658
position = LabelPosition.bottom,
59+
centerLabelOnEdge,
5760
secondaryLabel,
5861
status,
5962
badge,
@@ -76,6 +79,7 @@ const NodeLabel: React.FunctionComponent<NodeLabelProps> = ({
7679
actionIcon,
7780
actionIconClassName,
7881
onActionIconClick,
82+
boxRef,
7983
...other
8084
}) => {
8185
const [labelHover, labelHoverRef] = useHover();
@@ -123,26 +127,26 @@ const NodeLabel: React.FunctionComponent<NodeLabelProps> = ({
123127
const primaryWidth = iconSpace + badgeSpace + paddingX + textSize.width + actionSpace + contextSpace + paddingX;
124128
const secondaryWidth = secondaryLabel && secondaryTextSize ? secondaryTextSize.width + 2 * paddingX : 0;
125129
const width = Math.max(primaryWidth, secondaryWidth);
130+
const backgroundHeight =
131+
height + (secondaryLabel && secondaryTextSize ? secondaryTextSize.height + paddingY * 2 : 0);
126132

127133
let startX: number;
128134
let startY: number;
129135
if (position === LabelPosition.top) {
130136
startX = x - width / 2;
131-
startY = -y - height - paddingY;
137+
startY = -y - height - (centerLabelOnEdge ? -backgroundHeight / 2 : paddingY);
132138
} else if (position === LabelPosition.right) {
133-
startX = x + iconSpace;
139+
startX = x + iconSpace - (centerLabelOnEdge ? width / 2 : 0);
134140
startY = y - height / 2;
135141
} else if (position === LabelPosition.left) {
136-
startX = -width - paddingX;
137-
startY = y - height / 2 + paddingY;
142+
startX = centerLabelOnEdge ? x - width / 2 : -width - paddingX;
143+
startY = y - height / 2;
138144
} else {
139145
startX = x - width / 2 + iconSpace / 2;
140-
startY = y;
146+
startY = y - (centerLabelOnEdge ? backgroundHeight / 2 : 0);
141147
}
142148
const actionStartX = iconSpace + badgeSpace + paddingX + textSize.width + paddingX;
143149
const contextStartX = actionStartX + actionSpace;
144-
const backgroundHeight =
145-
height + (secondaryLabel && secondaryTextSize ? secondaryTextSize.height + paddingY * 2 : 0);
146150
let badgeStartX = 0;
147151
let badgeStartY = 0;
148152
if (badgeSize) {
@@ -183,6 +187,7 @@ const NodeLabel: React.FunctionComponent<NodeLabelProps> = ({
183187
contextSize,
184188
secondaryLabel,
185189
secondaryTextSize,
190+
centerLabelOnEdge,
186191
position,
187192
x,
188193
y
@@ -200,8 +205,9 @@ const NodeLabel: React.FunctionComponent<NodeLabelProps> = ({
200205
<NodeShadows />
201206
{textSize && (
202207
<rect
208+
ref={boxRef}
203209
className={css(styles.topologyNodeLabelBackground)}
204-
key={`rect-${filterId}`} // update key to force remount on filter update
210+
key={`rect-${filterId}-${width}`} // update key to force remount on filter or size update
205211
filter={filterId && createSvgIdUrl(filterId)}
206212
x={0}
207213
y={0}

packages/module/src/pipelines/components/anchors/TaskGroupSourceAnchor.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { Node } from '../../../types';
44

55
export default class TaskGroupSourceAnchor extends AbstractAnchor {
66
private vertical = false;
7+
private anchorOffset = 0;
78

8-
constructor(owner: Node, vertical: boolean = true) {
9+
constructor(owner: Node, vertical: boolean = true, offset: number = 0) {
910
super(owner);
1011
this.vertical = vertical;
12+
this.anchorOffset = offset;
1113
}
1214

1315
getLocation(): Point {
@@ -17,8 +19,8 @@ export default class TaskGroupSourceAnchor extends AbstractAnchor {
1719
getReferencePoint(): Point {
1820
const bounds = this.owner.getBounds();
1921
if (this.vertical) {
20-
return new Point(bounds.x + bounds.width / 2, bounds.bottom());
22+
return new Point(bounds.x + bounds.width / 2, bounds.bottom() + this.anchorOffset);
2123
}
22-
return new Point(bounds.right(), bounds.y + bounds.height / 2);
24+
return new Point(bounds.right() + this.anchorOffset, bounds.y + bounds.height / 2);
2325
}
2426
}

packages/module/src/pipelines/components/anchors/TaskGroupTargetAnchor.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { Point } from '../../../geom';
44

55
export default class TaskGroupTargetAnchor extends AbstractAnchor {
66
private vertical = false;
7+
private anchorOffset = 0;
78

8-
constructor(owner: Node, vertical = false) {
9+
constructor(owner: Node, vertical = false, offset: number = 0) {
910
super(owner);
1011
this.vertical = vertical;
12+
this.anchorOffset = offset;
1113
}
1214

1315
getLocation(): Point {
@@ -18,8 +20,8 @@ export default class TaskGroupTargetAnchor extends AbstractAnchor {
1820
const bounds = this.owner.getBounds();
1921

2022
if (this.vertical) {
21-
return new Point(bounds.x + bounds.width / 2, bounds.y);
23+
return new Point(bounds.x + bounds.width / 2, bounds.y - this.anchorOffset);
2224
}
23-
return new Point(bounds.x, bounds.y + bounds.height / 2);
25+
return new Point(bounds.x - this.anchorOffset, bounds.y + bounds.height / 2);
2426
}
2527
}

packages/module/src/pipelines/components/groups/DefaultTaskGroup.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ export interface DefaultTaskGroupProps {
6262
truncateLength?: number;
6363
/** Space between the label and the group. Defaults to 17 */
6464
labelOffset?: number;
65+
/** Center the label on the edge, overrides the label offset, only applicable to expanded groups */
66+
centerLabelOnEdge?: boolean;
6567
/** The Icon class to show in the label, ignored when labelIcon is specified */
6668
labelIconClass?: string;
6769
/** The label icon component to show in the label, takes precedence over labelIconClass */

0 commit comments

Comments
 (0)