@likec4/diagram 1.0.0-rc.1 → 1.0.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 (38) hide show
  1. package/dist/LikeC4Diagram.d.ts +1 -1
  2. package/dist/LikeC4Diagram.props.d.ts +6 -3
  3. package/dist/StaticLikeC4Diagram.d.ts +1 -1
  4. package/dist/index.d.ts +1 -2
  5. package/dist/index.mjs +124 -221
  6. package/dist/mantine/EnsureMantine.d.ts +2 -3
  7. package/dist/style.css +4 -33
  8. package/dist/xyflow/nodes/element/ElementIcon.d.ts +1 -1
  9. package/dist/xyflow/types.d.ts +1 -1
  10. package/package.json +14 -12
  11. package/src/LikeC4Diagram.props.ts +12 -8
  12. package/src/LikeC4Diagram.tsx +3 -3
  13. package/src/StaticLikeC4Diagram.tsx +0 -1
  14. package/src/globals.css.ts +1 -1
  15. package/src/index.ts +1 -2
  16. package/src/mantine/EnsureMantine.tsx +8 -18
  17. package/src/mantine/theme.ts +1 -0
  18. package/src/ui/KeepAspectRatio.css.ts +2 -2
  19. package/src/ui/OptionsPanel.tsx +1 -1
  20. package/src/ui/options/NodeOptions.tsx +1 -2
  21. package/src/xyflow/FitviewOnDiagramChange.tsx +1 -1
  22. package/src/xyflow/SyncWithDiagram.tsx +23 -81
  23. package/src/xyflow/XYFlowEvents.tsx +1 -2
  24. package/src/xyflow/XYFlowWrapper.tsx +3 -7
  25. package/src/xyflow/edges/RelationshipEdge.tsx +4 -5
  26. package/src/xyflow/edges/edges.css.ts +7 -4
  27. package/src/xyflow/nodes/compound/CompoundNode.css.ts +3 -2
  28. package/src/xyflow/nodes/compound/CompoundNode.tsx +2 -2
  29. package/src/xyflow/nodes/element/ElementIcon.tsx +1 -3
  30. package/src/xyflow/nodes/element/ElementLink.css.ts +2 -2
  31. package/src/xyflow/nodes/element/ElementLink.tsx +3 -3
  32. package/src/xyflow/nodes/element/ElementNode.tsx +0 -2
  33. package/src/xyflow/nodes/element/element.css.ts +4 -4
  34. package/src/xyflow/types.ts +11 -2
  35. package/dist/EmbeddedLikeC4Diagram.css.d.ts +0 -5
  36. package/dist/EmbeddedLikeC4Diagram.d.ts +0 -17
  37. package/src/EmbeddedLikeC4Diagram.css.ts +0 -35
  38. package/src/EmbeddedLikeC4Diagram.tsx +0 -101
