likec4 0.45.0 → 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.
- package/dist/@likec4/core/utils/relations.js +11 -2
- package/dist/@likec4/diagrams/diagram/Diagram.js +52 -45
- package/dist/@likec4/diagrams/diagram/Nodes.js +9 -11
- package/dist/@likec4/diagrams/diagram/shapes/Edge.js +3 -3
- package/dist/@likec4/diagrams/hooks/useDiagramApi.js +19 -22
- package/dist/__app__/index.html +1 -1
- package/dist/__app__/likec4.css +19 -0
- package/dist/__app__/src/App.jsx +4 -2
- package/dist/__app__/src/components/sidebar/Sidebar.jsx +1 -1
- package/dist/__app__/src/components/view-page/DisplayModeSelector.jsx +1 -1
- package/dist/__app__/src/components/view-page/ExportDiagram.jsx +13 -7
- package/dist/__app__/src/components/view-page/ViewActionsToolbar.jsx +12 -3
- package/dist/__app__/src/data/atoms.js +0 -11
- package/dist/__app__/src/pages/embed.page.jsx +4 -2
- package/dist/__app__/src/pages/export.page.jsx +2 -14
- package/dist/__app__/src/pages/useTransparentBackground.js +16 -0
- package/dist/__app__/src/pages/view-page.module.css +30 -0
- package/dist/__app__/src/pages/view.page.jsx +41 -5
- package/dist/__app__/src/router.js +12 -10
- package/dist/__app__/tsconfig.json +0 -5
- package/dist/cli/index.js +198 -205
- package/package.json +9 -9
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { compareFqnHierarchically, isAncestor } from "./fqn.js";
|
|
1
|
+
import { commonAncestor, compareFqnHierarchically, isAncestor } from "./fqn.js";
|
|
2
2
|
import { either } from "rambdax";
|
|
3
3
|
export const compareRelations = (a, b) => {
|
|
4
|
-
|
|
4
|
+
const parentA = commonAncestor(a.source, a.target);
|
|
5
|
+
const parentB = commonAncestor(b.source, b.target);
|
|
6
|
+
if (parentA && !parentB) {
|
|
7
|
+
return 1;
|
|
8
|
+
}
|
|
9
|
+
if (!parentA && parentB) {
|
|
10
|
+
return -1;
|
|
11
|
+
}
|
|
12
|
+
const compareParents = parentA && parentB ? compareFqnHierarchically(parentA, parentB) : 0;
|
|
13
|
+
return compareParents || compareFqnHierarchically(a.source, b.source) || compareFqnHierarchically(a.target, b.target);
|
|
5
14
|
};
|
|
6
15
|
const isInside = (parent) => {
|
|
7
16
|
const prefix = parent + ".";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { nonNullable, defaultTheme as theme } from "@likec4/core";
|
|
3
3
|
import { useHookableRef, useUpdateEffect } from "@react-hookz/web/esm";
|
|
4
|
-
import {
|
|
4
|
+
import { useSpring } from "@react-spring/konva";
|
|
5
5
|
import { clamp } from "rambdax";
|
|
6
6
|
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
|
|
7
7
|
import { AnimatedStage, Layer } from "../konva.js";
|
|
@@ -30,7 +30,7 @@ function diagramNodeId(konvaNode) {
|
|
|
30
30
|
export const Diagram = /* @__PURE__ */ forwardRef(
|
|
31
31
|
({
|
|
32
32
|
diagram,
|
|
33
|
-
padding
|
|
33
|
+
padding = NoPadding,
|
|
34
34
|
pannable = true,
|
|
35
35
|
zoomable = true,
|
|
36
36
|
animate = true,
|
|
@@ -56,11 +56,11 @@ export const Diagram = /* @__PURE__ */ forwardRef(
|
|
|
56
56
|
}
|
|
57
57
|
return value;
|
|
58
58
|
});
|
|
59
|
-
const
|
|
60
|
-
const
|
|
59
|
+
const [paddingTop, paddingRight, paddingBottom, paddingLeft] = Array.isArray(padding) ? padding : [padding, padding, padding, padding];
|
|
60
|
+
const width = _width ?? diagram.width + paddingLeft + paddingRight;
|
|
61
|
+
const height = _height ?? diagram.height + paddingTop + paddingBottom;
|
|
61
62
|
const toCenterOnRect = (centerTo) => {
|
|
62
|
-
const
|
|
63
|
-
const container = stageRef.current?.container();
|
|
63
|
+
const container = containerRef.current;
|
|
64
64
|
const viewRect = {
|
|
65
65
|
width: Math.min(container?.clientWidth ?? width, width) - paddingLeft - paddingRight,
|
|
66
66
|
height: Math.min(container?.clientHeight ?? height, height) - paddingTop - paddingBottom
|
|
@@ -80,11 +80,13 @@ export const Diagram = /* @__PURE__ */ forwardRef(
|
|
|
80
80
|
const [stageProps, stageSpringApi] = useSpring(
|
|
81
81
|
() => initialPosition ? {
|
|
82
82
|
from: initialPosition,
|
|
83
|
-
to: toFitDiagram()
|
|
83
|
+
to: toFitDiagram(),
|
|
84
|
+
immediate
|
|
84
85
|
} : {
|
|
85
|
-
|
|
86
|
+
to: toFitDiagram(),
|
|
86
87
|
immediate
|
|
87
|
-
}
|
|
88
|
+
},
|
|
89
|
+
[]
|
|
88
90
|
);
|
|
89
91
|
const centerOnRect = (centerTo) => {
|
|
90
92
|
stageSpringApi.start({
|
|
@@ -93,14 +95,10 @@ export const Diagram = /* @__PURE__ */ forwardRef(
|
|
|
93
95
|
});
|
|
94
96
|
return;
|
|
95
97
|
};
|
|
96
|
-
const centerAndFit = (delay =
|
|
98
|
+
const centerAndFit = (delay = 0) => {
|
|
97
99
|
stageSpringApi.start({
|
|
98
100
|
to: toFitDiagram(),
|
|
99
101
|
delay,
|
|
100
|
-
config: durationMs ? {
|
|
101
|
-
duration: durationMs,
|
|
102
|
-
easing: easings.easeInOutCubic
|
|
103
|
-
} : {},
|
|
104
102
|
immediate
|
|
105
103
|
});
|
|
106
104
|
return;
|
|
@@ -128,13 +126,13 @@ export const Diagram = /* @__PURE__ */ forwardRef(
|
|
|
128
126
|
ref,
|
|
129
127
|
() => ({
|
|
130
128
|
get stage() {
|
|
131
|
-
return
|
|
129
|
+
return stageRef.current;
|
|
132
130
|
},
|
|
133
131
|
get diagramView() {
|
|
134
132
|
return refs.current.diagram;
|
|
135
133
|
},
|
|
136
134
|
get container() {
|
|
137
|
-
return
|
|
135
|
+
return stageRef.current?.container() ?? null;
|
|
138
136
|
},
|
|
139
137
|
resetStageZoom: (_immediate) => {
|
|
140
138
|
refs.current.resetStageZoom(_immediate);
|
|
@@ -147,15 +145,18 @@ export const Diagram = /* @__PURE__ */ forwardRef(
|
|
|
147
145
|
}),
|
|
148
146
|
centerAndFit: () => refs.current.centerAndFit()
|
|
149
147
|
}),
|
|
150
|
-
[refs,
|
|
148
|
+
[refs, stageRef]
|
|
151
149
|
);
|
|
152
150
|
const resetHoveredStates = useResetHoveredStates();
|
|
153
151
|
useUpdateEffect(() => {
|
|
154
152
|
resetHoveredStates();
|
|
155
153
|
}, [id]);
|
|
156
154
|
useUpdateEffect(() => {
|
|
157
|
-
refs.current.centerAndFit(
|
|
158
|
-
}, [id
|
|
155
|
+
refs.current.centerAndFit(200);
|
|
156
|
+
}, [id]);
|
|
157
|
+
useUpdateEffect(() => {
|
|
158
|
+
refs.current.centerAndFit(50);
|
|
159
|
+
}, [height, width]);
|
|
159
160
|
useEffect(() => {
|
|
160
161
|
if (!zoomable) {
|
|
161
162
|
return;
|
|
@@ -170,39 +171,41 @@ export const Diagram = /* @__PURE__ */ forwardRef(
|
|
|
170
171
|
}, [zoomable]);
|
|
171
172
|
useGesture(
|
|
172
173
|
{
|
|
173
|
-
onDragEnd: () => {
|
|
174
|
-
DiagramGesture.isDragging = false;
|
|
175
|
-
},
|
|
176
174
|
onDrag: (state) => {
|
|
177
175
|
const {
|
|
178
|
-
|
|
176
|
+
first,
|
|
177
|
+
last,
|
|
179
178
|
down,
|
|
180
|
-
cancel,
|
|
181
179
|
intentional,
|
|
182
180
|
offset: [x, y]
|
|
183
181
|
} = state;
|
|
184
|
-
if (
|
|
185
|
-
return
|
|
182
|
+
if (!intentional) {
|
|
183
|
+
return;
|
|
186
184
|
}
|
|
187
|
-
if (
|
|
188
|
-
DiagramGesture.isDragging =
|
|
189
|
-
stageSpringApi.start({
|
|
190
|
-
to: {
|
|
191
|
-
x,
|
|
192
|
-
y
|
|
193
|
-
},
|
|
194
|
-
immediate: immediate || down
|
|
195
|
-
});
|
|
185
|
+
if (first || last) {
|
|
186
|
+
DiagramGesture.isDragging = first && !last;
|
|
196
187
|
}
|
|
188
|
+
stageSpringApi.start({
|
|
189
|
+
to: {
|
|
190
|
+
x,
|
|
191
|
+
y
|
|
192
|
+
},
|
|
193
|
+
delay: 0,
|
|
194
|
+
immediate: immediate || down && !last
|
|
195
|
+
});
|
|
197
196
|
},
|
|
198
197
|
onPinch: ({ memo, first, last, origin: [ox, oy], movement: [ms], offset: [scale] }) => {
|
|
199
198
|
if (first) {
|
|
199
|
+
DiagramGesture.isDragging = true;
|
|
200
200
|
const stage = nonNullable(stageRef.current);
|
|
201
201
|
const { x: x2, y: y2 } = stage.getAbsolutePosition();
|
|
202
202
|
const tx = Math.round(ox - x2);
|
|
203
203
|
const ty = Math.round(oy - y2);
|
|
204
204
|
memo = [stage.x(), stage.y(), tx, ty];
|
|
205
205
|
}
|
|
206
|
+
if (last) {
|
|
207
|
+
DiagramGesture.isDragging = false;
|
|
208
|
+
}
|
|
206
209
|
const x = Math.round(memo[0] - (ms - 1) * memo[2]);
|
|
207
210
|
const y = Math.round(memo[1] - (ms - 1) * memo[3]);
|
|
208
211
|
stageSpringApi.start({
|
|
@@ -211,7 +214,7 @@ export const Diagram = /* @__PURE__ */ forwardRef(
|
|
|
211
214
|
y,
|
|
212
215
|
scale
|
|
213
216
|
},
|
|
214
|
-
|
|
217
|
+
delay: 0
|
|
215
218
|
});
|
|
216
219
|
return memo;
|
|
217
220
|
}
|
|
@@ -222,23 +225,27 @@ export const Diagram = /* @__PURE__ */ forwardRef(
|
|
|
222
225
|
target: containerRef,
|
|
223
226
|
enabled: pannable,
|
|
224
227
|
threshold: 4,
|
|
225
|
-
from: () => [
|
|
226
|
-
pointer:
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
228
|
+
from: () => [stageRef.current?.x() ?? 0, stageRef.current?.y() ?? 0],
|
|
229
|
+
pointer: Object.assign(
|
|
230
|
+
{
|
|
231
|
+
keys: false,
|
|
232
|
+
mouse: true,
|
|
233
|
+
capture: true
|
|
234
|
+
},
|
|
235
|
+
!onNodeContextMenu && !onStageContextMenu && {
|
|
236
|
+
buttons: -1
|
|
237
|
+
}
|
|
238
|
+
)
|
|
232
239
|
},
|
|
233
240
|
pinch: {
|
|
234
241
|
target: containerRef,
|
|
235
242
|
pointer: {
|
|
236
243
|
touch: true
|
|
237
244
|
},
|
|
245
|
+
from: () => [stageRef.current?.scaleX() ?? 1, 0],
|
|
238
246
|
enabled: zoomable,
|
|
239
|
-
// eventOptions
|
|
240
247
|
scaleBounds: { min: minZoom, max: maxZoom + 0.4 },
|
|
241
|
-
rubberband: 0.
|
|
248
|
+
rubberband: 0.045,
|
|
242
249
|
pinchOnWheel: true
|
|
243
250
|
}
|
|
244
251
|
}
|
|
@@ -133,7 +133,7 @@ const NodeShape = memo(
|
|
|
133
133
|
({ animate, node, ctrl, theme, isHovered, expired, onNodeClick }) => {
|
|
134
134
|
const setHoveredNode = useSetHoveredNode();
|
|
135
135
|
const _isCompound = isCompound(node);
|
|
136
|
-
const isNavigatable = !!node.navigateTo && !!onNodeClick;
|
|
136
|
+
const isNavigatable = animate && !!node.navigateTo && !!onNodeClick;
|
|
137
137
|
const Shape = nodeShape(node);
|
|
138
138
|
const springs = ctrl.springs;
|
|
139
139
|
let zoomInIconY;
|
|
@@ -150,18 +150,16 @@ const NodeShape = memo(
|
|
|
150
150
|
{
|
|
151
151
|
name: node.id,
|
|
152
152
|
visible: expired !== true,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
mousePointer(e);
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
onPointerLeave: (e) => {
|
|
161
|
-
setHoveredNode(null);
|
|
162
|
-
mouseDefault(e);
|
|
153
|
+
onPointerEnter: (e) => {
|
|
154
|
+
setHoveredNode(node);
|
|
155
|
+
if (isNavigatable) {
|
|
156
|
+
mousePointer(e);
|
|
163
157
|
}
|
|
164
158
|
},
|
|
159
|
+
onPointerLeave: (e) => {
|
|
160
|
+
setHoveredNode(null);
|
|
161
|
+
mouseDefault(e);
|
|
162
|
+
},
|
|
165
163
|
...onNodeClick && {
|
|
166
164
|
onPointerClick: (e) => {
|
|
167
165
|
if (DiagramGesture.isDragging || e.evt.button !== 0) {
|
|
@@ -20,11 +20,11 @@ function EdgeArrow({
|
|
|
20
20
|
closed: true,
|
|
21
21
|
fill: isOutline ? void 0 : springs.lineColor,
|
|
22
22
|
stroke: springs.lineColor,
|
|
23
|
-
strokeWidth: 1
|
|
24
|
-
hitStrokeWidth: 5,
|
|
23
|
+
strokeWidth: 1,
|
|
25
24
|
lineCap: "round",
|
|
26
25
|
lineJoin: "miter",
|
|
27
26
|
perfectDrawEnabled: false,
|
|
27
|
+
listening: false,
|
|
28
28
|
globalCompositeOperation
|
|
29
29
|
}
|
|
30
30
|
);
|
|
@@ -35,7 +35,7 @@ function EdgeLabelBg({
|
|
|
35
35
|
isHovered,
|
|
36
36
|
springs
|
|
37
37
|
}) {
|
|
38
|
-
const padding =
|
|
38
|
+
const padding = 2;
|
|
39
39
|
const props = useSpring({
|
|
40
40
|
to: {
|
|
41
41
|
x: labelBBox.x - padding,
|
|
@@ -1,27 +1,24 @@
|
|
|
1
|
-
import { useRef, useMemo } from "react";
|
|
2
1
|
import { nonNullable } from "@likec4/core";
|
|
2
|
+
import { useRef, useState } from "react";
|
|
3
3
|
export function useDiagramApi() {
|
|
4
4
|
const ref = useRef(null);
|
|
5
|
-
|
|
6
|
-
() =>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
centerAndFit: () => nonNullable(ref.current, "not mounted, use ref").centerAndFit()
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
[ref]
|
|
5
|
+
const [api] = useState(
|
|
6
|
+
() => ({
|
|
7
|
+
get stage() {
|
|
8
|
+
return ref.current?.stage ?? null;
|
|
9
|
+
},
|
|
10
|
+
get diagramView() {
|
|
11
|
+
return nonNullable(ref.current, "not mounted, use ref").diagramView;
|
|
12
|
+
},
|
|
13
|
+
get container() {
|
|
14
|
+
return ref.current?.container ?? null;
|
|
15
|
+
},
|
|
16
|
+
resetStageZoom: (_immediate) => {
|
|
17
|
+
nonNullable(ref.current, "not mounted, use ref").resetStageZoom(_immediate);
|
|
18
|
+
},
|
|
19
|
+
centerOnNode: (node) => nonNullable(ref.current, "not mounted, use ref").centerOnNode(node),
|
|
20
|
+
centerAndFit: () => nonNullable(ref.current, "not mounted, use ref").centerAndFit()
|
|
21
|
+
})
|
|
26
22
|
);
|
|
23
|
+
return [ref, api];
|
|
27
24
|
}
|
package/dist/__app__/index.html
CHANGED
package/dist/__app__/likec4.css
CHANGED
|
@@ -8,9 +8,28 @@
|
|
|
8
8
|
--font-weight-bold: 600;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
*,
|
|
12
|
+
:before,
|
|
13
|
+
:after {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
outline: none;
|
|
16
|
+
border-width: 0;
|
|
17
|
+
border-style: solid;
|
|
18
|
+
border-color: transparent;
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
html, body {
|
|
12
22
|
margin: 0;
|
|
13
23
|
padding: 0;
|
|
24
|
+
width: 100%;
|
|
25
|
+
height: 100%;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#like4-root {
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: 0;
|
|
31
|
+
width: 100vw;
|
|
32
|
+
height: 100vh;
|
|
14
33
|
}
|
|
15
34
|
|
|
16
35
|
.transparent-bg {
|
package/dist/__app__/src/App.jsx
CHANGED
|
@@ -5,8 +5,10 @@ import { ExportPage, IndexPage, EmbedPage, ViewPage } from './pages';
|
|
|
5
5
|
import { useRoute } from './router';
|
|
6
6
|
import { Theme } from '@radix-ui/themes';
|
|
7
7
|
import { nonexhaustive } from '@likec4/core';
|
|
8
|
+
import { isNil } from 'remeda';
|
|
8
9
|
const Routes = () => {
|
|
9
10
|
const r = useRoute();
|
|
11
|
+
const theme = r.params?.theme;
|
|
10
12
|
let page = null;
|
|
11
13
|
switch (r.route) {
|
|
12
14
|
case 'view': {
|
|
@@ -18,7 +20,7 @@ const Routes = () => {
|
|
|
18
20
|
break;
|
|
19
21
|
}
|
|
20
22
|
case 'embed': {
|
|
21
|
-
page = <EmbedPage key='embed' viewId={r.params.viewId} padding={r.params.padding}
|
|
23
|
+
page = (<EmbedPage key='embed' viewId={r.params.viewId} padding={r.params.padding} transparentBg={isNil(r.params.theme)}/>);
|
|
22
24
|
break;
|
|
23
25
|
}
|
|
24
26
|
case 'index': {
|
|
@@ -28,7 +30,7 @@ const Routes = () => {
|
|
|
28
30
|
default:
|
|
29
31
|
nonexhaustive(r);
|
|
30
32
|
}
|
|
31
|
-
return (<Theme hasBackground={
|
|
33
|
+
return (<Theme hasBackground={!!theme} accentColor='indigo' radius='small' appearance={theme}>
|
|
32
34
|
{page}
|
|
33
35
|
{r.showUI && (<Fragment key='ui'>
|
|
34
36
|
<Sidebar />
|
|
@@ -2,7 +2,7 @@ import { Box, Button, Flex, IconButton, ScrollArea, Separator } from '@radix-ui/
|
|
|
2
2
|
import { useClickOutside, useToggle } from '@react-hookz/web/esm';
|
|
3
3
|
import { HamburgerMenuIcon, ArrowLeftIcon } from '@radix-ui/react-icons';
|
|
4
4
|
import { useRef } from 'react';
|
|
5
|
-
import { cn } from '
|
|
5
|
+
import { cn } from '../../utils';
|
|
6
6
|
import { DiagramsTree } from './DiagramsTree';
|
|
7
7
|
import styles from './styles.module.css';
|
|
8
8
|
import { $pages } from '../../router';
|
|
@@ -25,7 +25,7 @@ export const DisplayModeSelector = () => {
|
|
|
25
25
|
};
|
|
26
26
|
return (<Flex display={{
|
|
27
27
|
initial: 'none',
|
|
28
|
-
|
|
28
|
+
sm: 'flex'
|
|
29
29
|
}} gap='3' align='center'>
|
|
30
30
|
<Button variant={current === first ? 'solid' : 'ghost'} size='1' onClick={changeMode(first)}>
|
|
31
31
|
{Mode[first]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Diagram, useDiagramApi } from '@likec4/diagrams';
|
|
2
2
|
import { Box, Portal } from '@radix-ui/themes';
|
|
3
3
|
import { useDebouncedEffect } from '@react-hookz/web/esm';
|
|
4
|
-
import { memo } from 'react';
|
|
4
|
+
import { memo, useRef } from 'react';
|
|
5
5
|
function downloadBlob(blob, name) {
|
|
6
6
|
// Convert your blob into a Blob URL (a special url that points to an object in the browser's memory)
|
|
7
7
|
const blobUrl = URL.createObjectURL(blob);
|
|
@@ -32,13 +32,19 @@ function downloadBlob(blob, name) {
|
|
|
32
32
|
}
|
|
33
33
|
const ExportDiagram = memo(({ diagram, onCompleted }) => {
|
|
34
34
|
const id = diagram.id;
|
|
35
|
-
const [ref,
|
|
35
|
+
const [ref, api] = useDiagramApi();
|
|
36
36
|
const padding = 20;
|
|
37
37
|
const width = diagram.width + padding * 2;
|
|
38
38
|
const height = diagram.height + padding * 2;
|
|
39
|
+
const onCompletedRef = useRef(onCompleted);
|
|
40
|
+
onCompletedRef.current = onCompleted;
|
|
39
41
|
// To avoid flickering and double rendering
|
|
40
42
|
useDebouncedEffect(() => {
|
|
41
|
-
|
|
43
|
+
const stage = api.stage;
|
|
44
|
+
if (!stage) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
void stage
|
|
42
48
|
.toBlob({
|
|
43
49
|
pixelRatio: 2,
|
|
44
50
|
mimeType: 'image/png',
|
|
@@ -46,17 +52,17 @@ const ExportDiagram = memo(({ diagram, onCompleted }) => {
|
|
|
46
52
|
if (blob) {
|
|
47
53
|
downloadBlob(blob, `${diagram.id}.png`);
|
|
48
54
|
}
|
|
49
|
-
|
|
55
|
+
onCompletedRef.current();
|
|
50
56
|
}
|
|
51
57
|
})
|
|
52
58
|
.catch(err => {
|
|
53
|
-
|
|
59
|
+
onCompletedRef.current();
|
|
54
60
|
// Show error after 100ms to avoid blocking the UI
|
|
55
61
|
setTimeout(() => {
|
|
56
62
|
window.alert(err);
|
|
57
63
|
}, 100);
|
|
58
64
|
});
|
|
59
|
-
}, [id], 400);
|
|
65
|
+
}, [id, api], 400);
|
|
60
66
|
return (<Portal>
|
|
61
67
|
<Box position={'fixed'} style={{
|
|
62
68
|
top: 0,
|
|
@@ -65,7 +71,7 @@ const ExportDiagram = memo(({ diagram, onCompleted }) => {
|
|
|
65
71
|
height,
|
|
66
72
|
transform: `translateY(${-height}px)`
|
|
67
73
|
}}>
|
|
68
|
-
<Diagram ref={ref} animate={false} pannable={false} zoomable={false} diagram={diagram} padding={padding} width={width} height={height}/>
|
|
74
|
+
<Diagram ref={ref} animate={false} pannable={false} zoomable={false} minZoom={1} maxZoom={1} diagram={diagram} padding={padding} width={width} height={height}/>
|
|
69
75
|
</Box>
|
|
70
76
|
</Portal>);
|
|
71
77
|
});
|
|
@@ -34,11 +34,17 @@ const ExportMenu = ({ onExport, children }) => (<DropdownMenu.Root>
|
|
|
34
34
|
</DropdownMenu.Root>);
|
|
35
35
|
export const ViewActionsToolbar = ({ diagram }) => {
|
|
36
36
|
const [exportTo, setExportTo] = useState(null);
|
|
37
|
-
return (<Flex position='fixed' top='0' right='0' p=
|
|
37
|
+
return (<Flex position='fixed' top='0' right='0' p={{
|
|
38
|
+
initial: '3',
|
|
39
|
+
md: '2'
|
|
40
|
+
}} gap={'3'} justify='end' align='center'>
|
|
38
41
|
<DisplayModeSelector />
|
|
39
42
|
<Dialog.Root>
|
|
40
43
|
<Dialog.Trigger>
|
|
41
|
-
<Button variant='solid'
|
|
44
|
+
<Button variant='solid' size={{
|
|
45
|
+
initial: '1',
|
|
46
|
+
md: '2'
|
|
47
|
+
}}>
|
|
42
48
|
<ShareIcon />
|
|
43
49
|
<Text>Share</Text>
|
|
44
50
|
</Button>
|
|
@@ -46,7 +52,10 @@ export const ViewActionsToolbar = ({ diagram }) => {
|
|
|
46
52
|
<ShareDialog diagram={diagram}/>
|
|
47
53
|
</Dialog.Root>
|
|
48
54
|
<ExportMenu onExport={setExportTo}>
|
|
49
|
-
<Button variant='soft' color='gray'
|
|
55
|
+
<Button variant='soft' color='gray' size={{
|
|
56
|
+
initial: '1',
|
|
57
|
+
md: '2'
|
|
58
|
+
}}>
|
|
50
59
|
<Text>Export</Text>
|
|
51
60
|
<CaretDownIcon />
|
|
52
61
|
</Button>
|
|
@@ -58,26 +58,15 @@ export const selectLikeC4ViewAtom = (viewId) => {
|
|
|
58
58
|
if (import.meta.hot) {
|
|
59
59
|
let $updateViews;
|
|
60
60
|
viewsAtom.onMount = set => {
|
|
61
|
-
console.log('mount viewsAtom');
|
|
62
61
|
$updateViews = set;
|
|
63
62
|
return () => {
|
|
64
|
-
console.log('unmount viewsAtom');
|
|
65
63
|
$updateViews = undefined;
|
|
66
64
|
};
|
|
67
65
|
};
|
|
68
66
|
import.meta.hot.accept('/@vite-plugin-likec4/likec4-generated', md => {
|
|
69
67
|
const update = md?.LikeC4Views;
|
|
70
|
-
console.debug('accept ./data update');
|
|
71
|
-
console.dir(update, {
|
|
72
|
-
colors: true,
|
|
73
|
-
compact: false,
|
|
74
|
-
depth: 10
|
|
75
|
-
});
|
|
76
68
|
if (update) {
|
|
77
69
|
$updateViews?.(update);
|
|
78
70
|
}
|
|
79
|
-
else {
|
|
80
|
-
console.warn('no update', md);
|
|
81
|
-
}
|
|
82
71
|
});
|
|
83
72
|
}
|
|
@@ -2,11 +2,13 @@ import { Diagram } from '@likec4/diagrams';
|
|
|
2
2
|
import { useWindowSize } from '@react-hookz/web/esm';
|
|
3
3
|
import { DiagramNotFound } from '../components';
|
|
4
4
|
import { useLikeC4View } from '../data';
|
|
5
|
-
|
|
5
|
+
import { useTransparentBackground } from './useTransparentBackground';
|
|
6
|
+
export function EmbedPage({ viewId, padding, transparentBg = true }) {
|
|
6
7
|
const { width, height } = useWindowSize();
|
|
7
8
|
const diagram = useLikeC4View(viewId);
|
|
9
|
+
useTransparentBackground(transparentBg && !!diagram);
|
|
8
10
|
if (!diagram) {
|
|
9
11
|
return <DiagramNotFound viewId={viewId}/>;
|
|
10
12
|
}
|
|
11
|
-
return <Diagram diagram={diagram} padding={padding} width={width} height={height}
|
|
13
|
+
return (<Diagram animate={false} pannable={false} zoomable={false} diagram={diagram} padding={padding} width={width} height={height}/>);
|
|
12
14
|
}
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
import { Diagram } from '@likec4/diagrams';
|
|
2
2
|
import { useWindowSize } from '@react-hookz/web/esm';
|
|
3
|
-
import { useEffect } from 'react';
|
|
4
3
|
import { DiagramNotFound } from '../components';
|
|
5
4
|
import { useLikeC4View } from '../data';
|
|
5
|
+
import { useTransparentBackground } from './useTransparentBackground';
|
|
6
6
|
export function ExportPage({ viewId, padding }) {
|
|
7
7
|
const { width, height } = useWindowSize();
|
|
8
8
|
const diagram = useLikeC4View(viewId);
|
|
9
|
-
|
|
10
|
-
// We need to add a class to the HTML element
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
const htmlEl = document.body.parentElement;
|
|
13
|
-
if (!htmlEl)
|
|
14
|
-
return;
|
|
15
|
-
// see ../../likec4.css
|
|
16
|
-
const classname = 'transparent-bg';
|
|
17
|
-
htmlEl.classList.add(classname);
|
|
18
|
-
return () => {
|
|
19
|
-
htmlEl.classList.remove(classname);
|
|
20
|
-
};
|
|
21
|
-
}, []);
|
|
9
|
+
useTransparentBackground(!!diagram);
|
|
22
10
|
if (!diagram) {
|
|
23
11
|
return <DiagramNotFound viewId={viewId}/>;
|
|
24
12
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
// To get the transparent background
|
|
3
|
+
// We need to add a class to the HTML element
|
|
4
|
+
export function useTransparentBackground(enabled = true) {
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const htmlEl = document.body.parentElement;
|
|
7
|
+
if (!htmlEl || !enabled)
|
|
8
|
+
return;
|
|
9
|
+
// see ../../likec4.css
|
|
10
|
+
const classname = 'transparent-bg';
|
|
11
|
+
htmlEl.classList.add(classname);
|
|
12
|
+
return () => {
|
|
13
|
+
htmlEl.classList.remove(classname);
|
|
14
|
+
};
|
|
15
|
+
}, [enabled]);
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.diagramBg {
|
|
2
|
+
overscroll-behavior: none;
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
--diagram-bg-size: 24px;
|
|
5
|
+
--diagram-bg-position-x: 0;
|
|
6
|
+
--diagram-bg-position-y: 0;
|
|
7
|
+
|
|
8
|
+
:global(.konvajs-content) {
|
|
9
|
+
|
|
10
|
+
&::before {
|
|
11
|
+
content: '';
|
|
12
|
+
position: absolute;
|
|
13
|
+
padding: 0;
|
|
14
|
+
margin: 0;
|
|
15
|
+
top: 0;
|
|
16
|
+
left: 0;
|
|
17
|
+
width: 100%;
|
|
18
|
+
height: 100%;
|
|
19
|
+
pointer-events: none;
|
|
20
|
+
touch-action: none;
|
|
21
|
+
user-select: none;
|
|
22
|
+
background-origin: border-box;
|
|
23
|
+
background-attachment: fixed;
|
|
24
|
+
background-image: radial-gradient(var(--gray-a3) 12%, transparent 12%);
|
|
25
|
+
background-position: var(--diagram-bg-position-x) var(--diagram-bg-position-y);
|
|
26
|
+
background-size: var(--diagram-bg-size) var(--diagram-bg-size);
|
|
27
|
+
z-index: -1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|