@likec4/diagram 1.24.0 → 1.24.1
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/dist/LikeC4Diagram.css.d.ts +5 -0
- package/dist/LikeC4Diagram.css.js +1 -0
- package/dist/LikeC4Diagram.d.ts +1 -1
- package/dist/LikeC4Diagram.js +6 -2
- package/dist/LikeC4Diagram.props.d.ts +6 -0
- package/dist/base/BaseXYFlow.d.ts +1 -1
- package/dist/base/BaseXYFlow.js +12 -3
- package/dist/base/const.d.ts +1 -1
- package/dist/base/const.js +1 -1
- package/dist/base/primitives/compound/CompoundActionButton.css.js +2 -0
- package/dist/base/primitives/compound/CompoundDetailsButton.css.js +2 -0
- package/dist/base/primitives/compound/CompoundNodeContainer.css.d.ts +0 -1
- package/dist/base/primitives/compound/CompoundNodeContainer.css.js +4 -4
- package/dist/base/primitives/compound/CompoundNodeContainer.js +35 -22
- package/dist/base/primitives/compound/CompoundTitle.css.js +3 -1
- package/dist/base/primitives/customNode.js +1 -1
- package/dist/base/primitives/edge/EdgeContainer.js +5 -4
- package/dist/base/primitives/edge/EdgeLabel.css.d.ts +0 -1
- package/dist/base/primitives/edge/EdgeLabel.css.js +2 -5
- package/dist/base/primitives/edge/EdgeLabel.js +5 -5
- package/dist/base/primitives/edge/edge.css.d.ts +1 -1
- package/dist/base/primitives/edge/edge.css.js +4 -5
- package/dist/base/primitives/element/ElementActionButtons.css.js +2 -0
- package/dist/base/primitives/element/ElementActionButtons.js +4 -3
- package/dist/base/primitives/element/ElementDetailsButton.css.js +2 -0
- package/dist/base/primitives/element/ElementNodeContainer.css.js +2 -0
- package/dist/base/primitives/element/ElementNodeContainer.js +3 -1
- package/dist/base/primitives/element/ElementShape.css.js +4 -2
- package/dist/base/primitives/element/ElementShape.js +2 -3
- package/dist/base/primitives/element/ElementTitle.css.js +2 -0
- package/dist/base/primitives/element/styles.css.js +2 -0
- package/dist/components/SearchControl.css.js +2 -0
- package/dist/context/FramerMotionConfig.d.ts +9 -1
- package/dist/context/FramerMotionConfig.js +5 -2
- package/dist/context/RootContainer.js +13 -1
- package/dist/diagram.css +1 -1
- package/dist/globals.css.d.ts +2 -0
- package/dist/hooks/safeContext.js +1 -0
- package/dist/hooks/useDiagram.d.ts +554 -864
- package/dist/hooks/useDiagram.js +1 -0
- package/dist/hooks/useIsReducedGraphics.d.ts +4 -0
- package/dist/hooks/useIsReducedGraphics.js +8 -0
- package/dist/hooks/useXYFlow.js +1 -1
- package/dist/likec4diagram/DiagramUI.d.ts +2 -1
- package/dist/likec4diagram/DiagramUI.js +5 -3
- package/dist/likec4diagram/custom/nodes/nodeTypes.js +2 -2
- package/dist/likec4diagram/ui/controls/styles.css.js +2 -0
- package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.css.js +2 -0
- package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +3 -4
- package/dist/likec4diagram/ui/search/LikeC4Search.css.js +2 -0
- package/dist/overlays/element-details/ElementDetailsCard.js +12 -8
- package/dist/overlays/element-details/actor.d.ts +9084 -2569
- package/dist/overlays/overlay/Overlay.js +6 -3
- package/dist/overlays/relationship-details/RelationshipDetails.js +31 -17
- package/dist/overlays/relationship-details/actor.d.ts +228 -3
- package/dist/overlays/relationship-details/actor.js +86 -14
- package/dist/overlays/relationship-details/hooks.d.ts +3 -0
- package/dist/overlays/relationship-details/hooks.js +6 -2
- package/dist/overlays/relationships-browser/-useRelationshipsView.js +12 -3
- package/dist/overlays/relationships-browser/RelationshipsBrowser.js +12 -3
- package/dist/overlays/relationships-browser/actor.d.ts +271 -54
- package/dist/overlays/relationships-browser/actor.js +86 -23
- package/dist/state/DiagramActorProvider.js +12 -2
- package/dist/state/diagram-machine.d.ts +563 -867
- package/dist/state/diagram-machine.js +82 -81
- package/dist/state/persistence.d.ts +5 -0
- package/dist/state/persistence.js +23 -0
- package/package.json +9 -9
- package/src/LikeC4Diagram.css.ts +13 -5
- package/src/LikeC4Diagram.props.ts +7 -0
- package/src/LikeC4Diagram.tsx +33 -24
- package/src/base/BaseXYFlow.tsx +14 -9
- package/src/base/const.ts +1 -1
- package/src/base/primitives/compound/CompoundActionButton.css.ts +6 -0
- package/src/base/primitives/compound/CompoundDetailsButton.css.ts +4 -0
- package/src/base/primitives/compound/CompoundNodeContainer.css.ts +41 -59
- package/src/base/primitives/compound/CompoundNodeContainer.tsx +42 -22
- package/src/base/primitives/compound/CompoundTitle.css.ts +14 -13
- package/src/base/primitives/customNode.ts +3 -2
- package/src/base/primitives/edge/EdgeContainer.tsx +4 -3
- package/src/base/primitives/edge/EdgeLabel.css.ts +17 -11
- package/src/base/primitives/edge/EdgeLabel.tsx +4 -4
- package/src/base/primitives/edge/edge.css.ts +47 -27
- package/src/base/primitives/element/ElementActionButtons.css.ts +4 -0
- package/src/base/primitives/element/ElementActionButtons.tsx +4 -2
- package/src/base/primitives/element/ElementDetailsButton.css.ts +6 -8
- package/src/base/primitives/element/ElementNodeContainer.css.ts +18 -21
- package/src/base/primitives/element/ElementNodeContainer.tsx +3 -1
- package/src/base/primitives/element/ElementShape.css.ts +10 -6
- package/src/base/primitives/element/ElementShape.tsx +2 -2
- package/src/components/SearchControl.css.ts +11 -4
- package/src/context/FramerMotionConfig.tsx +13 -2
- package/src/context/RootContainer.tsx +9 -2
- package/src/globals.css.ts +3 -0
- package/src/hooks/safeContext.tsx +2 -1
- package/src/hooks/useDiagram.ts +1 -0
- package/src/hooks/useIsReducedGraphics.ts +5 -0
- package/src/hooks/useXYFlow.ts +1 -1
- package/src/likec4diagram/DiagramUI.tsx +5 -3
- package/src/likec4diagram/custom/nodes/nodeTypes.tsx +2 -2
- package/src/likec4diagram/ui/controls/styles.css.ts +11 -1
- package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.css.ts +10 -3
- package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +6 -3
- package/src/likec4diagram/ui/search/LikeC4Search.css.ts +1 -0
- package/src/overlays/element-details/ElementDetailsCard.tsx +238 -231
- package/src/overlays/overlay/Overlay.css.ts +1 -9
- package/src/overlays/overlay/Overlay.tsx +6 -3
- package/src/overlays/relationship-details/RelationshipDetails.tsx +83 -66
- package/src/overlays/relationship-details/actor.ts +106 -13
- package/src/overlays/relationship-details/hooks.ts +6 -2
- package/src/overlays/relationships-browser/-useRelationshipsView.ts +10 -0
- package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +16 -8
- package/src/overlays/relationships-browser/actor.ts +92 -22
- package/src/state/DiagramActorProvider.tsx +13 -2
- package/src/state/diagram-machine.ts +80 -79
- package/src/state/persistence.ts +22 -0
|
@@ -2,14 +2,15 @@ import { nonNullable, invariant, DiagramNode, nonexhaustive, getBBoxCenter, getP
|
|
|
2
2
|
import { getViewportForBounds, applyEdgeChanges, applyNodeChanges } from "@xyflow/react";
|
|
3
3
|
import { nodeToRect } from "@xyflow/system";
|
|
4
4
|
import { hasAtLeast, prop, clamp, first } from "remeda";
|
|
5
|
-
import { setup, enqueueActions, assertEvent, stopChild,
|
|
5
|
+
import { setup, enqueueActions, assign, assertEvent, stopChild, sendTo, cancel, raise, spawnChild, and, or } from "xstate";
|
|
6
6
|
import { MinZoom } from "../base/const.js";
|
|
7
7
|
import { AllDisabled } from "../context/DiagramFeatures.js";
|
|
8
8
|
import { createLayoutConstraints } from "../likec4diagram/useLayoutConstraints.js";
|
|
9
9
|
import { overlaysActorLogic } from "../overlays/overlaysActor.js";
|
|
10
10
|
import { getAligner, toNodeRect } from "./aligners.js";
|
|
11
|
-
import { resetEdgeControlPoints, updateEdgeData, updateNodeData, updateNavigationHistory, mergeXYNodesEdges, navigateForward, navigateBack, unfocusNodesEdges, updateActiveWalkthrough, focusNodesEdges
|
|
11
|
+
import { lastClickedNode, resetEdgeControlPoints, updateEdgeData, updateNodeData, updateNavigationHistory, mergeXYNodesEdges, navigateForward, navigateBack, unfocusNodesEdges, updateActiveWalkthrough, focusNodesEdges } from "./assign.js";
|
|
12
12
|
import { hotkeyActorLogic } from "./hotkeyActor.js";
|
|
13
|
+
import { DiagramToggledFeaturesPersistence } from "./persistence.js";
|
|
13
14
|
import { syncManualLayoutActorLogic } from "./syncManualLayoutActor.js";
|
|
14
15
|
import { typedSystem, findDiagramNode, focusedBounds } from "./utils.js";
|
|
15
16
|
const diagramMachine = setup({
|
|
@@ -40,7 +41,7 @@ const diagramMachine = setup({
|
|
|
40
41
|
}
|
|
41
42
|
nonexhaustive(event.type);
|
|
42
43
|
},
|
|
43
|
-
"click: selected node
|
|
44
|
+
"click: selected node": ({ event }) => {
|
|
44
45
|
assertEvent(event, "xyflow.nodeClick");
|
|
45
46
|
return event.node.selected === true;
|
|
46
47
|
},
|
|
@@ -52,6 +53,10 @@ const diagramMachine = setup({
|
|
|
52
53
|
"click: focused node": ({ context, event }) => {
|
|
53
54
|
assertEvent(event, "xyflow.nodeClick");
|
|
54
55
|
return context.focusedNode === event.node.id;
|
|
56
|
+
},
|
|
57
|
+
"click: node has connections": ({ context, event }) => {
|
|
58
|
+
assertEvent(event, "xyflow.nodeClick");
|
|
59
|
+
return context.xyedges.some((e) => e.source === event.node.id || e.target === event.node.id);
|
|
55
60
|
}
|
|
56
61
|
},
|
|
57
62
|
actions: {
|
|
@@ -61,21 +66,22 @@ const diagramMachine = setup({
|
|
|
61
66
|
},
|
|
62
67
|
"trigger:OpenSource": (_, _params) => {
|
|
63
68
|
},
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
"assign lastClickedNode": assign(({ context, event }) => {
|
|
70
|
+
assertEvent(event, "xyflow.nodeClick");
|
|
71
|
+
return {
|
|
72
|
+
lastClickedNode: lastClickedNode({ context, event })
|
|
73
|
+
};
|
|
74
|
+
}),
|
|
75
|
+
"open source of focused or last clicked node": enqueueActions(({ context, enqueue }) => {
|
|
76
|
+
var _a;
|
|
77
|
+
const nodeId = context.focusedNode ?? ((_a = context.lastClickedNode) == null ? void 0 : _a.id);
|
|
78
|
+
if (!nodeId || !context.features.enableVscode) return;
|
|
79
|
+
const diagramNode = findDiagramNode(context, nodeId);
|
|
67
80
|
if (!diagramNode) return;
|
|
68
|
-
enqueue.cancel("openSource");
|
|
69
81
|
if (DiagramNode.deploymentRef(diagramNode)) {
|
|
70
|
-
enqueue.raise({ type: "open.source", deployment: DiagramNode.deploymentRef(diagramNode) }
|
|
71
|
-
id: "openSource",
|
|
72
|
-
delay: 100
|
|
73
|
-
});
|
|
82
|
+
enqueue.raise({ type: "open.source", deployment: DiagramNode.deploymentRef(diagramNode) });
|
|
74
83
|
} else if (DiagramNode.modelRef(diagramNode)) {
|
|
75
|
-
enqueue.raise({ type: "open.source", element: DiagramNode.modelRef(diagramNode) }
|
|
76
|
-
id: "openSource",
|
|
77
|
-
delay: 100
|
|
78
|
-
});
|
|
84
|
+
enqueue.raise({ type: "open.source", element: DiagramNode.modelRef(diagramNode) });
|
|
79
85
|
}
|
|
80
86
|
}),
|
|
81
87
|
"xyflow:fitDiagram": ({ context }, params) => {
|
|
@@ -183,7 +189,7 @@ const diagramMachine = setup({
|
|
|
183
189
|
context: ({ input, self, spawn }) => ({
|
|
184
190
|
...input,
|
|
185
191
|
features: { ...AllDisabled },
|
|
186
|
-
toggledFeatures: {
|
|
192
|
+
toggledFeatures: DiagramToggledFeaturesPersistence.read() ?? {
|
|
187
193
|
enableReadOnly: true
|
|
188
194
|
},
|
|
189
195
|
initialized: false,
|
|
@@ -242,9 +248,10 @@ const diagramMachine = setup({
|
|
|
242
248
|
// TODO: xstate fails to infer the type of the guard
|
|
243
249
|
guard: and([
|
|
244
250
|
"enabled: FocusMode",
|
|
251
|
+
"click: node has connections",
|
|
245
252
|
or([
|
|
246
253
|
"click: same node",
|
|
247
|
-
"click: selected node
|
|
254
|
+
"click: selected node"
|
|
248
255
|
])
|
|
249
256
|
]),
|
|
250
257
|
actions: [
|
|
@@ -256,9 +263,10 @@ const diagramMachine = setup({
|
|
|
256
263
|
target: "focused"
|
|
257
264
|
},
|
|
258
265
|
{
|
|
259
|
-
actions:
|
|
260
|
-
lastClickedNode
|
|
261
|
-
|
|
266
|
+
actions: [
|
|
267
|
+
"assign lastClickedNode",
|
|
268
|
+
"open source of focused or last clicked node"
|
|
269
|
+
]
|
|
262
270
|
}
|
|
263
271
|
],
|
|
264
272
|
"xyflow.paneClick": {
|
|
@@ -301,7 +309,7 @@ const diagramMachine = setup({
|
|
|
301
309
|
...focusNodesEdges(s),
|
|
302
310
|
viewportBeforeFocus: { ...s.context.viewport }
|
|
303
311
|
})),
|
|
304
|
-
"open source of focused node",
|
|
312
|
+
"open source of focused or last clicked node",
|
|
305
313
|
spawnChild("hotkeyActorLogic", { id: "hotkey" }),
|
|
306
314
|
{
|
|
307
315
|
type: "xyflow:fitDiagram",
|
|
@@ -334,7 +342,7 @@ const diagramMachine = setup({
|
|
|
334
342
|
focusedNode: ({ event }) => event.node.id
|
|
335
343
|
}),
|
|
336
344
|
assign(focusNodesEdges),
|
|
337
|
-
"open source of focused node",
|
|
345
|
+
"open source of focused or last clicked node",
|
|
338
346
|
{
|
|
339
347
|
type: "xyflow:fitDiagram",
|
|
340
348
|
params: focusedBounds
|
|
@@ -348,7 +356,7 @@ const diagramMachine = setup({
|
|
|
348
356
|
focusedNode: ({ event }) => event.nodeId
|
|
349
357
|
}),
|
|
350
358
|
assign(focusNodesEdges),
|
|
351
|
-
"open source of focused node",
|
|
359
|
+
"open source of focused or last clicked node",
|
|
352
360
|
{
|
|
353
361
|
type: "xyflow:fitDiagram",
|
|
354
362
|
params: focusedBounds
|
|
@@ -380,69 +388,60 @@ const diagramMachine = setup({
|
|
|
380
388
|
},
|
|
381
389
|
// Navigating to another view (after `navigateTo` event)
|
|
382
390
|
navigating: {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
type: "trigger:NavigateTo",
|
|
392
|
-
params: ({ context }) => ({
|
|
393
|
-
viewId: nonNullable(context.lastOnNavigate, "Invalid state, lastOnNavigate is null").toView
|
|
394
|
-
})
|
|
395
|
-
});
|
|
396
|
-
}),
|
|
397
|
-
on: {
|
|
398
|
-
"update.view": {
|
|
399
|
-
actions: enqueueActions(({ enqueue, context, event }) => {
|
|
400
|
-
const { fromNode, toNode } = findCorrespondingNode(context, event);
|
|
401
|
-
if (fromNode && toNode) {
|
|
402
|
-
enqueue({
|
|
403
|
-
type: "xyflow:alignNodeFromToAfterNavigate",
|
|
404
|
-
params: {
|
|
405
|
-
fromNode: fromNode.id,
|
|
406
|
-
toPosition: {
|
|
407
|
-
x: toNode.position[0],
|
|
408
|
-
y: toNode.position[1]
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
enqueue.raise({ type: "fitDiagram" }, { id: "fitDiagram", delay: 80 });
|
|
413
|
-
} else {
|
|
414
|
-
enqueue({
|
|
415
|
-
type: "xyflow:setViewportCenter",
|
|
416
|
-
params: getBBoxCenter(event.view.bounds)
|
|
417
|
-
});
|
|
418
|
-
enqueue.raise({ type: "fitDiagram", duration: 200 }, { id: "fitDiagram", delay: 25 });
|
|
419
|
-
}
|
|
420
|
-
enqueue.assign(updateNavigationHistory);
|
|
421
|
-
enqueue.assign(mergeXYNodesEdges);
|
|
422
|
-
}),
|
|
423
|
-
target: "done"
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
},
|
|
427
|
-
done: {
|
|
428
|
-
type: "final"
|
|
391
|
+
entry: [
|
|
392
|
+
"closeAllOverlays",
|
|
393
|
+
"stopSyncLayout",
|
|
394
|
+
{
|
|
395
|
+
type: "trigger:NavigateTo",
|
|
396
|
+
params: ({ context }) => ({
|
|
397
|
+
viewId: nonNullable(context.lastOnNavigate, "Invalid state, lastOnNavigate is null").toView
|
|
398
|
+
})
|
|
429
399
|
}
|
|
430
|
-
|
|
431
|
-
onDone: {
|
|
432
|
-
target: "idle"
|
|
433
|
-
},
|
|
400
|
+
],
|
|
434
401
|
exit: assign({
|
|
435
402
|
syncLayoutActorRef: ({ self, context, spawn }) => spawn("syncManualLayoutActorLogic", {
|
|
436
403
|
id: "syncLayout",
|
|
437
404
|
systemId: "syncLayout",
|
|
438
405
|
input: { parent: self, viewId: context.view.id }
|
|
439
|
-
})
|
|
440
|
-
lastOnNavigate: null
|
|
441
|
-
})
|
|
406
|
+
})
|
|
407
|
+
// lastOnNavigate: null,
|
|
408
|
+
}),
|
|
409
|
+
on: {
|
|
410
|
+
"update.view": {
|
|
411
|
+
actions: enqueueActions(({ enqueue, context, event }) => {
|
|
412
|
+
const { fromNode, toNode } = findCorrespondingNode(context, event);
|
|
413
|
+
if (fromNode && toNode) {
|
|
414
|
+
enqueue({
|
|
415
|
+
type: "xyflow:alignNodeFromToAfterNavigate",
|
|
416
|
+
params: {
|
|
417
|
+
fromNode: fromNode.id,
|
|
418
|
+
toPosition: {
|
|
419
|
+
x: toNode.position[0],
|
|
420
|
+
y: toNode.position[1]
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
enqueue.raise({ type: "fitDiagram" }, { id: "fitDiagram", delay: 80 });
|
|
425
|
+
} else {
|
|
426
|
+
enqueue({
|
|
427
|
+
type: "xyflow:setViewportCenter",
|
|
428
|
+
params: getBBoxCenter(event.view.bounds)
|
|
429
|
+
});
|
|
430
|
+
enqueue.raise({ type: "fitDiagram", duration: 200 }, { id: "fitDiagram", delay: 25 });
|
|
431
|
+
}
|
|
432
|
+
enqueue.assign(updateNavigationHistory);
|
|
433
|
+
enqueue.assign(mergeXYNodesEdges);
|
|
434
|
+
enqueue.assign({
|
|
435
|
+
lastOnNavigate: null
|
|
436
|
+
});
|
|
437
|
+
}),
|
|
438
|
+
target: "#idle"
|
|
439
|
+
}
|
|
440
|
+
}
|
|
442
441
|
},
|
|
443
442
|
walkthrough: {
|
|
444
443
|
entry: [
|
|
445
|
-
|
|
444
|
+
spawnChild("hotkeyActorLogic", { id: "hotkey" }),
|
|
446
445
|
assign({
|
|
447
446
|
viewportBeforeFocus: ({ context }) => context.viewport,
|
|
448
447
|
activeWalkthrough: ({ context, event }) => {
|
|
@@ -702,10 +701,12 @@ const diagramMachine = setup({
|
|
|
702
701
|
},
|
|
703
702
|
"toggle.feature": {
|
|
704
703
|
actions: assign({
|
|
705
|
-
toggledFeatures: ({ context, event }) =>
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
704
|
+
toggledFeatures: ({ context, event }) => {
|
|
705
|
+
return DiagramToggledFeaturesPersistence.write({
|
|
706
|
+
...context.toggledFeatures,
|
|
707
|
+
[`enable${event.feature}`]: event.forceValue ?? !(context.toggledFeatures[`enable${event.feature}`] ?? context.features[`enable${event.feature}`])
|
|
708
|
+
});
|
|
709
|
+
}
|
|
709
710
|
})
|
|
710
711
|
}
|
|
711
712
|
},
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { pickBy, isBoolean } from "remeda";
|
|
2
|
+
const key = `likec4:diagram:toggledFeatures`;
|
|
3
|
+
const DiagramToggledFeaturesPersistence = {
|
|
4
|
+
read() {
|
|
5
|
+
try {
|
|
6
|
+
let fromStorage = sessionStorage.getItem(key);
|
|
7
|
+
if (fromStorage) {
|
|
8
|
+
return JSON.parse(fromStorage);
|
|
9
|
+
}
|
|
10
|
+
throw new Error(`Workspace ${key} not found`);
|
|
11
|
+
} catch (e) {
|
|
12
|
+
console.error(`Error reading fromStorage ${key}:`, e);
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
write(toggledFeatures) {
|
|
17
|
+
sessionStorage.setItem(key, JSON.stringify(pickBy(toggledFeatures, isBoolean)));
|
|
18
|
+
return toggledFeatures;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
DiagramToggledFeaturesPersistence
|
|
23
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/diagram",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
6
6
|
"homepage": "https://likec4.dev",
|
|
@@ -37,21 +37,21 @@
|
|
|
37
37
|
"@tabler/icons-react": "3.29.0",
|
|
38
38
|
"@vanilla-extract/dynamic": "^2.1.2",
|
|
39
39
|
"@xstate/react": "^5.0.2",
|
|
40
|
-
"@xyflow/react": "12.4.
|
|
41
|
-
"@xyflow/system": "0.0.
|
|
40
|
+
"@xyflow/react": "12.4.4",
|
|
41
|
+
"@xyflow/system": "0.0.52",
|
|
42
42
|
"bezier-js": "^6.1.4",
|
|
43
43
|
"clsx": "^2.1.1",
|
|
44
44
|
"d3-shape": "^3.2.0",
|
|
45
45
|
"esm-env": "^1.2.2",
|
|
46
46
|
"fast-equals": "^5.2.2",
|
|
47
|
-
"framer-motion": "^12.4.
|
|
47
|
+
"framer-motion": "^12.4.7",
|
|
48
48
|
"khroma": "^2.1.0",
|
|
49
49
|
"nanostores": "0.11.3",
|
|
50
50
|
"react-error-boundary": "^5.0.0",
|
|
51
|
-
"remeda": "^2.20.
|
|
51
|
+
"remeda": "^2.20.2",
|
|
52
52
|
"type-fest": "4.34.1",
|
|
53
53
|
"xstate": "^5.19.2",
|
|
54
|
-
"@likec4/core": "1.24.
|
|
54
|
+
"@likec4/core": "1.24.1"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@mantine/core": "7.17.0",
|
|
@@ -72,14 +72,14 @@
|
|
|
72
72
|
"typescript": "5.7.3",
|
|
73
73
|
"vite": "6.1.0",
|
|
74
74
|
"vite-plugin-dts": "^4.5.0",
|
|
75
|
-
"vitest": "^3.0.
|
|
76
|
-
"@likec4/tsconfig": "1.24.
|
|
75
|
+
"vitest": "^3.0.6",
|
|
76
|
+
"@likec4/tsconfig": "1.24.1"
|
|
77
77
|
},
|
|
78
78
|
"scripts": {
|
|
79
79
|
"typecheck": "tsc --noEmit",
|
|
80
80
|
"build": "vite build",
|
|
81
81
|
"lint": "run -T eslint src/ --fix",
|
|
82
|
-
"clean": "rm -r -f -g
|
|
82
|
+
"clean": "rm -r -f -g \"dist/*\" .tsbuildinfo",
|
|
83
83
|
"test": "vitest run --no-isolate"
|
|
84
84
|
}
|
|
85
85
|
}
|
package/src/LikeC4Diagram.css.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { globalStyle, style } from '@vanilla-extract/css'
|
|
2
|
+
import { whereNotReducedGraphics, whereReducedGraphics } from './globals.css'
|
|
2
3
|
import { vars, xyvars } from './theme-vars'
|
|
3
4
|
|
|
4
5
|
export const cssReactFlow = style({
|
|
@@ -13,6 +14,10 @@ export const cssReactFlow = style({
|
|
|
13
14
|
},
|
|
14
15
|
})
|
|
15
16
|
|
|
17
|
+
export const reactFlowReducedGraphics = `${whereReducedGraphics} ${cssReactFlow}`
|
|
18
|
+
export const reactFlow = `${whereNotReducedGraphics} ${cssReactFlow}`
|
|
19
|
+
export { whereNotReducedGraphics, whereReducedGraphics }
|
|
20
|
+
|
|
16
21
|
export const notInitialized = style({
|
|
17
22
|
opacity: 0,
|
|
18
23
|
})
|
|
@@ -30,11 +35,6 @@ globalStyle(`.react-flow${cssReactFlow}`, {
|
|
|
30
35
|
globalStyle(`.react-flow${cssReactFlow} .react-flow__pane`, {
|
|
31
36
|
WebkitUserSelect: 'none',
|
|
32
37
|
})
|
|
33
|
-
|
|
34
|
-
globalStyle(`.react-flow${cssReactFlow} .xyflow__viewport`, {
|
|
35
|
-
willChange: 'transform',
|
|
36
|
-
})
|
|
37
|
-
|
|
38
38
|
export const cssDisablePan = style({})
|
|
39
39
|
|
|
40
40
|
export const cssTransparentBg = style({})
|
|
@@ -58,3 +58,11 @@ export const hiddenIfZoomTooSmall = style({
|
|
|
58
58
|
},
|
|
59
59
|
},
|
|
60
60
|
})
|
|
61
|
+
|
|
62
|
+
export const hiddenIfReducedGraphics = style({
|
|
63
|
+
selectors: {
|
|
64
|
+
[`${reactFlowReducedGraphics} &`]: {
|
|
65
|
+
visibility: 'hidden',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
})
|
|
@@ -172,6 +172,13 @@ export interface LikeC4DiagramProperties {
|
|
|
172
172
|
*/
|
|
173
173
|
experimentalEdgeEditing?: boolean | undefined
|
|
174
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Improve performance by hiding certain elements and reducing visual effects (disable mix-blend, shadows, animations)
|
|
177
|
+
*
|
|
178
|
+
* @default 'auto' - will be set to true if view has more then 3000 * 2000 pixels
|
|
179
|
+
*/
|
|
180
|
+
reduceGraphics?: 'auto' | boolean | undefined
|
|
181
|
+
|
|
175
182
|
/**
|
|
176
183
|
* Render icon for an element, bundled or remote
|
|
177
184
|
* By default, if icon is http:// or https://, it will be rendered as an image
|
package/src/LikeC4Diagram.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
IconRendererProvider,
|
|
10
10
|
RootContainer,
|
|
11
11
|
} from './context'
|
|
12
|
+
import { ReducedGraphicsContext } from './hooks/useIsReducedGraphics'
|
|
12
13
|
import { LikeC4CustomColors } from './LikeC4CustomColors'
|
|
13
14
|
import { type LikeC4DiagramEventHandlers, type LikeC4DiagramProperties } from './LikeC4Diagram.props'
|
|
14
15
|
import type { Types } from './likec4diagram/types'
|
|
@@ -41,6 +42,7 @@ export function LikeC4Diagram({
|
|
|
41
42
|
initialWidth,
|
|
42
43
|
initialHeight,
|
|
43
44
|
experimentalEdgeEditing = false,
|
|
45
|
+
reduceGraphics = 'auto',
|
|
44
46
|
onCanvasClick,
|
|
45
47
|
onCanvasContextMenu,
|
|
46
48
|
onCanvasDblClick,
|
|
@@ -81,6 +83,11 @@ export function LikeC4Diagram({
|
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
// If view has more then 3000 * 2000 pixels - assume it is a big diagram
|
|
87
|
+
const isReducedGraphicsMode = reduceGraphics === 'auto'
|
|
88
|
+
? (view.bounds?.width ?? 1) * (view.bounds?.height ?? 1) > 6_000_000
|
|
89
|
+
: reduceGraphics
|
|
90
|
+
|
|
84
91
|
return (
|
|
85
92
|
<EnsureMantine>
|
|
86
93
|
<FramerMotionConfig>
|
|
@@ -119,30 +126,32 @@ export function LikeC4Diagram({
|
|
|
119
126
|
onOpenSource,
|
|
120
127
|
onBurgerMenuClick,
|
|
121
128
|
}}>
|
|
122
|
-
<
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
129
|
+
<ReducedGraphicsContext value={isReducedGraphicsMode}>
|
|
130
|
+
<RootContainer className={className}>
|
|
131
|
+
{!isEmpty(view.customColorDefinitions) && (
|
|
132
|
+
<LikeC4CustomColors customColors={view.customColorDefinitions} />
|
|
133
|
+
)}
|
|
134
|
+
<XYFlowProvider
|
|
135
|
+
fitView={fitView}
|
|
136
|
+
{...initialRef.current}
|
|
137
|
+
>
|
|
138
|
+
<DiagramActorProvider
|
|
139
|
+
input={{
|
|
140
|
+
view,
|
|
141
|
+
pannable,
|
|
142
|
+
zoomable,
|
|
143
|
+
fitViewPadding,
|
|
144
|
+
...xyNodesEdges,
|
|
145
|
+
}}>
|
|
146
|
+
<LikeC4DiagramXYFlow
|
|
147
|
+
nodesDraggable={nodesDraggable}
|
|
148
|
+
nodesSelectable={nodesSelectable}
|
|
149
|
+
background={background}
|
|
150
|
+
/>
|
|
151
|
+
</DiagramActorProvider>
|
|
152
|
+
</XYFlowProvider>
|
|
153
|
+
</RootContainer>
|
|
154
|
+
</ReducedGraphicsContext>
|
|
146
155
|
</DiagramEventHandlers>
|
|
147
156
|
</DiagramFeatures>
|
|
148
157
|
</IconRendererProvider>
|
package/src/base/BaseXYFlow.tsx
CHANGED
|
@@ -8,9 +8,11 @@ import {
|
|
|
8
8
|
useStoreApi,
|
|
9
9
|
} from '@xyflow/react'
|
|
10
10
|
import clsx from 'clsx'
|
|
11
|
+
import { shallowEqual } from 'fast-equals'
|
|
11
12
|
import { useMemo } from 'react'
|
|
12
13
|
import type { SetRequired, Simplify } from 'type-fest'
|
|
13
|
-
import {
|
|
14
|
+
import { useIsReducedGraphics } from '../hooks/useIsReducedGraphics'
|
|
15
|
+
import { useUpdateEffect } from '../hooks/useUpdateEffect'
|
|
14
16
|
import { useIsZoomTooSmall } from '../hooks/useXYFlow'
|
|
15
17
|
import * as css from '../LikeC4Diagram.css'
|
|
16
18
|
import { stopPropagation } from '../utils/xyflow'
|
|
@@ -41,10 +43,8 @@ export type BaseXYFlowProps<NodeType extends Base.Node, EdgeType extends Base.Ed
|
|
|
41
43
|
| 'zoomActivationKeyCode'
|
|
42
44
|
| 'zoomOnScroll'
|
|
43
45
|
| 'elementsSelectable'
|
|
44
|
-
| '
|
|
45
|
-
| '
|
|
46
|
-
| 'onEdgeMouseEnter'
|
|
47
|
-
| 'onEdgeMouseLeave'
|
|
46
|
+
| 'onNodeDoubleClick'
|
|
47
|
+
| 'onEdgeDoubleClick'
|
|
48
48
|
| 'fitViewOptions'
|
|
49
49
|
>,
|
|
50
50
|
// Required props
|
|
@@ -79,6 +79,7 @@ export const BaseXYFlow = <
|
|
|
79
79
|
}: BaseXYFlowProps<NodeType, EdgeType>) => {
|
|
80
80
|
const isBgWithPattern = background !== 'transparent' && background !== 'solid'
|
|
81
81
|
const isZoomTooSmall = useIsZoomTooSmall()
|
|
82
|
+
const reduceGraphics = useIsReducedGraphics()
|
|
82
83
|
return (
|
|
83
84
|
<ReactFlow<NodeType, EdgeType>
|
|
84
85
|
colorMode={colorMode}
|
|
@@ -90,7 +91,12 @@ export const BaseXYFlow = <
|
|
|
90
91
|
background === 'transparent' && css.cssTransparentBg,
|
|
91
92
|
className,
|
|
92
93
|
)}
|
|
93
|
-
|
|
94
|
+
{...isZoomTooSmall && {
|
|
95
|
+
['data-likec4-zoom-small']: true,
|
|
96
|
+
}}
|
|
97
|
+
{...reduceGraphics && {
|
|
98
|
+
['data-likec4-reduced-graphics']: true,
|
|
99
|
+
}}
|
|
94
100
|
zoomOnPinch={zoomable}
|
|
95
101
|
zoomOnScroll={!pannable && zoomable}
|
|
96
102
|
{...(!zoomable && {
|
|
@@ -115,8 +121,7 @@ export const BaseXYFlow = <
|
|
|
115
121
|
{...(!pannable && {
|
|
116
122
|
selectionKeyCode: null,
|
|
117
123
|
})}
|
|
118
|
-
|
|
119
|
-
//onlyRenderVisibleElements={nodes.length > 75}
|
|
124
|
+
onlyRenderVisibleElements={reduceGraphics}
|
|
120
125
|
elementsSelectable={nodesSelectable}
|
|
121
126
|
nodesFocusable={nodesDraggable || nodesSelectable}
|
|
122
127
|
edgesFocusable={false}
|
|
@@ -202,7 +207,7 @@ const ViewportResizeHanlder = ({
|
|
|
202
207
|
}: {
|
|
203
208
|
onViewportResize: () => void
|
|
204
209
|
}) => {
|
|
205
|
-
const { width, height } = useStore(selectDimensions)
|
|
210
|
+
const { width, height } = useStore(selectDimensions, shallowEqual)
|
|
206
211
|
useUpdateEffect(onViewportResize, [width, height])
|
|
207
212
|
|
|
208
213
|
return <></>
|
package/src/base/const.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { style } from '@vanilla-extract/css'
|
|
2
|
+
import { reactFlowReducedGraphics } from '../../../LikeC4Diagram.css'
|
|
2
3
|
import { transitions, vars } from '../../../theme-vars'
|
|
3
4
|
|
|
4
5
|
export const container = style({
|
|
@@ -45,11 +46,16 @@ export const actionIcon = style({
|
|
|
45
46
|
},
|
|
46
47
|
[`:where([data-hovered='true']) &`]: {
|
|
47
48
|
opacity: 1,
|
|
49
|
+
transitionDelay: '100ms',
|
|
50
|
+
transitionDuration: '300ms',
|
|
48
51
|
// boxShadow: '1px 1px 3px 0px rgba(0, 0, 0, 0.15)',
|
|
49
52
|
// color: `var(--ai-color-hover)`,
|
|
50
53
|
// vars: {
|
|
51
54
|
// '--ai-bg': `var(--ai-bg-hover)`,
|
|
52
55
|
// },
|
|
53
56
|
},
|
|
57
|
+
[`${reactFlowReducedGraphics} &`]: {
|
|
58
|
+
boxShadow: 'none',
|
|
59
|
+
},
|
|
54
60
|
},
|
|
55
61
|
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createVar, globalStyle, style } from '@vanilla-extract/css'
|
|
2
|
+
import { reactFlowReducedGraphics } from '../../../LikeC4Diagram.css'
|
|
2
3
|
import { vars } from '../../../theme-vars'
|
|
3
4
|
|
|
4
5
|
export const container = style({
|
|
@@ -47,6 +48,9 @@ export const actionIcon = style({
|
|
|
47
48
|
'--ai-bg': `color-mix(in srgb , ${vars.element.fill}, transparent 99%)`,
|
|
48
49
|
},
|
|
49
50
|
},
|
|
51
|
+
[`${reactFlowReducedGraphics} &`]: {
|
|
52
|
+
boxShadow: 'none',
|
|
53
|
+
},
|
|
50
54
|
},
|
|
51
55
|
})
|
|
52
56
|
|