@likec4/diagram 1.8.1 → 1.12.0

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