@likec4/diagram 1.16.0 → 1.17.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.
Files changed (52) hide show
  1. package/package.json +22 -18
  2. package/src/LikeC4CustomColors.tsx +24 -21
  3. package/src/LikeC4Diagram.props.ts +15 -9
  4. package/src/LikeC4Diagram.tsx +33 -27
  5. package/src/LikeC4Search.tsx +146 -0
  6. package/src/StaticLikeC4Diagram.tsx +10 -5
  7. package/src/overlays/OverlayContext.tsx +1 -1
  8. package/src/overlays/Overlays.css.ts +28 -23
  9. package/src/overlays/Overlays.tsx +116 -64
  10. package/src/overlays/edge-details/xyflow/ElementNode.tsx +15 -12
  11. package/src/overlays/edge-details/xyflow/RelationshipEdge.tsx +7 -3
  12. package/src/overlays/edge-details/xyflow/styles.css.ts +22 -16
  13. package/src/overlays/element-details/ElementDetailsCard.css.ts +322 -0
  14. package/src/overlays/element-details/ElementDetailsCard.tsx +639 -0
  15. package/src/overlays/element-details/TabPanelRelationships.css.ts +106 -0
  16. package/src/overlays/element-details/TabPanelRelationships.tsx +201 -0
  17. package/src/overlays/element-details/TabPanelStructure.css.ts +36 -0
  18. package/src/overlays/element-details/TabPanelStructure.tsx +120 -0
  19. package/src/overlays/relationships-of/RelationshipsOverlay.tsx +185 -0
  20. package/src/overlays/relationships-of/RelationshipsXYFlow.tsx +179 -213
  21. package/src/overlays/relationships-of/_types.ts +15 -4
  22. package/src/overlays/relationships-of/use-layouted-relationships.ts +301 -207
  23. package/src/overlays/relationships-of/xyflow/CompoundNode.tsx +68 -14
  24. package/src/overlays/relationships-of/xyflow/ElementNode.tsx +54 -14
  25. package/src/overlays/relationships-of/xyflow/EmptyNode.tsx +1 -1
  26. package/src/overlays/relationships-of/xyflow/RelationshipEdge.tsx +51 -23
  27. package/src/overlays/relationships-of/xyflow/styles.css.ts +34 -27
  28. package/src/state/DiagramContext.tsx +15 -8
  29. package/src/state/aligners.ts +473 -0
  30. package/src/state/diagramStore.layout.ts +67 -0
  31. package/src/state/diagramStore.ts +18 -73
  32. package/src/theme-vars.ts +9 -0
  33. package/src/ui/FramerMotionConfig.tsx +18 -0
  34. package/src/ui/notation/NotationPanel.tsx +3 -3
  35. package/src/ui/top-left/ManualLayoutToolsButton.tsx +15 -0
  36. package/src/ui/top-left/TopLeftPanel.tsx +14 -1
  37. package/src/xyflow/XYFlow.tsx +18 -35
  38. package/src/xyflow/XYFlowInner.tsx +2 -2
  39. package/src/xyflow/edges/EdgeLabel.tsx +3 -3
  40. package/src/xyflow/edges/RelationshipsDropdownMenu.tsx +25 -18
  41. package/src/xyflow/nodes/compound/CompoundNode.css.ts +96 -30
  42. package/src/xyflow/nodes/compound/CompoundNode.tsx +149 -165
  43. package/src/xyflow/nodes/element/ElementDetails.css.ts +101 -0
  44. package/src/xyflow/nodes/element/ElementDetails.tsx +393 -0
  45. package/src/xyflow/nodes/element/ElementNode.tsx +165 -133
  46. package/src/xyflow/nodes/element/element.css.ts +46 -17
  47. package/src/xyflow/nodes/shared/ElementIcon.tsx +43 -0
  48. package/src/xyflow/nodes/shared/Toolbar.tsx +6 -6
  49. package/src/xyflow/{XYFlowEvents.tsx → useXYFlowEvents.ts} +26 -22
  50. package/src/xyflow/utils.ts +12 -3
  51. package/src/LikeC4CustomColors.props.ts +0 -5
  52. /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.16.0",
3
+ "version": "1.17.1",
4
4
  "license": "MIT",
