@likec4/diagram 1.17.1 → 1.18.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 +17 -15
- package/src/LikeC4Diagram.props.ts +21 -12
- package/src/LikeC4Diagram.tsx +5 -7
- package/src/LikeC4Search.tsx +54 -51
- package/src/hooks/useDiagramState.ts +8 -3
- package/src/hooks/useXYFlow.ts +2 -1
- package/src/likec4model/useLikeC4ElementsTree.ts +6 -13
- package/src/likec4model/useLikeC4Model.ts +21 -11
- package/src/overlays/Overlays.tsx +96 -63
- package/src/overlays/edge-details/EdgeDetailsXYFlow.tsx +3 -1
- package/src/overlays/edge-details/_types.ts +7 -3
- package/src/overlays/edge-details/use-layouted-edge-details.ts +21 -14
- package/src/overlays/edge-details/xyflow/ElementNode.tsx +4 -2
- package/src/overlays/edge-details/xyflow/RelationshipEdge.tsx +4 -4
- package/src/overlays/element-details/ElementDetailsCard.tsx +91 -78
- package/src/overlays/element-details/TabPanelDeployments.css.ts +69 -0
- package/src/overlays/element-details/TabPanelDeployments.tsx +217 -0
- package/src/overlays/element-details/TabPanelRelationships.tsx +8 -9
- package/src/overlays/element-details/TabPanelStructure.tsx +10 -10
- package/src/overlays/relationships-of/RelationshipsOverlay.tsx +1 -1
- package/src/overlays/relationships-of/RelationshipsXYFlow.tsx +3 -11
- package/src/overlays/relationships-of/SelectElement.tsx +2 -2
- package/src/overlays/relationships-of/_types.ts +2 -2
- package/src/overlays/relationships-of/use-layouted-relationships.ts +18 -19
- package/src/overlays/relationships-of/xyflow/CompoundNode.tsx +1 -3
- package/src/overlays/relationships-of/xyflow/ElementNode.tsx +12 -6
- package/src/overlays/relationships-of/xyflow/RelationshipEdge.tsx +0 -1
- package/src/state/diagramStore.ts +28 -11
- package/src/ui/DiagramTitlePanel.tsx +2 -29
- package/src/ui/Link.tsx +40 -0
- package/src/ui/notation/NotationPanel.tsx +4 -2
- package/src/ui/top-left/ChangeAutoLayoutButton.tsx +2 -2
- package/src/ui/top-left/TopLeftPanel.tsx +5 -5
- package/src/xyflow/edges/EdgeLabel.tsx +4 -4
- package/src/xyflow/edges/RelationshipsDropdownMenu.tsx +20 -7
- package/src/xyflow/nodes/compound/CompoundNode.css.ts +28 -13
- package/src/xyflow/nodes/compound/CompoundNode.tsx +51 -19
- package/src/xyflow/nodes/element/ElementNode.tsx +147 -124
- package/src/xyflow/nodes/element/element.css.ts +52 -19
- package/src/xyflow/nodes/shared/ElementIcon.tsx +1 -1
- package/src/xyflow/nodes/shared/Toolbar.tsx +24 -8
- package/src/xyflow/useLayoutConstraints.ts +1 -1
- package/src/xyflow/useXYFlowEvents.ts +18 -5
- package/src/ui/OptionsPanel.css.ts +0 -9
- package/src/ui/OptionsPanel.tsx +0 -70
- package/src/ui/options/EdgeOptions.tsx +0 -142
- package/src/ui/options/NodeOptions.tsx +0 -362
- package/src/xyflow/nodes/element/ElementDetails.css.ts +0 -101
- package/src/xyflow/nodes/element/ElementDetails.tsx +0 -393
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/diagram",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
6
6
|
"homepage": "https://likec4.dev",
|
|
@@ -26,14 +26,15 @@
|
|
|
26
26
|
"packageManager": "yarn@4.5.3",
|
|
27
27
|
"scripts": {
|
|
28
28
|
"typecheck": "tsc --noEmit",
|
|
29
|
+
"build": "",
|
|
29
30
|
"lint": "run -T eslint src/ --fix",
|
|
30
31
|
"clean": "rm -r -f -g 'dist/*' .tsbuildinfo",
|
|
31
32
|
"test": "vitest run --no-isolate"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
35
|
"@dagrejs/dagre": "^1.1.4",
|
|
35
|
-
"@likec4/core": "1.
|
|
36
|
-
"@mantine/vanilla-extract": "7.
|
|
36
|
+
"@likec4/core": "1.18.0",
|
|
37
|
+
"@mantine/vanilla-extract": "7.15.1",
|
|
37
38
|
"@react-hookz/web": "^24.0.4",
|
|
38
39
|
"@tabler/icons-react": "3.17.0",
|
|
39
40
|
"@vanilla-extract/css": "^1.16.1",
|
|
@@ -44,26 +45,27 @@
|
|
|
44
45
|
"bezier-js": "^6.1.4",
|
|
45
46
|
"clsx": "^2.1.1",
|
|
46
47
|
"d3-shape": "^3.2.0",
|
|
47
|
-
"esm-env": "^1.1
|
|
48
|
+
"esm-env": "^1.2.1",
|
|
48
49
|
"fast-equals": "^5.0.1",
|
|
49
|
-
"framer-motion": "^11.
|
|
50
|
+
"framer-motion": "^11.14.1",
|
|
50
51
|
"khroma": "^2.1.0",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
52
|
+
"react-error-boundary": "^4.1.2",
|
|
53
|
+
"remeda": "^2.17.4",
|
|
54
|
+
"type-fest": "4.28.1",
|
|
53
55
|
"zustand": "^4.5.5"
|
|
54
56
|
},
|
|
55
57
|
"peerDependencies": {
|
|
56
|
-
"@mantine/core": "^7.
|
|
57
|
-
"@mantine/hooks": "^7.
|
|
58
|
-
"@mantine/spotlight": "^7.
|
|
58
|
+
"@mantine/core": "^7.15.1",
|
|
59
|
+
"@mantine/hooks": "^7.15.1",
|
|
60
|
+
"@mantine/spotlight": "^7.15.1",
|
|
59
61
|
"react": "^18.3.1",
|
|
60
62
|
"react-dom": "^18.3.1"
|
|
61
63
|
},
|
|
62
64
|
"devDependencies": {
|
|
63
|
-
"@likec4/tsconfig": "1.
|
|
64
|
-
"@mantine/core": "7.
|
|
65
|
-
"@mantine/hooks": "7.
|
|
66
|
-
"@mantine/spotlight": "7.
|
|
65
|
+
"@likec4/tsconfig": "1.18.0",
|
|
66
|
+
"@mantine/core": "7.15.1",
|
|
67
|
+
"@mantine/hooks": "7.15.1",
|
|
68
|
+
"@mantine/spotlight": "7.15.1",
|
|
67
69
|
"@types/bezier-js": "^4.1.3",
|
|
68
70
|
"@types/d3-shape": "^3.1.6",
|
|
69
71
|
"@types/react": "^18.3.11",
|
|
@@ -72,6 +74,6 @@
|
|
|
72
74
|
"react": "^18.3.1",
|
|
73
75
|
"react-dom": "^18.3.1",
|
|
74
76
|
"typescript": "^5.7.2",
|
|
75
|
-
"vitest": "^2.1.
|
|
77
|
+
"vitest": "^2.1.8"
|
|
76
78
|
}
|
|
77
79
|
}
|
|
@@ -3,9 +3,9 @@ import type {
|
|
|
3
3
|
DiagramNode,
|
|
4
4
|
DiagramView,
|
|
5
5
|
Fqn,
|
|
6
|
-
|
|
6
|
+
RelationId,
|
|
7
7
|
ViewChange,
|
|
8
|
-
|
|
8
|
+
ViewId,
|
|
9
9
|
WhereOperator
|
|
10
10
|
} from '@likec4/core'
|
|
11
11
|
import type { MouseEvent as ReactMouseEvent, ReactNode } from 'react'
|
|
@@ -13,7 +13,7 @@ import type { XYBackground } from './xyflow/XYFlowBackground'
|
|
|
13
13
|
|
|
14
14
|
export type { WhereOperator }
|
|
15
15
|
|
|
16
|
-
export type DiagramNodeWithNavigate<ID extends string =
|
|
16
|
+
export type DiagramNodeWithNavigate<ID extends string = ViewId> = Omit<DiagramNode, 'navigateTo'> & {
|
|
17
17
|
navigateTo: ID
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -27,7 +27,7 @@ export type ElementIconRenderer = (props: {
|
|
|
27
27
|
node: ElementIconNodeProps
|
|
28
28
|
}) => ReactNode
|
|
29
29
|
|
|
30
|
-
export type OnNavigateTo<ID extends string =
|
|
30
|
+
export type OnNavigateTo<ID extends string = ViewId> = (
|
|
31
31
|
to: ID,
|
|
32
32
|
// These fields present if navigateTo triggered by a node click
|
|
33
33
|
event?: ReactMouseEvent,
|
|
@@ -191,6 +191,22 @@ export interface LikeC4DiagramProperties {
|
|
|
191
191
|
where?: WhereOperator<string, string> | undefined
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
export type OpenSourceParams =
|
|
195
|
+
| {
|
|
196
|
+
element: Fqn
|
|
197
|
+
property?: string
|
|
198
|
+
}
|
|
199
|
+
| {
|
|
200
|
+
relation: RelationId
|
|
201
|
+
}
|
|
202
|
+
| {
|
|
203
|
+
deployment: Fqn
|
|
204
|
+
property?: string
|
|
205
|
+
}
|
|
206
|
+
| {
|
|
207
|
+
view: ViewId
|
|
208
|
+
}
|
|
209
|
+
|
|
194
210
|
export interface LikeC4DiagramEventHandlers {
|
|
195
211
|
onChange?: OnChange | null | undefined
|
|
196
212
|
onNavigateTo?: OnNavigateTo | null | undefined
|
|
@@ -205,12 +221,5 @@ export interface LikeC4DiagramEventHandlers {
|
|
|
205
221
|
// if set, will render a burger menu icon in the top left corner
|
|
206
222
|
onBurgerMenuClick?: null | undefined | (() => void)
|
|
207
223
|
|
|
208
|
-
|
|
209
|
-
onOpenSourceView?: null | undefined | (() => void)
|
|
210
|
-
|
|
211
|
-
// if set, will render an icon in properties webview for each element
|
|
212
|
-
onOpenSourceElement?: null | undefined | ((fqn: Fqn) => void)
|
|
213
|
-
|
|
214
|
-
// if set, will be called on edge click
|
|
215
|
-
onOpenSourceRelation?: null | undefined | ((id: RelationID) => void)
|
|
224
|
+
onOpenSource?: null | undefined | ((params: OpenSourceParams) => void)
|
|
216
225
|
}
|
package/src/LikeC4Diagram.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ComputedView, isDeploymentView } from '@likec4/core'
|
|
1
2
|
import { ReactFlowProvider as XYFlowProvider } from '@xyflow/react'
|
|
2
3
|
import clsx from 'clsx'
|
|
3
4
|
import { deepEqual } from 'fast-equals'
|
|
@@ -54,9 +55,7 @@ export function LikeC4Diagram({
|
|
|
54
55
|
onNavigateTo,
|
|
55
56
|
onNodeClick,
|
|
56
57
|
onNodeContextMenu,
|
|
57
|
-
|
|
58
|
-
onOpenSourceView,
|
|
59
|
-
onOpenSourceRelation,
|
|
58
|
+
onOpenSource,
|
|
60
59
|
onBurgerMenuClick,
|
|
61
60
|
renderIcon,
|
|
62
61
|
where,
|
|
@@ -112,11 +111,12 @@ export function LikeC4Diagram({
|
|
|
112
111
|
showNavigationButtons={showNavigationButtons && !!onNavigateTo}
|
|
113
112
|
showNotations={showNotations}
|
|
114
113
|
enableFocusMode={enableFocusMode}
|
|
115
|
-
enableRelationshipDetails={enableRelationshipDetails && hasLikec4model}
|
|
116
114
|
enableSearch={hasLikec4model && enableSearch}
|
|
117
115
|
enableElementDetails={enableElementDetails && hasLikec4model}
|
|
118
116
|
enableDynamicViewWalkthrough={enableDynamicViewWalkthrough}
|
|
119
117
|
enableRelationshipBrowser={enableRelationshipBrowser && hasLikec4model}
|
|
118
|
+
// TODO: temporary disable relationship details for deployment views
|
|
119
|
+
enableRelationshipDetails={enableRelationshipDetails && hasLikec4model && !isDeploymentView(view)}
|
|
120
120
|
// Apply where filter only in readonly mode
|
|
121
121
|
whereFilter={readonly ? (where ?? null) : null}
|
|
122
122
|
renderIcon={renderIcon ?? null}
|
|
@@ -129,9 +129,7 @@ export function LikeC4Diagram({
|
|
|
129
129
|
onChange={onChange ?? null}
|
|
130
130
|
onNavigateTo={onNavigateTo ?? null}
|
|
131
131
|
onCanvasDblClick={onCanvasDblClick ?? null}
|
|
132
|
-
|
|
133
|
-
onOpenSourceElement={onOpenSourceElement ?? null}
|
|
134
|
-
onOpenSourceRelation={onOpenSourceRelation ?? null}
|
|
132
|
+
onOpenSource={onOpenSource ?? null}
|
|
135
133
|
onBurgerMenuClick={onBurgerMenuClick ?? null}
|
|
136
134
|
>
|
|
137
135
|
<LikeC4DiagramInnerMemo
|
package/src/LikeC4Search.tsx
CHANGED
|
@@ -1,35 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Spotlight,
|
|
3
|
-
import { IconRectangularPrism,
|
|
1
|
+
import { Center, Group, Highlight, Text } from '@mantine/core'
|
|
2
|
+
import { Spotlight, type SpotlightActionData, SpotlightActionsGroup } from '@mantine/spotlight'
|
|
3
|
+
import { IconRectangularPrism, IconSearch, IconSitemap } from '@tabler/icons-react'
|
|
4
4
|
import { memo, useMemo, useState } from 'react'
|
|
5
5
|
import { filter, map, pipe } from 'remeda'
|
|
6
6
|
import { useDiagramState, useDiagramStoreApi, useMantinePortalProps } from './hooks'
|
|
7
7
|
import { useLikeC4Model } from './likec4model'
|
|
8
|
-
import { Center, Group, Highlight, Text } from '@mantine/core'
|
|
9
8
|
|
|
10
9
|
interface QueryMatch {
|
|
11
|
-
data: SpotlightActionData
|
|
12
|
-
keyword: string
|
|
10
|
+
data: SpotlightActionData
|
|
11
|
+
keyword: string
|
|
13
12
|
match: string
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
function filterAction(actionData: SpotlightActionData, query: string)
|
|
17
|
-
const queryLower = query.toLowerCase()
|
|
18
|
-
const keywords = Array.isArray(actionData.keywords) ? actionData.keywords : []
|
|
15
|
+
function filterAction(actionData: SpotlightActionData, query: string): QueryMatch | undefined {
|
|
16
|
+
const queryLower = query.toLowerCase()
|
|
17
|
+
const keywords = Array.isArray(actionData.keywords) ? actionData.keywords : []
|
|
19
18
|
const keywordMatch = keywords
|
|
20
19
|
.map(keyword => {
|
|
21
|
-
const index = keyword.toLowerCase().indexOf(queryLower)
|
|
22
|
-
return index !== -1
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
const index = keyword.toLowerCase().indexOf(queryLower)
|
|
21
|
+
return index !== -1
|
|
22
|
+
? {
|
|
23
|
+
keyword: keyword,
|
|
24
|
+
match: keyword.substring(index, index + query.length)
|
|
25
|
+
}
|
|
26
|
+
: null
|
|
26
27
|
})
|
|
27
|
-
.find(match => !!match)
|
|
28
|
-
return !!keywordMatch
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
.find(match => !!match)
|
|
29
|
+
return !!keywordMatch
|
|
30
|
+
? {
|
|
31
|
+
data: actionData,
|
|
32
|
+
keyword: keywordMatch.keyword,
|
|
33
|
+
match: keywordMatch.match
|
|
34
|
+
}
|
|
35
|
+
: undefined
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
function buildFilteredActions(actionsData: SpotlightActionData[], query: string): JSX.Element[] {
|
|
@@ -38,7 +41,7 @@ function buildFilteredActions(actionsData: SpotlightActionData[], query: string)
|
|
|
38
41
|
map(a => filterAction(a, query)),
|
|
39
42
|
filter(qm => !!qm),
|
|
40
43
|
map(qm => buildSpotlightAction(qm))
|
|
41
|
-
)
|
|
44
|
+
)
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
function highlightMatch(match: QueryMatch): JSX.Element {
|
|
@@ -46,27 +49,29 @@ function highlightMatch(match: QueryMatch): JSX.Element {
|
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
function buildSpotlightAction(matchedAction: QueryMatch): JSX.Element {
|
|
49
|
-
const isMatchInLabel = matchedAction.keyword == matchedAction.data.label
|
|
50
|
-
return
|
|
51
|
-
<
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
const isMatchInLabel = matchedAction.keyword == matchedAction.data.label
|
|
53
|
+
return (
|
|
54
|
+
<Spotlight.Action key={matchedAction.data.id} onClick={matchedAction.data.onClick}>
|
|
55
|
+
<Center style={{ marginRight: '8px ' }}>
|
|
56
|
+
{matchedAction.data.leftSection}
|
|
57
|
+
</Center>
|
|
58
|
+
<Group wrap="nowrap" w="100%">
|
|
59
|
+
<div style={{ flex: 1 }}>
|
|
60
|
+
<Text>
|
|
61
|
+
{isMatchInLabel ? highlightMatch(matchedAction) : matchedAction.data.label}
|
|
62
|
+
</Text>
|
|
63
|
+
{!isMatchInLabel && <Text opacity={0.6} size="xs">{highlightMatch(matchedAction)}</Text>}
|
|
64
|
+
</div>
|
|
65
|
+
</Group>
|
|
66
|
+
</Spotlight.Action>
|
|
67
|
+
)
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
export const LikeC4Search = memo(() => {
|
|
66
71
|
const view = useDiagramState(s => s.view)
|
|
67
72
|
const model = useLikeC4Model(true)
|
|
68
73
|
const store = useDiagramStoreApi()
|
|
69
|
-
const [
|
|
74
|
+
const [query, setQuery] = useState('')
|
|
70
75
|
|
|
71
76
|
const getNodeActionsData = (): SpotlightActionData[] => {
|
|
72
77
|
const { focusOnNode } = store.getState()
|
|
@@ -85,12 +90,12 @@ export const LikeC4Search = memo(() => {
|
|
|
85
90
|
onClick: () => focusOnNode(n.id),
|
|
86
91
|
leftSection: <IconRectangularPrism />
|
|
87
92
|
}))
|
|
88
|
-
)
|
|
93
|
+
)
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
const getViewActionsData = (): SpotlightActionData[] => {
|
|
92
97
|
const { onNavigateTo } = store.getState()
|
|
93
|
-
const views = model.views()
|
|
98
|
+
const views = model.views().toArray()
|
|
94
99
|
|
|
95
100
|
return map(views, v => ({
|
|
96
101
|
id: v.id,
|
|
@@ -98,7 +103,7 @@ export const LikeC4Search = memo(() => {
|
|
|
98
103
|
keywords: [
|
|
99
104
|
v.title ?? v.id,
|
|
100
105
|
...(v.tags ?? []),
|
|
101
|
-
...(v
|
|
106
|
+
...(v.$view.description ? [v.$view.description] : [])
|
|
102
107
|
],
|
|
103
108
|
onClick: () => {
|
|
104
109
|
store.setState({
|
|
@@ -112,17 +117,15 @@ export const LikeC4Search = memo(() => {
|
|
|
112
117
|
onNavigateTo?.(v.id)
|
|
113
118
|
},
|
|
114
119
|
leftSection: <IconSitemap />
|
|
115
|
-
}))
|
|
120
|
+
}))
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
const { nodeActions, viewActions } = useMemo(() => ({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
[model, store, view, query]
|
|
123
|
-
);
|
|
124
|
+
nodeActions: buildFilteredActions(getNodeActionsData(), query),
|
|
125
|
+
viewActions: buildFilteredActions(getViewActionsData(), query)
|
|
126
|
+
}), [model, store, view, query])
|
|
124
127
|
|
|
125
|
-
const portalProps = useMantinePortalProps()
|
|
128
|
+
const portalProps = useMantinePortalProps()
|
|
126
129
|
return (
|
|
127
130
|
<Spotlight.Root
|
|
128
131
|
{...portalProps}
|
|
@@ -137,10 +140,10 @@ export const LikeC4Search = memo(() => {
|
|
|
137
140
|
leftSection={<IconSearch stroke={1.5} />}
|
|
138
141
|
/>
|
|
139
142
|
<Spotlight.ActionsList>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
{nodeActions.length > 0 && <SpotlightActionsGroup label="Elements">{nodeActions}</SpotlightActionsGroup>}
|
|
144
|
+
{viewActions.length > 0 && <SpotlightActionsGroup label="Views">{viewActions}</SpotlightActionsGroup>}
|
|
145
|
+
{nodeActions.length == 0 && viewActions.length == 0 && <Spotlight.Empty>Nothing found...</Spotlight.Empty>}
|
|
143
146
|
</Spotlight.ActionsList>
|
|
144
147
|
</Spotlight.Root>
|
|
145
|
-
)
|
|
146
|
-
})
|
|
148
|
+
)
|
|
149
|
+
})
|
|
@@ -2,10 +2,16 @@ import { shallowEqual } from 'fast-equals'
|
|
|
2
2
|
import { useContext } from 'react'
|
|
3
3
|
import { useStoreWithEqualityFn as useZustandStore } from 'zustand/traditional'
|
|
4
4
|
|
|
5
|
+
import type { ViewId } from '@likec4/core'
|
|
5
6
|
import { DiagramContext } from '../state/DiagramContext'
|
|
6
7
|
import type { DiagramState, DiagramStoreApi } from '../state/diagramStore'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
const selectViewId = (state: DiagramState) => state.view.id
|
|
10
|
+
export function useCurrentViewId(): ViewId {
|
|
11
|
+
return useDiagramState(selectViewId)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function useDiagramState<StateSlice = unknown>(
|
|
9
15
|
selector: (state: DiagramState) => StateSlice,
|
|
10
16
|
equalityFn?: (a: StateSlice, b: StateSlice) => boolean
|
|
11
17
|
): StateSlice {
|
|
@@ -18,7 +24,7 @@ function useDiagramState<StateSlice = unknown>(
|
|
|
18
24
|
return useZustandStore(store, selector, equalityFn ?? shallowEqual)
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
function useDiagramStoreApi(): DiagramStoreApi {
|
|
27
|
+
export function useDiagramStoreApi(): DiagramStoreApi {
|
|
22
28
|
const store = useContext(DiagramContext)
|
|
23
29
|
|
|
24
30
|
if (store === null) {
|
|
@@ -28,4 +34,3 @@ function useDiagramStoreApi(): DiagramStoreApi {
|
|
|
28
34
|
return store
|
|
29
35
|
}
|
|
30
36
|
export type { DiagramState, DiagramStoreApi }
|
|
31
|
-
export { useDiagramState, useDiagramStoreApi }
|
package/src/hooks/useXYFlow.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useNodesData, useReactFlow, useStore, useStoreApi } from '@xyflow/react'
|
|
1
|
+
import { useInternalNode, useNodesData, useReactFlow, useStore, useStoreApi } from '@xyflow/react'
|
|
2
2
|
import { deepEqual, shallowEqual } from 'fast-equals'
|
|
3
3
|
import { useCallback } from 'react'
|
|
4
4
|
import type { XYFlowEdge, XYFlowNode, XYFlowState } from '../xyflow/types'
|
|
@@ -6,6 +6,7 @@ import type { XYFlowEdge, XYFlowNode, XYFlowState } from '../xyflow/types'
|
|
|
6
6
|
export const useXYFlow = useReactFlow<XYFlowNode, XYFlowEdge>
|
|
7
7
|
|
|
8
8
|
export const useXYNodesData = useNodesData<XYFlowNode>
|
|
9
|
+
export const useXYInternalNode = useInternalNode<XYFlowNode>
|
|
9
10
|
|
|
10
11
|
export function useXYStore<StateSlice = unknown>(
|
|
11
12
|
selector: (state: XYFlowState) => StateSlice,
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
compareNatural,
|
|
3
|
-
type Fqn,
|
|
4
|
-
type LikeC4DiagramModel,
|
|
5
|
-
type LikeC4Model,
|
|
6
|
-
type LikeC4ViewModel,
|
|
7
|
-
type ViewID
|
|
8
|
-
} from '@likec4/core'
|
|
1
|
+
import { compareNatural, type Fqn, type LikeC4Model, type ViewId } from '@likec4/core'
|
|
9
2
|
import { useMemo } from 'react'
|
|
10
3
|
import { useLikeC4Model } from './useLikeC4Model'
|
|
11
4
|
|
|
@@ -18,12 +11,12 @@ interface LikeC4ModelTreeNodeData {
|
|
|
18
11
|
const sortByLabel = (a: LikeC4ModelTreeNodeData, b: LikeC4ModelTreeNodeData) => compareNatural(a.label, b.label)
|
|
19
12
|
|
|
20
13
|
function buildNode(
|
|
21
|
-
element: LikeC4Model.
|
|
14
|
+
element: LikeC4Model.Node | LikeC4Model.Element
|
|
22
15
|
): LikeC4ModelTreeNodeData {
|
|
23
16
|
return {
|
|
24
17
|
label: element.title,
|
|
25
18
|
value: element.id,
|
|
26
|
-
children: element.children().map(buildNode).sort(sortByLabel)
|
|
19
|
+
children: [...element.children()].map(buildNode).sort(sortByLabel)
|
|
27
20
|
}
|
|
28
21
|
}
|
|
29
22
|
|
|
@@ -31,14 +24,14 @@ function buildNode(
|
|
|
31
24
|
* Returns a tree of elements in the model.
|
|
32
25
|
* If `viewId` is provided, returns the tree of elements in the view.
|
|
33
26
|
*/
|
|
34
|
-
export function useLikeC4ElementsTree(viewId?:
|
|
27
|
+
export function useLikeC4ElementsTree(viewId?: ViewId): LikeC4ModelTreeNodeData[] {
|
|
35
28
|
const model = useLikeC4Model(true)
|
|
36
29
|
return useMemo(() => {
|
|
37
30
|
if (viewId) {
|
|
38
31
|
const view = model.view(viewId)
|
|
39
|
-
return view.roots().map(buildNode).sort(sortByLabel)
|
|
32
|
+
return [...view.roots()].map(buildNode).sort(sortByLabel)
|
|
40
33
|
} else {
|
|
41
|
-
return model.roots().map(buildNode).sort(sortByLabel)
|
|
34
|
+
return [...model.roots()].map(buildNode).sort(sortByLabel)
|
|
42
35
|
}
|
|
43
36
|
}, [model, viewId ?? null])
|
|
44
37
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { type ComputedView, type DiagramView, type LikeC4Model, type
|
|
1
|
+
import { type ComputedView, type DiagramView, type Fqn, LikeC4Model, type ViewId } from '@likec4/core'
|
|
2
2
|
import { useContext } from 'react'
|
|
3
3
|
import { isDefined, isNonNullish, isString } from 'remeda'
|
|
4
4
|
import type { LiteralUnion } from 'type-fest'
|
|
5
|
+
import { useCurrentViewId } from '../hooks'
|
|
5
6
|
import { LikeC4ModelContext } from './LikeC4ModelContext'
|
|
6
7
|
|
|
7
8
|
export function useLikeC4Model(): LikeC4Model | null
|
|
@@ -9,11 +10,11 @@ export function useLikeC4Model(strict: true): LikeC4Model
|
|
|
9
10
|
export function useLikeC4Model(strict: boolean): LikeC4Model | null
|
|
10
11
|
export function useLikeC4Model(strict: true, type: 'layouted'): LikeC4Model.Layouted
|
|
11
12
|
export function useLikeC4Model(strict: true, type: 'computed'): LikeC4Model.Computed
|
|
12
|
-
export function useLikeC4Model(strict: true, type:
|
|
13
|
+
export function useLikeC4Model(strict: true, type: 'layouted' | 'computed' | undefined): LikeC4Model
|
|
13
14
|
export function useLikeC4Model(strict: boolean, type: 'layouted'): LikeC4Model.Layouted | null
|
|
14
15
|
export function useLikeC4Model(strict: boolean, type: 'computed'): LikeC4Model.Computed | null
|
|
15
|
-
export function useLikeC4Model(strict: boolean, type:
|
|
16
|
-
export function useLikeC4Model(strict?: boolean, type?:
|
|
16
|
+
export function useLikeC4Model(strict: boolean, type: 'layouted' | 'computed' | undefined): LikeC4Model | null
|
|
17
|
+
export function useLikeC4Model(strict?: boolean, type?: 'layouted' | 'computed') {
|
|
17
18
|
const model = useContext(LikeC4ModelContext)
|
|
18
19
|
|
|
19
20
|
if (isString(type) && isNonNullish(model) && model.type !== type) {
|
|
@@ -26,33 +27,42 @@ export function useLikeC4Model(strict?: boolean, type?: LikeC4Model['type']) {
|
|
|
26
27
|
return model
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
export function useLikeC4Views():
|
|
30
|
-
return useLikeC4Model(true).
|
|
30
|
+
export function useLikeC4Views(): Record<ViewId, ComputedView | DiagramView> {
|
|
31
|
+
return useLikeC4Model(true).$model.views
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
export function useLikeC4ViewModel(viewId: LiteralUnion<
|
|
34
|
+
export function useLikeC4ViewModel(viewId: LiteralUnion<ViewId, string>): LikeC4Model.View {
|
|
35
|
+
return useLikeC4Model(true).view(viewId)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function useLikeC4CurrentViewModel(): LikeC4Model.View {
|
|
39
|
+
const viewId = useCurrentViewId()
|
|
34
40
|
return useLikeC4Model(true).view(viewId)
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
/**
|
|
38
44
|
* Parsed view, computed or layouted
|
|
39
45
|
*/
|
|
40
|
-
export function useLikeC4View(viewId: LiteralUnion<
|
|
46
|
+
export function useLikeC4View(viewId: LiteralUnion<ViewId, string>): ComputedView | DiagramView | null {
|
|
41
47
|
const model = useLikeC4Model(true)
|
|
42
48
|
try {
|
|
43
|
-
return model.view(viewId)
|
|
49
|
+
return model.view(viewId).$view
|
|
44
50
|
} catch (error) {
|
|
45
51
|
console.warn(error)
|
|
46
52
|
return null
|
|
47
53
|
}
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
export function useLikeC4DiagramView(viewId: LiteralUnion<
|
|
56
|
+
export function useLikeC4DiagramView(viewId: LiteralUnion<ViewId, string>): DiagramView | null {
|
|
51
57
|
const model = useLikeC4Model(true, 'layouted')
|
|
52
58
|
try {
|
|
53
|
-
return model.view(viewId)
|
|
59
|
+
return model.view(viewId).$view
|
|
54
60
|
} catch (error) {
|
|
55
61
|
console.warn(error)
|
|
56
62
|
return null
|
|
57
63
|
}
|
|
58
64
|
}
|
|
65
|
+
|
|
66
|
+
export function useLikeC4ElementModel(fqn: LiteralUnion<Fqn, string>): LikeC4Model.Element {
|
|
67
|
+
return useLikeC4Model(true).element(fqn)
|
|
68
|
+
}
|