@likec4/diagram 1.9.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +18 -41
- package/src/LikeC4CustomColors.tsx +1 -1
- package/src/LikeC4Diagram.css.ts +1 -1
- package/src/LikeC4Diagram.props.ts +40 -94
- package/src/LikeC4Diagram.tsx +12 -2
- package/src/LikeC4ModelProvider.tsx +49 -0
- package/src/StaticLikeC4Diagram.tsx +1 -0
- package/src/globals.css.ts +2 -3
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useMantinePortalProps.ts +8 -0
- package/src/hooks/useXYFlow.ts +1 -2
- package/src/index.ts +3 -0
- package/src/likec4model/LikeC4ModelContext.ts +4 -0
- package/src/likec4model/index.ts +1 -0
- package/src/likec4model/useLikeC4Model.ts +55 -0
- package/src/state/diagram-to-xyflow.ts +1 -2
- package/src/state/diagramStore.ts +185 -66
- package/src/{theme.css.ts → theme-vars.ts} +11 -4
- package/src/ui/DiagramTitlePanel.css.ts +2 -3
- package/src/ui/DiagramTitlePanel.tsx +17 -1
- package/src/ui/DynamicViewWalkthrough.css.ts +28 -2
- package/src/ui/DynamicViewWalkthrough.tsx +97 -52
- package/src/ui/OptionsPanel.css.ts +1 -2
- package/src/ui/OptionsPanel.tsx +1 -1
- package/src/ui/notation/NotationPanel.css.ts +2 -3
- package/src/ui/notation/NotationPanel.tsx +9 -10
- package/src/ui/options/NodeOptions.tsx +1 -1
- package/src/ui/top-left/ChangeAutoLayoutButton.tsx +20 -32
- package/src/ui/top-left/TopLeftPanel.tsx +128 -117
- package/src/ui/top-left/_shared.ts +7 -6
- package/src/ui/top-left/styles.css.ts +17 -8
- package/src/utils/vector.ts +57 -0
- package/src/xyflow/FitviewOnDiagramChange.tsx +5 -4
- package/src/xyflow/SelectEdgesOnNodeFocus.tsx +7 -5
- package/src/xyflow/XYFlow.tsx +9 -11
- package/src/xyflow/XYFlowEvents.tsx +59 -60
- package/src/xyflow/XYFlowInner.tsx +5 -5
- package/src/xyflow/edges/EdgeLabel.tsx +185 -0
- package/src/xyflow/edges/RelationshipEdge.tsx +170 -137
- package/src/xyflow/edges/RelationshipsDropdownMenu.css.ts +60 -0
- package/src/xyflow/edges/RelationshipsDropdownMenu.tsx +214 -0
- package/src/xyflow/edges/edges.css.ts +75 -7
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +1 -2
- package/src/xyflow/nodes/compound/CompoundNode.tsx +4 -4
- package/src/xyflow/nodes/element/ElementLink.css.ts +1 -1
- package/src/xyflow/nodes/element/ElementLink.tsx +3 -8
- package/src/xyflow/nodes/element/ElementNode.tsx +44 -0
- package/src/xyflow/nodes/element/element.css.ts +1 -2
- package/src/xyflow/types.ts +1 -24
- package/src/xyflow/useLayoutConstraints.ts +10 -6
- package/dist/LikeC4CustomColors.d.ts +0 -3
- package/dist/LikeC4CustomColors.props.d.ts +0 -5
- package/dist/LikeC4Diagram.css.d.ts +0 -4
- package/dist/LikeC4Diagram.d.ts +0 -7
- package/dist/LikeC4Diagram.props.d.ts +0 -154
- package/dist/StaticLikeC4Diagram.d.ts +0 -4
- package/dist/globals.css.d.ts +0 -1
- package/dist/hooks/index.d.ts +0 -4
- package/dist/hooks/useDiagramState.d.ts +0 -6
- package/dist/hooks/useSetState.d.ts +0 -3
- package/dist/hooks/useUpdateEffect.d.ts +0 -5
- package/dist/hooks/useXYFlow.d.ts +0 -267
- package/dist/index.d.ts +0 -9
- package/dist/index.mjs +0 -3684
- package/dist/mantine/EnsureMantine.d.ts +0 -8
- package/dist/mantine.css.d.ts +0 -3
- package/dist/state/DiagramContext.d.ts +0 -1192
- package/dist/state/diagram-to-xyflow.d.ts +0 -12
- package/dist/state/diagramStore.d.ts +0 -1268
- package/dist/style.css +0 -2139
- package/dist/theme.css.d.ts +0 -63
- package/dist/ui/DiagramTitlePanel.css.d.ts +0 -4
- package/dist/ui/DiagramTitlePanel.d.ts +0 -1
- package/dist/ui/DynamicViewWalkthrough.css.d.ts +0 -3
- package/dist/ui/DynamicViewWalkthrough.d.ts +0 -1
- package/dist/ui/OptionsPanel.css.d.ts +0 -1
- package/dist/ui/OptionsPanel.d.ts +0 -1
- package/dist/ui/notation/NotationPanel.css.d.ts +0 -6
- package/dist/ui/notation/NotationPanel.d.ts +0 -1
- package/dist/ui/notation/index.d.ts +0 -1
- package/dist/ui/options/EdgeOptions.d.ts +0 -19
- package/dist/ui/options/NodeOptions.d.ts +0 -19
- package/dist/ui/top-left/ChangeAutoLayoutButton.d.ts +0 -3
- package/dist/ui/top-left/TopLeftPanel.d.ts +0 -4
- package/dist/ui/top-left/_shared.d.ts +0 -5
- package/dist/ui/top-left/styles.css.d.ts +0 -3
- package/dist/xyflow/EdgeMarkers.d.ts +0 -12
- package/dist/xyflow/FitviewOnDiagramChange.d.ts +0 -4
- package/dist/xyflow/SelectEdgesOnNodeFocus.d.ts +0 -1
- package/dist/xyflow/XYFlow.d.ts +0 -12
- package/dist/xyflow/XYFlowBackground.d.ts +0 -11
- package/dist/xyflow/XYFlowEvents.d.ts +0 -161
- package/dist/xyflow/XYFlowInner.d.ts +0 -10
- package/dist/xyflow/const.d.ts +0 -7
- package/dist/xyflow/edges/RelationshipEdge.d.ts +0 -32
- package/dist/xyflow/edges/edges.css.d.ts +0 -12
- package/dist/xyflow/edges/utils.d.ts +0 -17
- package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +0 -11
- package/dist/xyflow/nodes/compound/CompoundNode.d.ts +0 -6
- package/dist/xyflow/nodes/compound/index.d.ts +0 -1
- package/dist/xyflow/nodes/element/ElementLink.css.d.ts +0 -3
- package/dist/xyflow/nodes/element/ElementLink.d.ts +0 -7
- package/dist/xyflow/nodes/element/ElementNode.d.ts +0 -6
- package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +0 -18
- package/dist/xyflow/nodes/element/element.css.d.ts +0 -19
- package/dist/xyflow/nodes/element/index.d.ts +0 -1
- package/dist/xyflow/nodes/shared/NavigateToBtn.d.ts +0 -5
- package/dist/xyflow/types.d.ts +0 -50
- package/dist/xyflow/useLayoutConstraints.d.ts +0 -9
- package/dist/xyflow/utils.d.ts +0 -18
- package/src/mantine.css.ts +0 -7
- package/src/reset.d.ts +0 -2
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
DiagramNode,
|
|
3
3
|
DiagramView,
|
|
4
|
+
EdgeId,
|
|
4
5
|
ElementKind,
|
|
5
6
|
ElementNotation,
|
|
6
7
|
Fqn,
|
|
7
8
|
NodeId,
|
|
8
9
|
ViewChange,
|
|
9
|
-
ViewID
|
|
10
|
+
ViewID,
|
|
11
|
+
XYPoint
|
|
12
|
+
} from '@likec4/core'
|
|
13
|
+
import {
|
|
14
|
+
getBBoxCenter,
|
|
15
|
+
getParallelStepsPrefix,
|
|
16
|
+
invariant,
|
|
17
|
+
isStepEdgeId,
|
|
18
|
+
nonexhaustive,
|
|
19
|
+
nonNullable,
|
|
20
|
+
StepEdgeId
|
|
10
21
|
} from '@likec4/core'
|
|
11
|
-
import { getBBoxCenter, invariant, nonexhaustive, nonNullable, StepEdgeId } from '@likec4/core'
|
|
12
22
|
import {
|
|
13
23
|
applyEdgeChanges,
|
|
14
24
|
applyNodeChanges,
|
|
@@ -20,7 +30,7 @@ import { boxToRect, getBoundsOfRects, getNodeDimensions } from '@xyflow/system'
|
|
|
20
30
|
import { DEV } from 'esm-env'
|
|
21
31
|
import { deepEqual as eq, shallowEqual } from 'fast-equals'
|
|
22
32
|
import type { MouseEvent as ReactMouseEvent } from 'react'
|
|
23
|
-
import { entries, hasAtLeast, isNullish, map, prop, reduce } from 'remeda'
|
|
33
|
+
import { entries, first, hasAtLeast, isNullish, map, prop, reduce } from 'remeda'
|
|
24
34
|
import type { ConditionalKeys, Except, RequiredKeysOf, Simplify } from 'type-fest'
|
|
25
35
|
import { devtools, subscribeWithSelector } from 'zustand/middleware'
|
|
26
36
|
import { shallow } from 'zustand/shallow'
|
|
@@ -32,6 +42,7 @@ import type {
|
|
|
32
42
|
LikeC4DiagramEventHandlers,
|
|
33
43
|
WhereOperator
|
|
34
44
|
} from '../LikeC4Diagram.props'
|
|
45
|
+
import { type Vector, vector } from '../utils/vector'
|
|
35
46
|
import { MinZoom } from '../xyflow/const'
|
|
36
47
|
import type { XYFlowEdge, XYFlowInstance, XYFlowNode } from '../xyflow/types'
|
|
37
48
|
import { bezierControlPoints, isInside, isSamePoint, toDomPrecision } from '../xyflow/utils'
|
|
@@ -47,6 +58,7 @@ export type DiagramInitialState = {
|
|
|
47
58
|
showElementLinks: boolean
|
|
48
59
|
showNavigationButtons: boolean
|
|
49
60
|
showNotations: boolean
|
|
61
|
+
showRelationshipDetails: boolean
|
|
50
62
|
fitViewEnabled: boolean
|
|
51
63
|
fitViewPadding: number
|
|
52
64
|
zoomable: boolean
|
|
@@ -81,9 +93,17 @@ export type DiagramState = Simplify<
|
|
|
81
93
|
|
|
82
94
|
initialized: boolean
|
|
83
95
|
|
|
96
|
+
// readonly=false and onChange is not null
|
|
97
|
+
isEditable: () => boolean
|
|
98
|
+
|
|
84
99
|
// If Dynamic View
|
|
85
100
|
isDynamicView: boolean
|
|
86
|
-
|
|
101
|
+
activeWalkthrough: null | {
|
|
102
|
+
stepId: StepEdgeId
|
|
103
|
+
hasPrevious: boolean
|
|
104
|
+
hasNext: boolean
|
|
105
|
+
parallelPrefix: string | null
|
|
106
|
+
}
|
|
87
107
|
|
|
88
108
|
// This is XYFlow id's
|
|
89
109
|
lastClickedNodeId: string | null
|
|
@@ -109,7 +129,11 @@ export type DiagramState = Simplify<
|
|
|
109
129
|
// Actions
|
|
110
130
|
updateView: (view: DiagramView) => void
|
|
111
131
|
|
|
112
|
-
|
|
132
|
+
/**
|
|
133
|
+
* A function to set the focus on a specific node given its identifier.
|
|
134
|
+
* It can also be used to remove focus if the input is `false`.
|
|
135
|
+
*/
|
|
136
|
+
focusOnNode: (nodeId: string | false) => void
|
|
113
137
|
|
|
114
138
|
setHoveredNode: (nodeId: string | null) => void
|
|
115
139
|
setHoveredEdge: (edgeId: string | null) => void
|
|
@@ -117,7 +141,7 @@ export type DiagramState = Simplify<
|
|
|
117
141
|
setLastClickedNode: (nodeId: string | null) => void
|
|
118
142
|
setLastClickedEdge: (edgeId: string | null) => void
|
|
119
143
|
|
|
120
|
-
|
|
144
|
+
resetFocusAndLastClicked: () => void
|
|
121
145
|
|
|
122
146
|
getElement(id: Fqn): DiagramNode | null
|
|
123
147
|
triggerChangeElementStyle: (change: ViewChange.ChangeElementStyle) => void
|
|
@@ -136,14 +160,16 @@ export type DiagramState = Simplify<
|
|
|
136
160
|
goForward: () => void
|
|
137
161
|
|
|
138
162
|
nextDynamicStep: (increment?: number) => void
|
|
139
|
-
|
|
140
|
-
|
|
163
|
+
activateWalkthrough: (step: EdgeId | XYFlowEdge) => void
|
|
164
|
+
stopWalkthrough: () => void
|
|
141
165
|
|
|
142
166
|
onInit: (xyflow: XYFlowInstance) => void
|
|
143
167
|
onNodesChange: OnNodesChange<XYFlowNode>
|
|
144
168
|
onEdgesChange: OnEdgesChange<XYFlowEdge>
|
|
145
169
|
|
|
146
170
|
highlightByElementNotation: (notation: ElementNotation, onlyOfKind?: ElementKind) => void
|
|
171
|
+
|
|
172
|
+
resetEdgeControlPoints: () => void
|
|
147
173
|
}
|
|
148
174
|
>
|
|
149
175
|
|
|
@@ -158,7 +184,7 @@ const DEFAULT_PROPS: Except<
|
|
|
158
184
|
initialized: false,
|
|
159
185
|
navigationHistoryIndex: 0,
|
|
160
186
|
viewportChanged: false,
|
|
161
|
-
|
|
187
|
+
activeWalkthrough: null,
|
|
162
188
|
focusedNodeId: null,
|
|
163
189
|
hoveredNodeId: null,
|
|
164
190
|
hoveredEdgeId: null,
|
|
@@ -231,7 +257,7 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
231
257
|
focusedNodeId,
|
|
232
258
|
lastClickedNodeId,
|
|
233
259
|
lastClickedEdgeId,
|
|
234
|
-
|
|
260
|
+
activeWalkthrough,
|
|
235
261
|
nodesDraggable,
|
|
236
262
|
nodesSelectable,
|
|
237
263
|
hoveredEdgeId,
|
|
@@ -259,6 +285,7 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
259
285
|
}
|
|
260
286
|
if (focusedNodeId && !nodeIds.has(focusedNodeId)) {
|
|
261
287
|
focusedNodeId = null
|
|
288
|
+
dimmed = EmptyStringSet
|
|
262
289
|
}
|
|
263
290
|
if (lastClickedEdgeId && !edgeIds.has(lastClickedEdgeId)) {
|
|
264
291
|
lastClickedEdgeId = null
|
|
@@ -266,8 +293,9 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
266
293
|
if (hoveredEdgeId && !edgeIds.has(hoveredEdgeId)) {
|
|
267
294
|
hoveredEdgeId = null
|
|
268
295
|
}
|
|
269
|
-
if (
|
|
270
|
-
|
|
296
|
+
if (activeWalkthrough && !edgeIds.has(activeWalkthrough.stepId)) {
|
|
297
|
+
activeWalkthrough = null
|
|
298
|
+
dimmed = EmptyStringSet
|
|
271
299
|
}
|
|
272
300
|
if (dimmed.size > 0) {
|
|
273
301
|
let nextDimmed = new StringSet([...dimmed].filter(id => nodeIds.has(id) || edgeIds.has(id)))
|
|
@@ -335,7 +363,7 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
335
363
|
hoveredEdgeId = null
|
|
336
364
|
hoveredNodeId = null
|
|
337
365
|
focusedNodeId = null
|
|
338
|
-
|
|
366
|
+
activeWalkthrough = null
|
|
339
367
|
dimmed = EmptyStringSet
|
|
340
368
|
}
|
|
341
369
|
|
|
@@ -400,7 +428,7 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
400
428
|
isDynamicView: nextView.__ === 'dynamic',
|
|
401
429
|
viewSyncDebounceTimeout,
|
|
402
430
|
view: nextView,
|
|
403
|
-
|
|
431
|
+
activeWalkthrough,
|
|
404
432
|
lastOnNavigate,
|
|
405
433
|
lastClickedNodeId,
|
|
406
434
|
lastClickedEdgeId,
|
|
@@ -421,6 +449,18 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
421
449
|
focusOnNode: (nodeId) => {
|
|
422
450
|
const { focusedNodeId, view, enableFocusMode } = get()
|
|
423
451
|
invariant(enableFocusMode, 'focus mode is not enabled')
|
|
452
|
+
if (nodeId === false) {
|
|
453
|
+
set(
|
|
454
|
+
{
|
|
455
|
+
activeWalkthrough: null,
|
|
456
|
+
focusedNodeId: null,
|
|
457
|
+
dimmed: EmptyStringSet
|
|
458
|
+
},
|
|
459
|
+
noReplace,
|
|
460
|
+
`unfocus`
|
|
461
|
+
)
|
|
462
|
+
return
|
|
463
|
+
}
|
|
424
464
|
if (nodeId !== focusedNodeId) {
|
|
425
465
|
const notDimmed = new StringSet([nodeId])
|
|
426
466
|
const dimmed = new StringSet()
|
|
@@ -440,7 +480,7 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
440
480
|
}
|
|
441
481
|
set(
|
|
442
482
|
{
|
|
443
|
-
|
|
483
|
+
activeWalkthrough: null,
|
|
444
484
|
focusedNodeId: nodeId,
|
|
445
485
|
dimmed
|
|
446
486
|
},
|
|
@@ -474,17 +514,18 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
474
514
|
}
|
|
475
515
|
},
|
|
476
516
|
|
|
477
|
-
|
|
517
|
+
resetFocusAndLastClicked: () => {
|
|
478
518
|
let {
|
|
479
|
-
|
|
519
|
+
activeWalkthrough,
|
|
480
520
|
focusedNodeId,
|
|
481
521
|
lastClickedNodeId,
|
|
482
|
-
lastClickedEdgeId
|
|
522
|
+
lastClickedEdgeId,
|
|
523
|
+
xystore
|
|
483
524
|
} = get()
|
|
484
|
-
if (
|
|
525
|
+
if (activeWalkthrough || focusedNodeId || lastClickedNodeId || lastClickedEdgeId) {
|
|
485
526
|
set(
|
|
486
527
|
{
|
|
487
|
-
|
|
528
|
+
activeWalkthrough: null,
|
|
488
529
|
focusedNodeId: null,
|
|
489
530
|
lastClickedNodeId: null,
|
|
490
531
|
lastClickedEdgeId: null,
|
|
@@ -494,6 +535,7 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
494
535
|
'resetLastClicked'
|
|
495
536
|
)
|
|
496
537
|
}
|
|
538
|
+
xystore.getState().resetSelectedElements()
|
|
497
539
|
},
|
|
498
540
|
|
|
499
541
|
getElement: (fqn) => {
|
|
@@ -501,6 +543,11 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
501
543
|
return view.nodes.find(({ id }) => id === fqn) ?? null
|
|
502
544
|
},
|
|
503
545
|
|
|
546
|
+
isEditable: () => {
|
|
547
|
+
const { readonly, onChange } = get()
|
|
548
|
+
return !readonly && !!onChange
|
|
549
|
+
},
|
|
550
|
+
|
|
504
551
|
triggerChangeElementStyle: (change) => {
|
|
505
552
|
if (DEV) {
|
|
506
553
|
console.debug('triggerChangeElementStyle', change)
|
|
@@ -617,25 +664,25 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
617
664
|
}, {} as ViewChange.SaveManualLayout['layout']['nodes'])
|
|
618
665
|
|
|
619
666
|
const edges = reduce(xyflow.getEdges(), (acc, { source, target, data }) => {
|
|
620
|
-
let controlPoints = data.controlPoints
|
|
667
|
+
let controlPoints = data.controlPoints ?? []
|
|
621
668
|
const sourceOrTargetMoved = movedNodes.has(source) || movedNodes.has(target)
|
|
622
669
|
// If edge control points are not set, but the source or target node was moved
|
|
623
|
-
if (
|
|
670
|
+
if (controlPoints.length === 0 && sourceOrTargetMoved) {
|
|
624
671
|
controlPoints = bezierControlPoints(data.edge)
|
|
625
672
|
}
|
|
626
|
-
if (data.edge.points.length === 0 &&
|
|
673
|
+
if (data.edge.points.length === 0 && controlPoints.length === 0) {
|
|
627
674
|
return acc
|
|
628
675
|
}
|
|
629
676
|
const _updated: ViewChange.SaveManualLayout['layout']['edges'][string] = acc[data.edge.id] = {
|
|
630
677
|
points: data.edge.points
|
|
631
678
|
}
|
|
632
679
|
if (data.label?.bbox) {
|
|
633
|
-
_updated.labelBBox = data.label
|
|
680
|
+
_updated.labelBBox = data.label.bbox
|
|
634
681
|
}
|
|
635
682
|
if (data.edge.labelBBox) {
|
|
636
683
|
_updated.labelBBox ??= data.edge.labelBBox
|
|
637
684
|
}
|
|
638
|
-
if (
|
|
685
|
+
if (hasAtLeast(controlPoints, 1)) {
|
|
639
686
|
_updated.controlPoints = controlPoints
|
|
640
687
|
}
|
|
641
688
|
if (!sourceOrTargetMoved && data.edge.dotpos) {
|
|
@@ -643,12 +690,12 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
643
690
|
}
|
|
644
691
|
const allX = [
|
|
645
692
|
...data.edge.points.map(p => p[0]),
|
|
646
|
-
...
|
|
693
|
+
...controlPoints.map(p => p.x),
|
|
647
694
|
...(_updated.labelBBox ? [_updated.labelBBox.x, _updated.labelBBox.x + _updated.labelBBox.width] : [])
|
|
648
695
|
]
|
|
649
696
|
const allY = [
|
|
650
697
|
...data.edge.points.map(p => p[1]),
|
|
651
|
-
...
|
|
698
|
+
...controlPoints.map(p => p.y),
|
|
652
699
|
...(_updated.labelBBox ? [_updated.labelBBox.y, _updated.labelBBox.y + _updated.labelBBox.height] : [])
|
|
653
700
|
]
|
|
654
701
|
const rect = boxToRect({
|
|
@@ -733,8 +780,7 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
733
780
|
onNavigateTo(
|
|
734
781
|
element.navigateTo,
|
|
735
782
|
event,
|
|
736
|
-
element as DiagramNodeWithNavigate
|
|
737
|
-
xynode
|
|
783
|
+
element as DiagramNodeWithNavigate
|
|
738
784
|
)
|
|
739
785
|
},
|
|
740
786
|
|
|
@@ -790,51 +836,62 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
790
836
|
},
|
|
791
837
|
|
|
792
838
|
fitDiagram: (duration = 500) => {
|
|
793
|
-
const { fitViewPadding, view,
|
|
839
|
+
const { fitViewPadding, view, xystore } = get()
|
|
794
840
|
const { width, height, panZoom, transform } = xystore.getState()
|
|
795
841
|
|
|
796
842
|
const bounds = view.bounds
|
|
797
843
|
const maxZoom = Math.max(1, transform[2])
|
|
798
844
|
const viewport = getViewportForBounds(bounds, width, height, MinZoom, maxZoom, fitViewPadding)
|
|
799
845
|
panZoom?.setViewport(viewport, { duration })
|
|
800
|
-
if ((focusedNodeId ?? activeDynamicViewStep) !== null) {
|
|
801
|
-
set(
|
|
802
|
-
{
|
|
803
|
-
activeDynamicViewStep: null,
|
|
804
|
-
focusedNodeId: null,
|
|
805
|
-
dimmed: EmptyStringSet
|
|
806
|
-
},
|
|
807
|
-
noReplace,
|
|
808
|
-
'unfocus'
|
|
809
|
-
)
|
|
810
|
-
}
|
|
811
846
|
},
|
|
812
847
|
|
|
813
848
|
nextDynamicStep: (increment = 1) => {
|
|
814
|
-
const {
|
|
815
|
-
const
|
|
816
|
-
|
|
817
|
-
|
|
849
|
+
const { activeWalkthrough, xyedges, activateWalkthrough } = get()
|
|
850
|
+
const stepId = activeWalkthrough?.stepId
|
|
851
|
+
let nextStep
|
|
852
|
+
if (stepId) {
|
|
853
|
+
const currentIndex = xyedges.findIndex(({ id }) => id === stepId)
|
|
854
|
+
const nextIndex = currentIndex + increment
|
|
855
|
+
nextStep = xyedges[nextIndex]
|
|
856
|
+
} else {
|
|
857
|
+
nextStep = first(xyedges)
|
|
818
858
|
}
|
|
819
|
-
if (nextStep
|
|
820
|
-
|
|
859
|
+
if (nextStep) {
|
|
860
|
+
activateWalkthrough(nextStep.data.edge.id)
|
|
861
|
+
return
|
|
821
862
|
}
|
|
822
863
|
},
|
|
823
864
|
|
|
824
|
-
|
|
825
|
-
const
|
|
865
|
+
activateWalkthrough: (step: EdgeId | XYFlowEdge) => {
|
|
866
|
+
const stepId = typeof step === 'string' ? step : step.data.edge.id
|
|
867
|
+
invariant(isStepEdgeId(stepId), `stepId ${stepId} is not a step edge id`)
|
|
868
|
+
let {
|
|
869
|
+
isDynamicView,
|
|
870
|
+
xyflow,
|
|
871
|
+
xyedges,
|
|
872
|
+
xystore,
|
|
873
|
+
fitViewPadding,
|
|
874
|
+
activeWalkthrough
|
|
875
|
+
} = get()
|
|
826
876
|
invariant(isDynamicView, 'view is not dynamic')
|
|
827
|
-
const
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
dimmed.add(e.id)
|
|
877
|
+
const edge = typeof step === 'string' ? xyedges.find(({ id }) => id === stepId) : step
|
|
878
|
+
invariant(edge, `edge not found: ${stepId}`)
|
|
879
|
+
const currentIndex = xyedges.findIndex(({ id }) => id === stepId)
|
|
880
|
+
activeWalkthrough = {
|
|
881
|
+
stepId,
|
|
882
|
+
hasPrevious: currentIndex > 0,
|
|
883
|
+
hasNext: currentIndex < xyedges.length - 1,
|
|
884
|
+
parallelPrefix: getParallelStepsPrefix(stepId)
|
|
836
885
|
}
|
|
837
|
-
|
|
886
|
+
|
|
887
|
+
const dimmed = new StringSet(
|
|
888
|
+
xyedges
|
|
889
|
+
.filter(({ id }) =>
|
|
890
|
+
id !== stepId
|
|
891
|
+
&& !(activeWalkthrough.parallelPrefix && id.startsWith(activeWalkthrough.parallelPrefix))
|
|
892
|
+
)
|
|
893
|
+
.map(({ id }) => id)
|
|
894
|
+
)
|
|
838
895
|
const selected = [] as XYFlowNode[]
|
|
839
896
|
for (const n of xyflow.getNodes()) {
|
|
840
897
|
if (n.id === edge.source || n.id === edge.target) {
|
|
@@ -855,24 +912,24 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
855
912
|
set(
|
|
856
913
|
{
|
|
857
914
|
focusedNodeId: null,
|
|
858
|
-
|
|
915
|
+
activeWalkthrough,
|
|
859
916
|
dimmed
|
|
860
917
|
},
|
|
861
918
|
noReplace,
|
|
862
|
-
'
|
|
919
|
+
'activateWalkthrough'
|
|
863
920
|
)
|
|
864
921
|
},
|
|
865
922
|
|
|
866
|
-
|
|
867
|
-
if (get().
|
|
923
|
+
stopWalkthrough: () => {
|
|
924
|
+
if (get().activeWalkthrough !== null) {
|
|
868
925
|
set(
|
|
869
926
|
{
|
|
870
|
-
|
|
927
|
+
activeWalkthrough: null,
|
|
871
928
|
focusedNodeId: null,
|
|
872
929
|
dimmed: EmptyStringSet
|
|
873
930
|
},
|
|
874
931
|
noReplace,
|
|
875
|
-
'
|
|
932
|
+
'stopWalkthrough'
|
|
876
933
|
)
|
|
877
934
|
get().fitDiagram()
|
|
878
935
|
}
|
|
@@ -905,8 +962,70 @@ export function createDiagramStore(props: DiagramInitialState) {
|
|
|
905
962
|
}
|
|
906
963
|
})
|
|
907
964
|
set({ dimmed }, noReplace, 'highlightByElementNotation')
|
|
965
|
+
},
|
|
966
|
+
|
|
967
|
+
resetEdgeControlPoints: () => {
|
|
968
|
+
const { xyflow, scheduleSaveManualLayout, xynodes, xyedges } = get()
|
|
969
|
+
|
|
970
|
+
xyedges.forEach(edge => {
|
|
971
|
+
xyflow.updateEdgeData(edge.id, {
|
|
972
|
+
controlPoints: getControlPointForEdge(edge)
|
|
973
|
+
})
|
|
974
|
+
})
|
|
975
|
+
|
|
976
|
+
scheduleSaveManualLayout()
|
|
977
|
+
|
|
978
|
+
function getNodeCenter(node: XYFlowNode, nodes: XYFlowNode[]) {
|
|
979
|
+
const dimensions = vector({ x: node.width || 0, y: node.height || 0 })
|
|
980
|
+
let position = vector(node.position)
|
|
981
|
+
.add(dimensions.mul(0.5))
|
|
982
|
+
|
|
983
|
+
let currentNode = node
|
|
984
|
+
do {
|
|
985
|
+
const parent = currentNode.parentId && nodes.find(x => x.id == currentNode.parentId)
|
|
986
|
+
|
|
987
|
+
if (!parent) {
|
|
988
|
+
break
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
currentNode = parent
|
|
992
|
+
position = position.add(parent.position)
|
|
993
|
+
} while (true)
|
|
994
|
+
|
|
995
|
+
return position
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function getControlPointForEdge(edge: XYFlowEdge): XYPoint[] {
|
|
999
|
+
const source = xynodes.find(x => x.id == edge.source)
|
|
1000
|
+
const target = xynodes.find(x => x.id == edge.target)
|
|
1001
|
+
if (!source || !target) {
|
|
1002
|
+
return []
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
const sourceCenter = getNodeCenter(source, xynodes)
|
|
1006
|
+
const targetCenter = getNodeCenter(target, xynodes)
|
|
1007
|
+
|
|
1008
|
+
if (sourceCenter && targetCenter) {
|
|
1009
|
+
const sourceToTargetVector = targetCenter.sub(sourceCenter)
|
|
1010
|
+
const sourceBorderPoint = getBorderPointOnVector(source, sourceCenter, sourceToTargetVector)
|
|
1011
|
+
const targetBorderPoint = getBorderPointOnVector(target, targetCenter, sourceToTargetVector.mul(-1))
|
|
1012
|
+
|
|
1013
|
+
return [sourceBorderPoint.add(targetBorderPoint.sub(sourceBorderPoint).mul(0.3))]
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
return []
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function getBorderPointOnVector(node: XYFlowNode, nodeCenter: Vector, v: Vector) {
|
|
1020
|
+
const xScale = (node.width || 0) / 2 / v.x
|
|
1021
|
+
const yScale = (node.height || 0) / 2 / v.y
|
|
1022
|
+
|
|
1023
|
+
const scale = Math.min(Math.abs(xScale), Math.abs(yScale))
|
|
1024
|
+
|
|
1025
|
+
return vector(v).mul(scale).add(nodeCenter)
|
|
1026
|
+
}
|
|
908
1027
|
}
|
|
909
|
-
}),
|
|
1028
|
+
} satisfies DiagramState),
|
|
910
1029
|
{
|
|
911
1030
|
name: `${storeDevId} - ${props.view.id}`,
|
|
912
1031
|
enabled: DEV
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { themeToVars } from '@mantine/vanilla-extract'
|
|
1
2
|
import { createGlobalThemeContract } from '@vanilla-extract/css'
|
|
2
3
|
|
|
3
4
|
export const vars = createGlobalThemeContract({
|
|
@@ -31,12 +32,12 @@ export const vars = createGlobalThemeContract({
|
|
|
31
32
|
labelBgColor: 'relation-labelBg'
|
|
32
33
|
},
|
|
33
34
|
optionsPanel: {
|
|
34
|
-
top: 'options-
|
|
35
|
-
right: 'options-
|
|
35
|
+
top: 'options-webview-top',
|
|
36
|
+
right: 'options-webview-right'
|
|
36
37
|
},
|
|
37
38
|
navigationPanel: {
|
|
38
|
-
top: 'navigation-
|
|
39
|
-
left: 'navigation-
|
|
39
|
+
top: 'navigation-webview-top',
|
|
40
|
+
left: 'navigation-webview-left'
|
|
40
41
|
}
|
|
41
42
|
}, (value) => `likec4-${value}`)
|
|
42
43
|
|
|
@@ -67,3 +68,9 @@ export const xyvars = createGlobalThemeContract({
|
|
|
67
68
|
borderRadius: 'node-border-radius'
|
|
68
69
|
}
|
|
69
70
|
}, (value) => `xy-${value}`)
|
|
71
|
+
|
|
72
|
+
export const whereLight = ':where([data-mantine-color-scheme="light"])'
|
|
73
|
+
export const whereDark = ':where([data-mantine-color-scheme="dark"])'
|
|
74
|
+
|
|
75
|
+
// we need only variables names
|
|
76
|
+
export const mantine = themeToVars({})
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { fallbackVar,
|
|
2
|
-
import { mantine, whereDark } from '../
|
|
3
|
-
import { vars } from '../theme.css'
|
|
1
|
+
import { fallbackVar, style } from '@vanilla-extract/css'
|
|
2
|
+
import { mantine, vars, whereDark } from '../theme-vars'
|
|
4
3
|
|
|
5
4
|
export const container = style({
|
|
6
5
|
bottom: 0,
|
|
@@ -16,13 +16,29 @@ const selector = (s: DiagramState) => ({
|
|
|
16
16
|
export default function DiagramTitlePanel() {
|
|
17
17
|
const { id, title, description, links } = useDiagramState(selector)
|
|
18
18
|
const [isCollapsed, setCollapsed] = useLocalStorage({
|
|
19
|
-
key: 'diagram-title-
|
|
19
|
+
key: 'diagram-title-webview-collapsed',
|
|
20
20
|
defaultValue: false
|
|
21
21
|
})
|
|
22
22
|
const toggle = () => setCollapsed(v => !v)
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<AnimatePresence mode="wait">
|
|
26
|
+
{
|
|
27
|
+
/* <ViewportPortal>
|
|
28
|
+
<div className={css.inlineTitle} style={{ transform: `translate(0px,${height}px)` }}>
|
|
29
|
+
<m.div
|
|
30
|
+
key={id}
|
|
31
|
+
animate={{ opacity: 0.7, scale: .92 }}
|
|
32
|
+
whileHover={{ opacity: 1, scale: 1 }}
|
|
33
|
+
style={{
|
|
34
|
+
transformOrigin: 'left bottom'
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
<Title order={1}>{title}</Title>
|
|
38
|
+
</m.div>
|
|
39
|
+
</div>
|
|
40
|
+
</ViewportPortal> */
|
|
41
|
+
}
|
|
26
42
|
<m.div
|
|
27
43
|
key={id}
|
|
28
44
|
initial={{ opacity: 0.05, scale: 0.7 }}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createVar, globalStyle, style } from '@vanilla-extract/css'
|
|
2
|
-
import { mantine } from '../
|
|
2
|
+
import { mantine } from '../theme-vars'
|
|
3
3
|
|
|
4
4
|
export const container = style({
|
|
5
5
|
position: 'absolute',
|
|
@@ -7,7 +7,16 @@ export const container = style({
|
|
|
7
7
|
left: '50%',
|
|
8
8
|
transform: 'translateX(-50%)',
|
|
9
9
|
padding: 0,
|
|
10
|
-
margin: 0
|
|
10
|
+
margin: 0,
|
|
11
|
+
display: 'flex',
|
|
12
|
+
flexDirection: 'column',
|
|
13
|
+
gap: '0.4rem',
|
|
14
|
+
alignItems: 'center',
|
|
15
|
+
'@media': {
|
|
16
|
+
[mantine.largerThan('md')]: {
|
|
17
|
+
gap: '0.6rem'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
11
20
|
})
|
|
12
21
|
|
|
13
22
|
export const buttons = style({
|
|
@@ -45,3 +54,20 @@ globalStyle(`:where(${buttons}, ${btn}) .tabler-icon`, {
|
|
|
45
54
|
width: '0.85em',
|
|
46
55
|
height: '0.85em'
|
|
47
56
|
})
|
|
57
|
+
|
|
58
|
+
export const parallelStateFrame = style({
|
|
59
|
+
position: 'absolute',
|
|
60
|
+
margin: 0,
|
|
61
|
+
padding: 0,
|
|
62
|
+
top: 0,
|
|
63
|
+
left: 0,
|
|
64
|
+
width: '100%',
|
|
65
|
+
height: '100%',
|
|
66
|
+
border: `2px solid ${mantine.colors.orange[6]}`,
|
|
67
|
+
pointerEvents: 'none',
|
|
68
|
+
'@media': {
|
|
69
|
+
[mantine.largerThan('md')]: {
|
|
70
|
+
borderWidth: 4
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
})
|