@likec4/diagram 1.3.0 → 1.4.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 +6 -1
- package/dist/index.mjs +270 -168
- package/dist/state/DiagramContext.d.ts +155 -154
- package/dist/state/diagramStore.d.ts +154 -154
- package/dist/state/index.d.ts +0 -1
- package/dist/state/useDiagramStore.d.ts +3 -4622
- package/dist/state/useNavigationHistory.d.ts +5 -0
- package/dist/style.css +86 -54
- package/dist/theme.css.d.ts +5 -0
- package/dist/ui/BackwardForwardPanel.css.d.ts +1 -0
- package/dist/ui/BackwardForwardPanel.d.ts +1 -0
- package/dist/ui/DiagramTitlePanel.css.d.ts +1 -1
- package/dist/ui/OptionsPanel.d.ts +1 -3
- package/dist/xyflow/XYFlowEvents.d.ts +4 -4
- package/dist/xyflow/XYFlowInner.d.ts +1 -0
- package/dist/xyflow/edges/RelationshipEdge.d.ts +1 -1
- package/dist/xyflow/hooks/useXYFlow.d.ts +9 -9
- package/dist/xyflow/nodes/element/element.css.d.ts +1 -0
- package/dist/xyflow/types.d.ts +1 -1
- package/package.json +10 -10
- package/src/LikeC4Diagram.css.ts +11 -1
- package/src/LikeC4Diagram.props.ts +10 -3
- package/src/LikeC4Diagram.tsx +30 -7
- package/src/globals.css.ts +1 -1
- package/src/state/DiagramContext.tsx +3 -3
- package/src/state/diagramStore.ts +27 -30
- package/src/state/index.ts +0 -1
- package/src/state/useDiagramStore.ts +6 -13
- package/src/state/useNavigationHistory.ts +39 -0
- package/src/theme.css.ts +5 -0
- package/src/ui/BackwardForwardPanel.css.ts +22 -0
- package/src/ui/BackwardForwardPanel.tsx +59 -0
- package/src/ui/DiagramTitlePanel.css.ts +11 -5
- package/src/ui/DiagramTitlePanel.tsx +105 -93
- package/src/ui/OptionsPanel.tsx +28 -19
- package/src/xyflow/XYFlowInner.tsx +5 -2
- package/src/xyflow/diagram-to-xyflow.ts +2 -13
- package/src/xyflow/edges/RelationshipEdge.tsx +11 -8
- package/src/xyflow/edges/edges.css.ts +14 -10
- package/src/xyflow/hooks/useLayoutConstraints.ts +2 -1
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +8 -8
- package/src/xyflow/nodes/element/ElementNode.tsx +7 -8
- package/src/xyflow/nodes/element/element.css.ts +18 -5
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Anchor, Box, Button, CopyButton, Group,
|
|
1
|
+
import { Anchor, Box, Button, Card, CardSection, CopyButton, Group, Spoiler, Stack, Text } from '@mantine/core'
|
|
2
2
|
import { useLocalStorage } from '@mantine/hooks'
|
|
3
|
+
import { IconMenu } from '@tabler/icons-react'
|
|
3
4
|
import clsx from 'clsx'
|
|
4
5
|
import { AnimatePresence, m } from 'framer-motion'
|
|
5
6
|
import { type DiagramState, useDiagramState } from '../state'
|
|
@@ -35,103 +36,114 @@ export default function DiagramTitlePanel() {
|
|
|
35
36
|
transformOrigin: 'left center'
|
|
36
37
|
}}
|
|
37
38
|
>
|
|
38
|
-
<
|
|
39
|
+
<Card
|
|
39
40
|
radius="sm"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
p={isCollapsed ? 'xs' : 'lg'}
|
|
41
|
+
className={css.card}
|
|
42
|
+
p={'md'}
|
|
43
43
|
onDoubleClick={e => e.stopPropagation()}>
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
44
|
+
<CardSection mb={4}>
|
|
45
|
+
<Button
|
|
46
|
+
fullWidth
|
|
47
|
+
size="xs"
|
|
48
|
+
h={'sm'}
|
|
49
|
+
py={2}
|
|
50
|
+
radius={'0'}
|
|
51
|
+
variant="subtle"
|
|
52
|
+
color="gray"
|
|
53
|
+
onClick={toggle}
|
|
54
|
+
>
|
|
55
|
+
<IconMenu size={11} opacity={0.7} />
|
|
56
|
+
</Button>
|
|
57
|
+
</CardSection>
|
|
58
|
+
<Group justify="stretch" wrap="nowrap" mb={isCollapsed ? 0 : 'sm'}>
|
|
59
|
+
<Text
|
|
60
|
+
component={'div'}
|
|
61
|
+
flex={'1'}
|
|
62
|
+
size={isCollapsed ? 'sm' : 'lg'}
|
|
63
|
+
fw={500}
|
|
64
|
+
lh={1.1}
|
|
65
|
+
className={css.title}
|
|
66
|
+
>
|
|
67
|
+
{title}
|
|
68
|
+
</Text>
|
|
69
|
+
<Text
|
|
70
|
+
hidden={isCollapsed}
|
|
71
|
+
component={'div'}
|
|
72
|
+
flex={'0 0 auto'}
|
|
73
|
+
inline
|
|
74
|
+
size="xs"
|
|
75
|
+
fz={9}
|
|
76
|
+
fw={500}
|
|
77
|
+
c={'dimmed'}
|
|
78
|
+
style={{
|
|
79
|
+
userSelect: 'all'
|
|
80
|
+
}}>
|
|
81
|
+
<span style={{ userSelect: 'none' }}>id:{' '}</span>
|
|
82
|
+
{id}
|
|
83
|
+
</Text>
|
|
84
|
+
</Group>
|
|
85
|
+
{!isCollapsed && (
|
|
86
|
+
<>
|
|
87
|
+
{description && (
|
|
88
|
+
<Spoiler
|
|
89
|
+
maxHeight={42}
|
|
90
|
+
showLabel={<Button color="gray" variant="light" fz={'10'} size="compact-xs">show more</Button>}
|
|
91
|
+
hideLabel={<Button color="gray" variant="light" fz={'10'} size="compact-xs">hide</Button>}>
|
|
88
92
|
<Text
|
|
89
93
|
component={'div'}
|
|
90
|
-
size="
|
|
91
|
-
|
|
92
|
-
no description
|
|
94
|
+
size="sm"
|
|
95
|
+
className={css.description}>
|
|
96
|
+
{description || 'no description'}
|
|
93
97
|
</Text>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
98
|
+
</Spoiler>
|
|
99
|
+
)}
|
|
100
|
+
{!description && (
|
|
101
|
+
<Text
|
|
102
|
+
component={'div'}
|
|
103
|
+
size="xs"
|
|
104
|
+
c={'dimmed'}>
|
|
105
|
+
no description
|
|
106
|
+
</Text>
|
|
107
|
+
)}
|
|
108
|
+
{links && (
|
|
109
|
+
<Stack
|
|
110
|
+
gap={3}
|
|
111
|
+
justify="stretch"
|
|
112
|
+
align="stretch">
|
|
113
|
+
{links.map((link) => (
|
|
114
|
+
<Group key={link} wrap="nowrap" align="center" gap={'sm'}>
|
|
115
|
+
<Box flex={'1'} style={{ overflow: 'hidden' }}>
|
|
116
|
+
<Anchor
|
|
117
|
+
href={link}
|
|
118
|
+
target="_blank"
|
|
119
|
+
fz="xs"
|
|
120
|
+
truncate="end"
|
|
121
|
+
display={'inline-block'}
|
|
122
|
+
w={'100%'}>
|
|
123
|
+
{link}
|
|
124
|
+
</Anchor>
|
|
125
|
+
</Box>
|
|
126
|
+
<CopyButton value={link}>
|
|
127
|
+
{({ copied, copy }) => (
|
|
128
|
+
<Button
|
|
129
|
+
size="compact-xs"
|
|
130
|
+
fz={'10'}
|
|
131
|
+
variant="light"
|
|
132
|
+
onClick={copy}
|
|
133
|
+
color={copied
|
|
134
|
+
? 'teal'
|
|
135
|
+
: 'gray'}>
|
|
136
|
+
{copied ? 'copied' : 'copy'}
|
|
137
|
+
</Button>
|
|
138
|
+
)}
|
|
139
|
+
</CopyButton>
|
|
140
|
+
</Group>
|
|
141
|
+
))}
|
|
142
|
+
</Stack>
|
|
143
|
+
)}
|
|
144
|
+
</>
|
|
145
|
+
)}
|
|
146
|
+
</Card>
|
|
135
147
|
</m.div>
|
|
136
148
|
</AnimatePresence>
|
|
137
149
|
)
|
package/src/ui/OptionsPanel.tsx
CHANGED
|
@@ -1,29 +1,40 @@
|
|
|
1
1
|
import { Card } from '@mantine/core'
|
|
2
|
-
import { useOnSelectionChange } from '@xyflow/react'
|
|
3
2
|
import clsx from 'clsx'
|
|
4
|
-
import {
|
|
3
|
+
import { DEV } from 'esm-env'
|
|
4
|
+
import { shallowEqual } from 'fast-equals'
|
|
5
5
|
import { AnimatePresence, m } from 'framer-motion'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { useCallback } from 'react'
|
|
7
|
+
import { reduce } from 'remeda'
|
|
8
|
+
import useTilg from 'tilg'
|
|
8
9
|
import { useDiagramState } from '../state'
|
|
10
|
+
import { useXYStore } from '../xyflow/hooks'
|
|
9
11
|
import { NodeOptions } from './options/NodeOptions'
|
|
10
12
|
import * as styles from './OptionsPanel.css'
|
|
11
13
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
const Empty = [] as string[]
|
|
15
|
+
export default function OptionsPanel() {
|
|
16
|
+
const { isFocusDisabled, viewId } = useDiagramState(s => ({
|
|
17
|
+
viewId: s.view.id,
|
|
18
|
+
isFocusDisabled: s.focusedNodeId === null && s.activeDynamicViewStep === null
|
|
19
|
+
}))
|
|
20
|
+
const selectedNodes = useXYStore(
|
|
21
|
+
useCallback(s =>
|
|
22
|
+
s.elementsSelectable && isFocusDisabled
|
|
23
|
+
? reduce(s.nodes, (acc, n) => {
|
|
24
|
+
if (n.selected) {
|
|
25
|
+
acc.push(n.id)
|
|
26
|
+
}
|
|
27
|
+
return acc
|
|
28
|
+
}, [] as string[])
|
|
29
|
+
: Empty, [isFocusDisabled]),
|
|
30
|
+
shallowEqual
|
|
31
|
+
)
|
|
32
|
+
DEV && useTilg()
|
|
23
33
|
return (
|
|
24
34
|
<AnimatePresence mode="wait">
|
|
25
|
-
{
|
|
35
|
+
{selectedNodes.length > 0 && (
|
|
26
36
|
<m.div
|
|
37
|
+
key={viewId}
|
|
27
38
|
initial={{ opacity: 0, scale: 0.85 }}
|
|
28
39
|
animate={{ opacity: 1, scale: 1 }}
|
|
29
40
|
exit={{
|
|
@@ -43,6 +54,4 @@ const OptionsPanelMemo = memo(function OptionsPanel() {
|
|
|
43
54
|
)}
|
|
44
55
|
</AnimatePresence>
|
|
45
56
|
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export default OptionsPanelMemo
|
|
57
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { isDynamicView } from '@likec4/core'
|
|
2
1
|
import { Controls } from '@xyflow/react'
|
|
3
2
|
import { shallowEqual } from 'fast-equals'
|
|
4
3
|
import { memo, type PropsWithChildren } from 'react'
|
|
5
|
-
import { isNonNull
|
|
4
|
+
import { isNonNull } from 'remeda'
|
|
6
5
|
import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
|
|
7
6
|
import { useDiagramState } from '../state/useDiagramStore'
|
|
7
|
+
import { BackwardForwardPanel } from '../ui/BackwardForwardPanel'
|
|
8
8
|
import DiagramTitlePanel from '../ui/DiagramTitlePanel'
|
|
9
9
|
import { DynamicViewWalkthrough } from '../ui/DynamicViewWalkthrough'
|
|
10
10
|
import OptionsPanel from '../ui/OptionsPanel'
|
|
@@ -14,6 +14,7 @@ type XYFlowInnerProps = PropsWithChildren<{
|
|
|
14
14
|
background: NonNullable<LikeC4DiagramProperties['background']>
|
|
15
15
|
controls: boolean
|
|
16
16
|
showDiagramTitle: boolean
|
|
17
|
+
showNavigationButtons: boolean
|
|
17
18
|
enableDynamicViewWalkthrough: boolean
|
|
18
19
|
}>
|
|
19
20
|
|
|
@@ -22,6 +23,7 @@ export const XYFlowInner = memo(function XYFlowInnerR({
|
|
|
22
23
|
background,
|
|
23
24
|
controls,
|
|
24
25
|
showDiagramTitle,
|
|
26
|
+
showNavigationButtons,
|
|
25
27
|
enableDynamicViewWalkthrough
|
|
26
28
|
}: XYFlowInnerProps) {
|
|
27
29
|
const {
|
|
@@ -45,6 +47,7 @@ export const XYFlowInner = memo(function XYFlowInnerR({
|
|
|
45
47
|
{readonly === false && <OptionsPanel />}
|
|
46
48
|
{isDiagramTitleVisible && <DiagramTitlePanel />}
|
|
47
49
|
{isDynamicView && enableDynamicViewWalkthrough && <DynamicViewWalkthrough />}
|
|
50
|
+
{showNavigationButtons && <BackwardForwardPanel />}
|
|
48
51
|
{children}
|
|
49
52
|
</>
|
|
50
53
|
)
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type DiagramView,
|
|
4
|
-
extractStep,
|
|
5
|
-
type Fqn,
|
|
6
|
-
invariant,
|
|
7
|
-
type NonEmptyArray,
|
|
8
|
-
nonNullable,
|
|
9
|
-
type Point
|
|
10
|
-
} from '@likec4/core'
|
|
11
|
-
import { Bezier } from 'bezier-js'
|
|
12
|
-
import { hasAtLeast, reduce } from 'remeda'
|
|
1
|
+
import { type DiagramNode, type DiagramView, extractStep, type Fqn, invariant, nonNullable } from '@likec4/core'
|
|
2
|
+
import { hasAtLeast } from 'remeda'
|
|
13
3
|
import type { XYFlowData } from '../xyflow/types'
|
|
14
|
-
import { toDomPrecision } from './utils'
|
|
15
4
|
|
|
16
5
|
// const nodeZIndex = (node: DiagramNode) => node.level + (hasAtLeast(node.children, 1) ? 2 : 1)
|
|
17
6
|
const nodeZIndex = (node: DiagramNode) => node.level + 1
|
|
@@ -12,7 +12,7 @@ import { memo, useRef, useState } from 'react'
|
|
|
12
12
|
import { first, hasAtLeast, isArray, isTruthy, last } from 'remeda'
|
|
13
13
|
import { useDiagramState, useDiagramStoreApi } from '../../state'
|
|
14
14
|
import { ZIndexes } from '../const'
|
|
15
|
-
import {
|
|
15
|
+
import { useXYStoreApi } from '../hooks'
|
|
16
16
|
import { type XYFlowEdge } from '../types'
|
|
17
17
|
import { bezierControlPoints, toDomPrecision } from '../utils'
|
|
18
18
|
import * as edgesCss from './edges.css'
|
|
@@ -207,6 +207,9 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
207
207
|
let pointer = { x: e.clientX, y: e.clientY }
|
|
208
208
|
const onPointerMove = (e: PointerEvent) => {
|
|
209
209
|
if (!isSamePoint(pointer, [e.clientX, e.clientY])) {
|
|
210
|
+
if (!hasMoved) {
|
|
211
|
+
diagramStore.getState().cancelSaveManualLayout()
|
|
212
|
+
}
|
|
210
213
|
hasMoved = true
|
|
211
214
|
pointer = { x: e.clientX, y: e.clientY }
|
|
212
215
|
const { x, y } = xyflow.screenToFlowPosition(pointer, { snapToGrid: false })
|
|
@@ -246,6 +249,13 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
246
249
|
data-edge-dir={diagramEdge.dir}
|
|
247
250
|
data-edge-active={isActive}
|
|
248
251
|
data-edge-hovered={isHovered}>
|
|
252
|
+
<path
|
|
253
|
+
className={clsx('react-flow__edge-interaction')}
|
|
254
|
+
d={edgePath}
|
|
255
|
+
fill="none"
|
|
256
|
+
stroke={'transparent'}
|
|
257
|
+
strokeWidth={interactionWidth ?? 10}
|
|
258
|
+
/>
|
|
249
259
|
<g className={edgesCss.markerContext}>
|
|
250
260
|
<defs>
|
|
251
261
|
<marker
|
|
@@ -279,13 +289,6 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
279
289
|
markerEnd={markerEnd}
|
|
280
290
|
/>
|
|
281
291
|
</g>
|
|
282
|
-
<path
|
|
283
|
-
className={clsx('react-flow__edge-interaction')}
|
|
284
|
-
d={edgePath}
|
|
285
|
-
fill="none"
|
|
286
|
-
strokeOpacity={0}
|
|
287
|
-
strokeWidth={interactionWidth ?? 10}
|
|
288
|
-
/>
|
|
289
292
|
{isEdgePathEditable && controlPoints.map((p, i) => (
|
|
290
293
|
<circle
|
|
291
294
|
onPointerDown={e => onControlPointerDown(i, e)}
|
|
@@ -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, xyvars } from '../../theme.css'
|
|
5
5
|
|
|
@@ -62,7 +62,7 @@ globalStyle(`.react-flow__edges > svg:has(${dimmed})`, {
|
|
|
62
62
|
opacity: 0.8,
|
|
63
63
|
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
64
64
|
transitionDelay: '200ms',
|
|
65
|
-
filter:
|
|
65
|
+
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
|
|
66
66
|
willChange: 'opacity, filter'
|
|
67
67
|
})
|
|
68
68
|
|
|
@@ -112,8 +112,7 @@ export const controlPoint = style({
|
|
|
112
112
|
}
|
|
113
113
|
})
|
|
114
114
|
|
|
115
|
-
const strokeKeyframes =
|
|
116
|
-
globalKeyframes(strokeKeyframes, {
|
|
115
|
+
const strokeKeyframes = keyframes({
|
|
117
116
|
'from': {
|
|
118
117
|
strokeDashoffset: 18 * 2
|
|
119
118
|
},
|
|
@@ -126,19 +125,24 @@ export const cssEdgePath = style({
|
|
|
126
125
|
animationDuration: '800ms',
|
|
127
126
|
animationIterationCount: 'infinite',
|
|
128
127
|
animationTimingFunction: 'linear',
|
|
128
|
+
animationName: strokeKeyframes,
|
|
129
|
+
animationPlayState: 'paused',
|
|
129
130
|
selectors: {
|
|
130
131
|
[`:where([data-edge-hovered='true']) &`]: {
|
|
131
|
-
|
|
132
|
+
animationPlayState: 'running',
|
|
132
133
|
animationDelay: '350ms',
|
|
133
|
-
transition: 'stroke,stroke-width 130ms ease-out'
|
|
134
|
+
transition: 'stroke 130ms ease-out,stroke-width 130ms ease-out'
|
|
134
135
|
},
|
|
135
136
|
[`:where(${isSelected}, [data-edge-active='true']) &`]: {
|
|
136
|
-
|
|
137
|
+
animationPlayState: 'running',
|
|
137
138
|
animationDelay: '0ms',
|
|
138
|
-
transition: 'stroke,stroke-width 130ms ease-out'
|
|
139
|
+
transition: 'stroke 130ms ease-out,stroke-width 130ms ease-out'
|
|
139
140
|
},
|
|
140
141
|
[`:where([data-edge-dir='back']) &`]: {
|
|
141
142
|
animationDirection: 'reverse'
|
|
143
|
+
},
|
|
144
|
+
[`${dimmed} &`]: {
|
|
145
|
+
animationPlayState: 'paused'
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
148
|
})
|
|
@@ -170,10 +174,10 @@ export const edgeLabel = style({
|
|
|
170
174
|
transform: `${varTranslate} scale(1.1)`
|
|
171
175
|
},
|
|
172
176
|
[`&:is(${dimmed})`]: {
|
|
173
|
-
opacity: 0.
|
|
177
|
+
opacity: 0.6,
|
|
174
178
|
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
175
179
|
transitionDelay: '200ms',
|
|
176
|
-
filter:
|
|
180
|
+
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
|
|
177
181
|
willChange: 'opacity, filter'
|
|
178
182
|
}
|
|
179
183
|
}
|
|
@@ -286,7 +286,8 @@ export function useLayoutConstraints(): LayoutConstraints {
|
|
|
286
286
|
x: event.clientX,
|
|
287
287
|
y: event.clientY
|
|
288
288
|
}
|
|
289
|
-
const { xyflow } = diagramApi.getState()
|
|
289
|
+
const { xyflow, cancelSaveManualLayout } = diagramApi.getState()
|
|
290
|
+
cancelSaveManualLayout()
|
|
290
291
|
solverRef.current = createLayoutConstraints(xyflow, xyflowApi, xynode.id)
|
|
291
292
|
},
|
|
292
293
|
onNodeDrag: (_event, xynode) => {
|
|
@@ -18,7 +18,7 @@ globalStyle(`.react-flow__node-compound:has(${dimmed})`, {
|
|
|
18
18
|
opacity: 0.25,
|
|
19
19
|
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
20
20
|
transitionDelay: '200ms',
|
|
21
|
-
filter:
|
|
21
|
+
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
|
|
22
22
|
willChange: 'opacity, filter'
|
|
23
23
|
})
|
|
24
24
|
|
|
@@ -81,14 +81,14 @@ export const transparent = style({
|
|
|
81
81
|
opacity: calc.add(fallbackVar(varOpacity, '1'), opacityDeltaOnHover)
|
|
82
82
|
},
|
|
83
83
|
selectors: {
|
|
84
|
-
[`:where(.react-flow__node.selected) &`]: {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
84
|
+
// [`:where(.react-flow__node.selected) &`]: {
|
|
85
|
+
// vars: {
|
|
86
|
+
// [opacityDeltaOnHover]: '.05'
|
|
87
|
+
// }
|
|
88
|
+
// },
|
|
89
89
|
[`:where([data-hovered]) &`]: {
|
|
90
90
|
vars: {
|
|
91
|
-
[opacityDeltaOnHover]: '.
|
|
91
|
+
[opacityDeltaOnHover]: '.07'
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -212,7 +212,7 @@ export const navigateBtn = style({
|
|
|
212
212
|
},
|
|
213
213
|
':hover': {
|
|
214
214
|
boxShadow: mantine.shadows.lg,
|
|
215
|
-
transform: 'scale(1.
|
|
215
|
+
transform: 'scale(1.42)',
|
|
216
216
|
opacity: 1,
|
|
217
217
|
transitionDelay: '0'
|
|
218
218
|
},
|
|
@@ -24,13 +24,13 @@ const variants = {
|
|
|
24
24
|
idle: (_, { scale }) => ({
|
|
25
25
|
scale: 1,
|
|
26
26
|
transition: {
|
|
27
|
-
delay: isNumber(scale) && scale > selectedScale ? 0.
|
|
27
|
+
delay: isNumber(scale) && scale > selectedScale ? 0.09 : 0
|
|
28
28
|
}
|
|
29
29
|
}),
|
|
30
30
|
selected: (_, { scale }) => ({
|
|
31
31
|
scale: selectedScale,
|
|
32
32
|
transition: {
|
|
33
|
-
delay: isNumber(scale) && scale > selectedScale ? 0.
|
|
33
|
+
delay: isNumber(scale) && scale > selectedScale ? 0.09 : 0
|
|
34
34
|
}
|
|
35
35
|
}),
|
|
36
36
|
// dimmed: {
|
|
@@ -54,12 +54,9 @@ const variants = {
|
|
|
54
54
|
// delay: 1
|
|
55
55
|
// }
|
|
56
56
|
// }),
|
|
57
|
-
hovered:
|
|
58
|
-
scale: 1.06
|
|
59
|
-
|
|
60
|
-
delay: isNumber(scale) && scale !== 1 && scale !== selectedScale ? 0 : 0.15
|
|
61
|
-
}
|
|
62
|
-
}),
|
|
57
|
+
hovered: {
|
|
58
|
+
scale: 1.06
|
|
59
|
+
},
|
|
63
60
|
tap: {
|
|
64
61
|
scale: 0.975
|
|
65
62
|
}
|
|
@@ -128,8 +125,10 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
128
125
|
className={clsx([
|
|
129
126
|
css.container,
|
|
130
127
|
isDimmed && css.dimmed,
|
|
128
|
+
animate !== 'idle' && css.containerAnimated,
|
|
131
129
|
'likec4-element-node'
|
|
132
130
|
])}
|
|
131
|
+
data-hovered={!dragging && isHovered}
|
|
133
132
|
data-likec4-color={element.color}
|
|
134
133
|
data-likec4-shape={element.shape}
|
|
135
134
|
variants={variants}
|
|
@@ -16,16 +16,28 @@ export const container = style({
|
|
|
16
16
|
alignItems: 'stretch',
|
|
17
17
|
vars: {
|
|
18
18
|
[stokeFillMix]: `color-mix(in srgb, ${vars.element.stroke} 90%, ${vars.element.fill})`
|
|
19
|
+
},
|
|
20
|
+
selectors: {
|
|
21
|
+
':where(.react-flow__node.selected) &': {
|
|
22
|
+
willChange: 'transform'
|
|
23
|
+
},
|
|
24
|
+
'&[data-hovered="true"]': {
|
|
25
|
+
willChange: 'transform'
|
|
26
|
+
}
|
|
19
27
|
}
|
|
20
28
|
})
|
|
21
29
|
|
|
30
|
+
export const containerAnimated = style({
|
|
31
|
+
willChange: 'transform'
|
|
32
|
+
})
|
|
33
|
+
|
|
22
34
|
export const dimmed = style({})
|
|
23
35
|
|
|
24
36
|
globalStyle(`.react-flow__node-element:has(${dimmed})`, {
|
|
25
37
|
opacity: 0.25,
|
|
26
38
|
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
27
39
|
transitionDelay: '200ms',
|
|
28
|
-
filter:
|
|
40
|
+
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(2px)')}`,
|
|
29
41
|
willChange: 'opacity, filter'
|
|
30
42
|
})
|
|
31
43
|
|
|
@@ -49,16 +61,16 @@ export const indicator = style({
|
|
|
49
61
|
transformOrigin: 'center center',
|
|
50
62
|
strokeWidth: 6,
|
|
51
63
|
animationDuration: '800ms',
|
|
52
|
-
animationName: indicatorKeyframes,
|
|
53
64
|
animationIterationCount: 'infinite',
|
|
54
65
|
animationDirection: 'alternate',
|
|
66
|
+
opacity: 0.6,
|
|
55
67
|
visibility: 'hidden',
|
|
56
68
|
selectors: {
|
|
57
69
|
':where(.react-flow__node.selected) &': {
|
|
58
|
-
visibility: 'visible'
|
|
70
|
+
visibility: 'visible',
|
|
71
|
+
animationName: fallbackVar(vars.safariAnimationHook, indicatorKeyframes)
|
|
59
72
|
},
|
|
60
73
|
[`:where(.react-flow__node:focus-visible, ${container}:focus-visible) &`]: {
|
|
61
|
-
animationName: 'none',
|
|
62
74
|
strokeWidth: 10,
|
|
63
75
|
stroke: outlineColor,
|
|
64
76
|
visibility: 'visible'
|
|
@@ -67,7 +79,8 @@ export const indicator = style({
|
|
|
67
79
|
strokeWidth: 10
|
|
68
80
|
},
|
|
69
81
|
[`${dimmed} &`]: {
|
|
70
|
-
visibility: 'hidden'
|
|
82
|
+
visibility: 'hidden',
|
|
83
|
+
display: 'none'
|
|
71
84
|
}
|
|
72
85
|
}
|
|
73
86
|
})
|