@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,197 @@
1
+ import {
2
+ type DiagramNode,
3
+ type DiagramView,
4
+ type Fqn,
5
+ invariant,
6
+ type NonEmptyArray,
7
+ nonNullable,
8
+ type Point
9
+ } from '@likec4/core'
10
+ import { getBezierEdgeCenter } from '@xyflow/react'
11
+ import { hasAtLeast } from 'remeda'
12
+ import type { XYFlowData } from '../xyflow/types'
13
+
14
+ function deriveEdgePoints(bezierSpline: NonEmptyArray<Point>) {
15
+ let [start, ...bezierPoints] = bezierSpline
16
+ invariant(start, 'start should be defined')
17
+ const handles = [
18
+ // start
19
+ ] as Point[]
20
+
21
+ while (hasAtLeast(bezierPoints, 3)) {
22
+ const [cp1, cp2, end, ...rest] = bezierPoints
23
+ const [centerX, centerY] = getBezierEdgeCenter({
24
+ sourceX: start[0],
25
+ sourceY: start[1],
26
+ targetX: end[0],
27
+ targetY: end[1],
28
+ sourceControlX: cp1[0],
29
+ sourceControlY: cp1[1],
30
+ targetControlX: cp2[0],
31
+ targetControlY: cp2[1]
32
+ })
33
+ handles.push(
34
+ [centerX, centerY]
35
+ )
36
+ // if (rest.length === 0) {
37
+ // handles.push(
38
+ // end
39
+ // )
40
+ // }
41
+ bezierPoints = rest
42
+ start = end
43
+ }
44
+ invariant(bezierPoints.length === 0, 'all points should be consumed')
45
+
46
+ return handles
47
+ }
48
+
49
+ // const nodeZIndex = (node: DiagramNode) => node.level + (hasAtLeast(node.children, 1) ? 2 : 1)
50
+ const nodeZIndex = (node: DiagramNode) => node.level + 1
51
+
52
+ export function diagramViewToXYFlowData(
53
+ view: Pick<DiagramView, 'nodes' | 'edges'>,
54
+ opts: {
55
+ draggable: boolean
56
+ selectable: boolean
57
+ }
58
+ ): XYFlowData {
59
+ const editor: XYFlowData = {
60
+ nodes: [],
61
+ edges: []
62
+ }
63
+
64
+ const nodeLookup = new Map<Fqn, DiagramNode>()
65
+
66
+ const traverse = view.nodes.reduce(
67
+ (acc, node) => {
68
+ nodeLookup.set(node.id, node)
69
+ if (!node.parent) {
70
+ acc.push({ node, parent: null })
71
+ }
72
+ return acc
73
+ },
74
+ new Array<{
75
+ node: DiagramNode
76
+ parent: DiagramNode | null
77
+ }>()
78
+ )
79
+
80
+ // namespace to force unique ids
81
+ const ns = ''
82
+ const nodeById = (id: Fqn) => nonNullable(nodeLookup.get(id), `Node not found: ${id}`)
83
+
84
+ let next: typeof traverse[0] | undefined
85
+ while ((next = traverse.pop())) {
86
+ const { node, parent } = next
87
+ if (node.children.length > 0) {
88
+ traverse.unshift(...node.children.map(child => ({ node: nodeById(child), parent: node })))
89
+ }
90
+ const isCompound = hasAtLeast(node.children, 1)
91
+ const position = {
92
+ x: node.position[0],
93
+ y: node.position[1]
94
+ }
95
+ if (parent) {
96
+ position.x -= parent.position[0]
97
+ position.y -= parent.position[1]
98
+ }
99
+
100
+ // const outEdges = node.outEdges.map(e => view.edges.find(edge => edge.id === e)).filter(Boolean)
101
+ // const inEdges = node.inEdges.map(e => view.edges.find(edge => edge.id === e)).filter(Boolean)
102
+
103
+ const id = ns + node.id
104
+ editor.nodes.push({
105
+ id,
106
+ type: isCompound ? 'compound' : 'element',
107
+ data: {
108
+ fqn: node.id,
109
+ element: node
110
+ },
111
+ draggable: opts.draggable && (!parent || parent.children.length > 1),
112
+ selectable: opts.selectable,
113
+ deletable: false,
114
+ position,
115
+ zIndex: nodeZIndex(node),
116
+ hidden: false,
117
+ /* initialWidth: node.width,
118
+ initialHeight: node.height, */
119
+ width: node.width,
120
+ height: node.height,
121
+ measured: {
122
+ width: node.width,
123
+ height: node.height
124
+ },
125
+ ...(parent && {
126
+ parentId: ns + parent.id
127
+ })
128
+ // handles: [
129
+ // ...outEdges.map(out => ({
130
+ // id: `${out.id}`,
131
+ // type: 'source' as const,
132
+ // position: Position.Bottom,
133
+ // x: out.points[0][0],
134
+ // y: out.points[0][1],
135
+ // width: 10,
136
+ // height: 10
137
+ // })),
138
+ // ...inEdges.map(out => ({
139
+ // id: out.id,
140
+ // type: 'target' as const,
141
+ // position: Position.Top,
142
+ // x: out.points[0][0],
143
+ // y: out.points[0][1],
144
+ // width: 10,
145
+ // height: 10
146
+ // }))
147
+ // ],
148
+ })
149
+ }
150
+
151
+ for (const edge of view.edges) {
152
+ const source = edge.source
153
+ const target = edge.target
154
+ const id = ns + edge.id
155
+ // const points = isElkEdge(edge) ? edge.points : deriveEdgePoints(edge.points)
156
+ const controlPoints = deriveEdgePoints(edge.points)
157
+ // if (edge.tailArrowPoint) {
158
+ // controlPoints.unshift([...edge.tailArrowPoint])
159
+ // }
160
+ // if (edge.headArrowPoint) {
161
+ // controlPoints.push([...edge.headArrowPoint])
162
+ // }
163
+ invariant(hasAtLeast(edge.points, 2), 'edge should have at least 2 points')
164
+ // invariant(hasAtLeast(controlPoints, 2), 'edge controlPoints should have at least 2 points')
165
+
166
+ const level = Math.max(nodeZIndex(nodeById(source)), nodeZIndex(nodeById(target)))
167
+
168
+ editor.edges.push({
169
+ id,
170
+ type: 'relationship',
171
+ source: ns + source,
172
+ target: ns + target,
173
+ zIndex: level,
174
+ deletable: false,
175
+ data: {
176
+ edge,
177
+ type: 'bezier',
178
+ controlPoints,
179
+ headPoint: edge.headArrowPoint ?? null,
180
+ tailPoint: edge.tailArrowPoint ?? null,
181
+ label: edge.labelBBox && edge.label
182
+ ? {
183
+ bbox: edge.labelBBox,
184
+ // text: edge.labels ? edge.labels.map(l => l.text).join('\n') : ''
185
+ text: edge.label
186
+ }
187
+ : null
188
+ },
189
+ // markerEnd: {
190
+ // type: MarkerType.ArrowClosed
191
+ // },
192
+ interactionWidth: 20
193
+ })
194
+ }
195
+
196
+ return editor
197
+ }
@@ -0,0 +1,288 @@
1
+ import { invariant, type NonEmptyArray, nonNullable, type Point } from '@likec4/core'
2
+ import { Box } from '@mantine/core'
3
+ import { assignInlineVars } from '@vanilla-extract/dynamic'
4
+ import type { EdgeProps, XYPosition } from '@xyflow/react'
5
+ import { EdgeLabelRenderer, getBezierPath } from '@xyflow/react'
6
+ import clsx from 'clsx'
7
+ import { deepEqual as eq } from 'fast-equals'
8
+ import { type CSSProperties, memo } from 'react'
9
+ import { hasAtLeast } from 'remeda'
10
+ import { useDiagramState } from '../../state'
11
+ import { ZIndexes } from '../const'
12
+ import { useXYStoreApi } from '../hooks'
13
+ import { type XYFlowEdge } from '../types'
14
+ import { toDomPrecision } from '../utils'
15
+ import {
16
+ container,
17
+ cssEdgePath,
18
+ edgeLabel,
19
+ edgeLabelBody,
20
+ edgePathBg,
21
+ fillStrokeCtx,
22
+ varLabelX,
23
+ varLabelY
24
+ } from './edges.css'
25
+ import { getEdgeParams } from './utils'
26
+ // import { getEdgeParams } from './utils'
27
+
28
+ // function getBend(a: XYPosition, b: XYPosition, c: XYPosition, size = 8): string {
29
+ // const bendSize = Math.min(distance(a, b) / 2, distance(b, c) / 2, size)
30
+ // const { x, y } = b
31
+ // // no bend
32
+ // if ((a.x === x && x === c.x) || (a.y === y && y === c.y)) {
33
+ // return `L${x} ${y}`
34
+ // }
35
+
36
+ // // first segment is horizontal
37
+ // if (a.y === y) {
38
+ // const xDir = a.x < c.x ? -1 : 1
39
+ // const yDir = a.y < c.y ? 1 : -1
40
+ // return `L ${x + bendSize * xDir},${y}Q ${x},${y} ${x},${y + bendSize * yDir}`
41
+ // }
42
+
43
+ // const xDir = a.x < c.x ? 1 : -1
44
+ // const yDir = a.y < c.y ? -1 : 1
45
+ // return `L ${x},${y + bendSize * yDir}Q ${x},${y} ${x + bendSize * xDir},${y}`
46
+ // }
47
+
48
+ // const reduceToPath = (path: string, p: XYPosition, i: number, points: XYPosition[]) => {
49
+ // let segment = ''
50
+ // if (i > 0 && i < points.length - 1) {
51
+ // segment = getBend(points[i - 1]!, p, points[i + 1]!)
52
+ // } else {
53
+ // segment = `${i === 0 ? 'M' : 'L'}${p.x} ${p.y}`
54
+ // }
55
+ // return path + segment
56
+ // }
57
+
58
+ function bezierPath(bezierSpline: NonEmptyArray<Point>) {
59
+ let [start, ...points] = bezierSpline
60
+ invariant(start, 'start should be defined')
61
+ let path = `M ${start[0]},${start[1]}`
62
+
63
+ while (hasAtLeast(points, 3)) {
64
+ const [cp1, cp2, end, ...rest] = points
65
+ path = path + ` C ${cp1[0]},${cp1[1]} ${cp2[0]},${cp2[1]} ${end[0]},${end[1]}`
66
+ points = rest
67
+ }
68
+ invariant(points.length === 0, 'all points should be consumed')
69
+
70
+ return path
71
+ }
72
+
73
+ // If points are within 2px, consider them the same
74
+ const isSamePoint = (a: number, b: number) => {
75
+ return Math.abs(a - b) < 2.1
76
+ }
77
+
78
+ const isSamePosition = (a: XYPosition, [bx, by]: Point) => {
79
+ return isSamePoint(a.x, bx) && isSamePoint(a.y, by)
80
+ }
81
+
82
+ const isEqualProps = (prev: EdgeProps<XYFlowEdge>, next: EdgeProps<XYFlowEdge>) => (
83
+ prev.id === next.id
84
+ && prev.source === next.source
85
+ && prev.target === next.target
86
+ && isSamePoint(prev.sourceX, next.sourceX)
87
+ && isSamePoint(prev.sourceY, next.sourceY)
88
+ && isSamePoint(prev.targetX, next.targetX)
89
+ && isSamePoint(prev.targetY, next.targetY)
90
+ && eq(prev.data, next.data)
91
+ )
92
+
93
+ export const RelationshipEdge = /* @__PURE__ */ memo<EdgeProps<XYFlowEdge>>(function RelationshipEdgeR({
94
+ id,
95
+ data,
96
+ selected: _selected,
97
+ markerEnd: _markerEnd,
98
+ style,
99
+ source,
100
+ target,
101
+ interactionWidth
102
+ }) {
103
+ const { nodeLookup, edgeLookup } = useXYStoreApi().getState()
104
+ const sourceNode = nonNullable(nodeLookup.get(source)!, `source node ${source} not found`)
105
+ const targetNode = nonNullable(nodeLookup.get(target)!, `target node ${target} not found`)
106
+
107
+ const isNotModified = isSamePosition(sourceNode.internals.positionAbsolute, sourceNode.data.element.position)
108
+ && isSamePosition(targetNode.internals.positionAbsolute, targetNode.data.element.position)
109
+
110
+ invariant(data, 'data is required')
111
+ const {
112
+ edge
113
+ } = data
114
+ // const edgePath = bezierPath(edge.points)
115
+
116
+ const color = edge.color ?? 'gray'
117
+ const isHovered = useDiagramState(s => s.hoveredEdgeId === id)
118
+
119
+ const line = edge.line ?? 'dashed'
120
+ const isDotted = line === 'dotted'
121
+ const isDashed = isDotted || line === 'dashed'
122
+
123
+ let strokeDasharray: string | undefined
124
+ if (isDotted) {
125
+ strokeDasharray = '1,8'
126
+ } else if (isDashed) {
127
+ strokeDasharray = '8,10'
128
+ }
129
+
130
+ const marker = `url(#arrow-${id})`
131
+
132
+ let edgePath: string, labelX: number, labelY: number
133
+
134
+ if (isNotModified) {
135
+ edgePath = bezierPath(edge.points)
136
+ labelX = data.label?.bbox.x ?? 0
137
+ labelY = data.label?.bbox.y ?? 0
138
+ } else {
139
+ const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode)
140
+
141
+ const [_edgePath, _labelX, _labelY] = getBezierPath({
142
+ sourceX: sx,
143
+ sourceY: sy,
144
+ sourcePosition: sourcePos,
145
+ targetPosition: targetPos,
146
+ targetX: tx,
147
+ targetY: ty
148
+ })
149
+ edgePath = _edgePath
150
+ labelX = _labelX
151
+ labelY = _labelY
152
+ }
153
+ // const deferredEdgePath = edgePath
154
+ // useTilg()`
155
+ // ${id}
156
+ // path=${edgePath}
157
+ // isModified=${isModified}
158
+ // `
159
+
160
+ return (
161
+ <g className={clsx(container)} data-likec4-color={color} data-edge-hovered={isHovered}>
162
+ <g className={clsx(fillStrokeCtx)}>
163
+ <defs>
164
+ <marker
165
+ id={`arrow-${id}`}
166
+ viewBox="0 0 10 8"
167
+ refX="0"
168
+ refY="4"
169
+ markerWidth="5"
170
+ markerHeight="5"
171
+ markerUnits="strokeWidth"
172
+ preserveAspectRatio="xMaxYMid meet"
173
+ orient="auto-start-reverse">
174
+ <path d="M 0 0 L 10 4 L 0 8 z" stroke="context-stroke" fill="context-stroke" />
175
+ </marker>
176
+ </defs>
177
+ </g>
178
+ <RelationshipPath
179
+ edgePath={edgePath}
180
+ interactionWidth={interactionWidth ?? 10}
181
+ strokeDasharray={strokeDasharray}
182
+ markerStart={edge.tailArrow ? marker : undefined}
183
+ markerEnd={edge.headArrow ? marker : undefined}
184
+ style={style} />
185
+ {
186
+ /*
187
+ {controlPoints.map((p, i) => (
188
+ <circle
189
+ className={styles.controlPoint}
190
+ key={i}
191
+ cx={p[0]}
192
+ cy={p[1]}
193
+ r={5}
194
+ />
195
+ ))} */
196
+ }
197
+ {data.label && (
198
+ <EdgeLabelRenderer>
199
+ <Box
200
+ className={clsx(container, edgeLabel)}
201
+ data-likec4-color={color}
202
+ style={{
203
+ ...assignInlineVars({
204
+ [varLabelX]: toDomPrecision(labelX) + 'px',
205
+ [varLabelY]: toDomPrecision(labelY) + 'px'
206
+ }),
207
+ maxWidth: data.label.bbox.width + 10,
208
+ zIndex: edgeLookup.get(id)?.zIndex ?? ZIndexes.Edge
209
+ }}
210
+ mod={{
211
+ 'data-edge-hovered': isHovered
212
+ }}
213
+ >
214
+ <Box className={edgeLabelBody}>
215
+ {data.label.text}
216
+ </Box>
217
+ {
218
+ /* <Popover
219
+ position="bottom"
220
+ floatingStrategy="fixed"
221
+ shadow="lg"
222
+ disabled={!selected}
223
+ transitionProps={{
224
+ transition: 'pop'
225
+ }}>
226
+ <Popover.Target>
227
+ <Box className={clsx('nodrag nopan', edgeLabelBody)}>
228
+ {data.label.text}
229
+ </Box>
230
+ </Popover.Target>
231
+ <Popover.Dropdown>
232
+ {edge.relations.map((relation) => (
233
+ <Box key={relation}>
234
+ <Text size='xs'>{relation}</Text>
235
+ </Box>
236
+ ))}
237
+ <TextInput label="Name" placeholder="Name" size="xs" />
238
+ <TextInput label="Email" placeholder="john@doe.com" size="xs" mt="xs" />
239
+ </Popover.Dropdown>
240
+ </Popover> */
241
+ }
242
+ </Box>
243
+ </EdgeLabelRenderer>
244
+ )}
245
+ </g>
246
+ )
247
+ }, isEqualProps)
248
+
249
+ const RelationshipPath = memo<{
250
+ edgePath: string
251
+ interactionWidth: number
252
+ strokeDasharray: string | undefined
253
+ markerStart: string | undefined
254
+ markerEnd: string | undefined
255
+ style: CSSProperties | undefined
256
+ }>(({
257
+ edgePath,
258
+ interactionWidth,
259
+ strokeDasharray,
260
+ markerStart,
261
+ markerEnd,
262
+ style
263
+ }) => (
264
+ <>
265
+ <path
266
+ className={clsx('react-flow__edge-path', edgePathBg)}
267
+ d={edgePath}
268
+ style={style}
269
+ strokeLinecap={'round'}
270
+ />
271
+ <path
272
+ className={clsx('react-flow__edge-path', cssEdgePath)}
273
+ d={edgePath}
274
+ style={style}
275
+ strokeLinecap={'round'}
276
+ strokeDasharray={strokeDasharray}
277
+ markerStart={markerStart}
278
+ markerEnd={markerEnd}
279
+ />
280
+ <path
281
+ className={clsx('react-flow__edge-interaction')}
282
+ d={edgePath}
283
+ fill="none"
284
+ strokeOpacity={0}
285
+ strokeWidth={interactionWidth}
286
+ />
287
+ </>
288
+ ))
@@ -0,0 +1,124 @@
1
+ import { rem } from '@mantine/core'
2
+ import { createVar, globalStyle, style } from '@vanilla-extract/css'
3
+ import { mantine } from '../../mantine.css'
4
+ import { vars, xyvars } from '../../theme.css'
5
+
6
+ export const container = style({
7
+ vars: {
8
+ [xyvars.edge.stroke]: vars.relation.lineColor,
9
+ [xyvars.edge.strokeSelected]: `color-mix(in srgb, ${vars.relation.lineColor}, white 30%)`,
10
+ [xyvars.edge.labelColor]: `color-mix(in srgb, ${vars.relation.labelColor}, rgba(255 255 255 / 0.85) 20%)`,
11
+ [xyvars.edge.labelBgColor]: `color-mix(in srgb, ${vars.relation.labelBgColor}, transparent 35%)`,
12
+ [xyvars.edge.strokeWidth]: '2.5px'
13
+ },
14
+ selectors: {
15
+ [mantine.darkSelector]: {
16
+ vars: {
17
+ [xyvars.edge.labelColor]: vars.relation.labelColor,
18
+ [xyvars.edge.labelBgColor]: `color-mix(in srgb, ${vars.relation.labelBgColor}, transparent 75%)`
19
+ }
20
+ }
21
+ // [mantine.lightSelector]: {
22
+ // // mixBlendMode: 'luminosity',
23
+ // vars: {
24
+ // // [xyvars.edge.labelColor]: `color-mix(in srgb, ${vars.relation.labelColor}, #000 85%)`
25
+ // }
26
+ // }
27
+ }
28
+ })
29
+
30
+ globalStyle(`${container}[data-edge-hovered='true']`, {
31
+ vars: {
32
+ [xyvars.edge.stroke]: `color-mix(in srgb, ${vars.relation.lineColor}, white 30%)`,
33
+ [xyvars.edge.strokeWidth]: '3px'
34
+ }
35
+ })
36
+ // globalStyle(`${container}[data-edge-hovered='true']`, {
37
+ // vars: {
38
+ // [xyvars.edge.stroke]: `color-mix(in srgb, ${vars.relation.lineColor}, white 35%)`
39
+ // },
40
+ // })
41
+
42
+ globalStyle(`.react-flow__edge.selected ${container}`, {
43
+ vars: {
44
+ [xyvars.edge.strokeWidth]: '2.5'
45
+ }
46
+ })
47
+
48
+ globalStyle(`.react-flow__edges > svg`, {
49
+ mixBlendMode: 'screen'
50
+ })
51
+ globalStyle(`:where([data-mantine-color-scheme="dark"]) .react-flow__edges > svg`, {
52
+ mixBlendMode: 'lighten'
53
+ })
54
+
55
+ export const edgePathBg = style({
56
+ strokeWidth: `calc(${xyvars.edge.strokeWidth} + 8)`,
57
+ strokeOpacity: 0.1
58
+ })
59
+
60
+ export const fillStrokeCtx = style({
61
+ fill: xyvars.edge.stroke,
62
+ stroke: xyvars.edge.stroke,
63
+ selectors: {
64
+ '.react-flow__edge.selected &': {
65
+ vars: {
66
+ [xyvars.edge.stroke]: xyvars.edge.strokeSelected
67
+ }
68
+ }
69
+ }
70
+ })
71
+
72
+ export const cssEdgePath = style({
73
+ selectors: {
74
+ [`${container}[data-edge-hovered='true'] &`]: {
75
+ transition: 'all 130ms ease-out'
76
+ }
77
+ }
78
+ })
79
+
80
+ export const varLabelX = createVar('label-x')
81
+ export const varLabelY = createVar('label-y')
82
+
83
+ const varTranslate = createVar('translate')
84
+
85
+ export const edgeLabel = style({
86
+ top: 0,
87
+ left: 0,
88
+ fontFamily: vars.likec4.font,
89
+ position: 'absolute',
90
+ pointerEvents: 'all',
91
+ cursor: 'pointer',
92
+ transformOrigin: '50% 50%',
93
+ textAlign: 'left',
94
+ mixBlendMode: 'screen',
95
+ padding: '2px 6px 4px 6px',
96
+ width: 'max-content',
97
+ backgroundColor: xyvars.edge.labelBgColor,
98
+ borderRadius: '3px',
99
+ transform: varTranslate,
100
+ willChange: 'transform',
101
+ vars: {
102
+ // [varTranslate]: `translate(calc(${varLabelX} - 50%), calc(${varLabelY} - 50%))`,
103
+ [varTranslate]: `translate(${varLabelX}, ${varLabelY})`,
104
+ [varLabelX]: '50%',
105
+ [varLabelY]: '50%'
106
+ },
107
+ selectors: {
108
+ [mantine.darkSelector]: {
109
+ mixBlendMode: 'luminosity'
110
+ },
111
+ '&[data-edge-hovered="true"]': {
112
+ transition: 'all 140ms ease-out',
113
+ transform: `${varTranslate} scale(1.1)`
114
+ }
115
+ }
116
+ })
117
+
118
+ export const edgeLabelBody = style({
119
+ // display: 'inline',
120
+ color: xyvars.edge.labelColor,
121
+ fontSize: rem(12.5),
122
+ lineHeight: 1.12,
123
+ transition: 'color 120ms ease-out'
124
+ })