@likec4/diagram 1.0.0-next.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/LikeC4Diagram.css.d.ts +0 -1
  2. package/dist/LikeC4Diagram.d.ts +1 -2
  3. package/dist/LikeC4Diagram.props.d.ts +24 -25
  4. package/dist/StaticLikeC4Diagram.d.ts +1 -2
  5. package/dist/globals.css.d.ts +1 -0
  6. package/dist/hooks/index.d.ts +2 -3
  7. package/dist/hooks/useSetState.d.ts +3 -0
  8. package/dist/hooks/useUpdateEffect.d.ts +4 -0
  9. package/dist/icons/Clipboard.d.ts +0 -1
  10. package/dist/icons/Link.d.ts +0 -1
  11. package/dist/icons/ZoomIn.d.ts +0 -1
  12. package/dist/icons/index.d.ts +0 -1
  13. package/dist/index.d.ts +1 -4
  14. package/dist/index.mjs +9388 -0
  15. package/dist/mantine/EnsureMantine.d.ts +2 -4
  16. package/dist/mantine/theme.d.ts +3 -0
  17. package/dist/mantine.css.d.ts +1 -0
  18. package/dist/state/DiagramContext.d.ts +386 -0
  19. package/dist/state/WhenInitialized.d.ts +3 -0
  20. package/dist/state/diagramStore.d.ts +428 -0
  21. package/dist/state/index.d.ts +4 -2
  22. package/dist/state/useDiagramStore.d.ts +387 -0
  23. package/dist/style.css +1212 -1
  24. package/dist/theme.css.d.ts +16 -2
  25. package/dist/ui/Camera.d.ts +0 -1
  26. package/dist/ui/KeepAspectRatio.css.d.ts +0 -1
  27. package/dist/ui/KeepAspectRatio.d.ts +0 -1
  28. package/dist/ui/OptionsPanel.css.d.ts +0 -1
  29. package/dist/ui/OptionsPanel.d.ts +0 -1
  30. package/dist/ui/index.d.ts +0 -1
  31. package/dist/ui/options/NodeOptions.d.ts +0 -1
  32. package/dist/xyflow/FitviewOnDiagramChange.d.ts +1 -5
  33. package/dist/xyflow/SyncWithDiagram.d.ts +5 -0
  34. package/dist/xyflow/XYFlowBackground.d.ts +0 -1
  35. package/dist/xyflow/XYFlowEvents.d.ts +5 -15
  36. package/dist/xyflow/XYFlowWrapper.d.ts +4 -5
  37. package/dist/xyflow/const.d.ts +1 -2
  38. package/dist/xyflow/diagram-to-xyflow.d.ts +4 -2
  39. package/dist/xyflow/edges/RelationshipEdge.d.ts +7 -7
  40. package/dist/xyflow/edges/edges.css.d.ts +2 -1
  41. package/dist/xyflow/edges/utils.d.ts +3 -4
  42. package/dist/xyflow/hooks/index.d.ts +2 -37
  43. package/dist/xyflow/hooks/{use-layout-/321/201onstraints.d.ts → useLayoutConstraints.d.ts} +2 -4
  44. package/dist/xyflow/hooks/useXYFlow.d.ts +266 -0
  45. package/dist/xyflow/nodes/compound/CompoundNode.css.d.ts +0 -1
  46. package/dist/xyflow/nodes/compound/CompoundNode.d.ts +0 -1
  47. package/dist/xyflow/nodes/compound/index.d.ts +0 -1
  48. package/dist/xyflow/nodes/element/ElementIcon.d.ts +1 -2
  49. package/dist/xyflow/nodes/element/ElementLink.css.d.ts +0 -1
  50. package/dist/xyflow/nodes/element/ElementLink.d.ts +1 -2
  51. package/dist/xyflow/nodes/element/ElementNode.d.ts +1 -3
  52. package/dist/xyflow/nodes/element/ElementShapeSvg.d.ts +2 -3
  53. package/dist/xyflow/nodes/element/element.css.d.ts +0 -1
  54. package/dist/xyflow/nodes/element/index.d.ts +1 -2
  55. package/dist/xyflow/nodes/shared/NavigateToBtn.d.ts +0 -1
  56. package/dist/xyflow/types.d.ts +20 -8
  57. package/dist/xyflow/utils.d.ts +0 -1
  58. package/package.json +36 -34
  59. package/src/LikeC4Diagram.css.ts +31 -0
  60. package/src/LikeC4Diagram.props.ts +154 -0
  61. package/src/LikeC4Diagram.tsx +119 -0
  62. package/src/StaticLikeC4Diagram.tsx +39 -0
  63. package/src/globals.css.ts +128 -0
  64. package/src/hooks/index.ts +2 -0
  65. package/src/hooks/useSetState.ts +19 -0
  66. package/src/hooks/useUpdateEffect.ts +28 -0
  67. package/src/icons/Clipboard.tsx +24 -0
  68. package/src/icons/Link.tsx +23 -0
  69. package/src/icons/ZoomIn.tsx +25 -0
  70. package/src/icons/index.ts +3 -0
  71. package/src/index.ts +11 -0
  72. package/src/mantine/EnsureMantine.tsx +14 -0
  73. package/src/mantine/theme.ts +19 -0
  74. package/src/mantine.css.ts +4 -0
  75. package/src/reset.d.ts +2 -0
  76. package/src/state/DiagramContext.tsx +42 -0
  77. package/src/state/WhenInitialized.tsx +24 -0
  78. package/src/state/diagramStore.ts +358 -0
  79. package/src/state/index.ts +4 -0
  80. package/src/state/useDiagramStore.ts +39 -0
  81. package/src/theme.css.ts +64 -0
  82. package/src/ui/Camera.tsx +174 -0
  83. package/src/ui/KeepAspectRatio.css.ts +7 -0
  84. package/src/ui/KeepAspectRatio.tsx +35 -0
  85. package/src/ui/OptionsPanel.css.ts +10 -0
  86. package/src/ui/OptionsPanel.tsx +65 -0
  87. package/src/ui/index.ts +3 -0
  88. package/src/ui/options/NodeOptions.tsx +253 -0
  89. package/src/xyflow/FitviewOnDiagramChange.tsx +93 -0
  90. package/src/xyflow/SyncWithDiagram.tsx +113 -0
  91. package/src/xyflow/XYFlowBackground.tsx +33 -0
  92. package/src/xyflow/XYFlowEvents.tsx +100 -0
  93. package/src/xyflow/XYFlowWrapper.tsx +166 -0
  94. package/src/xyflow/const.ts +7 -0
  95. package/src/xyflow/diagram-to-xyflow.ts +197 -0
  96. package/src/xyflow/edges/RelationshipEdge.tsx +288 -0
  97. package/src/xyflow/edges/edges.css.ts +124 -0
  98. package/src/xyflow/edges/utils.ts +110 -0
  99. package/src/xyflow/hooks/index.ts +2 -0
  100. package/src/xyflow/hooks/useLayoutConstraints.ts +289 -0
  101. package/src/xyflow/hooks/useXYFlow.ts +20 -0
  102. package/src/xyflow/nodes/compound/CompoundNode.css.ts +191 -0
  103. package/src/xyflow/nodes/compound/CompoundNode.tsx +105 -0
  104. package/src/xyflow/nodes/compound/index.ts +1 -0
  105. package/src/xyflow/nodes/element/ElementIcon.tsx +44 -0
  106. package/src/xyflow/nodes/element/ElementLink.css.ts +67 -0
  107. package/src/xyflow/nodes/element/ElementLink.tsx +94 -0
  108. package/src/xyflow/nodes/element/ElementNode.tsx +225 -0
  109. package/src/xyflow/nodes/element/ElementShapeSvg.tsx +181 -0
  110. package/src/xyflow/nodes/element/element.css.ts +261 -0
  111. package/src/xyflow/nodes/element/index.ts +1 -0
  112. package/src/xyflow/nodes/shared/NavigateToBtn.tsx +25 -0
  113. package/src/xyflow/types.ts +100 -0
  114. package/src/xyflow/utils.ts +12 -0
  115. package/dist/EmbeddedLikeC4Diagram.css.d.ts +0 -6
  116. package/dist/EmbeddedLikeC4Diagram.d.ts +0 -18
  117. package/dist/bundle.d.ts +0 -3
  118. package/dist/hooks/use-set-state.d.ts +0 -2
  119. package/dist/hooks/use-update-effect.d.ts +0 -4
  120. package/dist/index.css.d.ts +0 -2
  121. package/dist/index.js +0 -13043
  122. package/dist/mantine/index.d.ts +0 -3
  123. package/dist/state/DiagramState.d.ts +0 -162
  124. package/dist/xyflow/UpdateOnDiagramChange.d.ts +0 -10
