@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.
Files changed (116) hide show
  1. package/dist/LikeC4Diagram.css.d.ts +5 -0
  2. package/dist/LikeC4Diagram.css.js +1 -0
  3. package/dist/LikeC4Diagram.d.ts +1 -1
  4. package/dist/LikeC4Diagram.js +6 -2
  5. package/dist/LikeC4Diagram.props.d.ts +6 -0
  6. package/dist/base/BaseXYFlow.d.ts +1 -1
  7. package/dist/base/BaseXYFlow.js +12 -3
  8. package/dist/base/const.d.ts +1 -1
  9. package/dist/base/const.js +1 -1
  10. package/dist/base/primitives/compound/CompoundActionButton.css.js +2 -0
  11. package/dist/base/primitives/compound/CompoundDetailsButton.css.js +2 -0
  12. package/dist/base/primitives/compound/CompoundNodeContainer.css.d.ts +0 -1
  13. package/dist/base/primitives/compound/CompoundNodeContainer.css.js +4 -4
  14. package/dist/base/primitives/compound/CompoundNodeContainer.js +35 -22
  15. package/dist/base/primitives/compound/CompoundTitle.css.js +3 -1
  16. package/dist/base/primitives/customNode.js +1 -1
  17. package/dist/base/primitives/edge/EdgeContainer.js +5 -4
  18. package/dist/base/primitives/edge/EdgeLabel.css.d.ts +0 -1
  19. package/dist/base/primitives/edge/EdgeLabel.css.js +2 -5
  20. package/dist/base/primitives/edge/EdgeLabel.js +5 -5
  21. package/dist/base/primitives/edge/edge.css.d.ts +1 -1
  22. package/dist/base/primitives/edge/edge.css.js +4 -5
  23. package/dist/base/primitives/element/ElementActionButtons.css.js +2 -0
  24. package/dist/base/primitives/element/ElementActionButtons.js +4 -3
  25. package/dist/base/primitives/element/ElementDetailsButton.css.js +2 -0
  26. package/dist/base/primitives/element/ElementNodeContainer.css.js +2 -0
  27. package/dist/base/primitives/element/ElementNodeContainer.js +3 -1
  28. package/dist/base/primitives/element/ElementShape.css.js +4 -2
  29. package/dist/base/primitives/element/ElementShape.js +2 -3
  30. package/dist/base/primitives/element/ElementTitle.css.js +2 -0
  31. package/dist/base/primitives/element/styles.css.js +2 -0
  32. package/dist/components/SearchControl.css.js +2 -0
  33. package/dist/context/FramerMotionConfig.d.ts +9 -1
  34. package/dist/context/FramerMotionConfig.js +5 -2
  35. package/dist/context/RootContainer.js +13 -1
  36. package/dist/diagram.css +1 -1
  37. package/dist/globals.css.d.ts +2 -0
  38. package/dist/hooks/safeContext.js +1 -0
  39. package/dist/hooks/useDiagram.d.ts +554 -864
  40. package/dist/hooks/useDiagram.js +1 -0
  41. package/dist/hooks/useIsReducedGraphics.d.ts +4 -0
  42. package/dist/hooks/useIsReducedGraphics.js +8 -0
  43. package/dist/hooks/useXYFlow.js +1 -1
  44. package/dist/likec4diagram/DiagramUI.d.ts +2 -1
  45. package/dist/likec4diagram/DiagramUI.js +5 -3
  46. package/dist/likec4diagram/custom/nodes/nodeTypes.js +2 -2
  47. package/dist/likec4diagram/ui/controls/styles.css.js +2 -0
  48. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.css.js +2 -0
  49. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +3 -4
  50. package/dist/likec4diagram/ui/search/LikeC4Search.css.js +2 -0
  51. package/dist/overlays/element-details/ElementDetailsCard.js +12 -8
  52. package/dist/overlays/element-details/actor.d.ts +9084 -2569
  53. package/dist/overlays/overlay/Overlay.js +6 -3
  54. package/dist/overlays/relationship-details/RelationshipDetails.js +31 -17
  55. package/dist/overlays/relationship-details/actor.d.ts +228 -3
  56. package/dist/overlays/relationship-details/actor.js +86 -14
  57. package/dist/overlays/relationship-details/hooks.d.ts +3 -0
  58. package/dist/overlays/relationship-details/hooks.js +6 -2
  59. package/dist/overlays/relationships-browser/-useRelationshipsView.js +12 -3
  60. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +12 -3
  61. package/dist/overlays/relationships-browser/actor.d.ts +271 -54
  62. package/dist/overlays/relationships-browser/actor.js +86 -23
  63. package/dist/state/DiagramActorProvider.js +12 -2
  64. package/dist/state/diagram-machine.d.ts +563 -867
  65. package/dist/state/diagram-machine.js +82 -81
  66. package/dist/state/persistence.d.ts +5 -0
  67. package/dist/state/persistence.js +23 -0
  68. package/package.json +9 -9
  69. package/src/LikeC4Diagram.css.ts +13 -5
  70. package/src/LikeC4Diagram.props.ts +7 -0
  71. package/src/LikeC4Diagram.tsx +33 -24
  72. package/src/base/BaseXYFlow.tsx +14 -9
  73. package/src/base/const.ts +1 -1
  74. package/src/base/primitives/compound/CompoundActionButton.css.ts +6 -0
  75. package/src/base/primitives/compound/CompoundDetailsButton.css.ts +4 -0
  76. package/src/base/primitives/compound/CompoundNodeContainer.css.ts +41 -59
  77. package/src/base/primitives/compound/CompoundNodeContainer.tsx +42 -22
  78. package/src/base/primitives/compound/CompoundTitle.css.ts +14 -13
  79. package/src/base/primitives/customNode.ts +3 -2
  80. package/src/base/primitives/edge/EdgeContainer.tsx +4 -3
  81. package/src/base/primitives/edge/EdgeLabel.css.ts +17 -11
  82. package/src/base/primitives/edge/EdgeLabel.tsx +4 -4
  83. package/src/base/primitives/edge/edge.css.ts +47 -27
  84. package/src/base/primitives/element/ElementActionButtons.css.ts +4 -0
  85. package/src/base/primitives/element/ElementActionButtons.tsx +4 -2
  86. package/src/base/primitives/element/ElementDetailsButton.css.ts +6 -8
  87. package/src/base/primitives/element/ElementNodeContainer.css.ts +18 -21
  88. package/src/base/primitives/element/ElementNodeContainer.tsx +3 -1
  89. package/src/base/primitives/element/ElementShape.css.ts +10 -6
  90. package/src/base/primitives/element/ElementShape.tsx +2 -2
  91. package/src/components/SearchControl.css.ts +11 -4
  92. package/src/context/FramerMotionConfig.tsx +13 -2
  93. package/src/context/RootContainer.tsx +9 -2
  94. package/src/globals.css.ts +3 -0
  95. package/src/hooks/safeContext.tsx +2 -1
  96. package/src/hooks/useDiagram.ts +1 -0
  97. package/src/hooks/useIsReducedGraphics.ts +5 -0
  98. package/src/hooks/useXYFlow.ts +1 -1
  99. package/src/likec4diagram/DiagramUI.tsx +5 -3
  100. package/src/likec4diagram/custom/nodes/nodeTypes.tsx +2 -2
  101. package/src/likec4diagram/ui/controls/styles.css.ts +11 -1
  102. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.css.ts +10 -3
  103. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +6 -3
  104. package/src/likec4diagram/ui/search/LikeC4Search.css.ts +1 -0
  105. package/src/overlays/element-details/ElementDetailsCard.tsx +238 -231
  106. package/src/overlays/overlay/Overlay.css.ts +1 -9
  107. package/src/overlays/overlay/Overlay.tsx +6 -3
  108. package/src/overlays/relationship-details/RelationshipDetails.tsx +83 -66
  109. package/src/overlays/relationship-details/actor.ts +106 -13
  110. package/src/overlays/relationship-details/hooks.ts +6 -2
  111. package/src/overlays/relationships-browser/-useRelationshipsView.ts +10 -0
  112. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +16 -8
  113. package/src/overlays/relationships-browser/actor.ts +92 -22
  114. package/src/state/DiagramActorProvider.tsx +13 -2
  115. package/src/state/diagram-machine.ts +80 -79
  116. package/src/state/persistence.ts +22 -0
