@likec4/diagram 1.27.0 → 1.27.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 (57) hide show
  1. package/bundle/index.js +327 -281
  2. package/dist/LikeC4Diagram.js +2 -8
  3. package/dist/base/primitives/compound/CompoundNodeContainer.js +1 -1
  4. package/dist/base/primitives/edge/EdgeLabel.css.js +1 -4
  5. package/dist/base/primitives/edge/EdgeLabelContainer.js +1 -1
  6. package/dist/base/primitives/edge/EdgePath.js +0 -3
  7. package/dist/base/primitives/edge/edge.css.js +2 -0
  8. package/dist/base/primitives/element/ElementNodeContainer.css.js +1 -0
  9. package/dist/base/primitives/element/ElementShape.css.js +1 -12
  10. package/dist/base/primitives/element/ElementTitle.css.js +3 -2
  11. package/dist/base/types.js +2 -2
  12. package/dist/components/Link.js +1 -1
  13. package/dist/hooks/useDiagram.d.ts +320 -490
  14. package/dist/likec4diagram/DiagramXYFlow.js +10 -9
  15. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +30 -41
  16. package/dist/likec4diagram/custom/nodes/toolbar/Toolbar.js +1 -1
  17. package/dist/likec4diagram/custom/nodes/toolbar/styles.css.js +6 -1
  18. package/dist/likec4diagram/ui/controls/LayoutDriftNotification.js +13 -5
  19. package/dist/likec4diagram/useLayoutConstraints.d.ts +11 -10
  20. package/dist/likec4diagram/useLayoutConstraints.js +64 -61
  21. package/dist/overlays/Overlays.js +2 -2
  22. package/dist/overlays/overlay/Overlay.css.js +31 -12
  23. package/dist/overlays/overlay/Overlay.js +30 -27
  24. package/dist/overlays/relationship-details/useLayoutedDetails.js +1 -1
  25. package/dist/overlays/relationships-browser/SelectElement.js +4 -4
  26. package/dist/state/DiagramActorProvider.d.ts +4 -2
  27. package/dist/state/DiagramActorProvider.js +30 -37
  28. package/dist/state/assign.js +2 -2
  29. package/dist/state/diagram-machine.d.ts +450 -608
  30. package/dist/state/diagram-machine.js +87 -57
  31. package/dist/styles.css.js +1 -1
  32. package/package.json +5 -5
  33. package/src/LikeC4Diagram.tsx +1 -7
  34. package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -1
  35. package/src/base/primitives/edge/EdgeLabel.css.ts +1 -4
  36. package/src/base/primitives/edge/EdgeLabelContainer.tsx +1 -1
  37. package/src/base/primitives/edge/EdgePath.tsx +0 -3
  38. package/src/base/primitives/edge/edge.css.ts +2 -0
  39. package/src/base/primitives/element/ElementNodeContainer.css.ts +1 -0
  40. package/src/base/primitives/element/ElementShape.css.ts +1 -12
  41. package/src/base/primitives/element/ElementTitle.css.ts +3 -2
  42. package/src/base/types.ts +2 -7
  43. package/src/components/Link.tsx +3 -1
  44. package/src/likec4diagram/DiagramXYFlow.tsx +11 -9
  45. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +39 -48
  46. package/src/likec4diagram/custom/nodes/toolbar/Toolbar.tsx +2 -2
  47. package/src/likec4diagram/custom/nodes/toolbar/styles.css.ts +5 -0
  48. package/src/likec4diagram/ui/controls/LayoutDriftNotification.tsx +8 -4
  49. package/src/likec4diagram/useLayoutConstraints.ts +84 -89
  50. package/src/overlays/Overlays.tsx +2 -2
  51. package/src/overlays/overlay/Overlay.css.ts +30 -12
  52. package/src/overlays/overlay/Overlay.tsx +35 -29
  53. package/src/overlays/relationship-details/useLayoutedDetails.ts +1 -1
  54. package/src/overlays/relationships-browser/SelectElement.tsx +5 -5
  55. package/src/state/DiagramActorProvider.tsx +40 -50
  56. package/src/state/assign.ts +2 -2
  57. package/src/state/diagram-machine.ts +100 -69
