likec4 0.44.2 → 0.46.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 (44) hide show
  1. package/dist/@likec4/core/utils/relations.js +11 -2
  2. package/dist/@likec4/diagrams/components/primitives/fullscreen/FullscreenDiagram.js +8 -7
  3. package/dist/@likec4/diagrams/diagram/Diagram.js +63 -45
  4. package/dist/@likec4/diagrams/diagram/Edges.js +32 -16
  5. package/dist/@likec4/diagrams/diagram/Nodes.js +66 -70
  6. package/dist/@likec4/diagrams/diagram/icons/ZoomIn.js +2 -3
  7. package/dist/@likec4/diagrams/diagram/shapes/Browser.js +43 -9
  8. package/dist/@likec4/diagrams/diagram/shapes/Compound.js +8 -10
  9. package/dist/@likec4/diagrams/diagram/shapes/Cylinder.js +3 -1
  10. package/dist/@likec4/diagrams/diagram/shapes/Edge.js +10 -10
  11. package/dist/@likec4/diagrams/diagram/shapes/Mobile.js +19 -4
  12. package/dist/@likec4/diagrams/diagram/shapes/NodeIcon.js +47 -9
  13. package/dist/@likec4/diagrams/diagram/shapes/NodeLabel.js +31 -54
  14. package/dist/@likec4/diagrams/diagram/shapes/Person.js +3 -1
  15. package/dist/@likec4/diagrams/diagram/shapes/Queue.js +3 -1
  16. package/dist/@likec4/diagrams/diagram/shapes/Rectangle.js +3 -1
  17. package/dist/@likec4/diagrams/diagram/shapes/index.js +1 -1
  18. package/dist/@likec4/diagrams/diagram/shapes/utils.js +1 -1
  19. package/dist/@likec4/diagrams/diagram/state/atoms.js +6 -0
  20. package/dist/@likec4/diagrams/diagram/state/hooks.js +10 -1
  21. package/dist/@likec4/diagrams/hooks/useDiagramApi.js +19 -22
  22. package/dist/@likec4/diagrams/hooks/useImageLoader.js +7 -1
  23. package/dist/__app__/index.html +1 -1
  24. package/dist/__app__/likec4.css +23 -0
  25. package/dist/__app__/src/App.jsx +27 -5
  26. package/dist/__app__/src/components/DiagramNotFound.jsx +10 -4
  27. package/dist/__app__/src/components/sidebar/Sidebar.jsx +1 -1
  28. package/dist/__app__/src/components/view-page/DisplayModeSelector.jsx +1 -1
  29. package/dist/__app__/src/components/view-page/ExportDiagram.jsx +13 -7
  30. package/dist/__app__/src/components/view-page/ViewActionsToolbar.jsx +12 -3
  31. package/dist/__app__/src/data/atoms.js +0 -11
  32. package/dist/__app__/src/pages/embed.page.jsx +14 -0
  33. package/dist/__app__/src/pages/export.page.jsx +4 -13
  34. package/dist/__app__/src/pages/index.js +1 -0
  35. package/dist/__app__/src/pages/useTransparentBackground.js +16 -0
  36. package/dist/__app__/src/pages/view-page.module.css +30 -0
  37. package/dist/__app__/src/pages/view.page.jsx +44 -8
  38. package/dist/__app__/src/router.js +14 -12
  39. package/dist/__app__/tailwind.config.cjs +1 -3
  40. package/dist/__app__/tsconfig.json +1 -7
  41. package/dist/cli/index.js +199 -206
  42. package/package.json +9 -10
  43. package/dist/@likec4/diagrams/hooks/useDarkMode.js +0 -5
  44. package/dist/@likec4/diagrams/index.mjs +0 -1927
