@likec4/diagram 1.28.1 → 1.29.0
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 +264 -231
- package/dist/base/primitives/customEdge.js +1 -1
- package/dist/base/primitives/edge/EdgeLabel.css.d.ts +3 -0
- package/dist/base/primitives/edge/EdgeLabel.css.js +3 -0
- package/dist/base/primitives/edge/EdgeLabel.d.ts +1 -1
- package/dist/base/primitives/edge/EdgeLabel.js +2 -1
- package/dist/hooks/safeContext.d.ts +2 -5
- package/dist/hooks/safeContext.js +2 -1
- package/dist/hooks/useDiagram.d.ts +711 -146
- package/dist/likec4diagram/ui/controls/Controls.js +3 -3
- package/dist/overlays/element-details/ElementDetails.js +1 -1
- package/dist/state/DiagramActorProvider.js +2 -2
- package/dist/state/diagram-machine.d.ts +715 -142
- package/dist/state/diagram-machine.js +48 -6
- package/package.json +8 -8
- package/src/LikeC4Diagram.tsx +0 -1
- package/src/base/primitives/customEdge.ts +1 -0
- package/src/base/primitives/edge/EdgeLabel.css.ts +3 -0
- package/src/base/primitives/edge/EdgeLabel.tsx +2 -1
- package/src/hooks/safeContext.tsx +3 -1
- package/src/likec4diagram/ui/controls/Controls.tsx +3 -3
- package/src/overlays/element-details/ElementDetails.tsx +2 -2
- package/src/state/DiagramActorProvider.tsx +3 -3
- package/src/state/diagram-machine.ts +48 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { nonNullable, invariant, DiagramNode, nonexhaustive, getBBoxCenter, getParallelStepsPrefix } from '@likec4/core';
|
|
1
|
+
import { nonNullable, invariant, DiagramNode, nonexhaustive, getBBoxCenter, isStepEdgeId, getParallelStepsPrefix } from '@likec4/core';
|
|
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';
|
|
@@ -32,6 +32,7 @@ const diagramMachine = setup({
|
|
|
32
32
|
"enabled: FitView": ({ context }) => context.features.enableFitView,
|
|
33
33
|
"enabled: FocusMode": ({ context }) => context.features.enableFocusMode,
|
|
34
34
|
"enabled: Readonly": ({ context }) => context.features.enableReadOnly,
|
|
35
|
+
"enabled: RelationshipDetails": ({ context }) => context.features.enableRelationshipDetails,
|
|
35
36
|
"not readonly": ({ context }) => !context.features.enableReadOnly,
|
|
36
37
|
"is dynamic view": ({ context }) => context.view.__ === "dynamic",
|
|
37
38
|
"is another view": ({ context, event }) => {
|
|
@@ -59,6 +60,10 @@ const diagramMachine = setup({
|
|
|
59
60
|
"click: node has connections": ({ context, event }) => {
|
|
60
61
|
assertEvent(event, "xyflow.nodeClick");
|
|
61
62
|
return context.xyedges.some((e) => e.source === event.node.id || e.target === event.node.id);
|
|
63
|
+
},
|
|
64
|
+
"click: selected edge": ({ event }) => {
|
|
65
|
+
assertEvent(event, "xyflow.edgeClick");
|
|
66
|
+
return event.edge.selected === true || event.edge.data.active === true;
|
|
62
67
|
}
|
|
63
68
|
},
|
|
64
69
|
actions: {
|
|
@@ -562,7 +567,27 @@ const diagramMachine = setup({
|
|
|
562
567
|
focusedNode: ({ event }) => event.nodeId
|
|
563
568
|
}),
|
|
564
569
|
target: "focused"
|
|
565
|
-
}
|
|
570
|
+
},
|
|
571
|
+
"xyflow.edgeClick": [{
|
|
572
|
+
guard: and([
|
|
573
|
+
"is dynamic view",
|
|
574
|
+
"click: selected edge"
|
|
575
|
+
]),
|
|
576
|
+
actions: raise(({ event }) => ({
|
|
577
|
+
type: "walkthrough.start",
|
|
578
|
+
stepId: event.edge.id
|
|
579
|
+
}))
|
|
580
|
+
}, {
|
|
581
|
+
guard: and([
|
|
582
|
+
"enabled: RelationshipDetails",
|
|
583
|
+
"click: selected edge"
|
|
584
|
+
]),
|
|
585
|
+
actions: sendTo(({ system }) => typedSystem(system).overlaysActorRef, ({ context, event }) => ({
|
|
586
|
+
type: "open.relationshipDetails",
|
|
587
|
+
viewId: context.view.id,
|
|
588
|
+
edgeId: event.edge.id
|
|
589
|
+
}))
|
|
590
|
+
}]
|
|
566
591
|
}
|
|
567
592
|
},
|
|
568
593
|
focused: {
|
|
@@ -575,18 +600,18 @@ const diagramMachine = setup({
|
|
|
575
600
|
spawnChild("hotkeyActorLogic", { id: "hotkey" }),
|
|
576
601
|
"xyflow:fitFocusedBounds"
|
|
577
602
|
],
|
|
578
|
-
exit: enqueueActions(({ enqueue, context }) => {
|
|
603
|
+
exit: enqueueActions(({ enqueue, context, event }) => {
|
|
579
604
|
enqueue.stopChild("hotkey");
|
|
580
605
|
if (context.viewportBeforeFocus) {
|
|
581
606
|
enqueue({ type: "xyflow:setViewport", params: { viewport: context.viewportBeforeFocus } });
|
|
582
607
|
} else {
|
|
583
608
|
enqueue({ type: "xyflow:fitDiagram" });
|
|
584
609
|
}
|
|
585
|
-
enqueue.assign(
|
|
586
|
-
...unfocusNodesEdges(
|
|
610
|
+
enqueue.assign({
|
|
611
|
+
...unfocusNodesEdges({ context}),
|
|
587
612
|
viewportBeforeFocus: null,
|
|
588
613
|
focusedNode: null
|
|
589
|
-
})
|
|
614
|
+
});
|
|
590
615
|
}),
|
|
591
616
|
on: {
|
|
592
617
|
"xyflow.nodeClick": [
|
|
@@ -681,6 +706,23 @@ const diagramMachine = setup({
|
|
|
681
706
|
enqueue("xyflow:fitFocusedBounds");
|
|
682
707
|
})
|
|
683
708
|
},
|
|
709
|
+
"xyflow.edgeClick": {
|
|
710
|
+
actions: [
|
|
711
|
+
assign(({ event, context }) => {
|
|
712
|
+
if (!isStepEdgeId(event.edge.id) || event.edge.id === context.activeWalkthrough?.stepId) {
|
|
713
|
+
return {};
|
|
714
|
+
}
|
|
715
|
+
return {
|
|
716
|
+
activeWalkthrough: {
|
|
717
|
+
stepId: event.edge.id,
|
|
718
|
+
parallelPrefix: getParallelStepsPrefix(event.edge.id)
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
}),
|
|
722
|
+
assign(updateActiveWalkthrough),
|
|
723
|
+
"xyflow:fitFocusedBounds"
|
|
724
|
+
]
|
|
725
|
+
},
|
|
684
726
|
"notations.unhighlight": {
|
|
685
727
|
actions: assign((s) => ({
|
|
686
728
|
...updateActiveWalkthrough(s)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/diagram",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
6
6
|
"homepage": "https://likec4.dev",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@dagrejs/dagre": "1.1.4",
|
|
45
45
|
"@mantine/core": "7.17.4",
|
|
46
46
|
"@mantine/hooks": "7.17.4",
|
|
47
|
-
"@nanostores/react": "0.
|
|
47
|
+
"@nanostores/react": "1.0.0",
|
|
48
48
|
"@react-hookz/web": "^25.1.1",
|
|
49
49
|
"@tabler/icons-react": "3.31.0",
|
|
50
50
|
"@xstate/react": "^5.0.3",
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
"clsx": "^2.1.1",
|
|
55
55
|
"d3-shape": "^3.2.0",
|
|
56
56
|
"fast-equals": "^5.2.2",
|
|
57
|
-
"motion": "^12.
|
|
58
|
-
"nanostores": "0.
|
|
57
|
+
"motion": "^12.7.3",
|
|
58
|
+
"nanostores": "1.0.1",
|
|
59
59
|
"react-error-boundary": "^5.0.0",
|
|
60
60
|
"react-shadow": "^20.6.0",
|
|
61
61
|
"remeda": "^2.21.2",
|
|
62
62
|
"type-fest": "^4.39.1",
|
|
63
63
|
"xstate": "^5.19.2",
|
|
64
|
-
"@likec4/core": "1.
|
|
65
|
-
"@likec4/styles": "1.
|
|
64
|
+
"@likec4/core": "1.29.0",
|
|
65
|
+
"@likec4/styles": "1.29.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"react": "^18.x || ^19.x",
|
|
@@ -84,8 +84,8 @@
|
|
|
84
84
|
"vite": "^6.2.5",
|
|
85
85
|
"vite-plugin-dts": "^4.5.3",
|
|
86
86
|
"vitest": "^3.1.1",
|
|
87
|
-
"wireit": "^0.14.
|
|
88
|
-
"@likec4/tsconfig": "1.
|
|
87
|
+
"wireit": "^0.14.12",
|
|
88
|
+
"@likec4/tsconfig": "1.29.0"
|
|
89
89
|
},
|
|
90
90
|
"scripts": {
|
|
91
91
|
"generate": "panda codegen",
|
package/src/LikeC4Diagram.tsx
CHANGED
|
@@ -15,7 +15,6 @@ import { LikeC4CustomColors } from './LikeC4CustomColors'
|
|
|
15
15
|
import { type LikeC4DiagramEventHandlers, type LikeC4DiagramProperties } from './LikeC4Diagram.props'
|
|
16
16
|
import { LikeC4DiagramXYFlow } from './likec4diagram/DiagramXYFlow'
|
|
17
17
|
import type { Types } from './likec4diagram/types'
|
|
18
|
-
import { useViewToNodesEdges } from './likec4diagram/useViewToNodesEdges'
|
|
19
18
|
import { useLikeC4Model } from './likec4model'
|
|
20
19
|
import { DiagramActorProvider } from './state/DiagramActorProvider'
|
|
21
20
|
|
|
@@ -13,6 +13,7 @@ export const edgePropsEqual = <P extends Record<string, unknown> = Base.EdgeData
|
|
|
13
13
|
) => (
|
|
14
14
|
prev.id === next.id
|
|
15
15
|
&& eq(prev.selected ?? false, next.selected ?? false)
|
|
16
|
+
&& eq(prev.animated ?? false, next.animated ?? false)
|
|
16
17
|
&& eq(prev.source, next.source)
|
|
17
18
|
&& eq(prev.sourceHandleId ?? null, next.sourceHandleId ?? null)
|
|
18
19
|
&& eq(prev.sourcePosition, next.sourcePosition)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type DiagramEdge, extractStep, isStepEdgeId } from '@likec4/core'
|
|
2
2
|
import { cx } from '@likec4/styles/css'
|
|
3
|
-
import { type BoxProps, Box
|
|
3
|
+
import { type BoxProps, Box } from '@likec4/styles/jsx'
|
|
4
|
+
import { Text } from '@mantine/core'
|
|
4
5
|
import { type PropsWithChildren } from 'react'
|
|
5
6
|
import { isTruthy } from 'remeda'
|
|
6
7
|
import type { UndefinedOnPartialDeep } from 'type-fest'
|
|
@@ -4,9 +4,11 @@ import type { DiagramActorRef } from '../state/types'
|
|
|
4
4
|
/**
|
|
5
5
|
* To improve experience with HMR, we use `createSafeContext` as a boundary for hoooks
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
const DiagramActorSafeContext = createContext<DiagramActorRef>(null as any)
|
|
8
8
|
DiagramActorSafeContext.displayName = 'DiagramActorSafeContext'
|
|
9
9
|
|
|
10
|
+
export const DiagramActorContextProvider = DiagramActorSafeContext.Provider
|
|
11
|
+
|
|
10
12
|
export const useDiagramActorRef = () => {
|
|
11
13
|
const ctx = useContext(DiagramActorSafeContext)
|
|
12
14
|
if (ctx === null) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { cx } from '@likec4/styles/css'
|
|
1
2
|
import { ActionIconGroup, Badge, Box, Group, Loader, Stack } from '@mantine/core'
|
|
2
3
|
import { IconFileSymlink, IconFocusCentered, IconMenu2 } from '@tabler/icons-react'
|
|
3
|
-
import { clsx } from 'clsx'
|
|
4
4
|
import { LayoutGroup, m } from 'motion/react'
|
|
5
5
|
import { memo } from 'react'
|
|
6
6
|
import { useDiagramEventHandlers, useEnabledFeatures } from '../../../context'
|
|
@@ -42,7 +42,7 @@ const ControlsDefaultLayout: ControlsCustomLayout = ({
|
|
|
42
42
|
translateY: -10,
|
|
43
43
|
}}
|
|
44
44
|
align="flex-start"
|
|
45
|
-
className={
|
|
45
|
+
className={cx(
|
|
46
46
|
'react-flow__panel',
|
|
47
47
|
css.panel,
|
|
48
48
|
'likec4-top-left-panel',
|
|
@@ -53,7 +53,7 @@ const ControlsDefaultLayout: ControlsCustomLayout = ({
|
|
|
53
53
|
<Stack align="flex-start" gap="xs">
|
|
54
54
|
<LayoutGroup>
|
|
55
55
|
<Group
|
|
56
|
-
className={
|
|
56
|
+
className={cx(css.navigationButtons, 'likec4-navigation-webview')}
|
|
57
57
|
gap={'xs'}>
|
|
58
58
|
{burgerMenu}
|
|
59
59
|
{navigationButtons}
|
|
@@ -24,10 +24,10 @@ export function ElementDetails({
|
|
|
24
24
|
shallowEqual,
|
|
25
25
|
)
|
|
26
26
|
return (
|
|
27
|
-
<ElementDetailsActorContext value={actorRef}>
|
|
27
|
+
<ElementDetailsActorContext.Provider value={actorRef}>
|
|
28
28
|
<ElementDetailsCard
|
|
29
29
|
onClose={onClose}
|
|
30
30
|
{...props} />
|
|
31
|
-
</ElementDetailsActorContext>
|
|
31
|
+
</ElementDetailsActorContext.Provider>
|
|
32
32
|
)
|
|
33
33
|
}
|
|
@@ -6,7 +6,7 @@ import { shallowEqual } from 'fast-equals'
|
|
|
6
6
|
import { type PropsWithChildren, useEffect } from 'react'
|
|
7
7
|
import { useDiagramEventHandlersRef } from '../context/DiagramEventHandlers'
|
|
8
8
|
import { DiagramFeatures, useEnabledFeatures } from '../context/DiagramFeatures'
|
|
9
|
-
import {
|
|
9
|
+
import { DiagramActorContextProvider } from '../hooks/safeContext'
|
|
10
10
|
import { useUpdateEffect } from '../hooks/useUpdateEffect'
|
|
11
11
|
import type { Types } from '../likec4diagram/types'
|
|
12
12
|
import { useViewToNodesEdges } from '../likec4diagram/useViewToNodesEdges'
|
|
@@ -92,10 +92,10 @@ export function DiagramActorProvider({
|
|
|
92
92
|
const toggledFeatures = useSelector(actorRef, selectToggledFeatures, shallowEqual)
|
|
93
93
|
|
|
94
94
|
return (
|
|
95
|
-
<
|
|
95
|
+
<DiagramActorContextProvider value={actorRef}>
|
|
96
96
|
<DiagramFeatures overrides={toggledFeatures}>
|
|
97
97
|
{children}
|
|
98
98
|
</DiagramFeatures>
|
|
99
|
-
</
|
|
99
|
+
</DiagramActorContextProvider>
|
|
100
100
|
)
|
|
101
101
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getBBoxCenter,
|
|
14
14
|
getParallelStepsPrefix,
|
|
15
15
|
invariant,
|
|
16
|
+
isStepEdgeId,
|
|
16
17
|
nonexhaustive,
|
|
17
18
|
nonNullable,
|
|
18
19
|
} from '@likec4/core'
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
useStoreApi,
|
|
25
26
|
} from '@xyflow/react'
|
|
26
27
|
import { type EdgeChange, type NodeChange, type Rect, type Viewport, nodeToRect } from '@xyflow/system'
|
|
27
|
-
import { clamp, first, hasAtLeast, prop
|
|
28
|
+
import { clamp, first, hasAtLeast, prop } from 'remeda'
|
|
28
29
|
import {
|
|
29
30
|
type ActorLogicFrom,
|
|
30
31
|
type ActorRef,
|
|
@@ -199,6 +200,7 @@ export const diagramMachine = setup({
|
|
|
199
200
|
'enabled: FitView': ({ context }) => context.features.enableFitView,
|
|
200
201
|
'enabled: FocusMode': ({ context }) => context.features.enableFocusMode,
|
|
201
202
|
'enabled: Readonly': ({ context }) => context.features.enableReadOnly,
|
|
203
|
+
'enabled: RelationshipDetails': ({ context }) => context.features.enableRelationshipDetails,
|
|
202
204
|
'not readonly': ({ context }) => !context.features.enableReadOnly,
|
|
203
205
|
'is dynamic view': ({ context }) => context.view.__ === 'dynamic',
|
|
204
206
|
'is another view': ({ context, event }) => {
|
|
@@ -227,6 +229,10 @@ export const diagramMachine = setup({
|
|
|
227
229
|
assertEvent(event, 'xyflow.nodeClick')
|
|
228
230
|
return context.xyedges.some(e => e.source === event.node.id || e.target === event.node.id)
|
|
229
231
|
},
|
|
232
|
+
'click: selected edge': ({ event }) => {
|
|
233
|
+
assertEvent(event, 'xyflow.edgeClick')
|
|
234
|
+
return event.edge.selected === true || event.edge.data.active === true
|
|
235
|
+
},
|
|
230
236
|
},
|
|
231
237
|
actions: {
|
|
232
238
|
'trigger:NavigateTo': (_, _params: { viewId: ViewId }) => {
|
|
@@ -767,6 +773,26 @@ export const diagramMachine = setup({
|
|
|
767
773
|
}),
|
|
768
774
|
target: 'focused',
|
|
769
775
|
},
|
|
776
|
+
'xyflow.edgeClick': [{
|
|
777
|
+
guard: and([
|
|
778
|
+
'is dynamic view',
|
|
779
|
+
'click: selected edge',
|
|
780
|
+
]) as any,
|
|
781
|
+
actions: raise(({ event }) => ({
|
|
782
|
+
type: 'walkthrough.start',
|
|
783
|
+
stepId: event.edge.id as StepEdgeId,
|
|
784
|
+
})),
|
|
785
|
+
}, {
|
|
786
|
+
guard: and([
|
|
787
|
+
'enabled: RelationshipDetails',
|
|
788
|
+
'click: selected edge',
|
|
789
|
+
]) as any,
|
|
790
|
+
actions: sendTo(({ system }) => typedSystem(system).overlaysActorRef!, ({ context, event }) => ({
|
|
791
|
+
type: 'open.relationshipDetails',
|
|
792
|
+
viewId: context.view.id,
|
|
793
|
+
edgeId: event.edge.id as EdgeId,
|
|
794
|
+
})),
|
|
795
|
+
}],
|
|
770
796
|
},
|
|
771
797
|
},
|
|
772
798
|
focused: {
|
|
@@ -779,18 +805,18 @@ export const diagramMachine = setup({
|
|
|
779
805
|
spawnChild('hotkeyActorLogic', { id: 'hotkey' }),
|
|
780
806
|
'xyflow:fitFocusedBounds',
|
|
781
807
|
],
|
|
782
|
-
exit: enqueueActions(({ enqueue, context }) => {
|
|
808
|
+
exit: enqueueActions(({ enqueue, context, event }) => {
|
|
783
809
|
enqueue.stopChild('hotkey')
|
|
784
810
|
if (context.viewportBeforeFocus) {
|
|
785
811
|
enqueue({ type: 'xyflow:setViewport', params: { viewport: context.viewportBeforeFocus } })
|
|
786
812
|
} else {
|
|
787
813
|
enqueue({ type: 'xyflow:fitDiagram' })
|
|
788
814
|
}
|
|
789
|
-
enqueue.assign(
|
|
790
|
-
...unfocusNodesEdges(
|
|
815
|
+
enqueue.assign({
|
|
816
|
+
...unfocusNodesEdges({ context, event }),
|
|
791
817
|
viewportBeforeFocus: null,
|
|
792
818
|
focusedNode: null,
|
|
793
|
-
})
|
|
819
|
+
})
|
|
794
820
|
}),
|
|
795
821
|
on: {
|
|
796
822
|
'xyflow.nodeClick': [
|
|
@@ -885,6 +911,23 @@ export const diagramMachine = setup({
|
|
|
885
911
|
enqueue('xyflow:fitFocusedBounds')
|
|
886
912
|
}),
|
|
887
913
|
},
|
|
914
|
+
'xyflow.edgeClick': {
|
|
915
|
+
actions: [
|
|
916
|
+
assign(({ event, context }) => {
|
|
917
|
+
if (!isStepEdgeId(event.edge.id) || event.edge.id === context.activeWalkthrough?.stepId) {
|
|
918
|
+
return {}
|
|
919
|
+
}
|
|
920
|
+
return {
|
|
921
|
+
activeWalkthrough: {
|
|
922
|
+
stepId: event.edge.id,
|
|
923
|
+
parallelPrefix: getParallelStepsPrefix(event.edge.id),
|
|
924
|
+
},
|
|
925
|
+
}
|
|
926
|
+
}),
|
|
927
|
+
assign(updateActiveWalkthrough),
|
|
928
|
+
'xyflow:fitFocusedBounds',
|
|
929
|
+
],
|
|
930
|
+
},
|
|
888
931
|
'notations.unhighlight': {
|
|
889
932
|
actions: assign(s => ({
|
|
890
933
|
...updateActiveWalkthrough(s),
|