Skip to content

Commit 3a6ae27

Browse files
committed
feature/COMPASS-9655 add getCoordinatesForNewNode
1 parent f9f288c commit 3a6ae27

File tree

6 files changed

+80
-27
lines changed

6 files changed

+80
-27
lines changed

src/components/diagram.stories.tsx

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,66 @@
1-
import { Meta, StoryObj } from '@storybook/react';
1+
import { useMemo, useCallback, useState } from 'react';
2+
import { Decorator, Meta, StoryObj } from '@storybook/react';
23

34
import { Diagram } from '@/components/diagram';
45
import { EMPLOYEE_TERRITORIES_NODE, EMPLOYEES_NODE, ORDERS_NODE } from '@/mocks/datasets/nodes';
56
import { EMPLOYEES_TO_EMPLOYEES_EDGE, ORDERS_TO_EMPLOYEES_EDGE } from '@/mocks/datasets/edges';
67
import { DiagramStressTestDecorator } from '@/mocks/decorators/diagram-stress-test.decorator';
78
import { DiagramConnectableDecorator } from '@/mocks/decorators/diagram-connectable.decorator';
9+
import { DiagramProps } from '@/types';
10+
11+
const randomId = () => Math.floor(Math.random() * 100);
12+
13+
const RefreshingDecorator: Decorator<DiagramProps> = (Story, context) => {
14+
const [nodes, setNodes] = useState(context.args.nodes || []);
15+
16+
const onNodeDragStop = useCallback((evt, node) => {
17+
setNodes(prevNodes =>
18+
prevNodes.map(n => {
19+
if (n.id === node.id) {
20+
return {
21+
...n,
22+
title: `${n.title.split('-')[0]}-${randomId()}`,
23+
position: { x: node.position.x, y: node.position.y },
24+
};
25+
}
26+
return {
27+
...n,
28+
title: `${n.title.split('-')[0]}-${randomId()}`,
29+
};
30+
}),
31+
);
32+
}, []);
33+
34+
return Story({
35+
...context,
36+
args: {
37+
...context.args,
38+
nodes,
39+
onNodeDragStop: onNodeDragStop,
40+
},
41+
});
42+
};
843

944
const diagram: Meta<typeof Diagram> = {
1045
title: 'Diagram',
1146
component: Diagram,
47+
decorators: [RefreshingDecorator],
1248
args: {
1349
title: 'MongoDB Diagram',
1450
isDarkMode: true,
51+
nodes: [
52+
ORDERS_NODE,
53+
EMPLOYEES_NODE,
54+
EMPLOYEE_TERRITORIES_NODE,
55+
EMPLOYEE_TERRITORIES_NODE,
56+
EMPLOYEE_TERRITORIES_NODE,
57+
EMPLOYEE_TERRITORIES_NODE,
58+
EMPLOYEE_TERRITORIES_NODE,
59+
EMPLOYEE_TERRITORIES_NODE,
60+
EMPLOYEE_TERRITORIES_NODE,
61+
EMPLOYEE_TERRITORIES_NODE,
62+
],
1563
edges: [ORDERS_TO_EMPLOYEES_EDGE, EMPLOYEES_TO_EMPLOYEES_EDGE],
16-
nodes: [ORDERS_NODE, EMPLOYEES_NODE, EMPLOYEE_TERRITORIES_NODE],
1764
},
1865
};
1966

src/components/edge/floating-edge.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import { EdgeProps, getSmoothStepPath, useNodes } from '@xyflow/react';
1+
import { EdgeProps, getBezierPath, getSmoothStepPath, getStraightPath, useNodes, XYPosition } from '@xyflow/react';
22
import { useMemo } from 'react';
33

44
import { getEdgeParams } from '@/utilities/get-edge-params';
55
import { InternalNode } from '@/types/internal';
66
import { Edge } from '@/components/edge/edge';
7+
import { DEFAULT_MARKER_SIZE } from '@/utilities/constants';
8+
9+
const getDistance = (pointA: XYPosition, pointB: XYPosition) => {
10+
const dx = pointB.x - pointA.x;
11+
const dy = pointB.y - pointA.y;
12+
return Math.sqrt(dx * dx + dy * dy);
13+
};
714

