@likec4/diagram 1.39.5 → 1.41.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.
Files changed (140) hide show
  1. package/bundle/{chunk-CW_KClZe.js → chunk-FGcODVxo.js} +1059 -1023
  2. package/bundle/custom.js +61 -60
  3. package/bundle/index.js +2932 -764
  4. package/dist/LikeC4Diagram.d.ts +1 -1
  5. package/dist/LikeC4Diagram.js +7 -4
  6. package/dist/LikeC4Diagram.props.d.ts +6 -1
  7. package/dist/base/const.d.ts +9 -0
  8. package/dist/base/const.js +10 -1
  9. package/dist/base/index.d.ts +1 -0
  10. package/dist/base/primitives/MarkdownBlock.js +2 -0
  11. package/dist/base/primitives/edge/EdgeLabel.js +2 -1
  12. package/dist/base/primitives/edge/EdgeLabelContainer.js +10 -4
  13. package/dist/base/primitives/edge/EdgePath.js +17 -8
  14. package/dist/base/primitives/edge/edge.css.d.ts +0 -1
  15. package/dist/base/primitives/edge/edge.css.js +6 -14
  16. package/dist/base/primitives/element/ElementActionButtons.js +1 -3
  17. package/dist/base/primitives/element/ElementNodeContainer.d.ts +0 -6
  18. package/dist/base/primitives/element/ElementNodeContainer.js +3 -26
  19. package/dist/base/primitives/element/ElementTitle.js +2 -2
  20. package/dist/base/types.d.ts +3 -3
  21. package/dist/bundle/LikeC4View.js +4 -12
  22. package/dist/bundle/LikeC4View.props.d.ts +12 -3
  23. package/dist/bundle/custom.js +1 -1
  24. package/dist/components/Link.js +28 -20
  25. package/dist/custom/customNodes.d.ts +6 -0
  26. package/dist/custom/customNodes.js +2 -1
  27. package/dist/custom/index.js +1 -1
  28. package/dist/hooks/useDiagram.d.ts +5 -1
  29. package/dist/hooks/useDiagram.js +3 -0
  30. package/dist/hooks/useXYFlow.d.ts +4 -439
  31. package/dist/likec4diagram/DiagramUI.js +1 -1
  32. package/dist/likec4diagram/DiagramXYFlow.js +8 -4
  33. package/dist/likec4diagram/convert-to-xyflow.d.ts +14 -0
  34. package/dist/likec4diagram/convert-to-xyflow.js +29 -0
  35. package/dist/likec4diagram/custom/edges/RelationshipEdge.d.ts +4 -4
  36. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +0 -1
  37. package/dist/likec4diagram/custom/edges/SequenceStepEdge.d.ts +3 -0
  38. package/dist/likec4diagram/custom/edges/SequenceStepEdge.js +71 -0
  39. package/dist/likec4diagram/custom/index.js +7 -2
  40. package/dist/likec4diagram/custom/nodes/ElementActions.d.ts +2 -1
  41. package/dist/likec4diagram/custom/nodes/SequenceActorNode.d.ts +4 -0
  42. package/dist/likec4diagram/custom/nodes/SequenceActorNode.js +157 -0
  43. package/dist/likec4diagram/custom/nodes/index.d.ts +1 -0
  44. package/dist/likec4diagram/custom/nodes/nodes.d.ts +1 -2
  45. package/dist/likec4diagram/relationship-popover/RelationshipPopover.js +31 -27
  46. package/dist/likec4diagram/types.d.ts +41 -7
  47. package/dist/likec4diagram/xyflow-diagram/diagram-view.d.ts +11 -0
  48. package/dist/likec4diagram/{useViewToNodesEdges.js → xyflow-diagram/diagram-view.js} +4 -13
  49. package/dist/likec4diagram/xyflow-sequence/_types.d.ts +41 -0
  50. package/dist/likec4diagram/xyflow-sequence/const.d.ts +17 -0
  51. package/dist/likec4diagram/xyflow-sequence/const.js +16 -0
  52. package/dist/likec4diagram/xyflow-sequence/layouter.d.ts +46 -0
  53. package/dist/likec4diagram/xyflow-sequence/layouter.js +408 -0
  54. package/dist/likec4diagram/xyflow-sequence/sequence-view.d.ts +7 -0
  55. package/dist/likec4diagram/xyflow-sequence/sequence-view.js +273 -0
  56. package/dist/likec4diagram/xyflow-sequence/utils.d.ts +14 -0
  57. package/dist/likec4diagram/xyflow-sequence/utils.js +92 -0
  58. package/dist/navigationpanel/NavigationPanelControls.js +2 -2
  59. package/dist/navigationpanel/controls/ViewDetailsCard.js +27 -12
  60. package/dist/navigationpanel/dropdown/ProjectsMenu.js +53 -44
  61. package/dist/navigationpanel/walkthrough/ActiveWalkthroughControls.d.ts +1 -2
  62. package/dist/navigationpanel/walkthrough/ActiveWalkthroughControls.js +10 -41
  63. package/dist/navigationpanel/walkthrough/DynamicViewControls.d.ts +292 -0
  64. package/dist/navigationpanel/walkthrough/DynamicViewControls.js +119 -0
  65. package/dist/navigationpanel/walkthrough/index.d.ts +2 -1
  66. package/dist/overlays/element-details/ElementDetailsCard.css.js +7 -1
  67. package/dist/overlays/element-details/ElementDetailsCard.js +17 -19
  68. package/dist/overlays/relationship-details/RelationshipDetails.js +4 -1
  69. package/dist/overlays/relationship-details/layout.d.ts +3 -2
  70. package/dist/overlays/relationship-details/layout.js +1 -2
  71. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +2 -1
  72. package/dist/overlays/relationships-browser/layout.js +1 -1
  73. package/dist/search/components/ElementsColumn.js +1 -1
  74. package/dist/state/DiagramActorProvider.d.ts +3 -2
  75. package/dist/state/DiagramActorProvider.js +40 -41
  76. package/dist/state/assign.js +11 -4
  77. package/dist/state/diagram-machine.d.ts +224 -194
  78. package/dist/state/diagram-machine.js +170 -63
  79. package/dist/state/utils.d.ts +7 -1
  80. package/dist/state/utils.js +67 -7
  81. package/dist/styles-min.css +1 -1
  82. package/dist/styles.css +1 -1
  83. package/dist/styles.css2.js +1 -1
  84. package/package.json +8 -7
  85. package/panda.buildinfo.json +45 -24
  86. package/src/LikeC4Diagram.props.ts +14 -1
  87. package/src/LikeC4Diagram.tsx +7 -4
  88. package/src/base/const.ts +12 -0
  89. package/src/base/index.ts +2 -0
  90. package/src/base/primitives/MarkdownBlock.tsx +2 -0
  91. package/src/base/primitives/edge/EdgeLabel.tsx +23 -19
  92. package/src/base/primitives/edge/EdgeLabelContainer.tsx +12 -7
  93. package/src/base/primitives/edge/EdgePath.tsx +25 -14
  94. package/src/base/primitives/edge/edge.css.ts +5 -14
  95. package/src/base/primitives/element/ElementActionButtons.tsx +1 -3
  96. package/src/base/primitives/element/ElementNodeContainer.tsx +3 -30
  97. package/src/base/primitives/element/ElementTitle.tsx +2 -3
  98. package/src/base/types.ts +7 -5
  99. package/src/bundle/LikeC4View.props.ts +14 -3
  100. package/src/bundle/LikeC4View.tsx +4 -14
  101. package/src/bundle/ReactLikeC4.tsx +1 -2
  102. package/src/components/Link.tsx +21 -18
  103. package/src/custom/customNodes.ts +7 -0
  104. package/src/hooks/useDiagram.ts +10 -0
  105. package/src/likec4diagram/DiagramUI.tsx +1 -1
  106. package/src/likec4diagram/DiagramXYFlow.tsx +10 -5
  107. package/src/likec4diagram/convert-to-xyflow.ts +45 -0
  108. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +0 -1
  109. package/src/likec4diagram/custom/edges/SequenceStepEdge.tsx +65 -0
  110. package/src/likec4diagram/custom/index.ts +7 -1
  111. package/src/likec4diagram/custom/nodes/ElementActions.tsx +4 -2
  112. package/src/likec4diagram/custom/nodes/SequenceActorNode.tsx +175 -0
  113. package/src/likec4diagram/custom/nodes/index.ts +1 -0
  114. package/src/likec4diagram/custom/nodes/nodes.tsx +1 -1
  115. package/src/likec4diagram/relationship-popover/RelationshipPopover.tsx +36 -30
  116. package/src/likec4diagram/types.ts +96 -13
  117. package/src/likec4diagram/{useViewToNodesEdges.ts → xyflow-diagram/diagram-view.ts} +7 -28
  118. package/src/likec4diagram/xyflow-sequence/_types.ts +45 -0
  119. package/src/likec4diagram/xyflow-sequence/const.ts +37 -0
  120. package/src/likec4diagram/xyflow-sequence/layouter.ts +567 -0
  121. package/src/likec4diagram/xyflow-sequence/sequence-view.ts +337 -0
  122. package/src/likec4diagram/xyflow-sequence/utils.ts +112 -0
  123. package/src/navigationpanel/NavigationPanelControls.tsx +2 -2
  124. package/src/navigationpanel/controls/ViewDetailsCard.tsx +29 -12
  125. package/src/navigationpanel/dropdown/ProjectsMenu.tsx +5 -1
  126. package/src/navigationpanel/walkthrough/ActiveWalkthroughControls.tsx +5 -40
  127. package/src/navigationpanel/walkthrough/DynamicViewControls.tsx +125 -0
  128. package/src/navigationpanel/walkthrough/index.tsx +2 -1
  129. package/src/overlays/element-details/ElementDetailsCard.css.ts +7 -1
  130. package/src/overlays/element-details/ElementDetailsCard.tsx +24 -25
  131. package/src/overlays/relationship-details/RelationshipDetails.tsx +5 -2
  132. package/src/overlays/relationship-details/layout.ts +4 -3
  133. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +2 -1
  134. package/src/overlays/relationships-browser/layout.ts +1 -1
  135. package/src/search/components/ElementsColumn.tsx +1 -1
  136. package/src/state/DiagramActorProvider.tsx +46 -51
  137. package/src/state/assign.ts +18 -5
  138. package/src/state/diagram-machine.ts +207 -75
  139. package/src/state/utils.ts +81 -7
  140. package/dist/likec4diagram/useViewToNodesEdges.d.ts +0 -12