5
5
  "bugs": "https://github.com/likec4/likec4/issues",
6
6
  "homepage": "https://likec4.dev",
@@ -23,44 +23,47 @@
23
23
  "exports": {
24
24
  ".": "./src/index.ts"
25
25
  },
26
- "packageManager": "yarn@4.5.1",
26
+ "packageManager": "yarn@4.5.3",
27
27
  "scripts": {
28
28
  "typecheck": "tsc --noEmit",
29
29
  "lint": "run -T eslint src/ --fix",
30
- "clean": "rm -r -f -g 'dist/*' .tsbuildinfo"
30
+ "clean": "rm -r -f -g 'dist/*' .tsbuildinfo",
31
+ "test": "vitest run --no-isolate"
31
32
  },
32
33
  "dependencies": {
33
34
  "@dagrejs/dagre": "^1.1.4",
34
- "@likec4/core": "1.16.0",
35
- "@mantine/vanilla-extract": "7.13.4",
35
+ "@likec4/core": "1.17.1",
36
+ "@mantine/vanilla-extract": "7.14.1",
36
37
  "@react-hookz/web": "^24.0.4",
37
38
  "@tabler/icons-react": "3.17.0",
38
- "@vanilla-extract/css": "^1.16.0",
39
+ "@vanilla-extract/css": "^1.16.1",
39
40
  "@vanilla-extract/css-utils": "^0.1.4",
40
41
  "@vanilla-extract/dynamic": "^2.1.2",
41
- "@xyflow/react": "12.3.2",
42
- "@xyflow/system": "0.0.43",
42
+ "@xyflow/react": "12.3.5",
43
+ "@xyflow/system": "0.0.46",
43
44
  "bezier-js": "^6.1.4",
44
45
  "clsx": "^2.1.1",
45
46
  "d3-shape": "^3.2.0",
46
- "esm-env": "^1.0.0",
47
+ "esm-env": "^1.1.4",
47
48
  "fast-equals": "^5.0.1",
48
- "framer-motion": "^11.11.10",
49
+ "framer-motion": "^11.11.17",
49
50
  "khroma": "^2.1.0",
50
- "remeda": "^2.16.0",
51
- "type-fest": "4.26.1",
51
+ "remeda": "^2.17.3",
52
+ "type-fest": "4.27.1",
52
53
  "zustand": "^4.5.5"
53
54
  },
54
55
  "peerDependencies": {
55
- "@mantine/core": "^7.13.4",
56
- "@mantine/hooks": "^7.13.4",
56
+ "@mantine/core": "^7.14.1",
57
+ "@mantine/hooks": "^7.14.1",
58
+ "@mantine/spotlight": "^7.14.1",
57
59
  "react": "^18.3.1",
58
60
  "react-dom": "^18.3.1"
59
61
  },
60
62
  "devDependencies": {
61
- "@likec4/tsconfig": "1.16.0",
62
- "@mantine/core": "7.13.4",
63
- "@mantine/hooks": "7.13.4",
63
+ "@likec4/tsconfig": "1.17.1",
64
+ "@mantine/core": "7.14.1",
65
+ "@mantine/hooks": "7.14.1",
66
+ "@mantine/spotlight": "7.14.1",
64
67
  "@types/bezier-js": "^4.1.3",
65
68
  "@types/d3-shape": "^3.1.6",
66
69
  "@types/react": "^18.3.11",
@@ -68,6 +71,7 @@
68
71
  "execa": "^9.3.1",
69
72
  "react": "^18.3.1",
70
73
  "react-dom": "^18.3.1",
71
- "typescript": "^5.6.3"
74
+ "typescript": "^5.7.2",
75
+ "vitest": "^2.1.5"
72
76
  }
73
77
  }
