@likec4/diagram 1.20.1 → 1.20.2
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/dist/LikeC4Diagram.css.d.ts +1 -0
- package/dist/LikeC4Diagram.css.js +3 -1
- package/dist/base/BaseXYFlow.js +15 -14
- package/dist/base/Link.css.d.ts +3 -0
- package/dist/base/Link.css.js +9 -0
- package/dist/base/Link.d.ts +5 -0
- package/dist/base/Link.js +58 -0
- package/dist/base/const.d.ts +1 -0
- package/dist/base/const.js +2 -1
- package/dist/base/example/custom.d.ts +1 -1
- package/dist/base/index.d.ts +1 -0
- package/dist/base/primitives/compound/CompoundActionButton.js +2 -1
- package/dist/base/primitives/compound/CompoundDetailsButton.js +2 -1
- package/dist/base/primitives/compound/CompoundNodeContainer.css.d.ts +0 -1
- package/dist/base/primitives/compound/CompoundNodeContainer.css.js +4 -6
- package/dist/base/primitives/compound/CompoundNodeContainer.js +2 -2
- package/dist/base/primitives/customEdge.d.ts +1 -1
- package/dist/base/primitives/customEdge.js +1 -1
- package/dist/base/primitives/customNode.js +1 -1
- package/dist/base/primitives/edge/EdgeLabel.js +3 -1
- package/dist/base/primitives/edge/EdgePath.d.ts +2 -1
- package/dist/base/primitives/edge/EdgePath.js +2 -0
- package/dist/base/primitives/element/ElementActionButtons.js +2 -1
- package/dist/base/primitives/element/ElementDetailsButton.js +2 -1
- package/dist/base/primitives/element/ElementNodeContainer.css.d.ts +0 -1
- package/dist/base/primitives/element/ElementNodeContainer.css.js +1 -3
- package/dist/base/primitives/element/ElementNodeContainer.js +2 -3
- package/dist/base/primitives/element/ElementShape.js +4 -2
- package/dist/base/primitives/element/ElementTitle.js +2 -1
- package/dist/base/updateEdges.js +3 -3
- package/dist/base/updateNodes.js +2 -2
- package/dist/context/FramerMotionConfig.js +1 -1
- package/dist/hooks/useDiagram.d.ts +313 -90
- package/dist/hooks/useDiagramActor.d.ts +2 -2
- package/dist/hooks/useDiagramContext.d.ts +1 -1
- package/dist/hooks/useXYFlow.d.ts +2 -1
- package/dist/hooks/useXYFlow.js +5 -0
- package/dist/likec4diagram/XYFlow.js +12 -10
- package/dist/likec4diagram/custom/edges/RelationshipEdge.d.ts +1 -1
- package/dist/likec4diagram/custom/edges/RelationshipsDropdownMenu.js +2 -2
- package/dist/likec4diagram/custom/index.d.ts +1 -1
- package/dist/likec4diagram/custom/nodes/nodeTypes.js +2 -2
- package/dist/likec4diagram/state/actorContext.d.ts +1438 -364
- package/dist/likec4diagram/state/assign.js +2 -2
- package/dist/likec4diagram/state/machine.d.ts +321 -93
- package/dist/likec4diagram/state/syncManualLayoutActor.d.ts +1 -1
- package/dist/likec4diagram/types.d.ts +1 -0
- package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +4 -3
- package/dist/likec4diagram/useViewToNodesEdges.js +3 -1
- package/dist/overlays/element-details/ElementDetailsCard.css.d.ts +0 -1
- package/dist/overlays/element-details/ElementDetailsCard.css.js +1 -3
- package/dist/overlays/element-details/ElementDetailsCard.js +5 -30
- package/dist/overlays/relationship-details/RelationshipDetails.js +4 -4
- package/dist/overlays/relationship-details/custom/ElementActions.js +1 -3
- package/dist/overlays/relationship-details/custom/edgeTypes.d.ts +1 -1
- package/dist/overlays/relationship-details/hooks.d.ts +2 -2
- package/dist/overlays/relationship-details/hooks.js +7 -7
- package/dist/overlays/relationship-details/useLayoutedDetails.js +7 -10
- package/dist/overlays/relationships-browser/-useRelationshipsView.d.ts +4 -1
- package/dist/overlays/relationships-browser/-useRelationshipsView.js +12 -11
- package/dist/overlays/relationships-browser/RelationshipsBrowser.js +81 -66
- package/dist/overlays/relationships-browser/_types.d.ts +2 -1
- package/dist/overlays/relationships-browser/actor.d.ts +15 -2
- package/dist/overlays/relationships-browser/actor.js +262 -65
- package/dist/overlays/relationships-browser/const.d.ts +1 -0
- package/dist/overlays/relationships-browser/const.js +4 -0
- package/dist/overlays/relationships-browser/custom/ElementActions.js +1 -1
- package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +3 -2
- package/dist/overlays/relationships-browser/custom/edgeTypes.js +24 -6
- package/dist/overlays/relationships-browser/custom/nodeTypes.js +3 -2
- package/dist/overlays/relationships-browser/hooks.d.ts +6 -3
- package/dist/overlays/relationships-browser/hooks.js +4 -2
- package/dist/overlays/relationships-browser/useViewToNodesEdges.js +7 -2
- package/dist/style.css +1 -1
- package/dist/utils/xyflow.js +9 -1
- package/package.json +8 -8
- package/src/LikeC4Diagram.css.ts +8 -0
- package/src/base/BaseXYFlow.tsx +23 -14
- package/src/base/Link.css.ts +49 -0
- package/src/base/Link.tsx +66 -0
- package/src/base/const.ts +1 -0
- package/src/base/index.ts +2 -0
- package/src/base/primitives/compound/CompoundActionButton.tsx +2 -1
- package/src/base/primitives/compound/CompoundDetailsButton.tsx +2 -1
- package/src/base/primitives/compound/CompoundNodeContainer.css.ts +16 -10
- package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -1
- package/src/base/primitives/customEdge.ts +2 -0
- package/src/base/primitives/customNode.ts +3 -0
- package/src/base/primitives/edge/EdgeLabel.tsx +3 -1
- package/src/base/primitives/edge/EdgePath.tsx +3 -0
- package/src/base/primitives/element/ElementActionButtons.tsx +2 -1
- package/src/base/primitives/element/ElementDetailsButton.tsx +2 -1
- package/src/base/primitives/element/ElementNodeContainer.css.ts +9 -4
- package/src/base/primitives/element/ElementNodeContainer.tsx +1 -2
- package/src/base/primitives/element/ElementShape.tsx +4 -3
- package/src/base/primitives/element/ElementTitle.tsx +2 -1
- package/src/base/updateEdges.ts +3 -2
- package/src/base/updateNodes.ts +3 -2
- package/src/context/DiagramFeatures.tsx +1 -0
- package/src/context/FramerMotionConfig.tsx +1 -1
- package/src/hooks/useDiagramActor.ts +7 -5
- package/src/hooks/useDiagramContext.ts +4 -1
- package/src/hooks/useXYFlow.ts +6 -1
- package/src/likec4diagram/XYFlow.tsx +20 -9
- package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.tsx +2 -2
- package/src/likec4diagram/custom/nodes/nodeTypes.tsx +2 -2
- package/src/likec4diagram/types.ts +1 -0
- package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +4 -3
- package/src/likec4diagram/useViewToNodesEdges.ts +1 -0
- package/src/overlays/element-details/ElementDetailsCard.css.ts +0 -28
- package/src/overlays/element-details/ElementDetailsCard.tsx +2 -50
- package/src/overlays/relationship-details/RelationshipDetails.tsx +4 -4
- package/src/overlays/relationship-details/custom/ElementActions.tsx +1 -2
- package/src/overlays/relationship-details/custom/edgeTypes.tsx +0 -5
- package/src/overlays/relationship-details/hooks.ts +6 -6
- package/src/overlays/relationship-details/useLayoutedDetails.ts +7 -10
- package/src/overlays/relationships-browser/-useRelationshipsView.ts +16 -12
- package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +105 -68
- package/src/overlays/relationships-browser/_types.ts +2 -1
- package/src/overlays/relationships-browser/actor.ts +321 -62
- package/src/overlays/relationships-browser/const.ts +1 -0
- package/src/overlays/relationships-browser/custom/ElementActions.tsx +1 -1
- package/src/overlays/relationships-browser/custom/edgeTypes.tsx +25 -6
- package/src/overlays/relationships-browser/custom/nodeTypes.tsx +3 -2
- package/src/overlays/relationships-browser/hooks.ts +3 -1
- package/src/overlays/relationships-browser/useViewToNodesEdges.ts +8 -2
- package/dist/likec4diagram/ui/diagram-title/Link.js +0 -36
- package/dist/ui/Link.d.ts +0 -4
- package/src/ui/Link.tsx +0 -40
|
@@ -275,34 +275,6 @@ export const propertyLabel = style({
|
|
|
275
275
|
justifySelf: 'end',
|
|
276
276
|
})
|
|
277
277
|
|
|
278
|
-
export const elementLink = style({
|
|
279
|
-
transition: transitions.fast,
|
|
280
|
-
border: `1px dashed ${mantine.colors.defaultBorder}`,
|
|
281
|
-
borderRadius: mantine.radius.sm,
|
|
282
|
-
minHeight: 30,
|
|
283
|
-
padding: '3px 16px 3px 3px',
|
|
284
|
-
cursor: 'pointer',
|
|
285
|
-
color: mantine.colors.gray[7],
|
|
286
|
-
':hover': {
|
|
287
|
-
transitionTimingFunction: easings.out,
|
|
288
|
-
borderStyle: 'solid',
|
|
289
|
-
color: mantine.colors.defaultColor,
|
|
290
|
-
background: mantine.colors.defaultHover,
|
|
291
|
-
},
|
|
292
|
-
selectors: {
|
|
293
|
-
[`${whereDark} &`]: {
|
|
294
|
-
color: mantine.colors.dark[1],
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
})
|
|
298
|
-
globalStyle(`${elementLink} > *`, {
|
|
299
|
-
transition: transitions.fast,
|
|
300
|
-
})
|
|
301
|
-
globalStyle(`${elementLink}:hover > *`, {
|
|
302
|
-
transitionTimingFunction: easings.out,
|
|
303
|
-
transform: 'translateX(1px)',
|
|
304
|
-
})
|
|
305
|
-
|
|
306
278
|
export const resizeHandle = style({
|
|
307
279
|
position: 'absolute',
|
|
308
280
|
width: 14,
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
type Element,
|
|
4
4
|
type Fqn,
|
|
5
5
|
type LikeC4View,
|
|
6
|
-
type Link,
|
|
7
6
|
ComputedNode,
|
|
8
7
|
ComputedView,
|
|
9
8
|
isDeploymentView,
|
|
@@ -43,6 +42,7 @@ import clsx from 'clsx'
|
|
|
43
42
|
import { type PanInfo, m, useDragControls, useMotionValue } from 'framer-motion'
|
|
44
43
|
import { type PropsWithChildren, memo, useCallback, useLayoutEffect, useRef } from 'react'
|
|
45
44
|
import { clamp, isNullish, map, only, partition, pipe } from 'remeda'
|
|
45
|
+
import { Link } from '../../base'
|
|
46
46
|
import { IconRenderer, IfEnabled } from '../../context'
|
|
47
47
|
import { useUpdateEffect } from '../../hooks'
|
|
48
48
|
import { useDiagram } from '../../hooks/useDiagram'
|
|
@@ -424,7 +424,7 @@ export const ElementDetailsCard = memo(({ fqn }: ElementDetailsCardProps) => {
|
|
|
424
424
|
<>
|
|
425
425
|
<PropertyLabel>links</PropertyLabel>
|
|
426
426
|
<Stack gap={'xs'} align="flex-start">
|
|
427
|
-
{elementModel.links.map((link, i) => <
|
|
427
|
+
{elementModel.links.map((link, i) => <Link key={i} value={link} />)}
|
|
428
428
|
</Stack>
|
|
429
429
|
</>
|
|
430
430
|
)}
|
|
@@ -601,54 +601,6 @@ function ElementProperty({
|
|
|
601
601
|
)
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
-
function ElementLink({
|
|
605
|
-
value,
|
|
606
|
-
}: {
|
|
607
|
-
value: Link
|
|
608
|
-
}) {
|
|
609
|
-
const url = new URL(value.url, window.location.href).toString()
|
|
610
|
-
return (
|
|
611
|
-
<CopyButton value={url}>
|
|
612
|
-
{({ copied, copy }) => (
|
|
613
|
-
<Anchor href={url} target="_blank" underline="never" className={css.elementLink}>
|
|
614
|
-
<Group gap={4} align="center" wrap="nowrap">
|
|
615
|
-
<ActionIcon
|
|
616
|
-
tabIndex={-1}
|
|
617
|
-
size={24}
|
|
618
|
-
variant={copied ? 'light' : 'subtle'}
|
|
619
|
-
color={copied ? 'teal' : 'gray'}
|
|
620
|
-
onClick={e => {
|
|
621
|
-
e.stopPropagation()
|
|
622
|
-
e.preventDefault()
|
|
623
|
-
copy()
|
|
624
|
-
}}
|
|
625
|
-
>
|
|
626
|
-
{copied ? <IconCheck /> : <IconCopy style={{ width: '65%', opacity: 0.8 }} />}
|
|
627
|
-
</ActionIcon>
|
|
628
|
-
<Box flex={1}>
|
|
629
|
-
<Text fz={'sm'} truncate lh={1.3} fw={value.title ? 500 : 400}>
|
|
630
|
-
{value.title || url}
|
|
631
|
-
</Text>
|
|
632
|
-
{value.title && (
|
|
633
|
-
<Text component="div" fz={10} c={'dimmed'} lh={1.2} truncate>
|
|
634
|
-
{url}
|
|
635
|
-
</Text>
|
|
636
|
-
)}
|
|
637
|
-
</Box>
|
|
638
|
-
</Group>
|
|
639
|
-
</Anchor>
|
|
640
|
-
)}
|
|
641
|
-
</CopyButton>
|
|
642
|
-
)
|
|
643
|
-
// <Anchor href={value.url} fz={'sm'}>
|
|
644
|
-
// {value.title || url}
|
|
645
|
-
// </Anchor><Button variant='default' size='sm'>
|
|
646
|
-
// {value.title || value.url}
|
|
647
|
-
// </Button>
|
|
648
|
-
// </Box>
|
|
649
|
-
// )
|
|
650
|
-
}
|
|
651
|
-
|
|
652
604
|
function ElementMetata({
|
|
653
605
|
value,
|
|
654
606
|
}: {
|
|
@@ -17,7 +17,7 @@ import { nodeTypes } from './custom/nodeTypes'
|
|
|
17
17
|
import {
|
|
18
18
|
RelationshipDetailsActorContext,
|
|
19
19
|
useRelationshipDetails,
|
|
20
|
-
|
|
20
|
+
useRelationshipDetailsState,
|
|
21
21
|
} from './hooks'
|
|
22
22
|
import { SelectEdge } from './SelectEdge'
|
|
23
23
|
import { useLayoutedDetails } from './useLayoutedDetails'
|
|
@@ -63,7 +63,7 @@ const RelationshipDetailsInner = memo(() => {
|
|
|
63
63
|
edgeId,
|
|
64
64
|
view,
|
|
65
65
|
initialized,
|
|
66
|
-
} =
|
|
66
|
+
} = useRelationshipDetailsState(selector)
|
|
67
67
|
|
|
68
68
|
const {
|
|
69
69
|
edge,
|
|
@@ -123,9 +123,9 @@ const RelationshipDetailsInner = memo(() => {
|
|
|
123
123
|
e.stopPropagation()
|
|
124
124
|
browser.send({ type: 'xyflow.paneClick' })
|
|
125
125
|
})}
|
|
126
|
-
onViewportResize={() => {
|
|
126
|
+
onViewportResize={useCallbackRef(() => {
|
|
127
127
|
browser.send({ type: 'xyflow.resized' })
|
|
128
|
-
}}
|
|
128
|
+
})}
|
|
129
129
|
nodesDraggable={false}
|
|
130
130
|
fitView={false}
|
|
131
131
|
pannable
|
|
@@ -11,11 +11,10 @@ type ElementActionsProps = NodeProps<Types.ElementNodeData>
|
|
|
11
11
|
export const ElementActions = (props: ElementActionsProps) => {
|
|
12
12
|
const { enableNavigateTo } = useEnabledFeature('NavigateTo')
|
|
13
13
|
const diagram = useDiagram()
|
|
14
|
-
const browser = useRelationshipDetails()
|
|
15
14
|
|
|
16
15
|
const buttons = [] as ElementActionButtons.Item[]
|
|
17
16
|
|
|
18
|
-
const { navigateTo
|
|
17
|
+
const { navigateTo } = props.data
|
|
19
18
|
if (navigateTo && enableNavigateTo) {
|
|
20
19
|
buttons.push({
|
|
21
20
|
key: 'navigate',
|
|
@@ -7,21 +7,21 @@ import type { RelationshipDetailsActorRef, RelationshipDetailsSnapshot } from '.
|
|
|
7
7
|
|
|
8
8
|
export const RelationshipDetailsActorContext = createContext<RelationshipDetailsActorRef | null>(null)
|
|
9
9
|
|
|
10
|
-
export function
|
|
11
|
-
return nonNullable(useContext(RelationshipDetailsActorContext), 'No
|
|
10
|
+
export function useRelationshipDetailsActor() {
|
|
11
|
+
return nonNullable(useContext(RelationshipDetailsActorContext), 'No RelationshipDetailsActorContext')
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function
|
|
14
|
+
export function useRelationshipDetailsState<T = unknown>(
|
|
15
15
|
selector: (state: RelationshipDetailsSnapshot) => T,
|
|
16
|
-
compare: (a: T
|
|
16
|
+
compare: (a: NoInfer<T>, b: NoInfer<T>) => boolean = shallowEqual,
|
|
17
17
|
) {
|
|
18
18
|
const select = useCallbackRef(selector)
|
|
19
|
-
const actor =
|
|
19
|
+
const actor = useRelationshipDetailsActor()
|
|
20
20
|
return useSelector(actor, select, compare)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function useRelationshipDetails() {
|
|
24
|
-
const actor =
|
|
24
|
+
const actor = useRelationshipDetailsActor()
|
|
25
25
|
const [, startTransition] = useTransition()
|
|
26
26
|
return useMemo(() => ({
|
|
27
27
|
getState: () => actor.getSnapshot().context,
|
|
@@ -290,8 +290,8 @@ function layout(
|
|
|
290
290
|
type: 'relationship',
|
|
291
291
|
source: source.id,
|
|
292
292
|
target: target.id,
|
|
293
|
-
sourceHandle:
|
|
294
|
-
targetHandle:
|
|
293
|
+
sourceHandle: source.id + '_out' + (source.data.ports.out.length - 1),
|
|
294
|
+
targetHandle: target.id + '_in' + (target.data.ports.in.length - 1),
|
|
295
295
|
data: {
|
|
296
296
|
relationId: relation.id,
|
|
297
297
|
color: relation.color,
|
|
@@ -309,15 +309,12 @@ function layout(
|
|
|
309
309
|
const nodebounds = applyDagreLayout(ctx.g)
|
|
310
310
|
|
|
311
311
|
// Sort ports
|
|
312
|
-
const sortedPorts = (ports: string[]) => {
|
|
313
|
-
if (ports.length < 2) {
|
|
314
|
-
return ports
|
|
315
|
-
}
|
|
312
|
+
const sortedPorts = (nodeId: string, type: 'in' | 'out', ports: string[]) => {
|
|
316
313
|
return pipe(
|
|
317
314
|
ports,
|
|
318
|
-
map(port => {
|
|
315
|
+
map((port, index) => {
|
|
319
316
|
return {
|
|
320
|
-
port,
|
|
317
|
+
port: nodeId + '_' + type + index,
|
|
321
318
|
topY: nodebounds(port).position.y,
|
|
322
319
|
}
|
|
323
320
|
}),
|
|
@@ -328,8 +325,8 @@ function layout(
|
|
|
328
325
|
|
|
329
326
|
const xynodes = [...ctx.xynodes.values()].map((node) => {
|
|
330
327
|
const { position, width, height } = nodebounds(node.id, node.parentId)
|
|
331
|
-
node.data.ports.in = sortedPorts(node.data.ports.in)
|
|
332
|
-
node.data.ports.out = sortedPorts(node.data.ports.out)
|
|
328
|
+
node.data.ports.in = sortedPorts(node.id, 'in', node.data.ports.in)
|
|
329
|
+
node.data.ports.out = sortedPorts(node.id, 'out', node.data.ports.out)
|
|
333
330
|
if (node.type === 'element') {
|
|
334
331
|
node.data.width = width
|
|
335
332
|
node.data.height = height
|
|
@@ -256,6 +256,8 @@ export namespace LayoutRelationshipsViewResult {
|
|
|
256
256
|
ports: RelationshipsBrowserTypes.Ports
|
|
257
257
|
}
|
|
258
258
|
export type Edge = DiagramEdge & {
|
|
259
|
+
sourceHandle: string
|
|
260
|
+
targetHandle: string
|
|
259
261
|
// column: RelationshipsBrowserTypes.Column
|
|
260
262
|
}
|
|
261
263
|
}
|
|
@@ -271,6 +273,8 @@ export function layoutRelationshipsView(data: RelationshipsViewData): LayoutRela
|
|
|
271
273
|
name: string
|
|
272
274
|
source: string // node id
|
|
273
275
|
target: string // node id
|
|
276
|
+
sourceHandle: string
|
|
277
|
+
targetHandle: string
|
|
274
278
|
relations: RelationshipModel[]
|
|
275
279
|
}>
|
|
276
280
|
|
|
@@ -319,7 +323,9 @@ export function layoutRelationshipsView(data: RelationshipsViewData): LayoutRela
|
|
|
319
323
|
edges.push({
|
|
320
324
|
name,
|
|
321
325
|
source: source.id,
|
|
326
|
+
sourceHandle: source.id + '_out' + (g.node(source.id).outPorts.length - 1),
|
|
322
327
|
target: target.id,
|
|
328
|
+
targetHandle: target.id + '_in' + (g.node(target.id).inPorts.length - 1),
|
|
323
329
|
relations: map(grouped, prop('relation')),
|
|
324
330
|
})
|
|
325
331
|
}),
|
|
@@ -446,15 +452,12 @@ export function layoutRelationshipsView(data: RelationshipsViewData): LayoutRela
|
|
|
446
452
|
}
|
|
447
453
|
|
|
448
454
|
// Sort ports in subject vertically
|
|
449
|
-
const sortedPorts = (ports: string[]) => {
|
|
450
|
-
if (ports.length < 2) {
|
|
451
|
-
return ports
|
|
452
|
-
}
|
|
455
|
+
const sortedPorts = (nodeId: string, type: 'in' | 'out', ports: string[]) => {
|
|
453
456
|
return pipe(
|
|
454
457
|
ports,
|
|
455
|
-
map(port => {
|
|
458
|
+
map((port, index) => {
|
|
456
459
|
return {
|
|
457
|
-
port,
|
|
460
|
+
port: nodeId + '_' + type + index,
|
|
458
461
|
topY: nodeBounds(port).position.y,
|
|
459
462
|
}
|
|
460
463
|
}),
|
|
@@ -462,7 +465,6 @@ export function layoutRelationshipsView(data: RelationshipsViewData): LayoutRela
|
|
|
462
465
|
map(prop('port')),
|
|
463
466
|
)
|
|
464
467
|
}
|
|
465
|
-
|
|
466
468
|
// In case we got negative positions
|
|
467
469
|
let minX = 0
|
|
468
470
|
let minY = 0
|
|
@@ -573,16 +575,16 @@ export function layoutRelationshipsView(data: RelationshipsViewData): LayoutRela
|
|
|
573
575
|
height,
|
|
574
576
|
column,
|
|
575
577
|
ports: {
|
|
576
|
-
in: sortedPorts(inPorts),
|
|
577
|
-
out: sortedPorts(outPorts),
|
|
578
|
+
in: sortedPorts(id, 'in', inPorts),
|
|
579
|
+
out: sortedPorts(id, 'out', outPorts),
|
|
578
580
|
},
|
|
579
581
|
}
|
|
580
582
|
})
|
|
581
583
|
|
|
582
584
|
return {
|
|
583
585
|
bounds: {
|
|
584
|
-
x: minX,
|
|
585
|
-
y: minY,
|
|
586
|
+
x: Math.min(minX, 0),
|
|
587
|
+
y: Math.min(minY, 0),
|
|
586
588
|
width: g.graph().width ?? 100,
|
|
587
589
|
height: g.graph().height ?? 100,
|
|
588
590
|
},
|
|
@@ -593,7 +595,7 @@ export function layoutRelationshipsView(data: RelationshipsViewData): LayoutRela
|
|
|
593
595
|
if (!ename) {
|
|
594
596
|
return acc
|
|
595
597
|
}
|
|
596
|
-
const { name, source, target, relations } = find(edges, e => e.name === ename)!
|
|
598
|
+
const { name, source, target, relations, sourceHandle, targetHandle } = find(edges, e => e.name === ename)!
|
|
597
599
|
const onlyRelation = only(relations)
|
|
598
600
|
const label = onlyRelation?.title ?? 'untitled'
|
|
599
601
|
const isMultiple = relations.length > 1
|
|
@@ -607,7 +609,9 @@ export function layoutRelationshipsView(data: RelationshipsViewData): LayoutRela
|
|
|
607
609
|
acc.push({
|
|
608
610
|
id: name as EdgeId,
|
|
609
611
|
source: source as NodeId,
|
|
612
|
+
sourceHandle,
|
|
610
613
|
target: target as NodeId,
|
|
614
|
+
targetHandle,
|
|
611
615
|
label: isMultiple ? `${relations.length} relationships` : label,
|
|
612
616
|
relations: relations.map(r => r.id),
|
|
613
617
|
parent: null,
|
|
@@ -2,15 +2,17 @@ import type { Fqn } from '@likec4/core'
|
|
|
2
2
|
import { ActionIcon, Box, Group } from '@mantine/core'
|
|
3
3
|
import { useCallbackRef, useStateHistory } from '@mantine/hooks'
|
|
4
4
|
import { IconChevronLeft, IconChevronRight, IconX } from '@tabler/icons-react'
|
|
5
|
-
import { Panel, ReactFlowProvider } from '@xyflow/react'
|
|
5
|
+
import { Panel, ReactFlowProvider, useStoreApi } from '@xyflow/react'
|
|
6
6
|
import clsx from 'clsx'
|
|
7
|
+
import { shallowEqual } from 'fast-equals'
|
|
7
8
|
import { AnimatePresence, LayoutGroup, m } from 'framer-motion'
|
|
8
9
|
import { memo, useEffect, useRef } from 'react'
|
|
9
10
|
import type { SnapshotFrom } from 'xstate'
|
|
10
11
|
import { BaseXYFlow } from '../../base/BaseXYFlow'
|
|
11
12
|
import { useRelationshipsView } from './-useRelationshipsView'
|
|
12
|
-
import type { RelationshipsBrowserTypes as Types } from './_types'
|
|
13
|
+
import type { RelationshipsBrowserTypes, RelationshipsBrowserTypes as Types } from './_types'
|
|
13
14
|
import type { RelationshipsBrowserActorRef } from './actor'
|
|
15
|
+
import { ViewPadding } from './const'
|
|
14
16
|
import { edgeTypes } from './custom/edgeTypes'
|
|
15
17
|
import { nodeTypes } from './custom/nodeTypes'
|
|
16
18
|
import {
|
|
@@ -29,69 +31,56 @@ export function RelationshipsBrowser({ actorRef }: RelationshipsBrowserProps) {
|
|
|
29
31
|
// return null
|
|
30
32
|
// }
|
|
31
33
|
const initialRef = useRef<{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
initialNodes: Types.Node[]
|
|
35
|
+
initialEdges: Types.Edge[]
|
|
34
36
|
}>(null)
|
|
35
37
|
|
|
36
38
|
if (initialRef.current == null) {
|
|
37
39
|
initialRef.current = {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
initialNodes: [],
|
|
41
|
+
initialEdges: [],
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
return (
|
|
44
46
|
<RelationshipsBrowserActorContext.Provider value={actorRef}>
|
|
45
47
|
<ReactFlowProvider {...initialRef.current}>
|
|
46
|
-
<
|
|
48
|
+
<RelationshipsBrowserXYFlow />
|
|
47
49
|
</ReactFlowProvider>
|
|
48
50
|
</RelationshipsBrowserActorContext.Provider>
|
|
49
51
|
)
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
const selector = (state: SnapshotFrom<RelationshipsBrowserActorRef>) => ({
|
|
53
|
-
subjectId: state.context.subject,
|
|
54
55
|
initialized: state.context.initialized,
|
|
55
|
-
|
|
56
|
+
isActive: state.hasTag('active'),
|
|
56
57
|
nodes: state.context.xynodes,
|
|
57
58
|
edges: state.context.xyedges,
|
|
58
|
-
closeable: state.context.closeable,
|
|
59
|
-
enableNavigationMenu: state.context.enableNavigationMenu,
|
|
60
59
|
})
|
|
60
|
+
const selectorEq = (a: ReturnType<typeof selector>, b: ReturnType<typeof selector>) =>
|
|
61
|
+
a.initialized === b.initialized &&
|
|
62
|
+
a.isActive === b.isActive &&
|
|
63
|
+
shallowEqual(a.nodes, b.nodes) &&
|
|
64
|
+
shallowEqual(a.edges, b.edges)
|
|
61
65
|
|
|
62
|
-
const
|
|
66
|
+
const RelationshipsBrowserXYFlow = memo(() => {
|
|
67
|
+
const xystore = useStoreApi<RelationshipsBrowserTypes.Node, RelationshipsBrowserTypes.Edge>()
|
|
63
68
|
const browser = useRelationshipsBrowser()
|
|
64
69
|
const {
|
|
65
|
-
subjectId,
|
|
66
70
|
initialized,
|
|
67
|
-
|
|
71
|
+
isActive,
|
|
68
72
|
nodes,
|
|
69
73
|
edges,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const layouted = useRelationshipsView(subjectId)
|
|
75
|
-
const [historySubjectId, historyOps, { history, current }] = useStateHistory(subjectId)
|
|
76
|
-
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (historySubjectId !== subjectId) {
|
|
79
|
-
historyOps.set(subjectId)
|
|
80
|
-
}
|
|
81
|
-
}, [subjectId])
|
|
82
|
-
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
if (historySubjectId !== subjectId) {
|
|
85
|
-
browser.navigateTo(historySubjectId)
|
|
86
|
-
}
|
|
87
|
-
}, [historySubjectId])
|
|
74
|
+
} = useRelationshipsBrowserState(
|
|
75
|
+
selector,
|
|
76
|
+
selectorEq,
|
|
77
|
+
)
|
|
88
78
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const hasStepForward = current + 1 < history.length
|
|
79
|
+
// useLifecycleLogger('RelationshipsBrowserXYFlow.browser', [browser])
|
|
80
|
+
// useLifecycleLogger('RelationshipsBrowserXYFlow.xystore', [xystore])
|
|
81
|
+
// useLifecycleLogger('RelationshipsBrowserXYFlow.initialized, isActive', [initialized, isActive])
|
|
82
|
+
// useLifecycleLogger('RelationshipsBrowserXYFlow.nodes', [nodes])
|
|
83
|
+
// useLifecycleLogger('RelationshipsBrowserXYFlow.edges', [edges])
|
|
95
84
|
|
|
96
85
|
return (
|
|
97
86
|
<LayoutGroup>
|
|
@@ -99,12 +88,12 @@ const RelationshipsBrowserInner = memo(() => {
|
|
|
99
88
|
id="relationships-browser"
|
|
100
89
|
nodes={nodes}
|
|
101
90
|
edges={edges}
|
|
102
|
-
className={clsx(initialized ? 'initialized' : 'not-initialized')}
|
|
91
|
+
className={clsx(initialized && isActive ? 'initialized' : 'not-initialized')}
|
|
103
92
|
nodeTypes={nodeTypes}
|
|
104
93
|
edgeTypes={edgeTypes}
|
|
105
|
-
fitViewPadding={
|
|
94
|
+
fitViewPadding={ViewPadding}
|
|
106
95
|
onInit={useCallbackRef((instance) => {
|
|
107
|
-
browser.send({ type: 'xyflow.init', instance })
|
|
96
|
+
browser.send({ type: 'xyflow.init', instance, store: xystore })
|
|
108
97
|
})}
|
|
109
98
|
onNodeClick={useCallbackRef((e, node) => {
|
|
110
99
|
e.stopPropagation()
|
|
@@ -122,9 +111,9 @@ const RelationshipsBrowserInner = memo(() => {
|
|
|
122
111
|
e.stopPropagation()
|
|
123
112
|
browser.send({ type: 'xyflow.paneDblClick' })
|
|
124
113
|
})}
|
|
125
|
-
onViewportResize={() => {
|
|
114
|
+
onViewportResize={useCallbackRef(() => {
|
|
126
115
|
browser.send({ type: 'xyflow.resized' })
|
|
127
|
-
}}
|
|
116
|
+
})}
|
|
128
117
|
onNodesChange={useCallbackRef((changes) => {
|
|
129
118
|
browser.send({ type: 'xyflow.applyNodeChages', changes })
|
|
130
119
|
})}
|
|
@@ -136,36 +125,84 @@ const RelationshipsBrowserInner = memo(() => {
|
|
|
136
125
|
pannable
|
|
137
126
|
zoomable
|
|
138
127
|
>
|
|
139
|
-
<
|
|
140
|
-
enableNavigationMenu={enableNavigationMenu}
|
|
141
|
-
subjectId={subjectId}
|
|
142
|
-
hasStepBack={hasStepBack}
|
|
143
|
-
hasStepForward={hasStepForward}
|
|
144
|
-
onStepBack={() => historyOps.back()}
|
|
145
|
-
onStepForward={() => historyOps.forward()}
|
|
146
|
-
/>
|
|
147
|
-
{closeable && (
|
|
148
|
-
<Panel position="top-right">
|
|
149
|
-
<ActionIcon
|
|
150
|
-
variant="default"
|
|
151
|
-
color="gray"
|
|
152
|
-
// color="gray"
|
|
153
|
-
// size={'lg'}
|
|
154
|
-
// data-autofocus
|
|
155
|
-
// autoFocus
|
|
156
|
-
onClick={(e) => {
|
|
157
|
-
e.stopPropagation()
|
|
158
|
-
browser.close()
|
|
159
|
-
}}>
|
|
160
|
-
<IconX />
|
|
161
|
-
</ActionIcon>
|
|
162
|
-
</Panel>
|
|
163
|
-
)}
|
|
128
|
+
{initialized && <RelationshipsBrowserInner />}
|
|
164
129
|
</BaseXYFlow>
|
|
165
130
|
</LayoutGroup>
|
|
166
131
|
)
|
|
167
132
|
})
|
|
168
133
|
|
|
134
|
+
const selector2 = (state: SnapshotFrom<RelationshipsBrowserActorRef>) => ({
|
|
135
|
+
subjectId: state.context.subject,
|
|
136
|
+
closeable: state.context.closeable,
|
|
137
|
+
enableNavigationMenu: state.context.enableNavigationMenu,
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
const RelationshipsBrowserInner = memo(() => {
|
|
141
|
+
const browser = useRelationshipsBrowser()
|
|
142
|
+
const {
|
|
143
|
+
subjectId,
|
|
144
|
+
closeable,
|
|
145
|
+
enableNavigationMenu,
|
|
146
|
+
} = useRelationshipsBrowserState(selector2)
|
|
147
|
+
|
|
148
|
+
const layouted = useRelationshipsView(subjectId)
|
|
149
|
+
const [historySubjectId, historyOps, { history, current }] = useStateHistory(subjectId)
|
|
150
|
+
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
if (historySubjectId !== subjectId) {
|
|
153
|
+
historyOps.set(subjectId)
|
|
154
|
+
}
|
|
155
|
+
}, [subjectId])
|
|
156
|
+
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (historySubjectId !== subjectId) {
|
|
159
|
+
browser.navigateTo(historySubjectId)
|
|
160
|
+
}
|
|
161
|
+
}, [historySubjectId])
|
|
162
|
+
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
browser.send({ type: 'update.view', layouted })
|
|
165
|
+
}, [layouted])
|
|
166
|
+
|
|
167
|
+
const hasStepBack = current > 0
|
|
168
|
+
const hasStepForward = current + 1 < history.length
|
|
169
|
+
|
|
170
|
+
// useLifecycleLogger('RelationshipsBrowserInner.browser', [browser])
|
|
171
|
+
// useLifecycleLogger('RelationshipsBrowserInner.layouted', [layouted])
|
|
172
|
+
// useLifecycleLogger('RelationshipsBrowserInner', [
|
|
173
|
+
// historySubjectId,
|
|
174
|
+
// subjectId,
|
|
175
|
+
// closeable,
|
|
176
|
+
// enableNavigationMenu,
|
|
177
|
+
// ])
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<>
|
|
181
|
+
<TopLeftPanel
|
|
182
|
+
enableNavigationMenu={enableNavigationMenu}
|
|
183
|
+
subjectId={subjectId}
|
|
184
|
+
hasStepBack={hasStepBack}
|
|
185
|
+
hasStepForward={hasStepForward}
|
|
186
|
+
onStepBack={() => historyOps.back()}
|
|
187
|
+
onStepForward={() => historyOps.forward()}
|
|
188
|
+
/>
|
|
189
|
+
{closeable && (
|
|
190
|
+
<Panel position="top-right">
|
|
191
|
+
<ActionIcon
|
|
192
|
+
variant="default"
|
|
193
|
+
color="gray"
|
|
194
|
+
onClick={(e) => {
|
|
195
|
+
e.stopPropagation()
|
|
196
|
+
browser.close()
|
|
197
|
+
}}>
|
|
198
|
+
<IconX />
|
|
199
|
+
</ActionIcon>
|
|
200
|
+
</Panel>
|
|
201
|
+
)}
|
|
202
|
+
</>
|
|
203
|
+
)
|
|
204
|
+
})
|
|
205
|
+
|
|
169
206
|
type TopLeftPanelProps = {
|
|
170
207
|
enableNavigationMenu: boolean
|
|
171
208
|
subjectId: Fqn
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Color, DiagramNode, Fqn, RelationshipLineType, ViewId } from '@likec4/core'
|
|
1
|
+
import type { Color, DiagramNode, Fqn, NonEmptyArray, RelationId, RelationshipLineType, ViewId } from '@likec4/core'
|
|
2
2
|
import type { Base, NonOptional, ReactFlowEdge, ReactFlowNode } from '../../base/types'
|
|
3
3
|
|
|
4
4
|
export namespace RelationshipsBrowserTypes {
|
|
@@ -79,6 +79,7 @@ export namespace RelationshipsBrowserTypes {
|
|
|
79
79
|
// Extend the edge types provided by SharedFlowTypes with RelationshipsOfEdgeData
|
|
80
80
|
|
|
81
81
|
export type EdgeData = Base.EdgeData<{
|
|
82
|
+
relations: NonEmptyArray<RelationId>
|
|
82
83
|
color: Color | undefined
|
|
83
84
|
label: string | null
|
|
84
85
|
navigateTo: ViewId | null
|