@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.
Files changed (151) hide show
  1. package/bundle/index.js +2343 -6880
  2. package/dist/LikeC4Diagram.js +3 -9
  3. package/dist/LikeC4Diagram.props.d.ts +5 -3
  4. package/dist/StaticLikeC4Diagram.js +1 -1
  5. package/dist/_virtual/set.js +5 -0
  6. package/dist/_virtual/set2.js +3 -0
  7. package/dist/base/BaseXYFlow.d.ts +2 -1
  8. package/dist/base/primitives/compound/CompoundNodeContainer.d.ts +1 -1
  9. package/dist/base/primitives/compound/CompoundNodeContainer.js +1 -2
  10. package/dist/base/primitives/edge/EdgeLabel.css.js +1 -4
  11. package/dist/base/primitives/edge/EdgeLabelContainer.js +1 -1
  12. package/dist/base/primitives/edge/EdgePath.js +0 -3
  13. package/dist/base/primitives/edge/edge.css.js +2 -0
  14. package/dist/base/primitives/element/ElementActionButtons.js +5 -10
  15. package/dist/base/primitives/element/ElementNodeContainer.css.js +5 -4
  16. package/dist/base/primitives/element/ElementShape.css.js +1 -12
  17. package/dist/base/primitives/element/ElementTitle.css.js +3 -2
  18. package/dist/base/types.js +2 -2
  19. package/dist/base/updateEdges.js +1 -9
  20. package/dist/base/updateNodes.js +1 -12
  21. package/dist/bundle/LikeC4View.js +2 -2
  22. package/dist/bundle/LikeC4View.props.d.ts +7 -7
  23. package/dist/components/Link.js +1 -1
  24. package/dist/hooks/useCurrentViewId.js +8 -2
  25. package/dist/hooks/useDiagram.d.ts +2659 -1996
  26. package/dist/hooks/useDiagram.js +16 -8
  27. package/dist/likec4diagram/DiagramXYFlow.js +10 -9
  28. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +30 -41
  29. package/dist/likec4diagram/custom/nodes/toolbar/Toolbar.js +1 -1
  30. package/dist/likec4diagram/custom/nodes/toolbar/styles.css.js +6 -1
  31. package/dist/likec4diagram/ui/controls/LayoutDriftNotification.js +13 -5
  32. package/dist/likec4diagram/ui/notation/NotationPanel.css.js +3 -3
  33. package/dist/likec4diagram/ui/notation/NotationPanel.js +10 -4
  34. package/dist/likec4diagram/useLayoutConstraints.d.ts +11 -10
  35. package/dist/likec4diagram/useLayoutConstraints.js +64 -61
  36. package/dist/node_modules/.pnpm/mnemonist@0.40.3_patch_hash_5c12abb43fcff6fd81e53021b89075d53249640d165a2556cc1e58d71201a13a/node_modules/mnemonist/set.js +369 -0
  37. package/dist/overlays/Overlays.js +5 -3
  38. package/dist/overlays/element-details/actor.d.ts +2769 -173
  39. package/dist/overlays/overlay/Overlay.css.d.ts +26 -2
  40. package/dist/overlays/overlay/Overlay.css.js +83 -16
  41. package/dist/overlays/overlay/Overlay.d.ts +7 -6
  42. package/dist/overlays/overlay/Overlay.js +39 -32
  43. package/dist/overlays/overlaysActor.js +2 -2
  44. package/dist/overlays/relationship-details/RelationshipDetails.js +113 -70
  45. package/dist/overlays/relationship-details/SelectEdge.d.ts +1 -1
  46. package/dist/overlays/relationship-details/SelectEdge.js +15 -9
  47. package/dist/overlays/relationship-details/_types.d.ts +4 -1
  48. package/dist/overlays/relationship-details/actor.d.ts +415 -128
  49. package/dist/overlays/relationship-details/actor.js +162 -53
  50. package/dist/overlays/relationship-details/compute.d.ts +12 -0
  51. package/dist/overlays/relationship-details/compute.js +139 -0
  52. package/dist/overlays/relationship-details/custom/ElementActions.js +27 -5
  53. package/dist/overlays/relationship-details/custom/edgeTypes.js +1 -1
  54. package/dist/overlays/relationship-details/custom/nodeTypes.d.ts +2 -1
  55. package/dist/overlays/relationship-details/hooks.d.ts +336 -6
  56. package/dist/overlays/relationship-details/hooks.js +9 -7
  57. package/dist/overlays/relationship-details/layout-to-xyflow.d.ts +12 -0
  58. package/dist/overlays/relationship-details/layout-to-xyflow.js +149 -0
  59. package/dist/overlays/relationship-details/layout.d.ts +22 -0
  60. package/dist/overlays/relationship-details/layout.js +314 -0
  61. package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +3 -1
  62. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +2 -2
  63. package/dist/overlays/relationships-browser/SelectElement.js +4 -4
  64. package/dist/overlays/relationships-browser/_types.d.ts +2 -0
  65. package/dist/overlays/relationships-browser/actor.d.ts +130 -14
  66. package/dist/overlays/relationships-browser/actor.js +30 -16
  67. package/dist/overlays/relationships-browser/custom/ElementActions.js +5 -3
  68. package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +3 -1
  69. package/dist/overlays/relationships-browser/custom/edgeTypes.js +1 -1
  70. package/dist/overlays/relationships-browser/hooks.d.ts +3 -1
  71. package/dist/overlays/relationships-browser/hooks.js +1 -1
  72. package/dist/overlays/relationships-browser/{-useRelationshipsView.d.ts → layout.d.ts} +2 -0
  73. package/dist/overlays/relationships-browser/{-useRelationshipsView.js → layout.js} +19 -2
  74. package/dist/overlays/relationships-browser/useViewToNodesEdges.d.ts +1 -1
  75. package/dist/overlays/relationships-browser/useViewToNodesEdges.js +3 -1
  76. package/dist/packages/core/src/compute-view/relationships-view/compute.js +1 -1
  77. package/dist/packages/core/src/model/connection/model/ConnectionModel.js +121 -0
  78. package/dist/packages/core/src/model/connection/model/find.js +39 -0
  79. package/dist/packages/core/src/model/connection/ops.js +7 -0
  80. package/dist/packages/core/src/utils/const.js +3 -0
  81. package/dist/packages/core/src/utils/fqn.js +9 -1
  82. package/dist/packages/core/src/utils/iterable/find.js +21 -0
  83. package/dist/packages/core/src/utils/iterable/some.js +21 -0
  84. package/dist/packages/core/src/utils/set.js +48 -0
  85. package/dist/packages/core/src/utils/string-hash.js +13 -0
  86. package/dist/state/DiagramActorProvider.d.ts +4 -2
  87. package/dist/state/DiagramActorProvider.js +30 -37
  88. package/dist/state/assign.js +2 -2
  89. package/dist/state/diagram-machine.d.ts +1731 -1040
  90. package/dist/state/diagram-machine.js +126 -60
  91. package/dist/state/syncManualLayoutActor.d.ts +1 -1
  92. package/dist/styles.css.js +1 -1
  93. package/package.json +11 -11
  94. package/src/LikeC4Diagram.props.ts +7 -3
  95. package/src/LikeC4Diagram.tsx +2 -8
  96. package/src/StaticLikeC4Diagram.tsx +1 -1
  97. package/src/base/BaseXYFlow.tsx +2 -1
  98. package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -3
  99. package/src/base/primitives/edge/EdgeLabel.css.ts +1 -4
  100. package/src/base/primitives/edge/EdgeLabelContainer.tsx +1 -1
  101. package/src/base/primitives/edge/EdgePath.tsx +0 -3
  102. package/src/base/primitives/edge/edge.css.ts +2 -0
  103. package/src/base/primitives/element/ElementActionButtons.tsx +6 -7
  104. package/src/base/primitives/element/ElementNodeContainer.css.ts +5 -4
  105. package/src/base/primitives/element/ElementShape.css.ts +1 -12
  106. package/src/base/primitives/element/ElementTitle.css.ts +3 -2
  107. package/src/base/types.ts +2 -7
  108. package/src/base/updateEdges.ts +8 -8
  109. package/src/base/updateNodes.ts +11 -11
  110. package/src/bundle/LikeC4View.props.ts +7 -7
  111. package/src/bundle/LikeC4View.tsx +2 -2
  112. package/src/components/Link.tsx +3 -1
  113. package/src/hooks/useCurrentViewId.ts +4 -2
  114. package/src/hooks/useDiagram.ts +18 -9
  115. package/src/likec4diagram/DiagramXYFlow.tsx +11 -9
  116. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +39 -48
  117. package/src/likec4diagram/custom/nodes/toolbar/Toolbar.tsx +2 -2
  118. package/src/likec4diagram/custom/nodes/toolbar/styles.css.ts +5 -0
  119. package/src/likec4diagram/ui/controls/LayoutDriftNotification.tsx +8 -4
  120. package/src/likec4diagram/ui/notation/NotationPanel.css.ts +2 -2
  121. package/src/likec4diagram/ui/notation/NotationPanel.tsx +8 -6
  122. package/src/likec4diagram/useLayoutConstraints.ts +84 -89
  123. package/src/overlays/Overlays.tsx +5 -3
  124. package/src/overlays/overlay/Overlay.css.ts +83 -15
  125. package/src/overlays/overlay/Overlay.tsx +52 -41
  126. package/src/overlays/overlaysActor.ts +1 -1
  127. package/src/overlays/relationship-details/RelationshipDetails.tsx +131 -68
  128. package/src/overlays/relationship-details/SelectEdge.tsx +19 -12
  129. package/src/overlays/relationship-details/_types.ts +3 -1
  130. package/src/overlays/relationship-details/actor.ts +217 -68
  131. package/src/overlays/relationship-details/compute.ts +186 -0
  132. package/src/overlays/relationship-details/custom/ElementActions.tsx +27 -18
  133. package/src/overlays/relationship-details/custom/edgeTypes.tsx +1 -1
  134. package/src/overlays/relationship-details/hooks.ts +10 -8
  135. package/src/overlays/relationship-details/layout-to-xyflow.ts +195 -0
  136. package/src/overlays/relationship-details/layout.ts +470 -0
  137. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +2 -2
  138. package/src/overlays/relationships-browser/SelectElement.tsx +5 -5
  139. package/src/overlays/relationships-browser/_types.ts +2 -0
  140. package/src/overlays/relationships-browser/actor.ts +39 -14
  141. package/src/overlays/relationships-browser/custom/ElementActions.tsx +5 -3
  142. package/src/overlays/relationships-browser/custom/edgeTypes.tsx +1 -1
  143. package/src/overlays/relationships-browser/hooks.ts +2 -2
  144. package/src/overlays/relationships-browser/{-useRelationshipsView.ts → layout.ts} +23 -13
  145. package/src/overlays/relationships-browser/useViewToNodesEdges.ts +3 -1
  146. package/src/state/DiagramActorProvider.tsx +40 -50
  147. package/src/state/assign.ts +2 -2
  148. package/src/state/diagram-machine.ts +151 -75
  149. package/dist/overlays/relationship-details/useLayoutedDetails.d.ts +0 -19
  150. package/dist/overlays/relationship-details/useLayoutedDetails.js +0 -267
  151. package/src/overlays/relationship-details/useLayoutedDetails.ts +0 -376