@@ -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
- export const LikeC4CustomColors = memo<LikeC4CustomColorsProperties>(({ customColors }) => {
12
- function toStyle(name: String, colorValues: ThemeColorValues): String {
13
- const rules = new Array<String>(
14
- ...entries(colorValues.elements).map(([key, value]) => `${stripCssVarReference(vars.element[key])}: ${value};`),
15
- ...entries(colorValues.relationships).map(([key, value]) =>
16
- `${stripCssVarReference(vars.relation[key])}: ${value};`
17
- )
18
- )
19
- .join('\n')
20
-
21
- return `:where([data-likec4-color=${name}]) {
22
- ${rules}
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 true
141
+ * @default false
148
142
  */
149
- showRelationshipDetails?: boolean | undefined
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,24 @@ export interface LikeC4DiagramProperties {
154
148
  */
155
149
  enableFocusMode?: boolean | undefined
156
150
 
151
+ /**
152
+ * Enable search popup for elements and views
153
+ * @default true
154
+ */
155
+ enableSearch?: boolean | undefined
156
+
157
+ /**
158
+ * Enable modal with element details
159
+ * @default false
160
+ */
161
+ enableElementDetails?: boolean | undefined
162
+
157
163
  /**
158
164
  * Experimental feature to browse relationships
159
165
  *
160
166
  * @default false
161
167
  */
162
- enableRelationshipsBrowser?: boolean | undefined
168
+ enableRelationshipBrowser?: boolean | undefined
163
169
 
164
170
  /**
165
171
  * If Walkthrough for dynamic views should be enabled
@@ -1,18 +1,19 @@
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 } 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'
12
+ import { LikeC4Search } from './LikeC4Search'
14
13
  import { Overlays } from './overlays'
15
14
  import { DiagramContextProvider } from './state/DiagramContext'
15
+ import { EnsureMantine } from './ui/EnsureMantine'
16
+ import { FramerMotionConfig } from './ui/FramerMotionConfig'
16
17
  import { FitViewOnDiagramChange } from './xyflow/FitviewOnDiagramChange'
17
18
  import { SelectEdgesOnNodeFocus } from './xyflow/SelectEdgesOnNodeFocus'
18
19
  import type { XYFlowEdge, XYFlowNode } from './xyflow/types'
@@ -32,13 +33,14 @@ export function LikeC4Diagram({
32
33
  nodesDraggable = !readonly,
33
34
  background = 'dots',
34
35
  controls = !readonly,
35
- showElementLinks = true,
36
36
  showDiagramTitle = true,
37
37
  showNotations = true,
38
- showRelationshipDetails = true,
39
38
  enableDynamicViewWalkthrough = false,
40
39
  enableFocusMode = false,
41
- enableRelationshipsBrowser = false,
40
+ enableElementDetails = false,
41
+ enableRelationshipDetails = enableElementDetails,
42
+ enableRelationshipBrowser = enableRelationshipDetails,
43
+ enableSearch = true,
42
44
  initialWidth,
43
45
  initialHeight,
44
46
  keepAspectRatio = false,
@@ -74,43 +76,47 @@ export function LikeC4Diagram({
74
76
  if (hasLikec4model) {
75
77
  return
76
78
  }
77
- if (showRelationshipDetails) {
79
+ if (enableRelationshipDetails) {
78
80
  console.warn('Invalid showRelationshipDetails=true, requires LikeC4ModelProvider')
79
81
  }
80
- if (enableRelationshipsBrowser) {
81
- console.warn('Invalid enableRelationshipsBrowser=true, requires LikeC4ModelProvider')
82
+ if (enableElementDetails) {
83
+ console.warn('Invalid enableElementDetails=true, requires LikeC4ModelProvider')
84
+ }
85
+ if (enableRelationshipBrowser) {
86
+ console.warn('Invalid enableRelationshipBrowser=true, requires LikeC4ModelProvider')
82
87
  }
83
88
  })
84
89
 
85
90
  return (
86
91
  <EnsureMantine>
87
- <LazyMotion features={domMax} strict>
92
+ <FramerMotionConfig>
88
93
  <XYFlowProvider
89
94
  fitView={fitView}
90
95
  {...initialRef.current}
91
96
  >
92
- {customColorsDefined(view) && <LikeC4CustomColors customColors={view.customColorDefinitions} />}
97
+ {!isEmpty(view.customColorDefinitions) && <LikeC4CustomColors customColors={view.customColorDefinitions} />}
93
98
  <DiagramContextProvider
94
99
  view={view}
95
100
  keepAspectRatio={keepAspectRatio}
96
101
  className={clsx(rootClassName, className)}
97
102
  readonly={readonly}
103
+ experimentalEdgeEditing={!readonly && experimentalEdgeEditing}
98
104
  pannable={pannable}
99
105
  zoomable={zoomable}
106
+ nodesDraggable={nodesDraggable}
107
+ nodesSelectable={nodesSelectable || enableFocusMode}
100
108
  hasLikeC4Model={hasLikec4model}
101
109
  fitViewEnabled={fitView}
102
110
  fitViewPadding={fitViewPadding}
103
111
  controls={controls}
104
- showElementLinks={showElementLinks}
105
112
  showNavigationButtons={showNavigationButtons && !!onNavigateTo}
106
113
  showNotations={showNotations}
107
- showRelationshipDetails={showRelationshipDetails && hasLikec4model}
108
- nodesDraggable={nodesDraggable}
109
- nodesSelectable={nodesSelectable}
110
- experimentalEdgeEditing={experimentalEdgeEditing}
111
- enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
112
114
  enableFocusMode={enableFocusMode}
113
- enableRelationshipsBrowser={enableRelationshipsBrowser && hasLikec4model}
115
+ enableRelationshipDetails={enableRelationshipDetails && hasLikec4model}
116
+ enableSearch={hasLikec4model && enableSearch}
117
+ enableElementDetails={enableElementDetails && hasLikec4model}
118
+ enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
119
+ enableRelationshipBrowser={enableRelationshipBrowser && hasLikec4model}
114
120
  // Apply where filter only in readonly mode
115
121
  whereFilter={readonly ? (where ?? null) : null}
116
122
  renderIcon={renderIcon ?? null}
@@ -131,10 +137,11 @@ export function LikeC4Diagram({
131
137
  <LikeC4DiagramInnerMemo
132
138
  background={background}
133
139
  showDiagramTitle={showDiagramTitle}
140
+ enableSearch={hasLikec4model && enableSearch}
134
141
  />
135
142
  </DiagramContextProvider>
136
143
  </XYFlowProvider>
137
- </LazyMotion>
144
+ </FramerMotionConfig>
138
145
  </EnsureMantine>
139
146
  )
140
147
  }
@@ -143,10 +150,12 @@ LikeC4Diagram.displayName = 'LikeC4Diagram'
143
150
  type LikeC4DiagramInnerProps = {
144
151
  background: NonNullable<LikeC4DiagramProperties['background']>
145
152
  showDiagramTitle: boolean
153
+ enableSearch: boolean
146
154
  }
147
155
  const LikeC4DiagramInnerMemo = /* @__PURE__ */ memo<LikeC4DiagramInnerProps>(function LikeC4DiagramInner({
148
156
  background,
149
- showDiagramTitle
157
+ showDiagramTitle,
158
+ enableSearch
150
159
  }) {
151
160
  const {
152
161
  isInitialized,
@@ -159,7 +168,7 @@ const LikeC4DiagramInnerMemo = /* @__PURE__ */ memo<LikeC4DiagramInnerProps>(fun
159
168
  pannable: s.pannable,
160
169
  fitView: s.fitViewEnabled,
161
170
  enableFocusMode: s.enableFocusMode,
162
- enableOverlays: s.hasLikeC4Model && (s.enableRelationshipsBrowser || s.showRelationshipDetails)
171
+ enableOverlays: s.hasLikeC4Model && (s.enableRelationshipBrowser || s.enableElementDetails)
163
172
  }))
164
173
 
165
174
  return (
@@ -180,6 +189,7 @@ const LikeC4DiagramInnerMemo = /* @__PURE__ */ memo<LikeC4DiagramInnerProps>(fun
180
189
  />
181
190
  </XYFlow>
182
191
  {enableOverlays && <Overlays />}
192
+ {enableSearch && <LikeC4Search />}
183
193
  {isInitialized && (
184
194
  <>
185
195
  {fitView && <FitViewOnDiagramChange />}
@@ -188,9 +198,5 @@ const LikeC4DiagramInnerMemo = /* @__PURE__ */ memo<LikeC4DiagramInnerProps>(fun
188
198
  )}
189
199
  </>
190
200
  )
191
- }, shallowEqual)
201
+ }, deepEqual)
192
202
  LikeC4DiagramInnerMemo.displayName = 'LikeC4DiagramInnerMemo'
193
-
194
- function customColorsDefined(view: DiagramView) {
195
- return Object.keys(view.customColorDefinitions).length > 0
196
- }
@@ -0,0 +1,146 @@
1
+ import type { DiagramView } from '@likec4/core'
2
+ import { Spotlight, SpotlightActionsGroup, type SpotlightActionData } from '@mantine/spotlight'
3
+ import { IconRectangularPrism, IconSitemap, IconSearch } from '@tabler/icons-react'
4
+ import { memo, useMemo, useState } from 'react'
5
+ import { filter, map, pipe } from 'remeda'
6
+ import { useDiagramState, useDiagramStoreApi, useMantinePortalProps } from './hooks'
7
+ import { useLikeC4Model } from './likec4model'
8
+ import { Center, Group, Highlight, Text } from '@mantine/core'
9
+
10
+ interface QueryMatch {
11
+ data: SpotlightActionData,
12
+ keyword: string,
13
+ match: string
14
+ }
15
+
16
+ function filterAction(actionData: SpotlightActionData, query: string) : QueryMatch | undefined {
17
+ const queryLower = query.toLowerCase();
18
+ const keywords = Array.isArray(actionData.keywords) ? actionData.keywords : [];
19
+ const keywordMatch = keywords
20
+ .map(keyword => {
21
+ const index = keyword.toLowerCase().indexOf(queryLower);
22
+ return index !== -1 ? {
23
+ keyword: keyword,
24
+ match: keyword.substring(index, index + query.length)
25
+ } : null;
26
+ })
27
+ .find(match => !!match);
28
+ return !!keywordMatch ? {
29
+ data: actionData,
30
+ keyword: keywordMatch.keyword,
31
+ match: keywordMatch.match
32
+ } : undefined;
33
+ }
34
+
35
+ function buildFilteredActions(actionsData: SpotlightActionData[], query: string): JSX.Element[] {
36
+ return pipe(
37
+ actionsData,
38
+ map(a => filterAction(a, query)),
39
+ filter(qm => !!qm),
40
+ map(qm => buildSpotlightAction(qm))
41
+ );
42
+ }
43
+
44
+ function highlightMatch(match: QueryMatch): JSX.Element {
45
+ return <Highlight highlight={match.match}>{match.keyword}</Highlight>
46
+ }
47
+
48
+ function buildSpotlightAction(matchedAction: QueryMatch): JSX.Element {
49
+ const isMatchInLabel = matchedAction.keyword == matchedAction.data.label;
50
+ return <Spotlight.Action onClick={matchedAction.data.onClick}>
51
+ <Center style={{ marginRight: '8px '}}>
52
+ {matchedAction.data.leftSection}
53
+ </Center>
54
+ <Group wrap="nowrap" w="100%">
55
+ <div style={{ flex: 1 }}>
56
+ <Text>
57
+ {isMatchInLabel ? highlightMatch(matchedAction) : matchedAction.data.label}
58
+ </Text>
59
+ {!isMatchInLabel && <Text opacity={0.6} size="xs">{highlightMatch(matchedAction)}</Text>}
60
+ </div>
61
+ </Group>
62
+ </Spotlight.Action>
63
+ }
64
+
65
+ export const LikeC4Search = memo(() => {
66
+ const view = useDiagramState(s => s.view)
67
+ const model = useLikeC4Model(true)
68
+ const store = useDiagramStoreApi()
69
+ const [ query, setQuery ] = useState('')
70
+
71
+ const getNodeActionsData = (): SpotlightActionData[] => {
72
+ const { focusOnNode } = store.getState()
73
+
74
+ return pipe(
75
+ view.nodes,
76
+ filter(n => !!n.title),
77
+ map(n => ({
78
+ id: n.id,
79
+ label: n.title,
80
+ keywords: [
81
+ n.title,
82
+ ...(n.tags ?? []).map(t => `#${t}`),
83
+ ...(n.description ? [n.description] : [])
84
+ ].filter(k => k.toLowerCase()),
85
+ onClick: () => focusOnNode(n.id),
86
+ leftSection: <IconRectangularPrism />
87
+ }))
88
+ );
89
+ }
90
+
91
+ const getViewActionsData = (): SpotlightActionData[] => {
92
+ const { onNavigateTo } = store.getState()
93
+ const views = model.views()
94
+
95
+ return map(views, v => ({
96
+ id: v.id,
97
+ label: v.title ?? v.id,
98
+ keywords: [
99
+ v.title ?? v.id,
100
+ ...(v.tags ?? []),
101
+ ...(v.view.description ? [v.view.description] : [])
102
+ ],
103
+ onClick: () => {
104
+ store.setState({
105
+ hoveredNodeId: null,
106
+ lastOnNavigate: {
107
+ fromView: view.id,
108
+ toView: v.id,
109
+ fromNode: null
110
+ }
111
+ })
112
+ onNavigateTo?.(v.id)
113
+ },
114
+ leftSection: <IconSitemap />
115
+ }));
116
+ }
117
+
118
+ const { nodeActions, viewActions } = useMemo(() => ({
119
+ nodeActions: buildFilteredActions(getNodeActionsData(), query),
120
+ viewActions: buildFilteredActions(getViewActionsData(), query)
121
+ }),
122
+ [model, store, view, query]
123
+ );
124
+
125
+ const portalProps = useMantinePortalProps();
126
+ return (
127
+ <Spotlight.Root
128
+ {...portalProps}
129
+ shortcut={['mod + f', 'mod + k']}
130
+ query={query}
131
+ onQueryChange={setQuery}
132
+ scrollable
133
+ maxHeight={'calc(100vh - 100px)'}
134
+ >
135
+ <Spotlight.Search
136
+ placeholder="Search elements in current view and other views..."
137
+ leftSection={<IconSearch stroke={1.5} />}
138
+ />
139
+ <Spotlight.ActionsList>
140
+ {nodeActions.length > 0 && <SpotlightActionsGroup label="Elements">{nodeActions}</SpotlightActionsGroup> }
141
+ {viewActions.length > 0 && <SpotlightActionsGroup label="Views">{viewActions}</SpotlightActionsGroup> }
142
+ {nodeActions.length == 0 && viewActions.length == 0 && <Spotlight.Empty>Nothing found...</Spotlight.Empty> }
143
+ </Spotlight.ActionsList>
144
+ </Spotlight.Root>
145
+ );
146
+ });
@@ -9,7 +9,9 @@ export type StaticLikeC4DiagramProps = Pick<
9
9
  | 'fitView'
