@likec4/diagram 1.9.0 → 1.12.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 (113) hide show
  1. package/package.json +20 -43
  2. package/src/LikeC4CustomColors.tsx +5 -2
  3. package/src/LikeC4Diagram.css.ts +1 -1
  4. package/src/LikeC4Diagram.props.ts +40 -94
  5. package/src/LikeC4Diagram.tsx +14 -4
  6. package/src/LikeC4ModelProvider.tsx +21 -0
  7. package/src/StaticLikeC4Diagram.tsx +1 -0
  8. package/src/globals.css.ts +2 -3
  9. package/src/hooks/index.ts +1 -0
  10. package/src/hooks/useMantinePortalProps.ts +8 -0
  11. package/src/hooks/useXYFlow.ts +1 -2
  12. package/src/index.ts +9 -0
  13. package/src/likec4model/LikeC4ModelContext.ts +4 -0
  14. package/src/likec4model/index.ts +1 -0
  15. package/src/likec4model/useLikeC4Model.ts +58 -0
  16. package/src/state/diagram-to-xyflow.ts +1 -2
  17. package/src/state/diagramStore.ts +185 -66
  18. package/src/{theme.css.ts → theme-vars.ts} +11 -4
  19. package/src/ui/DiagramTitlePanel.css.ts +2 -3
  20. package/src/ui/DiagramTitlePanel.tsx +25 -3
  21. package/src/ui/DynamicViewWalkthrough.css.ts +28 -2
  22. package/src/ui/DynamicViewWalkthrough.tsx +97 -52
  23. package/src/ui/OptionsPanel.css.ts +1 -2
  24. package/src/ui/OptionsPanel.tsx +5 -2
  25. package/src/ui/notation/NotationPanel.css.ts +2 -3
  26. package/src/ui/notation/NotationPanel.tsx +10 -11
  27. package/src/ui/options/NodeOptions.tsx +4 -1
  28. package/src/ui/top-left/ChangeAutoLayoutButton.tsx +122 -71
  29. package/src/ui/top-left/TopLeftPanel.tsx +128 -117
  30. package/src/ui/top-left/_shared.ts +7 -6
  31. package/src/ui/top-left/styles.css.ts +48 -8
  32. package/src/utils/vector.ts +57 -0
  33. package/src/xyflow/FitviewOnDiagramChange.tsx +5 -4
  34. package/src/xyflow/SelectEdgesOnNodeFocus.tsx +7 -5
  35. package/src/xyflow/XYFlow.tsx +13 -17
  36. package/src/xyflow/XYFlowEvents.tsx +58 -58
  37. package/src/xyflow/XYFlowInner.tsx +4 -8
  38. package/src/xyflow/edges/EdgeLabel.tsx +185 -0
  39. package/src/xyflow/edges/RelationshipEdge.tsx +170 -137
  40. package/src/xyflow/edges/RelationshipsDropdownMenu.css.ts +60 -0
  41. package/src/xyflow/edges/RelationshipsDropdownMenu.tsx +214 -0
  42. package/src/xyflow/edges/edges.css.ts +75 -7
  43. package/src/xyflow/nodes/compound/CompoundNode.css.ts +1 -2
  44. package/src/xyflow/nodes/compound/CompoundNode.tsx +25 -8
  45. package/src/xyflow/nodes/element/ElementLink.css.ts +1 -1
  46. package/src/xyflow/nodes/element/ElementLink.tsx +4 -8
  47. package/src/xyflow/nodes/element/ElementNode.tsx +47 -8
  48. package/src/xyflow/nodes/element/element.css.ts +1 -2
  49. package/src/xyflow/nodes/shared/Toolbar.tsx +426 -0
  50. package/src/xyflow/types.ts +1 -24
  51. package/src/xyflow/useLayoutConstraints.ts +10 -6
  52. package/dist/LikeC4CustomColors.d.ts +0 -3
  53. package/dist/LikeC4CustomColors.props.d.ts +0 -5
  54. package/dist/LikeC4Diagram.css.d.ts +0 -4
  55. package/dist/LikeC4Diagram.d.ts +0 -7
  56. package/dist/LikeC4Diagram.props.d.ts +0 -154
  57. package/dist/StaticLikeC4Diagram.d.ts +0 -4
  58. package/dist/globals.css.d.ts +0 -1
  59. package/dist/hooks/index.d.ts +0 -4
  60. package/dist/hooks/useDiagramState.d.ts +0 -6
  61. package/dist/hooks/useSetState.d.ts +0 -3
  62. package/dist/hooks/useUpdateEffect.d.ts +0 -5
  63. package/dist/hooks/useXYFlow.d.ts +0 -267
  64. package/dist/index.d.ts +0 -9
  65. package/dist/index.mjs +0 -3684
  66. package/dist/mantine/EnsureMantine.d.ts +0 -8
  67. package/dist/mantine.css.d.ts +0 -3
  68. package/dist/state/DiagramContext.d.ts +0 -1192
  69. package/dist/state/diagram-to-xyflow.d.ts +0 -12
  70. package/dist/state/diagramStore.d.ts +0 -1268
  71. package/dist/style.css +0 -2139
  72. package/dist/theme.css.d.ts +0 -63
  73. package/dist/ui/DiagramTitlePanel.css.d.ts +0 -4
  74. package/dist/ui/DiagramTitlePanel.d.ts +0 -1
  75. package/dist/ui/DynamicViewWalkthrough.css.d.ts +0 -3
  76. package/dist/ui/DynamicViewWalkthrough.d.ts +0 -1
  77. package/dist/ui/OptionsPanel.css.d.ts +0 -1
  78. package/dist/ui/OptionsPanel.d.ts +0 -1
  79. package/dist/ui/notation/NotationPanel.css.d.ts +0 -6
  80. package/dist/ui/notation/NotationPanel.d.ts +0 -1
  81. package/dist/ui/notation/index.d.ts +0 -1
  82. package/dist/ui/options/EdgeOptions.d.ts +0 -19
  83. package/dist/ui/options/NodeOptions.d.ts +0 -19
  84. package/dist/ui/top-left/ChangeAutoLayoutButton.d.ts +0 -3
  85. package/dist/ui/top-left/TopLeftPanel.d.ts +0 -4
  86. package/dist/ui/top-left/_shared.d.ts +0 -5
  87. package/dist/ui/top-left/styles.css.d.ts +0 -3
  88. package/dist/xyflow/EdgeMarkers.d.ts +0 -12
  89. package/dist/xyflow/FitviewOnDiagramChange.d.ts +0 -4
  90. package/dist/xyflow/SelectEdgesOnNodeFocus.d.ts +0 -1
  91. package/dist/xyflow/XYFlow.d.ts +0 -12
  92. package/dist/xyflow/XYFlowBackground.d.ts +0 -11
  93. package/dist/xyflow/XYFlowEvents.d.ts +0 -161
  94. package/dist/xyflow/XYFlowInner.d.ts +0 -10
  95. package/dist/xyflow/const.d.ts +0 -7
  96. package/dist/xyflow/edges/RelationshipEdge.d.ts +0 -32
  97. package/dist/xyflow/edges/edges.css.d.ts +0 -12
  98. package/dist/xyflow/edges/utils.d.ts +0 -17
  99. package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +0 -11
  100. package/dist/xyflow/nodes/compound/CompoundNode.d.ts +0 -6
  101. package/dist/xyflow/nodes/compound/index.d.ts +0 -1
  102. package/dist/xyflow/nodes/element/ElementLink.css.d.ts +0 -3
  103. package/dist/xyflow/nodes/element/ElementLink.d.ts +0 -7
  104. package/dist/xyflow/nodes/element/ElementNode.d.ts +0 -6
  105. package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +0 -18
  106. package/dist/xyflow/nodes/element/element.css.d.ts +0 -19
  107. package/dist/xyflow/nodes/element/index.d.ts +0 -1
  108. package/dist/xyflow/nodes/shared/NavigateToBtn.d.ts +0 -5
  109. package/dist/xyflow/types.d.ts +0 -50
  110. package/dist/xyflow/useLayoutConstraints.d.ts +0 -9
  111. package/dist/xyflow/utils.d.ts +0 -18
  112. package/src/mantine.css.ts +0 -7
  113. package/src/reset.d.ts +0 -2