815
export const FloatingEdge = ({ id, source, target, markerEnd, markerStart, selected }: EdgeProps) => {
916
const nodes = useNodes<InternalNode>();
@@ -18,7 +25,10 @@ export const FloatingEdge = ({ id, source, target, markerEnd, markerStart, selec
1825

1926
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
2027

21-
const [path] = getSmoothStepPath({
28+
const isShortDistance = getDistance({ x: sx, y: sy }, { x: tx, y: ty }) < DEFAULT_MARKER_SIZE * 2 + 20;
29+
const getPath = !isShortDistance ? getSmoothStepPath : getSmoothStepPath;
30+
31+
const [path] = getPath({
2232
sourceX: sx,
2333
sourceY: sy,
2434
sourcePosition: sourcePos,
@@ -30,6 +40,8 @@ export const FloatingEdge = ({ id, source, target, markerEnd, markerStart, selec
3040
return (
3141
<Edge
3242
data-testid={`floating-edge-${id}`}
43+
// markerEnd={!isShortDistance ? markerEnd : undefined}
44+
// markerStart={!isShortDistance ? markerStart : undefined}
3345
markerEnd={markerEnd}
3446
markerStart={markerStart}
3547
path={path}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from '@/components/diagram';
22
export * from '@/utilities/apply-layout';
33
export * from '@/utilities/add-nodes-within-bounds';
4+
export { getCoordinatesForNewNode } from '@/utilities/get-coordinates-for-new-node';
45
export * from '@/utilities/get-nodes-bounds';
56
export * from '@/types';
67
export { useDiagram, type DiagramInstance } from '@/hooks/use-diagram';
Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { DEFAULT_NODE_HEIGHT, DEFAULT_NODE_SPACING, DEFAULT_NODE_WIDTH } from '@/utilities/constants';
2-
import { BaseNode } from '@/types/layout';
1+
import type { BaseNode } from '@/types/layout';
2+
3+
import { getNodeWidth, getNodeHeight } from './node-dimensions';
4+
import { getCoordinatesForNewNodeFromMaxDimensions } from './get-coordinates-for-new-node';
35

46
/**
57
* Adds new nodes to an existing array of nodes, positioning them in a grid pattern.
@@ -14,28 +16,14 @@ import { BaseNode } from '@/types/layout';
1416
* @param newNodes A list of new nodes to add within the bounds of the diagram.
1517
*/
1618
export const addNodesWithinBounds = <N extends BaseNode>(nodes: N[], newNodes: N[]) => {
17-
const maxWidth = Math.max(0, ...nodes.map(n => n.position.x + (n.measured?.width || DEFAULT_NODE_WIDTH)));
18-
const maxHeight = Math.max(0, ...nodes.map(n => n.position.y + (n.measured?.height || DEFAULT_NODE_HEIGHT)));
19-
20-
let x = 0;
21-
let y = maxHeight + DEFAULT_NODE_SPACING;
22-
let rowHeight = 0;
19+
const maxWidth = Math.max(0, ...nodes.map(n => n.position.x + getNodeWidth(n)));
20+
const maxHeight = Math.max(0, ...nodes.map(n => n.position.y + getNodeHeight(n)));
2321

2422
return [
2523
...nodes,
26-
...newNodes.map(n => {
27-
if (!n.measured || !n.measured.height || !n.measured.width) return n;
28-
29-
if (x + n.measured.width + DEFAULT_NODE_SPACING > maxWidth) {
30-
x = 0;
31-
y += rowHeight + DEFAULT_NODE_SPACING;
32-
rowHeight = 0;
33-
}
34-
35-
x += n.measured.width + DEFAULT_NODE_SPACING;
36-
rowHeight = Math.max(rowHeight, n.measured.height);
37-
38-
return { ...n, position: { x, y } };
39-
}),
24+
...newNodes.map(n => ({
25+
...n,
26+
position: getCoordinatesForNewNodeFromMaxDimensions({ maxWidth, maxHeight, newNode: n }),
27+
})),
4028
];
4129
};

src/utilities/convert-nodes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { InternalNode } from '@/types/internal';
22
import { NodeProps, NodeType } from '@/types';
3+
import { getNodeHeight, getNodeWidth } from './node-dimensions';
34

45
export const convertToExternalNode = (node: InternalNode): NodeProps => {
56
const { data, ...rest } = node;
@@ -25,6 +26,10 @@ export const convertToInternalNode = (node: NodeProps): InternalNode => {
2526
fields,
2627
borderVariant,
2728
},
29+
minimapNodeProps: {
30+
height: getNodeHeight(node),
31+
width: getNodeWidth(node),
32+
},
2833
};
2934
};
3035

src/utilities/node-dimensions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const getNodeHeight = <N extends BaseNode | NodeProps | InternalNode>(nod
2424
}
2525
const calculatedHeight =
2626
DEFAULT_NODE_HEADER_HEIGHT + DEFAULT_FIELD_PADDING * 2 + fieldCount * DEFAULT_FIELD_HEIGHT + 2;
27-
return calculatedHeight;
27+
return 10;
2828
};
2929

3030
export const getNodeWidth = <N extends BaseNode>(node: N) => {

0 commit comments

Comments
 (0)