@@ -40,26 +40,28 @@ const NodePopover: React.FC<NodePopoverProps> = ({ nodeId }) => {
40
40
}
41
41
42
42
clearHideTimeout ( ) ;
43
- setIsPositionReady ( false ) ; // 重置位置状态
43
+ // 避免不必要的状态重置,只在真正需要时设置
44
+ if ( visible ) {
45
+ setIsPositionReady ( false ) ; // 重置位置状态
46
+ }
44
47
const node = sigma . getGraph ( ) . getNodeAttributes ( nodeId ) ;
45
48
const displayData = sigma . getNodeDisplayData ( nodeId ) ;
46
49
47
50
if ( node && displayData ) {
48
51
const containerRect = sigma . getContainer ( ) . getBoundingClientRect ( ) ;
49
52
const viewportPos = sigma . framedGraphToViewport ( displayData ) ;
50
53
51
- // 考虑左侧菜单栏和顶部区域的偏移
52
- const MENU_WIDTH = 270 ;
53
- const HEADER_HEIGHT = 156 ;
54
-
54
+ // 直接使用容器相对位置,不再硬编码菜单和头部偏移 这样更灵活,适应不同布局情况
55
55
const finalPosition = {
56
- x : containerRect . left + viewportPos . x - MENU_WIDTH ,
57
- y : containerRect . top + viewportPos . y - HEADER_HEIGHT + window . scrollY
56
+ x : viewportPos . x ,
57
+ y : viewportPos . y
58
58
} ;
59
59
60
60
// 调整 Popover 位置,考虑节点大小
61
- const nodeSize = displayData . size ;
62
- finalPosition . y -= nodeSize + 20 ;
61
+ const nodeSize = displayData . size || 5 ;
62
+
63
+ // 将弹出框定位在节点上方,距离为节点大小的 1.5 倍
64
+ finalPosition . y -= nodeSize * 1.5 ;
63
65
64
66
// 确保不超出图谱容器边界
65
67
const graphWidth = containerRect . width ;
@@ -75,9 +77,13 @@ const NodePopover: React.FC<NodePopoverProps> = ({ nodeId }) => {
75
77
}
76
78
77
79
if ( finalPosition . y < 0 ) {
78
- finalPosition . y = nodeSize + 20 ; // 如果超出顶部,显示在节点下方
79
- } else if ( finalPosition . y + POPOVER_HEIGHT > graphHeight ) {
80
- finalPosition . y = graphHeight - POPOVER_HEIGHT ;
80
+ // 如果上方放不下,则放在节点下方
81
+ finalPosition . y = viewportPos . y + nodeSize * 1.5 ;
82
+
83
+ // 如果下方也放不下,则尽量靠近顶部但不超出
84
+ if ( finalPosition . y + POPOVER_HEIGHT > graphHeight ) {
85
+ finalPosition . y = Math . min ( 5 , graphHeight - POPOVER_HEIGHT ) ;
86
+ }
81
87
}
82
88
83
89
setPosition ( finalPosition ) ;
@@ -89,7 +95,7 @@ const NodePopover: React.FC<NodePopoverProps> = ({ nodeId }) => {
89
95
setVisible ( true ) ;
90
96
} ) ;
91
97
}
92
- } , [ nodeId , sigma , hidePopover , clearHideTimeout ] ) ;
98
+ } , [ nodeId , sigma , hidePopover , clearHideTimeout , visible ] ) ;
93
99
94
100
useEffect ( ( ) => {
95
101
if ( ! nodeId ) {
@@ -99,7 +105,7 @@ const NodePopover: React.FC<NodePopoverProps> = ({ nodeId }) => {
99
105
clearHideTimeout ( ) ;
100
106
// 使用防抖处理位置更新
101
107
const createDebouncedUpdate = ( updateFn : ( ) => void ) =>
102
- debounce ( updateFn , 16 , { leading : true , trailing : true } ) ;
108
+ debounce ( updateFn , 50 , { leading : true , trailing : true } ) ;
103
109
104
110
const debouncedUpdatePosition = createDebouncedUpdate ( updatePosition ) ;
105
111
@@ -123,34 +129,47 @@ const NodePopover: React.FC<NodePopoverProps> = ({ nodeId }) => {
123
129
124
130
return (
125
131
< div
132
+ className = "node-popover-container"
126
133
style = { {
127
134
position : 'absolute' ,
128
- left : `${ position . x } px` ,
129
- top : `${ position . y } px` ,
130
- transform : 'translate(-50%, -100%)' ,
131
- pointerEvents : 'auto' ,
132
- zIndex : 1000 ,
133
- opacity : isPositionReady ? 1 : 0 ,
134
- transition : 'opacity 0.2s'
135
+ left : 0 ,
136
+ top : 0 ,
137
+ width : '100%' ,
138
+ height : '100%' ,
139
+ pointerEvents : 'none' ,
140
+ zIndex : 1000
135
141
} }
136
- onMouseEnter = { clearHideTimeout }
137
- onMouseLeave = { hidePopover }
138
142
>
139
- < Popover
140
- open = { visible }
141
- content = {
142
- < Typography . Link
143
- href = { parse2ReactRouterPath ( ROUTE_PATHS . SQLE . KNOWLEDGE . refined , {
144
- queries : {
145
- tags : nodeData . label
146
- }
147
- } ) }
148
- target = "__blank"
149
- >
150
- { t ( 'knowledgeBase.graph.viewRelatedRules' ) }
151
- </ Typography . Link >
152
- }
153
- />
143
+ < div
144
+ className = "node-popover-content"
145
+ style = { {
146
+ position : 'absolute' ,
147
+ left : `${ position . x } px` ,
148
+ top : `${ position . y } px` ,
149
+ transform : 'translate(-50%, -50%)' ,
150
+ pointerEvents : 'auto' ,
151
+ opacity : 1 ,
152
+ transition : 'opacity 0.2s'
153
+ } }
154
+ onMouseEnter = { clearHideTimeout }
155
+ onMouseLeave = { hidePopover }
156
+ >
157
+ < Popover
158
+ open = { true }
159
+ content = {
160
+ < Typography . Link
161
+ href = { parse2ReactRouterPath ( ROUTE_PATHS . SQLE . KNOWLEDGE . refined , {
162
+ queries : {
163
+ tags : nodeData . label
164
+ }
165
+ } ) }
166
+ target = "__blank"
167
+ >
168
+ { t ( 'knowledgeBase.graph.viewRelatedRules' ) }
169
+ </ Typography . Link >
170
+ }
171
+ />
172
+ </ div >
154
173
</ div >
155
174
) ;
156
175
} ;
0 commit comments