package/dist/style.css CHANGED
@@ -724,27 +724,6 @@ svg.react-flow__connectionline {
724
724
  --likec4-relation-lineColor: #6366f1;
725
725
  --likec4-relation-labelBg: #1e1b4b;
726
726
  --likec4-relation-labelColor: #818cf8;
727
- }._1yqpph0 {
728
- background-color: transparent;
729
- }
730
- ._1yqpph1 {
731
- padding: 0;
732
- height: 100%;
733
- }
734
- ._1yqpph2 {
735
- position: absolute;
736
- top: 1rem;
737
- left: 1rem;
738
- }
739
- ._1yqpph3 {
740
- position: absolute;
741
- top: 1rem;
742
- right: 1rem;
743
- }
744
- @media (min-width: 62em) {
745
- ._1yqpph4 {
746
- --cb-size: var(--cb-size-lg);
747
- }
748
727
  }.react-flow._2h8g8h0 {
749
728
  --xy-background-color: var(--likec4-background-color);
750
729
  --xy-background-pattern-color: var(--likec4-background-pattern-color);
@@ -761,9 +740,7 @@ svg.react-flow__connectionline {
761
740
  display: none;
762
741
  }._14qhjro0 {
763
742
  width: 100%;
764
- width: -webkit-fill-available;
765
743
  height: 100%;
766
- height: -webkit-fill-available;
767
744
  padding: 0;
768
745
  }._1lmzt1h0 {
769
746
  top: var(--likec4-options-panel-top, 1rem);
@@ -779,7 +756,7 @@ svg.react-flow__connectionline {
779
756
  }
780
757
  [data-mantine-color-scheme="dark"] .bexn7q0 {
781
758
  --xy-edge-label-color: var(--likec4-relation-labelColor);
782
- --xy-edge-label-background-color: color-mix(in srgb, var(--likec4-relation-labelBg), transparent 75%);
759
+ --xy-edge-label-background-color: color-mix(in srgb, var(--likec4-relation-labelBg), transparent 55%);
783
760
  }
784
761
  .bexn7q0[data-edge-hovered='true'] {
785
762
  --xy-edge-stroke: color-mix(in srgb, var(--likec4-relation-lineColor), white 30%);
@@ -789,6 +766,9 @@ svg.react-flow__connectionline {
789
766
  --xy-edge-stroke-width: 2.5;
790
767
  }
791
768
  .react-flow__edges > svg {
769
+ mix-blend-mode: screen;
770
+ }
771
+ :where([data-mantine-color-scheme="dark"]) .react-flow__edges > svg {
792
772
  mix-blend-mode: lighten;
793
773
  }
794
774
  .bexn7q1 {
@@ -825,9 +805,6 @@ svg.react-flow__connectionline {
825
805
  transform: var(--bexn7q6);
826
806
  will-change: transform;
827
807
  }
828
- [data-mantine-color-scheme="dark"] .bexn7q7 {
829
- mix-blend-mode: luminosity;
830
- }
831
808
  .bexn7q7[data-edge-hovered="true"] {
832
809
  transition: all 140ms ease-out;
833
810
  transform: var(--bexn7q6) scale(1.1);
@@ -847,9 +824,7 @@ svg.react-flow__connectionline {
847
824
  }
848
825
  .lbtzdo0 {
849
826
  width: 100%;
850
- width: -webkit-fill-available;
851
827
  height: 100%;
852
- height: -webkit-fill-available;
853
828
  position: relative;
854
829
  padding: 0;
855
830
  margin: 0;
@@ -992,9 +967,7 @@ svg.react-flow__connectionline {
992
967
  --_1b6tkn0: color-mix(in srgb, var(--likec4-element-stroke) 90%, var(--likec4-element-fill));
993
968
  position: relative;
994
969
  width: 100%;
995
- width: -webkit-fill-available;
996
970
  height: 100%;
997
- height: -webkit-fill-available;
998
971
  padding: 0;
999
972
  margin: 0;
1000
973
  }
@@ -1076,9 +1049,7 @@ svg.react-flow__connectionline {
1076
1049
  --_1b6tkn1: calc(1.5rem * var(--mantine-scale));
1077
1050
  position: relative;
1078
1051
  width: 100%;
1079
- width: -webkit-fill-available;
1080
1052
  height: 100%;
1081
- height: -webkit-fill-available;
1082
1053
  padding: var(--_1b6tkn1);
1083
1054
  display: flex;
1084
1055
  flex-direction: column;
@@ -6,5 +6,5 @@ type ElementIconProps = {
6
6
  paddingX?: number | undefined;
7
7
  paddingY?: number | undefined;
8
8
  };
9
- export declare function ElementIcon({ node, maxWidth, paddingX, paddingY }: ElementIconProps): JSX.Element | null;
9
+ export declare function ElementIcon({ node, paddingY }: ElementIconProps): JSX.Element | null;
10
10
  export {};
@@ -1,6 +1,6 @@
1
1
  import { SetReadonly, SetRequired, Simplify } from 'type-fest';
2
2
  import { MouseEvent as ReactMouseEvent } from 'react';
3
- import { Edge, InternalNode, Node, ReactFlowInstance, ReactFlowState, EdgeMouseHandler, NodeMouseHandler, OnMoveEnd, OnMoveStart } from '@xyflow/react';
3
+ import { Edge, EdgeMouseHandler, InternalNode, Node, NodeMouseHandler, OnMoveEnd, OnMoveStart, ReactFlowInstance, ReactFlowState } from '@xyflow/react';
4
4
  import { BBox, DiagramEdge, DiagramNode, Fqn, Point } from '@likec4/core';
5
5
 
6
6
  export type ElementNodeData = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@likec4/diagram",
3
- "version": "1.0.0-rc.1",
3
+ "version": "1.0.1",
4
4
  "license": "MIT",
5
5
  "bugs": "https://github.com/likec4/likec4/issues",
6
6
  "homepage": "https://likec4.dev",
@@ -40,19 +40,17 @@
40
40
  "./style.css": "./dist/style.css"
41
41
  }
42
42
  },
43
- "packageManager": "yarn@4.1.1",
43
+ "packageManager": "yarn@4.2.2",
44
44
  "scripts": {
45
45
  "typecheck": "tsc",
46
46
  "prepack": "vite build --mode production",
47
47
  "build:turbo": "run -T turbo build --filter='diagram'",
48
48
  "build": "vite build --mode development && tsc -b",
49
49
  "lint": "run -T eslint src/ --fix",
50
- "clean": "run -T rimraf -v dist/* .tsbuildinfo"
50
+ "clean": "run -T rimraf -v -g 'dist/*' .tsbuildinfo"
51
51
  },
52
52
  "dependencies": {
53
- "@likec4/core": "1.0.0-rc.1",
54
- "@mantine/core": "^7.9.0",
55
- "@mantine/hooks": "^7.9.0",
53
+ "@likec4/core": "1.0.1",
56
54
  "@react-hookz/web": "^24.0.4",
57
55
  "esm-env": "^1.0.0",
58
56
  "fast-equals": "^5.0.1",
@@ -61,27 +59,31 @@
61
59
  "zustand": "^4.5.2"
62
60
  },
63
61
  "peerDependencies": {
64
- "react": "^18.2.0",
65
- "react-dom": "^18.2.0"
62
+ "@mantine/core": "^7.9.0",
63
+ "@mantine/hooks": "^7.9.0",
64
+ "react": "^18.3.1",
65
+ "react-dom": "^18.3.1"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@lume/kiwi": "^0.4.3",
69
+ "@mantine/core": "^7.9.0",
70
+ "@mantine/hooks": "^7.9.0",
69
71
  "@mantine/vanilla-extract": "^7.9.0",
70
72
  "@total-typescript/ts-reset": "^0.5.1",
71
- "@types/react": "^18.2.0",
72
- "@types/react-dom": "^18.2.0",
73
+ "@types/react": "18.2.0",
74
+ "@types/react-dom": "18.2.4",
73
75
  "@vanilla-extract/css": "^1.15.1",
74
76
  "@vanilla-extract/dynamic": "^2.1.0",
75
77
  "@vanilla-extract/esbuild-plugin": "^2.3.5",
76
78
  "@vitejs/plugin-react": "^4.2.1",
77
- "@xyflow/react": "^12.0.0-next.17",
79
+ "@xyflow/react": "^12.0.0-next",
78
80
  "clsx": "^2.1.1",
79
81
  "execa": "^8.0.1",
80
82
  "khroma": "^2.1.0",
81
83
  "react": "^18.2.0",
82
84
  "react-dom": "^18.2.0",
83
85
  "tilg": "^0.1.1",
84
- "type-fest": "^4.18.1",
86
+ "type-fest": "^4.18.2",
85
87
  "typescript": "^5.4.5",
86
88
  "unbuild": "^2.0.0",
87
89
  "vite": "^5.2.11",
@@ -15,12 +15,14 @@ import type { XYBackgroundProps } from './xyflow/XYFlowBackground'
15
15
 
16
16
  export type DiagramNodeWithNavigate = Simplify<SetRequired<DiagramNode, 'navigateTo'>>
17
17
 
18
- export type OnNavigateTo = (
19
- to: ViewID,
20
- event: ReactMouseEvent,
21
- element: DiagramNodeWithNavigate,
22
- xynode: XYFlowNode
23
- ) => void
18
+ export type OnNavigateTo = {
19
+ (
20
+ to: ViewID,
21
+ event: ReactMouseEvent,
22
+ element: DiagramNodeWithNavigate,
23
+ xynode: XYFlowNode
24
+ ): void
25
+ }
24
26
  export type OnNodeClick = (
25
27
  event: {
26
28
  element: DiagramNode
@@ -62,7 +64,9 @@ export type Change =
62
64
  export type ChangeEvent = {
63
65
  changes: NonEmptyArray<Change>
64
66
  }
65
- export type OnChange = (event: ChangeEvent) => void
67
+ export type OnChange = {
68
+ (event: ChangeEvent): void
69
+ }
66
70
  export type LikeC4ColorScheme = 'light' | 'dark'
67
71
 
68
72
  export interface LikeC4DiagramProperties {
@@ -76,7 +80,7 @@ export interface LikeC4DiagramProperties {
76
80
  *
77
81
  * @example 'light' | 'dark'
78
82
  */
79
- colorScheme?: LikeC4ColorScheme | undefined
83
+ // colorScheme?: LikeC4ColorScheme | undefined
80
84
 
81
85
  /**
82
86
  * Show/hide controls menu
@@ -5,7 +5,8 @@ import { rootClassName } from './globals.css'
5
5
  import { cssDisablePan, cssNoControls, cssReactFlow, cssTransparentBg } from './LikeC4Diagram.css'
6
6
  import { type LikeC4DiagramEventHandlers, type LikeC4DiagramProperties } from './LikeC4Diagram.props'
7
7
  import { EnsureMantine } from './mantine/EnsureMantine'
8
- import { DiagramContextProvider, WhenInitialized } from './state'
8
+ import { DiagramContextProvider } from './state/DiagramContext'
9
+ import { WhenInitialized } from './state/WhenInitialized'
9
10
  import { KeepAspectRatio } from './ui/KeepAspectRatio'
10
11
  import OptionsPanel from './ui/OptionsPanel'
11
12
  import { diagramViewToXYFlowData } from './xyflow/diagram-to-xyflow'
@@ -20,7 +21,6 @@ export function LikeC4Diagram({
20
21
  className,
21
22
  fitView = true,
22
23
  fitViewPadding = 0,
23
- colorScheme,
24
24
  readonly = true,
25
25
  pannable = true,
26
26
  zoomable = true,
@@ -61,7 +61,7 @@ export function LikeC4Diagram({
61
61
  }
62
62
  }
63
63
  return (
64
- <EnsureMantine colorScheme={colorScheme}>
64
+ <EnsureMantine>
65
65
  <XYFlowProvider
66
66
  fitView={fitView}
67
67
  {...initialRef.current}
@@ -6,7 +6,6 @@ export type StaticLikeC4DiagramProps = Pick<
6
6
  | 'view'
7
7
  | 'keepAspectRatio'
8
8
  | 'className'
9
- | 'colorScheme'
10
9
  | 'fitView'
11
10
  | 'fitViewPadding'
12
11
  | 'background'
@@ -1,6 +1,6 @@
1
1
  import type { ThemeColor } from '@likec4/core'
2
2
  import { defaultTheme } from '@likec4/core'
3
- import { createGlobalTheme, createTheme, fallbackVar, globalStyle, style } from '@vanilla-extract/css'
3
+ import { createGlobalTheme } from '@vanilla-extract/css'
4
4
  import { omit } from 'remeda'
5
5
  import { mantine } from './mantine.css'
6
6
  import { vars } from './theme.css'
package/src/index.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  import '@xyflow/react/dist/style.css'
2
2
  import './globals.css'
3
3
 
4
- export { EmbeddedLikeC4Diagram } from './EmbeddedLikeC4Diagram'
5
4
  export { LikeC4Diagram } from './LikeC4Diagram'
6
5
  export { StaticLikeC4Diagram } from './StaticLikeC4Diagram'
7
6
 
8
- export type * from './EmbeddedLikeC4Diagram'
7
+ export * from './hooks'
9
8
  export type * from './LikeC4Diagram'
10
9
  export type * from './LikeC4Diagram.props'
11
10
  export type * from './StaticLikeC4Diagram'
@@ -1,24 +1,14 @@
1
- import { MantineContext, MantineProvider } from '@mantine/core'
1
+ import { MantineContext } from '@mantine/core'
2
2
  import { type PropsWithChildren, useContext } from 'react'
3
- import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
4
- import { theme } from './theme'
5
3
 
6
- type EnsureMantineProps = PropsWithChildren<Pick<LikeC4DiagramProperties, 'colorScheme'>>
4
+ type EnsureMantineProps = PropsWithChildren<{}>
7
5
 
8
- export function EnsureMantine({ colorScheme, children }: EnsureMantineProps) {
6
+ export function EnsureMantine({ children }: EnsureMantineProps) {
9
7
  const mantineCtx = useContext(MantineContext)
10
- if (mantineCtx) {
11
- return <>{children}</>
8
+
9
+ if (!mantineCtx) {
10
+ throw new Error('LikeC4Diagram must be a child of MantineProvider')
12
11
  }
13
- return (
14
- <MantineProvider
15
- {...(colorScheme && {
16
- forceColorScheme: colorScheme
17
- })}
18
- theme={theme}
19
- defaultColorScheme="auto"
20
- >
21
- {children}
22
- </MantineProvider>
23
- )
12
+
13
+ return <>{children}</>
24
14
  }
@@ -3,6 +3,7 @@ import { createTheme, type MantineTheme } from '@mantine/core'
3
3
  export const theme = createTheme({
4
4
  primaryColor: 'indigo',
5
5
  cursorType: 'pointer',
6
+ defaultRadius: 'sm',
6
7
  headings: {
7
8
  fontWeight: '500',
8
9
  sizes: {
@@ -1,7 +1,7 @@
1
1
  import { style } from '@vanilla-extract/css'
2
2
 
3
3
  export const wrapper = style({
4
- width: ['100%', '-webkit-fill-available'],
5
- height: ['100%', '-webkit-fill-available'],
4
+ width: '100%',
5
+ height: '100%',
6
6
  padding: 0
7
7
  })
@@ -11,7 +11,7 @@ const OptionsPanelMemo = /* @__PURE__ */ memo(function OptionsPanel() {
11
11
  const [selectedNodes, setSelectedNodes] = useState([] as string[])
12
12
 
13
13
  useOnSelectionChange({
14
- onChange: ({ nodes, edges }) => {
14
+ onChange: ({ nodes }) => {
15
15
  const next = nodes.map(n => n.id).sort()
16
16
  setSelectedNodes(prev => {
17
17
  return shallowEqual(prev, next) ? prev : next
@@ -21,7 +21,7 @@ import {
21
21
  } from '@mantine/core'
22
22
  import { hasAtLeast, keys, takeWhile } from 'remeda'
23
23
  import { useDiagramStoreApi } from '../../state'
24
- import { useXYFlow, useXYNodesData } from '../../xyflow/hooks'
24
+ import { useXYNodesData } from '../../xyflow/hooks'
25
25
  import { XYFlowNode } from '../../xyflow/types'
26
26
 
27
27
  // const ColorPanel = () => {
@@ -54,7 +54,6 @@ type XYNodesData = Pick<XYFlowNode, 'id' | 'data' | 'type'>
54
54
  export function NodeOptions({ selectedNodeIds }: { selectedNodeIds: string[] }) {
55
55
  const diagramApi = useDiagramStoreApi()
56
56
  const nodes = useXYNodesData(selectedNodeIds)
57
- const api = useXYFlow()
58
57
  if (!hasAtLeast(nodes, 1)) {
59
58
  return null
60
59
  }
@@ -38,7 +38,7 @@ function FitViewOnDiagramChanges() {
38
38
  const diagramApi = useDiagramStoreApi()
39
39
  const [viewportMoved, setViewportMoved] = useState(diagramApi.getState().viewportChanged)
40
40
  const [processedChangeId, setProcessed] = useState(diagramChangeId(diagramApi.getState()))
41
- const [pendingChangeId, setPending] = useDebouncedState(processedChangeId, 50)
41
+ const [pendingChangeId, setPending] = useDebouncedState(processedChangeId, 100)
42
42
 
43
43
  const isReady = useNodesInitialized({
44
44
  includeHiddenNodes: true
@@ -1,30 +1,15 @@
1
1
  import { deepEqual as eq, shallowEqual } from 'fast-equals'
2
2
  import { useXYFlow, useXYStoreApi } from './hooks'
3
- import type { XYFlowEdge, XYFlowNode } from './types'
3
+ import type { XYFlowNode } from './types'
4
4
 
5
- import { memo, useCallback, useEffect } from 'react'
6
- import { type DiagramState, useDiagramStoreApi } from '../state'
5
+ import { memo, useEffect } from 'react'
6
+ import { useDiagramStoreApi } from '../state'
7
7
  import { diagramViewToXYFlowData } from './diagram-to-xyflow'
8
8
  import { toDomPrecision } from './utils'
9
9
 
10
10
  const isSameNode = (a: XYFlowNode) => (b: XYFlowNode) =>
11
11
  a.id === b.id && a.type === b.type && Object.is(a.parentId, b.parentId)
12
12
 
13
- function hasNoChanges(existing: XYFlowNode, update: XYFlowNode) {
14
- return existing.id === update.id
15
- && existing.type === update.type
16
- && existing.parentId == update.parentId
17
- && eq(existing.data.element, update.data.element)
18
- }
19
-
20
- function edgeHasNoChanges(existing: XYFlowEdge, update: XYFlowEdge) {
21
- return existing.id === update.id
22
- && existing.type === update.type
23
- && existing.source === update.source
24
- && existing.target === update.target
25
- && eq(existing.data, update.data)
26
- }
27
-
28
13
  /**
29
14
  * Syncs the diagram state with the XYFlow instance
30
15
  */
@@ -33,51 +18,6 @@ export const SyncWithDiagram = memo(() => {
33
18
  const xyflowApi = useXYStoreApi()
34
19
  const diagramStoreApi = useDiagramStoreApi()
35
20
 
36
- const correctPosition = useCallback((
37
- lastOnNavigate: NonNullable<DiagramState['lastOnNavigate']>,
38
- nodes: DiagramState['view']['nodes']
39
- ) => {
40
- // const {
41
- // lastOnNavigate,
42
- // view: {
43
- // nodes,
44
- // id: viewId
45
- // }
46
- // } = diagramStoreApi.getState()
47
- // if (lastOnNavigate.toView !== viewId || lastOnNavigate.positionCorrected) {
48
- // return
49
- // }
50
-
51
- const elFrom = lastOnNavigate.element
52
- const elTo = nodes.find(n => n.id === elFrom.id)
53
- if (elTo) {
54
- const centerFrom = lastOnNavigate.elementCenterScreenPosition,
55
- centerTo = xyflow.flowToScreenPosition({
56
- x: elTo.position[0] + elTo.width / 2,
57
- y: elTo.position[1] + elTo.height / 2
58
- }),
59
- diff = {
60
- x: toDomPrecision(centerFrom.x - centerTo.x),
61
- y: toDomPrecision(centerFrom.y - centerTo.y)
62
- }
63
- requestAnimationFrame(() => {
64
- xyflowApi.getState().panBy(diff)
65
- })
66
- }
67
- queueMicrotask(() => {
68
- diagramStoreApi.setState(
69
- {
70
- lastOnNavigate: {
71
- ...lastOnNavigate,
72
- positionCorrected: true
73
- }
74
- },
75
- false,
76
- 'positionCorrected'
77
- )
78
- })
79
- }, [diagramStoreApi, xyflowApi])
80
-
81
21
  useEffect(() => {
82
22
  return diagramStoreApi.subscribe(
83
23
  // selector
@@ -123,28 +63,30 @@ export const SyncWithDiagram = memo(() => {
123
63
  )
124
64
 
125
65
  if (lastOnNavigate?.toView === viewId && !lastOnNavigate.positionCorrected) {
126
- // correctPosition(
127
- // lastOnNavigate,
128
- // nodes
129
- // )
130
66
  const elFrom = lastOnNavigate.element
131
67
  const elTo = nodes.find(n => n.id === elFrom.id)
132
68
  if (elTo) {
133
- const centerFrom = lastOnNavigate.elementCenterScreenPosition,
134
- // xyflow.flowToScreenPosition({
135
- // x: elFrom.position[0] + elFrom.width / 2,
136
- // y: elFrom.position[1] + elFrom.height / 2
137
- // }),
138
- centerTo = xyflow.flowToScreenPosition({
139
- x: elTo.position[0] + elTo.width / 2,
140
- y: elTo.position[1] + elTo.height / 2
141
- }),
142
- diff = {
143
- x: toDomPrecision(centerFrom.x - centerTo.x),
144
- y: toDomPrecision(centerFrom.y - centerTo.y)
145
- }
69
+ const centerFrom = lastOnNavigate.elementCenterScreenPosition
70
+ const nextZoom = Math.min(elFrom.width / elTo.width, elFrom.height / elTo.height)
71
+
146
72
  requestAnimationFrame(() => {
147
73
  requestAnimationFrame(() => {
74
+ const v = xyflow.getViewport()
75
+ if (nextZoom < v.zoom) {
76
+ xyflow.setViewport({
77
+ x: v.x,
78
+ y: v.y,
79
+ zoom: nextZoom
80
+ })
81
+ }
82
+ const centerTo = xyflow.flowToScreenPosition({
83
+ x: elTo.position[0] + elTo.width / 2,
84
+ y: elTo.position[1] + elTo.height / 2
85
+ }),
86
+ diff = {
87
+ x: toDomPrecision(centerFrom.x - centerTo.x),
88
+ y: toDomPrecision(centerFrom.y - centerTo.y)
89
+ }
148
90
  xyflowApi.getState().panBy(diff)
149
91
  })
150
92
  })
@@ -165,7 +107,7 @@ export const SyncWithDiagram = memo(() => {
165
107
  equalityFn: shallowEqual
166
108
  }
167
109
  )
168
- }, [xyflow, xyflowApi, diagramStoreApi, correctPosition])
110
+ }, [xyflow, xyflowApi, diagramStoreApi])
169
111
 
170
112
  return null
171
113
  })
@@ -1,5 +1,4 @@
1
- import { nonNullable } from '@likec4/core'
2
- import type { EdgeMouseHandler, NodeMouseHandler, OnMoveEnd, OnMoveStart, Viewport } from '@xyflow/react'
1
+ import type { EdgeMouseHandler, NodeMouseHandler, OnMoveEnd } from '@xyflow/react'
3
2
  import type { MouseEvent as ReactMouseEvent } from 'react'
4
3
  import { useMemo, useRef } from 'react'
5
4
  import { useDiagramStoreApi } from '../state'
@@ -1,6 +1,6 @@
1
1
  import { useMantineColorScheme } from '@mantine/core'
2
- import { Controls, ReactFlow as GenericReactFlow, type ReactFlowProps } from '@xyflow/react'
3
- import { type CSSProperties, memo, type PropsWithChildren, type RefAttributes } from 'react'
2
+ import { Controls, ReactFlow } from '@xyflow/react'
3
+ import { type CSSProperties, memo, type PropsWithChildren } from 'react'
4
4
  import type { SetNonNullable, Simplify } from 'type-fest'
5
5
  import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
6
6
  import { type DiagramState, useDiagramState, useDiagramStoreApi } from '../state'
@@ -13,10 +13,6 @@ import { XYFlowEdge, XYFlowNode } from './types'
13
13
  import { XYFlowBackground } from './XYFlowBackground'
14
14
  import { useXYFlowEvents } from './XYFlowEvents'
15
15
 
16
- // @ts-expect-error - typing in @xyflow/react
17
- const ReactFlow: (props: ReactFlowProps<XYFlowNode, XYFlowEdge> & RefAttributes<HTMLDivElement>) => JSX.Element =
18
- GenericReactFlow
19
-
20
16
  const nodeTypes = {
21
17
  element: ElementNode,
22
18
  compound: CompoundNode
@@ -89,7 +85,7 @@ function XYFlowWrapper({
89
85
  const colorMode = colorScheme !== 'auto' ? colorScheme : undefined
90
86
 
91
87
  return (
92
- <ReactFlow
88
+ <ReactFlow<XYFlowNode, XYFlowEdge>
93
89
  className={className}
94
90
  style={style}
95
91
  {...colorMode && { colorMode }}
@@ -1,5 +1,5 @@
1
1
  import { invariant, type NonEmptyArray, nonNullable, type Point } from '@likec4/core'
2
- import { Box, Text } from '@mantine/core'
2
+ import { Box } from '@mantine/core'
3
3
  import { assignInlineVars } from '@vanilla-extract/dynamic'
4
4
  import type { EdgeProps, XYPosition } from '@xyflow/react'
5
5
  import { EdgeLabelRenderer, getBezierPath } from '@xyflow/react'
@@ -93,8 +93,8 @@ const isEqualProps = (prev: EdgeProps<XYFlowEdge>, next: EdgeProps<XYFlowEdge>)
93
93
  export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(function RelationshipEdgeR({
94
94
  id,
95
95
  data,
96
- selected,
97
- markerEnd,
96
+ selected: _selected,
97
+ markerEnd: _markerEnd,
98
98
  style,
99
99
  source,
100
100
  target,
@@ -109,8 +109,7 @@ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(func
109
109
 
110
110
  invariant(data, 'data is required')
111
111
  const {
112
- edge,
113
- controlPoints
112
+ edge
114
113
  } = data
115
114
  // const edgePath = bezierPath(edge.points)
116
115
 
@@ -15,7 +15,7 @@ export const container = style({
15
15
  [mantine.darkSelector]: {
16
16
  vars: {
17
17
  [xyvars.edge.labelColor]: vars.relation.labelColor,
18
- [xyvars.edge.labelBgColor]: `color-mix(in srgb, ${vars.relation.labelBgColor}, transparent 75%)`
18
+ [xyvars.edge.labelBgColor]: `color-mix(in srgb, ${vars.relation.labelBgColor}, transparent 55%)`
19
19
  }
20
20
  }
21
21
  // [mantine.lightSelector]: {
@@ -46,6 +46,9 @@ globalStyle(`.react-flow__edge.selected ${container}`, {
46
46
  })
47
47
 
48
48
  globalStyle(`.react-flow__edges > svg`, {
49
+ mixBlendMode: 'screen'
50
+ })
51
+ globalStyle(`:where([data-mantine-color-scheme="dark"]) .react-flow__edges > svg`, {
49
52
  mixBlendMode: 'lighten'
50
53
  })
51
54
 
@@ -102,9 +105,9 @@ export const edgeLabel = style({
102
105
  [varLabelY]: '50%'
103
106
  },
104
107
  selectors: {
105
- [mantine.darkSelector]: {
106
- mixBlendMode: 'luminosity'
107
- },
108
+ // [mantine.darkSelector]: {
109
+ // mixBlendMode: 'luminosity'
110
+ // },
108
111
  '&[data-edge-hovered="true"]': {
109
112
  transition: 'all 140ms ease-out',
110
113
  transform: `${varTranslate} scale(1.1)`
@@ -4,8 +4,8 @@ import { mantine } from '../../../mantine.css'
4
4
  import { vars } from '../../../theme.css'
5
5
 
6
6
  export const cssContainer = style({
7
- width: ['100%', '-webkit-fill-available'],
8
- height: ['100%', '-webkit-fill-available'],
7
+ width: '100%',
8
+ height: '100%',
9
9
  position: 'relative',
10
10
  padding: 0,
11
11
  margin: 0
@@ -40,6 +40,7 @@ export const cssCompound = style({
40
40
  }
41
41
  })
42
42
 
43
+ // @ts-ignore
43
44
  const cssTransparentCompound = style({
44
45
  width: '100%',
45
46
  height: '100%',
@@ -16,6 +16,8 @@ type CompoundNodeProps = Pick<
16
16
  NodeProps<CompoundXYFlowNode>,
17
17
  'id' | 'data' | 'width' | 'height' | 'selected'
18
18
  >
19
+
20
+ // @ts-ignore
19
21
  const isEqualProps = (prev: CompoundNodeProps, next: CompoundNodeProps) => (
20
22
  prev.id === next.id
21
23
  && prev.selected === next.selected
@@ -72,7 +74,6 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>(function
72
74
  }}
73
75
  >
74
76
  <Handle
75
- // @ts-expect-error
76
77
  type="target"
77
78
  position={Position.Top}
78
79
  style={{ visibility: 'hidden' }}
@@ -95,7 +96,6 @@ export const CompoundNodeMemo = /* @__PURE__ */ memo<CompoundNodeProps>(function
95
96
  </div>
96
97
  {isnavigable && <NavigateToBtn xynodeId={id} className={cssNavigateBtn} />}
97
98
  <Handle
98
- // @ts-expect-error
99
99
  type="source"
100
100
  position={Position.Bottom}
101
101
  style={{ visibility: 'hidden' }}