@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
@@ -2,6 +2,7 @@ import {
2
2
  type BBox,
3
3
  type DiagramView,
4
4
  type EdgeId,
5
+ type ElementNotation,
5
6
  type Fqn,
6
7
  type NodeId,
7
8
  type StepEdgeId,
@@ -23,7 +24,7 @@ import {
23
24
  useStoreApi,
24
25
  } from '@xyflow/react'
25
26
  import { type EdgeChange, type NodeChange, type Rect, type Viewport, nodeToRect } from '@xyflow/system'
26
- import { clamp, first, hasAtLeast, prop } from 'remeda'
27
+ import { clamp, first, hasAtLeast, prop, reduce } from 'remeda'
27
28
  import {
28
29
  type ActorLogicFrom,
29
30
  type ActorRef,
@@ -44,7 +45,7 @@ import {
44
45
  import { MinZoom } from '../base/const'
45
46
  import { Base } from '../base/types'
46
47
  import { type EnabledFeatures, type FeatureName, AllDisabled } from '../context/DiagramFeatures'
47
- import type { OpenSourceParams } from '../LikeC4Diagram.props'
48
+ import type { OpenSourceParams, PaddingWithUnit } from '../LikeC4Diagram.props'
48
49
  import type { Types } from '../likec4diagram/types'
49
50
  import { createLayoutConstraints } from '../likec4diagram/useLayoutConstraints'
50
51
  import { overlaysActorLogic } from '../overlays/overlaysActor'
@@ -80,21 +81,25 @@ export interface NavigationHistory {
80
81
 
81
82
  export interface Input {
82
83
  view: DiagramView
83
- xynodes: Types.Node[]
84
- xyedges: Types.Edge[]
85
84
  xystore: XYStoreApi
86
85
  zoomable: boolean
87
86
  pannable: boolean
88
- fitViewPadding: number
87
+ fitViewPadding: PaddingWithUnit
88
+ nodesSelectable: boolean
89
89
  }
90
90
 
91
91
  type ToggledFeatures = Partial<EnabledFeatures>
92
92
 
93
93
  export interface Context extends Input {
94
+ xynodes: Types.Node[]
95
+ xyedges: Types.Edge[]
94
96
  features: EnabledFeatures
95
97
  // This is used to override features from props
96
98
  toggledFeatures: ToggledFeatures
97
- initialized: boolean
99
+ initialized: {
100
+ xydata: boolean
101
+ xyflow: boolean
102
+ }
98
103
  viewport: Viewport
99
104
  viewportChangedManually: boolean
100
105
  lastOnNavigate: null | {
@@ -120,7 +125,7 @@ export interface Context extends Input {
120
125
  viewportBeforeFocus: null | Viewport
121
126
  xyflow: ReactFlowInstance<Types.Node, Types.Edge> | null
122
127
 
123
- syncLayoutActorRef: ActorRef<Snapshot<unknown>, SyncLayoutEvents, AnyEventObject>
128
+ syncLayoutActorRef: null | ActorRef<Snapshot<unknown>, SyncLayoutEvents, AnyEventObject>
124
129
 
125
130
  // If Dynamic View
126
131
  activeWalkthrough: null | {
@@ -147,12 +152,12 @@ export type Events =
147
152
  | { type: 'update.nodeData'; nodeId: NodeId; data: Partial<Types.NodeData> }
148
153
  | { type: 'update.edgeData'; edgeId: EdgeId; data: Partial<Types.Edge['data']> }
149
154
  | { type: 'update.view'; view: DiagramView; xynodes: Types.Node[]; xyedges: Types.Edge[] }
150
- | { type: 'update.inputs'; inputs: Partial<Omit<Input, 'view'>> }
155
+ | { type: 'update.inputs'; inputs: Partial<Omit<Input, 'view' | 'xystore'>> }
151
156
  | { type: 'update.features'; features: EnabledFeatures }
152
157
  | { type: 'fitDiagram'; duration?: number; bounds?: BBox }
153
158
  | ({ type: 'open.source' } & OpenSourceParams)
154
159
  | { type: 'open.elementDetails'; fqn: Fqn; fromNode?: NodeId | undefined }
155
- | { type: 'open.relationshipDetails'; edgeId: EdgeId }
160
+ | { type: 'open.relationshipDetails'; params: { edgeId: EdgeId } | { source: Fqn; target: Fqn } }
156
161
  | { type: 'open.relationshipsBrowser'; fqn: Fqn }
157
162
  // | { type: 'close.overlay' }
158
163
  | { type: 'navigate.to'; viewId: ViewId; fromNode?: NodeId | undefined }
@@ -166,6 +171,8 @@ export type Events =
166
171
  | { type: 'walkthrough.start'; stepId?: StepEdgeId }
167
172
  | { type: 'walkthrough.step'; direction: 'next' | 'previous' }
168
173
  | { type: 'walkthrough.end' }
174
+ | { type: 'notations.highlight'; notation: ElementNotation; kind?: string }
175
+ | { type: 'notations.unhighlight' }
169
176
  | { type: 'toggle.feature'; feature: FeatureName; forceValue?: boolean }
170
177
 
171
178
  export type ActionArg = { context: Context; event: Events }
@@ -188,6 +195,7 @@ export const diagramMachine = setup({
188
195
  overlaysActorLogic,
189
196
  },
190
197
  guards: {
198
+ 'isReady': ({ context }) => context.initialized.xydata && context.initialized.xyflow,
191
199
  'enabled: FitView': ({ context }) => context.features.enableFitView,
192
200
  'enabled: FocusMode': ({ context }) => context.features.enableFocusMode,
193
201
  'enabled: Readonly': ({ context }) => context.features.enableReadOnly,
@@ -334,19 +342,21 @@ export const diagramMachine = setup({
334
342
  return
335
343
  }
336
344
  const constraints = createLayoutConstraints(xystore, nodesToAlign)
337
-
338
345
  const aligner = getAligner(mode)
339
346
 
340
- constraints.onMove(nodes => {
341
- aligner.computeLayout(nodes.map(({ node }) => toNodeRect(node)))
347
+ const nodes = nodesToAlign.map(id => ({
348
+ node: nonNullable(nodeLookup.get(id)),
349
+ rect: nonNullable(constraints.rects.get(id)),
350
+ }))
351
+ aligner.computeLayout(nodes.map(({ node }) => toNodeRect(node)))
342
352
 
343
- nodes.forEach(({ rect, node }) => {
344
- rect.positionAbsolute = {
345
- ...rect.positionAbsolute,
346
- ...aligner.applyPosition(toNodeRect(node)),
347
- }
348
- })
349
- })
353
+ for (const { rect, node } of nodes) {
354
+ rect.positionAbsolute = {
355
+ ...rect.positionAbsolute,
356
+ ...aligner.applyPosition(toNodeRect(node)),
357
+ }
358
+ }
359
+ constraints.updateXYFlowNodes()
350
360
  },
351
361
 
352
362
  'updateFeatures': enqueueActions(({ enqueue, system, event }) => {
@@ -378,9 +388,16 @@ export const diagramMachine = setup({
378
388
  },
379
389
  ),
380
390
 
391
+ 'startSyncLayout': assign(({ context, spawn, self }) => ({
392
+ syncLayoutActorRef: spawn('syncManualLayoutActorLogic', {
393
+ id: 'syncLayout',
394
+ input: { parent: self, viewId: context.view.id },
395
+ }),
396
+ })),
397
+
381
398
  'stopSyncLayout': enqueueActions(({ context, enqueue }) => {
382
- enqueue.sendTo(context.syncLayoutActorRef, { type: 'stop' })
383
- enqueue.stopChild(context.syncLayoutActorRef)
399
+ enqueue.sendTo(context.syncLayoutActorRef!, { type: 'stop' })
400
+ enqueue.stopChild(context.syncLayoutActorRef!)
384
401
  enqueue.assign({
385
402
  syncLayoutActorRef: null as any,
386
403
  })
@@ -426,16 +443,41 @@ export const diagramMachine = setup({
426
443
  }),
427
444
  }
428
445
  }),
446
+ 'notations.highlight': assign(({ context }, params: { notation: ElementNotation; kind?: string }) => {
447
+ const kinds = params.kind ? [params.kind] : params.notation.kinds
448
+ const xynodes = context.xynodes.map((n) => {
449
+ const node = findDiagramNode(context, n.id)
450
+ if (
451
+ node &&
452
+ node.notation === params.notation.title &&
453
+ node.shape === params.notation.shape &&
454
+ node.color === params.notation.color &&
455
+ kinds.includes(node.kind)
456
+ ) {
457
+ return Base.setDimmed(n, false)
458
+ }
459
+ return Base.setDimmed(n, 'immediate')
460
+ })
461
+ return {
462
+ xynodes,
463
+ xyedges: context.xyedges.map(Base.setDimmed('immediate')),
464
+ }
465
+ }),
429
466
  },
430
467
  }).createMachine({
431
468
  initial: 'initializing',
432
469
  context: ({ input, self, spawn }) => ({
433
470
  ...input,
471
+ xyedges: [],
472
+ xynodes: [],
434
473
  features: { ...AllDisabled },
435
474
  toggledFeatures: DiagramToggledFeaturesPersistence.read() ?? {
436
475
  enableReadOnly: true,
437
476
  },
438
- initialized: false,
477
+ initialized: {
478
+ xydata: false,
479
+ xyflow: false,
480
+ },
439
481
  viewportChangedManually: false,
440
482
  lastOnNavigate: null,
441
483
  lastClickedNode: null,
@@ -448,10 +490,7 @@ export const diagramMachine = setup({
448
490
  },
449
491
  viewport: { x: 0, y: 0, zoom: 1 },
450
492
  xyflow: null,
451
- syncLayoutActorRef: spawn('syncManualLayoutActorLogic', {
452
- id: 'syncLayout',
453
- input: { parent: self, viewId: input.view.id },
454
- }),
493
+ syncLayoutActorRef: null,
455
494
  activeWalkthrough: null,
456
495
  }),
457
496
  // entry: ({ spawn }) => spawn(layoutActor, { id: 'layout', input: { parent: self } }),
@@ -459,32 +498,54 @@ export const diagramMachine = setup({
459
498
  initializing: {
460
499
  on: {
461
500
  'xyflow.init': {
462
- actions: [
463
- assign(({ context, event }) => ({
464
- initialized: true,
465
- xyflow: event.instance,
466
- navigationHistory: {
467
- currentIndex: 0,
468
- history: [{
469
- viewId: context.view.id,
470
- fromNode: null,
471
- viewport: { ...event.instance.getViewport() },
472
- }],
473
- },
474
- })),
475
- enqueueActions(({ enqueue, check }) => {
476
- if (check('enabled: FitView')) {
477
- enqueue({
478
- type: 'xyflow:fitDiagram',
479
- params: { duration: 0 },
480
- })
481
- }
482
- }),
483
- ],
484
- target: 'ready',
501
+ actions: assign(({ context, event }) => ({
502
+ initialized: {
503
+ ...context.initialized,
504
+ xyflow: true,
505
+ },
506
+ xyflow: event.instance,
507
+ })),
508
+ target: 'isReady',
509
+ },
510
+ 'update.view': {
511
+ actions: assign(({ context, event, spawn, self }) => ({
512
+ initialized: {
513
+ ...context.initialized,
514
+ xydata: true,
515
+ },
516
+ view: event.view,
517
+ xynodes: event.xynodes,
518
+ xyedges: event.xyedges,
519
+ })),
520
+ target: 'isReady',
485
521
  },
486
522
  },
487
523
  },
524
+ 'isReady': {
525
+ always: [{
526
+ guard: 'isReady',
527
+ actions: [
528
+ {
529
+ type: 'xyflow:fitDiagram',
530
+ params: { duration: 0 },
531
+ },
532
+ 'startSyncLayout',
533
+ assign(({ context }) => ({
534
+ navigationHistory: {
535
+ currentIndex: 0,
536
+ history: [{
537
+ viewId: context.view.id,
538
+ fromNode: null,
539
+ viewport: { ...context.xyflow!.getViewport() },
540
+ }],
541
+ },
542
+ })),
543
+ ],
544
+ target: 'ready',
545
+ }, {
546
+ target: 'initializing',
547
+ }],
548
+ },
488
549
  ready: {
489
550
  initial: 'idle',
490
551
  on: {
@@ -579,8 +640,8 @@ export const diagramMachine = setup({
579
640
  'open.relationshipDetails': {
580
641
  actions: sendTo(({ system }) => typedSystem(system).overlaysActorRef!, ({ context, event }) => ({
581
642
  type: 'open.relationshipDetails',
582
- view: context.view,
583
- edgeId: event.edgeId,
643
+ viewId: context.view.id,
644
+ ...event.params,
584
645
  })),
585
646
  },
586
647
  'open.source': {
@@ -628,9 +689,21 @@ export const diagramMachine = setup({
628
689
  params: prop('event'),
629
690
  },
630
691
  },
692
+ 'notations.highlight': {
693
+ actions: {
694
+ type: 'notations.highlight',
695
+ params: prop('event'),
696
+ },
697
+ },
698
+ 'notations.unhighlight': {
699
+ actions: assign(({ context }) => ({
700
+ xynodes: context.xynodes.map(Base.setDimmed(false)),
701
+ xyedges: context.xyedges.map(Base.setDimmed(false)),
702
+ })),
703
+ },
631
704
  'saveManualLayout.*': {
632
705
  guard: 'not readonly',
633
- actions: sendTo((c) => c.context.syncLayoutActorRef, ({ event }) => {
706
+ actions: sendTo((c) => c.context.syncLayoutActorRef!, ({ event }) => {
634
707
  if (event.type === 'saveManualLayout.schedule') {
635
708
  return { type: 'sync' }
636
709
  }
@@ -756,6 +829,11 @@ export const diagramMachine = setup({
756
829
  }),
757
830
  target: 'idle',
758
831
  },
832
+ 'notations.unhighlight': {
833
+ actions: assign(s => ({
834
+ ...focusNodesEdges(s),
835
+ })),
836
+ },
759
837
  },
760
838
  },
761
839
  walkthrough: {
@@ -807,6 +885,11 @@ export const diagramMachine = setup({
807
885
  enqueue('xyflow:fitFocusedBounds')
808
886
  }),
809
887
  },
888
+ 'notations.unhighlight': {
889
+ actions: assign(s => ({
890
+ ...updateActiveWalkthrough(s),
891
+ })),
892
+ },
810
893
  'walkthrough.end': {
811
894
  target: 'idle',
812
895
  },
@@ -849,15 +932,6 @@ export const diagramMachine = setup({
849
932
  }),
850
933
  },
851
934
  ],
852
- exit: assign({
853
- syncLayoutActorRef: ({ self, context, spawn }) =>
854
- spawn('syncManualLayoutActorLogic', {
855
- id: 'syncLayout',
856
- systemId: 'syncLayout',
857
- input: { parent: self, viewId: context.view.id },
858
- }),
859
- // lastOnNavigate: null,
860
- }),
861
935
  on: {
862
936
  'update.view': {
863
937
  actions: enqueueActions(({ enqueue, context, event }) => {
@@ -882,10 +956,11 @@ export const diagramMachine = setup({
882
956
  enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
883
957
  }
884
958
  enqueue.assign(updateNavigationHistory)
885
- enqueue.assign(mergeXYNodesEdges)
886
959
  enqueue.assign({
960
+ ...mergeXYNodesEdges({ context, event }),
887
961
  lastOnNavigate: null,
888
962
  })
963
+ enqueue('startSyncLayout')
889
964
  }),
890
965
  target: '#idle',
891
966
  },
@@ -928,17 +1003,10 @@ export const diagramMachine = setup({
928
1003
  enqueueActions(({ enqueue, event, check, context }) => {
929
1004
  const isAnotherView = check('is another view')
930
1005
  if (isAnotherView) {
931
- const viewId = event.view.id
932
-
933
1006
  enqueue('closeAllOverlays')
934
1007
  enqueue('stopSyncLayout')
935
1008
  enqueue.assign({
936
1009
  focusedNode: null,
937
- syncLayoutActorRef: ({ self, spawn }) =>
938
- spawn('syncManualLayoutActorLogic', {
939
- id: 'syncLayout',
940
- input: { parent: self, viewId },
941
- }),
942
1010
  })
943
1011
  const { fromNode, toNode } = findCorrespondingNode(context, event)
944
1012
  if (fromNode && toNode) {
@@ -960,16 +1028,19 @@ export const diagramMachine = setup({
960
1028
  })
961
1029
  enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
962
1030
  }
1031
+ }
1032
+ enqueue.assign({
1033
+ ...mergeXYNodesEdges({ context, event }),
1034
+ lastOnNavigate: null,
1035
+ })
1036
+ if (isAnotherView) {
1037
+ enqueue('startSyncLayout')
963
1038
  } else {
964
- enqueue.sendTo(c => c.context.syncLayoutActorRef, { type: 'synced' })
1039
+ enqueue.sendTo(c => c.context.syncLayoutActorRef!, { type: 'synced' })
965
1040
  if (!context.viewportChangedManually) {
966
1041
  enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 50 })
967
1042
  }
968
1043
  }
969
- enqueue.assign(mergeXYNodesEdges)
970
- enqueue.assign({
971
- lastOnNavigate: null,
972
- })
973
1044
  }),
974
1045
  ],
975
1046
  },
@@ -987,7 +1058,12 @@ export const diagramMachine = setup({
987
1058
  assign({
988
1059
  xyflow: null,
989
1060
  xystore: null as any,
990
- initialized: false,
1061
+ xyedges: [],
1062
+ xynodes: [],
1063
+ initialized: {
1064
+ xydata: false,
1065
+ xyflow: false,
1066
+ },
991
1067
  syncLayoutActorRef: null as any,
992
1068
  }),
993
1069
  ],
@@ -1,19 +0,0 @@
1
- import { DiagramEdge, DiagramView, EdgeId } from '@likec4/core';
2
- import { RelationshipDetailsTypes } from './_types';
3
- export declare const ZIndexes: {
4
- compound: number;
5
- edge: number;
6
- element: number;
7
- max: number;
8
- };
9
- export declare function useLayoutedDetails(edgeId: EdgeId, view: DiagramView): {
10
- edge: DiagramEdge;
11
- xynodes: RelationshipDetailsTypes.Node[];
12
- xyedges: RelationshipDetailsTypes.Edge[];
13
- bounds: {
14
- x: number;
15
- y: number;
16
- width: number;
17
- height: number;
18
- };
19
- };
@@ -1,267 +0,0 @@
1
- import dagre from '@dagrejs/dagre';
2
- import { invariant, compareRelations, compareFqnHierarchically, isAncestor } from '@likec4/core';
3
- import { useMemo } from 'react';
4
- import { pipe, sort, reverse, forEach, takeWhile, first, map, sortBy, prop, filter, isTruthy } from 'remeda';
5
- import { useLikeC4Model } from '../../likec4model/useLikeC4Model.js';
6
-
7
- const Sizes = {
8
- dagre: {
9
- ranksep: 60,
10
- nodesep: 35,
11
- edgesep: 25
12
- },
13
- edgeLabel: {
14
- width: 120,
15
- height: 10,
16
- minlen: 1
17
- },
18
- nodeWidth: 330,
19
- hodeHeight: 180,
20
- compoundLabelHeight: 5
21
- };
22
- const ZIndexes = {
23
- compound: 0,
24
- // keep edges and elements in the same layer
25
- edge: 2,
26
- element: 2,
27
- max: 5
28
- };
29
- function createGraph() {
30
- const g = new dagre.graphlib.Graph({
31
- directed: true,
32
- compound: true
33
- });
34
- g.setGraph({
35
- ...Sizes.dagre,
36
- rankdir: "LR"
37
- });
38
- g.setDefaultEdgeLabel(() => ({ ...Sizes.edgeLabel }));
39
- g.setDefaultNodeLabel(() => ({}));
40
- return g;
41
- }
42
- const sized = (height = Sizes.hodeHeight) => ({
43
- width: Sizes.nodeWidth,
44
- height
45
- });
46
- const graphId = (node) => ({
47
- id: node.id,
48
- port: node.type === "compound" ? `${node.id}::port` : node.id
49
- });
50
- function nodeData(element, ctx) {
51
- let diagramNode = ctx.diagramNodes.get(element.id);
52
- const ancestor = diagramNode ?? pipe(
53
- element.ancestors().toArray(),
54
- map((ancestor2) => ctx.diagramNodes.get(ancestor2.id)),
55
- filter(isTruthy),
56
- first()
57
- );
58
- return {
59
- fqn: element.id,
60
- title: diagramNode?.title ?? element.title,
61
- technology: diagramNode?.technology ?? element.technology,
62
- description: diagramNode?.description ?? element.description,
63
- color: diagramNode?.color ?? ancestor?.color ?? element.color,
64
- shape: diagramNode?.shape ?? element.shape,
65
- icon: diagramNode?.icon ?? element.icon ?? "none",
66
- style: diagramNode?.style ?? element.$element.style ?? {},
67
- navigateTo: diagramNode?.navigateTo ?? first(element.scopedViews().take(1).toArray())?.id ?? null,
68
- width: 0,
69
- height: 0,
70
- depth: 1,
71
- ports: {
72
- in: [],
73
- out: []
74
- }
75
- };
76
- }
77
- function createXYNode(nodeType, element, ctx) {
78
- let node = ctx.xynodes.get(element.id);
79
- if (node) {
80
- return node;
81
- }
82
- const g = ctx.g;
83
- const parent = pipe(
84
- [...element.ancestors()],
85
- takeWhile((ancestor) => !isAncestor(ancestor.id, ctx.edge.source) && !isAncestor(ancestor.id, ctx.edge.target)),
86
- first(),
87
- (found) => found ? createXYNode("compound", found, ctx) : null
88
- );
89
- const base = {
90
- id: element.id,
91
- draggable: false,
92
- selectable: true,
93
- focusable: true,
94
- deletable: false,
95
- position: { x: 0, y: 0 },
96
- zIndex: ZIndexes[nodeType],
97
- initialWidth: 0,
98
- initialHeight: 0,
99
- ...!!parent && { parentId: parent.id }
100
- };
101
- const xynode = {
102
- ...base,
103
- type: nodeType,
104
- data: nodeData(element, ctx)
105
- };
106
- ctx.xynodes.set(element.id, xynode);
107
- const k = graphId(xynode);
108
- g.setNode(k.id, sized());
109
- if (xynode.type === "compound") {
110
- g.setNode(k.port, {
111
- width: Sizes.nodeWidth - Sizes.dagre.ranksep,
112
- height: Sizes.compoundLabelHeight
113
- });
114
- g.setParent(k.port, k.id);
115
- }
116
- if (parent) {
117
- const parentGraphId = graphId(parent).id;
118
- g.setParent(k.id, parentGraphId);
119
- }
120
- return xynode;
121
- }
122
- function applyDagreLayout(g) {
123
- dagre.layout(g);
124
- return function nodeBounds(nodeId, relativeTo) {
125
- const { x, y, width, height } = g.node(nodeId);
126
- const pos = {
127
- position: {
128
- x: x - Math.round(width / 2),
129
- y: y - Math.round(height / 2)
130
- },
131
- width,
132
- height
133
- };
134
- if (!relativeTo) {
135
- return pos;
136
- }
137
- const offset = nodeBounds(relativeTo).position;
138
- return {
139
- position: {
140
- x: pos.position.x - offset.x,
141
- y: pos.position.y - offset.y
142
- },
143
- width: pos.width,
144
- height: pos.height
145
- };
146
- };
147
- }
148
- function layout(edge, view, likec4model) {
149
- const all = /* @__PURE__ */ new Set([edge.source, edge.target]);
150
- const relations = edge.relations.map((r) => {
151
- const relation = likec4model.relationship(r);
152
- all.add(relation.source.id);
153
- all.add(relation.target.id);
154
- return {
155
- source: relation.source.id,
156
- target: relation.target.id,
157
- relation: relation.$relationship
158
- };
159
- }).sort(compareRelations).reverse();
160
- const diagramNodes = new Map(view.nodes.map((n) => [n.id, n]));
161
- const g = createGraph();
162
- const ctx = {
163
- g,
164
- xynodes: /* @__PURE__ */ new Map(),
165
- edge,
166
- diagramNodes,
167
- xyedges: []
168
- };
169
- pipe(
170
- [...all],
171
- sort(compareFqnHierarchically),
172
- reverse(),
173
- forEach((id) => {
174
- const element = likec4model.element(id);
175
- createXYNode("element", element, ctx);
176
- })
177
- );
178
- if (relations.length === 1 && all.size < 4) {
179
- g.setDefaultEdgeLabel(() => ({ width: 250 }));
180
- }
181
- for (const { relation, ...points } of relations) {
182
- const source = ctx.xynodes.get(points.source);
183
- invariant(source, "source node not found");
184
- const target = ctx.xynodes.get(points.target);
185
- invariant(target, "target node not found");
186
- source.data.ports.out.push(target.id);
187
- target.data.ports.in.push(source.id);
188
- g.setEdge(graphId(source).port, graphId(target).port);
189
- const edge2 = {
190
- id: relation.id,
191
- type: "relationship",
192
- source: source.id,
193
- target: target.id,
194
- sourceHandle: source.id + "_out" + (source.data.ports.out.length - 1),
195
- targetHandle: target.id + "_in" + (target.data.ports.in.length - 1),
196
- data: {
197
- relationId: relation.id,
198
- color: relation.color,
199
- label: relation.title ?? null,
200
- technology: relation.technology,
201
- description: relation.description,
202
- navigateTo: relation.navigateTo ?? null,
203
- line: relation.line ?? "dashed"
204
- },
205
- zIndex: ZIndexes.edge
206
- };
207
- ctx.xyedges.push(edge2);
208
- }
209
- const nodebounds = applyDagreLayout(ctx.g);
210
- const sortedPorts = (nodeId, type, ports) => {
211
- return pipe(
212
- ports,
213
- map((port, index) => {
214
- return {
215
- port: nodeId + "_" + type + index,
216
- topY: nodebounds(port).position.y
217
- };
218
- }),
219
- sortBy(prop("topY")),
220
- map(prop("port"))
221
- );
222
- };
223
- const xynodes = [...ctx.xynodes.values()].map((node) => {
224
- const { position, width, height } = nodebounds(node.id, node.parentId);
225
- node.data.ports.in = sortedPorts(node.id, "in", node.data.ports.in);
226
- node.data.ports.out = sortedPorts(node.id, "out", node.data.ports.out);
227
- if (node.type === "element") {
228
- node.data.width = width;
229
- node.data.height = height;
230
- }
231
- node.position = position;
232
- node.initialHeight = height;
233
- node.initialWidth = width;
234
- node.style = {
235
- width,
236
- height
237
- };
238
- return node;
239
- });
240
- return {
241
- edge,
242
- xyedges: ctx.xyedges,
243
- xynodes,
244
- bounds: {
245
- x: 0,
246
- y: 0,
247
- width: g.graph().width ?? 0,
248
- height: g.graph().height ?? 0
249
- }
250
- };
251
- }
252
- function useLayoutedDetails(edgeId, view) {
253
- const edge = view.edges.find((e) => e.id === edgeId);
254
- invariant(edge, `edge ${edgeId} not found in ${view.id}`);
255
- const likec4model = useLikeC4Model(true);
256
- return useMemo(() => layout(
257
- edge,
258
- view,
259
- likec4model
260
- ), [
261
- edge,
262
- view,
263
- layout
264
- ]);
265
- }
266
-
267
- export { ZIndexes, useLayoutedDetails };