@likec4/diagram 1.4.0 → 1.6.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/LICENSE +1 -1
- package/dist/LikeC4Diagram.css.d.ts +0 -1
- package/dist/LikeC4Diagram.d.ts +1 -1
- package/dist/LikeC4Diagram.props.d.ts +19 -4
- package/dist/StaticLikeC4Diagram.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +3937 -3646
- package/dist/state/DiagramContext.d.ts +95 -4159
- package/dist/state/diagramStore.d.ts +100 -4164
- package/dist/state/hooks.d.ts +4 -0
- package/dist/state/index.d.ts +1 -2
- package/dist/style.css +281 -191
- package/dist/ui/options/EdgeOptions.d.ts +19 -0
- package/dist/ui/options/NodeOptions.d.ts +2 -2
- package/dist/xyflow/EdgeMarkers.d.ts +11 -0
- package/dist/xyflow/XYFlowEvents.d.ts +8 -5
- package/dist/xyflow/const.d.ts +3 -3
- package/dist/xyflow/diagram-to-xyflow.d.ts +1 -1
- package/dist/xyflow/edges/RelationshipEdge.d.ts +3 -2
- package/dist/xyflow/edges/edges.css.d.ts +1 -0
- package/dist/xyflow/edges/utils.d.ts +1 -0
- package/dist/xyflow/hooks/index.d.ts +3 -2
- package/dist/xyflow/hooks/useXYFlow.d.ts +13 -12
- package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +1 -0
- package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +5 -0
- package/dist/xyflow/nodes/element/element.css.d.ts +5 -2
- package/dist/xyflow/types.d.ts +2 -32
- package/package.json +19 -19
- package/src/LikeC4Diagram.css.ts +0 -8
- package/src/LikeC4Diagram.props.ts +23 -5
- package/src/LikeC4Diagram.tsx +18 -21
- package/src/StaticLikeC4Diagram.tsx +1 -0
- package/src/globals.css.ts +10 -3
- package/src/index.ts +1 -1
- package/src/mantine.css.ts +2 -2
- package/src/state/DiagramContext.tsx +44 -13
- package/src/state/diagramStore.ts +72 -53
- package/src/state/hooks.ts +5 -0
- package/src/state/index.ts +1 -2
- package/src/ui/BackwardForwardPanel.css.ts +9 -9
- package/src/ui/DiagramTitlePanel.css.ts +7 -2
- package/src/ui/DiagramTitlePanel.tsx +5 -3
- package/src/ui/DynamicViewWalkthrough.tsx +6 -4
- package/src/ui/OptionsPanel.tsx +37 -27
- package/src/ui/options/EdgeOptions.tsx +157 -0
- package/src/ui/options/NodeOptions.tsx +37 -29
- package/src/xyflow/EdgeMarkers.tsx +148 -0
- package/src/xyflow/FitviewOnDiagramChange.tsx +9 -5
- package/src/xyflow/SelectEdgesOnNodeFocus.tsx +59 -55
- package/src/xyflow/SyncWithDiagram.tsx +13 -20
- package/src/xyflow/XYFlow.tsx +3 -4
- package/src/xyflow/XYFlowEvents.tsx +48 -33
- package/src/xyflow/const.ts +3 -3
- package/src/xyflow/diagram-to-xyflow.ts +17 -41
- package/src/xyflow/edges/RelationshipEdge.tsx +208 -119
- package/src/xyflow/edges/edges.css.ts +40 -26
- package/src/xyflow/edges/utils.ts +10 -0
- package/src/xyflow/hooks/index.ts +4 -2
- package/src/xyflow/hooks/useLayoutConstraints.ts +3 -4
- package/src/xyflow/hooks/useXYFlow.ts +28 -1
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +34 -24
- package/src/xyflow/nodes/compound/CompoundNode.tsx +52 -53
- package/src/xyflow/nodes/element/ElementLink.tsx +12 -16
- package/src/xyflow/nodes/element/ElementNode.tsx +118 -114
- package/src/xyflow/nodes/element/ElementShapeSvg.tsx +2 -2
- package/src/xyflow/nodes/element/element.css.ts +147 -55
- package/src/xyflow/nodes/shared/NavigateToBtn.tsx +1 -1
- package/src/xyflow/types.ts +2 -4
- package/src/xyflow/utils.ts +3 -6
- package/dist/KeepAspectRatioContainer.d.ts +0 -9
- package/dist/icons/Clipboard.d.ts +0 -3
- package/dist/icons/Link.d.ts +0 -3
- package/dist/icons/ZoomIn.d.ts +0 -3
- package/dist/icons/index.d.ts +0 -3
- package/dist/mantine/theme.d.ts +0 -3
- package/dist/state/WhenInitialized.d.ts +0 -3
- package/dist/ui/Camera.d.ts +0 -3
- package/dist/xyflow/nodes/element/ElementIcon.d.ts +0 -10
- package/src/KeepAspectRatioContainer.tsx +0 -36
- package/src/icons/Clipboard.tsx +0 -24
- package/src/icons/Link.tsx +0 -23
- package/src/icons/ZoomIn.tsx +0 -25
- package/src/icons/index.ts +0 -3
- package/src/mantine/theme.ts +0 -20
- package/src/state/WhenInitialized.tsx +0 -24
- package/src/ui/Camera.tsx +0 -174
- package/src/xyflow/nodes/element/ElementIcon.tsx +0 -44
package/src/ui/Camera.tsx
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { shallowEqual } from '@mantine/hooks'
|
|
2
|
-
import { useDebouncedEffect, useIsMounted } from '@react-hookz/web'
|
|
3
|
-
import {
|
|
4
|
-
type ReactFlowState,
|
|
5
|
-
useOnSelectionChange,
|
|
6
|
-
useOnViewportChange,
|
|
7
|
-
useStore,
|
|
8
|
-
useUpdateNodeInternals,
|
|
9
|
-
type Viewport
|
|
10
|
-
} from '@xyflow/react'
|
|
11
|
-
import { memo, useEffect, useRef, useState } from 'react'
|
|
12
|
-
import { uniq } from 'remeda'
|
|
13
|
-
import { useXYFlow } from '../xyflow/hooks'
|
|
14
|
-
import type { XYFlowNode } from '../xyflow/types'
|
|
15
|
-
import { distance } from '../xyflow/utils'
|
|
16
|
-
|
|
17
|
-
const selector = (state: ReactFlowState) => {
|
|
18
|
-
return {
|
|
19
|
-
isUserSelectionActive: state.userSelectionActive,
|
|
20
|
-
isDragging: state.paneDragging || state.nodes.some(n => n.dragging ?? false)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const CameraMemo = /* @__PURE__ */ memo(function Camera() {
|
|
25
|
-
const isMounted = useIsMounted()
|
|
26
|
-
// const isUserSelectionActive = useStore(selectUserSelectionActive)
|
|
27
|
-
const {
|
|
28
|
-
isUserSelectionActive,
|
|
29
|
-
isDragging
|
|
30
|
-
} = useStore(selector, shallowEqual)
|
|
31
|
-
const reactflow = useXYFlow()
|
|
32
|
-
const updateNd = useUpdateNodeInternals()
|
|
33
|
-
|
|
34
|
-
const padding = 0 // editor.fitViewPadding
|
|
35
|
-
const viewId = '' // editor.viewId
|
|
36
|
-
const fitOnSelect: boolean = false // editor.fitOnSelect
|
|
37
|
-
|
|
38
|
-
const previousViewport = useRef<Viewport | null>(null)
|
|
39
|
-
const isZoomPendingRef = useRef(false)
|
|
40
|
-
|
|
41
|
-
const viewportChangeStart = useRef<Viewport | null>(null)
|
|
42
|
-
const prevViewId = useRef(viewId)
|
|
43
|
-
const isReady = reactflow.viewportInitialized
|
|
44
|
-
const selectedNodesRef = useRef([] as string[])
|
|
45
|
-
|
|
46
|
-
const [selectedNodes, setSelectedNodes] = useState([] as XYFlowNode[])
|
|
47
|
-
|
|
48
|
-
// WORKAROUND
|
|
49
|
-
|
|
50
|
-
useOnViewportChange({
|
|
51
|
-
onStart: (viewport) => {
|
|
52
|
-
viewportChangeStart.current = { ...viewport }
|
|
53
|
-
},
|
|
54
|
-
onEnd: (end) => {
|
|
55
|
-
if (!previousViewport.current) {
|
|
56
|
-
viewportChangeStart.current = null
|
|
57
|
-
}
|
|
58
|
-
if (!viewportChangeStart.current) {
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
const start = {
|
|
62
|
-
...viewportChangeStart.current
|
|
63
|
-
}
|
|
64
|
-
const d = distance(start, end)
|
|
65
|
-
if (d > 15) {
|
|
66
|
-
previousViewport.current = null
|
|
67
|
-
}
|
|
68
|
-
viewportChangeStart.current = null
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const fixUseOnViewportChange = () => {
|
|
73
|
-
// WORKAROUND:
|
|
74
|
-
// react-flow triggers useOnViewportChange
|
|
75
|
-
// We reset the viewportChangeStart, before useOnViewportChange.onEnd called
|
|
76
|
-
setTimeout(() => {
|
|
77
|
-
viewportChangeStart.current = null
|
|
78
|
-
}, 50)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
useOnSelectionChange({
|
|
82
|
-
onChange: ({ nodes, edges }) => {
|
|
83
|
-
if (!isMounted() || fitOnSelect === false) {
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
if (nodes.length === 0 && edges.length === 0) {
|
|
87
|
-
setSelectedNodes([])
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
const selected = new Set([
|
|
91
|
-
...nodes.map(({ id }) => reactflow.getNode(id)),
|
|
92
|
-
...edges.flatMap((edge) => [
|
|
93
|
-
reactflow.getNode(edge.source),
|
|
94
|
-
reactflow.getNode(edge.target)
|
|
95
|
-
])
|
|
96
|
-
].filter(Boolean))
|
|
97
|
-
setSelectedNodes([...selected])
|
|
98
|
-
}
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
const selectedNodesHash = selectedNodes.map((node) => node.id).sort().join('\n')
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
isZoomPendingRef.current = selectedNodesHash !== ''
|
|
104
|
-
}, [selectedNodesHash])
|
|
105
|
-
|
|
106
|
-
useDebouncedEffect(
|
|
107
|
-
() => {
|
|
108
|
-
if (isUserSelectionActive || isDragging || !isReady) {
|
|
109
|
-
return
|
|
110
|
-
}
|
|
111
|
-
if (selectedNodes.length === 0) {
|
|
112
|
-
if (previousViewport.current) {
|
|
113
|
-
console.log(`Camera: revert viewport`)
|
|
114
|
-
reactflow.setViewport(previousViewport.current, {
|
|
115
|
-
duration: 300
|
|
116
|
-
})
|
|
117
|
-
previousViewport.current = null
|
|
118
|
-
}
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
if (!isZoomPendingRef.current) {
|
|
122
|
-
return
|
|
123
|
-
}
|
|
124
|
-
isZoomPendingRef.current = false
|
|
125
|
-
previousViewport.current ??= { ...reactflow.getViewport() }
|
|
126
|
-
const zoom = previousViewport.current.zoom
|
|
127
|
-
reactflow.fitView({
|
|
128
|
-
duration: 350,
|
|
129
|
-
maxZoom: Math.max(1, zoom),
|
|
130
|
-
padding,
|
|
131
|
-
nodes: selectedNodes
|
|
132
|
-
})
|
|
133
|
-
fixUseOnViewportChange()
|
|
134
|
-
},
|
|
135
|
-
[selectedNodesHash, isReady, isDragging, isUserSelectionActive],
|
|
136
|
-
previousViewport.current ? 100 : 1500
|
|
137
|
-
// previousViewport.current ? 200 : 2500
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
useEffect(() => {
|
|
141
|
-
const ids = selectedNodes.map((node) => node.id)
|
|
142
|
-
for (const id of uniq([...selectedNodesRef.current, ...ids])) {
|
|
143
|
-
try {
|
|
144
|
-
updateNd(id)
|
|
145
|
-
} catch (e) {
|
|
146
|
-
console.warn(`Camera: failed useUpdateNodeInternals`, e)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
selectedNodesRef.current = ids
|
|
150
|
-
}, [selectedNodesHash, updateNd])
|
|
151
|
-
|
|
152
|
-
useDebouncedEffect(
|
|
153
|
-
() => {
|
|
154
|
-
if (!isReady || prevViewId.current === viewId) {
|
|
155
|
-
return
|
|
156
|
-
}
|
|
157
|
-
const zoom = previousViewport.current?.zoom ?? reactflow.getZoom()
|
|
158
|
-
reactflow.fitView({
|
|
159
|
-
duration: 400,
|
|
160
|
-
maxZoom: Math.max(1, zoom),
|
|
161
|
-
padding
|
|
162
|
-
})
|
|
163
|
-
previousViewport.current = null
|
|
164
|
-
prevViewId.current = viewId
|
|
165
|
-
},
|
|
166
|
-
[isReady, viewId],
|
|
167
|
-
50,
|
|
168
|
-
500
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
return null
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
export default CameraMemo
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { assignInlineVars } from '@vanilla-extract/dynamic'
|
|
2
|
-
import clsx from 'clsx'
|
|
3
|
-
import type { XYFlowNode } from '../../types'
|
|
4
|
-
import { elementIcon, iconMaxH } from './element.css'
|
|
5
|
-
|
|
6
|
-
type ElementIconProps = {
|
|
7
|
-
node: XYFlowNode['data']['element']
|
|
8
|
-
maxWidth?: number | undefined
|
|
9
|
-
paddingX?: number | undefined
|
|
10
|
-
paddingY?: number | undefined
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function ElementIcon({
|
|
14
|
-
node,
|
|
15
|
-
paddingY = 32
|
|
16
|
-
}: ElementIconProps) {
|
|
17
|
-
if (!node.icon) {
|
|
18
|
-
return null
|
|
19
|
-
}
|
|
20
|
-
const firstLabel = node.labels[0]
|
|
21
|
-
if (!firstLabel) {
|
|
22
|
-
return null
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const firstLabelY = Math.floor(firstLabel.pt[1] - firstLabel.fontSize * 1.25)
|
|
26
|
-
const maxIconHeight = Math.floor(firstLabelY - paddingY - 16)
|
|
27
|
-
// const maxIconWidth = maxWidth ?? node.width - paddingX * 2
|
|
28
|
-
|
|
29
|
-
// const centerY = paddingY + Math.floor(maxIconHeight / 2)
|
|
30
|
-
// const centerX = Math.floor(node.width / 2)
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<div
|
|
34
|
-
className={clsx(elementIcon, 'likec4-element-icon')}
|
|
35
|
-
style={assignInlineVars({ iconMaxH }, {
|
|
36
|
-
iconMaxH: maxIconHeight + 'px'
|
|
37
|
-
})}>
|
|
38
|
-
<img
|
|
39
|
-
src={node.icon!}
|
|
40
|
-
alt={node.title}
|
|
41
|
-
/>
|
|
42
|
-
</div>
|
|
43
|
-
)
|
|
44
|
-
}
|