@likec4/diagram 1.5.0 → 1.6.1
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.d.ts +1 -1
- package/dist/LikeC4Diagram.props.d.ts +18 -3
- package/dist/StaticLikeC4Diagram.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +490 -8409
- package/dist/state/DiagramContext.d.ts +18 -9
- package/dist/state/diagramStore.d.ts +12 -2
- package/dist/style.css +149 -85
- package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +5 -0
- package/dist/xyflow/nodes/element/element.css.d.ts +4 -2
- package/package.json +17 -17
- package/src/LikeC4Diagram.props.ts +21 -3
- package/src/LikeC4Diagram.tsx +9 -11
- package/src/StaticLikeC4Diagram.tsx +1 -0
- package/src/index.ts +2 -0
- package/src/state/DiagramContext.tsx +5 -5
- package/src/state/diagramStore.ts +8 -1
- package/src/ui/BackwardForwardPanel.tsx +1 -1
- package/src/xyflow/FitviewOnDiagramChange.tsx +1 -1
- package/src/xyflow/SelectEdgesOnNodeFocus.tsx +1 -1
- package/src/xyflow/SyncWithDiagram.tsx +1 -1
- package/src/xyflow/XYFlow.tsx +2 -4
- package/src/xyflow/XYFlowEvents.tsx +1 -1
- package/src/xyflow/XYFlowInner.tsx +2 -1
- package/src/xyflow/edges/RelationshipEdge.tsx +3 -3
- package/src/xyflow/edges/edges.css.ts +6 -6
- package/src/xyflow/hooks/useLayoutConstraints.ts +1 -1
- package/src/xyflow/nodes/compound/CompoundNode.tsx +1 -1
- package/src/xyflow/nodes/element/ElementLink.tsx +2 -1
- package/src/xyflow/nodes/element/ElementNode.tsx +73 -24
- package/src/xyflow/nodes/element/ElementShapeSvg.tsx +2 -2
- package/src/xyflow/nodes/element/element.css.ts +126 -54
- package/src/xyflow/nodes/shared/NavigateToBtn.tsx +1 -1
- package/dist/xyflow/nodes/element/ElementIcon.d.ts +0 -7
- package/src/xyflow/nodes/element/ElementIcon.tsx +0 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/diagram",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
6
6
|
"homepage": "https://likec4.dev",
|
|
@@ -48,31 +48,36 @@
|
|
|
48
48
|
"clean": "run -T rimraf -v -g 'dist/*' .tsbuildinfo"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@
|
|
51
|
+
"@lume/kiwi": "^0.4.3",
|
|
52
52
|
"@react-hookz/web": "^24.0.4",
|
|
53
|
-
"@tabler/icons-react": "^3.
|
|
53
|
+
"@tabler/icons-react": "^3.11.0",
|
|
54
54
|
"@vanilla-extract/css": "^1.15.3",
|
|
55
55
|
"@vanilla-extract/css-utils": "^0.1.4",
|
|
56
56
|
"@vanilla-extract/dynamic": "^2.1.1",
|
|
57
|
+
"@xyflow/react": "12.0.3",
|
|
58
|
+
"@xyflow/system": "0.0.37",
|
|
59
|
+
"bezier-js": "^6.1.4",
|
|
60
|
+
"clsx": "^2.1.1",
|
|
57
61
|
"d3-shape": "^3.2.0",
|
|
58
62
|
"esm-env": "^1.0.0",
|
|
59
63
|
"fast-equals": "^5.0.1",
|
|
60
|
-
"framer-motion": "^11.
|
|
64
|
+
"framer-motion": "^11.3.8",
|
|
61
65
|
"remeda": "^2.3.0",
|
|
62
66
|
"zustand": "^4.5.4"
|
|
63
67
|
},
|
|
64
68
|
"peerDependencies": {
|
|
65
|
-
"@
|
|
66
|
-
"@mantine/
|
|
69
|
+
"@likec4/core": "1.6.1",
|
|
70
|
+
"@mantine/core": "^7.11.2",
|
|
71
|
+
"@mantine/hooks": "^7.11.2",
|
|
67
72
|
"react": "^18.3.1",
|
|
68
73
|
"react-dom": "^18.3.1"
|
|
69
74
|
},
|
|
70
75
|
"devDependencies": {
|
|
71
|
-
"@likec4/
|
|
72
|
-
"@
|
|
73
|
-
"@mantine/core": "7.11.
|
|
74
|
-
"@mantine/hooks": "7.11.
|
|
75
|
-
"@mantine/vanilla-extract": "7.11.
|
|
76
|
+
"@likec4/core": "1.6.1",
|
|
77
|
+
"@likec4/tsconfig": "1.6.1",
|
|
78
|
+
"@mantine/core": "7.11.2",
|
|
79
|
+
"@mantine/hooks": "7.11.2",
|
|
80
|
+
"@mantine/vanilla-extract": "7.11.2",
|
|
76
81
|
"@total-typescript/ts-reset": "^0.5.1",
|
|
77
82
|
"@types/bezier-js": "^4.1.3",
|
|
78
83
|
"@types/d3-shape": "^3.1.6",
|
|
@@ -80,18 +85,13 @@
|
|
|
80
85
|
"@types/react-dom": "^18.3.0",
|
|
81
86
|
"@vanilla-extract/esbuild-plugin": "^2.3.8",
|
|
82
87
|
"@vitejs/plugin-react": "^4.3.1",
|
|
83
|
-
"@xyflow/react": "12.0.1",
|
|
84
|
-
"@xyflow/system": "0.0.35",
|
|
85
|
-
"bezier-js": "^6.1.4",
|
|
86
|
-
"clsx": "^2.1.1",
|
|
87
88
|
"execa": "^9.3.0",
|
|
88
89
|
"khroma": "^2.1.0",
|
|
89
90
|
"react": "^18.3.1",
|
|
90
91
|
"react-dom": "^18.3.1",
|
|
91
|
-
"tilg": "^0.1.1",
|
|
92
92
|
"type-fest": "^4.21.0",
|
|
93
93
|
"typescript": "^5.5.3",
|
|
94
|
-
"vite": "5.3.
|
|
94
|
+
"vite": "5.3.4",
|
|
95
95
|
"vite-plugin-dts": "^3.9.1"
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -6,17 +6,29 @@ import type {
|
|
|
6
6
|
EdgeId,
|
|
7
7
|
ElementShape,
|
|
8
8
|
Fqn,
|
|
9
|
+
IconUrl,
|
|
9
10
|
NonEmptyArray,
|
|
10
11
|
ThemeColor,
|
|
11
12
|
ViewID
|
|
12
13
|
} from '@likec4/core'
|
|
13
14
|
import type { XYPosition } from '@xyflow/system'
|
|
14
|
-
import type { MouseEvent as ReactMouseEvent } from 'react'
|
|
15
|
-
import type { SetRequired, Simplify } from 'type-fest'
|
|
15
|
+
import type { MouseEvent as ReactMouseEvent, ReactNode } from 'react'
|
|
16
16
|
import type { XYFlowEdge, XYFlowNode } from './xyflow/types'
|
|
17
17
|
import type { XYBackground } from './xyflow/XYFlowBackground'
|
|
18
18
|
|
|
19
|
-
export type DiagramNodeWithNavigate =
|
|
19
|
+
export type DiagramNodeWithNavigate = Omit<DiagramNode, 'navigateTo'> & {
|
|
20
|
+
navigateTo: ViewID
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type ElementIconNodeProps = {
|
|
24
|
+
id: string
|
|
25
|
+
title: string
|
|
26
|
+
icon?: string | undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type ElementIconRenderer = (props: {
|
|
30
|
+
node: ElementIconNodeProps
|
|
31
|
+
}) => ReactNode
|
|
20
32
|
|
|
21
33
|
export type OnNavigateTo = {
|
|
22
34
|
(
|
|
@@ -177,6 +189,12 @@ export interface LikeC4DiagramProperties {
|
|
|
177
189
|
* @default false
|
|
178
190
|
*/
|
|
179
191
|
experimentalEdgeEditing?: boolean | undefined
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Render custom icon for a node
|
|
195
|
+
* By default, if icon is http:// or https://, it will be rendered as an image
|
|
196
|
+
*/
|
|
197
|
+
renderIcon?: ElementIconRenderer | undefined
|
|
180
198
|
}
|
|
181
199
|
|
|
182
200
|
export interface LikeC4DiagramEventHandlers {
|
package/src/LikeC4Diagram.tsx
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { ReactFlowProvider as XYFlowProvider } from '@xyflow/react'
|
|
2
|
-
import '@xyflow/react/dist/style.css'
|
|
3
2
|
import clsx from 'clsx'
|
|
4
|
-
import { DEV } from 'esm-env'
|
|
5
3
|
import { shallowEqual } from 'fast-equals'
|
|
6
4
|
import { domAnimation, LazyMotion } from 'framer-motion'
|
|
7
5
|
import { memo, useEffect, useRef, useState } from 'react'
|
|
8
|
-
import useTilg from 'tilg'
|
|
9
6
|
import { rootClassName } from './globals.css'
|
|
10
|
-
import
|
|
7
|
+
import * as css from './LikeC4Diagram.css'
|
|
11
8
|
import { type LikeC4DiagramEventHandlers, type LikeC4DiagramProperties } from './LikeC4Diagram.props'
|
|
12
9
|
import { EnsureMantine } from './mantine/EnsureMantine'
|
|
13
10
|
import { DiagramContextProvider } from './state/DiagramContext'
|
|
@@ -49,9 +46,9 @@ export function LikeC4Diagram({
|
|
|
49
46
|
onEdgeContextMenu,
|
|
50
47
|
onNavigateTo,
|
|
51
48
|
onNodeClick,
|
|
52
|
-
onNodeContextMenu
|
|
49
|
+
onNodeContextMenu,
|
|
50
|
+
renderIcon
|
|
53
51
|
}: LikeC4DiagramProps) {
|
|
54
|
-
DEV && useTilg()
|
|
55
52
|
const initialRef = useRef<{
|
|
56
53
|
defaultNodes: XYFlowData['nodes']
|
|
57
54
|
defaultEdges: XYFlowData['edges']
|
|
@@ -70,6 +67,7 @@ export function LikeC4Diagram({
|
|
|
70
67
|
initialHeight: initialHeight ?? view.height
|
|
71
68
|
}
|
|
72
69
|
}
|
|
70
|
+
|
|
73
71
|
return (
|
|
74
72
|
<EnsureMantine>
|
|
75
73
|
<XYFlowProvider
|
|
@@ -89,6 +87,7 @@ export function LikeC4Diagram({
|
|
|
89
87
|
nodesDraggable={nodesDraggable}
|
|
90
88
|
nodesSelectable={nodesSelectable}
|
|
91
89
|
experimentalEdgeEditing={experimentalEdgeEditing}
|
|
90
|
+
renderIcon={renderIcon ?? null}
|
|
92
91
|
onCanvasClick={onCanvasClick}
|
|
93
92
|
onCanvasContextMenu={onCanvasContextMenu}
|
|
94
93
|
onEdgeClick={onEdgeClick}
|
|
@@ -143,7 +142,6 @@ const LikeC4DiagramInnerMemo = memo<LikeC4DiagramInnerProps>(function LikeC4Diag
|
|
|
143
142
|
showNavigationButtons,
|
|
144
143
|
enableDynamicViewWalkthrough
|
|
145
144
|
}) {
|
|
146
|
-
DEV && useTilg()
|
|
147
145
|
const diagramApi = useDiagramStoreApi()
|
|
148
146
|
const [isInitialized, setIsInitialized] = useState(diagramApi.getState().initialized)
|
|
149
147
|
|
|
@@ -167,10 +165,10 @@ const LikeC4DiagramInnerMemo = memo<LikeC4DiagramInnerProps>(function LikeC4Diag
|
|
|
167
165
|
defaultEdges={defaultEdges}
|
|
168
166
|
className={clsx(
|
|
169
167
|
'likec4-diagram',
|
|
170
|
-
cssReactFlow,
|
|
171
|
-
controls === false && cssNoControls,
|
|
172
|
-
pannable !== true && cssDisablePan,
|
|
173
|
-
background === 'transparent' && cssTransparentBg
|
|
168
|
+
css.cssReactFlow,
|
|
169
|
+
controls === false && css.cssNoControls,
|
|
170
|
+
pannable !== true && css.cssDisablePan,
|
|
171
|
+
background === 'transparent' && css.cssTransparentBg
|
|
174
172
|
)}
|
|
175
173
|
>
|
|
176
174
|
<XYFlowInner
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { shallowEqual } from 'fast-equals'
|
|
|
2
2
|
import { createContext, type PropsWithChildren, useEffect, useRef } from 'react'
|
|
3
3
|
import { hasSubObject, isNonNullish, pickBy } from 'remeda'
|
|
4
4
|
import { useUpdateEffect } from '../hooks'
|
|
5
|
-
import { useXYFlow, useXYStoreApi } from '../xyflow/hooks'
|
|
5
|
+
import { useXYFlow, useXYStoreApi } from '../xyflow/hooks/useXYFlow'
|
|
6
6
|
import { createDiagramStore, type DiagramInitialState } from './diagramStore'
|
|
7
7
|
|
|
8
8
|
export type DiagramZustandStore = ReturnType<typeof createDiagramStore>
|
|
@@ -52,16 +52,16 @@ export function DiagramContextProvider({
|
|
|
52
52
|
() => {
|
|
53
53
|
if (!store.current) return
|
|
54
54
|
const state = store.current.getState()
|
|
55
|
-
if (state.view !== view) {
|
|
56
|
-
state.updateView(view)
|
|
57
|
-
}
|
|
58
55
|
const newProps = pickBy(props, isNonNullish)
|
|
59
56
|
if (!hasSubObject(state, newProps)) {
|
|
60
57
|
store.current.setState(newProps, false, 'update incoming props')
|
|
61
58
|
}
|
|
59
|
+
if (state.view !== view) {
|
|
60
|
+
state.updateView(view)
|
|
61
|
+
}
|
|
62
62
|
},
|
|
63
63
|
[view, props],
|
|
64
|
-
shallowEqual
|
|
64
|
+
(prev, next) => shallowEqual(prev[0], next[0]) && shallowEqual(prev[1], next[1])
|
|
65
65
|
)
|
|
66
66
|
return (
|
|
67
67
|
<div
|
|
@@ -10,7 +10,12 @@ import type { Exact, Except, RequiredKeysOf, Simplify } from 'type-fest'
|
|
|
10
10
|
import { devtools, subscribeWithSelector } from 'zustand/middleware'
|
|
11
11
|
import { shallow } from 'zustand/shallow'
|
|
12
12
|
import { createWithEqualityFn } from 'zustand/traditional'
|
|
13
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
Changes,
|
|
15
|
+
DiagramNodeWithNavigate,
|
|
16
|
+
ElementIconRenderer,
|
|
17
|
+
LikeC4DiagramEventHandlers
|
|
18
|
+
} from '../LikeC4Diagram.props'
|
|
14
19
|
import { MinZoom } from '../xyflow/const'
|
|
15
20
|
import type { XYStoreApi } from '../xyflow/hooks'
|
|
16
21
|
import type { XYFlowEdge, XYFlowInstance, XYFlowNode } from '../xyflow/types'
|
|
@@ -29,6 +34,7 @@ export type DiagramStore = {
|
|
|
29
34
|
nodesDraggable: boolean
|
|
30
35
|
nodesSelectable: boolean
|
|
31
36
|
experimentalEdgeEditing: boolean
|
|
37
|
+
renderIcon: ElementIconRenderer | null
|
|
32
38
|
// Diagram Container, see DiagramContainer.tsx
|
|
33
39
|
getContainer: () => HTMLDivElement | null
|
|
34
40
|
|
|
@@ -76,6 +82,7 @@ export type DiagramInitialState = // Required properties
|
|
|
76
82
|
& Pick<
|
|
77
83
|
DiagramStore,
|
|
78
84
|
| 'view'
|
|
85
|
+
| 'renderIcon'
|
|
79
86
|
| 'readonly'
|
|
80
87
|
| 'showElementLinks'
|
|
81
88
|
| 'fitViewEnabled'
|
|
@@ -2,7 +2,7 @@ import { ActionIcon, Group } from '@mantine/core'
|
|
|
2
2
|
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react'
|
|
3
3
|
import clsx from 'clsx'
|
|
4
4
|
import { AnimatePresence, m } from 'framer-motion'
|
|
5
|
-
import { useNavigationHistory } from '../state/
|
|
5
|
+
import { useNavigationHistory } from '../state/useNavigationHistory'
|
|
6
6
|
import * as css from './BackwardForwardPanel.css'
|
|
7
7
|
|
|
8
8
|
export function BackwardForwardPanel() {
|
|
@@ -3,7 +3,7 @@ import { useDebouncedEffect } from '@react-hookz/web'
|
|
|
3
3
|
import { shallowEqual } from 'fast-equals'
|
|
4
4
|
import { useEffect, useRef } from 'react'
|
|
5
5
|
import { type DiagramState, type DiagramStoreApi, useDiagramState, useDiagramStoreApi } from '../state/hooks'
|
|
6
|
-
import { useXYStore, useXYStoreApi, type XYStoreApi } from './hooks'
|
|
6
|
+
import { useXYStore, useXYStoreApi, type XYStoreApi } from './hooks/useXYFlow'
|
|
7
7
|
import type { XYFlowState } from './types'
|
|
8
8
|
import { toDomPrecision } from './utils'
|
|
9
9
|
|
|
@@ -2,7 +2,7 @@ import { nonNullable } from '@likec4/core'
|
|
|
2
2
|
import type { EdgeChange, NodeChange } from '@xyflow/react'
|
|
3
3
|
import { getBoundsOfRects, getViewportForBounds, nodeToRect } from '@xyflow/system'
|
|
4
4
|
import { useEffect } from 'react'
|
|
5
|
-
import { useDiagramStoreApi } from '../state/
|
|
5
|
+
import { useDiagramStoreApi } from '../state/useDiagramStore'
|
|
6
6
|
import { MinZoom } from './const'
|
|
7
7
|
import type { XYFlowEdge, XYFlowNode } from './types'
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@ import { deepEqual as eq } from 'fast-equals'
|
|
|
2
2
|
|
|
3
3
|
import { useEffect } from 'react'
|
|
4
4
|
import { omit } from 'remeda'
|
|
5
|
-
import { useDiagramState, useDiagramStoreApi } from '../state/
|
|
5
|
+
import { useDiagramState, useDiagramStoreApi } from '../state/useDiagramStore'
|
|
6
6
|
import { diagramViewToXYFlowData } from './diagram-to-xyflow'
|
|
7
7
|
|
|
8
8
|
/**
|
package/src/xyflow/XYFlow.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { ReactFlow, useOnViewportChange } from '@xyflow/react'
|
|
2
|
-
import { DEV } from 'esm-env'
|
|
3
2
|
import { deepEqual as eq } from 'fast-equals'
|
|
4
3
|
import { type CSSProperties, type PropsWithChildren, useCallback } from 'react'
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
4
|
+
import type { DiagramState } from '../state/diagramStore'
|
|
5
|
+
import { useDiagramState, useDiagramStoreApi } from '../state/useDiagramStore'
|
|
7
6
|
import { MinZoom } from './const'
|
|
8
7
|
import { RelationshipEdge } from './edges/RelationshipEdge'
|
|
9
8
|
import { useLayoutConstraints } from './hooks/useLayoutConstraints'
|
|
@@ -67,7 +66,6 @@ export function XYFlow({
|
|
|
67
66
|
defaultEdges,
|
|
68
67
|
style
|
|
69
68
|
}: XYFlowWrapperProps) {
|
|
70
|
-
DEV && useTilg()
|
|
71
69
|
const xyflowApi = useXYStoreApi()
|
|
72
70
|
const diagramApi = useDiagramStoreApi()
|
|
73
71
|
const {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactFlowProps } from '@xyflow/react'
|
|
2
2
|
import { useMemo, useRef } from 'react'
|
|
3
3
|
import type { Simplify } from 'type-fest'
|
|
4
|
-
import { useDiagramStoreApi } from '../state/
|
|
4
|
+
import { useDiagramStoreApi } from '../state/useDiagramStore'
|
|
5
5
|
import type { XYFlowEdge, XYFlowNode } from './types'
|
|
6
6
|
|
|
7
7
|
export type XYFlowEventHandlers = Simplify<
|
|
@@ -3,7 +3,7 @@ import { shallowEqual } from 'fast-equals'
|
|
|
3
3
|
import { memo, type PropsWithChildren } from 'react'
|
|
4
4
|
import { isNonNull } from 'remeda'
|
|
5
5
|
import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
|
|
6
|
-
import { useDiagramState } from '../state/
|
|
6
|
+
import { useDiagramState } from '../state/useDiagramStore'
|
|
7
7
|
import { BackwardForwardPanel } from '../ui/BackwardForwardPanel'
|
|
8
8
|
import DiagramTitlePanel from '../ui/DiagramTitlePanel'
|
|
9
9
|
import { DynamicViewWalkthrough } from '../ui/DynamicViewWalkthrough'
|
|
@@ -52,3 +52,4 @@ export const XYFlowInner = memo(function XYFlowInnerR({
|
|
|
52
52
|
</>
|
|
53
53
|
)
|
|
54
54
|
}, shallowEqual)
|
|
55
|
+
XYFlowInner.displayName = 'XYFlowInner'
|
|
@@ -16,10 +16,10 @@ import { curveCatmullRomOpen, line as d3line } from 'd3-shape'
|
|
|
16
16
|
import { deepEqual, deepEqual as eq } from 'fast-equals'
|
|
17
17
|
import { memo, type PointerEvent as ReactPointerEvent, useRef, useState } from 'react'
|
|
18
18
|
import { first, hasAtLeast, isArray, isTruthy, last } from 'remeda'
|
|
19
|
-
import { useDiagramState, useDiagramStoreApi } from '../../state/
|
|
19
|
+
import { useDiagramState, useDiagramStoreApi } from '../../state/useDiagramStore'
|
|
20
20
|
import { ZIndexes } from '../const'
|
|
21
21
|
import { EdgeMarkers } from '../EdgeMarkers'
|
|
22
|
-
import { useXYStoreApi } from '../hooks'
|
|
22
|
+
import { useXYStoreApi } from '../hooks/useXYFlow'
|
|
23
23
|
import { type RelationshipData, type XYFlowEdge } from '../types'
|
|
24
24
|
import { bezierControlPoints } from '../utils'
|
|
25
25
|
import * as edgesCss from './edges.css'
|
|
@@ -375,7 +375,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
|
|
|
375
375
|
selected: selected ?? false,
|
|
376
376
|
stepNum: data.stepNum,
|
|
377
377
|
label: data.label,
|
|
378
|
-
zIndex: edgeLookup.get(id)!.zIndex ?? ZIndexes.Edge,
|
|
378
|
+
zIndex: (edgeLookup.get(id)!.zIndex ?? ZIndexes.Edge) + 1,
|
|
379
379
|
isHovered,
|
|
380
380
|
isActive,
|
|
381
381
|
isStepEdge
|
|
@@ -53,7 +53,7 @@ globalStyle(`:where(${isSelected}) ${container}[data-edge-hovered='true']`, {
|
|
|
53
53
|
// })
|
|
54
54
|
|
|
55
55
|
globalStyle(`.react-flow__edges > svg`, {
|
|
56
|
-
mixBlendMode: '
|
|
56
|
+
mixBlendMode: 'plus-lighter'
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
// globalStyle(`:where([data-mantine-color-scheme="light"]) .react-flow__edges > svg`, {
|
|
@@ -63,7 +63,7 @@ globalStyle(`.react-flow__edges > svg`, {
|
|
|
63
63
|
export const dimmed = style({})
|
|
64
64
|
|
|
65
65
|
globalStyle(`.react-flow__edges > svg:has(${dimmed})`, {
|
|
66
|
-
opacity: 0.
|
|
66
|
+
opacity: 0.6,
|
|
67
67
|
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
68
68
|
transitionDelay: '200ms',
|
|
69
69
|
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
|
|
@@ -117,11 +117,11 @@ export const controlPoint = style({
|
|
|
117
117
|
})
|
|
118
118
|
|
|
119
119
|
export const controlDragging = style({
|
|
120
|
-
cursor: '
|
|
120
|
+
cursor: 'grab'
|
|
121
121
|
})
|
|
122
122
|
|
|
123
123
|
globalStyle(`${controlDragging} *`, {
|
|
124
|
-
cursor: '
|
|
124
|
+
cursor: 'grab'
|
|
125
125
|
})
|
|
126
126
|
|
|
127
127
|
const strokeKeyframes = keyframes({
|
|
@@ -174,7 +174,7 @@ export const edgeLabel = style({
|
|
|
174
174
|
cursor: 'pointer',
|
|
175
175
|
width: 'fit-content',
|
|
176
176
|
transformOrigin: '50% 50%',
|
|
177
|
-
mixBlendMode: '
|
|
177
|
+
mixBlendMode: 'plus-lighter',
|
|
178
178
|
color: xyvars.edge.labelColor,
|
|
179
179
|
backgroundColor: xyvars.edge.labelBgColor,
|
|
180
180
|
borderRadius: 3,
|
|
@@ -188,7 +188,7 @@ export const edgeLabel = style({
|
|
|
188
188
|
transform: `${varTranslate} scale(1.12)`
|
|
189
189
|
},
|
|
190
190
|
[`&:is(${dimmed})`]: {
|
|
191
|
-
opacity: 0.
|
|
191
|
+
opacity: 0.3,
|
|
192
192
|
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
193
193
|
transitionDelay: '200ms',
|
|
194
194
|
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
|
|
@@ -4,7 +4,7 @@ import type { InternalNode, ReactFlowProps, XYPosition } from '@xyflow/react'
|
|
|
4
4
|
import { getNodeDimensions } from '@xyflow/system'
|
|
5
5
|
import { useMemo, useRef } from 'react'
|
|
6
6
|
import { isNullish } from 'remeda'
|
|
7
|
-
import { useDiagramStoreApi } from '../../state/
|
|
7
|
+
import { useDiagramStoreApi } from '../../state/useDiagramStore'
|
|
8
8
|
import type { XYFlowInstance, XYFlowNode } from '../types'
|
|
9
9
|
import { isSamePoint } from '../utils'
|
|
10
10
|
import { useXYStoreApi, type XYStoreApi } from './useXYFlow'
|
|
@@ -5,7 +5,7 @@ import clsx from 'clsx'
|
|
|
5
5
|
import { deepEqual as eq } from 'fast-equals'
|
|
6
6
|
import { memo } from 'react'
|
|
7
7
|
import { clamp } from 'remeda'
|
|
8
|
-
import { useDiagramState } from '../../../state/
|
|
8
|
+
import { useDiagramState } from '../../../state/useDiagramStore'
|
|
9
9
|
import type { CompoundXYFlowNode } from '../../types'
|
|
10
10
|
import { NavigateToBtn } from '../shared/NavigateToBtn'
|
|
11
11
|
import * as css from './CompoundNode.css'
|
|
@@ -16,7 +16,8 @@ import { IconLink } from '@tabler/icons-react'
|
|
|
16
16
|
import clsx from 'clsx'
|
|
17
17
|
import { useId } from 'react'
|
|
18
18
|
import { clamp } from 'remeda'
|
|
19
|
-
import { type DiagramState
|
|
19
|
+
import { type DiagramState } from '../../../state/diagramStore'
|
|
20
|
+
import { useDiagramState } from '../../../state/useDiagramStore'
|
|
20
21
|
import type { XYFlowNode } from '../../types'
|
|
21
22
|
import { elementLink, trigger } from './ElementLink.css'
|
|
22
23
|
|
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { DiagramNode } from '@likec4/core/types'
|
|
2
|
+
import { Text as MantineText } from '@mantine/core'
|
|
2
3
|
import { Handle, type NodeProps, Position } from '@xyflow/react'
|
|
3
4
|
import clsx from 'clsx'
|
|
4
5
|
import { deepEqual as eq } from 'fast-equals'
|
|
5
6
|
import { m, type Variants } from 'framer-motion'
|
|
6
7
|
import { memo } from 'react'
|
|
7
|
-
import { isNumber } from 'remeda'
|
|
8
|
-
import { useDiagramState } from '../../../state/
|
|
8
|
+
import { isNumber, isTruthy } from 'remeda'
|
|
9
|
+
import { type DiagramStoreApi, useDiagramState, useDiagramStoreApi } from '../../../state/useDiagramStore'
|
|
9
10
|
import type { ElementXYFlowNode } from '../../types'
|
|
10
11
|
import { toDomPrecision } from '../../utils'
|
|
11
12
|
import { NavigateToBtn } from '../shared/NavigateToBtn'
|
|
12
13
|
import * as css from './element.css'
|
|
13
|
-
import { ElementIcon } from './ElementIcon'
|
|
14
14
|
import { ElementLink } from './ElementLink'
|
|
15
15
|
import { ElementShapeSvg, SelectedIndicator } from './ElementShapeSvg'
|
|
16
16
|
|
|
17
|
+
const Text = MantineText.withProps({
|
|
18
|
+
component: 'div'
|
|
19
|
+
})
|
|
20
|
+
|
|
17
21
|
type ElementNodeProps = NodeProps<ElementXYFlowNode>
|
|
18
22
|
|
|
19
23
|
const selectedScale = 1.015
|
|
@@ -67,7 +71,7 @@ const isEqualProps = (prev: ElementNodeProps, next: ElementNodeProps) => (
|
|
|
67
71
|
prev.id === next.id
|
|
68
72
|
&& eq(prev.selected ?? false, next.selected ?? false)
|
|
69
73
|
&& eq(prev.dragging ?? false, next.dragging ?? false)
|
|
70
|
-
&& eq(prev.draggable ?? false, next.draggable ?? false)
|
|
74
|
+
// && eq(prev.draggable ?? false, next.draggable ?? false)
|
|
71
75
|
&& eq(prev.width ?? 0, next.width ?? 0)
|
|
72
76
|
&& eq(prev.height ?? 0, next.height ?? 0)
|
|
73
77
|
&& eq(prev.data.element, next.data.element)
|
|
@@ -84,6 +88,7 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
84
88
|
width,
|
|
85
89
|
height
|
|
86
90
|
}) {
|
|
91
|
+
const store = useDiagramStoreApi()
|
|
87
92
|
const { isHovered, isDimmed, hasOnNavigateTo, isHovercards, isInteractive } = useDiagramState(s => ({
|
|
88
93
|
isHovered: s.hoveredNodeId === id,
|
|
89
94
|
isDimmed: s.dimmed.has(id),
|
|
@@ -116,7 +121,7 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
116
121
|
break
|
|
117
122
|
}
|
|
118
123
|
|
|
119
|
-
|
|
124
|
+
const elementIcon = ElementIcon({ node: element, store })
|
|
120
125
|
|
|
121
126
|
return (
|
|
122
127
|
<>
|
|
@@ -143,6 +148,22 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
143
148
|
whileTap: dragging ? animate : 'tap'
|
|
144
149
|
})}
|
|
145
150
|
>
|
|
151
|
+
{
|
|
152
|
+
/* <svg
|
|
153
|
+
className={clsx(
|
|
154
|
+
css.cssShapeSvgMultiple
|
|
155
|
+
)}
|
|
156
|
+
viewBox={`0 0 ${w} ${h}`}
|
|
157
|
+
width={w}
|
|
158
|
+
height={h}
|
|
159
|
+
>
|
|
160
|
+
<ElementShapeSvg
|
|
161
|
+
shape={element.shape}
|
|
162
|
+
w={w}
|
|
163
|
+
h={h}
|
|
164
|
+
/>
|
|
165
|
+
</svg> */
|
|
166
|
+
}
|
|
146
167
|
<svg
|
|
147
168
|
className={clsx(
|
|
148
169
|
css.cssShapeSvg
|
|
@@ -164,27 +185,32 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
164
185
|
h={h}
|
|
165
186
|
/>
|
|
166
187
|
</svg>
|
|
167
|
-
<div
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
{element.title}
|
|
173
|
-
</Text>
|
|
174
|
-
{element.technology && (
|
|
175
|
-
<Text
|
|
176
|
-
component="div"
|
|
177
|
-
className={clsx(css.technology, 'likec4-element-technology')}>
|
|
178
|
-
{element.technology}
|
|
179
|
-
</Text>
|
|
188
|
+
<div
|
|
189
|
+
className={clsx(
|
|
190
|
+
css.elementDataContainer,
|
|
191
|
+
isTruthy(elementIcon) && css.hasIcon,
|
|
192
|
+
'likec4-element'
|
|
180
193
|
)}
|
|
181
|
-
|
|
194
|
+
>
|
|
195
|
+
{elementIcon}
|
|
196
|
+
<div className={clsx(css.elementTextData, 'likec4-element-main-props')}>
|
|
182
197
|
<Text
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
{element.description}
|
|
198
|
+
className={clsx(css.title, 'likec4-element-title')}>
|
|
199
|
+
{element.title}
|
|
186
200
|
</Text>
|
|
187
|
-
|
|
201
|
+
{element.technology && (
|
|
202
|
+
<Text
|
|
203
|
+
className={clsx(css.technology, 'likec4-element-technology')}>
|
|
204
|
+
{element.technology}
|
|
205
|
+
</Text>
|
|
206
|
+
)}
|
|
207
|
+
{element.description && (
|
|
208
|
+
<Text
|
|
209
|
+
className={clsx(css.description, 'likec4-element-description')}>
|
|
210
|
+
{element.description}
|
|
211
|
+
</Text>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
188
214
|
</div>
|
|
189
215
|
{isHovercards && element.links && <ElementLink element={element} />}
|
|
190
216
|
{isNavigable && <NavigateToBtn xynodeId={id} className={css.cssNavigateBtn} />}
|
|
@@ -197,3 +223,26 @@ export const ElementNodeMemo = memo<ElementNodeProps>(function ElementNode({
|
|
|
197
223
|
</>
|
|
198
224
|
)
|
|
199
225
|
}, isEqualProps)
|
|
226
|
+
|
|
227
|
+
const ElementIcon = ({ node, store }: { node: DiagramNode; store: DiagramStoreApi }) => {
|
|
228
|
+
if (!node.icon) {
|
|
229
|
+
return null
|
|
230
|
+
}
|
|
231
|
+
if (node.icon.startsWith('http://') || node.icon.startsWith('https://')) {
|
|
232
|
+
return (
|
|
233
|
+
<div className={clsx(css.elementIcon, 'likec4-element-icon')}>
|
|
234
|
+
<img src={node.icon} alt={node.title} />
|
|
235
|
+
</div>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
const RenderIcon = store.getState().renderIcon
|
|
239
|
+
const icon = RenderIcon ? <RenderIcon node={node} /> : null
|
|
240
|
+
if (!icon) {
|
|
241
|
+
return null
|
|
242
|
+
}
|
|
243
|
+
return (
|
|
244
|
+
<div className={clsx(css.elementIcon, 'likec4-element-icon')}>
|
|
245
|
+
{icon}
|
|
246
|
+
</div>
|
|
247
|
+
)
|
|
248
|
+
}
|
|
@@ -2,7 +2,7 @@ import { type ElementShape, nonexhaustive } from '@likec4/core'
|
|
|
2
2
|
import { toDomPrecision } from '../../utils'
|
|
3
3
|
import { fillElementFill, fillMixStroke } from './element.css'
|
|
4
4
|
|
|
5
|
-
function cylinderSVGPath(diameter: number, height: number, tilt = 0.
|
|
5
|
+
export function cylinderSVGPath(diameter: number, height: number, tilt = 0.065) {
|
|
6
6
|
const radius = Math.round(diameter / 2)
|
|
7
7
|
// const tiltAdjustedHeight = height * Math.cos((tilt * Math.PI) / 2)
|
|
8
8
|
const rx = radius
|
|
@@ -25,7 +25,7 @@ function cylinderSVGPath(diameter: number, height: number, tilt = 0.0725) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function queueSVGPath(width: number, height: number, tilt = 0.
|
|
28
|
+
function queueSVGPath(width: number, height: number, tilt = 0.185) {
|
|
29
29
|
const diameter = height
|
|
30
30
|
const radius = Math.round(diameter / 2)
|
|
31
31
|
const ry = radius
|