@likec4/diagram 1.27.0 → 1.27.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/index.js +2343 -6880
- package/dist/LikeC4Diagram.js +3 -9
- package/dist/LikeC4Diagram.props.d.ts +5 -3
- package/dist/StaticLikeC4Diagram.js +1 -1
- package/dist/_virtual/set.js +5 -0
- package/dist/_virtual/set2.js +3 -0
- package/dist/base/BaseXYFlow.d.ts +2 -1
- package/dist/base/primitives/compound/CompoundNodeContainer.d.ts +1 -1
- package/dist/base/primitives/compound/CompoundNodeContainer.js +1 -2
- package/dist/base/primitives/edge/EdgeLabel.css.js +1 -4
- package/dist/base/primitives/edge/EdgeLabelContainer.js +1 -1
- package/dist/base/primitives/edge/EdgePath.js +0 -3
- package/dist/base/primitives/edge/edge.css.js +2 -0
- package/dist/base/primitives/element/ElementActionButtons.js +5 -10
- package/dist/base/primitives/element/ElementNodeContainer.css.js +5 -4
- package/dist/base/primitives/element/ElementShape.css.js +1 -12
- package/dist/base/primitives/element/ElementTitle.css.js +3 -2
- package/dist/base/types.js +2 -2
- package/dist/base/updateEdges.js +1 -9
- package/dist/base/updateNodes.js +1 -12
- package/dist/bundle/LikeC4View.js +2 -2
- package/dist/bundle/LikeC4View.props.d.ts +7 -7
- package/dist/components/Link.js +1 -1
- package/dist/hooks/useCurrentViewId.js +8 -2
- package/dist/hooks/useDiagram.d.ts +2659 -1996
- package/dist/hooks/useDiagram.js +16 -8
- package/dist/likec4diagram/DiagramXYFlow.js +10 -9
- package/dist/likec4diagram/custom/edges/RelationshipEdge.js +30 -41
- package/dist/likec4diagram/custom/nodes/toolbar/Toolbar.js +1 -1
- package/dist/likec4diagram/custom/nodes/toolbar/styles.css.js +6 -1
- package/dist/likec4diagram/ui/controls/LayoutDriftNotification.js +13 -5
- package/dist/likec4diagram/ui/notation/NotationPanel.css.js +3 -3
- package/dist/likec4diagram/ui/notation/NotationPanel.js +10 -4
- package/dist/likec4diagram/useLayoutConstraints.d.ts +11 -10
- package/dist/likec4diagram/useLayoutConstraints.js +64 -61
- package/dist/node_modules/.pnpm/mnemonist@0.40.3_patch_hash_5c12abb43fcff6fd81e53021b89075d53249640d165a2556cc1e58d71201a13a/node_modules/mnemonist/set.js +369 -0
- package/dist/overlays/Overlays.js +5 -3
- package/dist/overlays/element-details/actor.d.ts +2769 -173
- package/dist/overlays/overlay/Overlay.css.d.ts +26 -2
- package/dist/overlays/overlay/Overlay.css.js +83 -16
- package/dist/overlays/overlay/Overlay.d.ts +7 -6
- package/dist/overlays/overlay/Overlay.js +39 -32
- package/dist/overlays/overlaysActor.js +2 -2
- package/dist/overlays/relationship-details/RelationshipDetails.js +113 -70
- package/dist/overlays/relationship-details/SelectEdge.d.ts +1 -1
- package/dist/overlays/relationship-details/SelectEdge.js +15 -9
- package/dist/overlays/relationship-details/_types.d.ts +4 -1
- package/dist/overlays/relationship-details/actor.d.ts +415 -128
- package/dist/overlays/relationship-details/actor.js +162 -53
- package/dist/overlays/relationship-details/compute.d.ts +12 -0
- package/dist/overlays/relationship-details/compute.js +139 -0
- package/dist/overlays/relationship-details/custom/ElementActions.js +27 -5
- package/dist/overlays/relationship-details/custom/edgeTypes.js +1 -1
- package/dist/overlays/relationship-details/custom/nodeTypes.d.ts +2 -1
- package/dist/overlays/relationship-details/hooks.d.ts +336 -6
- package/dist/overlays/relationship-details/hooks.js +9 -7
- package/dist/overlays/relationship-details/layout-to-xyflow.d.ts +12 -0
- package/dist/overlays/relationship-details/layout-to-xyflow.js +149 -0
- package/dist/overlays/relationship-details/layout.d.ts +22 -0
- package/dist/overlays/relationship-details/layout.js +314 -0
- package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +3 -1
- package/dist/overlays/relationships-browser/RelationshipsBrowser.js +2 -2
- package/dist/overlays/relationships-browser/SelectElement.js +4 -4
- package/dist/overlays/relationships-browser/_types.d.ts +2 -0
- package/dist/overlays/relationships-browser/actor.d.ts +130 -14
- package/dist/overlays/relationships-browser/actor.js +30 -16
- package/dist/overlays/relationships-browser/custom/ElementActions.js +5 -3
- package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +3 -1
- package/dist/overlays/relationships-browser/custom/edgeTypes.js +1 -1
- package/dist/overlays/relationships-browser/hooks.d.ts +3 -1
- package/dist/overlays/relationships-browser/hooks.js +1 -1
- package/dist/overlays/relationships-browser/{-useRelationshipsView.d.ts → layout.d.ts} +2 -0
- package/dist/overlays/relationships-browser/{-useRelationshipsView.js → layout.js} +19 -2
- package/dist/overlays/relationships-browser/useViewToNodesEdges.d.ts +1 -1
- package/dist/overlays/relationships-browser/useViewToNodesEdges.js +3 -1
- package/dist/packages/core/src/compute-view/relationships-view/compute.js +1 -1
- package/dist/packages/core/src/model/connection/model/ConnectionModel.js +121 -0
- package/dist/packages/core/src/model/connection/model/find.js +39 -0
- package/dist/packages/core/src/model/connection/ops.js +7 -0
- package/dist/packages/core/src/utils/const.js +3 -0
- package/dist/packages/core/src/utils/fqn.js +9 -1
- package/dist/packages/core/src/utils/iterable/find.js +21 -0
- package/dist/packages/core/src/utils/iterable/some.js +21 -0
- package/dist/packages/core/src/utils/set.js +48 -0
- package/dist/packages/core/src/utils/string-hash.js +13 -0
- package/dist/state/DiagramActorProvider.d.ts +4 -2
- package/dist/state/DiagramActorProvider.js +30 -37
- package/dist/state/assign.js +2 -2
- package/dist/state/diagram-machine.d.ts +1731 -1040
- package/dist/state/diagram-machine.js +126 -60
- package/dist/state/syncManualLayoutActor.d.ts +1 -1
- package/dist/styles.css.js +1 -1
- package/package.json +11 -11
- package/src/LikeC4Diagram.props.ts +7 -3
- package/src/LikeC4Diagram.tsx +2 -8
- package/src/StaticLikeC4Diagram.tsx +1 -1
- package/src/base/BaseXYFlow.tsx +2 -1
- package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -3
- package/src/base/primitives/edge/EdgeLabel.css.ts +1 -4
- package/src/base/primitives/edge/EdgeLabelContainer.tsx +1 -1
- package/src/base/primitives/edge/EdgePath.tsx +0 -3
- package/src/base/primitives/edge/edge.css.ts +2 -0
- package/src/base/primitives/element/ElementActionButtons.tsx +6 -7
- package/src/base/primitives/element/ElementNodeContainer.css.ts +5 -4
- package/src/base/primitives/element/ElementShape.css.ts +1 -12
- package/src/base/primitives/element/ElementTitle.css.ts +3 -2
- package/src/base/types.ts +2 -7
- package/src/base/updateEdges.ts +8 -8
- package/src/base/updateNodes.ts +11 -11
- package/src/bundle/LikeC4View.props.ts +7 -7
- package/src/bundle/LikeC4View.tsx +2 -2
- package/src/components/Link.tsx +3 -1
- package/src/hooks/useCurrentViewId.ts +4 -2
- package/src/hooks/useDiagram.ts +18 -9
- package/src/likec4diagram/DiagramXYFlow.tsx +11 -9
- package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +39 -48
- package/src/likec4diagram/custom/nodes/toolbar/Toolbar.tsx +2 -2
- package/src/likec4diagram/custom/nodes/toolbar/styles.css.ts +5 -0
- package/src/likec4diagram/ui/controls/LayoutDriftNotification.tsx +8 -4
- package/src/likec4diagram/ui/notation/NotationPanel.css.ts +2 -2
- package/src/likec4diagram/ui/notation/NotationPanel.tsx +8 -6
- package/src/likec4diagram/useLayoutConstraints.ts +84 -89
- package/src/overlays/Overlays.tsx +5 -3
- package/src/overlays/overlay/Overlay.css.ts +83 -15
- package/src/overlays/overlay/Overlay.tsx +52 -41
- package/src/overlays/overlaysActor.ts +1 -1
- package/src/overlays/relationship-details/RelationshipDetails.tsx +131 -68
- package/src/overlays/relationship-details/SelectEdge.tsx +19 -12
- package/src/overlays/relationship-details/_types.ts +3 -1
- package/src/overlays/relationship-details/actor.ts +217 -68
- package/src/overlays/relationship-details/compute.ts +186 -0
- package/src/overlays/relationship-details/custom/ElementActions.tsx +27 -18
- package/src/overlays/relationship-details/custom/edgeTypes.tsx +1 -1
- package/src/overlays/relationship-details/hooks.ts +10 -8
- package/src/overlays/relationship-details/layout-to-xyflow.ts +195 -0
- package/src/overlays/relationship-details/layout.ts +470 -0
- package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +2 -2
- package/src/overlays/relationships-browser/SelectElement.tsx +5 -5
- package/src/overlays/relationships-browser/_types.ts +2 -0
- package/src/overlays/relationships-browser/actor.ts +39 -14
- package/src/overlays/relationships-browser/custom/ElementActions.tsx +5 -3
- package/src/overlays/relationships-browser/custom/edgeTypes.tsx +1 -1
- package/src/overlays/relationships-browser/hooks.ts +2 -2
- package/src/overlays/relationships-browser/{-useRelationshipsView.ts → layout.ts} +23 -13
- package/src/overlays/relationships-browser/useViewToNodesEdges.ts +3 -1
- package/src/state/DiagramActorProvider.tsx +40 -50
- package/src/state/assign.ts +2 -2
- package/src/state/diagram-machine.ts +151 -75
- package/dist/overlays/relationship-details/useLayoutedDetails.d.ts +0 -19
- package/dist/overlays/relationship-details/useLayoutedDetails.js +0 -267
- package/src/overlays/relationship-details/useLayoutedDetails.ts +0 -376
package/dist/hooks/useDiagram.js
CHANGED
|
@@ -37,17 +37,19 @@ function useDiagram() {
|
|
|
37
37
|
});
|
|
38
38
|
},
|
|
39
39
|
openSource: (params) => {
|
|
40
|
-
actor.send({ type: "open.source", ...params });
|
|
41
|
-
},
|
|
42
|
-
openElementDetails: (fqn, fromNode) => {
|
|
43
40
|
startTransition(() => {
|
|
44
|
-
actor.send({ type: "open.
|
|
41
|
+
actor.send({ type: "open.source", ...params });
|
|
45
42
|
});
|
|
46
43
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
openElementDetails: (fqn, fromNode) => {
|
|
45
|
+
actor.send({ type: "open.elementDetails", fqn, fromNode });
|
|
46
|
+
},
|
|
47
|
+
openRelationshipDetails: (...params) => {
|
|
48
|
+
if (params.length === 1) {
|
|
49
|
+
actor.send({ type: "open.relationshipDetails", params: { edgeId: params[0] } });
|
|
50
|
+
} else {
|
|
51
|
+
actor.send({ type: "open.relationshipDetails", params: { source: params[0], target: params[1] } });
|
|
52
|
+
}
|
|
51
53
|
},
|
|
52
54
|
updateNodeData: (nodeId, data) => {
|
|
53
55
|
actor.send({ type: "update.nodeData", nodeId, data });
|
|
@@ -117,6 +119,12 @@ function useDiagram() {
|
|
|
117
119
|
},
|
|
118
120
|
toggleFeature: (feature, forceValue) => {
|
|
119
121
|
actor.send({ type: "toggle.feature", feature, ...forceValue !== void 0 && { forceValue } });
|
|
122
|
+
},
|
|
123
|
+
highlightNotation: (notation, kind) => {
|
|
124
|
+
actor.send({ type: "notations.highlight", notation, ...kind && { kind } });
|
|
125
|
+
},
|
|
126
|
+
unhighlightNotation: () => {
|
|
127
|
+
actor.send({ type: "notations.unhighlight" });
|
|
120
128
|
}
|
|
121
129
|
}), [actor]);
|
|
122
130
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { nonNullable } from '@likec4/core';
|
|
3
3
|
import { cx } from '@likec4/styles/css';
|
|
4
|
-
import { useTimeout, useCallbackRef } from '@mantine/hooks';
|
|
4
|
+
import { useTimeout, useDebouncedCallback, useCallbackRef } from '@mantine/hooks';
|
|
5
5
|
import { deepEqual, shallowEqual } from 'fast-equals';
|
|
6
6
|
import { memo } from 'react';
|
|
7
7
|
import { BaseXYFlow } from '../base/BaseXYFlow.js';
|
|
8
|
-
import { usePanningAtom } from '../context/ReduceGraphics.js';
|
|
8
|
+
import { useIsReducedGraphics, usePanningAtom } from '../context/ReduceGraphics.js';
|
|
9
9
|
import { useDiagram, useDiagramContext } from '../hooks/useDiagram.js';
|
|
10
10
|
import { edgeTypes, nodeTypes } from './custom/index.js';
|
|
11
11
|
import { DiagramUI } from './DiagramUI.js';
|
|
@@ -13,7 +13,7 @@ import { useLayoutConstraints } from './useLayoutConstraints.js';
|
|
|
13
13
|
import { useDiagramEventHandlers } from '../context/DiagramEventHandlers.js';
|
|
14
14
|
|
|
15
15
|
const selectXYProps = (ctx) => ({
|
|
16
|
-
initialized: ctx.initialized,
|
|
16
|
+
initialized: ctx.initialized.xydata && ctx.initialized.xyflow,
|
|
17
17
|
nodes: ctx.xynodes,
|
|
18
18
|
edges: ctx.xyedges,
|
|
19
19
|
pannable: ctx.pannable,
|
|
@@ -56,9 +56,11 @@ const LikeC4DiagramXYFlow = memo(({
|
|
|
56
56
|
onCanvasClick,
|
|
57
57
|
onCanvasDblClick
|
|
58
58
|
} = useDiagramEventHandlers();
|
|
59
|
-
const notReadOnly = !enableReadOnly, layoutConstraints = useLayoutConstraints(), $isPanning = usePanningAtom(), isPanning = useTimeout(() => {
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const notReadOnly = !enableReadOnly, isReducedGraphics = useIsReducedGraphics(), layoutConstraints = useLayoutConstraints(), $isPanning = usePanningAtom(), isPanning = useTimeout(() => {
|
|
60
|
+
if (!$isPanning.get()) {
|
|
61
|
+
$isPanning.set(true);
|
|
62
|
+
}
|
|
63
|
+
}, isReducedGraphics ? 150 : 350), notPanning = useDebouncedCallback(() => {
|
|
62
64
|
isPanning.clear();
|
|
63
65
|
if ($isPanning.get()) {
|
|
64
66
|
$isPanning.set(false);
|
|
@@ -70,10 +72,9 @@ const LikeC4DiagramXYFlow = memo(({
|
|
|
70
72
|
if (!$isPanning.get()) {
|
|
71
73
|
isPanning.start();
|
|
72
74
|
}
|
|
73
|
-
notPanning
|
|
74
|
-
notPanning.start();
|
|
75
|
+
notPanning();
|
|
75
76
|
}), onMoveEnd = useCallbackRef((event, viewport) => {
|
|
76
|
-
notPanning.
|
|
77
|
+
notPanning.flush();
|
|
77
78
|
diagram.send({ type: "xyflow.viewportMoved", viewport, manually: !!event });
|
|
78
79
|
}), onViewportResize = useCallbackRef(() => {
|
|
79
80
|
diagram.send({ type: "xyflow.resized" });
|
|
@@ -103,7 +103,7 @@ const RelationshipEdge = customEdge((props) => {
|
|
|
103
103
|
if (!labelBBox || labelBBox.x === labelPos.x && labelBBox.y === labelPos.y) {
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
|
-
|
|
106
|
+
diagram.updateEdgeData(id, {
|
|
107
107
|
labelXY: {
|
|
108
108
|
x: labelPos.x,
|
|
109
109
|
y: labelPos.y
|
|
@@ -135,15 +135,13 @@ const RelationshipEdge = customEdge((props) => {
|
|
|
135
135
|
hasMoved = true;
|
|
136
136
|
pointer = clientPoint;
|
|
137
137
|
const { x, y } = xyflow.screenToFlowPosition(pointer, { snapToGrid: false });
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
controlPoints: cp
|
|
146
|
-
};
|
|
138
|
+
const cp = controlPoints.slice();
|
|
139
|
+
cp[index] = {
|
|
140
|
+
x: Math.round(x),
|
|
141
|
+
y: Math.round(y)
|
|
142
|
+
};
|
|
143
|
+
diagram.updateEdgeData(id, {
|
|
144
|
+
controlPoints: cp
|
|
147
145
|
});
|
|
148
146
|
}
|
|
149
147
|
e2.stopPropagation();
|
|
@@ -177,13 +175,9 @@ const RelationshipEdge = customEdge((props) => {
|
|
|
177
175
|
newControlPoints.splice(index, 1);
|
|
178
176
|
e2.stopPropagation();
|
|
179
177
|
setTimeout(() => {
|
|
180
|
-
|
|
178
|
+
diagram.updateEdgeData(id, { controlPoints: newControlPoints });
|
|
181
179
|
diagram.scheduleSaveManualLayout();
|
|
182
180
|
}, 10);
|
|
183
|
-
domNode.removeEventListener("pointerup", onPointerUp, {
|
|
184
|
-
capture: true
|
|
185
|
-
});
|
|
186
|
-
e2.stopPropagation();
|
|
187
181
|
};
|
|
188
182
|
domNode.addEventListener("pointerup", onPointerUp, {
|
|
189
183
|
once: true,
|
|
@@ -213,33 +207,28 @@ const RelationshipEdge = customEdge((props) => {
|
|
|
213
207
|
if (e.button !== 2) {
|
|
214
208
|
return;
|
|
215
209
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const distanceToEdge = Math.abs(fromCurrentToNext.cross(fromCurrentToNew).abs() / fromCurrentToNext.abs());
|
|
233
|
-
if (distanceToEdge < minDistance) {
|
|
234
|
-
minDistance = distanceToEdge;
|
|
235
|
-
insertionIndex = i;
|
|
236
|
-
}
|
|
210
|
+
const points2 = [
|
|
211
|
+
new VectorImpl(sourceX, sourceY),
|
|
212
|
+
...controlPoints.map(vector) || [],
|
|
213
|
+
new VectorImpl(targetX, targetY)
|
|
214
|
+
];
|
|
215
|
+
let pointer = { x: e.clientX, y: e.clientY };
|
|
216
|
+
const newPoint = vector(xyflow.screenToFlowPosition(pointer, { snapToGrid: false }));
|
|
217
|
+
let insertionIndex = 0;
|
|
218
|
+
let minDistance = Infinity;
|
|
219
|
+
for (let i = 0; i < points2.length - 1; i++) {
|
|
220
|
+
const a = points2[i], b = points2[i + 1], fromCurrentToNext = b.sub(a), fromCurrentToNew = newPoint.sub(a), fromNextToNew = newPoint.sub(b);
|
|
221
|
+
if (fromCurrentToNext.dot(fromCurrentToNew) * fromCurrentToNext.dot(fromNextToNew) < 0) {
|
|
222
|
+
const distanceToEdge = Math.abs(fromCurrentToNext.cross(fromCurrentToNew).abs() / fromCurrentToNext.abs());
|
|
223
|
+
if (distanceToEdge < minDistance) {
|
|
224
|
+
minDistance = distanceToEdge;
|
|
225
|
+
insertionIndex = i;
|
|
237
226
|
}
|
|
238
227
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
});
|
|
228
|
+
}
|
|
229
|
+
const newControlPoints = controlPoints.slice() || [];
|
|
230
|
+
newControlPoints.splice(insertionIndex, 0, newPoint);
|
|
231
|
+
diagram.updateEdgeData(id, { controlPoints: newControlPoints });
|
|
243
232
|
diagram.scheduleSaveManualLayout();
|
|
244
233
|
e.stopPropagation();
|
|
245
234
|
};
|
|
@@ -290,7 +279,7 @@ const RelationshipEdge = customEdge((props) => {
|
|
|
290
279
|
}
|
|
291
280
|
)
|
|
292
281
|
] }),
|
|
293
|
-
enableEdgeEditing && controlPoints.length > 0 && /* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsx(
|
|
282
|
+
enableEdgeEditing && controlPoints.length > 0 && (selected || hovered || isControlPointDragging) && /* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsx(
|
|
294
283
|
EdgeContainer,
|
|
295
284
|
{
|
|
296
285
|
component: "svg",
|
|
@@ -57,7 +57,7 @@ function Toolbar({ title, children, nodeProps, ...props }) {
|
|
|
57
57
|
onDoubleClick: stopPropagation,
|
|
58
58
|
withBorder: true,
|
|
59
59
|
children: /* @__PURE__ */ jsxs(Stack, { gap: "6px", children: [
|
|
60
|
-
/* @__PURE__ */ jsx(Box, { px: "4px", children: /* @__PURE__ */ jsx(Text, {
|
|
60
|
+
/* @__PURE__ */ jsx(Box, { px: "4px", children: /* @__PURE__ */ jsx(Text, { className: toolbarTitle, children: title }) }),
|
|
61
61
|
/* @__PURE__ */ jsx(Group, { gap: 4, children })
|
|
62
62
|
] })
|
|
63
63
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { css } from '@likec4/styles/css';
|
|
2
2
|
|
|
3
3
|
const toolbarTitle = css({
|
|
4
|
+
color: "mantine.colors.dimmed",
|
|
4
5
|
fontSize: "10px",
|
|
5
6
|
fontWeight: 600,
|
|
7
|
+
maxWidth: "220px",
|
|
6
8
|
cursor: "default",
|
|
7
|
-
userSelect: "all"
|
|
9
|
+
userSelect: "all",
|
|
10
|
+
textOverflow: "ellipsis",
|
|
11
|
+
overflow: "hidden",
|
|
12
|
+
whiteSpace: "nowrap"
|
|
8
13
|
});
|
|
9
14
|
|
|
10
15
|
export { toolbarTitle };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { HoverCard, HoverCardTarget,
|
|
2
|
+
import { HoverCard, HoverCardTarget, HoverCardDropdown, Notification, Text, Code } from '@mantine/core';
|
|
3
3
|
import { IconAlertTriangle } from '@tabler/icons-react';
|
|
4
|
+
import { css } from '@likec4/styles/css';
|
|
4
5
|
import { ActionIcon } from './_shared.js';
|
|
5
6
|
|
|
6
7
|
const LayoutDriftNotification = (props) => /* @__PURE__ */ jsxs(
|
|
@@ -11,10 +12,17 @@ const LayoutDriftNotification = (props) => /* @__PURE__ */ jsxs(
|
|
|
11
12
|
closeDelay: 100,
|
|
12
13
|
...props,
|
|
13
14
|
children: [
|
|
14
|
-
/* @__PURE__ */ jsx(HoverCardTarget, { children: /* @__PURE__ */
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/* @__PURE__ */ jsx(HoverCardTarget, { children: /* @__PURE__ */ jsx(
|
|
16
|
+
ActionIcon,
|
|
17
|
+
{
|
|
18
|
+
color: "orange",
|
|
19
|
+
c: "orange",
|
|
20
|
+
className: css({
|
|
21
|
+
bg: "mantine.colors.orange.light"
|
|
22
|
+
}),
|
|
23
|
+
children: /* @__PURE__ */ jsx(IconAlertTriangle, {})
|
|
24
|
+
}
|
|
25
|
+
) }),
|
|
18
26
|
/* @__PURE__ */ jsx(HoverCardDropdown, { p: "0", children: /* @__PURE__ */ jsxs(
|
|
19
27
|
Notification,
|
|
20
28
|
{
|
|
@@ -77,9 +77,9 @@ const shapeBadge = css({
|
|
|
77
77
|
["--badge-fz"]: "9.5px",
|
|
78
78
|
["--badge-padding-x"]: "3px",
|
|
79
79
|
["--badge-height"]: "13.5px",
|
|
80
|
-
["--badge-lh"]: "1"
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
["--badge-lh"]: "1",
|
|
81
|
+
["--badge-bg"]: "{colors.likec4.palette.fill}",
|
|
82
|
+
["--badge-color"]: "{colors.likec4.palette.hiContrast}"
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
export { card, container, elementNotation, icon, shapeBadge, tabPanel };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import '@likec4/core';
|
|
3
3
|
import { cx, css } from '@likec4/styles/css';
|
|
4
|
-
import
|
|
4
|
+
import '@likec4/styles/tokens';
|
|
5
5
|
import { Tooltip, ThemeIcon, ActionIcon, Paper, Tabs, TabsList, TabsTab, TabsPanel, ScrollAreaAutosize, Stack, Card, Group, Box, Badge, Text } from '@mantine/core';
|
|
6
6
|
import { useLocalStorage } from '@mantine/hooks';
|
|
7
7
|
import { IconAlertTriangle, IconHelpCircle, IconArrowDownRight } from '@tabler/icons-react';
|
|
@@ -22,7 +22,7 @@ const ElementNotation = ({ value }) => {
|
|
|
22
22
|
shape = "rectangle"
|
|
23
23
|
} = value;
|
|
24
24
|
const [onlyKind, setOnlyKind] = useState(null);
|
|
25
|
-
useDiagram();
|
|
25
|
+
const diagram = useDiagram();
|
|
26
26
|
const w = 300;
|
|
27
27
|
const h = 200;
|
|
28
28
|
return /* @__PURE__ */ jsx(
|
|
@@ -37,11 +37,14 @@ const ElementNotation = ({ value }) => {
|
|
|
37
37
|
likec4Palette: color
|
|
38
38
|
})
|
|
39
39
|
),
|
|
40
|
+
"data-likec4-color": color,
|
|
40
41
|
onMouseEnter: () => {
|
|
41
42
|
setOnlyKind(null);
|
|
43
|
+
diagram.highlightNotation(value);
|
|
42
44
|
},
|
|
43
45
|
onMouseLeave: () => {
|
|
44
46
|
setOnlyKind(null);
|
|
47
|
+
diagram.unhighlightNotation();
|
|
45
48
|
},
|
|
46
49
|
children: /* @__PURE__ */ jsxs(
|
|
47
50
|
Group,
|
|
@@ -75,15 +78,18 @@ const ElementNotation = ({ value }) => {
|
|
|
75
78
|
/* @__PURE__ */ jsx(Group, { gap: 4, flex: "0 0 auto", children: value.kinds.map((kind) => /* @__PURE__ */ jsx(
|
|
76
79
|
Badge,
|
|
77
80
|
{
|
|
78
|
-
className:
|
|
81
|
+
className: cx(
|
|
82
|
+
shapeBadge
|
|
83
|
+
),
|
|
79
84
|
onMouseEnter: () => {
|
|
80
85
|
setOnlyKind(kind);
|
|
86
|
+
diagram.highlightNotation(value, kind);
|
|
81
87
|
},
|
|
82
88
|
onMouseLeave: () => {
|
|
83
89
|
setOnlyKind(null);
|
|
90
|
+
diagram.highlightNotation(value);
|
|
84
91
|
},
|
|
85
92
|
opacity: isNonNullish(onlyKind) && onlyKind !== kind ? 0.25 : 1,
|
|
86
|
-
color: isNonNullish(onlyKind) && onlyKind !== kind ? "gray" : token("colors.likec4.palette.fill"),
|
|
87
93
|
children: kind
|
|
88
94
|
},
|
|
89
95
|
kind
|
|
@@ -20,21 +20,22 @@ declare abstract class Rect {
|
|
|
20
20
|
height: number;
|
|
21
21
|
};
|
|
22
22
|
get position(): XYPosition;
|
|
23
|
-
protected abstract parent:
|
|
24
|
-
constructor(xynode: InternalNode, parent?:
|
|
23
|
+
protected abstract parent: CompoundRect | null;
|
|
24
|
+
constructor(xynode: InternalNode, parent?: CompoundRect | null);
|
|
25
25
|
}
|
|
26
|
-
declare class
|
|
27
|
-
readonly parent:
|
|
26
|
+
declare class CompoundRect extends Rect {
|
|
27
|
+
readonly parent: CompoundRect | null;
|
|
28
28
|
readonly children: Rect[];
|
|
29
|
-
constructor(xynode: InternalNode, parent?:
|
|
29
|
+
constructor(xynode: InternalNode, parent?: CompoundRect | null);
|
|
30
|
+
}
|
|
31
|
+
declare class Leaf extends Rect {
|
|
32
|
+
readonly parent: CompoundRect | null;
|
|
33
|
+
constructor(xynode: InternalNode, parent?: CompoundRect | null);
|
|
30
34
|
}
|
|
31
|
-
type NodePositionUpdater = (nodes: Array<{
|
|
32
|
-
rect: Rect | Compound;
|
|
33
|
-
node: InternalNode;
|
|
34
|
-
}>) => void;
|
|
35
35
|
export declare function createLayoutConstraints(xyflowApi: XYStoreApi, editingNodeIds: NonEmptyArray<string>): {
|
|
36
|
+
rects: ReadonlyMap<string, Leaf | CompoundRect>;
|
|
36
37
|
updateXYFlowNodes: () => void;
|
|
37
|
-
onMove: (
|
|
38
|
+
onMove: () => void;
|
|
38
39
|
};
|
|
39
40
|
type LayoutConstraints = {
|
|
40
41
|
onNodeDragStart: OnNodeDrag<Types.Node>;
|
|
@@ -2,7 +2,7 @@ import { nonNullable } from '@likec4/core';
|
|
|
2
2
|
import '@xyflow/react';
|
|
3
3
|
import { getNodeDimensions } from '@xyflow/system';
|
|
4
4
|
import { useRef, useMemo } from 'react';
|
|
5
|
-
import { pipe, filter,
|
|
5
|
+
import { pipe, filter, hasAtLeast, map } from 'remeda';
|
|
6
6
|
import { useDiagram } from '../hooks/useDiagram.js';
|
|
7
7
|
import { useXYStoreApi } from '../hooks/useXYFlow.js';
|
|
8
8
|
|
|
@@ -62,7 +62,7 @@ class Rect {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
class
|
|
65
|
+
class CompoundRect extends Rect {
|
|
66
66
|
constructor(xynode, parent = null) {
|
|
67
67
|
super(xynode, parent);
|
|
68
68
|
this.parent = parent;
|
|
@@ -92,51 +92,51 @@ function createLayoutConstraints(xyflowApi, editingNodeIds) {
|
|
|
92
92
|
const ancestorsOfDraggingNodes = new Set(
|
|
93
93
|
editingNodeIds.flatMap(ancestorsOf)
|
|
94
94
|
);
|
|
95
|
-
const traverse =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
traverse.push({
|
|
99
|
-
xynode,
|
|
100
|
-
parent: null
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
95
|
+
const traverse = [...nodeLookup.values()].flatMap(
|
|
96
|
+
(x) => !x.parentId ? { xynode: x, parent: null } : []
|
|
97
|
+
);
|
|
104
98
|
while (traverse.length > 0) {
|
|
105
99
|
const { xynode, parent } = traverse.shift();
|
|
106
100
|
const isEditing = editingNodeIds.includes(xynode.id);
|
|
107
|
-
const shouldTraverse = !isEditing &&
|
|
108
|
-
const rect = shouldTraverse ? new Compound(xynode, parent) : new Leaf(xynode, parent);
|
|
109
|
-
rects.set(xynode.id, rect);
|
|
101
|
+
const shouldTraverse = !isEditing && xynode.type !== "element" && xynode.type !== "deployment" && ancestorsOfDraggingNodes.has(xynode.id);
|
|
110
102
|
if (shouldTraverse) {
|
|
103
|
+
const rect = new CompoundRect(xynode, parent);
|
|
104
|
+
rects.set(xynode.id, rect);
|
|
111
105
|
parentLookup.get(xynode.id)?.forEach((child) => {
|
|
112
106
|
traverse.push({
|
|
113
107
|
xynode: child,
|
|
114
108
|
parent: rect
|
|
115
109
|
});
|
|
116
110
|
});
|
|
111
|
+
} else {
|
|
112
|
+
rects.set(xynode.id, new Leaf(xynode, parent));
|
|
117
113
|
}
|
|
118
114
|
}
|
|
119
115
|
const rectsToUpdate = [...rects.values()];
|
|
120
116
|
applyConstraints(rectsToUpdate);
|
|
121
117
|
function applyConstraints(targets) {
|
|
122
|
-
|
|
118
|
+
for (const r of targets) {
|
|
119
|
+
if (!(r instanceof CompoundRect)) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
123
122
|
applyConstraints(r.children);
|
|
124
|
-
const childrenBB =
|
|
125
|
-
minX: Math.min(acc.minX, r2.minX),
|
|
126
|
-
minY: Math.min(acc.minY, r2.minY),
|
|
127
|
-
maxX: Math.max(acc.maxX, r2.maxX),
|
|
128
|
-
maxY: Math.max(acc.maxY, r2.maxY)
|
|
129
|
-
}), {
|
|
123
|
+
const childrenBB = {
|
|
130
124
|
minX: Infinity,
|
|
131
125
|
minY: Infinity,
|
|
132
126
|
maxX: -Infinity,
|
|
133
127
|
maxY: -Infinity
|
|
134
|
-
}
|
|
128
|
+
};
|
|
129
|
+
for (const child of r.children) {
|
|
130
|
+
childrenBB.minX = Math.min(childrenBB.minX, child.minX);
|
|
131
|
+
childrenBB.minY = Math.min(childrenBB.minY, child.minY);
|
|
132
|
+
childrenBB.maxX = Math.max(childrenBB.maxX, child.maxX);
|
|
133
|
+
childrenBB.maxY = Math.max(childrenBB.maxY, child.maxY);
|
|
134
|
+
}
|
|
135
135
|
r.minX = childrenBB.minX - Rect.LeftPadding;
|
|
136
136
|
r.minY = childrenBB.minY - Rect.TopPadding;
|
|
137
137
|
r.maxX = childrenBB.maxX + Rect.RightPadding;
|
|
138
138
|
r.maxY = childrenBB.maxY + Rect.BottomPadding;
|
|
139
|
-
}
|
|
139
|
+
}
|
|
140
140
|
}
|
|
141
141
|
function updateXYFlowNodes() {
|
|
142
142
|
applyConstraints(rectsToUpdate);
|
|
@@ -149,7 +149,7 @@ function createLayoutConstraints(xyflowApi, editingNodeIds) {
|
|
|
149
149
|
position: r.position,
|
|
150
150
|
positionAbsolute: r.positionAbsolute
|
|
151
151
|
});
|
|
152
|
-
if (r instanceof
|
|
152
|
+
if (r instanceof CompoundRect) {
|
|
153
153
|
acc.push({
|
|
154
154
|
id: r.id,
|
|
155
155
|
type: "dimensions",
|
|
@@ -162,26 +162,23 @@ function createLayoutConstraints(xyflowApi, editingNodeIds) {
|
|
|
162
162
|
);
|
|
163
163
|
}
|
|
164
164
|
let animationFrameId = null;
|
|
165
|
-
function onMove(
|
|
165
|
+
function onMove() {
|
|
166
166
|
if (rectsToUpdate.length === 0) {
|
|
167
167
|
return;
|
|
168
168
|
}
|
|
169
169
|
animationFrameId ??= requestAnimationFrame(() => {
|
|
170
170
|
animationFrameId = null;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
node: nonNullable(nodeLookup.get(id))
|
|
178
|
-
}))
|
|
179
|
-
)
|
|
180
|
-
);
|
|
171
|
+
const { nodeLookup: nodeLookup2 } = xyflowApi.getState();
|
|
172
|
+
for (const id of editingNodeIds) {
|
|
173
|
+
const rect = nonNullable(rects.get(id));
|
|
174
|
+
const node = nonNullable(nodeLookup2.get(id));
|
|
175
|
+
rect.positionAbsolute = node.internals.positionAbsolute;
|
|
176
|
+
}
|
|
181
177
|
updateXYFlowNodes();
|
|
182
178
|
});
|
|
183
179
|
}
|
|
184
180
|
return {
|
|
181
|
+
rects,
|
|
185
182
|
updateXYFlowNodes,
|
|
186
183
|
onMove
|
|
187
184
|
};
|
|
@@ -190,33 +187,39 @@ function useLayoutConstraints() {
|
|
|
190
187
|
const xystore = useXYStoreApi();
|
|
191
188
|
const diagram = useDiagram();
|
|
192
189
|
const solverRef = useRef(void 0);
|
|
193
|
-
return useMemo(() =>
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
190
|
+
return useMemo(() => {
|
|
191
|
+
let wasPending = false;
|
|
192
|
+
const initial = { x: 0, y: 0 };
|
|
193
|
+
let moved = false;
|
|
194
|
+
return {
|
|
195
|
+
onNodeDragStart: (_event, xynode) => {
|
|
196
|
+
wasPending = diagram.cancelSaveManualLayout();
|
|
197
|
+
const { nodeLookup } = xystore.getState();
|
|
198
|
+
const draggingNodes = pipe(
|
|
199
|
+
Array.from(nodeLookup.values()),
|
|
200
|
+
filter((n) => n.draggable !== false && (n.dragging === true || n.id === xynode.id || n.selected === true))
|
|
201
|
+
);
|
|
202
|
+
if (hasAtLeast(draggingNodes, 1)) {
|
|
203
|
+
solverRef.current = createLayoutConstraints(xystore, map(draggingNodes, (x) => x.id));
|
|
204
|
+
}
|
|
205
|
+
initial.x = _event.clientX;
|
|
206
|
+
initial.y = _event.clientY;
|
|
207
|
+
moved = false;
|
|
208
|
+
},
|
|
209
|
+
onNodeDrag: (_event) => {
|
|
210
|
+
moved = Math.abs(_event.clientX - initial.x) > 4 || Math.abs(_event.clientY - initial.y) > 4;
|
|
211
|
+
solverRef.current?.onMove();
|
|
212
|
+
},
|
|
213
|
+
onNodeDragStop: (_event) => {
|
|
214
|
+
moved = Math.abs(_event.clientX - initial.x) > 4 || Math.abs(_event.clientY - initial.y) > 4;
|
|
215
|
+
console.log("moved", moved);
|
|
216
|
+
if (wasPending || moved) {
|
|
217
|
+
diagram.scheduleSaveManualLayout();
|
|
218
|
+
}
|
|
219
|
+
solverRef.current = void 0;
|
|
205
220
|
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
solverRef.current?.onMove((nodes) => {
|
|
209
|
-
nodes.forEach(({ rect, node }) => {
|
|
210
|
-
rect.positionAbsolute = node.internals.positionAbsolute;
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
},
|
|
214
|
-
onNodeDragStop: () => {
|
|
215
|
-
solverRef.current?.updateXYFlowNodes();
|
|
216
|
-
diagram.scheduleSaveManualLayout();
|
|
217
|
-
solverRef.current = void 0;
|
|
218
|
-
}
|
|
219
|
-
}), [xystore, diagram]);
|
|
221
|
+
};
|
|
222
|
+
}, [xystore, diagram]);
|
|
220
223
|
}
|
|
221
224
|
|
|
222
225
|
export { createLayoutConstraints, useLayoutConstraints };
|