@likec4/diagram 1.16.0 → 1.17.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/package.json +7 -7
- package/src/LikeC4CustomColors.tsx +24 -21
- package/src/LikeC4Diagram.props.ts +9 -9
- package/src/LikeC4Diagram.tsx +25 -26
- package/src/StaticLikeC4Diagram.tsx +9 -5
- package/src/overlays/OverlayContext.tsx +1 -1
- package/src/overlays/Overlays.css.ts +28 -23
- package/src/overlays/Overlays.tsx +116 -64
- package/src/overlays/edge-details/xyflow/ElementNode.tsx +15 -12
- package/src/overlays/edge-details/xyflow/RelationshipEdge.tsx +7 -3
- package/src/overlays/edge-details/xyflow/styles.css.ts +22 -16
- package/src/overlays/element-details/ElementDetailsCard.css.ts +322 -0
- package/src/overlays/element-details/ElementDetailsCard.tsx +639 -0
- package/src/overlays/element-details/TabPanelRelationships.css.ts +106 -0
- package/src/overlays/element-details/TabPanelRelationships.tsx +201 -0
- package/src/overlays/element-details/TabPanelStructure.css.ts +36 -0
- package/src/overlays/element-details/TabPanelStructure.tsx +120 -0
- package/src/overlays/relationships-of/RelationshipsOverlay.tsx +185 -0
- package/src/overlays/relationships-of/RelationshipsXYFlow.tsx +179 -213
- package/src/overlays/relationships-of/_types.ts +15 -4
- package/src/overlays/relationships-of/use-layouted-relationships.ts +301 -207
- package/src/overlays/relationships-of/xyflow/CompoundNode.tsx +68 -14
- package/src/overlays/relationships-of/xyflow/ElementNode.tsx +54 -14
- package/src/overlays/relationships-of/xyflow/EmptyNode.tsx +1 -1
- package/src/overlays/relationships-of/xyflow/RelationshipEdge.tsx +51 -23
- package/src/overlays/relationships-of/xyflow/styles.css.ts +34 -27
- package/src/state/DiagramContext.tsx +15 -8
- package/src/state/diagramStore.ts +14 -6
- package/src/theme-vars.ts +9 -0
- package/src/ui/FramerMotionConfig.tsx +18 -0
- package/src/ui/notation/NotationPanel.tsx +3 -3
- package/src/xyflow/XYFlow.tsx +18 -35
- package/src/xyflow/XYFlowInner.tsx +2 -2
- package/src/xyflow/edges/EdgeLabel.tsx +3 -3
- package/src/xyflow/edges/RelationshipsDropdownMenu.tsx +25 -18
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +96 -30
- package/src/xyflow/nodes/compound/CompoundNode.tsx +149 -165
- package/src/xyflow/nodes/element/ElementDetails.css.ts +101 -0
- package/src/xyflow/nodes/element/ElementDetails.tsx +393 -0
- package/src/xyflow/nodes/element/ElementNode.tsx +110 -116
- package/src/xyflow/nodes/element/element.css.ts +38 -18
- package/src/xyflow/nodes/shared/ElementIcon.tsx +43 -0
- package/src/xyflow/nodes/shared/Toolbar.tsx +6 -6
- package/src/xyflow/{XYFlowEvents.tsx → useXYFlowEvents.ts} +26 -22
- package/src/xyflow/utils.ts +12 -3
- package/src/LikeC4CustomColors.props.ts +0 -5
- /package/src/{mantine → ui}/EnsureMantine.tsx +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/diagram",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
6
6
|
"homepage": "https://likec4.dev",
|
|
@@ -31,21 +31,21 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@dagrejs/dagre": "^1.1.4",
|
|
34
|
-
"@likec4/core": "1.
|
|
34
|
+
"@likec4/core": "1.17.0",
|
|
35
35
|
"@mantine/vanilla-extract": "7.13.4",
|
|
36
36
|
"@react-hookz/web": "^24.0.4",
|
|
37
37
|
"@tabler/icons-react": "3.17.0",
|
|
38
38
|
"@vanilla-extract/css": "^1.16.0",
|
|
39
39
|
"@vanilla-extract/css-utils": "^0.1.4",
|
|
40
40
|
"@vanilla-extract/dynamic": "^2.1.2",
|
|
41
|
-
"@xyflow/react": "12.3.
|
|
42
|
-
"@xyflow/system": "0.0.
|
|
41
|
+
"@xyflow/react": "12.3.4",
|
|
42
|
+
"@xyflow/system": "0.0.45",
|
|
43
43
|
"bezier-js": "^6.1.4",
|
|
44
44
|
"clsx": "^2.1.1",
|
|
45
45
|
"d3-shape": "^3.2.0",
|
|
46
|
-
"esm-env": "^1.
|
|
46
|
+
"esm-env": "^1.1.4",
|
|
47
47
|
"fast-equals": "^5.0.1",
|
|
48
|
-
"framer-motion": "^11.11.
|
|
48
|
+
"framer-motion": "^11.11.11",
|
|
49
49
|
"khroma": "^2.1.0",
|
|
50
50
|
"remeda": "^2.16.0",
|
|
51
51
|
"type-fest": "4.26.1",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"react-dom": "^18.3.1"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@likec4/tsconfig": "1.
|
|
61
|
+
"@likec4/tsconfig": "1.17.0",
|
|
62
62
|
"@mantine/core": "7.13.4",
|
|
63
63
|
"@mantine/hooks": "7.13.4",
|
|
64
64
|
"@types/bezier-js": "^4.1.3",
|
|
@@ -1,33 +1,36 @@
|
|
|
1
|
-
import { type ThemeColorValues } from '@likec4/core'
|
|
1
|
+
import { type CustomColorDefinitions, type ThemeColorValues } from '@likec4/core'
|
|
2
2
|
import { useMantineStyleNonce } from '@mantine/core'
|
|
3
3
|
import { deepEqual } from 'fast-equals'
|
|
4
4
|
import { memo } from 'react'
|
|
5
5
|
import { entries } from 'remeda'
|
|
6
|
-
import type { LikeC4CustomColorsProperties } from './LikeC4CustomColors.props'
|
|
7
6
|
import { vars } from './theme-vars'
|
|
8
7
|
|
|
9
8
|
type CSSVarFunction = `var(--${string})` | `var(--${string}, ${string | number} )`
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}`
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function stripCssVarReference(ref: CSSVarFunction): String {
|
|
27
|
-
const end = ref.indexOf(',')
|
|
28
|
-
return ref.substring(4, end == -1 ? ref.length - 1 : end)
|
|
29
|
-
}
|
|
10
|
+
interface LikeC4CustomColorsProperties {
|
|
11
|
+
customColors: CustomColorDefinitions
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function toStyle(name: String, colorValues: ThemeColorValues): String {
|
|
15
|
+
const rules = [
|
|
16
|
+
...entries(colorValues.elements)
|
|
17
|
+
.map(([key, value]) => `${stripCssVarReference(vars.element[key])}: ${value};`),
|
|
18
|
+
...entries(colorValues.relationships)
|
|
19
|
+
.map(([key, value]) => `${stripCssVarReference(vars.relation[key])}: ${value};`)
|
|
20
|
+
]
|
|
21
|
+
.join('\n')
|
|
30
22
|
|
|
23
|
+
return `:where([data-likec4-color=${name}]) {
|
|
24
|
+
${rules}
|
|
25
|
+
}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function stripCssVarReference(ref: CSSVarFunction): String {
|
|
29
|
+
const end = ref.indexOf(',')
|
|
30
|
+
return ref.substring(4, end == -1 ? ref.length - 1 : end)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const LikeC4CustomColors = memo<LikeC4CustomColorsProperties>(({ customColors }) => {
|
|
31
34
|
const styles = entries(customColors)
|
|
32
35
|
.map(([name, color]) => toStyle(name, color))
|
|
33
36
|
.join('\n')
|
|
@@ -118,12 +118,6 @@ export interface LikeC4DiagramProperties {
|
|
|
118
118
|
*/
|
|
119
119
|
background?: 'transparent' | 'solid' | XYBackground | undefined
|
|
120
120
|
|
|
121
|
-
/**
|
|
122
|
-
* Display hovercards with element links
|
|
123
|
-
* @default true
|
|
124
|
-
*/
|
|
125
|
-
showElementLinks?: boolean | undefined
|
|
126
|
-
|
|
127
121
|
/**
|
|
128
122
|
* Display webview with diagram title / description
|
|
129
123
|
* @default true
|
|
@@ -144,9 +138,9 @@ export interface LikeC4DiagramProperties {
|
|
|
144
138
|
|
|
145
139
|
/**
|
|
146
140
|
* Display dropdown with details on relationship's label click
|
|
147
|
-
* @default
|
|
141
|
+
* @default false
|
|
148
142
|
*/
|
|
149
|
-
|
|
143
|
+
enableRelationshipDetails?: boolean | undefined
|
|
150
144
|
|
|
151
145
|
/**
|
|
152
146
|
* If double click on a node should enable focus mode, i.e. highlight incoming/outgoing edges
|
|
@@ -154,12 +148,18 @@ export interface LikeC4DiagramProperties {
|
|
|
154
148
|
*/
|
|
155
149
|
enableFocusMode?: boolean | undefined
|
|
156
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Enable modal with element details
|
|
153
|
+
* @default false
|
|
154
|
+
*/
|
|
155
|
+
enableElementDetails?: boolean | undefined
|
|
156
|
+
|
|
157
157
|
/**
|
|
158
158
|
* Experimental feature to browse relationships
|
|
159
159
|
*
|
|
160
160
|
* @default false
|
|
161
161
|
*/
|
|
162
|
-
|
|
162
|
+
enableRelationshipBrowser?: boolean | undefined
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
165
|
* If Walkthrough for dynamic views should be enabled
|
package/src/LikeC4Diagram.tsx
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type { DiagramView } from '@likec4/core'
|
|
2
1
|
import { ReactFlowProvider as XYFlowProvider } from '@xyflow/react'
|
|
3
2
|
import clsx from 'clsx'
|
|
4
|
-
import { shallowEqual } from 'fast-equals'
|
|
5
|
-
import { domMax, LazyMotion } from 'framer-motion'
|
|
3
|
+
import { deepEqual, shallowEqual } from 'fast-equals'
|
|
6
4
|
import { memo, useEffect, useRef } from 'react'
|
|
5
|
+
import { isEmpty } from 'remeda'
|
|
7
6
|
import { rootClassName } from './globals.css'
|
|
8
7
|
import { useDiagramState } from './hooks/useDiagramState'
|
|
9
8
|
import { LikeC4CustomColors } from './LikeC4CustomColors'
|
|
10
9
|
import * as css from './LikeC4Diagram.css'
|
|
11
10
|
import { type LikeC4DiagramEventHandlers, type LikeC4DiagramProperties } from './LikeC4Diagram.props'
|
|
12
11
|
import { useLikeC4Model } from './likec4model'
|
|
13
|
-
import { EnsureMantine } from './mantine/EnsureMantine'
|
|
14
12
|
import { Overlays } from './overlays'
|
|
15
13
|
import { DiagramContextProvider } from './state/DiagramContext'
|
|
14
|
+
import { EnsureMantine } from './ui/EnsureMantine'
|
|
15
|
+
import { FramerMotionConfig } from './ui/FramerMotionConfig'
|
|
16
16
|
import { FitViewOnDiagramChange } from './xyflow/FitviewOnDiagramChange'
|
|
17
17
|
import { SelectEdgesOnNodeFocus } from './xyflow/SelectEdgesOnNodeFocus'
|
|
18
18
|
import type { XYFlowEdge, XYFlowNode } from './xyflow/types'
|
|
@@ -32,13 +32,13 @@ export function LikeC4Diagram({
|
|
|
32
32
|
nodesDraggable = !readonly,
|
|
33
33
|
background = 'dots',
|
|
34
34
|
controls = !readonly,
|
|
35
|
-
showElementLinks = true,
|
|
36
35
|
showDiagramTitle = true,
|
|
37
36
|
showNotations = true,
|
|
38
|
-
showRelationshipDetails = true,
|
|
39
37
|
enableDynamicViewWalkthrough = false,
|
|
40
38
|
enableFocusMode = false,
|
|
41
|
-
|
|
39
|
+
enableElementDetails = false,
|
|
40
|
+
enableRelationshipDetails = enableElementDetails,
|
|
41
|
+
enableRelationshipBrowser = enableRelationshipDetails,
|
|
42
42
|
initialWidth,
|
|
43
43
|
initialHeight,
|
|
44
44
|
keepAspectRatio = false,
|
|
@@ -74,43 +74,46 @@ export function LikeC4Diagram({
|
|
|
74
74
|
if (hasLikec4model) {
|
|
75
75
|
return
|
|
76
76
|
}
|
|
77
|
-
if (
|
|
77
|
+
if (enableRelationshipDetails) {
|
|
78
78
|
console.warn('Invalid showRelationshipDetails=true, requires LikeC4ModelProvider')
|
|
79
79
|
}
|
|
80
|
-
if (
|
|
81
|
-
console.warn('Invalid
|
|
80
|
+
if (enableElementDetails) {
|
|
81
|
+
console.warn('Invalid enableElementDetails=true, requires LikeC4ModelProvider')
|
|
82
|
+
}
|
|
83
|
+
if (enableRelationshipBrowser) {
|
|
84
|
+
console.warn('Invalid enableRelationshipBrowser=true, requires LikeC4ModelProvider')
|
|
82
85
|
}
|
|
83
86
|
})
|
|
84
87
|
|
|
85
88
|
return (
|
|
86
89
|
<EnsureMantine>
|
|
87
|
-
<
|
|
90
|
+
<FramerMotionConfig>
|
|
88
91
|
<XYFlowProvider
|
|
89
92
|
fitView={fitView}
|
|
90
93
|
{...initialRef.current}
|
|
91
94
|
>
|
|
92
|
-
{
|
|
95
|
+
{!isEmpty(view.customColorDefinitions) && <LikeC4CustomColors customColors={view.customColorDefinitions} />}
|
|
93
96
|
<DiagramContextProvider
|
|
94
97
|
view={view}
|
|
95
98
|
keepAspectRatio={keepAspectRatio}
|
|
96
99
|
className={clsx(rootClassName, className)}
|
|
97
100
|
readonly={readonly}
|
|
101
|
+
experimentalEdgeEditing={!readonly && experimentalEdgeEditing}
|
|
98
102
|
pannable={pannable}
|
|
99
103
|
zoomable={zoomable}
|
|
104
|
+
nodesDraggable={nodesDraggable}
|
|
105
|
+
nodesSelectable={nodesSelectable || enableFocusMode}
|
|
100
106
|
hasLikeC4Model={hasLikec4model}
|
|
101
107
|
fitViewEnabled={fitView}
|
|
102
108
|
fitViewPadding={fitViewPadding}
|
|
103
109
|
controls={controls}
|
|
104
|
-
showElementLinks={showElementLinks}
|
|
105
110
|
showNavigationButtons={showNavigationButtons && !!onNavigateTo}
|
|
106
111
|
showNotations={showNotations}
|
|
107
|
-
showRelationshipDetails={showRelationshipDetails && hasLikec4model}
|
|
108
|
-
nodesDraggable={nodesDraggable}
|
|
109
|
-
nodesSelectable={nodesSelectable}
|
|
110
|
-
experimentalEdgeEditing={experimentalEdgeEditing}
|
|
111
|
-
enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
|
|
112
112
|
enableFocusMode={enableFocusMode}
|
|
113
|
-
|
|
113
|
+
enableRelationshipDetails={enableRelationshipDetails && hasLikec4model}
|
|
114
|
+
enableElementDetails={enableElementDetails && hasLikec4model}
|
|
115
|
+
enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
|
|
116
|
+
enableRelationshipBrowser={enableRelationshipBrowser && hasLikec4model}
|
|
114
117
|
// Apply where filter only in readonly mode
|
|
115
118
|
whereFilter={readonly ? (where ?? null) : null}
|
|
116
119
|
renderIcon={renderIcon ?? null}
|
|
@@ -134,7 +137,7 @@ export function LikeC4Diagram({
|
|
|
134
137
|
/>
|
|
135
138
|
</DiagramContextProvider>
|
|
136
139
|
</XYFlowProvider>
|
|
137
|
-
</
|
|
140
|
+
</FramerMotionConfig>
|
|
138
141
|
</EnsureMantine>
|
|
139
142
|
)
|
|
140
143
|
}
|
|
@@ -159,7 +162,7 @@ const LikeC4DiagramInnerMemo = /* @__PURE__ */ memo<LikeC4DiagramInnerProps>(fun
|
|
|
159
162
|
pannable: s.pannable,
|
|
160
163
|
fitView: s.fitViewEnabled,
|
|
161
164
|
enableFocusMode: s.enableFocusMode,
|
|
162
|
-
enableOverlays: s.hasLikeC4Model && (s.
|
|
165
|
+
enableOverlays: s.hasLikeC4Model && (s.enableRelationshipBrowser || s.enableElementDetails)
|
|
163
166
|
}))
|
|
164
167
|
|
|
165
168
|
return (
|
|
@@ -188,9 +191,5 @@ const LikeC4DiagramInnerMemo = /* @__PURE__ */ memo<LikeC4DiagramInnerProps>(fun
|
|
|
188
191
|
)}
|
|
189
192
|
</>
|
|
190
193
|
)
|
|
191
|
-
},
|
|
194
|
+
}, deepEqual)
|
|
192
195
|
LikeC4DiagramInnerMemo.displayName = 'LikeC4DiagramInnerMemo'
|
|
193
|
-
|
|
194
|
-
function customColorsDefined(view: DiagramView) {
|
|
195
|
-
return Object.keys(view.customColorDefinitions).length > 0
|
|
196
|
-
}
|
|
@@ -9,7 +9,9 @@ export type StaticLikeC4DiagramProps = Pick<
|
|
|
9
9
|
| 'fitView'
|
|
10
10
|
| 'fitViewPadding'
|
|
11
11
|
| 'background'
|
|
12
|
-
| '
|
|
12
|
+
| 'enableElementDetails'
|
|
13
|
+
| 'enableRelationshipDetails'
|
|
14
|
+
| 'enableRelationshipBrowser'
|
|
13
15
|
| 'initialWidth'
|
|
14
16
|
| 'initialHeight'
|
|
15
17
|
| 'renderIcon'
|
|
@@ -20,7 +22,9 @@ export function StaticLikeC4Diagram({
|
|
|
20
22
|
view,
|
|
21
23
|
fitView = true,
|
|
22
24
|
fitViewPadding = 0,
|
|
23
|
-
|
|
25
|
+
enableElementDetails = false,
|
|
26
|
+
enableRelationshipDetails = false,
|
|
27
|
+
enableRelationshipBrowser = enableRelationshipDetails,
|
|
24
28
|
background = 'transparent',
|
|
25
29
|
...rest
|
|
26
30
|
}: StaticLikeC4DiagramProps) {
|
|
@@ -34,12 +38,12 @@ export function StaticLikeC4Diagram({
|
|
|
34
38
|
zoomable={false}
|
|
35
39
|
controls={false}
|
|
36
40
|
background={background}
|
|
37
|
-
showElementLinks={showElementLinks}
|
|
38
41
|
showDiagramTitle={false}
|
|
39
42
|
showNotations={false}
|
|
40
|
-
|
|
43
|
+
enableElementDetails={enableElementDetails}
|
|
44
|
+
enableRelationshipDetails={enableRelationshipDetails}
|
|
45
|
+
enableRelationshipBrowser={enableRelationshipBrowser}
|
|
41
46
|
enableDynamicViewWalkthrough={false}
|
|
42
|
-
enableRelationshipsBrowser={false}
|
|
43
47
|
experimentalEdgeEditing={false}
|
|
44
48
|
enableFocusMode={false}
|
|
45
49
|
nodesSelectable={false}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createVar, globalStyle,
|
|
2
|
-
import { mantine, vars, whereDark, xyvars } from '../theme-vars'
|
|
1
|
+
import { createVar, globalStyle, style } from '@vanilla-extract/css'
|
|
2
|
+
import { mantine, vars, whereDark, whereLight, xyvars } from '../theme-vars'
|
|
3
3
|
|
|
4
|
-
const mixColor = createVar('mix-color')
|
|
4
|
+
export const mixColor = createVar('mix-color')
|
|
5
5
|
export const container = style({
|
|
6
6
|
position: 'absolute',
|
|
7
7
|
top: 0,
|
|
@@ -16,41 +16,46 @@ export const container = style({
|
|
|
16
16
|
isolation: 'isolate',
|
|
17
17
|
WebkitBackdropFilter: 'blur(var(--backdrop-blur))',
|
|
18
18
|
backdropFilter: 'blur(var(--backdrop-blur))',
|
|
19
|
-
backgroundColor: `
|
|
19
|
+
backgroundColor: `rgb(200 200 200 / var(--backdrop-opacity))`,
|
|
20
20
|
vars: {
|
|
21
|
-
'--backdrop-blur': '
|
|
22
|
-
'--backdrop-opacity': '
|
|
23
|
-
[mixColor]: `black
|
|
24
|
-
[xyvars.edge.stroke]: vars.relation.lineColor,
|
|
25
|
-
[xyvars.edge.strokeSelected]: `color-mix(in srgb, ${vars.relation.lineColor}, ${mixColor} 35%)`,
|
|
26
|
-
[xyvars.edge.labelColor]: `color-mix(in srgb, ${vars.relation.labelColor}, rgba(255 255 255 / 0.85) 50%)`,
|
|
27
|
-
[xyvars.edge.labelBgColor]: `color-mix(in srgb, ${vars.relation.labelBgColor}, transparent 30%)`
|
|
21
|
+
'--backdrop-blur': '0px',
|
|
22
|
+
'--backdrop-opacity': '0%',
|
|
23
|
+
[mixColor]: `black`
|
|
28
24
|
},
|
|
29
25
|
selectors: {
|
|
30
26
|
[`${whereDark} &`]: {
|
|
27
|
+
backgroundColor: `rgb(36 36 36 / var(--backdrop-opacity))`,
|
|
31
28
|
vars: {
|
|
32
|
-
[mixColor]: `white
|
|
33
|
-
[xyvars.edge.labelColor]: vars.relation.labelColor,
|
|
34
|
-
[xyvars.edge.labelBgColor]: `color-mix(in srgb, ${vars.relation.labelBgColor}, transparent 50%)`
|
|
29
|
+
[mixColor]: `white`
|
|
35
30
|
}
|
|
36
31
|
}
|
|
37
32
|
}
|
|
38
33
|
})
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
export const cssReactflowMarker = style({})
|
|
36
|
+
globalStyle(`.react-flow${cssReactflowMarker}`, {
|
|
37
|
+
vars: {
|
|
38
|
+
// [xyvars.background.color]: vars.likec4.background.color,
|
|
39
|
+
[xyvars.background.pattern.color]: vars.likec4.background.pattern.color
|
|
40
|
+
}
|
|
42
41
|
})
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
|
|
43
|
+
globalStyle(`${whereDark} .react-flow${cssReactflowMarker}`, {
|
|
44
|
+
vars: {
|
|
45
|
+
[xyvars.edge.labelColor]: vars.relation.labelColor,
|
|
46
|
+
[xyvars.edge.labelBgColor]: `color-mix(in srgb, ${vars.relation.labelBgColor}, transparent 50%)`
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
globalStyle(`:where(${cssReactflowMarker}) .react-flow__attribution`, {
|
|
50
|
+
display: 'none'
|
|
45
51
|
})
|
|
46
|
-
globalStyle(`:where(${
|
|
47
|
-
|
|
52
|
+
globalStyle(`:where(${cssReactflowMarker}) .react-flow__edge-text`, {
|
|
53
|
+
fontSize: 16
|
|
48
54
|
})
|
|
49
|
-
|
|
50
|
-
globalStyle(`${container} .react-flow__edges > svg`, {
|
|
55
|
+
globalStyle(`${cssReactflowMarker} .react-flow__edges > svg`, {
|
|
51
56
|
mixBlendMode: 'plus-lighter'
|
|
52
57
|
})
|
|
53
|
-
globalStyle(
|
|
58
|
+
globalStyle(`${whereLight} ${cssReactflowMarker} .react-flow__edges > svg`, {
|
|
54
59
|
mixBlendMode: 'screen'
|
|
55
60
|
})
|
|
56
61
|
|
|
@@ -1,96 +1,118 @@
|
|
|
1
|
-
import { ActionIcon, Box, FocusTrap } from '@mantine/core'
|
|
1
|
+
import { ActionIcon, Box, FocusTrap, RemoveScroll } from '@mantine/core'
|
|
2
2
|
import { useHotkeys } from '@mantine/hooks'
|
|
3
3
|
import { IconX } from '@tabler/icons-react'
|
|
4
4
|
import { ReactFlowProvider as XYFlowProvider } from '@xyflow/react'
|
|
5
5
|
import { AnimatePresence, m } from 'framer-motion'
|
|
6
|
-
import { memo, useMemo } from 'react'
|
|
6
|
+
import { memo, type PropsWithChildren, useEffect, useMemo, useRef } from 'react'
|
|
7
|
+
import { isNullish } from 'remeda'
|
|
7
8
|
import { type DiagramState, useDiagramState, useDiagramStoreApi } from '../hooks/useDiagramState'
|
|
8
9
|
import { EdgeDetailsXYFlow } from './edge-details/EdgeDetailsXYFlow'
|
|
9
|
-
import {
|
|
10
|
+
import { ElementDetailsCard } from './element-details/ElementDetailsCard'
|
|
11
|
+
import { OverlayContext, useOverlayDialog } from './OverlayContext'
|
|
10
12
|
import * as css from './Overlays.css'
|
|
11
|
-
import {
|
|
13
|
+
import { RelationshipsOverlay } from './relationships-of/RelationshipsOverlay'
|
|
14
|
+
|
|
12
15
|
export const Overlays = memo(() => {
|
|
13
16
|
const diagramStore = useDiagramStoreApi()
|
|
14
|
-
const
|
|
17
|
+
const {
|
|
18
|
+
activeOverlay,
|
|
19
|
+
viewId
|
|
20
|
+
} = useDiagramState(s => ({
|
|
21
|
+
activeOverlay: s.activeOverlay,
|
|
22
|
+
viewId: s.view.id
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
const onCloseCbRef = useRef<(() => void)>()
|
|
15
26
|
|
|
16
27
|
const ctxValue = useMemo(() => ({
|
|
17
28
|
openOverlay: ((overlay) => {
|
|
18
29
|
diagramStore.getState().openOverlay(overlay)
|
|
19
30
|
}) as DiagramState['openOverlay'],
|
|
20
|
-
close: () => {
|
|
31
|
+
close: (cb?: () => void) => {
|
|
32
|
+
onCloseCbRef.current = cb
|
|
21
33
|
diagramStore.getState().closeOverlay()
|
|
22
34
|
}
|
|
23
35
|
}), [diagramStore])
|
|
24
36
|
|
|
37
|
+
const onExitComplete = () => {
|
|
38
|
+
onCloseCbRef.current?.()
|
|
39
|
+
onCloseCbRef.current = undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
const isActive = !!activeOverlay
|
|
26
43
|
useHotkeys(
|
|
27
44
|
isActive
|
|
28
45
|
? [
|
|
29
46
|
['Escape', (e) => {
|
|
30
|
-
e.
|
|
31
|
-
|
|
47
|
+
e.stopPropagation()
|
|
48
|
+
ctxValue.close()
|
|
32
49
|
}, { preventDefault: true }]
|
|
33
50
|
]
|
|
34
51
|
: []
|
|
35
52
|
)
|
|
53
|
+
|
|
36
54
|
return (
|
|
37
55
|
<OverlayContext.Provider value={ctxValue}>
|
|
38
|
-
<AnimatePresence>
|
|
39
|
-
{activeOverlay && (
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
56
|
+
<AnimatePresence initial={false} key={viewId} onExitComplete={onExitComplete}>
|
|
57
|
+
{activeOverlay?.elementDetails && (
|
|
58
|
+
<ElementDetailsCard key={'details card'} fqn={activeOverlay.elementDetails} />
|
|
59
|
+
)}
|
|
60
|
+
</AnimatePresence>
|
|
61
|
+
<AnimatePresence initial={false} onExitComplete={onExitComplete}>
|
|
62
|
+
{activeOverlay && isNullish(activeOverlay.elementDetails) && (
|
|
63
|
+
<RemoveScroll forwardProps>
|
|
64
|
+
<Box
|
|
65
|
+
component={m.div}
|
|
66
|
+
className={css.container}
|
|
67
|
+
data-likec4-color="gray"
|
|
68
|
+
initial={{
|
|
69
|
+
'--backdrop-blur': '0px',
|
|
70
|
+
'--backdrop-opacity': '0%',
|
|
71
|
+
opacity: 0,
|
|
72
|
+
translateY: -15
|
|
73
|
+
}}
|
|
74
|
+
animate={{
|
|
75
|
+
'--backdrop-blur': '10px',
|
|
76
|
+
'--backdrop-opacity': '70%',
|
|
77
|
+
opacity: 1,
|
|
78
|
+
translateY: 0
|
|
79
|
+
}}
|
|
80
|
+
exit={{
|
|
81
|
+
'--backdrop-blur': '1px',
|
|
82
|
+
'--backdrop-opacity': '0%',
|
|
83
|
+
translateY: -5,
|
|
84
|
+
opacity: 0,
|
|
85
|
+
transition: {
|
|
86
|
+
duration: .2
|
|
87
|
+
}
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
<FocusTrap>
|
|
91
|
+
{activeOverlay.relationshipsOf && <RelationshipsOverlay subjectId={activeOverlay.relationshipsOf} />}
|
|
92
|
+
{activeOverlay.edgeDetails && (
|
|
93
|
+
<XYFlowProvider
|
|
94
|
+
defaultNodes={[]}
|
|
95
|
+
defaultEdges={[]}>
|
|
96
|
+
<EdgeDetailsXYFlow edgeId={activeOverlay.edgeDetails} />
|
|
97
|
+
</XYFlowProvider>
|
|
98
|
+
)}
|
|
99
|
+
<Box pos={'absolute'} top={'1rem'} right={'1rem'}>
|
|
100
|
+
<ActionIcon
|
|
101
|
+
variant="default"
|
|
102
|
+
// color="gray"
|
|
103
|
+
size={'lg'}
|
|
104
|
+
// data-autofocus
|
|
105
|
+
// autoFocus
|
|
106
|
+
onClick={(e) => {
|
|
107
|
+
e.stopPropagation()
|
|
108
|
+
ctxValue.close()
|
|
109
|
+
}}>
|
|
110
|
+
<IconX />
|
|
111
|
+
</ActionIcon>
|
|
112
|
+
</Box>
|
|
113
|
+
</FocusTrap>
|
|
114
|
+
</Box>
|
|
115
|
+
</RemoveScroll>
|
|
94
116
|
)}
|
|
95
117
|
</AnimatePresence>
|
|
96
118
|
</OverlayContext.Provider>
|
|
@@ -208,3 +230,33 @@ export const Overlays = memo(() => {
|
|
|
208
230
|
// </Box>
|
|
209
231
|
// )
|
|
210
232
|
// }
|
|
233
|
+
const OverlayDialog = ({ children }: PropsWithChildren) => {
|
|
234
|
+
const overlay = useOverlayDialog()
|
|
235
|
+
const ref = useRef<HTMLDialogElement>(null)
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
ref.current?.showModal()
|
|
238
|
+
}, [])
|
|
239
|
+
return (
|
|
240
|
+
<m.dialog
|
|
241
|
+
ref={ref}
|
|
242
|
+
exit={{
|
|
243
|
+
opacity: 0,
|
|
244
|
+
transition: {
|
|
245
|
+
duration: .15
|
|
246
|
+
}
|
|
247
|
+
}}
|
|
248
|
+
onClick={e => {
|
|
249
|
+
if ((e.target as any)?.nodeName?.toUpperCase() === 'DIALOG') {
|
|
250
|
+
e.stopPropagation()
|
|
251
|
+
ref.current?.close()
|
|
252
|
+
}
|
|
253
|
+
}}
|
|
254
|
+
onClose={e => {
|
|
255
|
+
e.stopPropagation()
|
|
256
|
+
overlay.close()
|
|
257
|
+
}}
|
|
258
|
+
>
|
|
259
|
+
{children}
|
|
260
|
+
</m.dialog>
|
|
261
|
+
)
|
|
262
|
+
}
|
|
@@ -27,6 +27,7 @@ type ElementNodeProps = NodeProps<XYFlowTypes.ElementNode>
|
|
|
27
27
|
function selector(s: DiagramState) {
|
|
28
28
|
return {
|
|
29
29
|
currentViewId: s.view.id,
|
|
30
|
+
enableRelationshipBrowser: s.enableRelationshipBrowser,
|
|
30
31
|
onNavigateTo: s.onNavigateTo,
|
|
31
32
|
onOpenSource: s.onOpenSourceElement
|
|
32
33
|
}
|
|
@@ -47,7 +48,8 @@ export function ElementNode({
|
|
|
47
48
|
const {
|
|
48
49
|
currentViewId,
|
|
49
50
|
onNavigateTo,
|
|
50
|
-
onOpenSource
|
|
51
|
+
onOpenSource,
|
|
52
|
+
enableRelationshipBrowser
|
|
51
53
|
} = useDiagramState(selector)
|
|
52
54
|
return (
|
|
53
55
|
<>
|
|
@@ -97,21 +99,22 @@ export function ElementNode({
|
|
|
97
99
|
<Action
|
|
98
100
|
onClick={(event) => {
|
|
99
101
|
event.stopPropagation()
|
|
100
|
-
|
|
101
|
-
overlay.close()
|
|
102
|
+
overlay.close(() => onNavigateTo(navigateTo))
|
|
102
103
|
}}>
|
|
103
104
|
<IconZoomScan stroke={1.8} style={{ width: '75%' }} />
|
|
104
105
|
</Action>
|
|
105
106
|
)}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
event
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
{enableRelationshipBrowser && (
|
|
108
|
+
<Action
|
|
109
|
+
onClick={(event) => {
|
|
110
|
+
event.stopPropagation()
|
|
111
|
+
overlay.openOverlay({
|
|
112
|
+
relationshipsOf: data.fqn
|
|
113
|
+
})
|
|
114
|
+
}}>
|
|
115
|
+
<IconTransform stroke={1.8} style={{ width: '72%' }} />
|
|
116
|
+
</Action>
|
|
117
|
+
)}
|
|
115
118
|
{onOpenSource && (
|
|
116
119
|
<Action
|
|
117
120
|
onClick={(event) => {
|