@@ -80,21 +80,25 @@ export interface NavigationHistory {
80
80
 
81
81
  export interface Input {
82
82
  view: DiagramView
83
- xynodes: Types.Node[]
84
- xyedges: Types.Edge[]
85
83
  xystore: XYStoreApi
86
84
  zoomable: boolean
87
85
  pannable: boolean
88
86
  fitViewPadding: number
87
+ nodesSelectable: boolean
89
88
  }
90
89
 
91
90
  type ToggledFeatures = Partial<EnabledFeatures>
92
91
 
93
92
  export interface Context extends Input {
93
+ xynodes: Types.Node[]
94
+ xyedges: Types.Edge[]
94
95
  features: EnabledFeatures
95
96
  // This is used to override features from props
96
97
  toggledFeatures: ToggledFeatures
97
- initialized: boolean
98
+ initialized: {
99
+ xydata: boolean
100
+ xyflow: boolean
101
+ }
98
102
  viewport: Viewport
99
103
  viewportChangedManually: boolean
100
104
  lastOnNavigate: null | {
@@ -120,7 +124,7 @@ export interface Context extends Input {
120
124
  viewportBeforeFocus: null | Viewport
121
125
  xyflow: ReactFlowInstance<Types.Node, Types.Edge> | null
122
126
 
123
- syncLayoutActorRef: ActorRef<Snapshot<unknown>, SyncLayoutEvents, AnyEventObject>
127
+ syncLayoutActorRef: null | ActorRef<Snapshot<unknown>, SyncLayoutEvents, AnyEventObject>
124
128
 
125
129
  // If Dynamic View
126
130
  activeWalkthrough: null | {
@@ -147,7 +151,7 @@ export type Events =
147
151
  | { type: 'update.nodeData'; nodeId: NodeId; data: Partial<Types.NodeData> }
148
152
  | { type: 'update.edgeData'; edgeId: EdgeId; data: Partial<Types.Edge['data']> }
149
153
  | { type: 'update.view'; view: DiagramView; xynodes: Types.Node[]; xyedges: Types.Edge[] }
150
- | { type: 'update.inputs'; inputs: Partial<Omit<Input, 'view'>> }
154
+ | { type: 'update.inputs'; inputs: Partial<Omit<Input, 'view' | 'xystore'>> }
151
155
  | { type: 'update.features'; features: EnabledFeatures }
152
156
  | { type: 'fitDiagram'; duration?: number; bounds?: BBox }
153
157
  | ({ type: 'open.source' } & OpenSourceParams)
@@ -188,6 +192,7 @@ export const diagramMachine = setup({
188
192
  overlaysActorLogic,
189
193
  },
190
194
  guards: {
195
+ 'isReady': ({ context }) => context.initialized.xydata && context.initialized.xyflow,
191
196
  'enabled: FitView': ({ context }) => context.features.enableFitView,
192
197
  'enabled: FocusMode': ({ context }) => context.features.enableFocusMode,
193
198
  'enabled: Readonly': ({ context }) => context.features.enableReadOnly,
@@ -334,19 +339,21 @@ export const diagramMachine = setup({
334
339
  return
335
340
  }
336
341
  const constraints = createLayoutConstraints(xystore, nodesToAlign)
337
-
338
342
  const aligner = getAligner(mode)
339
343
 
340
- constraints.onMove(nodes => {
341
- aligner.computeLayout(nodes.map(({ node }) => toNodeRect(node)))
344
+ const nodes = nodesToAlign.map(id => ({
345
+ node: nonNullable(nodeLookup.get(id)),
346
+ rect: nonNullable(constraints.rects.get(id)),
347
+ }))
348
+ aligner.computeLayout(nodes.map(({ node }) => toNodeRect(node)))
342
349
 
343
- nodes.forEach(({ rect, node }) => {
344
- rect.positionAbsolute = {
345
- ...rect.positionAbsolute,
346
- ...aligner.applyPosition(toNodeRect(node)),
347
- }
348
- })
349
- })
350
+ for (const { rect, node } of nodes) {
351
+ rect.positionAbsolute = {
352
+ ...rect.positionAbsolute,
353
+ ...aligner.applyPosition(toNodeRect(node)),
354
+ }
355
+ }
356
+ constraints.updateXYFlowNodes()
350
357
  },
351
358
 
352
359
  'updateFeatures': enqueueActions(({ enqueue, system, event }) => {
@@ -378,9 +385,16 @@ export const diagramMachine = setup({
378
385
  },
379
386
  ),
380
387
 
388
+ 'startSyncLayout': assign(({ context, spawn, self }) => ({
389
+ syncLayoutActorRef: spawn('syncManualLayoutActorLogic', {
390
+ id: 'syncLayout',
391
+ input: { parent: self, viewId: context.view.id },
392
+ }),
393
+ })),
394
+
381
395
  'stopSyncLayout': enqueueActions(({ context, enqueue }) => {
382
- enqueue.sendTo(context.syncLayoutActorRef, { type: 'stop' })
383
- enqueue.stopChild(context.syncLayoutActorRef)
396
+ enqueue.sendTo(context.syncLayoutActorRef!, { type: 'stop' })
397
+ enqueue.stopChild(context.syncLayoutActorRef!)
384
398
  enqueue.assign({
385
399
  syncLayoutActorRef: null as any,
386
400
  })
@@ -431,11 +445,16 @@ export const diagramMachine = setup({
431
445
  initial: 'initializing',
432
446
  context: ({ input, self, spawn }) => ({
433
447
  ...input,
448
+ xyedges: [],
449
+ xynodes: [],
434
450
  features: { ...AllDisabled },
435
451
  toggledFeatures: DiagramToggledFeaturesPersistence.read() ?? {
436
452
  enableReadOnly: true,
437
453
  },
438
- initialized: false,
454
+ initialized: {
455
+ xydata: false,
456
+ xyflow: false,
457
+ },
439
458
  viewportChangedManually: false,
440
459
  lastOnNavigate: null,
441
460
  lastClickedNode: null,
@@ -448,10 +467,7 @@ export const diagramMachine = setup({
448
467
  },
449
468
  viewport: { x: 0, y: 0, zoom: 1 },
450
469
  xyflow: null,
451
- syncLayoutActorRef: spawn('syncManualLayoutActorLogic', {
452
- id: 'syncLayout',
453
- input: { parent: self, viewId: input.view.id },
454
- }),
470
+ syncLayoutActorRef: null,
455
471
  activeWalkthrough: null,
456
472
  }),
457
473
  // entry: ({ spawn }) => spawn(layoutActor, { id: 'layout', input: { parent: self } }),
@@ -459,32 +475,54 @@ export const diagramMachine = setup({
459
475
  initializing: {
460
476
  on: {
461
477
  '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',
478
+ actions: assign(({ context, event }) => ({
479
+ initialized: {
480
+ ...context.initialized,
481
+ xyflow: true,
482
+ },
483
+ xyflow: event.instance,
484
+ })),
485
+ target: 'isReady',
486
+ },
487
+ 'update.view': {
488
+ actions: assign(({ context, event, spawn, self }) => ({
489
+ initialized: {
490
+ ...context.initialized,
491
+ xydata: true,
492
+ },
493
+ view: event.view,
494
+ xynodes: event.xynodes,
495
+ xyedges: event.xyedges,
496
+ })),
497
+ target: 'isReady',
485
498
  },
486
499
  },
487
500
  },
501
+ 'isReady': {
502
+ always: [{
503
+ guard: 'isReady',
504
+ actions: [
505
+ {
506
+ type: 'xyflow:fitDiagram',
507
+ params: { duration: 0 },
508
+ },
509
+ 'startSyncLayout',
510
+ assign(({ context }) => ({
511
+ navigationHistory: {
512
+ currentIndex: 0,
513
+ history: [{
514
+ viewId: context.view.id,
515
+ fromNode: null,
516
+ viewport: { ...context.xyflow!.getViewport() },
517
+ }],
518
+ },
519
+ })),
520
+ ],
521
+ target: 'ready',
522
+ }, {
523
+ target: 'initializing',
524
+ }],
525
+ },
488
526
  ready: {
489
527
  initial: 'idle',
490
528
  on: {
@@ -630,7 +668,7 @@ export const diagramMachine = setup({
630
668
  },
631
669
  'saveManualLayout.*': {
632
670
  guard: 'not readonly',
633
- actions: sendTo((c) => c.context.syncLayoutActorRef, ({ event }) => {
671
+ actions: sendTo((c) => c.context.syncLayoutActorRef!, ({ event }) => {
634
672
  if (event.type === 'saveManualLayout.schedule') {
635
673
  return { type: 'sync' }
636
674
  }
@@ -849,15 +887,6 @@ export const diagramMachine = setup({
849
887
  }),
850
888
  },
851
889
  ],
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
890
  on: {
862
891
  'update.view': {
863
892
  actions: enqueueActions(({ enqueue, context, event }) => {
@@ -882,10 +911,11 @@ export const diagramMachine = setup({
882
911
  enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
883
912
  }
884
913
  enqueue.assign(updateNavigationHistory)
885
- enqueue.assign(mergeXYNodesEdges)
886
914
  enqueue.assign({
915
+ ...mergeXYNodesEdges({ context, event }),
887
916
  lastOnNavigate: null,
888
917
  })
918
+ enqueue('startSyncLayout')
889
919
  }),
890
920
  target: '#idle',
891
921
  },
@@ -928,17 +958,10 @@ export const diagramMachine = setup({
928
958
  enqueueActions(({ enqueue, event, check, context }) => {
929
959
  const isAnotherView = check('is another view')
930
960
  if (isAnotherView) {
931
- const viewId = event.view.id
932
-
933
961
  enqueue('closeAllOverlays')
934
962
  enqueue('stopSyncLayout')
935
963
  enqueue.assign({
936
964
  focusedNode: null,
937
- syncLayoutActorRef: ({ self, spawn }) =>
938
- spawn('syncManualLayoutActorLogic', {
939
- id: 'syncLayout',
940
- input: { parent: self, viewId },
941
- }),
942
965
  })
943
966
  const { fromNode, toNode } = findCorrespondingNode(context, event)
944
967
  if (fromNode && toNode) {
@@ -960,16 +983,19 @@ export const diagramMachine = setup({
960
983
  })
961
984
  enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
962
985
  }
986
+ }
987
+ enqueue.assign({
988
+ ...mergeXYNodesEdges({ context, event }),
989
+ lastOnNavigate: null,
990
+ })
991
+ if (isAnotherView) {
992
+ enqueue('startSyncLayout')
963
993
  } else {
964
- enqueue.sendTo(c => c.context.syncLayoutActorRef, { type: 'synced' })
994
+ enqueue.sendTo(c => c.context.syncLayoutActorRef!, { type: 'synced' })
965
995
  if (!context.viewportChangedManually) {
966
996
  enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 50 })
967
997
  }
968
998
  }
969
- enqueue.assign(mergeXYNodesEdges)
970
- enqueue.assign({
971
- lastOnNavigate: null,
972
- })
973
999
  }),
974
1000
  ],
975
1001
  },
@@ -987,7 +1013,12 @@ export const diagramMachine = setup({
987
1013
  assign({
988
1014
  xyflow: null,
989
1015
  xystore: null as any,
990
- initialized: false,
1016
+ xyedges: [],
1017
+ xynodes: [],
1018
+ initialized: {
1019
+ xydata: false,
1020
+ xyflow: false,
1021
+ },
991
1022
  syncLayoutActorRef: null as any,
992
1023
  }),
993
1024
  ],