@@ -44,6 +44,9 @@ export const renderReleaseVisualization = ({
4444} : ReleaseVisualizationProps ) => {
4545 const tooltip = d3 . select ( tooltipRef . current ) ;
4646
47+ let tooltipShowTimer : ReturnType < typeof setTimeout > | null = null ;
48+ let tooltipHideTimer : ReturnType < typeof setTimeout > | null = null ;
49+
4750 // 스케일 설정
4851 const uniqueContributors = Array . from ( new Set ( releaseContributorActivities . map ( ( a ) => a . contributorName ) ) ) ;
4952 const uniqueReleases = Array . from ( new Set ( releaseContributorActivities . map ( ( a ) => a . releaseIndex ) ) ) . sort (
@@ -184,6 +187,15 @@ export const renderReleaseVisualization = ({
184187 // 툴팁 이벤트
185188 dots
186189 . on ( "mouseover" , function ( event : MouseEvent , d : ReleaseContributorActivity ) {
190+ if ( tooltipHideTimer ) {
191+ clearTimeout ( tooltipHideTimer ) ;
192+ tooltipHideTimer = null ;
193+ }
194+ if ( tooltipShowTimer ) {
195+ clearTimeout ( tooltipShowTimer ) ;
196+ tooltipShowTimer = null ;
197+ }
198+
187199 const currentRadius = sizeScale ( d . changes ) ;
188200 const hoveredRadius = currentRadius * 1.2 ;
189201
@@ -196,7 +208,9 @@ export const renderReleaseVisualization = ({
196208 const cy = parseFloat ( nodeElement . getAttribute ( "cy" ) || "0" ) ;
197209
198210 // SVG 좌표를 화면 좌표로 변환
199- const svgPoint = nodeElement . ownerSVGElement ! . createSVGPoint ( ) ;
211+ const ownerSvgElement = nodeElement . ownerSVGElement ;
212+ if ( ! ownerSvgElement ) return ;
213+ const svgPoint = ownerSvgElement . createSVGPoint ( ) ;
200214 svgPoint . x = cx ;
201215 svgPoint . y = cy ;
202216 const ctm = nodeElement . getScreenCTM ( ) ;
@@ -224,20 +238,31 @@ export const renderReleaseVisualization = ({
224238 ` ) ;
225239
226240 // 페이드 인 애니메이션
227- setTimeout ( ( ) => {
241+ tooltipShowTimer = setTimeout ( ( ) => {
228242 tooltip . style ( "opacity" , "1" ) ;
243+ tooltipShowTimer = null ;
229244 } , 10 ) ;
230245 } )
231246 . on ( "mouseout" , function ( _event : MouseEvent , d : ReleaseContributorActivity ) {
247+ if ( tooltipShowTimer ) {
248+ clearTimeout ( tooltipShowTimer ) ;
249+ tooltipShowTimer = null ;
250+ }
251+ if ( tooltipHideTimer ) {
252+ clearTimeout ( tooltipHideTimer ) ;
253+ tooltipHideTimer = null ;
254+ }
255+
232256 const currentRadius = sizeScale ( d . changes ) ;
233257
234258 // 노드 크기 원래대로
235259 d3 . select ( this ) . attr ( "r" , currentRadius ) . attr ( "stroke-width" , 0 ) ;
236260
237261 // 페이드 아웃 애니메이션
238262 tooltip . style ( "opacity" , "0" ) ;
239- setTimeout ( ( ) => {
263+ tooltipHideTimer = setTimeout ( ( ) => {
240264 tooltip . style ( "display" , "none" ) ;
265+ tooltipHideTimer = null ;
241266 } , 200 ) ;
242267 } ) ;
243268
0 commit comments