@@ -0,0 +1,93 @@
1
+ import { useDebouncedEffect, useDebouncedState, useRafEffect } from '@react-hookz/web'
2
+ import { useNodesInitialized } from '@xyflow/react'
3
+ import { shallowEqual } from 'fast-equals'
4
+ import { memo, useEffect, useRef, useState } from 'react'
5
+ import { type DiagramState, type DiagramStoreApi, useDiagramState, useDiagramStoreApi } from '../state'
6
+ import { useXYStore } from './hooks'
7
+ import type { XYFlowState } from './types'
8
+
9
+ function selectDimensions(state: XYFlowState) {
10
+ return `${Math.round(state.width)}:${Math.round(state.height)}`
11
+ }
12
+
13
+ function FitViewOnViewportResize({ diagramApi }: { diagramApi: DiagramStoreApi }) {
14
+ const dimensions = useXYStore(selectDimensions)
15
+ const prevDimensionsRef = useRef(dimensions)
16
+
17
+ useDebouncedEffect(
18
+ () => {
19
+ if (prevDimensionsRef.current === dimensions) {
20
+ return
21
+ }
22
+ diagramApi.getState().fitDiagram()
23
+ prevDimensionsRef.current = dimensions
24
+ },
25
+ [dimensions],
26
+ 150
27
+ )
28
+
29
+ return null
30
+ }
31
+
32
+ const diagramChangeId = (s: DiagramState) => s.view.id + '_' + s.view.autoLayout + '_' + s.fitViewPadding
33
+
34
+ /**
35
+ * Fits the view when the view changes and nodes are initialized
36
+ */
37
+ function FitViewOnDiagramChanges() {
38
+ const diagramApi = useDiagramStoreApi()
39
+ const [viewportMoved, setViewportMoved] = useState(diagramApi.getState().viewportChanged)
40
+ const [processedChangeId, setProcessed] = useState(diagramChangeId(diagramApi.getState()))
41
+ const [pendingChangeId, setPending] = useDebouncedState(processedChangeId, 100)
42
+
43
+ const isReady = useNodesInitialized({
44
+ includeHiddenNodes: true
45
+ })
46
+
47
+ useEffect(
48
+ () => {
49
+ return diagramApi.subscribe(
50
+ s => ({
51
+ viewportMoved: s.viewportChanged,
52
+ changeid: diagramChangeId(s)
53
+ }),
54
+ ({ viewportMoved, changeid }) => {
55
+ setPending(changeid)
56
+ setViewportMoved(viewportMoved)
57
+ },
58
+ {
59
+ equalityFn: shallowEqual
60
+ }
61
+ )
62
+ },
63
+ [diagramApi]
64
+ )
65
+
66
+ useRafEffect(
67
+ () => {
68
+ if (!isReady || processedChangeId === pendingChangeId) {
69
+ return
70
+ }
71
+ diagramApi.getState().fitDiagram()
72
+ setProcessed(pendingChangeId)
73
+ },
74
+ [isReady, processedChangeId, pendingChangeId]
75
+ )
76
+
77
+ if (!viewportMoved && isReady) {
78
+ return <FitViewOnViewportResize diagramApi={diagramApi} />
79
+ }
80
+ return null
81
+ }
82
+
83
+ const selectFitViewFlag = (s: DiagramState) => s.fitViewEnabled
84
+
85
+ export const FitViewOnDiagramChange = memo(() => {
86
+ const fitView = useDiagramState(selectFitViewFlag)
87
+
88
+ if (!fitView) {
89
+ return null
90
+ }
91
+
92
+ return <FitViewOnDiagramChanges />
93
+ })
@@ -0,0 +1,113 @@
1
+ import { deepEqual as eq, shallowEqual } from 'fast-equals'
2
+ import { useXYFlow, useXYStoreApi } from './hooks'
3
+ import type { XYFlowNode } from './types'
4
+
5
+ import { memo, useEffect } from 'react'
6
+ import { useDiagramStoreApi } from '../state'
7
+ import { diagramViewToXYFlowData } from './diagram-to-xyflow'
8
+ import { toDomPrecision } from './utils'
9
+
10
+ const isSameNode = (a: XYFlowNode) => (b: XYFlowNode) =>
11
+ a.id === b.id && a.type === b.type && Object.is(a.parentId, b.parentId)
12
+
13
+ /**
14
+ * Syncs the diagram state with the XYFlow instance
15
+ */
16
+ export const SyncWithDiagram = memo(() => {
17
+ const xyflow = useXYFlow()
18
+ const xyflowApi = useXYStoreApi()
19
+ const diagramStoreApi = useDiagramStoreApi()
20
+
21
+ useEffect(() => {
22
+ return diagramStoreApi.subscribe(
23
+ // selector
24
+ state => ({
25
+ viewId: state.view.id,
26
+ nodes: state.view.nodes,
27
+ edges: state.view.edges,
28
+ draggable: state.nodesDraggable,
29
+ selectable: state.nodesSelectable
30
+ }),
31
+ // listener
32
+ ({ viewId, nodes, edges, ...opts }) => {
33
+ const { lastOnNavigate } = diagramStoreApi.getState()
34
+ const updates = diagramViewToXYFlowData({ nodes, edges }, opts)
35
+
36
+ xyflow.setNodes(prev =>
37
+ updates.nodes.map(update => {
38
+ const existing = prev.find(isSameNode(update))
39
+ if (existing) {
40
+ if (eq(existing.data.element, update.data.element)) {
41
+ return existing
42
+ }
43
+ return {
44
+ ...existing,
45
+ ...update
46
+ }
47
+ }
48
+ return update
49
+ })
50
+ )
51
+
52
+ xyflow.setEdges(prev =>
53
+ updates.edges.map(update => {
54
+ const existing = prev.find(e => e.id === update.id)
55
+ if (existing) {
56
+ return eq(existing.data, update.data) ? existing : {
57
+ ...existing,
58
+ ...update
59
+ }
60
+ }
61
+ return update
62
+ })
63
+ )
64
+
65
+ if (lastOnNavigate?.toView === viewId && !lastOnNavigate.positionCorrected) {
66
+ const elFrom = lastOnNavigate.element
67
+ const elTo = nodes.find(n => n.id === elFrom.id)
68
+ if (elTo) {
69
+ const centerFrom = lastOnNavigate.elementCenterScreenPosition
70
+ const nextZoom = Math.min(elFrom.width / elTo.width, elFrom.height / elTo.height)
71
+
72
+ requestAnimationFrame(() => {
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
+ }
90
+ xyflowApi.getState().panBy(diff)
91
+ })
92
+ })
93
+ }
94
+ diagramStoreApi.setState(
95
+ {
96
+ lastOnNavigate: {
97
+ ...lastOnNavigate,
98
+ positionCorrected: true
99
+ }
100
+ },
101
+ false,
102
+ 'positionCorrected'
103
+ )
104
+ }
105
+ },
106
+ {
107
+ equalityFn: shallowEqual
108
+ }
109
+ )
110
+ }, [xyflow, xyflowApi, diagramStoreApi])
111
+
112
+ return null
113
+ })
@@ -0,0 +1,33 @@
1
+ import { nonexhaustive } from '@likec4/core'
2
+ import { Background, type BackgroundProps, BackgroundVariant } from '@xyflow/react'
3
+ import { deepEqual } from 'fast-equals'
4
+ import { memo } from 'react'
5
+ import type { OverrideProperties } from 'type-fest'
6
+
7
+ export type XYBackgroundVariant = 'dots' | 'lines' | 'cross'
8
+ export type XYBackgroundProps =
9
+ | XYBackgroundVariant
10
+ | OverrideProperties<BackgroundProps, {
11
+ variant: XYBackgroundVariant
12
+ }>
13
+
14
+ function literalToEnum(value: XYBackgroundVariant): BackgroundVariant {
15
+ switch (value) {
16
+ case 'dots':
17
+ return BackgroundVariant.Dots
18
+ case 'lines':
19
+ return BackgroundVariant.Lines
20
+ case 'cross':
21
+ return BackgroundVariant.Cross
22
+ default:
23
+ nonexhaustive(value)
24
+ }
25
+ }
26
+
27
+ export const XYFlowBackground = memo<{ background: XYBackgroundProps }>(({ background }) => {
28
+ if (typeof background === 'string') {
29
+ return <Background variant={literalToEnum(background)} />
30
+ }
31
+ const { variant, ...rest } = background
32
+ return <Background variant={literalToEnum(variant)} {...rest} />
33
+ }, deepEqual)
@@ -0,0 +1,100 @@
1
+ import type { EdgeMouseHandler, NodeMouseHandler, OnMoveEnd } from '@xyflow/react'
2
+ import type { MouseEvent as ReactMouseEvent } from 'react'
3
+ import { useMemo, useRef } from 'react'
4
+ import { useDiagramStoreApi } from '../state'
5
+ import { useXYStoreApi } from './hooks'
6
+ import type { XYFlowEdge, XYFlowNode } from './types'
7
+
8
+ type XYFlowEventHandlers = {
9
+ onPaneClick: (event: ReactMouseEvent) => void
10
+ onNodeContextMenu: NodeMouseHandler<XYFlowNode>
11
+ onEdgeContextMenu: EdgeMouseHandler<XYFlowEdge>
12
+ onPaneContextMenu: (event: ReactMouseEvent | MouseEvent) => void
13
+ onNodeClick: NodeMouseHandler<XYFlowNode>
14
+ onEdgeClick: EdgeMouseHandler<XYFlowEdge>
15
+ // onMoveStart: OnMoveStart
16
+ onMoveEnd: OnMoveEnd
17
+ }
18
+
19
+ export function useXYFlowEvents() {
20
+ const diagramApi = useDiagramStoreApi()
21
+ const xyflowApi = useXYStoreApi()
22
+
23
+ const dblclickTimeout = useRef<number>()
24
+
25
+ return useMemo<XYFlowEventHandlers>(() => ({
26
+ onPaneClick: (event) => {
27
+ diagramApi.setState(
28
+ {
29
+ lastClickedNodeId: null,
30
+ lastClickedEdgeId: null
31
+ },
32
+ false,
33
+ 'onPaneClick'
34
+ )
35
+ const diagramState = diagramApi.getState()
36
+
37
+ if (dblclickTimeout.current) {
38
+ window.clearTimeout(dblclickTimeout.current)
39
+ dblclickTimeout.current = undefined
40
+ if (diagramState.onCanvasDblClick) {
41
+ diagramState.onCanvasDblClick(event)
42
+ return
43
+ }
44
+ diagramState.onCanvasClick?.(event)
45
+ if (diagramState.fitViewEnabled && diagramState.viewportChanged) {
46
+ diagramState.fitDiagram()
47
+ }
48
+ return
49
+ }
50
+
51
+ dblclickTimeout.current = window.setTimeout(() => {
52
+ dblclickTimeout.current = undefined
53
+ diagramApi.getState().onCanvasClick?.(event)
54
+ }, 300)
55
+ },
56
+ onNodeContextMenu: (event, xynode) => {
57
+ diagramApi.getState().setLastClickedNode(xynode.id)
58
+ diagramApi.getState().onNodeContextMenu?.({
59
+ element: xynode.data.element,
60
+ xynode,
61
+ event
62
+ })
63
+ },
64
+ onPaneContextMenu: (event) => {
65
+ diagramApi.getState().setLastClickedNode(null)
66
+ diagramApi.getState().setLastClickedEdge(null)
67
+ diagramApi.getState().onCanvasContextMenu?.(event as any)
68
+ },
69
+ onEdgeContextMenu: (event, xyedge) => {
70
+ diagramApi.getState().setLastClickedEdge(xyedge.id)
71
+ diagramApi.getState().onEdgeContextMenu?.({
72
+ relation: xyedge.data.edge,
73
+ xyedge,
74
+ event
75
+ })
76
+ },
77
+ onNodeClick: (event, xynode) => {
78
+ diagramApi.getState().setLastClickedNode(xynode.id)
79
+ diagramApi.getState().onNodeClick?.({
80
+ element: xynode.data.element,
81
+ xynode,
82
+ event
83
+ })
84
+ },
85
+ onEdgeClick: (event, xyedge) => {
86
+ diagramApi.getState().setLastClickedEdge(xyedge.id)
87
+ diagramApi.getState().onEdgeClick?.({
88
+ relation: xyedge.data.edge,
89
+ xyedge,
90
+ event
91
+ })
92
+ },
93
+ onMoveEnd: (event, _viewport) => {
94
+ const viewportChanged = !!event
95
+ if (viewportChanged !== diagramApi.getState().viewportChanged) {
96
+ diagramApi.setState({ viewportChanged }, false, `viewport-changed: ${viewportChanged}`)
97
+ }
98
+ }
99
+ }), [diagramApi, xyflowApi])
100
+ }
@@ -0,0 +1,166 @@
1
+ import { useMantineColorScheme } from '@mantine/core'
2
+ import { Controls, ReactFlow } from '@xyflow/react'
3
+ import { type CSSProperties, memo, type PropsWithChildren } from 'react'
4
+ import type { SetNonNullable, Simplify } from 'type-fest'
5
+ import type { LikeC4DiagramProperties } from '../LikeC4Diagram.props'
6
+ import { type DiagramState, useDiagramState, useDiagramStoreApi } from '../state'
7
+ import { MinZoom } from './const'
8
+ import { RelationshipEdge } from './edges/RelationshipEdge'
9
+ import { useLayoutConstraints } from './hooks/useLayoutConstraints'
10
+ import { CompoundNode } from './nodes/compound'
11
+ import { ElementNode } from './nodes/element'
12
+ import { XYFlowEdge, XYFlowNode } from './types'
13
+ import { XYFlowBackground } from './XYFlowBackground'
14
+ import { useXYFlowEvents } from './XYFlowEvents'
15
+
16
+ const nodeTypes = {
17
+ element: ElementNode,
18
+ compound: CompoundNode
19
+ }
20
+ const edgeTypes = {
21
+ relationship: RelationshipEdge
22
+ }
23
+
24
+ type OnlyExpectedProps = Required<
25
+ Pick<
26
+ LikeC4DiagramProperties,
27
+ | 'className'
28
+ | 'controls'
29
+ | 'pannable'
30
+ | 'zoomable'
31
+ | 'background'
32
+ >
33
+ >
34
+
35
+ type XYFlowWrapperProps = Simplify<
36
+ PropsWithChildren<
37
+ SetNonNullable<OnlyExpectedProps> & {
38
+ defaultNodes: XYFlowNode[]
39
+ defaultEdges: XYFlowEdge[]
40
+ style?: CSSProperties | undefined
41
+ }
42
+ >
43
+ >
44
+
45
+ const selector = (s: DiagramState) => ({
46
+ nodesSelectable: s.nodesSelectable,
47
+ nodesDraggable: s.nodesDraggable,
48
+ fitView: s.fitViewEnabled,
49
+ fitViewPadding: s.fitViewPadding,
50
+ hasOnNavigateTo: !!s.onNavigateTo,
51
+ hasOnNodeClick: !!s.onNodeClick,
52
+ hasOnNodeContextMenu: !!s.onNodeContextMenu,
53
+ hasOnCanvasContextMenu: !!s.onCanvasContextMenu,
54
+ hasOnEdgeContextMenu: !!s.onEdgeContextMenu,
55
+ hasOnEdgeClick: !!s.onEdgeClick,
56
+ hasOnCanvasClick: !!s.onCanvasClick || !!s.onCanvasDblClick
57
+ })
58
+
59
+ function XYFlowWrapper({
60
+ className,
61
+ children,
62
+ defaultNodes,
63
+ defaultEdges,
64
+ pannable,
65
+ zoomable,
66
+ controls,
67
+ background,
68
+ style
69
+ }: XYFlowWrapperProps) {
70
+ const diagramApi = useDiagramStoreApi()
71
+ const {
72
+ nodesSelectable,
73
+ nodesDraggable,
74
+ fitView,
75
+ fitViewPadding,
76
+ ...editor
77
+ } = useDiagramState(selector)
78
+
79
+ const layoutConstraints = useLayoutConstraints()
80
+
81
+ const handlers = useXYFlowEvents()
82
+ const isBgWithPattern = background !== 'transparent' && background !== 'solid'
83
+
84
+ const { colorScheme } = useMantineColorScheme()
85
+ const colorMode = colorScheme !== 'auto' ? colorScheme : undefined
86
+
87
+ return (
88
+ <ReactFlow<XYFlowNode, XYFlowEdge>
89
+ className={className}
90
+ style={style}
91
+ {...colorMode && { colorMode }}
92
+ defaultNodes={defaultNodes}
93
+ defaultEdges={defaultEdges}
94
+ nodeTypes={nodeTypes}
95
+ edgeTypes={edgeTypes}
96
+ zoomOnPinch={zoomable}
97
+ zoomOnScroll={!pannable && zoomable}
98
+ {...(!zoomable && {
99
+ zoomActivationKeyCode: null
100
+ })}
101
+ maxZoom={zoomable ? 1.9 : 1}
102
+ minZoom={zoomable ? MinZoom : 1}
103
+ fitView={fitView}
104
+ fitViewOptions={{
105
+ minZoom: MinZoom,
106
+ maxZoom: 1,
107
+ padding: fitViewPadding,
108
+ includeHiddenNodes: true
109
+ }}
110
+ preventScrolling={zoomable || pannable}
111
+ defaultMarkerColor="var(--xy-edge-stroke)"
112
+ noDragClassName="nodrag"
113
+ noPanClassName="nopan"
114
+ panOnScroll={pannable}
115
+ panOnDrag={pannable}
116
+ elementsSelectable={nodesSelectable}
117
+ nodesFocusable={nodesDraggable || nodesSelectable || editor.hasOnNodeClick || editor.hasOnNavigateTo}
118
+ edgesFocusable={editor.hasOnEdgeClick}
119
+ {...(!nodesSelectable && {
120
+ selectionKeyCode: null
121
+ })}
122
+ nodesDraggable={nodesDraggable}
123
+ {...(nodesDraggable && layoutConstraints)}
124
+ zoomOnDoubleClick={false}
125
+ elevateNodesOnSelect={false} // or edges are not visible after select
126
+ selectNodesOnDrag={false} // or weird camera movement
127
+ onPaneClick={handlers.onPaneClick}
128
+ {...(editor.hasOnNodeClick && {
129
+ onNodeClick: handlers.onNodeClick
130
+ })}
131
+ {...(editor.hasOnEdgeClick && {
132
+ onEdgeClick: handlers.onEdgeClick
133
+ })}
134
+ onMoveEnd={handlers.onMoveEnd}
135
+ onInit={() => {
136
+ diagramApi.setState({ initialized: true }, false, 'initialized')
137
+ }}
138
+ onNodeMouseEnter={(_event, node) => {
139
+ diagramApi.getState().setHoveredNode(node.id)
140
+ }}
141
+ onNodeMouseLeave={() => {
142
+ diagramApi.getState().setHoveredNode(null)
143
+ }}
144
+ onEdgeMouseEnter={(_event, edge) => {
145
+ diagramApi.getState().setHoveredEdge(edge.id)
146
+ }}
147
+ onEdgeMouseLeave={() => {
148
+ diagramApi.getState().setHoveredEdge(null)
149
+ }}
150
+ {...(editor.hasOnNodeContextMenu && {
151
+ onNodeContextMenu: handlers.onNodeContextMenu
152
+ })}
153
+ {...(editor.hasOnEdgeContextMenu && {
154
+ onEdgeContextMenu: handlers.onEdgeContextMenu
155
+ })}
156
+ {...(editor.hasOnCanvasContextMenu && {
157
+ onPaneContextMenu: handlers.onPaneContextMenu
158
+ })}>
159
+ {isBgWithPattern && <XYFlowBackground background={background} />}
160
+ {controls && <Controls />}
161
+ {children}
162
+ </ReactFlow>
163
+ )
164
+ }
165
+
166
+ export const XYFlow = memo(XYFlowWrapper) as typeof XYFlowWrapper
@@ -0,0 +1,7 @@
1
+ export const ZIndexes = {
2
+ Compound: 1,
3
+ Edge: 2,
4
+ Element: 3
5
+ } as const
6
+
7
+ export const MinZoom = 0.05