package/src/base/types.ts CHANGED
@@ -15,11 +15,11 @@ type RFEdge<
15
15
  type ReactFlowNodeProps<
16
16
  NodeData extends Record<string, unknown> = Record<string, unknown>,
17
17
  NodeType extends string | undefined = string | undefined,
18
- > = RF.NodeProps<RFNode<NodeData, NodeType>>
18
+ > = RF.NodeProps<RF.Node<NodeData, NodeType>>
19
19
  type ReactFlowEdgeProps<
20
20
  EdgeData extends Record<string, unknown> = Record<string, unknown>,
21
21
  EdgeType extends string | undefined = string | undefined,
22
- > = RF.EdgeProps<RFEdge<EdgeData, EdgeType>>
22
+ > = RF.EdgeProps<RF.Edge<EdgeData, EdgeType>>
23
23
 
24
24
  export namespace Base {
25
25
  // 'immediate' means that the node is dimmed without delay
@@ -156,9 +156,11 @@ export type NodeProps<T extends Record<string, unknown> = {}, NodeType extends s
156
156
  /**
157
157
  * ReactFlow Custom Edge properties with BaseEdgeData at least
158
158
  */
159
- export type EdgeProps<T extends Record<string, unknown> = {}, EdgeType extends string = string> = SetRequired<
160
- Simplify<ReactFlowEdgeProps<Base.EdgeData & T, EdgeType>>,
161
- 'data'
159
+ export type EdgeProps<T extends Record<string, unknown> = {}, EdgeType extends string = string> = Simplify<
160
+ SetRequired<
161
+ ReactFlowEdgeProps<Base.EdgeData & T, EdgeType>,
162
+ 'data'
163
+ >
162
164
  >
163
165
 
164
166
  export type ReactFlowNode<Data extends Record<string, unknown>, NodeType extends string> = SetRequired<
@@ -1,5 +1,4 @@
1
1
  import type * as t from '@likec4/core/types'
2
- import type { WhereOperator } from '@likec4/core/types'
3
2
  import type { CSSProperties } from 'react'
4
3
  import type {
5
4
  CustomNodes,
@@ -109,10 +108,16 @@ export interface LikeC4ViewProps<A extends t.aux.Any = t.aux.UnknownLayouted> {
109
108
 
110
109
  /**
111
110
  * If Walkthrough for dynamic views should be enabled
112
- * @default enableFocusMode
111
+ * @default false
113
112
  */
114
113
  enableDynamicViewWalkthrough?: boolean | undefined
115
114
 
115
+ /**
116
+ * Default dynamic view display variant
117
+ * @default 'diagram'
118
+ */
119
+ dynamicViewVariant?: t.DynamicViewDisplayVariant | undefined
120
+
116
121
  /**
117
122
  * Enable popup with element details
118
123
  * @default false
@@ -145,7 +150,7 @@ export interface LikeC4ViewProps<A extends t.aux.Any = t.aux.UnknownLayouted> {
145
150
  */
146
151
  reduceGraphics?: 'auto' | boolean | undefined
147
152
 
148
- where?: WhereOperator<A> | undefined
153
+ where?: t.WhereOperator<A> | undefined
149
154
 
150
155
  /**
151
156
  * Override some react flow props
@@ -228,6 +233,12 @@ export interface LikeC4BrowserProps {
228
233
  */
229
234
  enableDynamicViewWalkthrough?: boolean | undefined
230
235
 
236
+ /**
237
+ * Default dynamic view display variant
238
+ * @default 'diagram'
239
+ */
240
+ dynamicViewVariant?: t.DynamicViewDisplayVariant | undefined
241
+
231
242
  /**
232
243
  * Enable popup with element details
233
244
  * @default true
@@ -5,9 +5,9 @@ import { shallowEqual, useCallbackRef } from '@mantine/hooks'
5
5
  import { IconX } from '@tabler/icons-react'
6
6
  import { memo, useState } from 'react'
7
7
  import { isBoolean } from 'remeda'
8
+ import { FitViewPaddings } from '../base/const'
8
9
  import { FramerMotionConfig } from '../context/FramerMotionConfig'
9
10
  import { LikeC4Diagram } from '../LikeC4Diagram'
10
- import type { ViewPadding } from '../LikeC4Diagram.props'
11
11
  import { useLikeC4Model } from '../likec4model/useLikeC4Model'
12
12
  import { Overlay } from '../overlays/overlay/Overlay'
13
13
  import type { LikeC4ViewProps } from './LikeC4View.props'
@@ -37,13 +37,6 @@ export function LikeC4View<A extends t.aux.Any = t.aux.UnknownLayouted>({
37
37
  return <LikeC4ViewInner view={view.$view} {...props} />
38
38
  }
39
39
 
40
- const DEFAULT_BROWSER_PADDING: ViewPadding = {
41
- top: '50px',
42
- bottom: '16px',
43
- left: '16px',
44
- right: '16px',
45
- }
46
-
47
40
  type LikeC4ViewInnerProps<A extends t.aux.Any> = Omit<LikeC4ViewProps<A>, 'viewId'> & {
48
41
  view: t.DiagramView<A>
49
42
  }
@@ -56,8 +49,6 @@ const LikeC4ViewInner = memo<LikeC4ViewInnerProps<t.aux.UnknownLayouted>>(({
56
49
  colorScheme: explicitColorScheme,
57
50
  injectFontCss = true,
58
51
  controls = false,
59
- fitView = true,
60
- fitViewPadding = '16px',
61
52
  background = 'transparent',
62
53
  browser = true,
63
54
  showDiagramTitle = false,
@@ -129,8 +120,8 @@ const LikeC4ViewInner = memo<LikeC4ViewInnerProps<t.aux.UnknownLayouted>>(({
129
120
  pannable={pannable}
130
121
  zoomable={zoomable}
131
122
  background={background}
132
- fitView={fitView}
133
- fitViewPadding={fitViewPadding}
123
+ fitView={true}
124
+ fitViewPadding={FitViewPaddings.default}
134
125
  showDiagramTitle={showDiagramTitle}
135
126
  showNotations={showNotations && hasNotations}
136
127
  enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
@@ -152,7 +143,6 @@ const LikeC4ViewInner = memo<LikeC4ViewInnerProps<t.aux.UnknownLayouted>>(({
152
143
  // so we don't want enable search and hotkeys
153
144
  enableSearch={false}
154
145
  {...isBrowserEnabled && {
155
- onNavigateTo: onNavigateTo,
156
146
  onCanvasClick: onNavigateToThisView,
157
147
  onNodeClick: onNavigateToThisView,
158
148
  }}
@@ -176,8 +166,8 @@ const LikeC4ViewInner = memo<LikeC4ViewInnerProps<t.aux.UnknownLayouted>>(({
176
166
  controls="next"
177
167
  readonly
178
168
  fitView
179
- fitViewPadding={DEFAULT_BROWSER_PADDING}
180
169
  {...props}
170
+ fitViewPadding={FitViewPaddings.withControls}
181
171
  {...browserProps}
182
172
  showNotations={(browserProps.showNotations ?? true) &&
183
173
  (browserView.notation?.nodes.length ?? 0) > 0}
@@ -1,5 +1,4 @@
1
1
  import type * as t from '@likec4/core/types'
2
- import type { DiagramView } from '@likec4/core/types'
3
2
  import { cx } from '@likec4/styles/css'
4
3
  import { type CSSProperties } from 'react'
5
4
  import { isFunction, isString } from 'remeda'
@@ -65,7 +64,7 @@ export function ReactLikeC4<A extends t.aux.Any = t.aux.UnknownLayouted>({
65
64
  }
66
65
 
67
66
  type ReactLikeC4InnerProps<A extends t.aux.Any> = Omit<ReactLikeC4Props<A>, 'viewId'> & {
68
- view: DiagramView<A>
67
+ view: t.DiagramView<A>
69
68
  }
70
69
  function ReactLikeC4Inner<A extends t.aux.Any>({
71
70
  className,
@@ -1,5 +1,6 @@
1
1
  import type { Link as LinkData } from '@likec4/core'
2
2
  import { css, cx } from '@likec4/styles/css'
3
+ import { styled } from '@likec4/styles/jsx'
3
4
  import { type BadgeProps, ActionIcon, Badge, CopyButton } from '@mantine/core'
4
5
  import { IconCheck, IconCopy } from '@tabler/icons-react'
5
6
  import { forwardRef } from 'react'
@@ -72,29 +73,31 @@ export const Link = forwardRef<HTMLDivElement, Omit<BadgeProps, 'children' | 'cl
72
73
  userSelect: 'none',
73
74
  pointerEvents: 'none',
74
75
  _groupHover: {
75
- color: '[inherit]',
76
+ color: '[var(--badge-color)]',
76
77
  opacity: .7,
77
78
  },
78
79
  },
79
80
  }),
80
- label: css({
81
- '& > a': {
82
- color: '[inherit]',
83
- cursor: 'pointer',
84
- transition: 'fast',
85
- opacity: {
86
- base: 0.7,
87
- _hover: 1,
88
- },
89
- textDecoration: {
90
- base: 'none',
91
- _hover: 'underline',
92
- },
93
- },
94
- }),
95
81
  }}
96
82
  >
97
- <a href={url} target="_blank">
83
+ <styled.a
84
+ href={url}
85
+ target="_blank"
86
+ style={{
87
+ color: 'var(--badge-color)',
88
+ cursor: 'pointer',
89
+ }}
90
+ css={{
91
+ transition: 'fast',
92
+ opacity: {
93
+ base: 0.7,
94
+ _hover: 1,
95
+ },
96
+ textDecoration: {
97
+ base: 'none',
98
+ _hover: 'underline',
99
+ },
100
+ }}>
98
101
  {isGithub && (
99
102
  <GithubIcon
100
103
  height="12"
@@ -102,7 +105,7 @@ export const Link = forwardRef<HTMLDivElement, Omit<BadgeProps, 'children' | 'cl
102
105
  style={{ verticalAlign: 'middle', marginRight: '4px' }} />
103
106
  )}
104
107
  {isGithub ? url.replace(GITHUB_PREFIX, '') : url}
105
- </a>
108
+ </styled.a>
106
109
  </Badge>
107
110
  )
108
111
  },
@@ -266,10 +266,17 @@ export type CustomViewGroupNodeProps = {
266
266
  nodeModel: NodeModel.IsGroup<Any>
267
267
  }
268
268
 
269
+ export const sequenceActorNode: CustomNodeComponent<CustomSequenceActorNodeProps> = customDiagramNode as any
270
+ export type CustomSequenceActorNodeProps = {
271
+ nodeProps: NodeProps<Types.SequenceActorNodeData, 'seq-actor'>
272
+ nodeModel: NodeModel.WithElement<Any>
273
+ }
274
+
269
275
  export interface CustomNodes {
270
276
  element?: FunctionComponent<NodeProps<Types.ElementNodeData, 'element'>>
271
277
  deployment?: FunctionComponent<NodeProps<Types.DeploymentElementNodeData, 'deployment'>>
272
278
  compoundElement?: FunctionComponent<NodeProps<Types.CompoundElementNodeData, 'compound-element'>>
273
279
  compoundDeployment?: FunctionComponent<NodeProps<Types.CompoundDeploymentNodeData, 'compound-deployment'>>
274
280
  viewGroup?: FunctionComponent<NodeProps<Types.ViewGroupNodeData, 'view-group'>>
281
+ sequenceActor?: FunctionComponent<NodeProps<Types.SequenceActorNodeData, 'seq-actor'>>
275
282
  }
@@ -3,6 +3,7 @@ import {
3
3
  type DiagramEdge,
4
4
  type DiagramNode,
5
5
  type DiagramView,
6
+ type DynamicViewDisplayVariant,
6
7
  type NodeId,
7
8
  type NodeNotation as ElementNotation,
8
9
  type scalar,
@@ -115,6 +116,11 @@ export interface DiagramApi {
115
116
  highlightNotation(notation: ElementNotation, kind?: string): void
116
117
  unhighlightNotation(): void
117
118
  openSearch(searchValue?: string): void
119
+
120
+ /**
121
+ * Switch dynamic view display variant
122
+ */
123
+ switchDynamicViewVariant(variant: DynamicViewDisplayVariant): void
118
124
  }
119
125
 
120
126
  /**
@@ -246,6 +252,10 @@ export function useDiagram(): DiagramApi {
246
252
  openSearch: (searchValue?: string) => {
247
253
  actor.send({ type: 'open.search', ...(searchValue && { search: searchValue }) })
248
254
  },
255
+
256
+ switchDynamicViewVariant: (variant: DynamicViewDisplayVariant) => {
257
+ actor.send({ type: 'switch.dynamicViewVariant', variant })
258
+ },
249
259
  }), [actor])
250
260
  }
251
261
 
@@ -32,7 +32,7 @@ export const DiagramUI = memo(() => {
32
32
  : <Controls />}
33
33
  {overlaysActorRef && <Overlays overlaysActorRef={overlaysActorRef} />}
34
34
  {enableNotations && <NotationPanel />}
35
- {enableControls !== 'next' && (
35
+ {enableControls === true && (
36
36
  <>
37
37
  {enableViewTitle && <DiagramTitlePanel />}
38
38
  {enableDynamicViewWalkthrough && <DynamicViewWalkthrough />}
@@ -3,7 +3,7 @@ import { cx } from '@likec4/styles/css'
3
3
  import { useCallbackRef, useDebouncedCallback, useTimeout } from '@mantine/hooks'
4
4
  import { useCustomCompareMemo } from '@react-hookz/web'
5
5
  import type { OnMove, OnMoveEnd } from '@xyflow/system'
6
- import { shallowEqual } from 'fast-equals'
6
+ import { deepEqual, shallowEqual } from 'fast-equals'
7
7
  import { type PropsWithChildren } from 'react'
8
8
  import type { Simplify } from 'type-fest'
9
9
  import { BaseXYFlow } from '../base/BaseXYFlow'
@@ -13,7 +13,7 @@ import { useUpdateEffect } from '../hooks'
13
13
  import { useDiagram, useDiagramContext } from '../hooks/useDiagram'
14
14
  import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
15
15
  import type { DiagramContext } from '../state/types'
16
- import { BuiltinNodes as defaultNodeTypes, edgeTypes } from './custom'
16
+ import { BuiltinNodes as defaultNodeTypes, edgeTypes, SequenceParallelArea } from './custom'
17
17
  import { DiagramUI } from './DiagramUI'
18
18
  import type { Types } from './types'
19
19
  import { useLayoutConstraints } from './useLayoutConstraints'
@@ -26,7 +26,9 @@ const selectXYProps = (ctx: DiagramContext) => ({
26
26
  zoomable: ctx.zoomable,
27
27
  fitViewPadding: ctx.fitViewPadding,
28
28
  enableFitView: ctx.features.enableFitView,
29
- enableReadOnly: ctx.features.enableReadOnly || ctx.toggledFeatures.enableReadOnly,
29
+ enableReadOnly: ctx.features.enableReadOnly || ctx.toggledFeatures.enableReadOnly
30
+ // if dynamic view display mode is sequence, enable readonly
31
+ || (ctx.dynamicViewVariant === 'sequence' && ctx.view._type === 'dynamic'),
30
32
  ...(!ctx.features.enableFitView && {
31
33
  viewport: {
32
34
  x: -Math.min(ctx.view.bounds.x, 0),
@@ -41,7 +43,7 @@ const equalsXYProps = (a: ReturnType<typeof selectXYProps>, b: ReturnType<typeof
41
43
  a.zoomable === b.zoomable &&
42
44
  a.enableFitView === b.enableFitView &&
43
45
  a.enableReadOnly === b.enableReadOnly &&
44
- shallowEqual(a.fitViewPadding, b.fitViewPadding) &&
46
+ deepEqual(a.fitViewPadding, b.fitViewPadding) &&
45
47
  shallowEqual(a.nodes, b.nodes) &&
46
48
  shallowEqual(a.edges, b.edges) &&
47
49
  shallowEqual(a.viewport ?? null, b.viewport ?? null)
@@ -123,7 +125,9 @@ export function LikeC4DiagramXYFlow({
123
125
  'compound-element': renderNodes?.compoundElement ?? defaultNodeTypes.compoundElement,
124
126
  'compound-deployment': renderNodes?.compoundDeployment ?? defaultNodeTypes.compoundDeployment,
125
127
  'view-group': renderNodes?.viewGroup ?? defaultNodeTypes.viewGroup,
126
- }
128
+ 'seq-actor': renderNodes?.sequenceActor ?? defaultNodeTypes.sequenceActor,
129
+ 'seq-parallel': SequenceParallelArea,
130
+ } satisfies Record<Types.Node['type'], any>
127
131
  },
128
132
  [renderNodes],
129
133
  shallowEqual,
@@ -224,6 +228,7 @@ export function LikeC4DiagramXYFlow({
224
228
  }}
225
229
  nodesDraggable={notReadOnly && nodesDraggable}
226
230
  nodesSelectable={nodesSelectable}
231
+ edgesFocusable={false}
227
232
  {...(notReadOnly && nodesDraggable && layoutConstraints)}
228
233
  {...props}
229
234
  {...reactFlowProps}>
@@ -0,0 +1,45 @@
1
+ import type { DynamicViewDisplayVariant, LayoutedView, WhereOperator } from '@likec4/core/types'
2
+ import type { Types } from './types'
3
+ import { diagramToXY } from './xyflow-diagram/diagram-view'
4
+ import { sequenceViewToXY } from './xyflow-sequence/sequence-view'
5
+
6
+ type ConvertToXYFlowInput = {
7
+ view: LayoutedView
8
+ where: WhereOperator | null
9
+ nodesSelectable: boolean
10
+ dynamicViewVariant: DynamicViewDisplayVariant
11
+ }
12
+
13
+ export function convertToXYFlow({ dynamicViewVariant, ...params }: ConvertToXYFlowInput): {
14
+ view: LayoutedView
15
+ xynodes: Types.Node[]
16
+ xyedges: Types.Edge[]
17
+ } {
18
+ const view = params.view
19
+ const isDynamic = view._type === 'dynamic'
20
+
21
+ const { bounds, xynodes, xyedges } = isDynamic && dynamicViewVariant === 'sequence'
22
+ ? sequenceViewToXY(view)
23
+ : diagramToXY(params)
24
+
25
+ if (isDynamic && view.variant !== dynamicViewVariant) {
26
+ return {
27
+ view: {
28
+ ...view,
29
+ bounds,
30
+ variant: dynamicViewVariant,
31
+ },
32
+ xynodes,
33
+ xyedges,
34
+ }
35
+ }
36
+
37
+ return {
38
+ view: bounds === view.bounds ? view : {
39
+ ...view,
40
+ bounds,
41
+ },
42
+ xynodes,
43
+ xyedges,
44
+ }
45
+ }
@@ -329,7 +329,6 @@ export const RelationshipEdge = customEdge<Types.RelationshipEdgeData>((props) =
329
329
  <EdgeLabel edgeProps={props}>
330
330
  {navigateTo && (
331
331
  <EdgeActionButton
332
- {...props}
333
332
  onClick={e => {
334
333
  e.stopPropagation()
335
334
  diagram.navigateTo(navigateTo)
@@ -0,0 +1,65 @@
1
+ import { getSmoothStepPath } from '@xyflow/system'
2
+ import type { EdgeProps } from '../../../base'
3
+ import { EdgeActionButton, EdgeContainer, EdgeLabel, EdgeLabelContainer, EdgePath } from '../../../base/primitives'
4
+ import { useDiagram } from '../../../hooks/useDiagram'
5
+ import type { Types } from '../../types'
6
+
7
+ const LABEL_OFFSET = 16
8
+ export function SequenceStepEdge(props: EdgeProps<Types.SequenceStepEdgeData>) {
9
+ const diagram = useDiagram()
10
+ const { navigateTo } = props.data
11
+ const isSelfLoop = props.source === props.target
12
+ const isBack = props.sourceX > props.targetX
13
+ const [path] = getSmoothStepPath({
14
+ sourceX: props.sourceX,
15
+ sourceY: props.sourceY,
16
+ sourcePosition: props.sourcePosition,
17
+ targetX: props.targetX,
18
+ targetY: props.targetY,
19
+ targetPosition: props.targetPosition,
20
+ ...(isSelfLoop && {
21
+ offset: 30,
22
+ borderRadius: 16,
23
+ }),
24
+ })
25
+
26
+ let labelX = props.sourceX
27
+ switch (true) {
28
+ case isSelfLoop:
29
+ labelX = props.sourceX + 24 + LABEL_OFFSET
30
+ break
31
+ case isBack:
32
+ labelX = props.sourceX - LABEL_OFFSET
33
+ break
34
+ default:
35
+ labelX = props.sourceX + LABEL_OFFSET
36
+ break
37
+ }
38
+
39
+ return (
40
+ <EdgeContainer {...props}>
41
+ <EdgePath
42
+ edgeProps={props}
43
+ svgPath={path}
44
+ />
45
+ <EdgeLabelContainer
46
+ edgeProps={props}
47
+ labelPosition={{
48
+ x: labelX,
49
+ y: props.sourceY + (!isSelfLoop ? LABEL_OFFSET : 0),
50
+ translate: isBack ? 'translate(-100%, 0)' : undefined,
51
+ }}
52
+ >
53
+ <EdgeLabel edgeProps={props}>
54
+ {navigateTo && (
55
+ <EdgeActionButton
56
+ onClick={e => {
57
+ e.stopPropagation()
58
+ diagram.navigateTo(navigateTo)
59
+ }} />
60
+ )}
61
+ </EdgeLabel>
62
+ </EdgeLabelContainer>
63
+ </EdgeContainer>
64
+ )
65
+ }
@@ -1,13 +1,15 @@
1
1
  import type { CustomNodes } from '../../custom/customNodes'
2
2
  import type { Types } from '../types'
3
3
  import { RelationshipEdge } from './edges/RelationshipEdge'
4
+ import { SequenceStepEdge } from './edges/SequenceStepEdge'
4
5
  import {
5
6
  CompoundDeploymentNode,
6
7
  CompoundElementNode,
7
8
  DeploymentNode,
8
9
  ElementNode,
10
+ SequenceActorNode,
9
11
  ViewGroupNode,
10
- } from './nodes/nodes'
12
+ } from './nodes'
11
13
 
12
14
  export const BuiltinNodes = {
13
15
  element: ElementNode,
@@ -15,8 +17,12 @@ export const BuiltinNodes = {
15
17
  compoundElement: CompoundElementNode,
16
18
  compoundDeployment: CompoundDeploymentNode,
17
19
  viewGroup: ViewGroupNode,
20
+ sequenceActor: SequenceActorNode,
18
21
  } satisfies Required<CustomNodes>
19
22
 
23
+ export { SequenceParallelArea } from './nodes/SequenceActorNode'
24
+
20
25
  export const edgeTypes = {
21
26
  relationship: RelationshipEdge,
27
+ 'seq-step': SequenceStepEdge,
22
28
  } satisfies { [key in Types.Edge['type']]: any }
@@ -1,4 +1,4 @@
1
- import type { NodeId } from '@likec4/core'
1
+ import type { NodeId } from '@likec4/core/types'
2
2
  import { IconTransform, IconZoomScan } from '@tabler/icons-react'
3
3
  import { useMemo } from 'react'
4
4
  import { hasAtLeast } from 'remeda'
@@ -8,6 +8,8 @@ import { useEnabledFeatures } from '../../../context/DiagramFeatures'
8
8
  import { useDiagram } from '../../../hooks/useDiagram'
9
9
  import type { Types } from '../../types'
10
10
 
11
+ type RequiredData = Pick<Types.ElementNodeData, 'modelFqn' | 'navigateTo'>
12
+
11
13
  type WithExtraButtons = {
12
14
  /**
13
15
  * Add extra action buttons
@@ -31,7 +33,7 @@ type WithExtraButtons = {
31
33
  extraButtons?: ElementActionButtons.Item[]
32
34
  }
33
35
 
34
- export type ElementActionsProps = NodeProps<Types.ElementNodeData> & WithExtraButtons
36
+ export type ElementActionsProps = NodeProps<RequiredData> & WithExtraButtons
35
37
 
36
38
  export const ElementActions = ({
37
39
  extraButtons,
@@ -0,0 +1,175 @@
1
+ import { defaultTheme } from '@likec4/core'
2
+ import { css } from '@likec4/styles/css'
3
+ import { Box } from '@likec4/styles/jsx'
4
+ import type { NodeProps } from '@xyflow/react'
5
+ import { Handle } from '@xyflow/react'
6
+ import { Position } from '@xyflow/system'
7
+ import { ElementNodeContainer, ElementShape, ElementTitle } from '../../../custom'
8
+ import { type Types } from '../../types'
9
+ import { ElementActions } from './ElementActions'
10
+ import { ElementDetailsButtonWithHandler } from './nodes'
11
+
12
+ const positionMap: Record<Types.SequenceActorNodePort['position'], Position> = {
13
+ left: Position.Left,
14
+ right: Position.Right,
15
+ top: Position.Top,
16
+ bottom: Position.Bottom,
17
+ }
18
+
19
+ const ActorStepPort = ({
20
+ data,
21
+ port: p,
22
+ }: {
23
+ data: Types.SequenceActorNodeData
24
+ port: Types.SequenceActorNodePort
25
+ }) => {
26
+ return (
27
+ <>
28
+ <Box
29
+ data-likec4-color={data.color}
30
+ className={css({
31
+ position: 'absolute',
32
+ backgroundColor: 'var(--likec4-palette-fill)',
33
+ rounded: 'xs',
34
+ width: {
35
+ base: '5px',
36
+ _whenHovered: '7px',
37
+ _whenSelected: '7px',
38
+ },
39
+ // pointerEvents: 'none',
40
+ transition: 'fast',
41
+ translateX: '-1/2',
42
+ translateY: '-1/2',
43
+ translate: 'auto',
44
+ // transform: 'translate',
45
+ })}
46
+ style={{
47
+ top: p.cy,
48
+ left: p.cx,
49
+ height: p.height,
50
+ // transform: p.type === 'source' ? 'translate(-50%, -16px)' : 'translate(-50%, -50%)',
51
+ // transform: 'translate(-50%, -50%)',
52
+ // zIndex: p.type === 'source' ? 1 : 0,
53
+ }}
54
+ />
55
+ <Handle
56
+ id={p.id}
57
+ type={p.type}
58
+ position={positionMap[p.position]}
59
+ style={{
60
+ top: p.cy - 3,
61
+ left: p.cx - 3,
62
+ width: 6,
63
+ height: 6,
64
+ right: 'unset',
65
+ bottom: 'unset',
66
+ visibility: 'hidden',
67
+ transform: p.position === 'left' ? 'translate(-150%, 0)' : 'translate(100%, 0)',
68
+ }} />
69
+ </>
70
+ )
71
+ }
72
+
73
+ export function SequenceActorNode(props: NodeProps<Types.SequenceActorNode>) {
74
+ const data = props.data
75
+ const {
76
+ positionAbsoluteY,
77
+ data: {
78
+ viewHeight,
79
+ hovered: isHovered = false,
80
+ ports,
81
+ },
82
+ } = props
83
+
84
+ return (
85
+ <>
86
+ <Box
87
+ className={css({
88
+ position: 'absolute',
89
+ rounded: 'xs',
90
+ top: '1',
91
+ pointerEvents: 'none',
92
+ transition: 'fast',
93
+ translateX: '-1/2',
94
+ translate: 'auto',
95
+ // transform: 'translate',
96
+ })}
97
+ style={{
98
+ // position: 'absolute',
99
+ backgroundColor: defaultTheme.elements.gray.fill,
100
+ opacity: isHovered ? 0.5 : 0.4,
101
+ left: '50%',
102
+ width: isHovered ? 3 : 2,
103
+ height: viewHeight - positionAbsoluteY,
104
+ zIndex: -1,
105
+ pointerEvents: 'none',
106
+ // top: p.y,
107
+ // left: p.width / 2,
108
+ // height: p.type === 'source' ? 48 : 32,
109
+ // transform: p.type === 'source' ? 'translate(-50%, -16px)' : 'translate(-50%, -50%)',
110
+ // transform: 'translate(-50%, -50%)',
111
+ // zIndex: p.type === 'source' ? 1 : 0,
112
+ }}
113
+ />
114
+ {
115
+ /* <div
116
+ // css={{
117
+ // backgroundColor: 'var(--likec4-palette-fill)',
118
+ // }}
119
+ style={{
120
+ position: 'absolute',
121
+ backgroundColor: defaultTheme.elements.gray.fill,
122
+ opacity: 0.5,
123
+ top: 8,
124
+ left: 'calc(50% - 1px)',
125
+ width: 2,
126
+ height: viewHeight - positionAbsoluteY,
127
+ zIndex: -1,
128
+ pointerEvents: 'none',
129
+ }}>
130
+ </div> */
131
+ }
132
+ <ElementNodeContainer nodeProps={props}>
133
+ <ElementShape {...props} />
134
+ <ElementTitle {...props} />
135
+ {data.modelFqn && (
136
+ <>
137
+ <ElementActions {...props} data={data as Types.ElementNodeData} />
138
+ <ElementDetailsButtonWithHandler {...props} data={data as Types.ElementNodeData} />
139
+ </>
140
+ )}
141
+ </ElementNodeContainer>
142
+ {ports.map(p => <ActorStepPort key={p.id} port={p} data={props.data} />)}
143
+ </>
144
+ )
145
+ }
146
+
147
+ export function SequenceParallelArea(props: NodeProps<Types.SequenceActorNode>) {
148
+ return (
149
+ <Box
150
+ data-likec4-color={props.data.color}
151
+ css={{
152
+ width: '100%',
153
+ height: '100%',
154
+ border: 'default',
155
+ rounded: 'sm',
156
+ borderWidth: 1,
157
+ '--_color': {
158
+ base: 'var(--likec4-palette-stroke)',
159
+ _dark: '[color-mix(in srgb, var(--likec4-palette-hiContrast) 40%, var(--likec4-palette-fill))]',
160
+ },
161
+ borderColor: '[var(--_color)/30]',
162
+ backgroundColor: 'var(--likec4-palette-fill)/15',
163
+ pointerEvents: 'none',
164
+ paddingLeft: '2',
165
+ paddingTop: '0.5',
166
+ fontSize: 'xs',
167
+ fontWeight: 'bold',
168
+ letterSpacing: '.75px',
169
+ color: '[var(--_color)/75]',
170
+ }}
171
+ >
172
+ PARALLEL
173
+ </Box>
174
+ )
175
+ }