package/dist/index.mjs DELETED
@@ -1,3684 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key != "symbol" ? key + "" : key, value);
4
- import { jsx, Fragment, jsxs } from "react/jsx-runtime";
5
- import { useReactFlow, useStoreApi, useNodesData, useStore, applyNodeChanges, applyEdgeChanges, getViewportForBounds, EdgeLabelRenderer, Handle, Position, useOnViewportChange, ReactFlow, useOnSelectionChange, Background, BackgroundVariant, Controls, ReactFlowProvider } from "@xyflow/react";
6
- import clsx from "clsx";
7
- import { shallowEqual, deepEqual } from "fast-equals";
8
- import { m, AnimatePresence, LazyMotion, domAnimation } from "framer-motion";
9
- import { createContext, useRef, useCallback, useContext, useState, memo, useEffect, useId, useMemo } from "react";
10
- import { createWithEqualityFn, useStoreWithEqualityFn } from "zustand/traditional";
11
- import { invariant, hasAtLeast, whereOperatorAsPredicate, extractStep, nonNullable, StepEdgeId, getBBoxCenter, nonexhaustive, isAncestor, defaultTheme, ElementShapes } from "@likec4/core";
12
- import { useFirstMountState, useCustomCompareEffect, useDebouncedEffect } from "@react-hookz/web";
13
- import { getNodeDimensions, getBoundsOfRects, boxToRect, getViewportForBounds as getViewportForBounds$1 } from "@xyflow/system";
14
- import { DEV } from "esm-env";
15
- import { isArray, hasAtLeast as hasAtLeast$1, isNullish, entries, reduce, map, prop, isTruthy, first, last, clamp, isNumber, isNonNullish, ceil, keys, takeWhile } from "remeda";
16
- import { subscribeWithSelector, devtools } from "zustand/middleware";
17
- import { shallow } from "zustand/shallow";
18
- import { Bezier } from "bezier-js";
19
- import { MantineContext, Box, ActionIcon as ActionIcon$2, Text as Text$1, HoverCard, HoverCardTarget, UnstyledButton, HoverCardDropdown, Stack, Group, Anchor, CopyButton, Button, Card, CardSection, Spoiler, ButtonGroup, Tooltip as Tooltip$2, ThemeIcon, Paper, Tabs, TabsList, TabsTab, TabsPanel, ScrollAreaAutosize, Badge, rem, Divider, Space, TooltipGroup, Flex, ColorSwatch, CheckIcon, Select, Slider, SegmentedControl, Popover, PopoverTarget, PopoverDropdown, FloatingIndicator, Notification, Code } from "@mantine/core";
20
- import { useHotkeys, useLocalStorage } from "@mantine/hooks";
21
- import { assignInlineVars } from "@vanilla-extract/dynamic";
22
- import { line, curveCatmullRomOpen } from "d3-shape";
23
- import { Solver, Operator, Expression, Strength, Variable } from "@lume/kiwi";
24
- import { IconZoomScan, IconLink, IconMenu, IconPlayerPlayFilled, IconPlayerSkipBackFilled, IconPlayerStopFilled, IconPlayerSkipForwardFilled, IconAlertTriangle, IconHelpCircle, IconArrowDownRight, IconFileSymlink, IconLayoutDashboard, IconArrowBigDownLines, IconFocusCentered, IconMenu2, IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
25
- var rootClassName = "likec4-diagram-root";
26
- const noop = () => {
27
- }, depsShallowEqual = (d1, d2) => {
28
- if (d1 === d2)
29
- return !0;
30
- if (d1.length !== d2.length)
31
- return !1;
32
- for (const [i, element] of d1.entries())
33
- if (!shallowEqual(element, d2[i]))
34
- return !1;
35
- return !0;
36
- };
37
- function useUpdateEffect(callback, deps, equalityFn, effectHook) {
38
- const isFirstMount = useFirstMountState();
39
- useCustomCompareEffect(
40
- isFirstMount ? noop : callback,
41
- deps,
42
- equalityFn ?? depsShallowEqual,
43
- effectHook
44
- );
45
- }
46
- const useXYFlow = useReactFlow, useXYNodesData = useNodesData;
47
- function useXYStore(selector2, equalityFn) {
48
- return useStore(
49
- // @ts-expect-error - types are not correct
50
- selector2,
51
- shallowEqual
52
- );
53
- }
54
- const useXYStoreApi = useStoreApi, ZIndexes = {
55
- Compound: 2,
56
- Edge: 4,
57
- Element: 6
58
- }, MinZoom = 0.1, MaxZoom = 2;
59
- function toDomPrecision(v) {
60
- return v === null ? 0.01 : Math.round(v * 100) / 100;
61
- }
62
- const nodeToRect = (nd) => ({
63
- x: nd.internals.positionAbsolute.x,
64
- y: nd.internals.positionAbsolute.y,
65
- width: nd.width ?? nd.data.element.width,
66
- height: nd.height ?? nd.data.element.height
67
- }), isInside = (test, target) => test.x >= target.x && test.y >= target.y && test.x + test.width <= target.x + target.width && test.y + test.height <= target.y + target.height;
68
- function bezierControlPoints(diagramEdge) {
69
- let [start, ...bezierPoints] = diagramEdge.points;
70
- invariant(start, "start should be defined");
71
- const handles = [
72
- // start
73
- ];
74
- for (; hasAtLeast(bezierPoints, 3); ) {
75
- const [cp1, cp2, end, ...rest] = bezierPoints, bezier = new Bezier(start[0], start[1], cp1[0], cp1[1], cp2[0], cp2[1], end[0], end[1]), inflections = bezier.inflections();
76
- inflections.length === 0 && inflections.push(0.5), inflections.forEach((t) => {
77
- const { x, y } = bezier.get(t);
78
- handles.push({
79
- x: Math.round(x),
80
- y: Math.round(y)
81
- });
82
- }), bezierPoints = rest, start = end;
83
- }
84
- return invariant(bezierPoints.length === 0, "all points should be consumed"), handles;
85
- }
86
- const isClose = (a, b) => Math.abs(a - b) < 3.1;
87
- function isSamePoint$1(a, b) {
88
- const [ax, ay] = isArray(a) ? a : [a.x, a.y], [bx, by] = isArray(b) ? b : [b.x, b.y];
89
- return isClose(ax, bx) && isClose(ay, by);
90
- }
91
- function diagramViewToXYFlowData(view, opts) {
92
- const isDynamicView = view.__ === "dynamic", xynodes = [], xyedges = [], nodeLookup = /* @__PURE__ */ new Map(), traverse = view.nodes.reduce(
93
- (acc, node) => (nodeLookup.set(node.id, node), node.parent || acc.push({ node, parent: null }), acc),
94
- new Array()
95
- );
96
- let visiblePredicate = (_nodeOrEdge) => !0;
97
- if (opts.where)
98
- try {
99
- visiblePredicate = whereOperatorAsPredicate(opts.where);
100
- } catch (e) {
101
- console.error("Error in where filter:", e);
102
- }
103
- const ns = "", nodeById = (id) => nonNullable(nodeLookup.get(id), `Node not found: ${id}`);
104
- let next;
105
- for (; next = traverse.shift(); ) {
106
- const { node, parent } = next, isCompound = hasAtLeast$1(node.children, 1);
107
- isCompound && traverse.push(...node.children.map((child) => ({ node: nodeById(child), parent: node })));
108
- const position = {
109
- x: node.position[0],
110
- y: node.position[1]
111
- };
112
- parent && (position.x -= parent.position[0], position.y -= parent.position[1]);
113
- const id = ns + node.id, draggable = opts.draggable && (!parent || parent.children.length > 1);
114
- xynodes.push({
115
- id,
116
- type: isCompound ? "compound" : "element",
117
- data: {
118
- fqn: node.id,
119
- element: node
120
- },
121
- draggable,
122
- selectable: opts.selectable,
123
- focusable: opts.selectable && !isCompound,
124
- deletable: !1,
125
- position,
126
- zIndex: isCompound ? ZIndexes.Compound : ZIndexes.Element,
127
- width: node.width,
128
- height: node.height,
129
- hidden: !visiblePredicate(node),
130
- // parentId: parent ? ns + parent.id : null,
131
- ...parent && {
132
- parentId: ns + parent.id
133
- },
134
- ...isCompound && {
135
- dragHandle: ".likec4-compound-title"
136
- }
137
- });
138
- }
139
- for (const edge of view.edges) {
140
- const source = edge.source, target = edge.target, id = ns + edge.id;
141
- if (!hasAtLeast$1(edge.points, 2)) {
142
- console.error("edge should have at least 2 points", edge);
143
- continue;
144
- }
145
- xyedges.push({
146
- id,
147
- type: "relationship",
148
- source: ns + source,
149
- target: ns + target,
150
- zIndex: ZIndexes.Edge,
151
- selectable: opts.selectable,
152
- hidden: !visiblePredicate(edge),
153
- deletable: !1,
154
- data: {
155
- edge,
156
- controlPoints: edge.controlPoints || null,
157
- stepNum: isDynamicView ? extractStep(edge.id) : null,
158
- label: edge.labelBBox ? {
159
- bbox: edge.labelBBox,
160
- text: edge.label ?? ""
161
- } : null
162
- },
163
- interactionWidth: 20
164
- });
165
- }
166
- return {
167
- xynodes,
168
- xyedges
169
- };
170
- }
171
- const StringSet = Set, DEFAULT_PROPS = {
172
- xyedges: [],
173
- xynodes: [],
174
- isDynamicView: !1,
175
- viewSyncDebounceTimeout: null,
176
- initialized: !1,
177
- navigationHistoryIndex: 0,
178
- viewportChanged: !1,
179
- activeDynamicViewStep: null,
180
- focusedNodeId: null,
181
- hoveredNodeId: null,
182
- hoveredEdgeId: null,
183
- lastClickedNodeId: null,
184
- lastClickedEdgeId: null,
185
- dimmed: new StringSet(),
186
- lastOnNavigate: null
187
- }, noReplace = !1;
188
- let StoreDevId = 1;
189
- const EmptyStringSet = new StringSet();
190
- function createDiagramStore(props) {
191
- const storeDevId = "DiagramStore" + String(StoreDevId++).padStart(2, "0"), {
192
- xynodes,
193
- xyedges
194
- } = diagramViewToXYFlowData(props.view, {
195
- where: props.whereFilter,
196
- draggable: props.nodesDraggable,
197
- selectable: props.nodesSelectable
198
- });
199
- return createWithEqualityFn(
200
- subscribeWithSelector(
201
- devtools(
202
- (set, get) => ({
203
- ...DEFAULT_PROPS,
204
- ...props,
205
- storeDevId,
206
- isDynamicView: props.view.__ === "dynamic",
207
- xynodes,
208
- xyedges,
209
- navigationHistory: [{
210
- viewId: props.view.id,
211
- nodeId: null
212
- }],
213
- navigationHistoryIndex: 0,
214
- onNodesChange: (changes) => {
215
- set({
216
- xynodes: applyNodeChanges(changes, get().xynodes)
217
- });
218
- },
219
- onEdgesChange: (changes) => {
220
- set({
221
- xyedges: applyEdgeChanges(changes, get().xyedges)
222
- });
223
- },
224
- updateView: (nextView) => {
225
- let {
226
- viewSyncDebounceTimeout,
227
- xyflow,
228
- xystore,
229
- dimmed: dimmed2,
230
- whereFilter,
231
- view: current,
232
- lastOnNavigate,
233
- navigationHistory,
234
- navigationHistoryIndex,
235
- focusedNodeId,
236
- lastClickedNodeId,
237
- lastClickedEdgeId,
238
- activeDynamicViewStep,
239
- nodesDraggable,
240
- nodesSelectable,
241
- hoveredEdgeId,
242
- hoveredNodeId,
243
- xyedges: xyedges2,
244
- xynodes: xynodes2
245
- } = get();
246
- viewSyncDebounceTimeout !== null && (clearTimeout(viewSyncDebounceTimeout), viewSyncDebounceTimeout = null);
247
- const isSameView = current.id === nextView.id;
248
- if (isSameView) {
249
- const nodeIds = new StringSet(nextView.nodes.map((n) => n.id)), edgeIds = new StringSet(nextView.edges.map((e) => e.id));
250
- if (lastClickedNodeId && !nodeIds.has(lastClickedNodeId) && (lastClickedNodeId = null), hoveredNodeId && !nodeIds.has(hoveredNodeId) && (hoveredNodeId = null), focusedNodeId && !nodeIds.has(focusedNodeId) && (focusedNodeId = null), lastClickedEdgeId && !edgeIds.has(lastClickedEdgeId) && (lastClickedEdgeId = null), hoveredEdgeId && !edgeIds.has(hoveredEdgeId) && (hoveredEdgeId = null), activeDynamicViewStep && !edgeIds.has(StepEdgeId(activeDynamicViewStep)) && (activeDynamicViewStep = null), dimmed2.size > 0) {
251
- let nextDimmed = new StringSet([...dimmed2].filter((id) => nodeIds.has(id) || edgeIds.has(id)));
252
- nextDimmed.size !== dimmed2.size && (dimmed2 = nextDimmed);
253
- }
254
- } else {
255
- const stepCurrent = nonNullable(navigationHistory[navigationHistoryIndex]);
256
- stepCurrent.viewId !== nextView.id ? (navigationHistory = [
257
- ...navigationHistory.slice(0, navigationHistoryIndex + 1),
258
- {
259
- viewId: nextView.id,
260
- nodeId: (lastOnNavigate == null ? void 0 : lastOnNavigate.fromNode) || null
261
- }
262
- ], navigationHistoryIndex = navigationHistory.length - 1) : stepCurrent.nodeId && (lastOnNavigate ?? (lastOnNavigate = {
263
- fromView: current.id,
264
- toView: nextView.id,
265
- fromNode: stepCurrent.nodeId
266
- })), lastOnNavigate && lastOnNavigate.toView !== nextView.id && (lastOnNavigate = null);
267
- const elTo = lastOnNavigate && nextView.nodes.find((n) => n.id === (lastOnNavigate == null ? void 0 : lastOnNavigate.fromNode)), xynodeFrom = elTo && xyflow.getInternalNode(elTo.id);
268
- if (!lastOnNavigate || isNullish(elTo) || isNullish(xynodeFrom)) {
269
- const zoom = xyflow.getZoom(), { x, y } = getBBoxCenter(nextView.bounds);
270
- xyflow.setCenter(x, y, { zoom }), lastOnNavigate = null;
271
- }
272
- if (lastOnNavigate && elTo && xynodeFrom) {
273
- const fromPos = xyflow.flowToScreenPosition({
274
- x: xynodeFrom.internals.positionAbsolute.x,
275
- // + dimensions.width / 2,
276
- y: xynodeFrom.internals.positionAbsolute.y
277
- // + dimensions.height / 2
278
- }), toPos = xyflow.flowToScreenPosition({
279
- x: elTo.position[0],
280
- // + elFrom.width / 2,
281
- y: elTo.position[1]
282
- // + elFrom.height / 2
283
- }), diff = {
284
- x: toDomPrecision(fromPos.x - toPos.x),
285
- y: toDomPrecision(fromPos.y - toPos.y)
286
- };
287
- xystore.getState().panBy(diff), lastOnNavigate = null;
288
- }
289
- lastClickedEdgeId = null, lastClickedNodeId = null, hoveredEdgeId = null, hoveredNodeId = null, focusedNodeId = null, activeDynamicViewStep = null, dimmed2 = EmptyStringSet;
290
- }
291
- const update = diagramViewToXYFlowData(nextView, {
292
- where: whereFilter,
293
- draggable: nodesDraggable,
294
- selectable: nodesSelectable
295
- });
296
- update.xynodes = update.xynodes.map((update2) => {
297
- const existing = xynodes2.find((n) => n.id === update2.id);
298
- return existing && existing.type === update2.type && deepEqual(existing.parentId ?? null, update2.parentId ?? null) ? existing.hidden === update2.hidden && existing.width === update2.width && existing.height === update2.height && deepEqual(existing.position, update2.position) && deepEqual(existing.data.element, update2.data.element) ? existing : {
299
- ...existing,
300
- ...update2
301
- } : update2;
302
- }), isSameView && !nextView.hasLayoutDrift && (update.xyedges = update.xyedges.map((update2) => {
303
- const existing = xyedges2.find((n) => n.id === update2.id);
304
- return existing ? existing.hidden === update2.hidden && deepEqual(existing.data.label, update2.data.label) && deepEqual(existing.data.controlPoints, update2.data.controlPoints) && deepEqual(existing.data.edge, update2.data.edge) ? existing : {
305
- ...existing,
306
- ...update2,
307
- data: {
308
- ...existing.data,
309
- ...update2.data
310
- }
311
- } : update2;
312
- })), set(
313
- {
314
- isDynamicView: nextView.__ === "dynamic",
315
- viewSyncDebounceTimeout,
316
- view: nextView,
317
- activeDynamicViewStep,
318
- lastOnNavigate,
319
- lastClickedNodeId,
320
- lastClickedEdgeId,
321
- focusedNodeId,
322
- hoveredEdgeId,
323
- hoveredNodeId,
324
- navigationHistory,
325
- navigationHistoryIndex,
326
- dimmed: dimmed2,
327
- xynodes: !isSameView || !shallowEqual(update.xynodes, xynodes2) ? update.xynodes : xynodes2,
328
- xyedges: !isSameView || !shallowEqual(update.xyedges, xyedges2) ? update.xyedges : xyedges2
329
- },
330
- noReplace,
331
- isSameView ? "update-view [same]" : "update-view [another]"
332
- );
333
- },
334
- focusOnNode: (nodeId) => {
335
- const { focusedNodeId, view, enableFocusMode } = get();
336
- if (invariant(enableFocusMode, "focus mode is not enabled"), nodeId !== focusedNodeId) {
337
- const notDimmed = new StringSet([nodeId]), dimmed2 = new StringSet();
338
- for (const edge of view.edges)
339
- edge.source === nodeId || edge.target === nodeId ? (notDimmed.add(edge.source), notDimmed.add(edge.target)) : dimmed2.add(edge.id);
340
- for (const node of view.nodes)
341
- notDimmed.has(node.id) || dimmed2.add(node.id);
342
- set(
343
- {
344
- activeDynamicViewStep: null,
345
- focusedNodeId: nodeId,
346
- dimmed: dimmed2
347
- },
348
- noReplace,
349
- `focus on node: ${nodeId}`
350
- );
351
- }
352
- },
353
- setHoveredNode: (nodeId) => {
354
- nodeId !== get().hoveredNodeId && set({ hoveredNodeId: nodeId });
355
- },
356
- setHoveredEdge: (edgeId) => {
357
- edgeId !== get().hoveredEdgeId && set({ hoveredEdgeId: edgeId });
358
- },
359
- setLastClickedNode: (nodeId) => {
360
- nodeId !== get().lastClickedNodeId && set({ lastClickedNodeId: nodeId });
361
- },
362
- setLastClickedEdge: (edgeId) => {
363
- edgeId !== get().lastClickedEdgeId && set({ lastClickedEdgeId: edgeId });
364
- },
365
- resetLastClicked: () => {
366
- let {
367
- activeDynamicViewStep,
368
- focusedNodeId,
369
- lastClickedNodeId,
370
- lastClickedEdgeId
371
- } = get();
372
- (activeDynamicViewStep || focusedNodeId || lastClickedNodeId || lastClickedEdgeId) && set(
373
- {
374
- activeDynamicViewStep: null,
375
- focusedNodeId: null,
376
- lastClickedNodeId: null,
377
- lastClickedEdgeId: null,
378
- dimmed: EmptyStringSet
379
- },
380
- noReplace,
381
- "resetLastClicked"
382
- );
383
- },
384
- getElement: (fqn) => {
385
- const { view } = get();
386
- return view.nodes.find(({ id }) => id === fqn) ?? null;
387
- },
388
- triggerChangeElementStyle: (change) => {
389
- DEV && console.debug("triggerChangeElementStyle", change);
390
- const { view, updateView, onChange } = get();
391
- let hasChanges = !1;
392
- const nodes = view.nodes.map((origin) => {
393
- var _a, _b;
394
- if (!change.targets.includes(origin.id))
395
- return origin;
396
- let element = origin;
397
- for (const [key, value] of entries(change.style))
398
- switch (key) {
399
- case "shape":
400
- value !== element.shape && (element = {
401
- ...element,
402
- shape: value
403
- });
404
- break;
405
- case "color":
406
- value !== element.color && (element = {
407
- ...element,
408
- color: value
409
- });
410
- break;
411
- case "opacity":
412
- value !== ((_a = element.style) == null ? void 0 : _a.opacity) && (element = {
413
- ...element,
414
- style: {
415
- ...element.style,
416
- opacity: value
417
- }
418
- });
419
- break;
420
- case "border":
421
- value !== ((_b = element.style) == null ? void 0 : _b.border) && (element = {
422
- ...element,
423
- style: {
424
- ...element.style,
425
- border: value
426
- }
427
- });
428
- break;
429
- default:
430
- nonexhaustive(key);
431
- }
432
- return element !== origin ? (hasChanges = !0, element) : origin;
433
- });
434
- hasChanges && updateView({
435
- ...view,
436
- nodes
437
- }), onChange == null || onChange({ change });
438
- },
439
- cancelSaveManualLayout: () => {
440
- let { viewSyncDebounceTimeout } = get();
441
- return viewSyncDebounceTimeout !== null ? (clearTimeout(viewSyncDebounceTimeout), set({ viewSyncDebounceTimeout: null }), !0) : !1;
442
- },
443
- triggerSaveManualLayout: () => {
444
- const { xyflow, view, onChange, xystore, viewSyncDebounceTimeout } = get();
445
- viewSyncDebounceTimeout !== null && (clearTimeout(viewSyncDebounceTimeout), set({ viewSyncDebounceTimeout: null }));
446
- const { nodeLookup } = xystore.getState(), movedNodes = new StringSet();
447
- let bounds = {
448
- x: 0,
449
- y: 0,
450
- width: 1,
451
- height: 1
452
- };
453
- const nodes = reduce([...nodeLookup.values()], (acc, node) => {
454
- const dimensions = getNodeDimensions(node);
455
- isSamePoint$1(node.internals.positionAbsolute, node.data.element.position) || movedNodes.add(node.id);
456
- const rect = acc[node.data.fqn] = {
457
- isCompound: node.data.element.children.length > 0,
458
- x: Math.floor(node.internals.positionAbsolute.x),
459
- y: Math.floor(node.internals.positionAbsolute.y),
460
- width: Math.ceil(dimensions.width),
461
- height: Math.ceil(dimensions.height)
462
- };
463
- return bounds = getBoundsOfRects(bounds, rect), acc;
464
- }, {}), edges = reduce(xyflow.getEdges(), (acc, { source, target, data }) => {
465
- var _a, _b;
466
- let controlPoints = data.controlPoints;
467
- const sourceOrTargetMoved = movedNodes.has(source) || movedNodes.has(target);
468
- if ((!controlPoints || controlPoints.length === 0) && sourceOrTargetMoved && (controlPoints = bezierControlPoints(data.edge)), data.edge.points.length === 0 && (!controlPoints || controlPoints.length === 0))
469
- return acc;
470
- const _updated = acc[data.edge.id] = {
471
- points: data.edge.points
472
- };
473
- (_a = data.label) != null && _a.bbox && (_updated.labelBBox = (_b = data.label) == null ? void 0 : _b.bbox), data.edge.labelBBox && (_updated.labelBBox ?? (_updated.labelBBox = data.edge.labelBBox)), controlPoints && hasAtLeast$1(controlPoints, 1) && (_updated.controlPoints = controlPoints), !sourceOrTargetMoved && data.edge.dotpos && (_updated.dotpos = data.edge.dotpos);
474
- const allX = [
475
- ...data.edge.points.map((p) => p[0]),
476
- ...(controlPoints ?? []).map((p) => p.x),
477
- ..._updated.labelBBox ? [_updated.labelBBox.x, _updated.labelBBox.x + _updated.labelBBox.width] : []
478
- ], allY = [
479
- ...data.edge.points.map((p) => p[1]),
480
- ...(controlPoints ?? []).map((p) => p.y),
481
- ..._updated.labelBBox ? [_updated.labelBBox.y, _updated.labelBBox.y + _updated.labelBBox.height] : []
482
- ], rect = boxToRect({
483
- x: Math.floor(Math.min(...allX)),
484
- y: Math.floor(Math.min(...allY)),
485
- x2: Math.ceil(Math.max(...allX)),
486
- y2: Math.ceil(Math.max(...allY))
487
- });
488
- return bounds = getBoundsOfRects(bounds, rect), acc;
489
- }, {}), change = {
490
- op: "save-manual-layout",
491
- layout: {
492
- hash: view.hash,
493
- autoLayout: view.autoLayout,
494
- nodes,
495
- edges,
496
- ...bounds
497
- }
498
- };
499
- DEV && console.debug("triggerSaveManualLayout", change), isInside(bounds, view.bounds) || set(
500
- {
501
- view: {
502
- ...view,
503
- bounds
504
- }
505
- },
506
- noReplace,
507
- "update view bounds"
508
- ), onChange == null || onChange({ change });
509
- },
510
- scheduleSaveManualLayout: () => {
511
- let { viewSyncDebounceTimeout } = get();
512
- viewSyncDebounceTimeout && clearTimeout(viewSyncDebounceTimeout), set(
513
- {
514
- viewSyncDebounceTimeout: setTimeout(() => {
515
- get().triggerSaveManualLayout();
516
- }, 1e3)
517
- // explicit typecast to number to suppress TS error in astro build
518
- },
519
- noReplace,
520
- "debounce sync state"
521
- );
522
- },
523
- triggerOnNavigateTo: (xynodeId, event) => {
524
- const { view, xynodes: xynodes2, onNavigateTo, cancelSaveManualLayout } = get();
525
- if (!onNavigateTo)
526
- return;
527
- const xynode = xynodes2.find(({ id }) => id === xynodeId);
528
- invariant(xynode, `xynode not found: ${xynodeId}`);
529
- const element = view.nodes.find(({ id }) => id === xynodeId);
530
- invariant(element == null ? void 0 : element.navigateTo, `node is not navigable: ${xynodeId}`), cancelSaveManualLayout(), set(
531
- {
532
- lastClickedNodeId: xynodeId,
533
- lastOnNavigate: {
534
- fromView: view.id,
535
- toView: element.navigateTo,
536
- fromNode: element.id
537
- }
538
- },
539
- noReplace,
540
- "triggerOnNavigateTo"
541
- ), onNavigateTo(
542
- element.navigateTo,
543
- event,
544
- element,
545
- xynode
546
- );
547
- },
548
- goBack: () => {
549
- const { navigationHistory, navigationHistoryIndex, onNavigateTo } = get(), { viewId, nodeId } = nonNullable(navigationHistory[navigationHistoryIndex]), stepBack = navigationHistoryIndex > 0 && navigationHistory[navigationHistoryIndex - 1] || null;
550
- stepBack && onNavigateTo && (set(
551
- {
552
- lastClickedEdgeId: null,
553
- lastClickedNodeId: null,
554
- navigationHistoryIndex: navigationHistoryIndex - 1,
555
- lastOnNavigate: nodeId ? {
556
- fromView: viewId,
557
- toView: stepBack.viewId,
558
- fromNode: nodeId
559
- } : null
560
- },
561
- noReplace,
562
- "goBack"
563
- ), onNavigateTo(stepBack.viewId));
564
- },
565
- goForward: () => {
566
- const { navigationHistory, navigationHistoryIndex, onNavigateTo } = get(), { viewId } = nonNullable(navigationHistory[navigationHistoryIndex]), stepForward = navigationHistoryIndex < navigationHistory.length - 1 ? navigationHistory[navigationHistoryIndex + 1] : null;
567
- stepForward && onNavigateTo && (set(
568
- {
569
- lastClickedEdgeId: null,
570
- lastClickedNodeId: null,
571
- navigationHistoryIndex: navigationHistoryIndex + 1,
572
- lastOnNavigate: stepForward.nodeId ? {
573
- fromView: viewId,
574
- toView: stepForward.viewId,
575
- fromNode: stepForward.nodeId
576
- } : null
577
- },
578
- noReplace,
579
- "goForward"
580
- ), onNavigateTo(stepForward.viewId));
581
- },
582
- fitDiagram: (duration = 500) => {
583
- const { fitViewPadding, view, focusedNodeId, activeDynamicViewStep, xystore } = get(), { width, height, panZoom, transform } = xystore.getState(), bounds = view.bounds, maxZoom = Math.max(1, transform[2]), viewport = getViewportForBounds(bounds, width, height, MinZoom, maxZoom, fitViewPadding);
584
- panZoom == null || panZoom.setViewport(viewport, { duration }), (focusedNodeId ?? activeDynamicViewStep) !== null && set(
585
- {
586
- activeDynamicViewStep: null,
587
- focusedNodeId: null,
588
- dimmed: EmptyStringSet
589
- },
590
- noReplace,
591
- "unfocus"
592
- );
593
- },
594
- nextDynamicStep: (increment = 1) => {
595
- const { activeDynamicViewStep, activateDynamicStep } = get(), nextStep = (activeDynamicViewStep ?? 0) + increment;
596
- nextStep <= 0 || nextStep !== activeDynamicViewStep && activateDynamicStep(nextStep);
597
- },
598
- activateDynamicStep: (nextStep) => {
599
- const { isDynamicView, xyflow, xystore, fitViewPadding } = get();
600
- invariant(isDynamicView, "view is not dynamic");
601
- const edgeId = StepEdgeId(nextStep), dimmed2 = new StringSet();
602
- let edge = null;
603
- for (const e of xyflow.getEdges()) {
604
- if (e.id === edgeId) {
605
- edge = e;
606
- continue;
607
- }
608
- dimmed2.add(e.id);
609
- }
610
- invariant(!!edge, `edge not found: ${edgeId}`);
611
- const selected = [];
612
- for (const n of xyflow.getNodes()) {
613
- if (n.id === edge.source || n.id === edge.target) {
614
- selected.push(n);
615
- continue;
616
- }
617
- dimmed2.add(n.id);
618
- }
619
- const { fitView, transform } = xystore.getState();
620
- fitView({
621
- duration: 400,
622
- includeHiddenNodes: !0,
623
- maxZoom: Math.max(1, transform[2]),
624
- minZoom: MinZoom,
625
- padding: Math.max(fitViewPadding, 0.2),
626
- nodes: selected
627
- }), set(
628
- {
629
- focusedNodeId: null,
630
- activeDynamicViewStep: nextStep,
631
- dimmed: dimmed2
632
- },
633
- noReplace,
634
- "activateDynamicStep"
635
- );
636
- },
637
- stopDynamicView: () => {
638
- get().activeDynamicViewStep !== null && (set(
639
- {
640
- activeDynamicViewStep: null,
641
- focusedNodeId: null,
642
- dimmed: EmptyStringSet
643
- },
644
- noReplace,
645
- "stopDynamicView"
646
- ), get().fitDiagram());
647
- },
648
- onInit: (instance) => {
649
- const { xyflow, initialized } = get();
650
- (!initialized || xyflow !== instance) && set(
651
- {
652
- xyflow: instance,
653
- initialized: !0
654
- },
655
- noReplace,
656
- "onInit"
657
- );
658
- },
659
- highlightByElementNotation: (notation, onlyOfKind) => {
660
- const { xynodes: xynodes2, xyedges: xyedges2 } = get(), dimmed2 = new StringSet(map(xyedges2, prop("id")));
661
- xynodes2.forEach(({ id, data }) => {
662
- const node = data.element;
663
- (node.shape !== notation.shape || node.color !== notation.color || !notation.kinds.includes(node.kind) || onlyOfKind && node.kind !== onlyOfKind) && dimmed2.add(id);
664
- }), set({ dimmed: dimmed2 }, noReplace, "highlightByElementNotation");
665
- }
666
- }),
667
- {
668
- name: `${storeDevId} - ${props.view.id}`,
669
- enabled: DEV
670
- }
671
- )
672
- ),
673
- shallow
674
- );
675
- }
676
- const DiagramContext = createContext(null);
677
- function DiagramContextProvider({
678
- children,
679
- view,
680
- className,
681
- keepAspectRatio,
682
- whereFilter,
683
- ...props
684
- }) {
685
- const containerRef = useRef(null), xystore = useXYStoreApi(), xyflow = useXYFlow(), store = useRef(), getContainer = useCallback(() => containerRef.current, [containerRef]);
686
- return store.current || (store.current = createDiagramStore({
687
- xystore,
688
- xyflow,
689
- view,
690
- getContainer,
691
- whereFilter: structuredClone(whereFilter),
692
- ...props
693
- })), useUpdateEffect(
694
- () => {
695
- var _a;
696
- return (_a = store.current) == null ? void 0 : _a.setState({ xyflow, xystore, getContainer }, !1, "update xyflow and xystore");
697
- },
698
- [xyflow, xystore, getContainer]
699
- ), useUpdateEffect(
700
- () => {
701
- var _a;
702
- return (_a = store.current) == null ? void 0 : _a.setState(props, !1, "update incoming props");
703
- },
704
- [props]
705
- ), useUpdateEffect(
706
- () => {
707
- const current = store.current;
708
- invariant(current, "DiagramContext.store.current is not defined"), deepEqual(whereFilter, current.getState().whereFilter) || current.setState({ whereFilter: structuredClone(whereFilter) }, !1, "update where filter"), current.getState().updateView(view);
709
- },
710
- [view, whereFilter],
711
- (a, b) => shallowEqual(a[0], b[0]) && deepEqual(a[1], b[1])
712
- ), /* @__PURE__ */ jsx(
713
- "div",
714
- {
715
- ref: containerRef,
716
- className,
717
- ...keepAspectRatio && {
718
- style: {
719
- aspectRatio: `${Math.ceil(view.bounds.width)}/${Math.ceil(view.bounds.height)}`,
720
- maxHeight: Math.ceil(view.bounds.height)
721
- }
722
- },
723
- children: /* @__PURE__ */ jsx(DiagramContext.Provider, { value: store.current, children })
724
- }
725
- );
726
- }
727
- DiagramContextProvider.displayName = "DiagramContextProvider";
728
- function useDiagramState(selector2, equalityFn) {
729
- const store = useContext(DiagramContext);
730
- if (store === null)
731
- throw new Error("useDiagramStore could be used only inside DiagramContext");
732
- return useStoreWithEqualityFn(store, selector2, equalityFn ?? shallowEqual);
733
- }
734
- function useDiagramStoreApi() {
735
- const store = useContext(DiagramContext);
736
- if (store === null)
737
- throw new Error("useDiagramStoreApi could be used only inside DiagramContext");
738
- return store;
739
- }
740
- var vars = { safariAnimationHook: "var(--likec4-safari-animation-hook)", default: { font: "var(--likec4-default-font-family)" }, likec4: { font: "var(--likec4-font-family)", background: { color: "var(--likec4-background-color)", pattern: { color: "var(--likec4-background-pattern-color)" } } }, compound: { font: "var(--likec4-compound-font-family)", titleColor: "var(--likec4-compound-title-color)" }, element: { font: "var(--likec4-element-font-family)", fill: "var(--likec4-element-fill)", stroke: "var(--likec4-element-stroke)", hiContrast: "var(--likec4-element-hiContrast)", loContrast: "var(--likec4-element-loContrast)" }, relation: { lineColor: "var(--likec4-relation-lineColor)", labelColor: "var(--likec4-relation-labelColor)", labelBgColor: "var(--likec4-relation-labelBg)" }, optionsPanel: { top: "var(--likec4-options-panel-top)", right: "var(--likec4-options-panel-right)" }, navigationPanel: { top: "var(--likec4-navigation-panel-top)", left: "var(--likec4-navigation-panel-left)" } };
741
- function LikeC4CustomColors({ customColors }) {
742
- function toStyle(name, colorValues) {
743
- const rules = new Array(
744
- ...entries(colorValues.elements).map(([key, value]) => `${stripCssVarReference(vars.element[key])}: ${value};`),
745
- ...entries(colorValues.relationships).map(
746
- ([key, value]) => `${stripCssVarReference(vars.relation[key])}: ${value};`
747
- )
748
- ).join(`
749
- `);
750
- return `:where([data-likec4-color=${name}]) {
751
- ${rules}
752
- }`;
753
- }
754
- function stripCssVarReference(ref) {
755
- const end = ref.indexOf(",");
756
- return ref.substring(4, end == -1 ? ref.length - 1 : end);
757
- }
758
- const styles = entries(customColors).map(([name, color]) => toStyle(name, color)).join(`
759
- `);
760
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("style", { type: "text/css", dangerouslySetInnerHTML: { __html: styles } }) });
761
- }
762
- var cssReactFlow = "_2h8g8h0", cssDisablePan = "_2h8g8h1", cssTransparentBg = "_2h8g8h2", cssNoControls = "_2h8g8h3";
763
- function EnsureMantine({ children }) {
764
- if (!useContext(MantineContext))
765
- throw new Error("LikeC4Diagram must be a child of MantineProvider");
766
- return /* @__PURE__ */ jsx(Fragment, { children });
767
- }
768
- EnsureMantine.displayName = "EnsureMantine";
769
- function selectXYFlowSize(state) {
770
- return `${Math.round(state.width)}:${Math.round(state.height)}`;
771
- }
772
- function selectDiagramsize({ view, fitViewPadding }) {
773
- return [
774
- view.bounds.x,
775
- view.bounds.y,
776
- view.bounds.width,
777
- view.bounds.height,
778
- fitViewPadding
779
- ].map(Math.round).join(":");
780
- }
781
- function FitViewOnViewportResize({ diagramApi }) {
782
- const xyflowsize = useXYStore(selectXYFlowSize), viewsize = useDiagramState(selectDiagramsize), dimensions = xyflowsize + viewsize, prevDimensionsRef = useRef(dimensions);
783
- return useDebouncedEffect(
784
- () => {
785
- const { focusedNodeId, fitDiagram } = diagramApi.getState();
786
- focusedNodeId || prevDimensionsRef.current === dimensions || (prevDimensionsRef.current = dimensions, fitDiagram(250));
787
- },
788
- [dimensions, diagramApi],
789
- 250,
790
- 1e3
791
- ), null;
792
- }
793
- function selector$4({ view, viewportChanged, fitViewPadding }) {
794
- return {
795
- layoutId: view.id + "_" + view.autoLayout + "_" + fitViewPadding,
796
- viewportNotMoved: !viewportChanged
797
- };
798
- }
799
- function FitViewOnDiagramChange() {
800
- const {
801
- layoutId,
802
- viewportNotMoved
803
- } = useDiagramState(selector$4, shallowEqual), diagramApi = useDiagramStoreApi(), [currentLayoutId, setCurrent] = useState(layoutId), requiresFit = layoutId !== currentLayoutId;
804
- return useDebouncedEffect(
805
- () => {
806
- layoutId !== currentLayoutId && (setCurrent(layoutId), diagramApi.getState().fitDiagram(450));
807
- },
808
- [requiresFit, diagramApi],
809
- 50
810
- ), viewportNotMoved && !requiresFit ? /* @__PURE__ */ jsx(FitViewOnViewportResize, { diagramApi }) : null;
811
- }
812
- function useSetState(initialState) {
813
- const [state, _setState] = useState(initialState), setState = useCallback(
814
- (statePartial) => _setState((current) => {
815
- const next = {
816
- ...current,
817
- ...typeof statePartial == "function" ? statePartial(current) : statePartial
818
- };
819
- return shallowEqual(current, next) ? current : next;
820
- }),
821
- []
822
- );
823
- return [state, setState];
824
- }
825
- function SelectEdgesOnNodeFocus() {
826
- const diagramStore = useDiagramStoreApi(), focusedNodeId = useDiagramState((s) => s.focusedNodeId);
827
- return useUpdateEffect(() => {
828
- if (!focusedNodeId) {
829
- diagramStore.getState().xystore.getState().resetSelectedElements();
830
- return;
831
- }
832
- const container2 = diagramStore.getState().getContainer();
833
- if (!container2)
834
- return;
835
- const { width, height } = container2.getBoundingClientRect(), edgeChanges = [], nodeChanges = [], {
836
- edgeLookup,
837
- nodeLookup,
838
- panZoom,
839
- transform,
840
- triggerNodeChanges,
841
- triggerEdgeChanges
842
- } = diagramStore.getState().xystore.getState();
843
- let focusBounds = nodeToRect(nonNullable(nodeLookup.get(focusedNodeId)));
844
- for (const [, edge] of edgeLookup) {
845
- if (edge.source === focusedNodeId || edge.target === focusedNodeId) {
846
- const anotherNodeId = edge.source === focusedNodeId ? edge.target : edge.source, nd = nonNullable(nodeLookup.get(anotherNodeId));
847
- focusBounds = getBoundsOfRects(focusBounds, nodeToRect(nd));
848
- }
849
- edge.selected && edgeChanges.push({
850
- id: edge.id,
851
- type: "select",
852
- selected: !1
853
- });
854
- }
855
- for (const [, node] of nodeLookup)
856
- node.selected && node.id !== focusedNodeId && nodeChanges.push({
857
- id: node.id,
858
- type: "select",
859
- selected: !1
860
- });
861
- nodeChanges.push({
862
- id: focusedNodeId,
863
- type: "select",
864
- selected: !0
865
- }), edgeChanges.length > 0 && triggerEdgeChanges(edgeChanges), triggerNodeChanges(nodeChanges);
866
- const maxZoom = Math.max(1, transform[2]), viewport = getViewportForBounds$1(
867
- {
868
- x: focusBounds.x - 16,
869
- y: focusBounds.y - 16,
870
- width: focusBounds.width + 32,
871
- height: focusBounds.height + 32
872
- },
873
- width,
874
- height,
875
- 0.1,
876
- maxZoom,
877
- 0
878
- );
879
- panZoom == null || panZoom.setViewport(viewport, {
880
- duration: 350
881
- });
882
- }, [focusedNodeId]), useHotkeys(
883
- focusedNodeId !== null ? [
884
- ["Escape", (e) => {
885
- e.stopImmediatePropagation();
886
- const { xystore, fitDiagram } = diagramStore.getState();
887
- fitDiagram(), xystore.getState().resetSelectedElements();
888
- }, { preventDefault: !0 }]
889
- ] : []
890
- ), null;
891
- }
892
- const Open = (props) => /* @__PURE__ */ jsx(
893
- "marker",
894
- {
895
- viewBox: "-4 -4 14 16",
896
- refX: 5,
897
- refY: 4,
898
- markerWidth: "7",
899
- markerHeight: "8",
900
- preserveAspectRatio: "xMaxYMid meet",
901
- orient: "auto-start-reverse",
902
- ...props,
903
- children: /* @__PURE__ */ jsx(
904
- "path",
905
- {
906
- d: "M0,0 L7,4 L0,8 L4,4 Z",
907
- stroke: "context-stroke",
908
- fill: "context-stroke",
909
- strokeDasharray: 0,
910
- strokeWidth: 1,
911
- strokeLinecap: "round"
912
- }
913
- )
914
- }
915
- ), Arrow = (props) => /* @__PURE__ */ jsx(
916
- "marker",
917
- {
918
- viewBox: "-1 -1 12 10",
919
- refX: 4,
920
- refY: 3,
921
- markerWidth: "8",
922
- markerHeight: "6",
923
- preserveAspectRatio: "xMaxYMid meet",
924
- orient: "auto-start-reverse",
925
- ...props,
926
- children: /* @__PURE__ */ jsx(
927
- "path",
928
- {
929
- d: "M 0 0 L 8 3 L 0 6 L 1 3 z",
930
- fill: "context-stroke",
931
- strokeWidth: 0
932
- }
933
- )
934
- }
935
- ), OArrow = (props) => /* @__PURE__ */ jsx(
936
- "marker",
937
- {
938
- viewBox: "-1 -1 12 10",
939
- refX: 4,
940
- refY: 3,
941
- markerWidth: "8",
942
- markerHeight: "6",
943
- preserveAspectRatio: "xMaxYMid meet",
944
- orient: "auto-start-reverse",
945
- ...props,
946
- children: /* @__PURE__ */ jsx(
947
- "path",
948
- {
949
- d: "M 0 0 L 8 3 L 0 6 L 1 3 z",
950
- stroke: "context-stroke",
951
- fill: "var(--likec4-background-color)",
952
- strokeWidth: 1.25,
953
- strokeLinejoin: "miter",
954
- strokeLinecap: "square"
955
- }
956
- )
957
- }
958
- ), Diamond = (props) => /* @__PURE__ */ jsx(
959
- "marker",
960
- {
961
- viewBox: "-4 -4 16 14",
962
- refX: 5,
963
- refY: 4,
964
- markerWidth: "10",
965
- markerHeight: "8",
966
- preserveAspectRatio: "xMaxYMid meet",
967
- orient: "auto-start-reverse",
968
- ...props,
969
- children: /* @__PURE__ */ jsx(
970
- "path",
971
- {
972
- d: "M5,0 L10,4 L5,8 L0,4 Z",
973
- fill: "context-stroke",
974
- strokeWidth: 0,
975
- strokeLinecap: "round"
976
- }
977
- )
978
- }
979
- ), ODiamond = (props) => /* @__PURE__ */ jsx(
980
- "marker",
981
- {
982
- viewBox: "-4 -4 16 14",
983
- refX: 6,
984
- refY: 4,
985
- markerWidth: "10",
986
- markerHeight: "8",
987
- preserveAspectRatio: "xMaxYMid meet",
988
- orient: "auto-start-reverse",
989
- ...props,
990
- children: /* @__PURE__ */ jsx(
991
- "path",
992
- {
993
- d: "M5,0 L10,4 L5,8 L0,4 Z",
994
- stroke: "context-stroke",
995
- fill: "var(--likec4-background-color)",
996
- strokeWidth: 1.25,
997
- strokeLinecap: "round"
998
- }
999
- )
1000
- }
1001
- ), Dot = (props) => /* @__PURE__ */ jsx(
1002
- "marker",
1003
- {
1004
- viewBox: "0 0 10 10",
1005
- refX: 4,
1006
- refY: 4,
1007
- markerWidth: "6",
1008
- markerHeight: "6",
1009
- ...props,
1010
- children: /* @__PURE__ */ jsx(
1011
- "circle",
1012
- {
1013
- strokeWidth: 0,
1014
- fill: "context-stroke",
1015
- cx: 4,
1016
- cy: 4,
1017
- r: 3
1018
- }
1019
- )
1020
- }
1021
- ), ODot = (props) => /* @__PURE__ */ jsx(
1022
- "marker",
1023
- {
1024
- viewBox: "0 0 10 10",
1025
- refX: 4,
1026
- refY: 4,
1027
- markerWidth: "6",
1028
- markerHeight: "6",
1029
- ...props,
1030
- children: /* @__PURE__ */ jsx(
1031
- "circle",
1032
- {
1033
- strokeWidth: 1.25,
1034
- stroke: "context-stroke",
1035
- fill: "var(--likec4-background-color)",
1036
- cx: 4,
1037
- cy: 4,
1038
- r: 3
1039
- }
1040
- )
1041
- }
1042
- ), EdgeMarkers = {
1043
- Arrow,
1044
- OArrow,
1045
- Open,
1046
- Diamond,
1047
- ODiamond,
1048
- Dot,
1049
- ODot
1050
- };
1051
- var container$5 = "bexn7q1", dimmed$2 = "bexn7q2", edgePathBg = "bexn7q3", markerContext = "bexn7q4", controlPoint = "bexn7q5", controlDragging = "bexn7q6", cssEdgePath = "bexn7q8", stepEdgeNumber = "bexn7q9", varLabelX = "var(--bexn7qa)", varLabelY = "var(--bexn7qb)", edgeLabel = "bexn7qd", edgeLabelText = "bexn7qe";
1052
- function getNodeIntersectionFromCenterToPoint(intersectionNode, { x: x1, y: y1 }) {
1053
- const {
1054
- width: intersectionNodeWidth,
1055
- height: intersectionNodeHeight
1056
- } = getNodeDimensions(intersectionNode), intersectionNodePosition = intersectionNode.internals.positionAbsolute, w = intersectionNodeWidth / 2, h = intersectionNodeHeight / 2, x2 = intersectionNodePosition.x + w, y2 = intersectionNodePosition.y + h, xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h), yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h), a = 1 / (Math.abs(xx1) + Math.abs(yy1)), xx3 = a * xx1, yy3 = a * yy1, x = w * (xx3 + yy3) + x2, y = h * (-xx3 + yy3) + y2;
1057
- return { x, y };
1058
- }
1059
- const toMarker = (arrowType) => {
1060
- if (!(!arrowType || arrowType === "none"))
1061
- switch (arrowType) {
1062
- case "normal":
1063
- case "crow":
1064
- return "Arrow";
1065
- case "onormal":
1066
- return "OArrow";
1067
- case "diamond":
1068
- return "Diamond";
1069
- case "odiamond":
1070
- return "ODiamond";
1071
- case "open":
1072
- case "vee":
1073
- return "Open";
1074
- case "dot":
1075
- return "Dot";
1076
- case "odot":
1077
- return "ODot";
1078
- default:
1079
- nonexhaustive(arrowType);
1080
- }
1081
- };
1082
- function bezierPath(bezierSpline) {
1083
- let [start, ...points] = bezierSpline;
1084
- invariant(start, "start should be defined");
1085
- let path = `M ${start[0]},${start[1]}`;
1086
- for (; hasAtLeast$1(points, 3); ) {
1087
- const [cp1, cp2, end, ...rest] = points;
1088
- path = path + ` C ${cp1[0]},${cp1[1]} ${cp2[0]},${cp2[1]} ${end[0]},${end[1]}`, points = rest;
1089
- }
1090
- return invariant(points.length === 0, "all points should be consumed"), path;
1091
- }
1092
- const isSame = (a, b) => Math.abs(a - b) < 2.5, isSamePoint = (a, b) => {
1093
- const [ax, ay] = isArray(a) ? a : [a.x, a.y], [bx, by] = isArray(b) ? b : [b.x, b.y];
1094
- return isSame(ax, bx) && isSame(ay, by);
1095
- }, sameControlPoints = (a, b) => a === b ? !0 : !a || !b || a.length !== b.length ? !1 : a.every((ap, i) => isSamePoint(ap, b[i])), isEqualProps$2 = (prev, next) => prev.id === next.id && deepEqual(prev.source, next.source) && deepEqual(prev.target, next.target) && deepEqual(prev.selected ?? !1, next.selected ?? !1) && isSame(prev.sourceX, next.sourceX) && isSame(prev.sourceY, next.sourceY) && isSame(prev.targetX, next.targetX) && isSame(prev.targetY, next.targetY) && deepEqual(prev.data.stepNum, next.data.stepNum) && deepEqual(prev.data.label, next.data.label) && sameControlPoints(prev.data.controlPoints, next.data.controlPoints) && deepEqual(prev.data.edge, next.data.edge), curve = line().curve(curveCatmullRomOpen).x((d) => d.x).y((d) => d.y), RelationshipEdge = /* @__PURE__ */ memo(function({
1096
- id,
1097
- data,
1098
- selected,
1099
- sourceX,
1100
- sourceY,
1101
- targetX,
1102
- targetY,
1103
- style,
1104
- source,
1105
- target,
1106
- interactionWidth
1107
- }) {
1108
- const [isControlPointDragging, setIsControlPointDragging] = useState(!1), diagramStore = useDiagramStoreApi(), xyflowStore = useXYStoreApi(), { isActive, isEdgePathEditable, isHovered, isDimmed, hasOpenSourceRelation } = useDiagramState((s) => ({
1109
- isEdgePathEditable: s.readonly !== !0 && s.experimentalEdgeEditing === !0 && s.focusedNodeId === null && s.activeDynamicViewStep === null,
1110
- isActive: s.focusedNodeId === source || s.focusedNodeId === target || s.activeDynamicViewStep !== null && s.activeDynamicViewStep === data.stepNum,
1111
- isHovered: s.hoveredEdgeId === id,
1112
- isDimmed: s.dimmed.has(id),
1113
- hasOpenSourceRelation: !!s.onOpenSourceRelation
1114
- })), { nodeLookup, edgeLookup } = xyflowStore.getState(), sourceNode = nonNullable(nodeLookup.get(source), `source node ${source} not found`), targetNode = nonNullable(nodeLookup.get(target), `target node ${target} not found`), isModified = isTruthy(data.controlPoints) || !isSamePoint(sourceNode.internals.positionAbsolute, sourceNode.data.element.position) || !isSamePoint(targetNode.internals.positionAbsolute, targetNode.data.element.position), {
1115
- label,
1116
- edge: {
1117
- points: diagramEdgePoints,
1118
- line: line2 = "dashed",
1119
- color = "gray",
1120
- labelBBox,
1121
- ...diagramEdge
1122
- }
1123
- } = data;
1124
- let controlPoints = data.controlPoints ?? bezierControlPoints(data.edge);
1125
- const isStepEdge = data.stepNum !== null, isDotted = line2 === "dotted", isDashed = isDotted || line2 === "dashed";
1126
- let strokeDasharray;
1127
- isDotted ? strokeDasharray = "1,8" : isDashed && (strokeDasharray = "8,10");
1128
- let labelX = (labelBBox == null ? void 0 : labelBBox.x) ?? 0, labelY = (labelBBox == null ? void 0 : labelBBox.y) ?? 0;
1129
- const [labelPos, setLabelPosition] = useState({
1130
- x: (label == null ? void 0 : label.bbox.x) ?? labelX,
1131
- y: (label == null ? void 0 : label.bbox.y) ?? labelY
1132
- });
1133
- let edgePath;
1134
- if (isModified) {
1135
- const sourceCenterPos = { x: sourceX, y: sourceY }, targetCenterPos = { x: targetX, y: targetY }, points = diagramEdge.dir === "back" ? [
1136
- targetCenterPos,
1137
- getNodeIntersectionFromCenterToPoint(targetNode, first(controlPoints) ?? sourceCenterPos),
1138
- ...controlPoints,
1139
- getNodeIntersectionFromCenterToPoint(sourceNode, last(controlPoints) ?? targetCenterPos),
1140
- sourceCenterPos
1141
- ] : [
1142
- sourceCenterPos,
1143
- getNodeIntersectionFromCenterToPoint(sourceNode, first(controlPoints) ?? targetCenterPos),
1144
- ...controlPoints,
1145
- getNodeIntersectionFromCenterToPoint(targetNode, last(controlPoints) ?? sourceCenterPos),
1146
- targetCenterPos
1147
- ];
1148
- edgePath = nonNullable(curve(points));
1149
- } else
1150
- edgePath = bezierPath(diagramEdgePoints);
1151
- const svgPathRef = useRef(null);
1152
- useEffect(() => {
1153
- const path = svgPathRef.current;
1154
- if (!path) return;
1155
- const dompoint = path.getPointAtLength(path.getTotalLength() * 0.5), point = {
1156
- x: Math.round(dompoint.x),
1157
- y: Math.round(dompoint.y)
1158
- };
1159
- setLabelPosition((current) => isSamePoint(current, point) ? current : point);
1160
- }, [edgePath]), useDebouncedEffect(
1161
- () => {
1162
- !label || label.bbox.x === labelPos.x && label.bbox.y === labelPos.y || diagramStore.getState().xyflow.updateEdgeData(id, {
1163
- label: {
1164
- ...label,
1165
- bbox: {
1166
- ...label.bbox,
1167
- ...labelPos
1168
- }
1169
- }
1170
- });
1171
- },
1172
- [labelPos],
1173
- 50,
1174
- 300
1175
- ), (isModified || isControlPointDragging) && (labelX = labelPos.x, labelY = labelPos.y);
1176
- const onControlPointerDown = (index, e) => {
1177
- const { domNode } = xyflowStore.getState();
1178
- if (!domNode || e.pointerType !== "mouse")
1179
- return;
1180
- const { xyflow } = diagramStore.getState();
1181
- if (e.button === 2 && controlPoints.length > 1) {
1182
- const newControlPoints = controlPoints.slice();
1183
- newControlPoints.splice(index, 1), e.stopPropagation(), setTimeout(() => {
1184
- xyflow.updateEdgeData(id, { controlPoints: newControlPoints });
1185
- }, 10);
1186
- return;
1187
- }
1188
- if (e.button !== 0)
1189
- return;
1190
- const wasCanceled = diagramStore.getState().cancelSaveManualLayout();
1191
- e.stopPropagation();
1192
- let hasMoved = !1, pointer = { x: e.clientX, y: e.clientY };
1193
- const onPointerMove = (e2) => {
1194
- if (hasMoved || !isSamePoint(pointer, [e2.clientX, e2.clientY])) {
1195
- setIsControlPointDragging(!0), hasMoved = !0, pointer = { x: e2.clientX, y: e2.clientY };
1196
- const { x, y } = xyflow.screenToFlowPosition(pointer, { snapToGrid: !1 });
1197
- xyflow.updateEdgeData(id, (xyedge) => {
1198
- const cp = (xyedge.data.controlPoints ?? controlPoints).slice();
1199
- return cp[index] = {
1200
- x: Math.round(x),
1201
- y: Math.round(y)
1202
- }, {
1203
- controlPoints: cp
1204
- };
1205
- });
1206
- }
1207
- e2.stopPropagation();
1208
- }, onPointerUp = (e2) => {
1209
- domNode.removeEventListener("pointermove", onPointerMove, {
1210
- capture: !0
1211
- }), hasMoved && e2.stopPropagation(), (hasMoved || wasCanceled) && diagramStore.getState().scheduleSaveManualLayout(), setIsControlPointDragging(!1);
1212
- };
1213
- domNode.addEventListener("pointermove", onPointerMove, {
1214
- capture: !0
1215
- }), domNode.addEventListener("pointerup", onPointerUp, {
1216
- once: !0,
1217
- capture: !0
1218
- });
1219
- };
1220
- let markerStartName = toMarker(diagramEdge.tail), markerEndName = toMarker(diagramEdge.head ?? "normal");
1221
- diagramEdge.dir === "back" && ([markerStartName, markerEndName] = [markerEndName, markerStartName]);
1222
- const MarkerStart = markerStartName ? EdgeMarkers[markerStartName] : null, MarkerEnd = markerEndName ? EdgeMarkers[markerEndName] : null, labelZIndex = 1 + (isHovered ? ZIndexes.Element : edgeLookup.get(id).zIndex ?? ZIndexes.Edge), relationId = first(data.edge.relations), showOpenSourceRelation = hasOpenSourceRelation && relationId !== void 0;
1223
- return /* @__PURE__ */ jsxs(
1224
- "g",
1225
- {
1226
- className: clsx(
1227
- container$5,
1228
- isDimmed && dimmed$2,
1229
- isControlPointDragging && controlDragging
1230
- ),
1231
- "data-likec4-color": color,
1232
- "data-edge-dir": diagramEdge.dir,
1233
- "data-edge-active": isActive,
1234
- "data-edge-hovered": isHovered,
1235
- children: [
1236
- /* @__PURE__ */ jsx(
1237
- "path",
1238
- {
1239
- className: clsx("react-flow__edge-interaction"),
1240
- d: edgePath,
1241
- fill: "none",
1242
- stroke: "transparent",
1243
- strokeWidth: interactionWidth ?? 10
1244
- }
1245
- ),
1246
- /* @__PURE__ */ jsxs("g", { className: markerContext, children: [
1247
- /* @__PURE__ */ jsxs("defs", { children: [
1248
- MarkerStart && /* @__PURE__ */ jsx(MarkerStart, { id: "start" + id }),
1249
- MarkerEnd && /* @__PURE__ */ jsx(MarkerEnd, { id: "end" + id })
1250
- ] }),
1251
- /* @__PURE__ */ jsx(
1252
- "path",
1253
- {
1254
- className: clsx("react-flow__edge-path", edgePathBg),
1255
- d: edgePath,
1256
- style,
1257
- strokeLinecap: "round"
1258
- }
1259
- ),
1260
- /* @__PURE__ */ jsx(
1261
- "path",
1262
- {
1263
- ref: svgPathRef,
1264
- className: clsx("react-flow__edge-path", cssEdgePath),
1265
- d: edgePath,
1266
- style,
1267
- strokeLinecap: "round",
1268
- strokeDasharray,
1269
- markerStart: MarkerStart ? `url(#start${id})` : void 0,
1270
- markerEnd: MarkerEnd ? `url(#end${id})` : void 0
1271
- }
1272
- )
1273
- ] }),
1274
- isEdgePathEditable && /* @__PURE__ */ jsx(
1275
- "g",
1276
- {
1277
- onContextMenu: (e) => {
1278
- e.preventDefault(), e.stopPropagation();
1279
- },
1280
- children: controlPoints.map((p, i) => /* @__PURE__ */ jsx(
1281
- "circle",
1282
- {
1283
- onPointerDown: (e) => onControlPointerDown(i, e),
1284
- className: controlPoint,
1285
- cx: p.x,
1286
- cy: p.y,
1287
- r: 3
1288
- },
1289
- i
1290
- ))
1291
- }
1292
- ),
1293
- (data.label || isStepEdge) && /* @__PURE__ */ jsx(
1294
- EdgeLabel,
1295
- {
1296
- isDimmed,
1297
- color,
1298
- isModified,
1299
- labelX,
1300
- labelY,
1301
- isEdgePathEditable,
1302
- selected: selected ?? !1,
1303
- stepNum: data.stepNum,
1304
- label: data.label,
1305
- zIndex: labelZIndex,
1306
- isHovered,
1307
- isActive,
1308
- isStepEdge,
1309
- onClick: showOpenSourceRelation && ((e) => {
1310
- var _a, _b;
1311
- e.stopPropagation(), (_b = (_a = diagramStore.getState()).onOpenSourceRelation) == null || _b.call(_a, relationId);
1312
- })
1313
- }
1314
- )
1315
- ]
1316
- }
1317
- );
1318
- }, isEqualProps$2), EdgeLabel = ({
1319
- isDimmed,
1320
- color,
1321
- isModified,
1322
- labelX,
1323
- labelY,
1324
- isEdgePathEditable,
1325
- selected,
1326
- label,
1327
- stepNum,
1328
- isHovered,
1329
- isActive,
1330
- zIndex,
1331
- onClick
1332
- }) => /* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsxs(
1333
- Box,
1334
- {
1335
- className: clsx([
1336
- "nodrag nopan",
1337
- container$5,
1338
- edgeLabel,
1339
- isDimmed && dimmed$2
1340
- ]),
1341
- style: {
1342
- ...assignInlineVars({
1343
- [varLabelX]: isModified ? `calc(${labelX}px - 10%)` : `${labelX}px`,
1344
- [varLabelY]: isModified ? `${labelY - 5}px` : `${labelY}px`
1345
- }),
1346
- ...isEdgePathEditable && selected && {
1347
- pointerEvents: "none"
1348
- },
1349
- ...label && {
1350
- maxWidth: label.bbox.width + 18
1351
- },
1352
- zIndex
1353
- },
1354
- mod: {
1355
- "data-likec4-color": color,
1356
- "data-edge-hovered": isHovered,
1357
- "data-edge-active": isActive
1358
- },
1359
- onClick: onClick || void 0,
1360
- children: [
1361
- stepNum !== null && /* @__PURE__ */ jsx(Box, { className: stepEdgeNumber, children: stepNum }),
1362
- isTruthy(label == null ? void 0 : label.text) && /* @__PURE__ */ jsx(Box, { className: edgeLabelText, children: label.text })
1363
- ]
1364
- }
1365
- ) });
1366
- function NavigateToBtn({ xynodeId, className }) {
1367
- const diagramApi = useDiagramStoreApi();
1368
- return /* @__PURE__ */ jsx(
1369
- ActionIcon$2,
1370
- {
1371
- className: clsx("nodrag nopan", className),
1372
- radius: "md",
1373
- onPointerDownCapture: (e) => e.stopPropagation(),
1374
- onClick: (event) => {
1375
- event.stopPropagation(), diagramApi.getState().triggerOnNavigateTo(xynodeId, event);
1376
- },
1377
- role: "button",
1378
- onDoubleClick: (event) => event.stopPropagation(),
1379
- children: /* @__PURE__ */ jsx(
1380
- IconZoomScan,
1381
- {
1382
- style: {
1383
- width: "75%",
1384
- height: "75%"
1385
- }
1386
- }
1387
- )
1388
- }
1389
- );
1390
- }
1391
- var container$4 = "lbtzdo0", nodeHandlerInCenter = "lbtzdo1", dimmed$1 = "lbtzdo2", varOpacity = "var(--lbtzdo3)", varBorderTransparency = "var(--lbtzdo4)", compoundBody = "lbtzdo5", transparent = "lbtzdo7", title$2 = "lbtzdo8", titleWithNavigation = "lbtzdo9", indicator$1 = "lbtzdod", navigateBtn = "lbtzdof";
1392
- const isEqualProps$1 = (prev, next) => prev.id === next.id && deepEqual(prev.data.element, next.data.element), CompoundNodeMemo = /* @__PURE__ */ memo(function({
1393
- id,
1394
- data: {
1395
- element
1396
- }
1397
- }) {
1398
- const { color, style, depth = 1, ...compound } = element, opacity = clamp((style.opacity ?? 100) / 100, {
1399
- min: 0,
1400
- max: 1
1401
- }), borderTransparency = clamp(50 - opacity * 50, {
1402
- min: 0,
1403
- max: 50
1404
- }), { isHovered, isDimmed, hasOnNavigateTo } = useDiagramState((s) => ({
1405
- isHovered: s.hoveredNodeId === id,
1406
- isDimmed: s.dimmed.has(id),
1407
- hasOnNavigateTo: !!s.onNavigateTo
1408
- })), isnavigable = !!compound.navigateTo && hasOnNavigateTo;
1409
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1410
- /* @__PURE__ */ jsx(
1411
- Handle,
1412
- {
1413
- type: "target",
1414
- position: Position.Top,
1415
- className: nodeHandlerInCenter
1416
- }
1417
- ),
1418
- /* @__PURE__ */ jsxs(
1419
- Box,
1420
- {
1421
- className: clsx(
1422
- container$4,
1423
- "likec4-compound-node",
1424
- opacity < 1 && "likec4-compound-transparent",
1425
- isDimmed && dimmed$1
1426
- ),
1427
- mod: {
1428
- "compound-depth": depth,
1429
- "likec4-color": color,
1430
- hovered: isHovered
1431
- },
1432
- children: [
1433
- /* @__PURE__ */ jsx("svg", { className: indicator$1, children: /* @__PURE__ */ jsx(
1434
- "rect",
1435
- {
1436
- x: 0,
1437
- y: 0,
1438
- width: "100%",
1439
- height: "100%",
1440
- rx: 6
1441
- }
1442
- ) }),
1443
- /* @__PURE__ */ jsx(
1444
- "div",
1445
- {
1446
- className: clsx(
1447
- compoundBody,
1448
- opacity < 1 && transparent,
1449
- "likec4-compound"
1450
- ),
1451
- style: opacity < 1 ? {
1452
- ...assignInlineVars({
1453
- [varBorderTransparency]: `${borderTransparency}%`,
1454
- [varOpacity]: opacity.toFixed(2)
1455
- }),
1456
- borderStyle: style.border ?? "dashed"
1457
- } : void 0,
1458
- children: /* @__PURE__ */ jsx(
1459
- Text$1,
1460
- {
1461
- component: "div",
1462
- className: clsx(
1463
- title$2,
1464
- isnavigable && titleWithNavigation,
1465
- "likec4-compound-title"
1466
- ),
1467
- children: compound.title
1468
- }
1469
- )
1470
- }
1471
- ),
1472
- isnavigable && /* @__PURE__ */ jsx(NavigateToBtn, { xynodeId: id, className: navigateBtn })
1473
- ]
1474
- }
1475
- ),
1476
- /* @__PURE__ */ jsx(
1477
- Handle,
1478
- {
1479
- type: "source",
1480
- position: Position.Top,
1481
- className: nodeHandlerInCenter
1482
- }
1483
- )
1484
- ] });
1485
- }, isEqualProps$1);
1486
- var container$3 = "_1b6tkn1", handleCenter = "_1b6tkn2", containerAnimated = "_1b6tkn3", dimmed = "_1b6tkn4", indicator = "_1b6tkn7", fillElementFill = "_1b6tkn8", fillMixStroke = "_1b6tkna", hasIcon = "_1b6tknb", title$1 = "_1b6tkne", description$1 = "_1b6tknf", technology = "_1b6tkng", elementDataContainer = "_1b6tknh", elementTextData = "_1b6tkni", elementIcon = "_1b6tknj", cssShapeSvg = "_1b6tknm", cssNavigateBtn = "_1b6tknn", elementLink = "_4ysv0o0", trigger = "_4ysv0o1";
1487
- const selector$3 = (s) => {
1488
- const target = s.getContainer();
1489
- return target ? { target } : null;
1490
- };
1491
- function ElementLink({
1492
- element
1493
- }) {
1494
- invariant(element.links, "ElementLink: links are required");
1495
- const id = useId(), portalProps = useDiagramState(selector$3);
1496
- return /* @__PURE__ */ jsx("div", { className: elementLink, "data-likec4-linkid": id, children: /* @__PURE__ */ jsxs(
1497
- HoverCard,
1498
- {
1499
- position: "bottom-start",
1500
- shadow: "lg",
1501
- radius: "sm",
1502
- classNames: {
1503
- dropdown: clsx("nodrag", "nopan")
1504
- },
1505
- transitionProps: {
1506
- transition: "pop"
1507
- },
1508
- ...portalProps ? { portalProps } : { withinPortal: !1 },
1509
- floatingStrategy: "fixed",
1510
- openDelay: 350,
1511
- closeDelay: 800,
1512
- offset: {
1513
- mainAxis: 5,
1514
- crossAxis: -10
1515
- },
1516
- children: [
1517
- /* @__PURE__ */ jsx(HoverCardTarget, { children: /* @__PURE__ */ jsxs(
1518
- UnstyledButton,
1519
- {
1520
- className: clsx("nodrag nopan", trigger),
1521
- autoFocus: !1,
1522
- children: [
1523
- /* @__PURE__ */ jsx(IconLink, { size: 12 }),
1524
- /* @__PURE__ */ jsx("span", { children: "links" })
1525
- ]
1526
- }
1527
- ) }),
1528
- /* @__PURE__ */ jsx(
1529
- HoverCardDropdown,
1530
- {
1531
- onPointerDownCapture: (e) => e.stopPropagation(),
1532
- onClick: (e) => e.stopPropagation(),
1533
- p: "xs",
1534
- children: /* @__PURE__ */ jsx(Stack, { gap: "xs", children: element.links.map((link) => /* @__PURE__ */ jsxs(Group, { wrap: "nowrap", gap: "sm", children: [
1535
- /* @__PURE__ */ jsx(Box, { flex: "1", style: { overflow: "clip", maxWidth: clamp(element.width, { min: 200, max: 400 }) }, children: /* @__PURE__ */ jsx(Anchor, { href: link.url, target: "_blank", fz: "13", truncate: "end", children: link.title || link.url }) }),
1536
- /* @__PURE__ */ jsx(CopyButton, { value: link.url, children: ({ copied, copy }) => /* @__PURE__ */ jsx(
1537
- Button,
1538
- {
1539
- size: "compact-xs",
1540
- fz: "10",
1541
- variant: "light",
1542
- onClick: copy,
1543
- color: copied ? "teal" : "gray",
1544
- children: copied ? "copied" : "copy"
1545
- }
1546
- ) })
1547
- ] }, link.url)) })
1548
- }
1549
- )
1550
- ]
1551
- }
1552
- ) });
1553
- }
1554
- function cylinderSVGPath(diameter, height, tilt = 0.065) {
1555
- const radius = Math.round(diameter / 2), rx = radius, ry = toDomPrecision(tilt * radius), tiltAdjustedHeight = height - 2 * ry;
1556
- return {
1557
- path: ` M ${diameter},${ry}
1558
- a ${rx},${ry} 0,0,0 ${-diameter} 0
1559
- l 0,${tiltAdjustedHeight}
1560
- a ${rx},${ry} 0,0,0 ${diameter} 0
1561
- l 0,${-tiltAdjustedHeight}
1562
- z
1563
- `.replace(/\s+/g, " ").trim(),
1564
- ry,
1565
- rx
1566
- };
1567
- }
1568
- function queueSVGPath(width, height, tilt = 0.185) {
1569
- const diameter = height, ry = Math.round(diameter / 2), rx = toDomPrecision(diameter / 2 * tilt), tiltAdjustedWidth = width - 2 * rx;
1570
- return {
1571
- path: `
1572
- M ${rx},0
1573
- a ${rx},${ry} 0,0,0 0 ${diameter}
1574
- l ${tiltAdjustedWidth},0
1575
- a ${rx},${ry} 0,0,0 0 ${-diameter}
1576
- z`.replace(/\s+/g, " ").trim(),
1577
- ry,
1578
- rx
1579
- };
1580
- }
1581
- const PersonIcon = {
1582
- width: 115,
1583
- height: 120,
1584
- path: "M57.9197 0C10.9124 0 33.5766 54.75 33.5766 54.75C38.6131 62.25 45.3285 60.75 45.3285 66C45.3285 70.5 39.4526 72 33.5766 72.75C24.3431 72.75 15.9489 71.25 7.55474 84.75C2.51825 93 0 120 0 120H115C115 120 112.482 93 108.285 84.75C99.8905 70.5 91.4963 72.75 82.2628 72C76.3869 71.25 70.5109 69.75 70.5109 65.25C70.5109 60.75 77.2263 62.25 82.2628 54C82.2628 54.75 104.927 0 57.9197 0V0Z"
1585
- };
1586
- function ElementShapeSvg({ shape, w, h }) {
1587
- switch (shape) {
1588
- case "mobile":
1589
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1590
- /* @__PURE__ */ jsx(
1591
- "rect",
1592
- {
1593
- width: w,
1594
- height: h,
1595
- rx: 6,
1596
- className: fillMixStroke,
1597
- strokeWidth: 0
1598
- }
1599
- ),
1600
- /* @__PURE__ */ jsxs("g", { className: fillElementFill, strokeWidth: 0, children: [
1601
- /* @__PURE__ */ jsx("circle", { cx: 17, cy: h / 2, r: 12 }),
1602
- /* @__PURE__ */ jsx("rect", { x: 33, y: 12, width: w - 44, height: h - 24, rx: 5 })
1603
- ] })
1604
- ] });
1605
- case "browser":
1606
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1607
- /* @__PURE__ */ jsx(
1608
- "rect",
1609
- {
1610
- width: w,
1611
- height: h,
1612
- rx: 6,
1613
- className: fillMixStroke,
1614
- strokeWidth: 0
1615
- }
1616
- ),
1617
- /* @__PURE__ */ jsxs("g", { className: fillElementFill, strokeWidth: 0, children: [
1618
- /* @__PURE__ */ jsx("circle", { cx: 16, cy: 17, r: 7 }),
1619
- /* @__PURE__ */ jsx("circle", { cx: 36, cy: 17, r: 7 }),
1620
- /* @__PURE__ */ jsx("circle", { cx: 56, cy: 17, r: 7 }),
1621
- /* @__PURE__ */ jsx("rect", { x: 70, y: 8, width: w - 80, height: 17, rx: 4 }),
1622
- /* @__PURE__ */ jsx("rect", { x: 10, y: 32, width: w - 20, height: h - 42, rx: 4 })
1623
- ] })
1624
- ] });
1625
- case "person":
1626
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1627
- /* @__PURE__ */ jsx(
1628
- "rect",
1629
- {
1630
- width: w,
1631
- height: h,
1632
- rx: 6,
1633
- strokeWidth: 0
1634
- }
1635
- ),
1636
- /* @__PURE__ */ jsx(
1637
- "svg",
1638
- {
1639
- x: w - PersonIcon.width - 6,
1640
- y: h - PersonIcon.height,
1641
- width: PersonIcon.width,
1642
- height: PersonIcon.height,
1643
- viewBox: `0 0 ${PersonIcon.width} ${PersonIcon.height}`,
1644
- className: fillMixStroke,
1645
- children: /* @__PURE__ */ jsx(
1646
- "path",
1647
- {
1648
- strokeWidth: 0,
1649
- d: PersonIcon.path
1650
- }
1651
- )
1652
- }
1653
- )
1654
- ] });
1655
- case "queue": {
1656
- const { path, rx, ry } = queueSVGPath(w, h);
1657
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1658
- /* @__PURE__ */ jsx("path", { d: path, strokeWidth: 2 }),
1659
- /* @__PURE__ */ jsx("ellipse", { cx: rx, cy: ry, ry: ry - 0.75, rx, className: fillMixStroke, strokeWidth: 2 })
1660
- ] });
1661
- }
1662
- case "storage":
1663
- case "cylinder": {
1664
- const { path, rx, ry } = cylinderSVGPath(w, h);
1665
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1666
- /* @__PURE__ */ jsx("path", { d: path, strokeWidth: 2 }),
1667
- /* @__PURE__ */ jsx("ellipse", { cx: rx, cy: ry, ry, rx: rx - 0.75, className: fillMixStroke, strokeWidth: 2 })
1668
- ] });
1669
- }
1670
- case "rectangle":
1671
- return /* @__PURE__ */ jsx(
1672
- "rect",
1673
- {
1674
- width: w,
1675
- height: h,
1676
- rx: 6,
1677
- strokeWidth: 0
1678
- }
1679
- );
1680
- default:
1681
- return nonexhaustive(shape);
1682
- }
1683
- }
1684
- function SelectedIndicator({ shape, w, h }) {
1685
- switch (shape) {
1686
- case "queue":
1687
- return /* @__PURE__ */ jsx("path", { d: queueSVGPath(w, h).path });
1688
- case "storage":
1689
- case "cylinder":
1690
- return /* @__PURE__ */ jsx("path", { d: cylinderSVGPath(w, h).path });
1691
- default:
1692
- return /* @__PURE__ */ jsx(
1693
- "rect",
1694
- {
1695
- x: -1,
1696
- y: -1,
1697
- width: w + 2,
1698
- height: h + 2,
1699
- rx: 6
1700
- }
1701
- );
1702
- }
1703
- }
1704
- const Text = Text$1.withProps({
1705
- component: "div"
1706
- }), selectedScale = 1.015, variants = {
1707
- idle: (_, { scale }) => ({
1708
- scale: 1,
1709
- transition: {
1710
- delay: isNumber(scale) && scale > selectedScale ? 0.09 : 0
1711
- }
1712
- }),
1713
- selected: (_, { scale }) => ({
1714
- scale: selectedScale,
1715
- transition: {
1716
- delay: isNumber(scale) && scale > selectedScale ? 0.09 : 0
1717
- }
1718
- }),
1719
- // dimmed: {
1720
- // filter: 'brightness(0.5)',
1721
- // transition: {
1722
- // duration: 0.8,
1723
- // ease: 'easeInOut',
1724
- // }
1725
- // },
1726
- // dragging: {
1727
- // scale: selectedScale
1728
- // },
1729
- // hovered: {
1730
- // scale: 1.08
1731
- // },
1732
- // hover: (_, {scale}) => isNumber(scale) && scale < 1.02 ? ({
1733
- // scale: 1.08,
1734
- // }) : ({
1735
- // scale: 1.08,
1736
- // transition: {
1737
- // delay: 1
1738
- // }
1739
- // }),
1740
- hovered: {
1741
- scale: 1.06
1742
- },
1743
- tap: {
1744
- scale: 0.975
1745
- }
1746
- // tap: {
1747
- // scale: 0.9
1748
- // }
1749
- }, isEqualProps = (prev, next) => prev.id === next.id && deepEqual(prev.selected ?? !1, next.selected ?? !1) && deepEqual(prev.dragging ?? !1, next.dragging ?? !1) && deepEqual(prev.width ?? 0, next.width ?? 0) && deepEqual(prev.height ?? 0, next.height ?? 0) && deepEqual(prev.data.element, next.data.element), ElementNodeMemo = memo(function({
1750
- id,
1751
- data: {
1752
- element
1753
- },
1754
- dragging,
1755
- // draggable = false,
1756
- selected = !1,
1757
- width,
1758
- height
1759
- }) {
1760
- const store = useDiagramStoreApi(), { isHovered, isDimmed, hasOnNavigateTo, isHovercards, isInteractive, hasGoToSource } = useDiagramState((s) => ({
1761
- isHovered: s.hoveredNodeId === id,
1762
- isDimmed: s.dimmed.has(id),
1763
- isInteractive: s.nodesDraggable || s.nodesSelectable || !!s.onNavigateTo,
1764
- isHovercards: s.showElementLinks,
1765
- hasOnNavigateTo: !!s.onNavigateTo,
1766
- hasGoToSource: !!s.onOpenSourceElement
1767
- })), isNavigable = hasOnNavigateTo && !!element.navigateTo, w = toDomPrecision(width ?? element.width), h = toDomPrecision(height ?? element.height);
1768
- let animate = "idle";
1769
- switch (!0) {
1770
- case (dragging && selected):
1771
- animate = "selected";
1772
- break;
1773
- case dragging:
1774
- animate = "idle";
1775
- break;
1776
- case (isInteractive && isHovered):
1777
- animate = "hovered";
1778
- break;
1779
- case selected:
1780
- animate = "selected";
1781
- break;
1782
- }
1783
- const elementIcon2 = ElementIcon({ node: element, store });
1784
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1785
- /* @__PURE__ */ jsx(
1786
- Handle,
1787
- {
1788
- type: "target",
1789
- position: Position.Top,
1790
- className: handleCenter
1791
- }
1792
- ),
1793
- /* @__PURE__ */ jsxs(
1794
- m.div,
1795
- {
1796
- id,
1797
- className: clsx([
1798
- container$3,
1799
- isDimmed && dimmed,
1800
- animate !== "idle" && containerAnimated,
1801
- "likec4-element-node"
1802
- ]),
1803
- "data-hovered": !dragging && isHovered,
1804
- "data-likec4-color": element.color,
1805
- "data-likec4-shape": element.shape,
1806
- variants,
1807
- initial: !1,
1808
- animate,
1809
- ...isInteractive && {
1810
- whileTap: dragging ? animate : "tap"
1811
- },
1812
- children: [
1813
- /* @__PURE__ */ jsxs(
1814
- "svg",
1815
- {
1816
- className: clsx(
1817
- cssShapeSvg
1818
- ),
1819
- viewBox: `0 0 ${w} ${h}`,
1820
- width: w,
1821
- height: h,
1822
- children: [
1823
- /* @__PURE__ */ jsx("g", { className: indicator, children: /* @__PURE__ */ jsx(
1824
- SelectedIndicator,
1825
- {
1826
- shape: element.shape,
1827
- w,
1828
- h
1829
- }
1830
- ) }),
1831
- /* @__PURE__ */ jsx(
1832
- ElementShapeSvg,
1833
- {
1834
- shape: element.shape,
1835
- w,
1836
- h
1837
- }
1838
- )
1839
- ]
1840
- }
1841
- ),
1842
- /* @__PURE__ */ jsxs(
1843
- "div",
1844
- {
1845
- className: clsx(
1846
- elementDataContainer,
1847
- isTruthy(elementIcon2) && hasIcon,
1848
- "likec4-element"
1849
- ),
1850
- children: [
1851
- elementIcon2,
1852
- /* @__PURE__ */ jsxs("div", { className: clsx(elementTextData, "likec4-element-main-props"), children: [
1853
- /* @__PURE__ */ jsx(
1854
- Text,
1855
- {
1856
- className: clsx(title$1, "likec4-element-title"),
1857
- children: element.title
1858
- }
1859
- ),
1860
- element.technology && /* @__PURE__ */ jsx(
1861
- Text,
1862
- {
1863
- className: clsx(technology, "likec4-element-technology"),
1864
- children: element.technology
1865
- }
1866
- ),
1867
- element.description && /* @__PURE__ */ jsx(
1868
- Text,
1869
- {
1870
- className: clsx(description$1, "likec4-element-description"),
1871
- children: element.description
1872
- }
1873
- )
1874
- ] })
1875
- ]
1876
- }
1877
- ),
1878
- isHovercards && element.links && /* @__PURE__ */ jsx(ElementLink, { element }),
1879
- isNavigable && /* @__PURE__ */ jsx(NavigateToBtn, { xynodeId: id, className: cssNavigateBtn })
1880
- ]
1881
- }
1882
- ),
1883
- /* @__PURE__ */ jsx(
1884
- Handle,
1885
- {
1886
- type: "source",
1887
- position: Position.Top,
1888
- className: handleCenter
1889
- }
1890
- )
1891
- ] });
1892
- }, isEqualProps), ElementIcon = ({ node, store }) => {
1893
- if (!node.icon)
1894
- return null;
1895
- if (node.icon.startsWith("http://") || node.icon.startsWith("https://"))
1896
- return /* @__PURE__ */ jsx("div", { className: clsx(elementIcon, "likec4-element-icon"), children: /* @__PURE__ */ jsx("img", { src: node.icon, alt: node.title }) });
1897
- const RenderIcon = store.getState().renderIcon, icon = RenderIcon ? /* @__PURE__ */ jsx(RenderIcon, { node }) : null;
1898
- return icon ? /* @__PURE__ */ jsx("div", { className: clsx(elementIcon, "likec4-element-icon"), children: icon }) : null;
1899
- };
1900
- class Rect {
1901
- constructor() {
1902
- __publicField(this, "id");
1903
- __publicField(this, "minX", new Variable());
1904
- __publicField(this, "minY", new Variable());
1905
- __publicField(this, "maxX", new Variable());
1906
- __publicField(this, "maxY", new Variable());
1907
- }
1908
- get x() {
1909
- return this.minX;
1910
- }
1911
- get y() {
1912
- return this.minY;
1913
- }
1914
- get positionAbsolute() {
1915
- return {
1916
- x: this.x.value(),
1917
- y: this.y.value()
1918
- };
1919
- }
1920
- get dimensions() {
1921
- return {
1922
- width: this.maxX.value() - this.minX.value(),
1923
- height: this.maxY.value() - this.minY.value()
1924
- };
1925
- }
1926
- // Position relative to parent
1927
- get position() {
1928
- const positionAbsolute = this.positionAbsolute;
1929
- if (!this.parent)
1930
- return positionAbsolute;
1931
- const parentPosition = this.parent.positionAbsolute;
1932
- return {
1933
- x: positionAbsolute.x - parentPosition.x,
1934
- y: positionAbsolute.y - parentPosition.y
1935
- };
1936
- }
1937
- }
1938
- class Compound extends Rect {
1939
- constructor(solver, xynode, parent = null) {
1940
- super();
1941
- __publicField(this, "children", []);
1942
- this.solver = solver, this.parent = parent, this.id = xynode.id, solver.createConstraint(this.maxX, Operator.Ge, this.minX), solver.createConstraint(this.maxY, Operator.Ge, this.minY), parent && parent.addChild(this);
1943
- }
1944
- addChild(rect) {
1945
- this.children.push(rect);
1946
- const leftPadding = new Expression(
1947
- rect.minX,
1948
- [-1, this.minX]
1949
- ), { weak } = Strength;
1950
- this.solver.createConstraint(leftPadding, Operator.Ge, 40), this.solver.createConstraint(leftPadding, Operator.Eq, 41, weak);
1951
- const topPadding = new Expression(
1952
- rect.minY,
1953
- [-1, this.minY]
1954
- );
1955
- this.solver.createConstraint(topPadding, Operator.Ge, 54), this.solver.createConstraint(topPadding, Operator.Eq, 55, weak);
1956
- const rightPadding = new Expression(
1957
- this.maxX,
1958
- [-1, rect.maxX]
1959
- );
1960
- this.solver.createConstraint(rightPadding, Operator.Ge, 40), this.solver.createConstraint(rightPadding, Operator.Eq, 41, weak);
1961
- const bottomPadding = new Expression(
1962
- this.maxY,
1963
- [-1, rect.maxY]
1964
- );
1965
- this.solver.createConstraint(bottomPadding, Operator.Ge, 40), this.solver.createConstraint(bottomPadding, Operator.Eq, 41, weak);
1966
- }
1967
- }
1968
- class Leaf extends Rect {
1969
- constructor(solver, xynode, parent = null, isEditing = !1) {
1970
- super(), this.parent = parent, this.isEditing = isEditing, this.id = xynode.id;
1971
- const x = Math.ceil(xynode.internals.positionAbsolute.x), y = Math.ceil(xynode.internals.positionAbsolute.y), { width, height } = getNodeDimensions(xynode);
1972
- if (this.maxX = new Expression(this.minX, Math.ceil(width)), this.maxY = new Expression(this.minY, Math.ceil(height)), isEditing) {
1973
- const superStrong = Strength.create(100, 0, 0);
1974
- solver.addEditVariable(this.minX, superStrong), solver.addEditVariable(this.minY, superStrong), solver.suggestValue(this.minX, x), solver.suggestValue(this.minY, y);
1975
- } else {
1976
- const halfMedium = Strength.create(0, 1, 0, 0.5);
1977
- solver.createConstraint(this.minX, Operator.Eq, x, halfMedium), solver.createConstraint(this.minY, Operator.Eq, y, halfMedium);
1978
- }
1979
- parent && parent.addChild(this);
1980
- }
1981
- }
1982
- function createLayoutConstraints(xyflowApi, draggingNodeId) {
1983
- var _a;
1984
- const { parentLookup, nodeLookup } = xyflowApi.getState(), solver = new Solver(), rects = /* @__PURE__ */ new Map(), traverse = new Array();
1985
- for (const [, xynode] of nodeLookup)
1986
- isNullish(xynode.parentId) && traverse.push({
1987
- xynode,
1988
- parent: null
1989
- });
1990
- for (; traverse.length > 0; ) {
1991
- const { xynode, parent } = traverse.shift(), isDragging = xynode.dragging === !0 || xynode.id === draggingNodeId, shouldTraverse = !isDragging && xynode.type === "compound" && isAncestor(xynode.id, draggingNodeId), rect = shouldTraverse ? new Compound(solver, xynode, parent) : new Leaf(solver, xynode, parent, isDragging);
1992
- rects.set(xynode.id, rect), shouldTraverse && ((_a = parentLookup.get(xynode.id)) == null || _a.forEach((child) => {
1993
- traverse.push({
1994
- xynode: child,
1995
- parent: rect
1996
- });
1997
- }));
1998
- }
1999
- solver.updateVariables(), solver.maxIterations = 1e3;
2000
- const rectsToUpdate = [...rects.values()].filter((r) => r.id !== draggingNodeId);
2001
- function updateXYFlowNodes() {
2002
- solver.updateVariables(), xyflowApi.getState().triggerNodeChanges(
2003
- rectsToUpdate.reduce((acc, r) => (acc.push({
2004
- id: r.id,
2005
- type: "position",
2006
- dragging: !1,
2007
- position: r.position,
2008
- positionAbsolute: r.positionAbsolute
2009
- }), r instanceof Compound && acc.push({
2010
- id: r.id,
2011
- type: "dimensions",
2012
- setAttributes: !0,
2013
- dimensions: r.dimensions
2014
- }), acc), [])
2015
- );
2016
- }
2017
- let animationFrameId = null;
2018
- function onNodeDrag(xynode) {
2019
- rectsToUpdate.length !== 0 && (animationFrameId ?? (animationFrameId = requestAnimationFrame(() => {
2020
- animationFrameId = null;
2021
- const pos = nodeLookup.get(xynode.id).internals.positionAbsolute, rect = nonNullable(rects.get(xynode.id));
2022
- solver.suggestValue(rect.minX, Math.ceil(pos.x)), solver.suggestValue(rect.minY, Math.ceil(pos.y)), updateXYFlowNodes();
2023
- })));
2024
- }
2025
- return {
2026
- onNodeDrag,
2027
- updateXYFlowNodes
2028
- };
2029
- }
2030
- function useLayoutConstraints() {
2031
- const diagramApi = useDiagramStoreApi(), solverRef = useRef();
2032
- return useMemo(() => ({
2033
- onNodeDragStart: (_event, xynode) => {
2034
- const { cancelSaveManualLayout, xystore } = diagramApi.getState();
2035
- cancelSaveManualLayout(), solverRef.current = createLayoutConstraints(xystore, xynode.id);
2036
- },
2037
- onNodeDrag: (_event, xynode) => {
2038
- var _a;
2039
- invariant(solverRef.current, "solverRef.current should be defined"), (_a = solverRef.current) == null || _a.onNodeDrag(xynode);
2040
- },
2041
- onNodeDragStop: (_event, _xynode) => {
2042
- diagramApi.getState().scheduleSaveManualLayout(), solverRef.current = void 0;
2043
- }
2044
- }), [diagramApi]);
2045
- }
2046
- function useXYFlowEvents() {
2047
- const diagramApi = useDiagramStoreApi(), lastClickTimestamp = useRef(), dblclickTimeout = useRef(), hoveredNodeFromOnEdgeEnterRef = useRef("");
2048
- return useMemo(() => {
2049
- const dbclickLock = () => dblclickTimeout.current !== void 0 ? !0 : (dblclickTimeout.current = window.setTimeout(() => {
2050
- dblclickTimeout.current = void 0;
2051
- }, 250), !1), lastClickWasRecent = (ms = 1500) => lastClickTimestamp.current && Date.now() - lastClickTimestamp.current < ms;
2052
- return {
2053
- onDoubleClick: (event) => {
2054
- const {
2055
- onCanvasDblClick,
2056
- zoomable,
2057
- xystore,
2058
- viewportChanged,
2059
- fitDiagram,
2060
- resetLastClicked
2061
- } = diagramApi.getState();
2062
- resetLastClicked(), xystore.getState().resetSelectedElements(), onCanvasDblClick || event.stopPropagation(), zoomable && viewportChanged && fitDiagram(), onCanvasDblClick == null || onCanvasDblClick(event);
2063
- },
2064
- onPaneClick: (event) => {
2065
- if (dbclickLock())
2066
- return;
2067
- const {
2068
- focusedNodeId,
2069
- activeDynamicViewStep,
2070
- fitDiagram,
2071
- onCanvasClick,
2072
- xystore,
2073
- resetLastClicked
2074
- } = diagramApi.getState();
2075
- (focusedNodeId ?? activeDynamicViewStep) !== null && (fitDiagram(), onCanvasClick || event.stopPropagation()), resetLastClicked(), xystore.getState().resetSelectedElements(), onCanvasClick == null || onCanvasClick(event);
2076
- },
2077
- onNodeContextMenu: (event, xynode) => {
2078
- var _a, _b;
2079
- diagramApi.getState().setLastClickedNode(xynode.id), (_b = (_a = diagramApi.getState()).onNodeContextMenu) == null || _b.call(_a, {
2080
- element: xynode.data.element,
2081
- xynode,
2082
- event
2083
- });
2084
- },
2085
- onPaneContextMenu: (event) => {
2086
- var _a, _b;
2087
- diagramApi.getState().resetLastClicked(), (_b = (_a = diagramApi.getState()).onCanvasContextMenu) == null || _b.call(_a, event);
2088
- },
2089
- onEdgeContextMenu: (event, xyedge) => {
2090
- var _a, _b;
2091
- diagramApi.getState().setLastClickedEdge(xyedge.id), (_b = (_a = diagramApi.getState()).onEdgeContextMenu) == null || _b.call(_a, {
2092
- edge: xyedge.data.edge,
2093
- xyedge,
2094
- event
2095
- });
2096
- },
2097
- onNodeClick: (event, xynode) => {
2098
- const {
2099
- focusedNodeId,
2100
- fitDiagram,
2101
- focusOnNode,
2102
- onNodeClick,
2103
- xystore,
2104
- enableFocusMode,
2105
- lastClickedNodeId,
2106
- nodesSelectable,
2107
- setLastClickedNode,
2108
- onOpenSourceElement
2109
- } = diagramApi.getState();
2110
- setLastClickedNode(xynode.id);
2111
- const shallChangeFocus = !!focusedNodeId && focusedNodeId !== xynode.id, clickedRecently = lastClickedNodeId === xynode.id && lastClickWasRecent();
2112
- if (clickedRecently && onOpenSourceElement && onOpenSourceElement(xynode.data.element.id), enableFocusMode || nodesSelectable) {
2113
- let stopPropagation = !0;
2114
- switch (!0) {
2115
- case (enableFocusMode && !focusedNodeId && clickedRecently):
2116
- case (enableFocusMode && shallChangeFocus): {
2117
- focusOnNode(xynode.id);
2118
- break;
2119
- }
2120
- case (enableFocusMode && focusedNodeId === xynode.id && clickedRecently): {
2121
- fitDiagram();
2122
- break;
2123
- }
2124
- case nodesSelectable: {
2125
- xystore.getState().addSelectedNodes([xynode.id]);
2126
- break;
2127
- }
2128
- default:
2129
- stopPropagation = !1;
2130
- }
2131
- !onNodeClick && stopPropagation && event.stopPropagation();
2132
- }
2133
- lastClickTimestamp.current = Date.now(), onNodeClick == null || onNodeClick({
2134
- element: xynode.data.element,
2135
- xynode,
2136
- event
2137
- });
2138
- },
2139
- onNodeDoubleClick: (event, xynode) => {
2140
- const {
2141
- focusedNodeId,
2142
- enableFocusMode,
2143
- fitDiagram,
2144
- focusOnNode,
2145
- setLastClickedNode
2146
- } = diagramApi.getState();
2147
- setLastClickedNode(xynode.id), lastClickTimestamp.current = Date.now(), (isNonNullish(focusedNodeId) || enableFocusMode) && (focusedNodeId === xynode.id ? fitDiagram() : focusOnNode(xynode.id), event.stopPropagation());
2148
- },
2149
- onEdgeClick: (event, xyedge) => {
2150
- const {
2151
- lastClickedEdgeId,
2152
- isDynamicView,
2153
- enableDynamicViewWalkthrough,
2154
- activateDynamicStep,
2155
- activeDynamicViewStep,
2156
- focusedNodeId,
2157
- xystore,
2158
- nodesSelectable,
2159
- focusOnNode,
2160
- onEdgeClick,
2161
- setLastClickedEdge
2162
- } = diagramApi.getState();
2163
- lastClickedEdgeId !== xyedge.id && setLastClickedEdge(xyedge.id);
2164
- const isNotAFirstClick = lastClickedEdgeId === xyedge.id, isEdgeOfFocusedNode = isTruthy(focusedNodeId) && (focusedNodeId === xyedge.source || focusedNodeId === xyedge.target);
2165
- if (isDynamicView && enableDynamicViewWalkthrough && (isEdgeOfFocusedNode || isNotAFirstClick || isNonNullish(activeDynamicViewStep))) {
2166
- const nextStep = extractStep(xyedge.data.edge.id);
2167
- if (activeDynamicViewStep !== nextStep) {
2168
- activateDynamicStep(nextStep), event.stopPropagation();
2169
- return;
2170
- }
2171
- }
2172
- isEdgeOfFocusedNode ? (focusOnNode(focusedNodeId === xyedge.source ? xyedge.target : xyedge.source), onEdgeClick || event.stopPropagation()) : nodesSelectable && (xystore.getState().addSelectedEdges([xyedge.id]), onEdgeClick || event.stopPropagation()), onEdgeClick == null || onEdgeClick({
2173
- edge: xyedge.data.edge,
2174
- xyedge,
2175
- event
2176
- });
2177
- },
2178
- onEdgeDoubleClick: (event, xyedge) => {
2179
- diagramApi.getState().setLastClickedEdge(xyedge.id);
2180
- const {
2181
- enableFocusMode,
2182
- isDynamicView,
2183
- enableDynamicViewWalkthrough,
2184
- focusOnNode,
2185
- activateDynamicStep,
2186
- activeDynamicViewStep
2187
- } = diagramApi.getState();
2188
- if (isDynamicView && enableDynamicViewWalkthrough) {
2189
- const nextStep = extractStep(xyedge.data.edge.id);
2190
- activeDynamicViewStep !== nextStep && (activateDynamicStep(extractStep(xyedge.data.edge.id)), event.stopPropagation());
2191
- return;
2192
- }
2193
- if (enableFocusMode) {
2194
- focusOnNode(xyedge.source), event.stopPropagation();
2195
- return;
2196
- }
2197
- },
2198
- onMoveEnd: (event, _viewport) => {
2199
- const viewportChanged = !!event;
2200
- viewportChanged !== diagramApi.getState().viewportChanged && diagramApi.setState({ viewportChanged }, !1, `viewport-changed: ${viewportChanged}`);
2201
- },
2202
- onNodeMouseEnter: (_event, xynode) => {
2203
- hoveredNodeFromOnEdgeEnterRef.current = "", diagramApi.getState().setHoveredNode(xynode.id);
2204
- },
2205
- onNodeMouseLeave: (_event, xynode) => {
2206
- const { hoveredNodeId, setHoveredNode } = diagramApi.getState();
2207
- hoveredNodeId === xynode.id && setHoveredNode(null);
2208
- },
2209
- onEdgeMouseEnter: (_event, { id, source, target }) => {
2210
- const { hoveredNodeId, focusedNodeId, setHoveredEdge, setHoveredNode } = diagramApi.getState();
2211
- if (setHoveredEdge(id), (focusedNodeId === source || focusedNodeId === target) && focusedNodeId !== hoveredNodeId) {
2212
- const next = hoveredNodeFromOnEdgeEnterRef.current = source === focusedNodeId ? target : source;
2213
- setHoveredNode(next);
2214
- }
2215
- },
2216
- onEdgeMouseLeave: (_event, xyedge) => {
2217
- const { hoveredEdgeId, setHoveredEdge, hoveredNodeId, setHoveredNode } = diagramApi.getState();
2218
- hoveredEdgeId === xyedge.id && setHoveredEdge(null), hoveredNodeId === hoveredNodeFromOnEdgeEnterRef.current && setHoveredNode(null), hoveredNodeFromOnEdgeEnterRef.current = "";
2219
- }
2220
- };
2221
- }, [diagramApi]);
2222
- }
2223
- const nodeTypes = {
2224
- element: ElementNodeMemo,
2225
- compound: CompoundNodeMemo
2226
- }, edgeTypes = {
2227
- relationship: RelationshipEdge
2228
- }, selector$2 = (s) => ({
2229
- nodes: s.xynodes,
2230
- edges: s.xyedges,
2231
- onInit: s.onInit,
2232
- onNodesChange: s.onNodesChange,
2233
- onEdgesChange: s.onEdgesChange,
2234
- nodesSelectable: s.nodesSelectable || s.focusedNodeId !== null,
2235
- nodesDraggable: s.nodesDraggable,
2236
- fitView: s.fitViewEnabled,
2237
- fitViewPadding: s.fitViewPadding,
2238
- hasOnNavigateTo: !!s.onNavigateTo,
2239
- hasOnNodeClick: !!s.onNodeClick,
2240
- hasOnNodeContextMenu: !!s.onNodeContextMenu,
2241
- hasOnCanvasContextMenu: !!s.onCanvasContextMenu,
2242
- hasOnEdgeContextMenu: !!s.onEdgeContextMenu,
2243
- hasOnEdgeClick: !!s.onEdgeClick,
2244
- zoomable: s.zoomable,
2245
- pannable: s.pannable,
2246
- // If fitView is not enabled
2247
- // And diagram starts with a negative x or y - we need to translate viewprot
2248
- translateX: s.fitViewEnabled ? 0 : -Math.min(s.view.bounds.x, 0),
2249
- translateY: s.fitViewEnabled ? 0 : -Math.min(s.view.bounds.y, 0)
2250
- });
2251
- function XYFlow({
2252
- colorMode = "system",
2253
- className,
2254
- children,
2255
- style
2256
- }) {
2257
- const xyflowApi = useXYStoreApi(), {
2258
- nodes,
2259
- edges,
2260
- onInit,
2261
- onNodesChange,
2262
- onEdgesChange,
2263
- nodesSelectable,
2264
- nodesDraggable,
2265
- fitView,
2266
- fitViewPadding,
2267
- pannable,
2268
- zoomable,
2269
- hasOnNodeClick,
2270
- hasOnNavigateTo,
2271
- hasOnNodeContextMenu,
2272
- hasOnCanvasContextMenu,
2273
- hasOnEdgeContextMenu,
2274
- translateX,
2275
- translateY
2276
- } = useDiagramState(selector$2, shallowEqual), [zoomOnDoubleClick, setZoomOnDoubleClick] = useState(zoomable), layoutConstraints = useLayoutConstraints(), handlers = useXYFlowEvents();
2277
- return useOnViewportChange({
2278
- onEnd: ({ x, y, zoom }) => {
2279
- const roundedX = Math.round(x), roundedY = Math.round(y);
2280
- (x !== roundedX || y !== roundedY) && xyflowApi.setState({ transform: [roundedX, roundedY, zoom] }), setZoomOnDoubleClick(zoomable && zoom < 1.1);
2281
- }
2282
- }), /* @__PURE__ */ jsx(
2283
- ReactFlow,
2284
- {
2285
- className,
2286
- style,
2287
- ...colorMode && { colorMode },
2288
- nodes,
2289
- edges,
2290
- nodeTypes,
2291
- edgeTypes,
2292
- zoomOnPinch: zoomable,
2293
- zoomOnScroll: !pannable && zoomable,
2294
- ...!zoomable && {
2295
- zoomActivationKeyCode: null
2296
- },
2297
- maxZoom: zoomable ? MaxZoom : 1,
2298
- minZoom: zoomable ? MinZoom : 1,
2299
- fitView,
2300
- fitViewOptions: useMemo(() => ({
2301
- minZoom: MinZoom,
2302
- maxZoom: 1,
2303
- padding: fitViewPadding,
2304
- includeHiddenNodes: !0
2305
- }), [fitViewPadding]),
2306
- ...!fitView && {
2307
- viewport: {
2308
- x: translateX,
2309
- y: translateY,
2310
- zoom: 1
2311
- }
2312
- },
2313
- preventScrolling: zoomable || pannable,
2314
- defaultMarkerColor: "var(--xy-edge-stroke)",
2315
- noDragClassName: "nodrag",
2316
- noPanClassName: "nopan",
2317
- panOnScroll: pannable,
2318
- panOnDrag: pannable,
2319
- ...!pannable && {
2320
- selectionKeyCode: null
2321
- },
2322
- elementsSelectable: nodesSelectable,
2323
- nodesFocusable: nodesDraggable || nodesSelectable || hasOnNodeClick || hasOnNavigateTo,
2324
- edgesFocusable: !1,
2325
- nodesDraggable,
2326
- ...nodesDraggable && {
2327
- onNodeDragStart: layoutConstraints.onNodeDragStart,
2328
- onNodeDrag: layoutConstraints.onNodeDrag,
2329
- onNodeDragStop: layoutConstraints.onNodeDragStop
2330
- },
2331
- nodeDragThreshold: 3,
2332
- zoomOnDoubleClick,
2333
- elevateNodesOnSelect: !1,
2334
- selectNodesOnDrag: !1,
2335
- onNodesChange,
2336
- onEdgesChange,
2337
- onDoubleClick: handlers.onDoubleClick,
2338
- onPaneClick: handlers.onPaneClick,
2339
- onMoveEnd: handlers.onMoveEnd,
2340
- onNodeMouseEnter: handlers.onNodeMouseEnter,
2341
- onNodeMouseLeave: handlers.onNodeMouseLeave,
2342
- onEdgeMouseEnter: handlers.onEdgeMouseEnter,
2343
- onEdgeMouseLeave: handlers.onEdgeMouseLeave,
2344
- onNodeClick: handlers.onNodeClick,
2345
- onNodeDoubleClick: handlers.onNodeDoubleClick,
2346
- onEdgeClick: handlers.onEdgeClick,
2347
- onEdgeDoubleClick: handlers.onEdgeDoubleClick,
2348
- onInit,
2349
- ...hasOnNodeContextMenu && {
2350
- onNodeContextMenu: handlers.onNodeContextMenu
2351
- },
2352
- ...hasOnEdgeContextMenu && {
2353
- onEdgeContextMenu: handlers.onEdgeContextMenu
2354
- },
2355
- ...hasOnCanvasContextMenu && {
2356
- onPaneContextMenu: handlers.onPaneContextMenu
2357
- },
2358
- children
2359
- }
2360
- );
2361
- }
2362
- XYFlow.displayName = "XYFlow";
2363
- var container$2 = "hbkbtj0", card$1 = "hbkbtj1", title = "hbkbtj2", description = "hbkbtj3";
2364
- const selector$1 = (s) => ({
2365
- id: s.view.id,
2366
- title: s.view.title ?? "untitled",
2367
- description: s.view.description,
2368
- links: s.view.links
2369
- });
2370
- function DiagramTitlePanel() {
2371
- const { id, title: title$12, description: description$12, links } = useDiagramState(selector$1), [isCollapsed, setCollapsed] = useLocalStorage({
2372
- key: "diagram-title-panel-collapsed",
2373
- defaultValue: !1
2374
- }), toggle = () => setCollapsed((v) => !v);
2375
- return /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx(
2376
- m.div,
2377
- {
2378
- initial: { opacity: 0.05, scale: 0.7 },
2379
- animate: { opacity: 1, scale: 1 },
2380
- exit: {
2381
- opacity: 0.05,
2382
- scale: 0.6
2383
- },
2384
- className: clsx("react-flow__panel", container$2),
2385
- style: {
2386
- transformOrigin: "left center"
2387
- },
2388
- children: /* @__PURE__ */ jsxs(
2389
- Card,
2390
- {
2391
- radius: "sm",
2392
- className: card$1,
2393
- withBorder: !isCollapsed,
2394
- p: isCollapsed ? "sm" : "md",
2395
- onDoubleClick: (e) => e.stopPropagation(),
2396
- children: [
2397
- isCollapsed && /* @__PURE__ */ jsx(CardSection, { children: /* @__PURE__ */ jsx(
2398
- Button,
2399
- {
2400
- fullWidth: !0,
2401
- justify: "stretch",
2402
- size: "md",
2403
- radius: "0",
2404
- variant: "subtle",
2405
- color: "gray",
2406
- onClick: toggle,
2407
- fw: 500,
2408
- fz: "sm",
2409
- children: title$12
2410
- }
2411
- ) }),
2412
- !isCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
2413
- /* @__PURE__ */ jsx(CardSection, { mb: "xs", children: /* @__PURE__ */ jsx(
2414
- Button,
2415
- {
2416
- fullWidth: !0,
2417
- size: "xs",
2418
- h: "sm",
2419
- py: 2,
2420
- radius: "0",
2421
- variant: "subtle",
2422
- color: "gray",
2423
- onClick: toggle,
2424
- children: /* @__PURE__ */ jsx(IconMenu, { size: 11, opacity: 0.7 })
2425
- }
2426
- ) }),
2427
- /* @__PURE__ */ jsxs(Group, { justify: "stretch", wrap: "nowrap", mb: "sm", children: [
2428
- /* @__PURE__ */ jsx(
2429
- Text$1,
2430
- {
2431
- component: "div",
2432
- flex: "1",
2433
- size: "md",
2434
- fw: 500,
2435
- lh: 1.1,
2436
- className: title,
2437
- children: title$12
2438
- }
2439
- ),
2440
- /* @__PURE__ */ jsxs(
2441
- Text$1,
2442
- {
2443
- hidden: isCollapsed,
2444
- component: "div",
2445
- flex: "0 0 auto",
2446
- inline: !0,
2447
- size: "xs",
2448
- fz: 9,
2449
- fw: 500,
2450
- c: "dimmed",
2451
- style: {
2452
- userSelect: "all"
2453
- },
2454
- children: [
2455
- /* @__PURE__ */ jsxs("span", { style: { userSelect: "none" }, children: [
2456
- "id:",
2457
- " "
2458
- ] }),
2459
- id
2460
- ]
2461
- }
2462
- )
2463
- ] }),
2464
- description$12 && /* @__PURE__ */ jsx(
2465
- Spoiler,
2466
- {
2467
- maxHeight: 42,
2468
- showLabel: /* @__PURE__ */ jsx(Button, { color: "gray", variant: "light", fz: "10", size: "compact-xs", children: "show more" }),
2469
- hideLabel: /* @__PURE__ */ jsx(Button, { color: "gray", variant: "light", fz: "10", size: "compact-xs", children: "hide" }),
2470
- children: /* @__PURE__ */ jsx(
2471
- Text$1,
2472
- {
2473
- component: "div",
2474
- size: "sm",
2475
- className: description,
2476
- children: description$12 || "no description"
2477
- }
2478
- )
2479
- }
2480
- ),
2481
- !description$12 && /* @__PURE__ */ jsx(
2482
- Text$1,
2483
- {
2484
- component: "div",
2485
- size: "xs",
2486
- c: "dimmed",
2487
- children: "no description"
2488
- }
2489
- ),
2490
- links && /* @__PURE__ */ jsx(
2491
- Stack,
2492
- {
2493
- gap: 3,
2494
- justify: "stretch",
2495
- align: "stretch",
2496
- children: links.map((link) => /* @__PURE__ */ jsxs(Group, { wrap: "nowrap", align: "center", gap: "sm", children: [
2497
- /* @__PURE__ */ jsx(Box, { flex: "1", style: { overflow: "hidden" }, children: /* @__PURE__ */ jsx(
2498
- Anchor,
2499
- {
2500
- href: link.url,
2501
- target: "_blank",
2502
- fz: "xs",
2503
- truncate: "end",
2504
- display: "inline-block",
2505
- w: "100%",
2506
- children: link.title || link.url
2507
- }
2508
- ) }),
2509
- /* @__PURE__ */ jsx(CopyButton, { value: link.url, children: ({ copied, copy }) => /* @__PURE__ */ jsx(
2510
- Button,
2511
- {
2512
- size: "compact-xs",
2513
- fz: "10",
2514
- variant: "light",
2515
- onClick: copy,
2516
- color: copied ? "teal" : "gray",
2517
- children: copied ? "copied" : "copy"
2518
- }
2519
- ) })
2520
- ] }, link.url))
2521
- }
2522
- )
2523
- ] })
2524
- ]
2525
- }
2526
- )
2527
- },
2528
- id
2529
- ) });
2530
- }
2531
- var container$1 = "_2jkb2y0", buttons = "_2jkb2y1", btn = "_2jkb2y3";
2532
- function DynamicViewWalkthrough() {
2533
- const isMobile = useXYStore((s) => s.width <= 750), {
2534
- nextDynamicStep,
2535
- stopDynamicView,
2536
- activeDynamicViewStep,
2537
- hasNextSteps
2538
- } = useDiagramState((s) => ({
2539
- nextDynamicStep: s.nextDynamicStep,
2540
- stopDynamicView: s.stopDynamicView,
2541
- activeDynamicViewStep: s.activeDynamicViewStep,
2542
- hasNextSteps: (s.activeDynamicViewStep ?? 1) < s.view.edges.length
2543
- })), isActive = isNumber(activeDynamicViewStep);
2544
- useHotkeys(
2545
- isActive ? [
2546
- ["ArrowLeft", () => nextDynamicStep(-1)],
2547
- ["ArrowRight", () => nextDynamicStep()],
2548
- ["Escape", (e) => {
2549
- e.stopImmediatePropagation(), stopDynamicView();
2550
- }, { preventDefault: !0 }]
2551
- ] : [
2552
- ["ArrowLeft", () => nextDynamicStep()],
2553
- ["ArrowRight", () => nextDynamicStep()]
2554
- ]
2555
- );
2556
- const buttonProps = {
2557
- className: btn,
2558
- size: isMobile ? "compact-md" : "lg",
2559
- radius: isMobile ? "lg" : "xl"
2560
- }, nextStep = (increment = 1) => (e) => {
2561
- e.stopPropagation(), nextDynamicStep(increment);
2562
- };
2563
- return /* @__PURE__ */ jsxs(
2564
- Box,
2565
- {
2566
- className: clsx("react-flow__panel", container$1),
2567
- onClick: (e) => e.stopPropagation(),
2568
- onDoubleClick: (e) => e.stopPropagation(),
2569
- children: [
2570
- isNullish(activeDynamicViewStep) && /* @__PURE__ */ jsx(
2571
- Button,
2572
- {
2573
- ...buttonProps,
2574
- className: clsx(buttons, btn),
2575
- rightSection: /* @__PURE__ */ jsx(IconPlayerPlayFilled, {}),
2576
- onClick: nextStep(),
2577
- px: "xl",
2578
- children: "Start"
2579
- }
2580
- ),
2581
- isNumber(activeDynamicViewStep) && /* @__PURE__ */ jsxs(ButtonGroup, { className: buttons, children: [
2582
- /* @__PURE__ */ jsx(
2583
- Button,
2584
- {
2585
- ...buttonProps,
2586
- pl: "lg",
2587
- disabled: activeDynamicViewStep === 1,
2588
- onClick: nextStep(-1),
2589
- children: /* @__PURE__ */ jsx(IconPlayerSkipBackFilled, {})
2590
- }
2591
- ),
2592
- /* @__PURE__ */ jsxs(
2593
- Button,
2594
- {
2595
- ...buttonProps,
2596
- px: hasNextSteps ? "md" : "xl",
2597
- onClick: (e) => {
2598
- e.stopPropagation(), stopDynamicView();
2599
- },
2600
- children: [
2601
- hasNextSteps && /* @__PURE__ */ jsx(IconPlayerStopFilled, {}),
2602
- !hasNextSteps && "End"
2603
- ]
2604
- }
2605
- ),
2606
- hasNextSteps && /* @__PURE__ */ jsx(
2607
- Button,
2608
- {
2609
- ...buttonProps,
2610
- pr: "lg",
2611
- onClick: nextStep(),
2612
- children: /* @__PURE__ */ jsx(IconPlayerSkipForwardFilled, {})
2613
- }
2614
- )
2615
- ] })
2616
- ]
2617
- }
2618
- );
2619
- }
2620
- var container = "_1ncdyfa0", card = "_1ncdyfa1", tabPanel = "_1ncdyfa2", elementNotation = "_1ncdyfa3", shapeSvg = "_1ncdyfa4", shapeBadge = "_1ncdyfa5";
2621
- const ElementNotation = ({ value }) => {
2622
- const {
2623
- title: title2,
2624
- color = "primary",
2625
- shape = "rectangle"
2626
- } = value, [onlyKind, setOnlyKind] = useState(null), diagramStore = useDiagramStoreApi(), w = 300, h = 200;
2627
- return /* @__PURE__ */ jsx(
2628
- Card,
2629
- {
2630
- shadow: "none",
2631
- px: "xs",
2632
- py: "sm",
2633
- className: elementNotation,
2634
- mod: {
2635
- "data-likec4-color": color
2636
- },
2637
- onMouseEnter: () => {
2638
- setOnlyKind(null), diagramStore.getState().highlightByElementNotation(value);
2639
- },
2640
- onMouseLeave: () => {
2641
- setOnlyKind(null), diagramStore.setState({ dimmed: /* @__PURE__ */ new Set() });
2642
- },
2643
- children: /* @__PURE__ */ jsxs(
2644
- Group,
2645
- {
2646
- gap: "sm",
2647
- align: "stretch",
2648
- wrap: "nowrap",
2649
- children: [
2650
- /* @__PURE__ */ jsx(
2651
- Box,
2652
- {
2653
- flex: "0 0 70px",
2654
- style: {
2655
- width: 70,
2656
- height: ceil(70 * (h / w), 0)
2657
- },
2658
- children: /* @__PURE__ */ jsx(
2659
- "svg",
2660
- {
2661
- className: clsx(
2662
- shapeSvg
2663
- ),
2664
- viewBox: `0 0 ${w} ${h}`,
2665
- children: /* @__PURE__ */ jsx(
2666
- ElementShapeSvg,
2667
- {
2668
- shape,
2669
- w,
2670
- h
2671
- }
2672
- )
2673
- }
2674
- )
2675
- }
2676
- ),
2677
- /* @__PURE__ */ jsxs(Stack, { gap: 4, flex: 1, children: [
2678
- /* @__PURE__ */ jsx(Group, { gap: 4, flex: "0 0 auto", children: value.kinds.map((kind) => /* @__PURE__ */ jsx(
2679
- Badge,
2680
- {
2681
- className: shapeBadge,
2682
- onMouseEnter: () => {
2683
- setOnlyKind(kind), diagramStore.getState().highlightByElementNotation(value, kind);
2684
- },
2685
- onMouseLeave: () => {
2686
- setOnlyKind(null), diagramStore.getState().highlightByElementNotation(value);
2687
- },
2688
- opacity: isNonNullish(onlyKind) && onlyKind !== kind ? 0.25 : 1,
2689
- color: isNonNullish(onlyKind) && onlyKind !== kind ? "gray" : vars.element.fill,
2690
- children: kind
2691
- },
2692
- kind
2693
- )) }),
2694
- /* @__PURE__ */ jsx(
2695
- Text$1,
2696
- {
2697
- component: "div",
2698
- fz: "sm",
2699
- fw: 500,
2700
- lh: "1.25",
2701
- style: {
2702
- textWrap: "pretty"
2703
- },
2704
- children: title2
2705
- }
2706
- )
2707
- ] })
2708
- ]
2709
- }
2710
- )
2711
- }
2712
- );
2713
- }, selector = (s) => {
2714
- var _a;
2715
- return {
2716
- id: s.view.id,
2717
- notations: ((_a = s.view.notation) == null ? void 0 : _a.elements) ?? [],
2718
- height: s.xystore.getState().height,
2719
- isVisible: isNullish(s.focusedNodeId ?? s.activeDynamicViewStep),
2720
- target: s.getContainer()
2721
- };
2722
- };
2723
- function NotationPanel() {
2724
- const {
2725
- id,
2726
- notations,
2727
- isVisible,
2728
- height,
2729
- target
2730
- } = useDiagramState(selector), [isCollapsed, setCollapsed] = useLocalStorage({
2731
- key: "notation-panel-collapsed",
2732
- defaultValue: !0
2733
- }), hasNotations = notations.length > 0, portalProps = target ? { portalProps: { target } } : { withinPortal: !1 };
2734
- return /* @__PURE__ */ jsxs(AnimatePresence, { children: [
2735
- !hasNotations && isVisible && /* @__PURE__ */ jsx(
2736
- m.div,
2737
- {
2738
- initial: { opacity: 0.75, translateX: "50%" },
2739
- animate: { opacity: 1, translateX: 0 },
2740
- exit: {
2741
- translateX: "100%",
2742
- opacity: 0.6
2743
- },
2744
- className: clsx("react-flow__panel", container),
2745
- children: /* @__PURE__ */ jsx(Tooltip$2, { label: "View has no notations", color: "orange", ...portalProps, children: /* @__PURE__ */ jsx(
2746
- ThemeIcon,
2747
- {
2748
- size: "xl",
2749
- variant: "light",
2750
- color: "orange",
2751
- radius: "md",
2752
- children: /* @__PURE__ */ jsx(IconAlertTriangle, {})
2753
- }
2754
- ) })
2755
- },
2756
- "empty"
2757
- ),
2758
- hasNotations && isVisible && isCollapsed && /* @__PURE__ */ jsx(
2759
- m.div,
2760
- {
2761
- initial: { opacity: 0.75, translateX: "50%" },
2762
- animate: { opacity: 1, translateX: 0 },
2763
- exit: {
2764
- translateX: "100%",
2765
- opacity: 0.6
2766
- },
2767
- className: clsx("react-flow__panel", container),
2768
- children: /* @__PURE__ */ jsx(Tooltip$2, { label: "Show notation", color: "dark", fz: "xs", ...portalProps, children: /* @__PURE__ */ jsx(
2769
- ActionIcon$2,
2770
- {
2771
- size: "xl",
2772
- variant: "light",
2773
- color: "gray",
2774
- radius: "md",
2775
- onClick: () => setCollapsed(!1),
2776
- children: /* @__PURE__ */ jsx(IconHelpCircle, { stroke: 1.5 })
2777
- }
2778
- ) })
2779
- },
2780
- "collapsed"
2781
- ),
2782
- hasNotations && isVisible && !isCollapsed && /* @__PURE__ */ jsx(
2783
- m.div,
2784
- {
2785
- initial: {
2786
- opacity: 0.75,
2787
- // translateX: '50%',
2788
- scale: 0.2
2789
- },
2790
- animate: { opacity: 1, scale: 1 },
2791
- exit: {
2792
- opacity: 0,
2793
- scale: 0.25
2794
- },
2795
- className: clsx("react-flow__panel", container),
2796
- style: {
2797
- transformOrigin: "bottom right"
2798
- },
2799
- children: /* @__PURE__ */ jsx(
2800
- Paper,
2801
- {
2802
- radius: "sm",
2803
- withBorder: !0,
2804
- shadow: "lg",
2805
- className: card,
2806
- children: /* @__PURE__ */ jsxs(Tabs, { defaultValue: "first", radius: "xs", children: [
2807
- /* @__PURE__ */ jsxs(TabsList, { children: [
2808
- /* @__PURE__ */ jsx(
2809
- ActionIcon$2,
2810
- {
2811
- size: "md",
2812
- variant: "subtle",
2813
- color: "gray",
2814
- ml: 2,
2815
- style: {
2816
- alignSelf: "center"
2817
- },
2818
- onClick: () => setCollapsed(!0),
2819
- children: /* @__PURE__ */ jsx(IconArrowDownRight, { stroke: 2 })
2820
- }
2821
- ),
2822
- /* @__PURE__ */ jsx(TabsTab, { value: "first", fz: "xs", children: "Elements" }),
2823
- /* @__PURE__ */ jsx(TabsTab, { value: "second", fz: "xs", disabled: !0, children: "Relationships" })
2824
- ] }),
2825
- /* @__PURE__ */ jsx(TabsPanel, { value: "first", className: tabPanel, hidden: isCollapsed, children: /* @__PURE__ */ jsx(
2826
- ScrollAreaAutosize,
2827
- {
2828
- viewportProps: {
2829
- style: {
2830
- maxHeight: height > 50 ? `min(40vh, ${height - 20}px)` : "40vh"
2831
- }
2832
- },
2833
- children: /* @__PURE__ */ jsx(Stack, { gap: 0, children: notations.map((n, i) => /* @__PURE__ */ jsx(ElementNotation, { value: n }, i)) })
2834
- }
2835
- ) })
2836
- ] })
2837
- }
2838
- )
2839
- },
2840
- id
2841
- )
2842
- ] });
2843
- }
2844
- const {
2845
- primary,
2846
- secondary,
2847
- muted,
2848
- ...otherColors
2849
- } = defaultTheme.elements, themedColors = [
2850
- { key: "primary", value: primary.fill },
2851
- { key: "secondary", value: secondary.fill },
2852
- { key: "muted", value: muted.fill }
2853
- ], colors = keys(otherColors).map((key) => ({
2854
- key,
2855
- value: defaultTheme.elements[key].fill
2856
- })), NodeOptions = memo(({ selectedNodeIds }) => {
2857
- const diagramApi = useDiagramStoreApi(), nodes = useXYNodesData(selectedNodeIds);
2858
- if (!hasAtLeast$1(nodes, 1))
2859
- return null;
2860
- if (nodes.length !== selectedNodeIds.length)
2861
- throw new Error("NodeOptions: nodes and props.nodes should have the same length");
2862
- const showShapeOption = nodes.some((node) => node.type === "element"), [firstNode, ...rest] = nodes, showGoToSource = rest.length === 0 && diagramApi.getState().onOpenSourceElement, showOpacityOption = firstNode.type === "compound" && (rest.length === 0 || rest.every((node) => node.type === "compound")), triggerChange = (style) => {
2863
- const targets = nodes.map((node) => node.data.element.id);
2864
- invariant(hasAtLeast$1(targets, 1), "At least one target is required"), diagramApi.getState().triggerChangeElementStyle({
2865
- op: "change-element-style",
2866
- style,
2867
- targets
2868
- });
2869
- };
2870
- return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
2871
- /* @__PURE__ */ jsxs(Group, { wrap: "nowrap", align: "flex-start", children: [
2872
- /* @__PURE__ */ jsxs(Box, { flex: 1, children: [
2873
- /* @__PURE__ */ jsxs(Text$1, { fz: rem(9), fw: "500", c: "dimmed", children: [
2874
- "ELEMENT",
2875
- rest.length > 0 ? "S" : ""
2876
- ] }),
2877
- /* @__PURE__ */ jsx(Text$1, { size: "xs", c: rest.length > 0 ? "dimmed" : "", truncate: !0, children: rest.length === 0 ? firstNode.data.element.title : "[ multiple ]" })
2878
- ] }),
2879
- showGoToSource && /* @__PURE__ */ jsx(Box, { flex: 0, children: /* @__PURE__ */ jsx(
2880
- ActionIcon$2,
2881
- {
2882
- size: "sm",
2883
- variant: "light",
2884
- color: "gray",
2885
- onClick: (e) => {
2886
- var _a, _b;
2887
- e.stopPropagation(), (_b = (_a = diagramApi.getState()).onOpenSourceElement) == null || _b.call(_a, firstNode.data.element.id);
2888
- },
2889
- children: /* @__PURE__ */ jsx(
2890
- IconFileSymlink,
2891
- {
2892
- stroke: 1.2,
2893
- style: {
2894
- width: "70%",
2895
- height: "70%"
2896
- }
2897
- }
2898
- )
2899
- }
2900
- ) })
2901
- ] }),
2902
- showShapeOption && /* @__PURE__ */ jsx(
2903
- ShapeOption,
2904
- {
2905
- nodes,
2906
- onShapeChange: (shape) => {
2907
- triggerChange({ shape });
2908
- }
2909
- }
2910
- ),
2911
- /* @__PURE__ */ jsx(
2912
- Colors,
2913
- {
2914
- nodes,
2915
- onColorChange: (color) => {
2916
- triggerChange({ color });
2917
- }
2918
- }
2919
- ),
2920
- showOpacityOption && /* @__PURE__ */ jsxs("div", { children: [
2921
- /* @__PURE__ */ jsx(Divider, { label: "opacity and border", labelPosition: "left" }),
2922
- /* @__PURE__ */ jsx(Space, { h: "xs" }),
2923
- /* @__PURE__ */ jsx(
2924
- OpacityOption,
2925
- {
2926
- node: firstNode,
2927
- onOpacityChange: (opacity) => {
2928
- triggerChange({ opacity });
2929
- }
2930
- }
2931
- ),
2932
- /* @__PURE__ */ jsx(Space, { h: "sm" }),
2933
- /* @__PURE__ */ jsx(
2934
- BorderStyleOption,
2935
- {
2936
- node: firstNode,
2937
- onChange: (border) => {
2938
- triggerChange({ border });
2939
- }
2940
- }
2941
- )
2942
- ] }, firstNode.id),
2943
- /* @__PURE__ */ jsx(NavigateToOption, { nodes })
2944
- ] });
2945
- }, (prevProps, nextProps) => shallowEqual(prevProps.selectedNodeIds, nextProps.selectedNodeIds));
2946
- function Colors({
2947
- nodes: [firstNode, ...nodes],
2948
- onColorChange
2949
- }) {
2950
- let selectedColor = firstNode.data.element.color;
2951
- takeWhile(nodes, (node) => node.data.element.color !== selectedColor ? (selectedColor = null, !1) : !0);
2952
- const changeColor = (color) => () => {
2953
- selectedColor !== color && onColorChange(color);
2954
- };
2955
- return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(TooltipGroup, { openDelay: 400, closeDelay: 300, children: [
2956
- /* @__PURE__ */ jsx(Divider, { label: "color", labelPosition: "left" }),
2957
- /* @__PURE__ */ jsx(Flex, { mt: "xs", maw: 150, gap: "xs", justify: "flex-start", align: "flex-start", direction: "row", wrap: "wrap", children: themedColors.map(({ key, value }) => /* @__PURE__ */ jsx(
2958
- Tooltip$2,
2959
- {
2960
- label: key,
2961
- fz: "xs",
2962
- color: "dark",
2963
- offset: 2,
2964
- transitionProps: { duration: 140, transition: "slide-up" },
2965
- children: /* @__PURE__ */ jsx(
2966
- ColorSwatch,
2967
- {
2968
- color: value,
2969
- size: 20,
2970
- withShadow: !0,
2971
- onClick: changeColor(key),
2972
- style: { color: "#fff", cursor: "pointer" },
2973
- children: selectedColor === key && /* @__PURE__ */ jsx(CheckIcon, { style: { width: rem(12), height: rem(12) } })
2974
- }
2975
- )
2976
- },
2977
- key
2978
- )) }),
2979
- /* @__PURE__ */ jsx(Flex, { mt: "sm", maw: 150, gap: "xs", justify: "flex-start", align: "flex-start", direction: "row", wrap: "wrap", children: colors.map(({ key, value }) => /* @__PURE__ */ jsx(
2980
- Tooltip$2,
2981
- {
2982
- label: key,
2983
- fz: "xs",
2984
- color: "dark",
2985
- offset: 2,
2986
- transitionProps: { duration: 140, transition: "slide-up" },
2987
- children: /* @__PURE__ */ jsx(
2988
- ColorSwatch,
2989
- {
2990
- color: value,
2991
- size: 20,
2992
- onClick: changeColor(key),
2993
- style: { color: "#fff", cursor: "pointer" },
2994
- children: selectedColor === key && /* @__PURE__ */ jsx(CheckIcon, { style: { width: rem(12), height: rem(12) } })
2995
- }
2996
- )
2997
- },
2998
- key
2999
- )) })
3000
- ] }) });
3001
- }
3002
- function ShapeOption({
3003
- nodes: [firstNode, ...nodes],
3004
- onShapeChange
3005
- }) {
3006
- let selectedShape = firstNode.data.element.shape;
3007
- return takeWhile(nodes, (node) => node.data.element.shape !== selectedShape ? (selectedShape = null, !1) : !0), /* @__PURE__ */ jsxs("div", { children: [
3008
- /* @__PURE__ */ jsx(Divider, { label: "shape", labelPosition: "left" }),
3009
- /* @__PURE__ */ jsx(
3010
- Select,
3011
- {
3012
- mt: "xs",
3013
- size: "xs",
3014
- variant: "filled",
3015
- value: selectedShape,
3016
- placeholder: "[ multiple ]",
3017
- data: ElementShapes,
3018
- allowDeselect: !1,
3019
- checkIconPosition: "right",
3020
- onChange: (value) => {
3021
- !value || value === selectedShape || onShapeChange(value);
3022
- }
3023
- }
3024
- )
3025
- ] });
3026
- }
3027
- function NavigateToOption({
3028
- nodes: [node, ...nodes]
3029
- }) {
3030
- const isMultiple = nodes.length >= 1;
3031
- return /* @__PURE__ */ jsxs("div", { children: [
3032
- /* @__PURE__ */ jsx(Divider, { label: "navigate to", labelPosition: "left" }),
3033
- isMultiple && /* @__PURE__ */ jsx(Text$1, { size: "xs", c: "dimmed", children: "[ multiple ]" }),
3034
- !isMultiple && /* @__PURE__ */ jsxs(Fragment, { children: [
3035
- /* @__PURE__ */ jsx(Space, { h: "xs" }),
3036
- /* @__PURE__ */ jsx(
3037
- Select,
3038
- {
3039
- size: "xs",
3040
- variant: "filled",
3041
- value: node.data.element.navigateTo ?? null,
3042
- placeholder: "select",
3043
- data: node.data.element.navigateTo ? [{ value: node.data.element.navigateTo, label: node.data.element.navigateTo }] : [],
3044
- allowDeselect: !0,
3045
- checkIconPosition: "right"
3046
- }
3047
- )
3048
- ] })
3049
- ] });
3050
- }
3051
- function OpacityOption({
3052
- node,
3053
- onOpacityChange
3054
- }) {
3055
- let selectedOpacity = node.data.element.style.opacity ?? 100;
3056
- const [value, setValue] = useState(selectedOpacity);
3057
- return useUpdateEffect(() => {
3058
- setValue(selectedOpacity);
3059
- }, [selectedOpacity]), /* @__PURE__ */ jsx(
3060
- Slider,
3061
- {
3062
- size: "sm",
3063
- color: "dark",
3064
- value,
3065
- onChange: setValue,
3066
- onChangeEnd: onOpacityChange
3067
- }
3068
- );
3069
- }
3070
- function BorderStyleOption({
3071
- node,
3072
- onChange
3073
- }) {
3074
- let selecteBorderStyle = node.data.element.style.border ?? "dashed";
3075
- const [value, setValue] = useState(selecteBorderStyle);
3076
- return useUpdateEffect(() => {
3077
- setValue(selecteBorderStyle);
3078
- }, [selecteBorderStyle]), /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
3079
- SegmentedControl,
3080
- {
3081
- size: "xs",
3082
- fullWidth: !0,
3083
- withItemsBorders: !1,
3084
- value,
3085
- onChange: (v) => {
3086
- setValue(v), onChange(v);
3087
- },
3088
- data: [
3089
- { label: "Solid", value: "solid" },
3090
- { label: "Dashed", value: "dashed" },
3091
- { label: "Dotted", value: "dotted" },
3092
- { label: "None", value: "none" }
3093
- ]
3094
- }
3095
- ) });
3096
- }
3097
- var panel$1 = "_1lmzt1h0";
3098
- function OptionsPanel() {
3099
- const { isFocused, viewId } = useDiagramState((s) => ({
3100
- viewId: s.view.id,
3101
- isFocused: s.focusedNodeId !== null || s.activeDynamicViewStep !== null
3102
- })), [selected, setSelected] = useState({
3103
- nodes: [],
3104
- edges: []
3105
- });
3106
- useOnSelectionChange({
3107
- onChange: ({ nodes: nodes2, edges }) => {
3108
- const newSelected = {
3109
- nodes: nodes2.map((n) => n.id).sort(),
3110
- edges: edges.map((e) => e.id).sort()
3111
- };
3112
- deepEqual(selected, newSelected) || setSelected(newSelected);
3113
- }
3114
- });
3115
- const { nodes } = selected, hasAnySelection = !isFocused && nodes.length > 0;
3116
- return /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: hasAnySelection && /* @__PURE__ */ jsx(
3117
- m.div,
3118
- {
3119
- initial: { opacity: 0, scale: 0.85 },
3120
- animate: { opacity: 1, scale: 1 },
3121
- exit: {
3122
- opacity: 0,
3123
- scale: 0.85
3124
- },
3125
- transition: { duration: 0.14 },
3126
- className: clsx("react-flow__panel", panel$1),
3127
- style: {
3128
- transformOrigin: "center right",
3129
- maxWidth: "320px"
3130
- },
3131
- children: /* @__PURE__ */ jsx(Card, { shadow: "md", children: nodes.length > 0 && /* @__PURE__ */ jsx(NodeOptions, { selectedNodeIds: nodes }) })
3132
- },
3133
- viewId
3134
- ) });
3135
- }
3136
- const Tooltip$1 = Tooltip$2.withProps({
3137
- color: "gray",
3138
- fz: "xs",
3139
- position: "right",
3140
- openDelay: 400,
3141
- closeDelay: 100,
3142
- label: "",
3143
- children: null,
3144
- offset: 8,
3145
- transitionProps: { transition: "fade-right", duration: 200 }
3146
- }), ActionIcon$1 = ActionIcon$2.withProps({
3147
- className: "action-icon",
3148
- variant: "light",
3149
- color: "gray"
3150
- });
3151
- var panel = "_1snasyb0", autolayoutIcon = "_1snasyb1", autolayoutIndicator = "_1snasyb2";
3152
- const ActionIcon = ActionIcon$2.withProps({
3153
- size: "md",
3154
- variant: "subtle",
3155
- color: "gray"
3156
- }), Tooltip = Tooltip$2.withProps({
3157
- color: "gray",
3158
- fz: "xs",
3159
- position: "right",
3160
- openDelay: 300,
3161
- label: "",
3162
- children: null,
3163
- offset: 5,
3164
- transitionProps: { transition: "fade", duration: 200 }
3165
- }), ChangeAutoLayoutButton = (props) => {
3166
- const store = useDiagramStoreApi(), [rootRef, setRootRef] = useState(null), [controlsRefs, setControlsRefs] = useState({}), autoLayout = useDiagramState((s) => s.view.autoLayout), setControlRef = (name) => (node) => {
3167
- controlsRefs[name] = node, setControlsRefs(controlsRefs);
3168
- }, setAutoLayout = (layout) => (event) => {
3169
- var _a, _b;
3170
- event.stopPropagation(), (_b = (_a = store.getState()).onChange) == null || _b.call(_a, {
3171
- change: {
3172
- op: "change-autolayout",
3173
- layout
3174
- }
3175
- });
3176
- };
3177
- return /* @__PURE__ */ jsxs(
3178
- Popover,
3179
- {
3180
- position: "right-start",
3181
- clickOutsideEvents: [
3182
- "pointerdown"
3183
- ],
3184
- radius: "xs",
3185
- shadow: "lg",
3186
- offset: {
3187
- mainAxis: 10
3188
- },
3189
- ...props,
3190
- children: [
3191
- /* @__PURE__ */ jsx(PopoverTarget, { children: /* @__PURE__ */ jsx(
3192
- ActionIcon$2,
3193
- {
3194
- className: "action-icon",
3195
- variant: "light",
3196
- color: "gray",
3197
- children: /* @__PURE__ */ jsx(IconLayoutDashboard, { stroke: 1.3 })
3198
- }
3199
- ) }),
3200
- /* @__PURE__ */ jsx(PopoverDropdown, { className: "likec4-top-left-panel", p: 8, pt: 4, children: /* @__PURE__ */ jsxs(Box, { pos: "relative", ref: setRootRef, children: [
3201
- /* @__PURE__ */ jsx(
3202
- FloatingIndicator,
3203
- {
3204
- target: controlsRefs[autoLayout],
3205
- parent: rootRef,
3206
- className: autolayoutIndicator
3207
- }
3208
- ),
3209
- /* @__PURE__ */ jsx(Box, { mb: 10, children: /* @__PURE__ */ jsx(Text$1, { inline: !0, fz: "xs", c: "dimmed", fw: 500, children: "Auto layout:" }) }),
3210
- /* @__PURE__ */ jsx(TooltipGroup, { openDelay: 100, children: /* @__PURE__ */ jsxs(Stack, { align: "center", gap: 1, children: [
3211
- /* @__PURE__ */ jsx(Tooltip, { label: "Top to Bottom", children: /* @__PURE__ */ jsx(
3212
- ActionIcon,
3213
- {
3214
- className: autolayoutIcon,
3215
- ref: setControlRef("TB"),
3216
- onClick: setAutoLayout("TB"),
3217
- children: /* @__PURE__ */ jsx(IconArrowBigDownLines, { stroke: 1.3 })
3218
- }
3219
- ) }),
3220
- /* @__PURE__ */ jsxs(Group, { gap: 30, children: [
3221
- /* @__PURE__ */ jsx(Tooltip, { label: "Left to Right", children: /* @__PURE__ */ jsx(
3222
- ActionIcon,
3223
- {
3224
- className: autolayoutIcon,
3225
- ref: setControlRef("LR"),
3226
- onClick: setAutoLayout("LR"),
3227
- children: /* @__PURE__ */ jsx(IconArrowBigDownLines, { stroke: 1.3, style: { rotate: "270deg" } })
3228
- }
3229
- ) }),
3230
- /* @__PURE__ */ jsx(Tooltip, { label: "Right to Left", children: /* @__PURE__ */ jsx(
3231
- ActionIcon,
3232
- {
3233
- className: autolayoutIcon,
3234
- ref: setControlRef("RL"),
3235
- onClick: setAutoLayout("RL"),
3236
- children: /* @__PURE__ */ jsx(IconArrowBigDownLines, { stroke: 1.3, style: { rotate: "90deg" } })
3237
- }
3238
- ) })
3239
- ] }),
3240
- /* @__PURE__ */ jsx(Tooltip, { label: "Bottom to Top", children: /* @__PURE__ */ jsx(
3241
- ActionIcon,
3242
- {
3243
- className: autolayoutIcon,
3244
- ref: setControlRef("BT"),
3245
- onClick: setAutoLayout("BT"),
3246
- children: /* @__PURE__ */ jsx(IconArrowBigDownLines, { stroke: 1.3, style: { rotate: "180deg" } })
3247
- }
3248
- ) })
3249
- ] }) })
3250
- ] }) })
3251
- ]
3252
- }
3253
- );
3254
- }, historySelector = (s) => ({
3255
- showBurgerMenu: !!s.onBurgerMenuClick,
3256
- hasStepBack: s.navigationHistoryIndex > 0,
3257
- hasStepForward: s.navigationHistoryIndex < s.navigationHistory.length - 1
3258
- }), BackwardForwardButtons = () => {
3259
- const store = useDiagramStoreApi(), {
3260
- showBurgerMenu,
3261
- hasStepBack,
3262
- hasStepForward
3263
- } = useDiagramState(historySelector);
3264
- return /* @__PURE__ */ jsxs(
3265
- Group,
3266
- {
3267
- className: "likec4-navigation-panel",
3268
- gap: "xs",
3269
- children: [
3270
- showBurgerMenu && /* @__PURE__ */ jsx(
3271
- ActionIcon$1,
3272
- {
3273
- onClick: (e) => {
3274
- var _a, _b;
3275
- e.stopPropagation(), (_b = (_a = store.getState()).onBurgerMenuClick) == null || _b.call(_a);
3276
- },
3277
- children: /* @__PURE__ */ jsx(IconMenu2, {})
3278
- }
3279
- ),
3280
- /* @__PURE__ */ jsxs(AnimatePresence, { children: [
3281
- hasStepBack && /* @__PURE__ */ jsx(
3282
- m.div,
3283
- {
3284
- layout: !0,
3285
- initial: { opacity: 0.05, transform: "translateX(-10px)" },
3286
- animate: { opacity: 1, transform: "translateX(0)" },
3287
- exit: {
3288
- opacity: 0.05,
3289
- transform: "translateX(-10px)"
3290
- },
3291
- children: /* @__PURE__ */ jsx(
3292
- ActionIcon$1,
3293
- {
3294
- onClick: (e) => {
3295
- e.stopPropagation(), store.getState().goBack();
3296
- },
3297
- children: /* @__PURE__ */ jsx(IconChevronLeft, {})
3298
- }
3299
- )
3300
- },
3301
- "back"
3302
- ),
3303
- hasStepForward && /* @__PURE__ */ jsx(
3304
- m.div,
3305
- {
3306
- layout: !0,
3307
- initial: { opacity: 0.05, transform: "translateX(10px)" },
3308
- animate: { opacity: 1, transform: "translateX(0)" },
3309
- exit: {
3310
- opacity: 0,
3311
- transform: "translateX(10px)"
3312
- },
3313
- children: /* @__PURE__ */ jsx(
3314
- ActionIcon$1,
3315
- {
3316
- onClick: (e) => {
3317
- e.stopPropagation(), store.getState().goForward();
3318
- },
3319
- children: /* @__PURE__ */ jsx(IconChevronRight, {})
3320
- }
3321
- )
3322
- },
3323
- "forward"
3324
- )
3325
- ] })
3326
- ]
3327
- }
3328
- );
3329
- }, LayoutDriftNotification = (props) => /* @__PURE__ */ jsxs(
3330
- HoverCard,
3331
- {
3332
- position: "right-start",
3333
- openDelay: 200,
3334
- closeDelay: 100,
3335
- ...props,
3336
- children: [
3337
- /* @__PURE__ */ jsx(HoverCardTarget, { children: /* @__PURE__ */ jsx(
3338
- ActionIcon$1,
3339
- {
3340
- className: "action-icon",
3341
- variant: "light",
3342
- color: "orange",
3343
- children: /* @__PURE__ */ jsx(IconAlertTriangle, {})
3344
- }
3345
- ) }),
3346
- /* @__PURE__ */ jsx(HoverCardDropdown, { p: "0", children: /* @__PURE__ */ jsxs(
3347
- Notification,
3348
- {
3349
- color: "orange",
3350
- withBorder: !1,
3351
- withCloseButton: !1,
3352
- title: "Manual layout issues",
3353
- children: [
3354
- /* @__PURE__ */ jsxs(Text$1, { mt: 2, size: "sm", lh: "xs", children: [
3355
- "View contains new elements or their sizes have changed,",
3356
- /* @__PURE__ */ jsx("br", {}),
3357
- "last manual layout can not be applied."
3358
- ] }),
3359
- /* @__PURE__ */ jsxs(Text$1, { mt: "xs", size: "sm", lh: "xs", children: [
3360
- "Update view predicates or remove ",
3361
- /* @__PURE__ */ jsx(Code, { children: "@likec4-generated" })
3362
- ] })
3363
- ]
3364
- }
3365
- ) })
3366
- ]
3367
- }
3368
- ), TopLeftPanel = () => {
3369
- const store = useDiagramStoreApi(), {
3370
- showNavigationButtons,
3371
- showFitDiagram,
3372
- showLayoutDriftWarning,
3373
- showChangeAutoLayout,
3374
- target,
3375
- showGoToSource
3376
- } = useDiagramState((s) => {
3377
- const isNotActive = s.activeDynamicViewStep === null && s.focusedNodeId === null;
3378
- return {
3379
- showNavigationButtons: !!s.onBurgerMenuClick || s.showNavigationButtons && !!s.onNavigateTo,
3380
- showFitDiagram: s.fitViewEnabled && s.zoomable && s.viewportChanged,
3381
- showLayoutDriftWarning: s.readonly !== !0 && s.view.hasLayoutDrift === !0 && isNotActive,
3382
- showChangeAutoLayout: s.readonly !== !0 && !!s.onChange && isNotActive,
3383
- showGoToSource: !!s.onOpenSourceView,
3384
- target: s.getContainer()
3385
- };
3386
- }), portalProps = target ? { portalProps: { target } } : void 0;
3387
- return /* @__PURE__ */ jsxs(
3388
- Stack,
3389
- {
3390
- className: clsx(
3391
- "react-flow__panel",
3392
- panel,
3393
- "likec4-top-left-panel"
3394
- ),
3395
- onClick: (e) => e.stopPropagation(),
3396
- gap: "xs",
3397
- children: [
3398
- showNavigationButtons && /* @__PURE__ */ jsx(BackwardForwardButtons, {}),
3399
- showGoToSource && /* @__PURE__ */ jsx(Tooltip$1, { label: "Go to source", ...portalProps, children: /* @__PURE__ */ jsx(
3400
- ActionIcon$1,
3401
- {
3402
- className: "action-icon",
3403
- onClick: (e) => {
3404
- var _a, _b;
3405
- e.stopPropagation(), (_b = (_a = store.getState()).onOpenSourceView) == null || _b.call(_a);
3406
- },
3407
- children: /* @__PURE__ */ jsx(IconFileSymlink, { stroke: 1.5 })
3408
- }
3409
- ) }),
3410
- showChangeAutoLayout && /* @__PURE__ */ jsx(ChangeAutoLayoutButton, { ...portalProps }),
3411
- /* @__PURE__ */ jsxs(AnimatePresence, { children: [
3412
- showLayoutDriftWarning && /* @__PURE__ */ jsx(
3413
- m.div,
3414
- {
3415
- initial: { opacity: 0.05, transform: "translateX(-40%)" },
3416
- animate: { opacity: 1, transform: "translateX(0)" },
3417
- exit: {
3418
- opacity: 0,
3419
- transform: "translateX(-40%)"
3420
- },
3421
- children: /* @__PURE__ */ jsx(LayoutDriftNotification, { ...portalProps })
3422
- },
3423
- "layout"
3424
- ),
3425
- showFitDiagram && /* @__PURE__ */ jsx(
3426
- m.div,
3427
- {
3428
- initial: { opacity: 0.05, transform: "translateX(-20%)" },
3429
- animate: { opacity: 1, transform: "translateX(0)" },
3430
- exit: {
3431
- opacity: 0,
3432
- transform: "translateX(-30%)"
3433
- },
3434
- children: /* @__PURE__ */ jsx(Tooltip$1, { label: "Center diagram", ...portalProps, children: /* @__PURE__ */ jsx(
3435
- ActionIcon$1,
3436
- {
3437
- className: "action-icon",
3438
- onClick: (e) => {
3439
- e.stopPropagation(), store.getState().fitDiagram();
3440
- },
3441
- children: /* @__PURE__ */ jsx(IconFocusCentered, {})
3442
- }
3443
- ) })
3444
- },
3445
- "fit-view"
3446
- )
3447
- ] })
3448
- ]
3449
- }
3450
- );
3451
- };
3452
- TopLeftPanel.displayName = "TopLeftPanel";
3453
- function literalToEnum(value) {
3454
- switch (value) {
3455
- case "dots":
3456
- return BackgroundVariant.Dots;
3457
- case "lines":
3458
- return BackgroundVariant.Lines;
3459
- case "cross":
3460
- return BackgroundVariant.Cross;
3461
- default:
3462
- nonexhaustive(value);
3463
- }
3464
- }
3465
- function XYFlowBackground({ background }) {
3466
- if (typeof background == "string")
3467
- return /* @__PURE__ */ jsx(Background, { variant: literalToEnum(background), size: 2, gap: 20 });
3468
- const { variant, ...rest } = background;
3469
- return /* @__PURE__ */ jsx(Background, { variant: literalToEnum(variant), ...rest });
3470
- }
3471
- const XYFlowInner = memo(function({
3472
- children,
3473
- background,
3474
- controls,
3475
- showDiagramTitle
3476
- }) {
3477
- const {
3478
- showNotations,
3479
- isDynamicView,
3480
- isDynamicViewActive,
3481
- readonly,
3482
- enableDynamicViewWalkthrough
3483
- } = useDiagramState((s) => ({
3484
- showNotations: s.showNotations,
3485
- isDynamicView: s.isDynamicView,
3486
- readonly: s.readonly,
3487
- enableDynamicViewWalkthrough: s.enableDynamicViewWalkthrough,
3488
- isDynamicViewActive: isNonNullish(s.activeDynamicViewStep)
3489
- }));
3490
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3491
- background !== "transparent" && background !== "solid" && /* @__PURE__ */ jsx(XYFlowBackground, { background }),
3492
- controls && /* @__PURE__ */ jsx(Controls, { position: "bottom-right" }),
3493
- /* @__PURE__ */ jsx(TopLeftPanel, {}),
3494
- readonly !== !0 && /* @__PURE__ */ jsx(OptionsPanel, {}),
3495
- showDiagramTitle === !0 && !isDynamicViewActive && /* @__PURE__ */ jsx(DiagramTitlePanel, {}),
3496
- isDynamicView && enableDynamicViewWalkthrough && /* @__PURE__ */ jsx(DynamicViewWalkthrough, {}),
3497
- showNotations && /* @__PURE__ */ jsx(NotationPanel, {}),
3498
- children
3499
- ] });
3500
- }, shallowEqual);
3501
- XYFlowInner.displayName = "XYFlowInner";
3502
- function LikeC4Diagram({
3503
- view,
3504
- className,
3505
- fitView = !0,
3506
- fitViewPadding = 0,
3507
- readonly = !0,
3508
- pannable = !0,
3509
- zoomable = !0,
3510
- nodesSelectable = !readonly,
3511
- nodesDraggable = !readonly,
3512
- background = "dots",
3513
- controls = !1,
3514
- showElementLinks = !0,
3515
- showDiagramTitle = !0,
3516
- showNotations = !0,
3517
- enableDynamicViewWalkthrough = !1,
3518
- enableFocusMode = !1,
3519
- initialWidth,
3520
- initialHeight,
3521
- keepAspectRatio = !1,
3522
- experimentalEdgeEditing = !1,
3523
- onCanvasClick,
3524
- onCanvasContextMenu,
3525
- onCanvasDblClick,
3526
- onEdgeClick,
3527
- onChange,
3528
- onEdgeContextMenu,
3529
- onNavigateTo,
3530
- onNodeClick,
3531
- onNodeContextMenu,
3532
- onOpenSourceElement,
3533
- onOpenSourceView,
3534
- onOpenSourceRelation,
3535
- onBurgerMenuClick,
3536
- renderIcon,
3537
- where,
3538
- showNavigationButtons = !!onNavigateTo
3539
- }) {
3540
- const initialRef = useRef({
3541
- defaultNodes: [],
3542
- defaultEdges: [],
3543
- initialWidth: initialWidth ?? view.bounds.width,
3544
- initialHeight: initialHeight ?? view.bounds.height
3545
- });
3546
- return readonly !== !0 && where && console.warn("where filter is only supported in readonly mode"), /* @__PURE__ */ jsx(EnsureMantine, { children: /* @__PURE__ */ jsx(LazyMotion, { features: domAnimation, strict: !0, children: /* @__PURE__ */ jsxs(
3547
- ReactFlowProvider,
3548
- {
3549
- fitView,
3550
- ...initialRef.current,
3551
- children: [
3552
- customColorsDefined(view) && /* @__PURE__ */ jsx(LikeC4CustomColors, { customColors: view.customColorDefinitions }),
3553
- /* @__PURE__ */ jsx(
3554
- DiagramContextProvider,
3555
- {
3556
- view,
3557
- keepAspectRatio,
3558
- className: clsx(rootClassName, className),
3559
- readonly,
3560
- pannable,
3561
- zoomable,
3562
- fitViewEnabled: fitView,
3563
- fitViewPadding,
3564
- showElementLinks,
3565
- showNavigationButtons,
3566
- showNotations,
3567
- nodesDraggable,
3568
- nodesSelectable,
3569
- experimentalEdgeEditing,
3570
- enableDynamicViewWalkthrough,
3571
- enableFocusMode,
3572
- whereFilter: where ?? null,
3573
- renderIcon: renderIcon ?? null,
3574
- onCanvasClick: onCanvasClick ?? null,
3575
- onCanvasContextMenu: onCanvasContextMenu ?? null,
3576
- onEdgeClick: onEdgeClick ?? null,
3577
- onEdgeContextMenu: onEdgeContextMenu ?? null,
3578
- onNodeClick: onNodeClick ?? null,
3579
- onNodeContextMenu: onNodeContextMenu ?? null,
3580
- onChange: onChange ?? null,
3581
- onNavigateTo: onNavigateTo ?? null,
3582
- onCanvasDblClick: onCanvasDblClick ?? null,
3583
- onOpenSourceView: onOpenSourceView ?? null,
3584
- onOpenSourceElement: onOpenSourceElement ?? null,
3585
- onOpenSourceRelation: onOpenSourceRelation ?? null,
3586
- onBurgerMenuClick: onBurgerMenuClick ?? null,
3587
- children: /* @__PURE__ */ jsx(
3588
- LikeC4DiagramInnerMemo,
3589
- {
3590
- controls,
3591
- background,
3592
- showDiagramTitle
3593
- }
3594
- )
3595
- }
3596
- )
3597
- ]
3598
- }
3599
- ) }) });
3600
- }
3601
- LikeC4Diagram.displayName = "LikeC4Diagram";
3602
- const LikeC4DiagramInnerMemo = memo(function({
3603
- background,
3604
- controls,
3605
- showDiagramTitle
3606
- }) {
3607
- const {
3608
- isInitialized,
3609
- pannable,
3610
- fitView,
3611
- enableFocusMode
3612
- } = useDiagramState((s) => ({
3613
- isInitialized: s.initialized,
3614
- zoomable: s.zoomable,
3615
- pannable: s.pannable,
3616
- fitView: s.fitViewEnabled,
3617
- enableFocusMode: s.enableFocusMode
3618
- }));
3619
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3620
- /* @__PURE__ */ jsx(
3621
- XYFlow,
3622
- {
3623
- className: clsx(
3624
- "likec4-diagram",
3625
- cssReactFlow,
3626
- controls === !1 && cssNoControls,
3627
- pannable !== !0 && cssDisablePan,
3628
- background === "transparent" && cssTransparentBg
3629
- ),
3630
- children: /* @__PURE__ */ jsx(
3631
- XYFlowInner,
3632
- {
3633
- showDiagramTitle,
3634
- background,
3635
- controls
3636
- }
3637
- )
3638
- }
3639
- ),
3640
- isInitialized && /* @__PURE__ */ jsxs(Fragment, { children: [
3641
- fitView && /* @__PURE__ */ jsx(FitViewOnDiagramChange, {}),
3642
- enableFocusMode && /* @__PURE__ */ jsx(SelectEdgesOnNodeFocus, {})
3643
- ] })
3644
- ] });
3645
- }, shallowEqual);
3646
- LikeC4DiagramInnerMemo.displayName = "LikeC4DiagramInnerMemo";
3647
- function customColorsDefined(view) {
3648
- return Object.keys(view.customColorDefinitions).length > 0;
3649
- }
3650
- function StaticLikeC4Diagram({
3651
- view,
3652
- fitView = !0,
3653
- fitViewPadding = 0,
3654
- background = "transparent",
3655
- ...rest
3656
- }) {
3657
- return /* @__PURE__ */ jsx(
3658
- LikeC4Diagram,
3659
- {
3660
- view,
3661
- readonly: !0,
3662
- fitView,
3663
- fitViewPadding,
3664
- pannable: !1,
3665
- zoomable: !1,
3666
- controls: !1,
3667
- background,
3668
- showElementLinks: !0,
3669
- showDiagramTitle: !1,
3670
- showNotations: !1,
3671
- enableDynamicViewWalkthrough: !1,
3672
- enableFocusMode: !1,
3673
- nodesSelectable: !1,
3674
- nodesDraggable: !1,
3675
- ...rest
3676
- }
3677
- );
3678
- }
3679
- export {
3680
- LikeC4Diagram,
3681
- StaticLikeC4Diagram,
3682
- useSetState,
3683
- useUpdateEffect
3684
- };