@@ -66,60 +66,75 @@ export const renderClusterVisualization = ({
6666 . domain ( [ 0 , d3 . max ( contributorActivities , ( d ) => d . changes ) || 1 ] )
6767 . range ( [ 3 , 12 ] ) ;
6868
69- const colorScale = d3 . scaleOrdinal ( ) . domain ( uniqueContributors ) . range ( d3 . schemeCategory10 ) ;
69+ const svgElement = svg . node ( ) ;
70+ const parentElement = svgElement ?. parentElement ;
71+ const chartColors = Array . from ( { length : 10 } , ( _ , i ) =>
72+ getComputedStyle ( parentElement || document . documentElement )
73+ . getPropertyValue ( `--chart-color-${ i + 1 } ` )
74+ . trim ( )
75+ ) ;
76+
77+ const colorScale = d3 . scaleOrdinal ( ) . domain ( uniqueContributors ) . range ( chartColors ) ;
7078
7179 const mainGroup = svg . append ( "g" ) ;
7280
73- // 폴더 레인 그리기
7481 mainGroup
75- . selectAll ( ".folder-lane " )
82+ . selectAll ( ".folder-label " )
7683 . data ( topFolders )
7784 . enter ( )
78- . append ( "g" )
79- . attr ( "class" , "folder-lane" )
80- . each ( function ( this : SVGGElement , d : FolderActivity ) {
81- const lane = d3 . select ( this ) ;
82-
83- lane
84- . append ( "rect" )
85- . attr ( "class" , "lane-background" )
86- . attr ( "x" , DIMENSIONS . margin . left )
87- . attr ( "y" , yScale ( d . folderPath ) || 0 )
88- . attr ( "width" , DIMENSIONS . width - DIMENSIONS . margin . left - DIMENSIONS . margin . right )
89- . attr ( "height" , yScale . bandwidth ( ) )
90- . attr ( "fill" , "#f8f9fa" )
91- . attr ( "stroke" , "#dee2e6" )
92- . attr ( "stroke-width" , 1 ) ;
93-
94- lane
95- . append ( "text" )
96- . attr ( "class" , "folder-label clickable" )
97- . attr ( "x" , DIMENSIONS . width - DIMENSIONS . margin . right + 10 )
98- . attr ( "y" , ( yScale ( d . folderPath ) || 0 ) + yScale . bandwidth ( ) / 2 )
99- . attr ( "text-anchor" , "start" )
100- . attr ( "dominant-baseline" , "middle" )
101- . text ( ( ) => {
102- if ( d . folderPath === "." ) return "root" ;
103-
104- const fileName = d . folderPath . includes ( "/" ) ? d . folderPath . split ( "/" ) . pop ( ) : d . folderPath ;
105-
106- return fileName && fileName . length > 15 ? `${ fileName . substring ( 0 , 12 ) } ...` : fileName || "unknown" ;
107- } )
108- . style ( "font-size" , "12px" )
109- . style ( "fill" , "#495057" )
110- . style ( "font-weight" , "500" )
111- . style ( "cursor" , "pointer" )
112- . on ( "click" , ( ) => {
113- if ( d . folderPath !== "." ) {
114- onFolderClick ( d . folderPath ) ;
115- }
116- } )
117- . on ( "mouseover" , function ( ) {
118- d3 . select ( this ) . style ( "fill" , "#007bff" ) ;
119- } )
120- . on ( "mouseout" , function ( ) {
121- d3 . select ( this ) . style ( "fill" , "#495057" ) ;
122- } ) ;
85+ . append ( "text" )
86+ . attr ( "class" , ( d : FolderActivity ) => {
87+ const isFile = d . folderPath . includes ( "." ) ;
88+ return isFile ? "folder-label" : "folder-label clickable" ;
89+ } )
90+ . attr ( "x" , DIMENSIONS . width - DIMENSIONS . margin . right + 10 )
91+ . attr ( "y" , ( d : FolderActivity ) => ( yScale ( d . folderPath ) || 0 ) + yScale . bandwidth ( ) / 2 )
92+ . attr ( "text-anchor" , "start" )
93+ . attr ( "dominant-baseline" , "middle" )
94+ . text ( ( d : FolderActivity ) => {
95+ if ( d . folderPath === "." ) return "root" ;
96+
97+ const fileName = d . folderPath . includes ( "/" ) ? d . folderPath . split ( "/" ) . pop ( ) : d . folderPath ;
98+
99+ return fileName && fileName . length > 15 ? `${ fileName . substring ( 0 , 15 ) } ...` : fileName || "unknown" ;
100+ } )
101+ . style ( "font-size" , "12px" )
102+ . style ( "fill" , "#b4bac6" )
103+ . style ( "font-weight" , "500" )
104+ . style ( "cursor" , ( d : FolderActivity ) => {
105+ const isFile = d . folderPath . includes ( "." ) ;
106+ return isFile ? "default" : "pointer" ;
107+ } )
108+ . on ( "click" , ( _event : MouseEvent , d : FolderActivity ) => {
109+ const isFile = d . folderPath . includes ( "." ) ;
110+ if ( ! isFile && d . folderPath !== "." ) {
111+ onFolderClick ( d . folderPath ) ;
112+ }
113+ } )
114+ . on ( "mouseover" , function ( _event : MouseEvent , d : FolderActivity ) {
115+ const isFile = d . folderPath . includes ( "." ) ;
116+ if ( ! isFile ) {
117+ d3 . select ( this ) . style ( "fill" , "#e06091" ) ;
118+ }
119+ } )
120+ . on ( "mouseout" , function ( _event : MouseEvent , d : FolderActivity ) {
121+ const isFile = d . folderPath . includes ( "." ) ;
122+ const element = d3 . select ( this ) ;
123+ if ( ! isFile ) {
124+ element . style ( "fill" , "#b4bac6" ) ;
125+ }
126+ // 호버 끝나면 축약된 텍스트로 복원
127+ const fileName = d . folderPath . includes ( "/" ) ? d . folderPath . split ( "/" ) . pop ( ) : d . folderPath ;
128+ const displayName = fileName && fileName . length > 15 ? `${ fileName . substring ( 0 , 15 ) } ...` : fileName || "unknown" ;
129+ element . text ( d . folderPath === "." ? "root" : displayName ) ;
130+ } ) ;
131+
132+ // 호버 시 전체 이름 표시를 위한 추가 이벤트
133+ mainGroup
134+ . selectAll < SVGTextElement , FolderActivity > ( ".folder-label" )
135+ . on ( "mouseover.showfull" , function ( _event , d ) {
136+ const fileName = d . folderPath . includes ( "/" ) ? d . folderPath . split ( "/" ) . pop ( ) : d . folderPath ;
137+ d3 . select ( this ) . text ( d . folderPath === "." ? "root" : fileName || "unknown" ) ;
123138 } ) ;
124139
125140 // 클러스터 축
@@ -151,9 +166,7 @@ export const renderClusterVisualization = ({
151166 . attr ( "cy" , ( d : ContributorActivity ) => ( yScale ( d . folderPath ) || 0 ) + yScale . bandwidth ( ) / 2 )
152167 . attr ( "r" , ( d : ContributorActivity ) => sizeScale ( d . changes ) )
153168 . attr ( "fill" , ( d : ContributorActivity ) => colorScale ( d . contributorName ) as string )
154- . attr ( "fill-opacity" , 0.8 )
155- . attr ( "stroke" , "#fff" )
156- . attr ( "stroke-width" , 1 ) ;
169+ . attr ( "fill-opacity" , 0.8 ) ;
157170
158171 // 툴팁 이벤트
159172 dots
@@ -198,7 +211,7 @@ export const renderClusterVisualization = ({
198211 . attr ( "dominant-baseline" , "bottom" )
199212 . text ( ( d : ContributorActivity ) => d . contributorName )
200213 . style ( "font-size" , "10px" )
201- . style ( "fill" , "#495057 " )
214+ . style ( "fill" , "#f7f7f7 " )
202215 . style ( "font-weight" , "500" )
203216 . style ( "pointer-events" , "none" ) ;
204217
@@ -215,5 +228,5 @@ export const renderClusterVisualization = ({
215228 . attr ( "fill" , "none" )
216229 . attr ( "stroke" , ( d ) => colorScale ( d . contributorName ) as string )
217230 . attr ( "stroke-width" , 2 )
218- . attr ( "stroke-opacity" , 0.4 ) ;
231+ . attr ( "stroke-opacity" , 1 ) ;
219232} ;
0 commit comments