@likec4/diagram 1.2.1 → 1.3.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/LikeC4Diagram.d.ts +1 -1
- package/dist/LikeC4Diagram.props.d.ts +21 -3
- package/dist/index.mjs +1335 -1240
- package/dist/state/DiagramContext.d.ts +2263 -175
- package/dist/state/diagramStore.d.ts +2272 -180
- package/dist/state/useDiagramStore.d.ts +2409 -321
- package/dist/style.css +123 -92
- package/dist/theme.css.d.ts +0 -1
- package/dist/ui/OptionsPanel.d.ts +3 -1
- package/dist/xyflow/XYFlow.d.ts +2 -1
- package/dist/xyflow/XYFlowInner.d.ts +1 -1
- package/dist/xyflow/const.d.ts +1 -1
- package/dist/xyflow/diagram-to-xyflow.d.ts +1 -1
- package/dist/xyflow/edges/RelationshipEdge.d.ts +3 -4
- package/dist/xyflow/edges/utils.d.ts +4 -2
- package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +3 -1
- package/dist/xyflow/nodes/element/ElementNode.d.ts +1 -1
- package/dist/xyflow/nodes/element/index.d.ts +1 -1
- package/dist/xyflow/types.d.ts +2 -2
- package/dist/xyflow/utils.d.ts +3 -0
- package/package.json +15 -14
- package/src/LikeC4Diagram.css.ts +4 -4
- package/src/LikeC4Diagram.props.ts +23 -2
- package/src/LikeC4Diagram.tsx +79 -31
- package/src/globals.css.ts +0 -1
- package/src/mantine/theme.ts +1 -0
- package/src/state/DiagramContext.tsx +0 -1
- package/src/state/diagramStore.ts +177 -100
- package/src/theme.css.ts +0 -1
- package/src/ui/DiagramTitlePanel.tsx +3 -3
- package/src/ui/DynamicViewWalkthrough.css.ts +3 -2
- package/src/ui/DynamicViewWalkthrough.tsx +36 -17
- package/src/ui/OptionsPanel.tsx +12 -12
- package/src/ui/options/NodeOptions.tsx +16 -19
- package/src/xyflow/FitviewOnDiagramChange.tsx +15 -23
- package/src/xyflow/SelectEdgesOnNodeFocus.tsx +3 -6
- package/src/xyflow/SyncWithDiagram.tsx +21 -2
- package/src/xyflow/XYFlow.tsx +20 -23
- package/src/xyflow/XYFlowEvents.tsx +26 -8
- package/src/xyflow/XYFlowInner.tsx +5 -4
- package/src/xyflow/const.ts +1 -1
- package/src/xyflow/diagram-to-xyflow.ts +5 -31
- package/src/xyflow/edges/RelationshipEdge.tsx +158 -112
- package/src/xyflow/edges/edges.css.ts +32 -21
- package/src/xyflow/edges/utils.ts +5 -2
- package/src/xyflow/hooks/useLayoutConstraints.ts +17 -3
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +40 -32
- package/src/xyflow/nodes/compound/CompoundNode.tsx +36 -26
- package/src/xyflow/nodes/element/ElementNode.tsx +20 -6
- package/src/xyflow/nodes/element/element.css.ts +15 -15
- package/src/xyflow/nodes/element/index.ts +1 -1
- package/src/xyflow/nodes/shared/NavigateToBtn.tsx +7 -3
- package/src/xyflow/types.ts +5 -2
- package/src/xyflow/utils.ts +48 -0
- package/dist/ui/index.d.ts +0 -2
- package/src/ui/index.ts +0 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { rem } from '@mantine/core'
|
|
2
|
-
import { createVar, fallbackVar,
|
|
2
|
+
import { createVar, fallbackVar, globalStyle, keyframes, style } from '@vanilla-extract/css'
|
|
3
|
+
import { calc } from '@vanilla-extract/css-utils'
|
|
3
4
|
import { mantine } from '../../../mantine.css'
|
|
4
5
|
import { vars } from '../../../theme.css'
|
|
5
6
|
|
|
@@ -11,12 +12,18 @@ export const container = style({
|
|
|
11
12
|
margin: 0
|
|
12
13
|
})
|
|
13
14
|
|
|
14
|
-
export const dimmed = style({
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
export const dimmed = style({})
|
|
16
|
+
|
|
17
|
+
globalStyle(`.react-flow__node-compound:has(${dimmed})`, {
|
|
18
|
+
opacity: 0.25,
|
|
19
|
+
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
20
|
+
transitionDelay: '200ms',
|
|
21
|
+
filter: 'grayscale(0.85) blur(1px)',
|
|
22
|
+
willChange: 'opacity, filter'
|
|
17
23
|
})
|
|
18
24
|
|
|
19
|
-
export const
|
|
25
|
+
export const varOpacity = createVar('opacity')
|
|
26
|
+
export const varBorderTransparency = createVar('border-transparency')
|
|
20
27
|
|
|
21
28
|
const outlineColor = fallbackVar(
|
|
22
29
|
mantine.colors.primaryColors.outline,
|
|
@@ -31,7 +38,6 @@ export const compoundBody = style({
|
|
|
31
38
|
borderRadius: 6,
|
|
32
39
|
boxShadow: '0 4px 10px 0.5px rgba(0,0,0,0.1) , 0 2px 2px -1px rgba(0,0,0,0.4)',
|
|
33
40
|
padding: 8,
|
|
34
|
-
background: vars.element.fill,
|
|
35
41
|
transition: 'all 200ms ease-out',
|
|
36
42
|
selectors: {
|
|
37
43
|
[`:where(.react-flow__node.selected) &`]: {
|
|
@@ -42,39 +48,47 @@ export const compoundBody = style({
|
|
|
42
48
|
outline: `3px solid ${outlineColor}`,
|
|
43
49
|
outlineOffset: rem(1.5)
|
|
44
50
|
}
|
|
51
|
+
},
|
|
52
|
+
':before': {
|
|
53
|
+
content: '" "',
|
|
54
|
+
position: 'absolute',
|
|
55
|
+
top: 0,
|
|
56
|
+
left: 0,
|
|
57
|
+
width: '100%',
|
|
58
|
+
height: '100%',
|
|
59
|
+
borderRadius: 6,
|
|
60
|
+
pointerEvents: 'none',
|
|
61
|
+
transition: 'background 175ms ease-out, opacity 175ms ease-out',
|
|
62
|
+
background: vars.element.fill,
|
|
63
|
+
backgroundClip: 'padding-box'
|
|
45
64
|
}
|
|
46
65
|
})
|
|
47
66
|
|
|
48
|
-
const
|
|
49
|
-
const bgTransparencyDelta = createVar('bgTransparencyDelta')
|
|
67
|
+
const opacityDeltaOnHover = createVar('opacityDeltaOnHover')
|
|
50
68
|
export const transparent = style({
|
|
51
69
|
borderStyle: 'dashed',
|
|
52
70
|
borderWidth: 3,
|
|
53
71
|
padding: 6,
|
|
54
|
-
|
|
55
|
-
|
|
72
|
+
boxShadow: 'none',
|
|
73
|
+
borderColor: `color-mix(in srgb , ${vars.element.stroke}, transparent ${fallbackVar(varBorderTransparency, '10%')})`,
|
|
56
74
|
backgroundClip: 'padding-box',
|
|
57
|
-
// transitionTimingFunction: 'ease-in',
|
|
58
|
-
// transition: 'all 150ms ease-in-out',
|
|
59
75
|
vars: {
|
|
60
|
-
[
|
|
61
|
-
[bgTransparencyDelta]: '0%',
|
|
76
|
+
[opacityDeltaOnHover]: '0',
|
|
62
77
|
'--ai-bg': `color-mix(in srgb , ${vars.element.fill}, transparent 99%)`
|
|
63
78
|
},
|
|
79
|
+
':before': {
|
|
80
|
+
transitionDelay: '200ms',
|
|
81
|
+
opacity: calc.add(fallbackVar(varOpacity, '1'), opacityDeltaOnHover)
|
|
82
|
+
},
|
|
64
83
|
selectors: {
|
|
65
84
|
[`:where(.react-flow__node.selected) &`]: {
|
|
66
|
-
transitionDelay: '50ms',
|
|
67
|
-
// borderColor: 'transparent',
|
|
68
|
-
// transitionTimingFunction: 'ease-out',
|
|
69
85
|
vars: {
|
|
70
|
-
[
|
|
86
|
+
[opacityDeltaOnHover]: '.05'
|
|
71
87
|
}
|
|
72
88
|
},
|
|
73
89
|
[`:where([data-hovered]) &`]: {
|
|
74
|
-
transitionDelay: '175ms',
|
|
75
|
-
// transitionTimingFunction: 'ease-out',
|
|
76
90
|
vars: {
|
|
77
|
-
[
|
|
91
|
+
[opacityDeltaOnHover]: '.1'
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
94
|
}
|
|
@@ -93,15 +107,10 @@ export const title = style({
|
|
|
93
107
|
lineHeight: 1,
|
|
94
108
|
opacity: 0.75,
|
|
95
109
|
color: `var(--_compound-title-color,${vars.compound.titleColor})`,
|
|
96
|
-
paddingLeft: 12
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
// [`:where([data-mantine-color-scheme='light']) &`]: {
|
|
102
|
-
// opacity: 1
|
|
103
|
-
// }
|
|
104
|
-
}
|
|
110
|
+
paddingLeft: 12
|
|
111
|
+
})
|
|
112
|
+
export const titleWithNavigation = style({
|
|
113
|
+
paddingLeft: 26
|
|
105
114
|
})
|
|
106
115
|
|
|
107
116
|
globalStyle(`:where([data-mantine-color-scheme='light'] .likec4-compound-transparent)`, {
|
|
@@ -114,8 +123,7 @@ globalStyle(`:where([data-mantine-color-scheme='light'] .likec4-compound-transpa
|
|
|
114
123
|
opacity: 1
|
|
115
124
|
})
|
|
116
125
|
|
|
117
|
-
const indicatorKeyframes =
|
|
118
|
-
globalKeyframes(indicatorKeyframes, {
|
|
126
|
+
const indicatorKeyframes = keyframes({
|
|
119
127
|
'from': {
|
|
120
128
|
opacity: 0.6
|
|
121
129
|
},
|
|
@@ -2,13 +2,14 @@ import { Box, Text } from '@mantine/core'
|
|
|
2
2
|
import { assignInlineVars } from '@vanilla-extract/dynamic'
|
|
3
3
|
import { Handle, type NodeProps, Position } from '@xyflow/react'
|
|
4
4
|
import clsx from 'clsx'
|
|
5
|
+
import { DEV } from 'esm-env'
|
|
5
6
|
import { deepEqual as eq } from 'fast-equals'
|
|
6
7
|
import { memo } from 'react'
|
|
7
8
|
import { clamp } from 'remeda'
|
|
9
|
+
import useTilg from 'tilg'
|
|
8
10
|
import { useDiagramState } from '../../../state'
|
|
9
11
|
import type { CompoundXYFlowNode } from '../../types'
|
|
10
12
|
import { NavigateToBtn } from '../shared/NavigateToBtn'
|
|
11
|
-
import { compoundBody, container, indicator, navigateBtn, title } from './CompoundNode.css'
|
|
12
13
|
import * as css from './CompoundNode.css'
|
|
13
14
|
|
|
14
15
|
type CompoundNodeProps = Pick<
|
|
@@ -16,13 +17,12 @@ type CompoundNodeProps = Pick<
|
|
|
16
17
|
'id' | 'data' | 'width' | 'height' | 'selected'
|
|
17
18
|
>
|
|
18
19
|
|
|
19
|
-
// @ts-ignore
|
|
20
20
|
const isEqualProps = (prev: CompoundNodeProps, next: CompoundNodeProps) => (
|
|
21
21
|
prev.id === next.id
|
|
22
|
-
&& prev.selected === next.selected
|
|
23
|
-
&& prev.width
|
|
24
|
-
&& prev.height
|
|
25
|
-
&& eq(prev.data, next.data)
|
|
22
|
+
// && prev.selected === next.selected
|
|
23
|
+
// && eq(prev.width, next.width)
|
|
24
|
+
// && eq(prev.height, next.height)
|
|
25
|
+
&& eq(prev.data.element, next.data.element)
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>(function CompoundNode({
|
|
@@ -31,11 +31,18 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>(function
|
|
|
31
31
|
element
|
|
32
32
|
}
|
|
33
33
|
}) {
|
|
34
|
-
|
|
34
|
+
DEV && useTilg()
|
|
35
35
|
const { color, style, depth = 1, ...compound } = element
|
|
36
36
|
// const w = toDomPrecision(width ?? compound.width)
|
|
37
37
|
// const h = toDomPrecision(height ?? compound.height)
|
|
38
|
-
const opacity = style.opacity ?? 100
|
|
38
|
+
const opacity = clamp((style.opacity ?? 100) / 100, {
|
|
39
|
+
min: 0,
|
|
40
|
+
max: 1
|
|
41
|
+
})
|
|
42
|
+
const borderTransparency = clamp(50 - opacity * 50, {
|
|
43
|
+
min: 0,
|
|
44
|
+
max: 50
|
|
45
|
+
})
|
|
39
46
|
|
|
40
47
|
const { isHovered, isDimmed, hasOnNavigateTo } = useDiagramState(s => ({
|
|
41
48
|
isHovered: s.hoveredNodeId === id,
|
|
@@ -44,30 +51,19 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>(function
|
|
|
44
51
|
}))
|
|
45
52
|
const isnavigable = !!compound.navigateTo && hasOnNavigateTo
|
|
46
53
|
|
|
47
|
-
// invert opacity to transparency
|
|
48
|
-
const transparency = clamp(100 - opacity, {
|
|
49
|
-
min: 0,
|
|
50
|
-
max: 99
|
|
51
|
-
})
|
|
52
|
-
|
|
53
54
|
return (
|
|
54
55
|
<Box
|
|
55
56
|
className={clsx(
|
|
56
57
|
css.container,
|
|
57
58
|
'likec4-compound-node',
|
|
58
|
-
opacity <
|
|
59
|
+
opacity < 1 && 'likec4-compound-transparent',
|
|
59
60
|
isDimmed && css.dimmed
|
|
60
61
|
)}
|
|
61
62
|
mod={{
|
|
62
63
|
'compound-depth': depth,
|
|
63
64
|
'likec4-color': color,
|
|
64
|
-
'likec4-shape': compound.shape,
|
|
65
|
-
'likec4-navigable': isnavigable,
|
|
66
65
|
hovered: isHovered
|
|
67
66
|
}}
|
|
68
|
-
style={assignInlineVars({
|
|
69
|
-
[css.varTransparency]: transparency + '%'
|
|
70
|
-
})}
|
|
71
67
|
>
|
|
72
68
|
<Handle
|
|
73
69
|
type="target"
|
|
@@ -84,14 +80,28 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>(function
|
|
|
84
80
|
/>
|
|
85
81
|
</svg>
|
|
86
82
|
<div
|
|
87
|
-
className={clsx(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
className={clsx(
|
|
84
|
+
css.compoundBody,
|
|
85
|
+
opacity < 1 && css.transparent,
|
|
86
|
+
'likec4-compound'
|
|
87
|
+
)}
|
|
88
|
+
style={opacity < 1
|
|
89
|
+
? {
|
|
90
|
+
...assignInlineVars({
|
|
91
|
+
[css.varBorderTransparency]: `${borderTransparency}%`,
|
|
92
|
+
[css.varOpacity]: opacity.toFixed(2)
|
|
93
|
+
}),
|
|
94
|
+
borderStyle: style.border ?? 'dashed'
|
|
95
|
+
}
|
|
96
|
+
: undefined}
|
|
91
97
|
>
|
|
92
98
|
<Text
|
|
93
99
|
component="div"
|
|
94
|
-
className={clsx(
|
|
100
|
+
className={clsx(
|
|
101
|
+
css.title,
|
|
102
|
+
isnavigable && css.titleWithNavigation,
|
|
103
|
+
'likec4-compound-title'
|
|
104
|
+
)}>
|
|
95
105
|
{compound.title}
|
|
96
106
|
</Text>
|
|
97
107
|
</div>
|
|
@@ -103,4 +113,4 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>(function
|
|
|
103
113
|
/>
|
|
104
114
|
</Box>
|
|
105
115
|
)
|
|
106
|
-
})
|
|
116
|
+
}, isEqualProps)
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { Text } from '@mantine/core'
|
|
2
2
|
import { Handle, type NodeProps, Position } from '@xyflow/react'
|
|
3
3
|
import clsx from 'clsx'
|
|
4
|
-
import {
|
|
4
|
+
import { DEV } from 'esm-env'
|
|
5
|
+
import { deepEqual as eq } from 'fast-equals'
|
|
6
|
+
import { m, type Variants } from 'framer-motion'
|
|
7
|
+
import { memo } from 'react'
|
|
5
8
|
import { isNumber } from 'remeda'
|
|
9
|
+
import useTilg from 'tilg'
|
|
6
10
|
import { useDiagramState } from '../../../state'
|
|
7
11
|
import type { ElementXYFlowNode } from '../../types'
|
|
8
12
|
import { toDomPrecision } from '../../utils'
|
|
@@ -64,7 +68,16 @@ const variants = {
|
|
|
64
68
|
// }
|
|
65
69
|
} satisfies Variants
|
|
66
70
|
|
|
67
|
-
|
|
71
|
+
const isEqualProps = (prev: ElementNodeProps, next: ElementNodeProps) => (
|
|
72
|
+
prev.id === next.id
|
|
73
|
+
&& eq(prev.selected ?? false, next.selected ?? false)
|
|
74
|
+
&& eq(prev.dragging ?? false, next.dragging ?? false)
|
|
75
|
+
&& eq(prev.width ?? 0, next.width ?? 0)
|
|
76
|
+
&& eq(prev.height ?? 0, next.height ?? 0)
|
|
77
|
+
&& eq(prev.data.element, next.data.element)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
68
81
|
id,
|
|
69
82
|
data: {
|
|
70
83
|
element
|
|
@@ -73,7 +86,8 @@ export function ElementNode({
|
|
|
73
86
|
selected = false,
|
|
74
87
|
width,
|
|
75
88
|
height
|
|
76
|
-
}
|
|
89
|
+
}) {
|
|
90
|
+
DEV && useTilg()
|
|
77
91
|
const { isHovered, isDimmed, hasOnNavigateTo, isHovercards, isInteractive } = useDiagramState(s => ({
|
|
78
92
|
isHovered: s.hoveredNodeId === id,
|
|
79
93
|
isDimmed: s.dimmed.has(id),
|
|
@@ -109,7 +123,7 @@ export function ElementNode({
|
|
|
109
123
|
// useTilg(animate, isHovered, isInteractive)
|
|
110
124
|
|
|
111
125
|
return (
|
|
112
|
-
<
|
|
126
|
+
<m.div
|
|
113
127
|
id={id}
|
|
114
128
|
className={clsx([
|
|
115
129
|
css.container,
|
|
@@ -225,6 +239,6 @@ export function ElementNode({
|
|
|
225
239
|
}
|
|
226
240
|
{isHovercards && element.links && <ElementLink element={element} />}
|
|
227
241
|
{isNavigable && <NavigateToBtn xynodeId={id} className={css.cssNavigateBtn} />}
|
|
228
|
-
</
|
|
242
|
+
</m.div>
|
|
229
243
|
)
|
|
230
|
-
}
|
|
244
|
+
}, isEqualProps)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { rem } from '@mantine/core'
|
|
2
|
-
import { createVar, fallbackVar,
|
|
2
|
+
import { createVar, fallbackVar, globalStyle, keyframes, style } from '@vanilla-extract/css'
|
|
3
3
|
import { mantine } from '../../../mantine.css'
|
|
4
4
|
import { vars } from '../../../theme.css'
|
|
5
5
|
|
|
@@ -8,12 +8,12 @@ const elPadding = createVar('el-padding')
|
|
|
8
8
|
// assignVars
|
|
9
9
|
|
|
10
10
|
export const container = style({
|
|
11
|
-
position: 'relative',
|
|
12
11
|
width: '100%',
|
|
13
12
|
height: '100%',
|
|
14
13
|
padding: 0,
|
|
15
14
|
margin: 0,
|
|
16
|
-
|
|
15
|
+
display: 'flex',
|
|
16
|
+
alignItems: 'stretch',
|
|
17
17
|
vars: {
|
|
18
18
|
[stokeFillMix]: `color-mix(in srgb, ${vars.element.stroke} 90%, ${vars.element.fill})`
|
|
19
19
|
}
|
|
@@ -21,13 +21,15 @@ export const container = style({
|
|
|
21
21
|
|
|
22
22
|
export const dimmed = style({})
|
|
23
23
|
|
|
24
|
-
globalStyle(`.react-flow__node:has(${dimmed})`, {
|
|
25
|
-
|
|
26
|
-
transition: 'filter
|
|
24
|
+
globalStyle(`.react-flow__node-element:has(${dimmed})`, {
|
|
25
|
+
opacity: 0.25,
|
|
26
|
+
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
27
|
+
transitionDelay: '200ms',
|
|
28
|
+
filter: 'grayscale(0.85) blur(2px)',
|
|
29
|
+
willChange: 'opacity, filter'
|
|
27
30
|
})
|
|
28
31
|
|
|
29
|
-
const indicatorKeyframes =
|
|
30
|
-
globalKeyframes(indicatorKeyframes, {
|
|
32
|
+
const indicatorKeyframes = keyframes({
|
|
31
33
|
'from': {
|
|
32
34
|
opacity: 0.6
|
|
33
35
|
},
|
|
@@ -63,6 +65,9 @@ export const indicator = style({
|
|
|
63
65
|
},
|
|
64
66
|
':where([data-likec4-shape="queue"], [data-likec4-shape="cylinder"], [data-likec4-shape="storage"]) &': {
|
|
65
67
|
strokeWidth: 10
|
|
68
|
+
},
|
|
69
|
+
[`${dimmed} &`]: {
|
|
70
|
+
visibility: 'hidden'
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
})
|
|
@@ -128,9 +133,7 @@ export const technology = style({
|
|
|
128
133
|
})
|
|
129
134
|
|
|
130
135
|
export const element = style({
|
|
131
|
-
|
|
132
|
-
width: '100%',
|
|
133
|
-
height: '100%',
|
|
136
|
+
flex: '1',
|
|
134
137
|
padding: elPadding,
|
|
135
138
|
display: 'flex',
|
|
136
139
|
flexDirection: 'column',
|
|
@@ -165,10 +168,6 @@ export const elementIcon = style({
|
|
|
165
168
|
flex: `0 1 ${iconMaxH}`,
|
|
166
169
|
maxHeight: iconMaxH,
|
|
167
170
|
marginTop: -8,
|
|
168
|
-
// display: 'flex',
|
|
169
|
-
// justifyContent: 'flex-end',
|
|
170
|
-
// flexDirection: 'column',
|
|
171
|
-
// alignItems: 'center',
|
|
172
171
|
position: 'relative',
|
|
173
172
|
userSelect: 'none',
|
|
174
173
|
pointerEvents: 'none',
|
|
@@ -203,6 +202,7 @@ export const cssShapeSvg = style({
|
|
|
203
202
|
filter: fallbackVar(filterShadowHovered, filterShadow),
|
|
204
203
|
transition: 'filter 300ms ease-out',
|
|
205
204
|
transitionDelay: '0ms',
|
|
205
|
+
zIndex: -1,
|
|
206
206
|
vars: {
|
|
207
207
|
[filterShadow]: `
|
|
208
208
|
drop-shadow(0 2px 1px rgba(0, 0, 0, 0.21))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { ElementNode } from './ElementNode'
|
|
1
|
+
export { ElementNodeMemo as ElementNode } from './ElementNode'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ActionIcon } from '@mantine/core'
|
|
2
|
+
import { IconZoomScan } from '@tabler/icons-react'
|
|
2
3
|
import clsx from 'clsx'
|
|
3
|
-
import { ZoomIn } from '../../../icons'
|
|
4
4
|
import { useDiagramStoreApi } from '../../../state'
|
|
5
5
|
|
|
6
6
|
export type NavigateToBtnProps = {
|
|
@@ -13,7 +13,7 @@ export function NavigateToBtn({ xynodeId, className }: NavigateToBtnProps) {
|
|
|
13
13
|
return (
|
|
14
14
|
<ActionIcon
|
|
15
15
|
className={clsx('nodrag nopan', className)}
|
|
16
|
-
radius="
|
|
16
|
+
radius="md"
|
|
17
17
|
onPointerDownCapture={e => e.stopPropagation()}
|
|
18
18
|
onClick={event => {
|
|
19
19
|
event.stopPropagation()
|
|
@@ -21,7 +21,11 @@ export function NavigateToBtn({ xynodeId, className }: NavigateToBtnProps) {
|
|
|
21
21
|
}}
|
|
22
22
|
onDoubleClick={event => event.stopPropagation()}
|
|
23
23
|
>
|
|
24
|
-
<
|
|
24
|
+
<IconZoomScan
|
|
25
|
+
style={{
|
|
26
|
+
width: '75%',
|
|
27
|
+
height: '75%'
|
|
28
|
+
}} />
|
|
25
29
|
</ActionIcon>
|
|
26
30
|
)
|
|
27
31
|
}
|
package/src/xyflow/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BBox, DiagramEdge, DiagramNode, Fqn, Point } from '@likec4/core'
|
|
1
|
+
import type { BBox, DiagramEdge, DiagramNode, Fqn, Point, ViewID, XYPosition } from '@likec4/core'
|
|
2
2
|
import type {
|
|
3
3
|
Edge,
|
|
4
4
|
EdgeMouseHandler,
|
|
@@ -57,7 +57,10 @@ export namespace XYFlowNode {
|
|
|
57
57
|
|
|
58
58
|
export type RelationshipData = {
|
|
59
59
|
edge: DiagramEdge
|
|
60
|
-
|
|
60
|
+
// to replace edges on view change
|
|
61
|
+
// viewId: ViewID
|
|
62
|
+
// if set - edge was changed by user
|
|
63
|
+
controlPoints: XYPosition[] | null
|
|
61
64
|
headPoint: Point | null
|
|
62
65
|
tailPoint: Point | null
|
|
63
66
|
type: 'bezier' | 'poly'
|
package/src/xyflow/utils.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { hasAtLeast, invariant } from '@likec4/core'
|
|
2
|
+
import type { DiagramEdge, NonEmptyArray, Point } from '@likec4/core/types'
|
|
1
3
|
import type { XYPosition } from '@xyflow/react'
|
|
4
|
+
import { Bezier } from 'bezier-js'
|
|
5
|
+
import { isArray } from 'remeda'
|
|
2
6
|
|
|
3
7
|
export function toDomPrecision(v: number | null) {
|
|
4
8
|
if (v === null) {
|
|
@@ -10,3 +14,47 @@ export function toDomPrecision(v: number | null) {
|
|
|
10
14
|
export function distance(a: XYPosition, b: XYPosition) {
|
|
11
15
|
return Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2))
|
|
12
16
|
}
|
|
17
|
+
|
|
18
|
+
export function bezierControlPoints(diagramEdge: DiagramEdge) {
|
|
19
|
+
let [start, ...bezierPoints] = diagramEdge.points
|
|
20
|
+
invariant(start, 'start should be defined')
|
|
21
|
+
const handles = [
|
|
22
|
+
// start
|
|
23
|
+
] as XYPosition[]
|
|
24
|
+
|
|
25
|
+
while (hasAtLeast(bezierPoints, 3)) {
|
|
26
|
+
const [cp1, cp2, end, ...rest] = bezierPoints
|
|
27
|
+
const bezier = new Bezier(start[0], start[1], cp1[0], cp1[1], cp2[0], cp2[1], end[0], end[1])
|
|
28
|
+
// TODO: temporary, we need correcly derive catmull-rom from bezier. Actually, from poly-bezier
|
|
29
|
+
const inflections = bezier.inflections()
|
|
30
|
+
if (inflections.length === 0) {
|
|
31
|
+
inflections.push(0.5)
|
|
32
|
+
}
|
|
33
|
+
inflections.forEach(t => {
|
|
34
|
+
const { x, y } = bezier.get(t)
|
|
35
|
+
handles.push({
|
|
36
|
+
x: toDomPrecision(x),
|
|
37
|
+
y: toDomPrecision(y)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
bezierPoints = rest
|
|
41
|
+
start = end
|
|
42
|
+
}
|
|
43
|
+
invariant(bezierPoints.length === 0, 'all points should be consumed')
|
|
44
|
+
if (diagramEdge.dir === 'back' && handles.length > 0) {
|
|
45
|
+
handles.reverse()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return handles
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// If points are within 3px, consider them the same
|
|
52
|
+
const isClose = (a: number, b: number) => {
|
|
53
|
+
return Math.abs(a - b) < 3.1
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isSamePoint(a: XYPosition | Point, b: XYPosition | Point) {
|
|
57
|
+
const [ax, ay] = isArray(a) ? a : [a.x, a.y]
|
|
58
|
+
const [bx, by] = isArray(b) ? b : [b.x, b.y]
|
|
59
|
+
return isClose(ax, bx) && isClose(ay, by)
|
|
60
|
+
}
|
package/dist/ui/index.d.ts
DELETED
package/src/ui/index.ts
DELETED