@@ -62,6 +62,7 @@ import {
62
62
  updateNodeData,
63
63
  } from './assign'
64
64
  import { type HotKeyEvent, hotkeyActorLogic } from './hotkeyActor'
65
+ import { DiagramToggledFeaturesPersistence } from './persistence'
65
66
  import { type Events as SyncLayoutEvents, syncManualLayoutActorLogic } from './syncManualLayoutActor'
66
67
  import { findDiagramNode, focusedBounds, typedSystem } from './utils'
67
68
 
@@ -197,7 +198,7 @@ export const diagramMachine = setup({
197
198
  }
198
199
  nonexhaustive(event.type)
199
200
  },
200
- 'click: selected node ': ({ event }) => {
201
+ 'click: selected node': ({ event }) => {
201
202
  assertEvent(event, 'xyflow.nodeClick')
202
203
  return event.node.selected === true
203
204
  },
@@ -209,6 +210,10 @@ export const diagramMachine = setup({
209
210
  assertEvent(event, 'xyflow.nodeClick')
210
211
  return context.focusedNode === event.node.id
211
212
  },
213
+ 'click: node has connections': ({ context, event }) => {
214
+ assertEvent(event, 'xyflow.nodeClick')
215
+ return context.xyedges.some(e => e.source === event.node.id || e.target === event.node.id)
216
+ },
212
217
  },
213
218
  actions: {
214
219
  'trigger:NavigateTo': (_, _params: { viewId: ViewId }) => {
@@ -220,22 +225,23 @@ export const diagramMachine = setup({
220
225
  'trigger:OpenSource': (_, _params: OpenSourceParams) => {
221
226
  },
222
227
 
223
- 'open source of focused node': enqueueActions(({ context, enqueue }) => {
224
- if (!context.focusedNode || !context.features.enableVscode) return
225
- const diagramNode = findDiagramNode(context, context.focusedNode)
228
+ 'assign lastClickedNode': assign(({ context, event }) => {
229
+ assertEvent(event, 'xyflow.nodeClick')
230
+ return {
231
+ lastClickedNode: lastClickedNode({ context, event }),
232
+ }
233
+ }),
234
+
235
+ 'open source of focused or last clicked node': enqueueActions(({ context, enqueue }) => {
236
+ const nodeId = context.focusedNode ?? context.lastClickedNode?.id
237
+ if (!nodeId || !context.features.enableVscode) return
238
+ const diagramNode = findDiagramNode(context, nodeId)
226
239
  if (!diagramNode) return
227
240
 
228
- enqueue.cancel('openSource')
229
241
  if (DiagramNode.deploymentRef(diagramNode)) {
230
- enqueue.raise({ type: 'open.source', deployment: DiagramNode.deploymentRef(diagramNode)! }, {
231
- id: 'openSource',
232
- delay: 100,
233
- })
242
+ enqueue.raise({ type: 'open.source', deployment: DiagramNode.deploymentRef(diagramNode)! })
234
243
  } else if (DiagramNode.modelRef(diagramNode)) {
235
- enqueue.raise({ type: 'open.source', element: DiagramNode.modelRef(diagramNode)! }, {
236
- id: 'openSource',
237
- delay: 100,
238
- })
244
+ enqueue.raise({ type: 'open.source', element: DiagramNode.modelRef(diagramNode)! })
239
245
  }
240
246
  }),
241
247
 
@@ -359,7 +365,7 @@ export const diagramMachine = setup({
359
365
  context: ({ input, self, spawn }) => ({
360
366
  ...input,
361
367
  features: { ...AllDisabled },
362
- toggledFeatures: {
368
+ toggledFeatures: DiagramToggledFeaturesPersistence.read() ?? {
363
369
  enableReadOnly: true,
364
370
  },
365
371
  initialized: false,
@@ -417,10 +423,11 @@ export const diagramMachine = setup({
417
423
  {
418
424
  // TODO: xstate fails to infer the type of the guard
419
425
  guard: and([
420
- 'enabled: FocusMode' as const,
426
+ 'enabled: FocusMode',
427
+ 'click: node has connections',
421
428
  or([
422
- 'click: same node' as const,
423
- 'click: selected node ' as const,
429
+ 'click: same node',
430
+ 'click: selected node',
424
431
  ]),
425
432
  ]) as any,
426
433
  actions: [
@@ -432,9 +439,10 @@ export const diagramMachine = setup({
432
439
  target: 'focused',
433
440
  },
434
441
  {
435
- actions: assign({
436
- lastClickedNode,
437
- }),
442
+ actions: [
443
+ 'assign lastClickedNode',
444
+ 'open source of focused or last clicked node',
445
+ ],
438
446
  },
439
447
  ],
440
448
  'xyflow.paneClick': {
@@ -477,7 +485,7 @@ export const diagramMachine = setup({
477
485
  ...focusNodesEdges(s),
478
486
  viewportBeforeFocus: { ...s.context.viewport },
479
487
  })),
480
- 'open source of focused node',
488
+ 'open source of focused or last clicked node',
481
489
  spawnChild('hotkeyActorLogic', { id: 'hotkey' }),
482
490
  {
483
491
  type: 'xyflow:fitDiagram',
@@ -510,7 +518,7 @@ export const diagramMachine = setup({
510
518
  focusedNode: ({ event }) => event.node.id as NodeId,
511
519
  }),
512
520
  assign(focusNodesEdges),
513
- 'open source of focused node',
521
+ 'open source of focused or last clicked node',
514
522
  {
515
523
  type: 'xyflow:fitDiagram',
516
524
  params: focusedBounds,
@@ -524,7 +532,7 @@ export const diagramMachine = setup({
524
532
  focusedNode: ({ event }) => event.nodeId,
525
533
  }),
526
534
  assign(focusNodesEdges),
527
- 'open source of focused node',
535
+ 'open source of focused or last clicked node',
528
536
  {
529
537
  type: 'xyflow:fitDiagram',
530
538
  params: focusedBounds,
@@ -556,57 +564,16 @@ export const diagramMachine = setup({
556
564
  },
557
565
  // Navigating to another view (after `navigateTo` event)
558
566
  navigating: {
559
- initial: 'pending',
560
- type: 'compound',
561
- states: {
562
- pending: {
563
- entry: enqueueActions(({ enqueue }) => {
564
- enqueue('closeAllOverlays')
565
- enqueue('stopSyncLayout')
566
- enqueue({
567
- type: 'trigger:NavigateTo',
568
- params: ({ context }) => ({
569
- viewId: nonNullable(context.lastOnNavigate, 'Invalid state, lastOnNavigate is null').toView,
570
- }),
571
- })
567
+ entry: [
568
+ 'closeAllOverlays',
569
+ 'stopSyncLayout',
570
+ {
571
+ type: 'trigger:NavigateTo',
572
+ params: ({ context }) => ({
573
+ viewId: nonNullable(context.lastOnNavigate, 'Invalid state, lastOnNavigate is null').toView,
572
574
  }),
573
- on: {
574
- 'update.view': {
575
- actions: enqueueActions(({ enqueue, context, event }) => {
576
- const { fromNode, toNode } = findCorrespondingNode(context, event)
577
- if (fromNode && toNode) {
578
- enqueue({
579
- type: 'xyflow:alignNodeFromToAfterNavigate',
580
- params: {
581
- fromNode: fromNode.id,
582
- toPosition: {
583
- x: toNode.position[0],
584
- y: toNode.position[1],
585
- },
586
- },
587
- })
588
- enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 80 })
589
- } else {
590
- enqueue({
591
- type: 'xyflow:setViewportCenter',
592
- params: getBBoxCenter(event.view.bounds),
593
- })
594
- enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
595
- }
596
- enqueue.assign(updateNavigationHistory)
597
- enqueue.assign(mergeXYNodesEdges)
598
- }),
599
- target: 'done',
600
- },
601
- },
602
- },
603
- done: {
604
- type: 'final',
605
575
  },
606
- },
607
- onDone: {
608
- target: 'idle',
609
- },
576
+ ],
610
577
  exit: assign({
611
578
  syncLayoutActorRef: ({ self, context, spawn }) =>
612
579
  spawn('syncManualLayoutActorLogic', {
@@ -614,12 +581,44 @@ export const diagramMachine = setup({
614
581
  systemId: 'syncLayout',
615
582
  input: { parent: self, viewId: context.view.id },
616
583
  }),
617
- lastOnNavigate: null,
584
+ // lastOnNavigate: null,
618
585
  }),
586
+ on: {
587
+ 'update.view': {
588
+ actions: enqueueActions(({ enqueue, context, event }) => {
589
+ const { fromNode, toNode } = findCorrespondingNode(context, event)
590
+ if (fromNode && toNode) {
591
+ enqueue({
592
+ type: 'xyflow:alignNodeFromToAfterNavigate',
593
+ params: {
594
+ fromNode: fromNode.id,
595
+ toPosition: {
596
+ x: toNode.position[0],
597
+ y: toNode.position[1],
598
+ },
599
+ },
600
+ })
601
+ enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 80 })
602
+ } else {
603
+ enqueue({
604
+ type: 'xyflow:setViewportCenter',
605
+ params: getBBoxCenter(event.view.bounds),
606
+ })
607
+ enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
608
+ }
609
+ enqueue.assign(updateNavigationHistory)
610
+ enqueue.assign(mergeXYNodesEdges)
611
+ enqueue.assign({
612
+ lastOnNavigate: null,
613
+ })
614
+ }),
615
+ target: '#idle',
616
+ },
617
+ },
619
618
  },
620
619
  walkthrough: {
621
620
  entry: [
622
- // spawnChild('hotkeyActor', { id: 'hotkey' }),
621
+ spawnChild('hotkeyActorLogic', { id: 'hotkey' }),
623
622
  assign({
624
623
  viewportBeforeFocus: ({ context }) => context.viewport,
625
624
  activeWalkthrough: ({ context, event }) => {
@@ -885,11 +884,13 @@ export const diagramMachine = setup({
885
884
  },
886
885
  'toggle.feature': {
887
886
  actions: assign({
888
- toggledFeatures: ({ context, event }) => ({
889
- ...context.toggledFeatures,
890
- [`enable${event.feature}`]: event.forceValue ??
891
- !(context.toggledFeatures[`enable${event.feature}`] ?? context.features[`enable${event.feature}`]),
892
- }),
887
+ toggledFeatures: ({ context, event }) => {
888
+ return DiagramToggledFeaturesPersistence.write({
889
+ ...context.toggledFeatures,
890
+ [`enable${event.feature}`]: event.forceValue ??
891
+ !(context.toggledFeatures[`enable${event.feature}`] ?? context.features[`enable${event.feature}`]),
892
+ })
893
+ },
893
894
  }),
894
895
  },
895
896
  },
@@ -0,0 +1,22 @@
1
+ import { isBoolean, pickBy } from 'remeda'
2
+ import type { EnabledFeatures } from '../context/DiagramFeatures'
3
+
4
+ const key = `likec4:diagram:toggledFeatures`
5
+ export const DiagramToggledFeaturesPersistence = {
6
+ read() {
7
+ try {
8
+ let fromStorage = sessionStorage.getItem(key)
9
+ if (fromStorage) {
10
+ return JSON.parse(fromStorage) as Partial<EnabledFeatures>
11
+ }
12
+ throw new Error(`Workspace ${key} not found`)
13
+ } catch (e) {
14
+ console.error(`Error reading fromStorage ${key}:`, e)
15
+ return null
16
+ }
17
+ },
18
+ write<F extends Partial<EnabledFeatures>>(toggledFeatures: F): F {
19
+ sessionStorage.setItem(key, JSON.stringify(pickBy(toggledFeatures, isBoolean)))
20
+ return toggledFeatures
21
+ },
22
+ }