@@ -57,16 +57,22 @@ const AuthorBarChart = () => {
5757 const svg = d3 . select ( svgRef . current ) . attr ( "width" , DIMENSIONS . width ) . attr ( "height" , DIMENSIONS . height ) ;
5858 const tooltip = d3 . select ( tooltipRef . current ) ;
5959
60- svg . selectAll ( "*" ) . remove ( ) ;
61-
6260 const totalMetricValues = data . reduce ( ( acc , item ) => acc + item [ metric ] , 0 ) ;
6361
6462 const xAxisGroup = svg
65- . append ( "g" )
63+ . selectAll ( ".x-axis" )
64+ . data ( [ null ] )
65+ . join ( "g" )
6666 . attr ( "class" , "author-bar-chart__axis x-axis" )
6767 . style ( "transform" , `translateY(${ pxToRem ( DIMENSIONS . height ) } )` ) ;
68- const yAxisGroup = svg . append ( "g" ) . attr ( "class" , "author-bar-chart__axis y-axis" ) ;
69- const barGroup = svg . append ( "g" ) . attr ( "class" , "author-bar-chart__container" ) ;
68+
69+ const yAxisGroup = svg . selectAll ( ".y-axis" ) . data ( [ null ] ) . join ( "g" ) . attr ( "class" , "author-bar-chart__axis y-axis" ) ;
70+
71+ const barGroup = svg
72+ . selectAll ( ".author-bar-chart__container" )
73+ . data ( [ null ] )
74+ . join ( "g" )
75+ . attr ( "class" , "author-bar-chart__container" ) ;
7076
7177 // Scales
7278 const xScale = d3
@@ -84,13 +90,15 @@ const AuthorBarChart = () => {
8490
8591 // Axis
8692 const xAxis = d3 . axisBottom ( xScale ) . ticks ( 0 ) . tickSizeInner ( 0 ) . tickSizeOuter ( 0 ) ;
87- xAxisGroup . call ( xAxis ) ;
93+ xAxisGroup . call ( xAxis as any ) ;
8894
8995 const yAxis = d3 . axisLeft ( yScale ) . ticks ( 10 ) . tickFormat ( convertNumberFormat ) . tickSizeOuter ( 0 ) ;
90- yAxisGroup . call ( yAxis ) ;
96+ yAxisGroup . call ( yAxis as any ) ;
9197
9298 xAxisGroup
93- . append ( "text" )
99+ . selectAll ( ".x-axis__label" )
100+ . data ( [ null ] )
101+ . join ( "text" )
94102 . attr ( "class" , "x-axis__label" )
95103 . style ( "transform" , `translate(${ pxToRem ( DIMENSIONS . width / 2 ) } , ${ pxToRem ( DIMENSIONS . margins - 10 ) } )` )
96104 . text ( `${ metric } # / Total ${ metric } # (%)` ) ;
@@ -159,48 +167,88 @@ const AuthorBarChart = () => {
159167 } ;
160168
161169 // Draw bars
162- barGroup
163- . selectAll ( "rect " )
164- . data ( data )
170+ const bars = barGroup
171+ . selectAll ( ".author-bar-chart__bar " )
172+ . data ( data , ( d : any ) => d ?. name || "" )
165173 . join (
166- ( enter ) =>
167- enter
168- . append ( "g" )
169- . attr ( "class" , "author-bar-chart__bar" )
174+ ( enter ) => {
175+ const barElement = enter . append ( "g" ) . attr ( "class" , "author-bar-chart__bar" ) ;
176+
177+ // 각 바 그룹에 rect 추가
178+ barElement
170179 . append ( "rect" )
171180 . attr ( "width" , xScale . bandwidth ( ) )
172181 . attr ( "height" , 0 )
173- . attr ( "x" , ( d ) => xScale ( d . name ) || 0 )
174- . attr ( "y" , DIMENSIONS . height ) ,
175- ( update ) => update ,
176- ( exit ) => exit . attr ( "height" , 0 ) . attr ( "y" , 0 ) . remove ( )
177- )
178- . on ( "mouseover" , handleMouseOver )
179- . on ( "mousemove" , handleMouseMove )
180- . on ( "mouseout" , handleMouseOut )
181- . on ( "click" , handleClickBar )
182+ . attr ( "x" , ( d : any ) => xScale ( d ?. name ) || 0 )
183+ . attr ( "y" , DIMENSIONS . height )
184+ . on ( "mouseover" , handleMouseOver )
185+ . on ( "mousemove" , handleMouseMove )
186+ . on ( "mouseout" , handleMouseOut )
187+ . on ( "click" , handleClickBar ) ;
188+
189+ return barElement ;
190+ } ,
191+ ( update ) => {
192+ update
193+ . select ( "rect" )
194+ . on ( "mouseover" , handleMouseOver )
195+ . on ( "mousemove" , handleMouseMove )
196+ . on ( "mouseout" , handleMouseOut )
197+ . on ( "click" , handleClickBar ) ;
198+
199+ return update ;
200+ } ,
201+ ( exit ) => {
202+ exit . select ( "rect" ) . transition ( ) . duration ( 250 ) . attr ( "height" , 0 ) . attr ( "y" , DIMENSIONS . height ) ;
203+
204+ return exit . transition ( ) . duration ( 250 ) . remove ( ) ;
205+ }
206+ ) ;
207+
208+ bars
209+ . select ( "rect" )
182210 . transition ( )
183211 . duration ( 500 )
184212 . attr ( "width" , xScale . bandwidth ( ) )
185- . attr ( "height" , ( d : AuthorDataType ) => DIMENSIONS . height - yScale ( d [ metric ] ) )
186- . attr ( "x" , ( d : AuthorDataType ) => xScale ( d . name ) || 0 )
187- . attr ( "y" , ( d : AuthorDataType ) => yScale ( d [ metric ] ) ) ;
213+ . attr ( "height" , ( d : any ) => DIMENSIONS . height - yScale ( d ?. [ metric ] || 0 ) )
214+ . attr ( "x" , ( d : any ) => xScale ( d ? .name ) || 0 )
215+ . attr ( "y" , ( d : any ) => yScale ( d ?. [ metric ] || 0 ) ) ;
188216
189217 // Draw author thumbnails
190- const barElements = d3 . selectAll ( ".author-bar-chart__bar" ) . nodes ( ) ;
191- if ( ! barElements . length ) return ;
218+ bars . selectAll ( "image.author-bar-chart__profile-image" ) . remove ( ) ;
219+
220+ // 새로운 이미지들 추가 (비동기 로딩)
221+ const imagePromises = data . map ( async ( d : AuthorDataType ) => {
222+ if ( ! d ?. name ) return null ;
223+
224+ try {
225+ const profileImgSrc : string = await getAuthorProfileImgSrc ( d . name ) . then ( ( res : AuthorInfo ) => res . src ) ;
226+ return { name : d . name , src : profileImgSrc } ;
227+ } catch ( error ) {
228+ console . warn ( `Failed to load profile image for ${ d . name } :` , error ) ;
229+ return null ;
230+ }
231+ } ) ;
232+
233+ // 모든 이미지 로딩 완료 후 한번에 표시
234+ Promise . all ( imagePromises ) . then ( ( imageResults ) => {
235+ const validImages = imageResults . filter ( ( result ) => result !== null ) ;
192236
193- barElements . forEach ( async ( barElement , i ) => {
194- const bar = d3 . select ( barElement ) . datum ( data [ i ] ) ;
195- const profileImgSrc : string = await getAuthorProfileImgSrc ( data [ i ] . name ) . then ( ( res : AuthorInfo ) => res . src ) ;
196- bar
237+ bars
238+ . selectAll ( "image.author-bar-chart__profile-image" )
239+ . data ( validImages , ( d : any ) => d ?. name || "" )
240+ . enter ( )
197241 . append ( "image" )
198242 . attr ( "class" , "author-bar-chart__profile-image" )
199- . attr ( "xlink:href" , profileImgSrc ?? "" )
200- . attr ( "x" , ( d : AuthorDataType ) => ( xScale ( d . name ) ?? 0 ) + xScale . bandwidth ( ) / 2 - 7 )
243+ . attr ( "x" , ( d : any ) => ( xScale ( d ?. name ) ?? 0 ) + xScale . bandwidth ( ) / 2 - 7 )
201244 . attr ( "y" , 204 )
202245 . attr ( "width" , 14 )
203- . attr ( "height" , 14 ) ;
246+ . attr ( "height" , 14 )
247+ . attr ( "xlink:href" , ( d : any ) => d ?. src ?? "" )
248+ . style ( "opacity" , 0 )
249+ . transition ( )
250+ . duration ( 300 )
251+ . style ( "opacity" , 1 ) ;
204252 } ) ;
205253 } , [
206254 data ,
0 commit comments