@@ -1,1927 +0,0 @@
1
- import { useState, useCallback, useRef, useLayoutEffect, useEffect, forwardRef, useImperativeHandle, useMemo } from 'react';
2
- import { DefaultArrowType, DefaultLineStyle, DefaultRelationshipColor, invariant, nonexhaustive, nonNullable, defaultTheme, isString } from '@likec4/core';
3
- import { useHookableRef, useUpdateEffect, useMeasure, useKeyboardEvent, useSyncedRef as useSyncedRef$1, useMediaQuery } from '@react-hookz/web/esm';
4
- import { animated, useSpring, useTransition, easings } from '@react-spring/konva';
5
- import { equals, memoize, clamp } from 'rambdax';
6
- import { Stage, Group, Rect, Text, Line, Path, Ellipse, Image, Circle, Layer } from 'react-konva/es/ReactKonvaCore';
7
- import KonvaCore from 'konva/lib/Core';
8
- import 'konva/lib/shapes/Rect';
9
- import 'konva/lib/shapes/Text';
10
- import 'konva/lib/shapes/Path';
11
- import 'konva/lib/shapes/Circle';
12
- import 'konva/lib/shapes/Line';
13
- import 'konva/lib/shapes/Image';
14
- import 'konva/lib/shapes/Ellipse';
15
- import { toHex, scale } from 'khroma';
16
- import { atom, useAtom, useAtomValue, useSetAtom, createStore, Provider } from 'jotai';
17
- import { selectAtom } from 'jotai/utils';
18
- import { createUseGesture, dragAction, pinchAction } from '@use-gesture/react';
19
- import { useIsMounted } from '@react-hookz/web/esm/useIsMounted';
20
- import { createPortal } from 'react-dom';
21
- import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock-upgrade';
22
-
23
- KonvaCore.hitOnDragEnabled = true;
24
- KonvaCore.capturePointerEventsEnabled = true;
25
- KonvaCore.dragButtons = [0, 2];
26
- const AnimatedStage = /* @__PURE__ */ animated(Stage);
27
- const AnimatedRect = /* @__PURE__ */ animated(Rect);
28
- const AnimatedGroup = /* @__PURE__ */ animated(Group);
29
- const AnimatedText = /* @__PURE__ */ animated(Text);
30
- const AnimatedPath = /* @__PURE__ */ animated(Path);
31
- const AnimatedLine = /* @__PURE__ */ animated(Line);
32
- const AnimatedEllipse = /* @__PURE__ */ animated(Ellipse);
33
-
34
- function EdgeArrow({
35
- arrowType,
36
- points,
37
- springs,
38
- globalCompositeOperation
39
- }) {
40
- const isOutline = arrowType === "odiamond" || arrowType === "onormal";
41
- return /* @__PURE__ */ React.createElement(
42
- AnimatedLine,
43
- {
44
- opacity: springs.opacity,
45
- points: points.flat(),
46
- closed: true,
47
- fill: isOutline ? void 0 : springs.lineColor,
48
- stroke: springs.lineColor,
49
- strokeWidth: 1.6,
50
- hitStrokeWidth: 5,
51
- lineCap: "round",
52
- lineJoin: "miter",
53
- globalCompositeOperation
54
- }
55
- );
56
- }
57
- function EdgeLabelBg({
58
- animate,
59
- labelBBox,
60
- isHovered,
61
- springs
62
- }) {
63
- const padding = 4;
64
- const props = useSpring({
65
- to: {
66
- x: labelBBox.x - padding,
67
- y: labelBBox.y - padding,
68
- width: labelBBox.width + padding * 2,
69
- height: labelBBox.height + padding * 2,
70
- opacity: isHovered ? 0.25 : 0.1
71
- },
72
- immediate: !animate
73
- });
74
- return /* @__PURE__ */ React.createElement(
75
- AnimatedRect,
76
- {
77
- ...props,
78
- fill: springs.labelBgColor,
79
- cornerRadius: 2,
80
- globalCompositeOperation: "darken",
81
- hitStrokeWidth: 5
82
- }
83
- );
84
- }
85
- function EdgeShape({ animate = true, edge, theme, isHovered, springs }) {
86
- const {
87
- points,
88
- head,
89
- color,
90
- line = DefaultLineStyle,
91
- headArrow,
92
- tail,
93
- tailArrow,
94
- labelBBox,
95
- labels
96
- } = edge;
97
- const globalCompositeOperation = !color || color === DefaultRelationshipColor ? "luminosity" : "lighten";
98
- const isDotted = line === "dotted";
99
- const isDashed = isDotted || line === "dashed";
100
- let dash;
101
- if (isDotted) {
102
- dash = [1, 5];
103
- } else if (isDashed) {
104
- dash = [10, 8];
105
- }
106
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
107
- AnimatedLine,
108
- {
109
- opacity: springs.opacity,
110
- bezier: true,
111
- dashEnabled: isDashed,
112
- dashOffset: 1,
113
- points: points.flat(),
114
- dash,
115
- stroke: springs.lineColor,
116
- strokeWidth: springs.lineWidth,
117
- hitStrokeWidth: 20,
118
- lineCap: "round",
119
- lineJoin: "round",
120
- globalCompositeOperation
121
- }
122
- ), head !== "none" && headArrow && /* @__PURE__ */ React.createElement(
123
- EdgeArrow,
124
- {
125
- key: "head",
126
- arrowType: head ?? DefaultArrowType,
127
- points: headArrow,
128
- springs,
129
- globalCompositeOperation
130
- }
131
- ), tail !== "none" && tailArrow && /* @__PURE__ */ React.createElement(
132
- EdgeArrow,
133
- {
134
- key: "tail",
135
- arrowType: tail ?? DefaultArrowType,
136
- points: tailArrow,
137
- springs,
138
- globalCompositeOperation
139
- }
140
- ), labelBBox && labelBBox.width > 0 && /* @__PURE__ */ React.createElement(
141
- EdgeLabelBg,
142
- {
143
- animate,
144
- labelBBox,
145
- isHovered,
146
- springs
147
- }
148
- ), labels && labels.map((label, i) => /* @__PURE__ */ React.createElement(
149
- AnimatedText,
150
- {
151
- key: i,
152
- x: label.pt[0],
153
- y: label.pt[1],
154
- offsetY: label.fontSize / 2,
155
- opacity: springs.opacity,
156
- fill: springs.labelColor,
157
- fontFamily: theme.font,
158
- fontSize: label.fontSize,
159
- fontStyle: label.fontStyle ?? "normal",
160
- text: label.text,
161
- listening: false,
162
- globalCompositeOperation,
163
- shadowEnabled: springs.opacity.to((o) => o > 0.5),
164
- shadowColor: "#222",
165
- shadowOpacity: 0.15,
166
- shadowOffsetX: 1,
167
- shadowOffsetY: 2,
168
- shadowBlur: 2
169
- }
170
- )));
171
- }
172
-
173
- function mousePointer(e) {
174
- const container = e.target.getStage()?.container();
175
- if (container) {
176
- container.style.cursor = "pointer";
177
- }
178
- }
179
- function mouseDefault(e) {
180
- const container = e.target.getStage()?.container();
181
- if (container) {
182
- container.style.cursor = "auto";
183
- }
184
- }
185
-
186
- const currentHoveredNodeAtom = atom(null);
187
- const nodeTimeoutAtom = atom(void 0);
188
- const hoveredNodeAtom = atom(
189
- (get) => get(currentHoveredNodeAtom),
190
- (get, set, update) => {
191
- clearTimeout(get(nodeTimeoutAtom));
192
- clearTimeout(get(edgeTimeoutAtom));
193
- const _prev = get(currentHoveredNodeAtom);
194
- const _next = typeof update === "function" ? update(_prev) : update;
195
- if (equals(_prev, _next)) {
196
- return false;
197
- }
198
- if (_next != null && _prev == null) {
199
- set(
200
- nodeTimeoutAtom,
201
- setTimeout(() => {
202
- set(currentHoveredNodeAtom, _next);
203
- set(currentHoveredEdgeAtom, null);
204
- }, 200)
205
- );
206
- return true;
207
- }
208
- if (_next != null && _prev != null) {
209
- set(
210
- nodeTimeoutAtom,
211
- setTimeout(() => {
212
- set(currentHoveredNodeAtom, _next);
213
- set(currentHoveredEdgeAtom, null);
214
- }, 150)
215
- );
216
- return true;
217
- }
218
- if (_next == null && _prev != null) {
219
- set(
220
- nodeTimeoutAtom,
221
- setTimeout(() => {
222
- set(currentHoveredNodeAtom, null);
223
- }, 150)
224
- );
225
- return true;
226
- }
227
- set(currentHoveredNodeAtom, _next);
228
- return true;
229
- }
230
- );
231
- const hoveredNodeIdAtom = selectAtom(hoveredNodeAtom, (node) => node?.id ?? null);
232
- const currentHoveredEdgeAtom = atom(null);
233
- const edgeTimeoutAtom = atom(void 0);
234
- const hoveredEdgeAtom = atom(
235
- (get) => get(currentHoveredEdgeAtom),
236
- (get, set, update) => {
237
- clearTimeout(get(nodeTimeoutAtom));
238
- clearTimeout(get(edgeTimeoutAtom));
239
- const _prev = get(currentHoveredEdgeAtom);
240
- const _next = typeof update === "function" ? update(_prev) : update;
241
- if (equals(_prev, _next)) {
242
- return false;
243
- }
244
- if (_next != null && _prev == null) {
245
- set(
246
- edgeTimeoutAtom,
247
- setTimeout(() => {
248
- set(currentHoveredEdgeAtom, _next);
249
- set(currentHoveredNodeAtom, null);
250
- }, 400)
251
- );
252
- return true;
253
- }
254
- if (_next != null && _prev != null) {
255
- set(
256
- edgeTimeoutAtom,
257
- setTimeout(() => {
258
- set(currentHoveredEdgeAtom, _next);
259
- set(currentHoveredNodeAtom, null);
260
- }, 150)
261
- );
262
- return true;
263
- }
264
- if (_next == null && _prev != null) {
265
- set(
266
- edgeTimeoutAtom,
267
- setTimeout(() => {
268
- set(currentHoveredEdgeAtom, null);
269
- }, 150)
270
- );
271
- return true;
272
- }
273
- set(currentHoveredEdgeAtom, null);
274
- return true;
275
- }
276
- );
277
- const hoveredEdgeIdAtom = selectAtom(hoveredEdgeAtom, (edge) => edge?.id ?? null);
278
-
279
- function useHoveredNode() {
280
- return useAtom(hoveredNodeAtom);
281
- }
282
- function useHoveredNodeId() {
283
- return useAtomValue(hoveredNodeIdAtom);
284
- }
285
- function useSetHoveredNode() {
286
- return useSetAtom(hoveredNodeAtom);
287
- }
288
- function useHoveredEdge() {
289
- return useAtom(hoveredEdgeAtom);
290
- }
291
- function useHoveredEdgeId() {
292
- return useAtomValue(hoveredEdgeIdAtom);
293
- }
294
- function useSetHoveredEdge() {
295
- return useSetAtom(hoveredEdgeAtom);
296
- }
297
-
298
- let _isDragging = false;
299
- let tm;
300
- const DiagramGesture = {
301
- get isDragging() {
302
- return _isDragging;
303
- },
304
- set isDragging(value) {
305
- clearTimeout(tm);
306
- if (value) {
307
- _isDragging = value;
308
- return;
309
- }
310
- if (_isDragging) {
311
- tm = setTimeout(() => {
312
- _isDragging = false;
313
- }, 100);
314
- }
315
- }
316
- };
317
-
318
- function DiagramStateProvider({ children }) {
319
- const [store] = useState(() => createStore());
320
- return /* @__PURE__ */ React.createElement(Provider, { store }, children);
321
- }
322
-
323
- const edgeColors = memoize((colors, isHovered) => {
324
- if (isHovered) {
325
- return {
326
- lineColor: toHex(
327
- scale(colors.lineColor, {
328
- l: 25,
329
- s: -5
330
- })
331
- ),
332
- labelColor: toHex(
333
- scale(colors.labelColor, {
334
- l: 40
335
- })
336
- ),
337
- labelBgColor: toHex(
338
- scale(colors.labelBgColor, {
339
- l: -10
340
- })
341
- )
342
- };
343
- } else {
344
- return colors;
345
- }
346
- });
347
- function Edges({ animate, theme, diagram, onEdgeClick }) {
348
- const hoveredEdgeId = useHoveredEdgeId();
349
- const setHoveredEdge = useSetHoveredEdge();
350
- const edgeSprings = useCallback(
351
- (edge, isHovered = false) => {
352
- return {
353
- opacity: 1,
354
- lineWidth: 2,
355
- ...edgeColors(theme.relationships[edge.color ?? DefaultRelationshipColor], isHovered)
356
- };
357
- },
358
- [theme]
359
- );
360
- const edgeTransitions = useTransition(diagram.edges, {
361
- from: (edge) => ({
362
- ...edgeSprings(edge),
363
- opacity: 0.15,
364
- lineWidth: 2
365
- }),
366
- initial: (edge) => edgeSprings(edge),
367
- update: (edge) => {
368
- const isInactive = animate && hoveredEdgeId !== null && hoveredEdgeId !== edge.id;
369
- const isHovered = animate && hoveredEdgeId === edge.id;
370
- return {
371
- ...edgeSprings(edge, isHovered),
372
- opacity: isInactive ? 0.4 : 1,
373
- lineWidth: isHovered ? 3 : 2
374
- };
375
- },
376
- enter: {
377
- opacity: 1
378
- },
379
- leave: (edge) => {
380
- return {
381
- ...edgeSprings(edge),
382
- opacity: 0.05,
383
- lineWidth: 2
384
- };
385
- },
386
- expires: true,
387
- exitBeforeEnter: true,
388
- immediate: !animate,
389
- // delay: 30,
390
- config: {
391
- duration: 160,
392
- precision: 5e-3
393
- },
394
- // unique edge key, scoped to this diagram
395
- // to avoid any issues with diagram-to-diagram transitions
396
- keys: (e) => e.id + diagram.id
397
- });
398
- return edgeTransitions((springs, edge, { key }) => /* @__PURE__ */ React.createElement(
399
- Group,
400
- {
401
- key,
402
- onPointerClick: (e) => {
403
- if (!onEdgeClick || DiagramGesture.isDragging || e.evt.button !== 0) {
404
- return;
405
- }
406
- e.cancelBubble = true;
407
- onEdgeClick(edge, e);
408
- },
409
- onPointerEnter: (e) => {
410
- if (animate) {
411
- setHoveredEdge(edge);
412
- mousePointer(e);
413
- }
414
- },
415
- onPointerLeave: (e) => {
416
- setHoveredEdge(null);
417
- mouseDefault(e);
418
- }
419
- },
420
- /* @__PURE__ */ React.createElement(
421
- EdgeShape,
422
- {
423
- animate,
424
- edge,
425
- isHovered: hoveredEdgeId === edge.id,
426
- theme,
427
- springs
428
- }
429
- )
430
- ));
431
- }
432
-
433
- function Portal({ selector, enabled, children }) {
434
- const outer = useRef(null);
435
- const inner = useRef(null);
436
- const safeRef = useRef();
437
- const shouldMove = enabled ?? true;
438
- useLayoutEffect(() => {
439
- if (!outer.current || !inner.current) {
440
- return;
441
- }
442
- const stage = outer.current.getStage();
443
- if (!stage) {
444
- return;
445
- }
446
- if (shouldMove) {
447
- const newContainer = stage.findOne(selector);
448
- if (newContainer) {
449
- safeRef.current = inner.current;
450
- inner.current.moveTo(newContainer);
451
- }
452
- } else if (safeRef.current) {
453
- safeRef.current.moveTo(outer.current);
454
- safeRef.current = void 0;
455
- }
456
- }, [selector, shouldMove]);
457
- useEffect(() => {
458
- return () => {
459
- safeRef.current?.destroy();
460
- };
461
- }, []);
462
- return /* @__PURE__ */ React.createElement(Group, { ref: outer }, /* @__PURE__ */ React.createElement(Group, { ref: inner }, children));
463
- }
464
-
465
- const ZoomInIcon = ({ fill, opacity = 1, size = 20, x, y }) => {
466
- const originalSize = 15;
467
- const scale = size / originalSize;
468
- const offsetIcon = originalSize / 2;
469
- return /* @__PURE__ */ React.createElement(
470
- Path,
471
- {
472
- data: "M10 6.5C10 8.433 8.433 10 6.5 10C4.567 10 3 8.433 3 6.5C3 4.567 4.567 3 6.5 3C8.433 3 10 4.567 10 6.5ZM9.30884 10.0159C8.53901 10.6318 7.56251 11 6.5 11C4.01472 11 2 8.98528 2 6.5C2 4.01472 4.01472 2 6.5 2C8.98528 2 11 4.01472 11 6.5C11 7.56251 10.6318 8.53901 10.0159 9.30884L12.8536 12.1464C13.0488 12.3417 13.0488 12.6583 12.8536 12.8536C12.6583 13.0488 12.3417 13.0488 12.1464 12.8536L9.30884 10.0159ZM4.25 6.5C4.25 6.22386 4.47386 6 4.75 6H6V4.75C6 4.47386 6.22386 4.25 6.5 4.25C6.77614 4.25 7 4.47386 7 4.75V6H8.25C8.52614 6 8.75 6.22386 8.75 6.5C8.75 6.77614 8.52614 7 8.25 7H7V8.25C7 8.52614 6.77614 8.75 6.5 8.75C6.22386 8.75 6 8.52614 6 8.25V7H4.75C4.47386 7 4.25 6.77614 4.25 6.5Z",
473
- fill,
474
- fillRule: "evenodd",
475
- strokeEnabled: false,
476
- x,
477
- y,
478
- offsetX: offsetIcon,
479
- offsetY: offsetIcon,
480
- scaleX: scale,
481
- scaleY: scale,
482
- width: originalSize,
483
- height: originalSize,
484
- opacity,
485
- globalCompositeOperation: "luminosity",
486
- hitStrokeWidth: 5
487
- }
488
- );
489
- };
490
-
491
- function CompoundShape({ node, theme, springs, labelOffsetX = 4 }) {
492
- const { labels } = node;
493
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
494
- AnimatedRect,
495
- {
496
- cornerRadius: 4,
497
- shadowColor: theme.shadow,
498
- shadowBlur: node.level > 0 ? 20 : 10,
499
- shadowOpacity: node.level > 0 ? 0.35 : 0.8,
500
- shadowOffsetX: 0,
501
- shadowOffsetY: 4,
502
- shadowEnabled: springs.opacity.to((v) => v > 0.7),
503
- width: springs.width,
504
- height: springs.height,
505
- fill: springs.fill,
506
- strokeEnabled: false,
507
- listening: false
508
- }
509
- ), labels.map(({ pt: [x, y], ...label }, i) => /* @__PURE__ */ React.createElement(
510
- AnimatedText,
511
- {
512
- key: i,
513
- x,
514
- y: y - 4,
515
- offsetX: labelOffsetX,
516
- offsetY: label.fontSize / 2,
517
- width: springs.width.to((v) => v - x - 4),
518
- fill: "#BABABA",
519
- fontFamily: theme.font,
520
- fontSize: label.fontSize,
521
- fontStyle: label.fontStyle ?? "normal",
522
- letterSpacing: 0.8,
523
- align: label.align,
524
- text: label.text,
525
- wrap: "none",
526
- ellipsis: true,
527
- perfectDrawEnabled: false,
528
- padding: 6,
529
- hitStrokeWidth: 3,
530
- globalCompositeOperation: "luminosity"
531
- }
532
- )));
533
- }
534
-
535
- const compoundColor = memoize(
536
- (color, depth) => toHex(
537
- scale(color, {
538
- l: -35 - 5 * depth,
539
- s: -15 - 5 * depth
540
- })
541
- )
542
- );
543
- function isCompound(node) {
544
- return node.children.length > 0;
545
- }
546
- function nodeSprings(theme, node) {
547
- const {
548
- position: [x, y],
549
- size: { width, height },
550
- color
551
- } = node;
552
- const colors = theme.elements[color];
553
- const offsetX = Math.round(width / 2);
554
- const offsetY = Math.round(height / 2);
555
- return {
556
- opacity: 1,
557
- scaleX: 1,
558
- scaleY: 1,
559
- x: x + offsetX,
560
- y: y + offsetY,
561
- fill: isCompound(node) ? compoundColor(colors.fill, node.depth ?? 1) : colors.fill,
562
- stroke: isCompound(node) ? compoundColor(colors.stroke, node.depth ?? 1) : colors.stroke,
563
- width,
564
- height,
565
- offsetX,
566
- offsetY
567
- };
568
- }
569
- const useNodeSpringsFn = (theme) => {
570
- return useCallback((node) => nodeSprings(theme, node), [theme]);
571
- };
572
- const useShadowSprings = (isHovered = false, theme, springs) => {
573
- const [values] = useSpring(
574
- {
575
- shadowBlur: isHovered ? 30 : 12,
576
- shadowOpacity: isHovered ? 0.5 : 0.35,
577
- shadowOffsetX: 0,
578
- shadowOffsetY: isHovered ? 16 : 4,
579
- shadowColor: theme.shadow
580
- },
581
- [isHovered, theme]
582
- );
583
- return {
584
- shadowEnabled: springs.opacity.to((v) => v > 0.8),
585
- ...values
586
- };
587
- };
588
-
589
- const imageElements = /* @__PURE__ */ new Map();
590
- function useImageLoader(url, crossOrigin, referrerpolicy) {
591
- const isMounted = useIsMounted();
592
- const imageRef = useRef(url ? imageElements.get(url) : void 0);
593
- const statusRef = useRef(imageRef.current ? "loaded" : "loading");
594
- const [_, setStateToken] = useState(0);
595
- const urlRef = useRef(url);
596
- if (!url || urlRef.current !== url) {
597
- statusRef.current = "loading";
598
- imageRef.current = void 0;
599
- urlRef.current = url;
600
- }
601
- useLayoutEffect(() => {
602
- if (!url) {
603
- return;
604
- }
605
- const urlClosure = url;
606
- const imgCached = imageElements.get(url);
607
- if (imgCached) {
608
- statusRef.current = "loaded";
609
- if (imageRef.current !== imgCached) {
610
- imageRef.current = imgCached;
611
- setStateToken(Math.random());
612
- }
613
- return;
614
- }
615
- const img = document.createElement("img");
616
- function onload() {
617
- if (!isMounted())
618
- return;
619
- imageElements.set(urlClosure, img);
620
- if (urlRef.current === urlClosure) {
621
- statusRef.current = "loaded";
622
- imageRef.current = img;
623
- setStateToken(Math.random());
624
- }
625
- }
626
- function onerror() {
627
- if (!isMounted() || urlRef.current !== urlClosure)
628
- return;
629
- statusRef.current = "failed";
630
- imageRef.current = void 0;
631
- setStateToken(Math.random());
632
- }
633
- img.addEventListener("load", onload);
634
- img.addEventListener("error", onerror);
635
- crossOrigin && (img.crossOrigin = crossOrigin);
636
- referrerpolicy && (img.referrerPolicy = referrerpolicy);
637
- img.src = url;
638
- return () => {
639
- img.removeEventListener("load", onload);
640
- img.removeEventListener("error", onerror);
641
- };
642
- }, [url, crossOrigin ?? null, referrerpolicy ?? null]);
643
- if (imageRef.current !== void 0) {
644
- statusRef.current = "loaded";
645
- return [imageRef.current, "loaded"];
646
- }
647
- invariant(statusRef.current !== "loaded", "image status cant be loaded");
648
- return [imageRef.current, statusRef.current];
649
- }
650
-
651
- function NodeIcon({ icon, maxWidth, maxHeight, offsetX = 0, offsetY = 0 }) {
652
- const [image] = useImageLoader(icon);
653
- if (!image) {
654
- return null;
655
- }
656
- const padding = 16;
657
- const maxIconWidth = Math.round(maxWidth - padding * 2);
658
- const maxIconHeight = Math.round(maxHeight - padding * 2);
659
- const scale = Math.min(maxIconWidth / image.width, maxIconHeight / image.height, 1);
660
- const iconWidth = Math.floor(image.width * scale);
661
- const iconHeight = Math.floor(image.height * scale);
662
- return /* @__PURE__ */ React.createElement(
663
- Image,
664
- {
665
- image,
666
- x: padding + (maxIconWidth - iconWidth) / 2,
667
- y: padding + (maxIconHeight - iconHeight) / 2,
668
- offsetX,
669
- offsetY,
670
- width: iconWidth,
671
- height: iconHeight,
672
- listening: false,
673
- globalCompositeOperation: "hard-light"
674
- }
675
- );
676
- }
677
-
678
- function NodeLabels({
679
- node: { icon, labels, size, color },
680
- theme,
681
- offsetX = 0,
682
- offsetY = 0,
683
- maxWidth
684
- }) {
685
- const colors = theme.elements[color];
686
- const width = maxWidth ?? size.width;
687
- const firstLabel = labels[0];
688
- const titleFontSize = firstLabel?.fontSize ?? 12;
689
- let nodeIcon;
690
- if (icon) {
691
- const maxHeight = firstLabel ? Math.floor(firstLabel.pt[1] - firstLabel.fontSize / 2) : size.height;
692
- nodeIcon = /* @__PURE__ */ React.createElement(
693
- NodeIcon,
694
- {
695
- icon,
696
- maxWidth: width,
697
- maxHeight,
698
- offsetX,
699
- offsetY
700
- }
701
- );
702
- }
703
- return /* @__PURE__ */ React.createElement(React.Fragment, null, nodeIcon, labels.map((label, i) => {
704
- let color2 = colors.hiContrast;
705
- if (label.fontSize !== titleFontSize) {
706
- color2 = colors.loContrast;
707
- }
708
- return /* @__PURE__ */ React.createElement(
709
- Text,
710
- {
711
- key: label.text + i,
712
- x: 8,
713
- width: width - 16,
714
- y: label.pt[1],
715
- offsetY: offsetY + label.fontSize / 2,
716
- offsetX,
717
- fill: color2,
718
- fontFamily: theme.font,
719
- fontSize: label.fontSize,
720
- fontStyle: label.fontStyle ?? "normal",
721
- align: "center",
722
- text: label.text,
723
- strokeEnabled: false,
724
- perfectDrawEnabled: false,
725
- listening: false
726
- }
727
- );
728
- }));
729
- }
730
- NodeLabels.displayName = "NodeLabels";
731
-
732
- function RectangleShape({ node, theme, springs, isHovered }) {
733
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
734
- AnimatedRect,
735
- {
736
- ...useShadowSprings(isHovered, theme, springs),
737
- cornerRadius: 6,
738
- strokeEnabled: false,
739
- width: springs.width,
740
- height: springs.height,
741
- fill: springs.fill
742
- }
743
- ), /* @__PURE__ */ React.createElement(NodeLabels, { node, theme }));
744
- }
745
-
746
- function cylinderSVGPath(diameter, height, tilt = 0.0825) {
747
- const radius = Math.round(diameter / 2);
748
- const rx = radius;
749
- const ry = Math.round(tilt * radius);
750
- const tiltAdjustedHeight = height - 2 * ry;
751
- const path = ` M ${diameter},${ry}
752
- a ${rx},${ry} 0,0,0 ${-diameter} 0
753
- l 0,${tiltAdjustedHeight}
754
- a ${rx},${ry} 0,0,0 ${diameter} 0
755
- l 0,${-tiltAdjustedHeight}
756
- `.replace(/\s+/g, " ").trim();
757
- return {
758
- path,
759
- ry,
760
- rx
761
- };
762
- }
763
- function CylinderShape({ node, theme, springs, isHovered }) {
764
- const {
765
- size: { width, height }
766
- } = node;
767
- const { path, rx, ry } = cylinderSVGPath(width, height);
768
- const cylinder = useSpring({
769
- to: {
770
- rx,
771
- ry
772
- }
773
- });
774
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
775
- AnimatedPath,
776
- {
777
- ...useShadowSprings(isHovered, theme, springs),
778
- data: path,
779
- shadowForStrokeEnabled: false,
780
- fill: springs.fill
781
- }
782
- ), /* @__PURE__ */ React.createElement(
783
- AnimatedEllipse,
784
- {
785
- x: cylinder.rx,
786
- y: cylinder.ry,
787
- radiusX: cylinder.rx,
788
- radiusY: cylinder.ry,
789
- fill: springs.stroke
790
- }
791
- ), /* @__PURE__ */ React.createElement(NodeLabels, { node, offsetY: -ry * (node.icon ? 1.5 : 0.5), theme }));
792
- }
793
-
794
- function MobileShape({ node, theme, springs, isHovered }) {
795
- const colors = theme.elements[node.color];
796
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
797
- AnimatedRect,
798
- {
799
- ...useShadowSprings(isHovered, theme, springs),
800
- cornerRadius: 6,
801
- width: springs.width,
802
- height: springs.height,
803
- fill: springs.stroke
804
- }
805
- ), /* @__PURE__ */ React.createElement(Circle, { x: 16, y: node.size.height / 2, radius: 10, fill: colors.fill, listening: false }), /* @__PURE__ */ React.createElement(
806
- AnimatedRect,
807
- {
808
- cornerRadius: 4,
809
- x: 31,
810
- y: 12,
811
- width: springs.width.to((w) => w - 43),
812
- height: springs.height.to((h) => h - 24),
813
- fill: springs.fill,
814
- listening: false
815
- }
816
- ), /* @__PURE__ */ React.createElement(NodeLabels, { node, theme, offsetX: -6 }));
817
- }
818
-
819
- function queueSVGPath(width, height, tilt = 0.2) {
820
- const diameter = height;
821
- const radius = Math.round(diameter / 2);
822
- const ry = radius;
823
- const rx = Math.round(diameter / 2 * tilt);
824
- const tiltAdjustedWidth = width - 2 * rx;
825
- const path = ` M ${rx},0
826
- a ${rx},${ry} 0,0,0 0 ${diameter}
827
- l ${tiltAdjustedWidth},0
828
- a ${rx},${ry} 0,0,0 0 ${-diameter}
829
- `.replace(/\s+/g, " ").trim();
830
- return {
831
- path,
832
- ry,
833
- rx
834
- };
835
- }
836
- function QueueShape({ node, theme, springs, isHovered }) {
837
- const {
838
- size: { width, height }
839
- } = node;
840
- const { path, rx, ry } = queueSVGPath(width, height);
841
- const props = useSpring({
842
- to: {
843
- x: width - rx,
844
- y: ry,
845
- rx,
846
- ry
847
- }
848
- });
849
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
850
- AnimatedPath,
851
- {
852
- ...useShadowSprings(isHovered, theme, springs),
853
- data: path,
854
- fill: springs.fill
855
- }
856
- ), /* @__PURE__ */ React.createElement(
857
- AnimatedEllipse,
858
- {
859
- x: props.x,
860
- y: props.y,
861
- radiusX: props.rx,
862
- radiusY: props.ry,
863
- fill: springs.stroke
864
- }
865
- ), /* @__PURE__ */ React.createElement(NodeLabels, { node, maxWidth: width - rx * 2, theme }));
866
- }
867
-
868
- const PersonIcon = {
869
- width: 115,
870
- height: 120,
871
- 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`
872
- };
873
- function PersonShape({ node, theme, springs, isHovered }) {
874
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
875
- AnimatedRect,
876
- {
877
- ...useShadowSprings(isHovered, theme, springs),
878
- cornerRadius: 6,
879
- perfectDrawEnabled: false,
880
- strokeEnabled: false,
881
- width: springs.width,
882
- height: springs.height,
883
- fill: springs.fill
884
- }
885
- ), /* @__PURE__ */ React.createElement(
886
- AnimatedPath,
887
- {
888
- x: springs.width.to((v) => v - 8),
889
- y: springs.height,
890
- data: PersonIcon.path,
891
- width: PersonIcon.width,
892
- height: PersonIcon.height,
893
- fill: springs.stroke,
894
- opacity: 0.7,
895
- perfectDrawEnabled: false,
896
- offsetX: PersonIcon.width,
897
- offsetY: PersonIcon.height,
898
- listening: false
899
- }
900
- ), /* @__PURE__ */ React.createElement(NodeLabels, { node, theme }));
901
- }
902
-
903
- function BrowserShape({ node, theme, springs, isHovered }) {
904
- const colors = theme.elements[node.color];
905
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
906
- AnimatedRect,
907
- {
908
- ...useShadowSprings(isHovered, theme, springs),
909
- cornerRadius: 6,
910
- strokeEnabled: false,
911
- width: springs.width,
912
- height: springs.height,
913
- fill: springs.stroke
914
- }
915
- ), /* @__PURE__ */ React.createElement(Circle, { x: 16, y: 15, radius: 7, fill: colors.fill, listening: false }), /* @__PURE__ */ React.createElement(Circle, { x: 36, y: 15, radius: 7, fill: colors.fill, listening: false }), /* @__PURE__ */ React.createElement(Circle, { x: 56, y: 15, radius: 7, fill: colors.fill, listening: false }), /* @__PURE__ */ React.createElement(
916
- AnimatedRect,
917
- {
918
- cornerRadius: 5,
919
- x: 70,
920
- y: 7,
921
- width: springs.width.to((w) => w - 80),
922
- height: 16,
923
- fill: springs.fill,
924
- listening: false
925
- }
926
- ), /* @__PURE__ */ React.createElement(
927
- AnimatedRect,
928
- {
929
- cornerRadius: 5,
930
- x: 9,
931
- y: 31,
932
- width: springs.width.to((w) => w - 18),
933
- height: springs.height.to((h) => h - 40),
934
- fill: springs.fill,
935
- listening: false
936
- }
937
- ), /* @__PURE__ */ React.createElement(NodeLabels, { node, theme, offsetY: -8 }));
938
- }
939
-
940
- function nodeShape({ shape }) {
941
- switch (shape) {
942
- case "cylinder":
943
- case "storage": {
944
- return CylinderShape;
945
- }
946
- case "queue": {
947
- return QueueShape;
948
- }
949
- case "browser": {
950
- return BrowserShape;
951
- }
952
- case "person": {
953
- return PersonShape;
954
- }
955
- case "rectangle": {
956
- return RectangleShape;
957
- }
958
- case "mobile": {
959
- return MobileShape;
960
- }
961
- default: {
962
- return nonexhaustive(shape);
963
- }
964
- }
965
- }
966
- const keyOf = (node) => {
967
- const key = (node.parent ? node.parent + "-" : "") + node.id;
968
- if (isCompound(node)) {
969
- return "compound-" + key;
970
- }
971
- return key;
972
- };
973
- function Nodes({ animate, theme, diagram, onNodeClick }) {
974
- const _prev = useRef(/* @__PURE__ */ new Map());
975
- const _last = useRef(diagram);
976
- if (_last.current.id !== diagram.id) {
977
- _prev.current = new Map(_last.current.nodes.map((n) => [n.id, n]));
978
- }
979
- _last.current = diagram;
980
- const prevNodes = _prev.current;
981
- const hoveredNodeId = useHoveredNodeId();
982
- const [hoveredEdge] = useHoveredEdge();
983
- const nodeSprings = useNodeSpringsFn(theme);
984
- const nodeTransitions = useTransition(diagram.nodes, {
985
- initial: nodeSprings,
986
- from: (node) => {
987
- const prevNode = prevNodes.get(node.id);
988
- if (prevNode) {
989
- return nodeSprings(prevNode);
990
- }
991
- return {
992
- ...nodeSprings(node),
993
- opacity: 0,
994
- scaleX: isCompound(node) ? 0.85 : 0.6,
995
- scaleY: isCompound(node) ? 0.85 : 0.6
996
- };
997
- },
998
- enter: (node) => {
999
- const isReplacing = prevNodes.has(node.id);
1000
- return {
1001
- ...nodeSprings(node),
1002
- delay: isReplacing ? 50 : 70
1003
- };
1004
- },
1005
- // update: nodeSprings(),
1006
- update: (node) => {
1007
- const isInactive = animate && hoveredEdge && hoveredEdge.source !== node.id && hoveredEdge.target !== node.id;
1008
- const scale = animate && !isCompound(node) && hoveredNodeId === node.id ? 1.08 : 1;
1009
- return {
1010
- ...nodeSprings(node),
1011
- opacity: isInactive ? 0.3 : 1,
1012
- scaleX: scale,
1013
- scaleY: scale
1014
- };
1015
- },
1016
- leave: (node) => {
1017
- const replacedWith = diagram.nodes.find((n) => n.id === node.id);
1018
- if (replacedWith && keyOf(node) !== keyOf(replacedWith)) {
1019
- return {
1020
- opacity: 0,
1021
- immediate: true
1022
- };
1023
- }
1024
- return {
1025
- opacity: 0,
1026
- scaleX: isCompound(node) ? 0.7 : 0.5,
1027
- scaleY: isCompound(node) ? 0.7 : 0.5,
1028
- config: {
1029
- duration: 120
1030
- }
1031
- };
1032
- },
1033
- sort: (a, b) => {
1034
- if (isCompound(a) === isCompound(b)) {
1035
- return a.level - b.level;
1036
- }
1037
- return isCompound(a) ? -1 : 1;
1038
- },
1039
- expires: true,
1040
- immediate: !animate,
1041
- keys: keyOf
1042
- });
1043
- return nodeTransitions((_, node, { key, ctrl, expired }) => /* @__PURE__ */ React.createElement(
1044
- NodeSnape,
1045
- {
1046
- key,
1047
- animate,
1048
- node,
1049
- theme,
1050
- ctrl,
1051
- expired,
1052
- onNodeClick,
1053
- isHovered: node.id === hoveredNodeId
1054
- }
1055
- ));
1056
- }
1057
- function NodeSnape({
1058
- animate,
1059
- node,
1060
- ctrl,
1061
- theme,
1062
- isHovered,
1063
- expired,
1064
- onNodeClick
1065
- }) {
1066
- const setHoveredNode = useSetHoveredNode();
1067
- const _isCompound = isCompound(node);
1068
- const isNavigatable = !!node.navigateTo && !!onNodeClick;
1069
- const Shape = nodeShape(node);
1070
- const springs = ctrl.springs;
1071
- return /* @__PURE__ */ React.createElement(Portal, { selector: ".top", enabled: isHovered && !_isCompound }, /* @__PURE__ */ React.createElement(
1072
- AnimatedGroup,
1073
- {
1074
- name: node.id,
1075
- visible: expired !== true,
1076
- onPointerEnter: (e) => {
1077
- if (animate) {
1078
- setHoveredNode(node);
1079
- onNodeClick && mousePointer(e);
1080
- }
1081
- },
1082
- onPointerLeave: (e) => {
1083
- setHoveredNode(null);
1084
- mouseDefault(e);
1085
- },
1086
- ...onNodeClick && {
1087
- onPointerClick: (e) => {
1088
- if (DiagramGesture.isDragging || e.evt.button !== 0) {
1089
- return;
1090
- }
1091
- e.cancelBubble = true;
1092
- onNodeClick(node, e);
1093
- }
1094
- },
1095
- x: springs.x,
1096
- y: springs.y,
1097
- offsetX: springs.offsetX,
1098
- offsetY: springs.offsetY,
1099
- width: springs.width,
1100
- height: springs.height,
1101
- scaleX: springs.scaleX,
1102
- scaleY: springs.scaleY,
1103
- opacity: springs.opacity
1104
- },
1105
- _isCompound && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1106
- CompoundShape,
1107
- {
1108
- node,
1109
- theme,
1110
- springs,
1111
- labelOffsetX: isNavigatable ? -12 : 4
1112
- }
1113
- ), isNavigatable && /* @__PURE__ */ React.createElement(ZoomInIcon, { fill: "#BABABA", opacity: 0.9, size: 16, x: 16, y: 18 })),
1114
- !_isCompound && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Shape, { node, theme, springs, isHovered }), isNavigatable && /* @__PURE__ */ React.createElement(
1115
- ZoomInIcon,
1116
- {
1117
- fill: "#BABABA",
1118
- size: 16,
1119
- x: node.size.width / 2,
1120
- y: node.size.height - 20
1121
- }
1122
- ))
1123
- ));
1124
- }
1125
-
1126
- const useGesture = createUseGesture([dragAction, pinchAction]);
1127
- const useSyncedRef = (value) => {
1128
- const ref = useRef(value);
1129
- Object.assign(ref.current, value);
1130
- return ref;
1131
- };
1132
- const NoPadding = [0, 0, 0, 0];
1133
- function diagramNodeId(konvaNode) {
1134
- let shape = konvaNode;
1135
- while (shape && shape.nodeType !== "Stage") {
1136
- const name = shape.name();
1137
- if (name !== "") {
1138
- return name;
1139
- }
1140
- shape = shape.parent;
1141
- }
1142
- return null;
1143
- }
1144
- const Diagram = /* @__PURE__ */ forwardRef(
1145
- ({
1146
- diagram,
1147
- padding: _padding = NoPadding,
1148
- pannable = true,
1149
- zoomable = true,
1150
- animate = true,
1151
- initialPosition,
1152
- onEdgeClick,
1153
- onNodeClick,
1154
- onNodeContextMenu,
1155
- onStageClick,
1156
- onStageContextMenu,
1157
- width: _width,
1158
- height: _height,
1159
- ...props
1160
- }, ref) => {
1161
- const immediate = !animate;
1162
- const id = diagram.id;
1163
- const containerRef = useRef(null);
1164
- const stageRef = useHookableRef(null, (value) => {
1165
- containerRef.current = value?.container() ?? null;
1166
- if (containerRef.current) {
1167
- containerRef.current.style.touchAction = "none";
1168
- }
1169
- return value;
1170
- });
1171
- const width = _width ?? diagram.width;
1172
- const height = _height ?? diagram.height;
1173
- const toCenterOnRect = (centerTo) => {
1174
- const [paddingTop, paddingRight, paddingBottom, paddingLeft] = Array.isArray(_padding) ? _padding : [_padding, _padding, _padding, _padding];
1175
- const container = stageRef.current?.container();
1176
- const viewRect = {
1177
- width: Math.min(container?.clientWidth ?? width, width) - paddingLeft - paddingRight,
1178
- height: Math.min(container?.clientHeight ?? height, height) - paddingTop - paddingBottom
1179
- }, viewScale = Math.min(viewRect.width / centerTo.width, viewRect.height / centerTo.height), scale = clamp(0.1, 1.1, viewScale), centeringAjustment = {
1180
- x: ((width - centerTo.width) * scale + viewRect.width) / 2,
1181
- y: ((height - centerTo.height) * scale + viewRect.height) / 2
1182
- }, finalPosition = {
1183
- x: Math.ceil(paddingLeft + centeringAjustment.x - centerTo.x * scale),
1184
- y: Math.ceil(paddingTop + centeringAjustment.y - centerTo.y * scale)
1185
- };
1186
- return {
1187
- ...finalPosition,
1188
- scale
1189
- };
1190
- };
1191
- const toFitDiagram = () => toCenterOnRect({ x: 0, y: 0, width: diagram.width, height: diagram.height });
1192
- const [stageProps, stageSpringApi] = useSpring(
1193
- () => initialPosition ? {
1194
- from: initialPosition,
1195
- to: toFitDiagram()
1196
- } : {
1197
- from: toFitDiagram(),
1198
- immediate
1199
- }
1200
- );
1201
- const centerOnRect = (centerTo) => {
1202
- stageSpringApi.start({
1203
- to: toCenterOnRect(centerTo),
1204
- immediate
1205
- });
1206
- return;
1207
- };
1208
- const centerAndFit = (delay = 70, durationMs) => {
1209
- stageSpringApi.start({
1210
- to: toFitDiagram(),
1211
- delay,
1212
- config: durationMs ? {
1213
- duration: durationMs,
1214
- easing: easings.easeInOutCubic
1215
- } : {},
1216
- immediate
1217
- });
1218
- return;
1219
- };
1220
- const resetStageZoom = (_immediate) => {
1221
- stageSpringApi.start({
1222
- to: {
1223
- x: 0,
1224
- y: 0,
1225
- scale: 1
1226
- },
1227
- immediate
1228
- });
1229
- return;
1230
- };
1231
- const refs = useSyncedRef({
1232
- diagram,
1233
- width,
1234
- height,
1235
- centerOnRect,
1236
- centerAndFit,
1237
- resetStageZoom
1238
- });
1239
- useImperativeHandle(
1240
- ref,
1241
- () => ({
1242
- get stage() {
1243
- return nonNullable(stageRef.current, "not mounted");
1244
- },
1245
- get diagramView() {
1246
- return refs.current.diagram;
1247
- },
1248
- get container() {
1249
- return nonNullable(stageRef.current?.container(), "not mounted");
1250
- },
1251
- resetStageZoom: (_immediate) => {
1252
- refs.current.resetStageZoom(_immediate);
1253
- },
1254
- centerOnNode: (node) => refs.current.centerOnRect({
1255
- x: node.position[0],
1256
- y: node.position[1],
1257
- width: node.size.width,
1258
- height: node.size.height
1259
- }),
1260
- centerAndFit: () => refs.current.centerAndFit()
1261
- }),
1262
- [refs, id, stageRef]
1263
- );
1264
- useUpdateEffect(() => {
1265
- refs.current.centerAndFit(80, 650);
1266
- }, [id, height, width]);
1267
- useEffect(() => {
1268
- if (!zoomable) {
1269
- return;
1270
- }
1271
- const handler = (e) => e.preventDefault();
1272
- document.addEventListener("gesturestart", handler);
1273
- document.addEventListener("gesturechange", handler);
1274
- return () => {
1275
- document.removeEventListener("gesturestart", handler);
1276
- document.removeEventListener("gesturechange", handler);
1277
- };
1278
- }, [zoomable]);
1279
- useGesture(
1280
- {
1281
- onDragEnd: () => {
1282
- DiagramGesture.isDragging = false;
1283
- },
1284
- onDrag: (state) => {
1285
- const {
1286
- pinching,
1287
- down,
1288
- cancel,
1289
- intentional,
1290
- offset: [x, y]
1291
- } = state;
1292
- if (pinching) {
1293
- return cancel();
1294
- }
1295
- if (intentional) {
1296
- DiagramGesture.isDragging = true;
1297
- stageSpringApi.start({
1298
- to: {
1299
- x,
1300
- y
1301
- },
1302
- immediate: immediate || down
1303
- });
1304
- }
1305
- },
1306
- onPinch: ({ memo, first, last, origin: [ox, oy], movement: [ms], offset: [scale] }) => {
1307
- if (first) {
1308
- const stage = nonNullable(stageRef.current);
1309
- const { x: x2, y: y2 } = stage.getAbsolutePosition();
1310
- const tx = Math.round(ox - x2);
1311
- const ty = Math.round(oy - y2);
1312
- memo = [stage.x(), stage.y(), tx, ty];
1313
- }
1314
- const x = Math.round(memo[0] - (ms - 1) * memo[2]);
1315
- const y = Math.round(memo[1] - (ms - 1) * memo[3]);
1316
- stageSpringApi.start({
1317
- to: {
1318
- x,
1319
- y,
1320
- scale
1321
- },
1322
- immediate: immediate || !last || !first
1323
- });
1324
- return memo;
1325
- }
1326
- },
1327
- {
1328
- target: containerRef,
1329
- drag: {
1330
- enabled: pannable,
1331
- threshold: 4,
1332
- from: () => [stageProps.x.get(), stageProps.y.get()],
1333
- pointer: {
1334
- buttons: -1,
1335
- keys: false
1336
- }
1337
- },
1338
- pinch: {
1339
- pointer: {
1340
- touch: true
1341
- },
1342
- enabled: zoomable,
1343
- scaleBounds: { min: 0.1, max: 1.4 },
1344
- rubberband: 0.05,
1345
- pinchOnWheel: true
1346
- }
1347
- }
1348
- );
1349
- const sharedProps = {
1350
- animate,
1351
- theme: defaultTheme,
1352
- diagram
1353
- };
1354
- return /* @__PURE__ */ React.createElement(
1355
- AnimatedStage,
1356
- {
1357
- ref: stageRef,
1358
- width,
1359
- height,
1360
- offsetX: width / 2,
1361
- offsetY: height / 2,
1362
- x: stageProps.x,
1363
- y: stageProps.y,
1364
- scaleX: stageProps.scale,
1365
- scaleY: stageProps.scale,
1366
- ...(onStageContextMenu || onNodeContextMenu) && {
1367
- onContextMenu: (e) => {
1368
- if (DiagramGesture.isDragging || !stageRef.current) {
1369
- return;
1370
- }
1371
- if (e.target === stageRef.current || !onNodeContextMenu) {
1372
- e.cancelBubble = true;
1373
- onStageContextMenu?.(stageRef.current, e);
1374
- return;
1375
- }
1376
- if (onNodeContextMenu) {
1377
- const nodeId = diagramNodeId(e.target);
1378
- const node = nodeId && refs.current.diagram.nodes.find((n) => n.id === nodeId);
1379
- if (node) {
1380
- e.cancelBubble = true;
1381
- onNodeContextMenu(node, e);
1382
- return;
1383
- }
1384
- }
1385
- }
1386
- },
1387
- ...onStageClick && {
1388
- onPointerClick: (e) => {
1389
- if (DiagramGesture.isDragging || e.evt.button !== 0 || !stageRef.current) {
1390
- return;
1391
- }
1392
- if (e.target === stageRef.current) {
1393
- e.cancelBubble = true;
1394
- onStageClick(stageRef.current, e);
1395
- }
1396
- }
1397
- },
1398
- ...zoomable && {
1399
- onPointerDblClick: (e) => {
1400
- if (DiagramGesture.isDragging || e.evt.button !== 0 || !stageRef.current) {
1401
- return;
1402
- }
1403
- if (e.target === stageRef.current) {
1404
- e.cancelBubble = true;
1405
- centerAndFit();
1406
- }
1407
- }
1408
- },
1409
- ...props
1410
- },
1411
- /* @__PURE__ */ React.createElement(Layer, null, /* @__PURE__ */ React.createElement(Nodes, { ...sharedProps, onNodeClick }), /* @__PURE__ */ React.createElement(Edges, { ...sharedProps, onEdgeClick })),
1412
- /* @__PURE__ */ React.createElement(Layer, { name: "top" })
1413
- );
1414
- }
1415
- );
1416
- Diagram.displayName = "Diagram";
1417
-
1418
- function readViewIdFromUrl(url) {
1419
- return readViewId(new URL(url).hash);
1420
- }
1421
- function readViewId(hash = window.location.hash) {
1422
- if (hash.startsWith("#")) {
1423
- hash = hash.slice(1);
1424
- }
1425
- if (hash.includes("likec4")) {
1426
- return new URLSearchParams(hash).get("likec4");
1427
- }
1428
- return null;
1429
- }
1430
- function writeViewId(viewId) {
1431
- let hash = window.location.hash;
1432
- if (hash.startsWith("#")) {
1433
- hash = hash.slice(1);
1434
- }
1435
- const hashParams = new URLSearchParams(hash);
1436
- if (viewId != null) {
1437
- if (hashParams.get("likec4") !== viewId) {
1438
- hashParams.set("likec4", viewId);
1439
- window.location.hash = hashParams.toString();
1440
- }
1441
- return;
1442
- }
1443
- if (viewId === null && hashParams.has("likec4")) {
1444
- hashParams.delete("likec4");
1445
- window.location.hash = hashParams.toString();
1446
- return;
1447
- }
1448
- }
1449
- const noopIsViewId = (value) => isString(value) && value.length > 0;
1450
- function useViewIdFromHash({
1451
- initialViewId,
1452
- onReturnToInitial,
1453
- resetHashOnUnmount = true,
1454
- isViewId = noopIsViewId
1455
- }) {
1456
- const [viewId, setStateViewId] = useState(() => {
1457
- const id = readViewId();
1458
- return isViewId(id) ? id : initialViewId;
1459
- });
1460
- const viewIdRef = useRef(viewId);
1461
- const prevIdRef = useRef();
1462
- if (viewIdRef.current !== viewId) {
1463
- prevIdRef.current = viewIdRef.current;
1464
- viewIdRef.current = viewId;
1465
- }
1466
- const onReturnRef = useRef(onReturnToInitial);
1467
- onReturnRef.current = onReturnToInitial;
1468
- useEffect(() => {
1469
- const tm = setTimeout(() => {
1470
- writeViewId(viewIdRef.current);
1471
- }, 300);
1472
- return () => clearTimeout(tm);
1473
- }, []);
1474
- useEffect(() => {
1475
- const onHashChange = (ev) => {
1476
- const newViewId = readViewIdFromUrl(ev.newURL);
1477
- if (newViewId === null) {
1478
- const oldViewId = readViewIdFromUrl(ev.oldURL);
1479
- if (oldViewId != null && onReturnRef.current) {
1480
- onReturnRef.current?.();
1481
- }
1482
- return;
1483
- }
1484
- if (isViewId(newViewId) && newViewId !== viewIdRef.current) {
1485
- setStateViewId(newViewId);
1486
- }
1487
- };
1488
- window.addEventListener("hashchange", onHashChange);
1489
- return () => {
1490
- window.removeEventListener("hashchange", onHashChange);
1491
- if (resetHashOnUnmount) {
1492
- writeViewId(null);
1493
- }
1494
- };
1495
- }, []);
1496
- const setViewId = useCallback((nextViewId) => {
1497
- if (isViewId(nextViewId) && nextViewId !== viewIdRef.current) {
1498
- writeViewId(nextViewId);
1499
- }
1500
- }, []);
1501
- return [viewId, setViewId];
1502
- }
1503
-
1504
- const ResponsiveDiagram = /* @__PURE__ */ forwardRef(
1505
- ({ diagram, animate = false, className, diagramClassName, style, ...props }, ref) => {
1506
- const [measures, containerRef] = useMeasure();
1507
- const w = diagram.width, h = diagram.height;
1508
- return /* @__PURE__ */ React.createElement(
1509
- "div",
1510
- {
1511
- style: {
1512
- position: "relative",
1513
- display: "flex",
1514
- aspectRatio: `${w} / ${h}`,
1515
- width: "100%",
1516
- height: "auto",
1517
- marginLeft: "auto",
1518
- marginRight: "auto",
1519
- padding: 0,
1520
- maxWidth: w,
1521
- boxSizing: "border-box",
1522
- ...style
1523
- },
1524
- className
1525
- },
1526
- /* @__PURE__ */ React.createElement("div", { ref: containerRef, style: { flex: "1 1 100%", overflow: "hidden" } }, measures && /* @__PURE__ */ React.createElement(
1527
- Diagram,
1528
- {
1529
- ref,
1530
- width: Math.floor(measures.width),
1531
- height: Math.floor(measures.height),
1532
- diagram,
1533
- className: diagramClassName,
1534
- animate,
1535
- ...props
1536
- }
1537
- ))
1538
- );
1539
- }
1540
- );
1541
- ResponsiveDiagram.displayName = "ResponsiveDiagram";
1542
-
1543
- const CrossIcon = forwardRef((props, ref) => /* @__PURE__ */ React.createElement(
1544
- "svg",
1545
- {
1546
- ref,
1547
- width: "15",
1548
- height: "15",
1549
- viewBox: "0 0 15 15",
1550
- fill: "none",
1551
- xmlns: "http://www.w3.org/2000/svg",
1552
- ...props
1553
- },
1554
- /* @__PURE__ */ React.createElement(
1555
- "path",
1556
- {
1557
- d: "M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z",
1558
- fill: "currentColor",
1559
- fillRule: "evenodd",
1560
- clipRule: "evenodd"
1561
- }
1562
- )
1563
- ));
1564
- const CloseButton = ({
1565
- style,
1566
- ...props
1567
- }) => /* @__PURE__ */ React.createElement(
1568
- "div",
1569
- {
1570
- ...props,
1571
- style: {
1572
- position: "absolute",
1573
- top: "1rem",
1574
- right: "1rem",
1575
- width: 24,
1576
- height: 24,
1577
- cursor: "pointer",
1578
- color: "var(--likec4-browser-close-btn-color, rgba(200,200,200,0.8))",
1579
- ...style
1580
- }
1581
- },
1582
- /* @__PURE__ */ React.createElement(CrossIcon, { width: 24, height: 24 })
1583
- );
1584
-
1585
- const DefaultPadding = [30, 30, 30, 30];
1586
- const StyleOverlay = {
1587
- position: "fixed",
1588
- top: 0,
1589
- left: 0,
1590
- right: 0,
1591
- bottom: 0,
1592
- backgroundColor: "var(--likec4-browser-overlay-bg, rgba(18,18,18,0.8))",
1593
- backdropFilter: "var(--likec4-browser-backdrop, blur(4px))",
1594
- zIndex: "var(--likec4-overlay-z-index, 100)",
1595
- display: "flex",
1596
- placeContent: "strech",
1597
- placeItems: "strech",
1598
- touchAction: "pan-x pan-y pinch-zoom",
1599
- boxSizing: "border-box",
1600
- margin: 0,
1601
- padding: 0,
1602
- border: "0 solid transparent"
1603
- };
1604
- const StyleContainer = {
1605
- margin: 0,
1606
- padding: 0,
1607
- flex: "1 1 100%",
1608
- overflow: "hidden"
1609
- };
1610
- function FullscreenDiagram({
1611
- diagram,
1612
- initialPosition,
1613
- padding,
1614
- style,
1615
- styleDiagram,
1616
- closeOnEsc = true,
1617
- closeOnOutsideClick,
1618
- onNodeClick,
1619
- onEdgeClick,
1620
- onStageClick,
1621
- onClose,
1622
- children,
1623
- ...props
1624
- }) {
1625
- if (onStageClick && closeOnOutsideClick === true) {
1626
- throw new Error("onStageClick cannot be used with closeOnOutsideClick");
1627
- }
1628
- if (closeOnEsc || closeOnOutsideClick) {
1629
- invariant(onClose, "onClose is required when closeOnEsc or closeOnOutsideClick");
1630
- }
1631
- const overlayRef = useRef(null);
1632
- const [measures, containerRef] = useMeasure();
1633
- useEffect(() => {
1634
- const target = overlayRef.current;
1635
- if (!target) {
1636
- return;
1637
- }
1638
- disableBodyScroll(target);
1639
- return () => {
1640
- enableBodyScroll(target);
1641
- };
1642
- }, []);
1643
- useKeyboardEvent(
1644
- closeOnEsc && "Escape",
1645
- () => {
1646
- onClose?.();
1647
- },
1648
- [],
1649
- { eventOptions: { passive: true } }
1650
- );
1651
- return /* @__PURE__ */ React.createElement(
1652
- "div",
1653
- {
1654
- ref: overlayRef,
1655
- style: {
1656
- ...StyleOverlay,
1657
- ...style
1658
- },
1659
- ...props
1660
- },
1661
- /* @__PURE__ */ React.createElement(
1662
- "div",
1663
- {
1664
- ref: containerRef,
1665
- style: {
1666
- ...StyleContainer
1667
- }
1668
- },
1669
- measures && /* @__PURE__ */ React.createElement(
1670
- Diagram,
1671
- {
1672
- animate: true,
1673
- pannable: true,
1674
- zoomable: true,
1675
- diagram,
1676
- width: measures.width,
1677
- height: measures.height,
1678
- initialPosition,
1679
- padding: padding ?? DefaultPadding,
1680
- style: styleDiagram,
1681
- onNodeClick,
1682
- onEdgeClick,
1683
- onStageClick: onStageClick ?? (closeOnOutsideClick ? onClose : void 0)
1684
- }
1685
- )
1686
- ),
1687
- children,
1688
- onClose && /* @__PURE__ */ React.createElement(CloseButton, { onClick: () => onClose() })
1689
- );
1690
- }
1691
- FullscreenDiagram.displayName = "FullscreenDiagram";
1692
-
1693
- const ContainerStyles = {
1694
- position: "absolute",
1695
- pointerEvents: "none",
1696
- userSelect: "none",
1697
- top: 0,
1698
- left: 0,
1699
- width: "100%",
1700
- margin: 0,
1701
- padding: "10px 20px 0 20px",
1702
- boxSizing: "border-box",
1703
- color: "rgb(241 245 249 / 95%)",
1704
- fontFamily: 'ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif',
1705
- fontSize: "16px",
1706
- lineHeight: "1.1",
1707
- fontWeight: "500",
1708
- textShadow: "4px 4px 5px rgb(17 17 17)"
1709
- };
1710
- function FullscreenDiagramTitle({ children, style, ...props }) {
1711
- return /* @__PURE__ */ React.createElement(
1712
- "div",
1713
- {
1714
- style: {
1715
- ...ContainerStyles,
1716
- ...style
1717
- },
1718
- ...props
1719
- },
1720
- children
1721
- );
1722
- }
1723
- FullscreenDiagramTitle.displayName = "FullscreenDiagramTitle";
1724
-
1725
- function FullscreenDiagramBrowser({
1726
- initialViewId,
1727
- views,
1728
- initialPosition,
1729
- onClose,
1730
- closeOnEsc,
1731
- closeOnOutsideClick,
1732
- ...props
1733
- }) {
1734
- const viewsRef = useSyncedRef$1(views);
1735
- const [viewId, setViewId] = useViewIdFromHash({
1736
- initialViewId,
1737
- onReturnToInitial: () => {
1738
- onClose?.();
1739
- }
1740
- });
1741
- const diagram = views[viewId];
1742
- invariant(diagram, `View "${viewId}" not found in views`);
1743
- const onNodeClick = useCallback(
1744
- (node) => {
1745
- if (node.navigateTo && node.navigateTo in viewsRef.current) {
1746
- setViewId(node.navigateTo);
1747
- }
1748
- },
1749
- [viewsRef, setViewId]
1750
- );
1751
- return createPortal(
1752
- /* @__PURE__ */ React.createElement(
1753
- FullscreenDiagram,
1754
- {
1755
- diagram,
1756
- initialPosition,
1757
- closeOnEsc: true,
1758
- closeOnOutsideClick: true,
1759
- onNodeClick,
1760
- onClose,
1761
- ...props
1762
- },
1763
- /* @__PURE__ */ React.createElement(FullscreenDiagramTitle, null, diagram.title)
1764
- ),
1765
- document.body,
1766
- "FullscreenDiagramBrowser"
1767
- );
1768
- }
1769
- FullscreenDiagramBrowser.displayName = "FullscreenDiagramBrowser";
1770
-
1771
- const EmbeddedPadding = [20, 20, 20, 20];
1772
- const defaultRenderDisclose = ({
1773
- views,
1774
- viewId,
1775
- initialPosition,
1776
- onClose
1777
- }) => /* @__PURE__ */ React.createElement(
1778
- FullscreenDiagramBrowser,
1779
- {
1780
- views,
1781
- initialViewId: viewId,
1782
- initialPosition,
1783
- onClose
1784
- }
1785
- );
1786
- const EmbeddedDiagram = /* @__PURE__ */ forwardRef(
1787
- ({
1788
- animate = false,
1789
- views,
1790
- viewId,
1791
- padding,
1792
- noBrowser = false,
1793
- renderDisclose = defaultRenderDisclose,
1794
- ...props
1795
- }, ref) => {
1796
- const diagram = views[viewId];
1797
- invariant(diagram, `View "${viewId}" not found in views`);
1798
- const [initialPosition, setBrowserInitialPosition] = useState(
1799
- null
1800
- );
1801
- const isOpened = initialPosition !== null;
1802
- const openBrowser = (s) => {
1803
- const rect = s.container().getBoundingClientRect(), scale = s.scaleX(), embeddedX = s.x() - s.offsetX(), embeddedY = s.y() - s.offsetY(), offset = {
1804
- x: window.innerWidth / 2,
1805
- y: window.innerHeight / 2
1806
- };
1807
- setBrowserInitialPosition({
1808
- x: Math.ceil(embeddedX + rect.x * scale + offset.x),
1809
- y: Math.ceil(embeddedY + rect.y * scale + offset.y),
1810
- scale
1811
- });
1812
- };
1813
- const onNodeEdgeClick = (_node, e) => {
1814
- const stage = e.target.getStage();
1815
- if (stage) {
1816
- e.cancelBubble = true;
1817
- openBrowser(stage);
1818
- }
1819
- };
1820
- const onClose = useCallback(() => setBrowserInitialPosition(null), []);
1821
- const enableBrowseClicks = !noBrowser && !isOpened;
1822
- return /* @__PURE__ */ React.createElement("div", { ...props }, /* @__PURE__ */ React.createElement(
1823
- ResponsiveDiagram,
1824
- {
1825
- ref,
1826
- animate,
1827
- zoomable: false,
1828
- pannable: false,
1829
- diagram,
1830
- padding: padding ?? EmbeddedPadding,
1831
- ...enableBrowseClicks && {
1832
- onStageClick: openBrowser,
1833
- onEdgeClick: onNodeEdgeClick,
1834
- onNodeClick: onNodeEdgeClick
1835
- }
1836
- }
1837
- ), isOpened && renderDisclose({ views, viewId, initialPosition, onClose }));
1838
- }
1839
- );
1840
-
1841
- const COLOR_SCHEME_QUERY = "(prefers-color-scheme: dark)";
1842
- function useDarkMode() {
1843
- return useMediaQuery(COLOR_SCHEME_QUERY) ?? true;
1844
- }
1845
-
1846
- function useDiagramApi() {
1847
- const ref = useRef(null);
1848
- return useMemo(
1849
- () => [
1850
- ref,
1851
- {
1852
- get stage() {
1853
- return nonNullable(ref.current, "not mounted, use ref").stage;
1854
- },
1855
- get diagramView() {
1856
- return nonNullable(ref.current, "not mounted, use ref").diagramView;
1857
- },
1858
- get container() {
1859
- return nonNullable(ref.current, "not mounted, use ref").container;
1860
- },
1861
- resetStageZoom: (_immediate) => {
1862
- nonNullable(ref.current, "not mounted, use ref").resetStageZoom(_immediate);
1863
- },
1864
- centerOnNode: (node) => nonNullable(ref.current, "not mounted, use ref").centerOnNode(node),
1865
- centerAndFit: () => nonNullable(ref.current, "not mounted, use ref").centerAndFit()
1866
- }
1867
- ],
1868
- [ref]
1869
- );
1870
- }
1871
-
1872
- var __defProp = Object.defineProperty;
1873
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1874
- var __publicField = (obj, key, value) => {
1875
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1876
- return value;
1877
- };
1878
- class LikeC4 {
1879
- constructor(views) {
1880
- this.views = views;
1881
- __publicField(this, "isViewId", (value) => {
1882
- return value != null && typeof value === "string" && Object.prototype.hasOwnProperty.call(this.views, value);
1883
- });
1884
- /**
1885
- * React hook to use location hash for ViewId.
1886
- * When the element with this hook is mounted, hash is set to `...#likec4={initialViewId}`
1887
- * When element with this hook is unmounted, `likec4` is removed from hash if `resetHashOnUnmount` is true (default: true)
1888
- * You can also provide `onReturnToInitial` callback to handle back navigation (when user goes beyond the initialViewId)
1889
- */
1890
- __publicField(this, "useViewId", (props) => useViewIdFromHash({ ...props, isViewId: this.isViewId }));
1891
- __publicField(this, "Diagram", forwardRef(({ viewId, ...props }, ref) => {
1892
- const diagram = this.views[viewId];
1893
- if (!diagram) {
1894
- throw new Error(`View "${viewId}" not found in views`);
1895
- }
1896
- return /* @__PURE__ */ React.createElement(Diagram, { ref, diagram, ...props });
1897
- }));
1898
- __publicField(this, "Responsive", forwardRef(({ viewId, ...props }, ref) => {
1899
- const diagram = this.views[viewId];
1900
- if (!diagram) {
1901
- throw new Error(`View "${viewId}" not found in views`);
1902
- }
1903
- return /* @__PURE__ */ React.createElement(ResponsiveDiagram, { ref, diagram, ...props });
1904
- }));
1905
- __publicField(this, "Fullscreen", ({ viewId, ...props }) => {
1906
- const diagram = this.views[viewId];
1907
- if (!diagram) {
1908
- throw new Error(`View "${viewId}" not found in views`);
1909
- }
1910
- return /* @__PURE__ */ React.createElement(FullscreenDiagram, { diagram, ...props });
1911
- });
1912
- __publicField(this, "Embedded", forwardRef((props, ref) => {
1913
- return /* @__PURE__ */ React.createElement(EmbeddedDiagram, { ref, views: this.views, ...props });
1914
- }));
1915
- __publicField(this, "Browser", (props) => {
1916
- return /* @__PURE__ */ React.createElement(FullscreenDiagramBrowser, { views: this.views, ...props });
1917
- });
1918
- }
1919
- static create(views) {
1920
- if (Object.keys(views).length === 0) {
1921
- throw new Error("LikeC4: views must not be empty");
1922
- }
1923
- return new LikeC4(views);
1924
- }
1925
- }
1926
-
1927
- export { Diagram, DiagramGesture, DiagramStateProvider, EmbeddedDiagram, FullscreenDiagram, FullscreenDiagramBrowser, FullscreenDiagramTitle, LikeC4, ResponsiveDiagram, useDarkMode, useDiagramApi, useHoveredEdge, useHoveredEdgeId, useHoveredNode, useHoveredNodeId, useSetHoveredEdge, useSetHoveredNode, useViewIdFromHash };