@@ -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.elementDetails", fqn, fromNode });
41
+ actor.send({ type: "open.source", ...params });
45
42
  });
46
43
  },
47
- openRelationshipDetails: (edgeId) => {
48
- startTransition(() => {
49
- actor.send({ type: "open.relationshipDetails", edgeId });
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
- $isPanning.set(true);
61
- }, 160), notPanning = useTimeout(() => {
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.clear();
74
- notPanning.start();
75
+ notPanning();
75
76
  }), onMoveEnd = useCallbackRef((event, viewport) => {
76
- notPanning.start();
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
- xyflow.updateEdgeData(id, {
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
- xyflow.updateEdgeData(id, (xyedge) => {
139
- const cp = (xyedge.data.controlPoints ?? controlPoints).slice();
140
- cp[index] = {
141
- x: Math.round(x),
142
- y: Math.round(y)
143
- };
144
- return {
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
- xyflow.updateEdgeData(id, { controlPoints: newControlPoints });
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
- xyflow.updateEdgeData(id, (edge) => {
217
- const points2 = [
218
- new VectorImpl(sourceX, sourceY),
219
- ...controlPoints.map(vector) || [],
220
- new VectorImpl(targetX, targetY)
221
- ];
222
- let pointer = { x: e.clientX, y: e.clientY };
223
- const newPoint = vector(xyflow.screenToFlowPosition(pointer, { snapToGrid: false }));
224
- let insertionIndex = 0;
225
- let minDistance = Infinity;
226
- for (let i = 0; i < points2.length - 1; i++) {
227
- const a = points2[i], b = points2[i + 1];
228
- const fromCurrentToNext = b.sub(a);
229
- const fromCurrentToNew = newPoint.sub(a);
230
- const fromNextToNew = newPoint.sub(b);
231
- if (fromCurrentToNext.dot(fromCurrentToNew) * fromCurrentToNext.dot(fromNextToNew) < 0) {
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
- const newControlPoints = edge.data.controlPoints?.slice() || [];
240
- newControlPoints.splice(insertionIndex, 0, newPoint);
241
- return { controlPoints: newControlPoints };
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, { c: "dimmed", className: toolbarTitle, children: title }) }),
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, Overlay, HoverCardDropdown, Notification, Text, Code } from '@mantine/core';
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__ */ jsxs(ActionIcon, { c: "orange", children: [
15
- /* @__PURE__ */ jsx(Overlay, { color: "orange.lightHover", backgroundOpacity: 1 }),
16
- /* @__PURE__ */ jsx(IconAlertTriangle, {})
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
- // ['--badge-bg']: vars.element.fill,
82
- // ['--badge-color']: vars.element.hiContrast
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 { token } from '@likec4/styles/tokens';
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: shapeBadge,
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: Compound | null;
24
- constructor(xynode: InternalNode, parent?: Compound | null);
23
+ protected abstract parent: CompoundRect | null;
24
+ constructor(xynode: InternalNode, parent?: CompoundRect | null);
25
25
  }
26
- declare class Compound extends Rect {
27
- readonly parent: Compound | null;
26
+ declare class CompoundRect extends Rect {
27
+ readonly parent: CompoundRect | null;
28
28
  readonly children: Rect[];
29
- constructor(xynode: InternalNode, parent?: Compound | null);
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: (updater: NodePositionUpdater) => void;
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, map, hasAtLeast, isNullish } from 'remeda';
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 Compound extends Rect {
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 = new Array();
96
- for (const [, xynode] of nodeLookup) {
97
- if (isNullish(xynode.parentId)) {
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 && (xynode.type === "compound-element" || xynode.type === "compound-deployment" || xynode.type === "view-group") && ancestorsOfDraggingNodes.has(xynode.id);
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
- targets.filter((x) => x instanceof Compound).forEach((r) => {
118
+ for (const r of targets) {
119
+ if (!(r instanceof CompoundRect)) {
120
+ continue;
121
+ }
123
122
  applyConstraints(r.children);
124
- const childrenBB = r.children.reduce((acc, r2) => ({
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 Compound) {
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(updater) {
165
+ function onMove() {
166
166
  if (rectsToUpdate.length === 0) {
167
167
  return;
168
168
  }
169
169
  animationFrameId ??= requestAnimationFrame(() => {
170
170
  animationFrameId = null;
171
- updater(
172
- pipe(
173
- editingNodeIds,
174
- filter((id) => rects.has(id) && nodeLookup.has(id)),
175
- map((id) => ({
176
- rect: nonNullable(rects.get(id)),
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
- onNodeDragStart: (_event, xynode) => {
195
- diagram.cancelSaveManualLayout();
196
- const { nodeLookup } = xystore.getState();
197
- const draggingNodes = pipe(
198
- Array.from(nodeLookup.values()),
199
- filter((n) => n.dragging === true || n.id === xynode.id || n.selected === true),
200
- filter((n) => n.draggable !== false),
201
- map((x) => x.id)
202
- );
203
- if (hasAtLeast(draggingNodes, 1)) {
204
- solverRef.current = createLayoutConstraints(xystore, draggingNodes);
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
- onNodeDrag: () => {
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 };