@likec4/diagram 1.20.0 → 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/edges/utils.js +0 -1
- 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/SelectElement.js +0 -1
- 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/edges/utils.ts +0 -1
- 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/SelectElement.tsx +0 -1
- 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
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Link as LinkData } from '@likec4/core'
|
|
2
|
+
import { ActionIcon, Anchor, Box, CopyButton, Text } from '@mantine/core'
|
|
3
|
+
import { IconCheck, IconCopy } from '@tabler/icons-react'
|
|
4
|
+
import { stopPropagation } from '../utils'
|
|
5
|
+
import * as css from './Link.css'
|
|
6
|
+
|
|
7
|
+
export function Link({
|
|
8
|
+
value,
|
|
9
|
+
size = 'md',
|
|
10
|
+
}: {
|
|
11
|
+
size?: 'sm' | 'md'
|
|
12
|
+
value: LinkData
|
|
13
|
+
}) {
|
|
14
|
+
const isNormalSize = size === 'md'
|
|
15
|
+
const url = new URL(value.url, window.location.href).toString()
|
|
16
|
+
return (
|
|
17
|
+
<CopyButton value={url}>
|
|
18
|
+
{({ copied, copy }) => (
|
|
19
|
+
<Anchor
|
|
20
|
+
href={url}
|
|
21
|
+
target="_blank"
|
|
22
|
+
underline="never"
|
|
23
|
+
className={css.elementLink}
|
|
24
|
+
data-size={size}
|
|
25
|
+
onClick={stopPropagation}>
|
|
26
|
+
<ActionIcon
|
|
27
|
+
className={css.linkIcon}
|
|
28
|
+
tabIndex={-1}
|
|
29
|
+
size={isNormalSize ? 24 : 20}
|
|
30
|
+
variant={copied ? 'light' : 'subtle'}
|
|
31
|
+
color={copied ? 'teal' : 'gray'}
|
|
32
|
+
onClick={e => {
|
|
33
|
+
e.stopPropagation()
|
|
34
|
+
e.preventDefault()
|
|
35
|
+
copy()
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
{copied ? <IconCheck /> : <IconCopy style={{ width: '65%', opacity: 0.65 }} />}
|
|
39
|
+
</ActionIcon>
|
|
40
|
+
<Box className={css.linkTitleBox}>
|
|
41
|
+
<Text
|
|
42
|
+
component="div"
|
|
43
|
+
fz={isNormalSize ? 'xs' : 11}
|
|
44
|
+
truncate
|
|
45
|
+
lh={isNormalSize ? 1.3 : 1.2}
|
|
46
|
+
fw={value.title ? 500 : 400}>
|
|
47
|
+
{value.title || url}
|
|
48
|
+
</Text>
|
|
49
|
+
{value.title && (
|
|
50
|
+
<Text component="div" fz={isNormalSize ? 10 : 9} c={'dimmed'} lh={isNormalSize ? 1.2 : 1.1} truncate>
|
|
51
|
+
{url}
|
|
52
|
+
</Text>
|
|
53
|
+
)}
|
|
54
|
+
</Box>
|
|
55
|
+
</Anchor>
|
|
56
|
+
)}
|
|
57
|
+
</CopyButton>
|
|
58
|
+
)
|
|
59
|
+
// <Anchor href={value.url} fz={'sm'}>
|
|
60
|
+
// {value.title || url}
|
|
61
|
+
// </Anchor><Button variant='default' size='sm'>
|
|
62
|
+
// {value.title || value.url}
|
|
63
|
+
// </Button>
|
|
64
|
+
// </Box>
|
|
65
|
+
// )
|
|
66
|
+
}
|
package/src/base/const.ts
CHANGED
package/src/base/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ActionIcon, Box } from '@mantine/core'
|
|
|
2
2
|
import { IconZoomScan } from '@tabler/icons-react'
|
|
3
3
|
import clsx from 'clsx'
|
|
4
4
|
import { m } from 'framer-motion'
|
|
5
|
+
import { hiddenIfZoomTooSmall } from '../../../LikeC4Diagram.css'
|
|
5
6
|
import { stopPropagation } from '../../../utils/xyflow'
|
|
6
7
|
import type { NodeProps } from '../../types'
|
|
7
8
|
import * as css from './CompoundActionButton.css'
|
|
@@ -19,7 +20,7 @@ export function CompoundActionButton({
|
|
|
19
20
|
onClick,
|
|
20
21
|
}: CompoundActionButtonProps) {
|
|
21
22
|
return (
|
|
22
|
-
<Box className={clsx(css.container, 'compound-action')}>
|
|
23
|
+
<Box className={clsx(css.container, hiddenIfZoomTooSmall, 'compound-action')}>
|
|
23
24
|
<Box
|
|
24
25
|
className={clsx('nodrag nopan', css.actionButton)}
|
|
25
26
|
component={m.div}
|
|
@@ -2,6 +2,7 @@ import { ActionIcon, Box } from '@mantine/core'
|
|
|
2
2
|
import { IconId } from '@tabler/icons-react'
|
|
3
3
|
import clsx from 'clsx'
|
|
4
4
|
import { m } from 'framer-motion'
|
|
5
|
+
import { hiddenIfZoomTooSmall } from '../../../LikeC4Diagram.css'
|
|
5
6
|
import { stopPropagation } from '../../../utils/xyflow'
|
|
6
7
|
import type { NodeProps } from '../../types'
|
|
7
8
|
import * as css from './CompoundDetailsButton.css'
|
|
@@ -19,7 +20,7 @@ export function CompoundDetailsButton({
|
|
|
19
20
|
onClick,
|
|
20
21
|
}: CompoundDetailsButtonProps) {
|
|
21
22
|
return (
|
|
22
|
-
<Box className={clsx(css.container, 'details-button')} onClick={stopPropagation}>
|
|
23
|
+
<Box className={clsx(css.container, hiddenIfZoomTooSmall, 'details-button')} onClick={stopPropagation}>
|
|
23
24
|
<Box
|
|
24
25
|
component={m.div}
|
|
25
26
|
className={clsx('nodrag nopan', css.actionIconWrapper)}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { rem } from '@mantine/core'
|
|
2
2
|
import { createVar, fallbackVar, globalStyle, keyframes, style } from '@vanilla-extract/css'
|
|
3
3
|
import { calc } from '@vanilla-extract/css-utils'
|
|
4
|
-
import { easings, mantine,
|
|
4
|
+
import { easings, mantine, vars, whereLight } from '../../../theme-vars'
|
|
5
5
|
|
|
6
6
|
export const varCompoundOpacity = createVar('opacity')
|
|
7
7
|
export const varBorderRadius = createVar('borderRadius')
|
|
@@ -9,15 +9,6 @@ export const varBorderTransparency = createVar('borderTransparency')
|
|
|
9
9
|
export const varBorderColor = createVar('border-color')
|
|
10
10
|
const varBorderWidth = createVar('border-width')
|
|
11
11
|
|
|
12
|
-
export const dimmed = style({})
|
|
13
|
-
globalStyle(`.react-flow__node:has(${dimmed})`, {
|
|
14
|
-
opacity: 0.25,
|
|
15
|
-
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
16
|
-
transitionDelay: '200ms',
|
|
17
|
-
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
|
|
18
|
-
willChange: 'opacity, filter',
|
|
19
|
-
})
|
|
20
|
-
|
|
21
12
|
export const container = style({
|
|
22
13
|
position: 'relative',
|
|
23
14
|
width: '100%',
|
|
@@ -31,6 +22,21 @@ export const container = style({
|
|
|
31
22
|
},
|
|
32
23
|
})
|
|
33
24
|
|
|
25
|
+
globalStyle(`.react-flow__node:has(${container}[data-likec4-dimmed="true"])`, {
|
|
26
|
+
opacity: 0.25,
|
|
27
|
+
transition: 'opacity 600ms ease-in-out, filter 600ms ease-in-out',
|
|
28
|
+
transitionDelay: '200ms',
|
|
29
|
+
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
|
|
30
|
+
willChange: 'opacity, filter',
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
globalStyle(`.react-flow__node:has(${container}[data-likec4-dimmed="immediate"])`, {
|
|
34
|
+
opacity: 0.25,
|
|
35
|
+
transition: 'opacity 100ms ease-in-out, filter 100ms ease-in-out',
|
|
36
|
+
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(1px)')}`,
|
|
37
|
+
willChange: 'opacity, filter',
|
|
38
|
+
})
|
|
39
|
+
|
|
34
40
|
const outlineColor = fallbackVar(
|
|
35
41
|
mantine.colors.primaryColors.outline,
|
|
36
42
|
mantine.colors.primaryColors.filled,
|
|
@@ -54,7 +54,6 @@ export const CompoundNodeContainer = createPolymorphicComponent<'div', CompoundN
|
|
|
54
54
|
component={m.div}
|
|
55
55
|
className={clsx([
|
|
56
56
|
css.container,
|
|
57
|
-
isDimmed && css.dimmed,
|
|
58
57
|
'likec4-compound-node',
|
|
59
58
|
])}
|
|
60
59
|
initial={false}
|
|
@@ -62,6 +61,7 @@ export const CompoundNodeContainer = createPolymorphicComponent<'div', CompoundN
|
|
|
62
61
|
data-likec4-color={data.color}
|
|
63
62
|
data-compound-depth={data.depth ?? 1}
|
|
64
63
|
data-compound-transparent={isTransparent}
|
|
64
|
+
data-likec4-dimmed={isDimmed}
|
|
65
65
|
data-likec4-shape={data.shape}
|
|
66
66
|
style={assignInlineVars({
|
|
67
67
|
[css.varCompoundOpacity]: opacity.toFixed(2),
|
|
@@ -17,6 +17,8 @@ export const edgePropsEqual = <P extends Record<string, unknown> = Base.EdgeData
|
|
|
17
17
|
&& eq(prev.target, next.target)
|
|
18
18
|
&& eq(prev.targetHandleId ?? null, next.targetHandleId ?? null)
|
|
19
19
|
&& eq(prev.selected ?? false, next.selected ?? false)
|
|
20
|
+
&& eq(prev.sourcePosition, next.sourcePosition)
|
|
21
|
+
&& eq(prev.targetPosition, next.targetPosition)
|
|
20
22
|
&& isSame(prev.sourceX, next.sourceX)
|
|
21
23
|
&& isSame(prev.sourceY, next.sourceY)
|
|
22
24
|
&& isSame(prev.targetX, next.targetX)
|
|
@@ -11,6 +11,9 @@ export const nodePropsEqual = <P extends Record<string, unknown> = Base.NodeData
|
|
|
11
11
|
&& eq(prev.dragging ?? false, next.dragging ?? false)
|
|
12
12
|
&& eq(prev.width ?? 0, next.width ?? 0)
|
|
13
13
|
&& eq(prev.height ?? 0, next.height ?? 0)
|
|
14
|
+
&& eq(prev.zIndex, next.zIndex)
|
|
15
|
+
&& eq(prev.positionAbsoluteX, next.positionAbsoluteX)
|
|
16
|
+
&& eq(prev.positionAbsoluteY, next.positionAbsoluteY)
|
|
14
17
|
&& eq(prev.data, next.data)
|
|
15
18
|
)
|
|
16
19
|
|
|
@@ -6,6 +6,7 @@ import clsx from 'clsx'
|
|
|
6
6
|
import { type PropsWithChildren, forwardRef } from 'react'
|
|
7
7
|
import { isNumber, isTruthy } from 'remeda'
|
|
8
8
|
import type { UndefinedOnPartialDeep } from 'type-fest'
|
|
9
|
+
import { useIsZoomTooSmall } from '../../../hooks/useXYFlow'
|
|
9
10
|
import { ZIndexes } from '../../const'
|
|
10
11
|
import type { EdgeProps } from '../../types'
|
|
11
12
|
import * as css from './EdgeLabel.css'
|
|
@@ -59,6 +60,7 @@ export const EdgeLabel = createPolymorphicComponent<'div', EdgeLabelProps>(
|
|
|
59
60
|
children,
|
|
60
61
|
...rest
|
|
61
62
|
}, ref) => {
|
|
63
|
+
const isZoomTooSmall = useIsZoomTooSmall()
|
|
62
64
|
const stepNum = isStepEdgeId(id) ? extractStep(id) : null
|
|
63
65
|
|
|
64
66
|
let zIndex = ZIndexes.Edge
|
|
@@ -70,7 +72,7 @@ export const EdgeLabel = createPolymorphicComponent<'div', EdgeLabelProps>(
|
|
|
70
72
|
let labelX = labelXY?.x ?? labelBBox?.x,
|
|
71
73
|
labelY = labelXY?.y ?? labelBBox?.y
|
|
72
74
|
|
|
73
|
-
if (labelX === undefined || labelY === undefined) {
|
|
75
|
+
if (labelX === undefined || labelY === undefined || isZoomTooSmall) {
|
|
74
76
|
return null
|
|
75
77
|
}
|
|
76
78
|
|
|
@@ -18,6 +18,7 @@ type Data = UndefinedOnPartialDeep<
|
|
|
18
18
|
|
|
19
19
|
type EdgePathProps = EdgeProps<Data> & {
|
|
20
20
|
svgPath: string
|
|
21
|
+
strokeWidth?: number
|
|
21
22
|
onEdgePointerDown?: PointerEventHandler<SVGGElement> | undefined
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -29,6 +30,7 @@ export const EdgePath = forwardRef<SVGPathElement, EdgePathProps>(({
|
|
|
29
30
|
tail,
|
|
30
31
|
head,
|
|
31
32
|
},
|
|
33
|
+
strokeWidth,
|
|
32
34
|
svgPath,
|
|
33
35
|
style,
|
|
34
36
|
interactionWidth,
|
|
@@ -78,6 +80,7 @@ export const EdgePath = forwardRef<SVGPathElement, EdgePathProps>(({
|
|
|
78
80
|
className={clsx('react-flow__edge-path', css.cssEdgePath)}
|
|
79
81
|
d={svgPath}
|
|
80
82
|
style={style}
|
|
83
|
+
strokeWidth={strokeWidth}
|
|
81
84
|
strokeLinecap={'round'}
|
|
82
85
|
strokeDasharray={strokeDasharray}
|
|
83
86
|
markerStart={MarkerStart ? `url(#start${id})` : undefined}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ActionIcon, Box } from '@mantine/core'
|
|
2
2
|
import clsx from 'clsx'
|
|
3
3
|
import { m } from 'framer-motion'
|
|
4
|
+
import { hiddenIfZoomTooSmall } from '../../../LikeC4Diagram.css'
|
|
4
5
|
import { stopPropagation } from '../../../utils/xyflow'
|
|
5
6
|
import type { NodeProps } from '../../types'
|
|
6
7
|
import * as css from './ElementActionButtons.css'
|
|
@@ -21,7 +22,7 @@ export function ElementActionButtons({
|
|
|
21
22
|
return null
|
|
22
23
|
}
|
|
23
24
|
return (
|
|
24
|
-
<Box className={css.container}>
|
|
25
|
+
<Box className={clsx(css.container, hiddenIfZoomTooSmall)}>
|
|
25
26
|
<Box
|
|
26
27
|
component={m.div}
|
|
27
28
|
layoutRoot
|
|
@@ -2,6 +2,7 @@ import { ActionIcon, Box } from '@mantine/core'
|
|
|
2
2
|
import { IconId } from '@tabler/icons-react'
|
|
3
3
|
import clsx from 'clsx'
|
|
4
4
|
import { m } from 'framer-motion'
|
|
5
|
+
import { hiddenIfZoomTooSmall } from '../../../LikeC4Diagram.css'
|
|
5
6
|
import { stopPropagation } from '../../../utils/xyflow'
|
|
6
7
|
import type { NodeProps } from '../../types'
|
|
7
8
|
import * as css from './ElementDetailsButton.css'
|
|
@@ -20,7 +21,7 @@ export function ElementDetailsButton({
|
|
|
20
21
|
onClick,
|
|
21
22
|
}: ElementDetailsButtonProps) {
|
|
22
23
|
return (
|
|
23
|
-
<Box className={clsx(css.container, 'details-button')}>
|
|
24
|
+
<Box className={clsx(css.container, hiddenIfZoomTooSmall, 'details-button')}>
|
|
24
25
|
<Box
|
|
25
26
|
className={clsx('nodrag nopan', css.actionIconWrapper)}
|
|
26
27
|
component={m.div}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createVar, fallbackVar, globalStyle, style } from '@vanilla-extract/css'
|
|
2
|
-
import {
|
|
2
|
+
import { vars } from '../../../theme-vars'
|
|
3
3
|
|
|
4
4
|
export const stokeFillMix = createVar('stroke-fill-mix')
|
|
5
5
|
|
|
@@ -36,12 +36,17 @@ export const container = style({
|
|
|
36
36
|
},
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
globalStyle(`.react-flow__node:has(${dimmed})`, {
|
|
39
|
+
globalStyle(`.react-flow__node:has(${container}[data-likec4-dimmed="true"])`, {
|
|
42
40
|
opacity: 0.25,
|
|
43
41
|
transition: 'opacity 400ms ease-in-out, filter 500ms ease-in-out',
|
|
44
42
|
transitionDelay: '50ms',
|
|
45
43
|
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(2px)')}`,
|
|
46
44
|
willChange: 'opacity, filter',
|
|
47
45
|
})
|
|
46
|
+
|
|
47
|
+
globalStyle(`.react-flow__node:has(${container}[data-likec4-dimmed="immediate"])`, {
|
|
48
|
+
opacity: 0.25,
|
|
49
|
+
transition: 'opacity 100ms ease-in-out, filter 100ms ease-in-out',
|
|
50
|
+
filter: `grayscale(0.85) ${fallbackVar(vars.safariAnimationHook, 'blur(2px)')}`,
|
|
51
|
+
willChange: 'opacity, filter',
|
|
52
|
+
})
|
|
@@ -48,11 +48,9 @@ export const ElementNodeContainer = createPolymorphicComponent<'div', ElementNod
|
|
|
48
48
|
ref={ref}
|
|
49
49
|
className={clsx([
|
|
50
50
|
css.container,
|
|
51
|
-
isDimmed && css.dimmed,
|
|
52
51
|
'likec4-element-node',
|
|
53
52
|
])}
|
|
54
53
|
initial={false}
|
|
55
|
-
layoutRoot
|
|
56
54
|
{...selectable && {
|
|
57
55
|
animate: {
|
|
58
56
|
scale,
|
|
@@ -62,6 +60,7 @@ export const ElementNodeContainer = createPolymorphicComponent<'div', ElementNod
|
|
|
62
60
|
data-hovered={isHovered}
|
|
63
61
|
data-likec4-color={data.color}
|
|
64
62
|
data-likec4-shape={data.shape}
|
|
63
|
+
data-likec4-dimmed={isDimmed}
|
|
65
64
|
{...rest}
|
|
66
65
|
>
|
|
67
66
|
{children}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type DiagramNode, type ElementShape, nonexhaustive } from '@likec4/core'
|
|
2
|
+
import clsx from 'clsx'
|
|
3
|
+
import { hiddenIfZoomTooSmall } from '../../../LikeC4Diagram.css'
|
|
2
4
|
import { toDomPrecision } from '../../../utils/xyflow'
|
|
3
|
-
import type { NodeProps } from '../../types'
|
|
4
5
|
import * as css from './ElementShape.css'
|
|
5
6
|
|
|
6
7
|
export function cylinderSVGPath(diameter: number, height: number, tilt = 0.065) {
|
|
@@ -184,7 +185,7 @@ export function ElementShape(
|
|
|
184
185
|
return (
|
|
185
186
|
<>
|
|
186
187
|
{isMultiple && (
|
|
187
|
-
<svg className={css.shapeSvgMultiple} viewBox={`0 0 ${w} ${h}`}>
|
|
188
|
+
<svg className={clsx(css.shapeSvgMultiple, hiddenIfZoomTooSmall)} viewBox={`0 0 ${w} ${h}`}>
|
|
188
189
|
<ElementShapeSvg shape={data.shape} w={w} h={h} />
|
|
189
190
|
</svg>
|
|
190
191
|
)}
|
|
@@ -222,5 +223,5 @@ export function SelectedIndicator({ shape, w, h }: ElementShapeProps) {
|
|
|
222
223
|
break
|
|
223
224
|
}
|
|
224
225
|
}
|
|
225
|
-
return <g className={css.indicator}>{svg}</g>
|
|
226
|
+
return <g className={clsx(css.indicator, hiddenIfZoomTooSmall)}>{svg}</g>
|
|
226
227
|
}
|
|
@@ -4,6 +4,7 @@ import { assignInlineVars } from '@vanilla-extract/dynamic'
|
|
|
4
4
|
import clsx from 'clsx'
|
|
5
5
|
import { isNumber, isTruthy } from 'remeda'
|
|
6
6
|
import { IconRenderer } from '../../../context/IconRenderer'
|
|
7
|
+
import { hiddenIfZoomTooSmall } from '../../../LikeC4Diagram.css'
|
|
7
8
|
import type { NodeProps } from '../../types'
|
|
8
9
|
import * as css from './ElementTitle.css'
|
|
9
10
|
|
|
@@ -57,7 +58,7 @@ export function ElementTitle({ id, data, iconSize }: ElementTitleProps) {
|
|
|
57
58
|
{data.technology && (
|
|
58
59
|
<Text
|
|
59
60
|
component="div"
|
|
60
|
-
className={clsx(css.technology, 'likec4-element-technology')}>
|
|
61
|
+
className={clsx(css.technology, hiddenIfZoomTooSmall, 'likec4-element-technology')}>
|
|
61
62
|
{data.technology}
|
|
62
63
|
</Text>
|
|
63
64
|
)}
|
package/src/base/updateEdges.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { deepEqual as eq } from 'fast-equals'
|
|
2
|
-
import { isDefined } from 'remeda'
|
|
2
|
+
import { isDefined, omit } from 'remeda'
|
|
3
3
|
import type { Base } from './types'
|
|
4
4
|
|
|
5
5
|
function _update<E extends Base.Edge>(current: E[], update: E[]): E[] {
|
|
@@ -12,12 +12,13 @@ function _update<E extends Base.Edge>(current: E[], update: E[]): E[] {
|
|
|
12
12
|
&& eq(existing.sourceHandle ?? null, next.sourceHandle ?? null)
|
|
13
13
|
&& eq(existing.target, next.target)
|
|
14
14
|
&& eq(existing.targetHandle ?? null, next.targetHandle ?? null)
|
|
15
|
+
&& eq(existing.zIndex ?? 0, next.zIndex ?? 0)
|
|
15
16
|
&& eq(existing.data, next.data)
|
|
16
17
|
) {
|
|
17
18
|
return existing
|
|
18
19
|
}
|
|
19
20
|
return {
|
|
20
|
-
...existing,
|
|
21
|
+
...omit(existing, ['hidden', 'zIndex']),
|
|
21
22
|
...next,
|
|
22
23
|
data: {
|
|
23
24
|
...existing.data,
|
package/src/base/updateNodes.ts
CHANGED
|
@@ -12,15 +12,16 @@ function _update<N extends Base.Node>(current: N[], updated: N[]): N[] {
|
|
|
12
12
|
eq(existing.type, update.type)
|
|
13
13
|
&& eq(existingWidth, update.initialWidth)
|
|
14
14
|
&& eq(existingHeight, update.initialHeight)
|
|
15
|
+
&& eq(existing.parentId ?? null, update.parentId ?? null)
|
|
15
16
|
&& eq(existing.hidden ?? false, update.hidden ?? false)
|
|
17
|
+
&& eq(existing.zIndex ?? 0, update.zIndex ?? 0)
|
|
16
18
|
&& eq(existing.position, update.position)
|
|
17
19
|
&& eq(existing.data, update.data)
|
|
18
|
-
&& eq(existing.parentId ?? null, update.parentId ?? null)
|
|
19
20
|
) {
|
|
20
21
|
return existing
|
|
21
22
|
}
|
|
22
23
|
return {
|
|
23
|
-
...omit(existing, ['measured', 'parentId']),
|
|
24
|
+
...omit(existing, ['measured', 'parentId', 'hidden', 'zIndex']),
|
|
24
25
|
...update,
|
|
25
26
|
// Force dimensions from update
|
|
26
27
|
width: update.initialWidth,
|
|
@@ -6,7 +6,7 @@ export const FramerMotionConfig = ({ children }: PropsWithChildren) => {
|
|
|
6
6
|
const nonce = useMantineStyleNonce()?.()
|
|
7
7
|
return (
|
|
8
8
|
<LazyMotion features={domMax} strict>
|
|
9
|
-
<MotionConfig
|
|
9
|
+
<MotionConfig {...nonce && { nonce }}>
|
|
10
10
|
{children}
|
|
11
11
|
</MotionConfig>
|
|
12
12
|
</LazyMotion>
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { useCallbackRef } from '@mantine/hooks'
|
|
2
2
|
import { useSelector as useXstateSelector } from '@xstate/react'
|
|
3
3
|
import { shallowEqual } from 'fast-equals'
|
|
4
|
-
import { useActorRef } from '../likec4diagram/state/actorContext'
|
|
5
|
-
import { useSelector } from '../likec4diagram/state/actorContext'
|
|
4
|
+
import { useActorRef, useSelector } from '../likec4diagram/state/actorContext'
|
|
6
5
|
import type { LikeC4ViewActorRef } from '../likec4diagram/state/machine'
|
|
7
6
|
import { type MachineSnapshot } from '../likec4diagram/state/machine'
|
|
8
7
|
import type { SyncLayoutActorSnapshot } from '../likec4diagram/state/syncManualLayoutActor'
|
|
@@ -10,14 +9,17 @@ import type { SyncLayoutActorSnapshot } from '../likec4diagram/state/syncManualL
|
|
|
10
9
|
export function useDiagramActor(): LikeC4ViewActorRef {
|
|
11
10
|
return useActorRef()
|
|
12
11
|
}
|
|
13
|
-
export function useDiagramActorState<T
|
|
12
|
+
export function useDiagramActorState<T = unknown>(
|
|
13
|
+
selector: (state: MachineSnapshot) => T,
|
|
14
|
+
compare: (a: NoInfer<T>, b: NoInfer<T>) => boolean = shallowEqual,
|
|
15
|
+
): T {
|
|
14
16
|
const select = useCallbackRef((s: MachineSnapshot) => selector(s))
|
|
15
17
|
return useSelector(select, compare)
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
export function useDiagramSyncLayoutState<T>(
|
|
20
|
+
export function useDiagramSyncLayoutState<T = unknown>(
|
|
19
21
|
selector: (state: SyncLayoutActorSnapshot) => T,
|
|
20
|
-
compare = shallowEqual,
|
|
22
|
+
compare: (a: NoInfer<T>, b: NoInfer<T>) => boolean = shallowEqual,
|
|
21
23
|
): T {
|
|
22
24
|
const syncLayoutActorRef = useDiagramActorState(s => s.context.syncLayoutActorRef)
|
|
23
25
|
const select = useCallbackRef(selector)
|
|
@@ -3,7 +3,10 @@ import { shallowEqual } from 'fast-equals'
|
|
|
3
3
|
import { useSelector } from '../likec4diagram/state/actorContext'
|
|
4
4
|
import { type DiagramContext, type MachineSnapshot } from '../likec4diagram/state/machine'
|
|
5
5
|
|
|
6
|
-
export function useDiagramContext<T
|
|
6
|
+
export function useDiagramContext<T = unknown>(
|
|
7
|
+
selector: (state: DiagramContext) => T,
|
|
8
|
+
compare: (a: NoInfer<T>, b: NoInfer<T>) => boolean = shallowEqual,
|
|
9
|
+
) {
|
|
7
10
|
const select = useCallbackRef((s: MachineSnapshot) => selector(s.context))
|
|
8
11
|
return useSelector(select, compare)
|
|
9
12
|
}
|
package/src/hooks/useXYFlow.ts
CHANGED
|
@@ -7,7 +7,7 @@ export const useXYFlow = useReactFlow<Types.Node, Types.Edge>
|
|
|
7
7
|
|
|
8
8
|
export function useXYStore<StateSlice = unknown>(
|
|
9
9
|
selector: (state: ReactFlowState<Types.Node, Types.Edge>) => StateSlice,
|
|
10
|
-
equalityFn?: (a: StateSlice
|
|
10
|
+
equalityFn?: (a: NoInfer<StateSlice>, b: NoInfer<StateSlice>) => boolean,
|
|
11
11
|
): StateSlice {
|
|
12
12
|
return useStore(
|
|
13
13
|
useCallbackRef(selector as any),
|
|
@@ -19,3 +19,8 @@ export const useXYStoreApi = useStoreApi<Types.Node, Types.Edge>
|
|
|
19
19
|
export type XYStoreApi = ReturnType<typeof useXYStoreApi>
|
|
20
20
|
|
|
21
21
|
export const useXYInternalNode = useInternalNode<Types.Node>
|
|
22
|
+
|
|
23
|
+
const selectZoom = (state: ReactFlowState) => state.transform[2] < 0.2
|
|
24
|
+
export function useIsZoomTooSmall() {
|
|
25
|
+
return useStore(selectZoom)
|
|
26
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type EdgeId, type NodeId, nonNullable } from '@likec4/core'
|
|
2
2
|
import { useCallbackRef } from '@mantine/hooks'
|
|
3
3
|
import clsx from 'clsx'
|
|
4
|
+
import { shallowEqual } from 'fast-equals'
|
|
4
5
|
import type { EnforceOptional } from 'type-fest/source/enforce-optional'
|
|
5
6
|
import { BaseXYFlow } from '../base'
|
|
6
7
|
import { useDiagramEventHandlers, useEnabledFeature } from '../context'
|
|
@@ -19,7 +20,18 @@ const selectXYProps = (ctx: DiagramContext) => ({
|
|
|
19
20
|
pannable: ctx.pannable,
|
|
20
21
|
zoomable: ctx.zoomable,
|
|
21
22
|
fitViewPadding: ctx.fitViewPadding,
|
|
23
|
+
enableFitView: ctx.features.enableFitView,
|
|
24
|
+
enableReadOnly: ctx.features.enableReadOnly,
|
|
22
25
|
})
|
|
26
|
+
const equalsXYProps = (a: ReturnType<typeof selectXYProps>, b: ReturnType<typeof selectXYProps>): boolean =>
|
|
27
|
+
a.initialized === b.initialized &&
|
|
28
|
+
a.pannable === b.pannable &&
|
|
29
|
+
a.zoomable === b.zoomable &&
|
|
30
|
+
a.fitViewPadding === b.fitViewPadding &&
|
|
31
|
+
a.enableFitView === b.enableFitView &&
|
|
32
|
+
a.enableReadOnly === b.enableReadOnly &&
|
|
33
|
+
shallowEqual(a.nodes, b.nodes) &&
|
|
34
|
+
shallowEqual(a.edges, b.edges)
|
|
23
35
|
|
|
24
36
|
type Picked = EnforceOptional<
|
|
25
37
|
Pick<
|
|
@@ -37,8 +49,10 @@ export const LikeC4DiagramXYFlow = ({ background, ...rest }: LikeC4DiagramXYFlow
|
|
|
37
49
|
initialized,
|
|
38
50
|
nodes,
|
|
39
51
|
edges,
|
|
52
|
+
enableReadOnly,
|
|
53
|
+
enableFitView,
|
|
40
54
|
...props
|
|
41
|
-
} = useDiagramContext(selectXYProps)
|
|
55
|
+
} = useDiagramContext(selectXYProps, equalsXYProps)
|
|
42
56
|
|
|
43
57
|
const {
|
|
44
58
|
onNodeContextMenu,
|
|
@@ -50,15 +64,14 @@ export const LikeC4DiagramXYFlow = ({ background, ...rest }: LikeC4DiagramXYFlow
|
|
|
50
64
|
onCanvasDblClick,
|
|
51
65
|
} = useDiagramEventHandlers()
|
|
52
66
|
|
|
53
|
-
const {
|
|
54
|
-
enableFitView,
|
|
55
|
-
enableReadOnly,
|
|
56
|
-
} = useEnabledFeature('ReadOnly', 'FitView')
|
|
57
|
-
|
|
58
67
|
const notReadOnly = !enableReadOnly
|
|
59
68
|
|
|
60
69
|
const layoutConstraints = useLayoutConstraints()
|
|
61
70
|
|
|
71
|
+
const onViewportResize = useCallbackRef(() => {
|
|
72
|
+
diagram.send({ type: 'xyflow.resized' })
|
|
73
|
+
})
|
|
74
|
+
|
|
62
75
|
return (
|
|
63
76
|
<BaseXYFlow<Types.Node, Types.Edge>
|
|
64
77
|
nodes={nodes}
|
|
@@ -126,9 +139,7 @@ export const LikeC4DiagramXYFlow = ({ background, ...rest }: LikeC4DiagramXYFlow
|
|
|
126
139
|
}),
|
|
127
140
|
}}
|
|
128
141
|
{...enableFitView && {
|
|
129
|
-
onViewportResize
|
|
130
|
-
diagram.send({ type: 'xyflow.resized' })
|
|
131
|
-
},
|
|
142
|
+
onViewportResize,
|
|
132
143
|
}}
|
|
133
144
|
{...(notReadOnly && rest.nodesDraggable && layoutConstraints)}
|
|
134
145
|
{...props}
|
|
@@ -27,12 +27,12 @@ import { IconArrowRight, IconFileSymlink, IconInfoCircle, IconZoomScan } from '@
|
|
|
27
27
|
import clsx from 'clsx'
|
|
28
28
|
import { type MouseEventHandler, type PropsWithChildren, forwardRef, Fragment, useCallback } from 'react'
|
|
29
29
|
import { filter, isTruthy, map, partition, pipe } from 'remeda'
|
|
30
|
+
import { Link } from '../../../base'
|
|
30
31
|
import { IfEnabled, useDiagramEventHandlers, useEnabledFeature } from '../../../context'
|
|
31
32
|
import { useMantinePortalProps } from '../../../hooks'
|
|
32
33
|
import { useDiagram } from '../../../hooks/useDiagram'
|
|
33
34
|
import { DiagramContext, useDiagramContext } from '../../../hooks/useDiagramContext'
|
|
34
35
|
import { useLikeC4Model } from '../../../likec4model'
|
|
35
|
-
import { Link } from '../../ui/diagram-title/Link'
|
|
36
36
|
import * as css from './RelationshipsDropdownMenu.css'
|
|
37
37
|
|
|
38
38
|
const stopPropagation: MouseEventHandler = (e) => e.stopPropagation()
|
|
@@ -252,7 +252,7 @@ const Relationship = forwardRef<
|
|
|
252
252
|
gap={3}
|
|
253
253
|
justify="stretch"
|
|
254
254
|
align="stretch">
|
|
255
|
-
{links.map((link) => <Link key={link.url}
|
|
255
|
+
{links.map((link) => <Link key={link.url} size="sm" value={link} />)}
|
|
256
256
|
</Stack>
|
|
257
257
|
)}
|
|
258
258
|
</Stack>
|
|
@@ -29,7 +29,6 @@ export function getNodeIntersectionFromCenterToPoint(
|
|
|
29
29
|
const nodeCenter = getNodeCenter(intersectionNode)
|
|
30
30
|
const { width, height } = getNodeDimensions(intersectionNode)
|
|
31
31
|
const v = new VectorImpl(target.x, target.y).sub(nodeCenter)
|
|
32
|
-
console.log(intersectionNode)
|
|
33
32
|
const xScale = (nodeMargin + (width || 0) / 2) / v.x
|
|
34
33
|
const yScale = (nodeMargin + (height || 0) / 2) / v.y
|
|
35
34
|
|
|
@@ -39,7 +39,7 @@ const ElementDetailsButtonWithHandler = ({ fqn, ...props }: NodeProps<Types.Node
|
|
|
39
39
|
export const nodeTypes = {
|
|
40
40
|
element: customNode<Types.ElementNodeData>((props) => (
|
|
41
41
|
<ElementNodeContainer nodeProps={props}>
|
|
42
|
-
<ElementShape {...props} />
|
|
42
|
+
<ElementShape {...props} isMultiple={props.data.isMultiple} />
|
|
43
43
|
<ElementTitle {...props} />
|
|
44
44
|
<ElementActions {...props} />
|
|
45
45
|
<ElementDetailsButtonWithHandler
|
|
@@ -53,7 +53,7 @@ export const nodeTypes = {
|
|
|
53
53
|
)),
|
|
54
54
|
deployment: customNode<Types.DeploymentElementNodeData>((props) => (
|
|
55
55
|
<ElementNodeContainer nodeProps={props}>
|
|
56
|
-
<ElementShape {...props} />
|
|
56
|
+
<ElementShape {...props} isMultiple={props.data.isMultiple} />
|
|
57
57
|
<ElementTitle {...props} />
|
|
58
58
|
<DeploymentElementActions {...props} />
|
|
59
59
|
{!!props.data.modelFqn && (
|