10
10
  | 'fitViewPadding'
11
11
  | 'background'
12
- | 'showElementLinks'
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
- showElementLinks = true,
25
+ enableElementDetails = false,
26
+ enableRelationshipDetails = false,
27
+ enableRelationshipBrowser = enableRelationshipDetails,
24
28
  background = 'transparent',
25
29
  ...rest
26
30
  }: StaticLikeC4DiagramProps) {
@@ -34,16 +38,17 @@ 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
- showRelationshipDetails={false}
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}
46
50
  nodesDraggable={false}
51
+ enableSearch={false}
47
52
  {...rest}
48
53
  />
49
54
  )
@@ -3,7 +3,7 @@ import type { DiagramState } from '../hooks'
3
3
 
4
4
  export const OverlayContext = createContext({} as {
5
5
  openOverlay: DiagramState['openOverlay']
6
- close: () => void
6
+ close: (onClosed?: () => void) => void
7
7
  })
8
8
 
9
9
  export function useOverlayDialog() {
@@ -1,7 +1,7 @@
1
- import { createVar, globalStyle, keyframes, style } from '@vanilla-extract/css'
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: `color-mix(in srgb, ${mantine.colors.body}, transparent var(--backdrop-opacity))`,
19
+ backgroundColor: `rgb(200 200 200 / var(--backdrop-opacity))`,
20
20
  vars: {
21
- '--backdrop-blur': '3px',
22
- '--backdrop-opacity': '50%',
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
- globalStyle(`:where(${container}) .react-flow__attribution`, {
41
- display: 'none'
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
- globalStyle(`:where(${container}) .react-flow__edge-text`, {
44
- fontSize: 14
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(${container}) .react-flow__node-compound`, {
47
- mixBlendMode: 'hard-light'
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(`:where([data-mantine-color-scheme="light"]) ${container} .react-flow__edges > svg`, {
58
+ globalStyle(`${whereLight} ${cssReactflowMarker} .react-flow__edges > svg`, {
54
59
  mixBlendMode: 'screen'
55
60
  })
56
61