1818 gdpAdjustment : 100 ,
1919 rankingSeason : "Summer" ,
2020 rankingYear : null ,
21- expandedPredictionCountry : null
21+ expandedPredictionCountry : null ,
22+ highlightedCountry : null ,
2223 } ;
2324
2425 const medalOrder = [ "Gold" , "Silver" , "Bronze" ] ;
10001001 </div>
10011002
10021003 <div class="fair-ranking-layout">
1003- ${ rankingTable ( "Actual medal table " , actualRanking , "actual" ) }
1004- ${ rankingTable ( "Fair medal table " , fairRanking , "fair" ) }
1004+ ${ rankingTable ( "Actual medal ranking " , actualRanking , "actual" ) }
1005+ ${ rankingTable ( "Fair medal ranking " , fairRanking , "fair" ) }
10051006 </div>
10061007 ` ) ;
10071008
10181019 state . rankingYear = Number ( this . value ) ;
10191020 renderRankingTable ( ) ;
10201021 } ) ;
1022+
1023+ // Click highlight country
1024+ d3 . selectAll ( ".fair-ranking-table tbody tr" ) . style ( "cursor" , "pointer" )
1025+ . on ( "click" , function ( ) {
1026+ const country = d3 . select ( this ) . select ( "td:nth-child(2)" ) . text ( ) . trim ( ) ;
1027+ const alreadyHighlighted = state . highlightedCountry === country ;
1028+ state . highlightedCountry = alreadyHighlighted ? null : country ;
1029+ if ( state . highlightedCountry ) {
1030+ highlightCountryInRanking ( state . highlightedCountry ) ;
1031+ } else {
1032+ clearRankingHighlight ( ) ;
1033+ }
1034+ } ) ;
1035+
1036+ if ( state . highlightedCountry ) {
1037+ highlightCountryInRanking ( state . highlightedCountry ) ;
1038+ }
1039+
1040+ // Hover country
1041+ d3 . selectAll ( ".fair-ranking-table tbody tr" )
1042+ . on ( "mouseover" , function ( ) {
1043+ const country = d3 . select ( this ) . select ( "td:nth-child(2)" ) . text ( ) . trim ( ) ;
1044+ const indicators = getCountryIndicators ( country ) ;
1045+
1046+ const tooltip = d3 . select ( "body" ) . select ( ".fair-ranking-tooltip" ) ;
1047+
1048+ const html = `
1049+ <strong>${ country } </strong><br>
1050+ Population: ${ formatBig ( indicators . population ) } <br>
1051+ GDP per capita: ${ formatBig ( indicators . gdp ) }
1052+ ` ;
1053+
1054+ tooltip . html ( html ) . style ( "opacity" , 1 ) ;
1055+ } )
1056+ . on ( "mousemove" , function ( event ) {
1057+ d3 . select ( ".fair-ranking-tooltip" )
1058+ . style ( "left" , `${ event . pageX + 14 } px` )
1059+ . style ( "top" , `${ event . pageY + 14 } px` ) ;
1060+ } )
1061+ . on ( "mouseleave" , function ( ) {
1062+ d3 . select ( ".fair-ranking-tooltip" ) . style ( "opacity" , 0 ) ;
1063+ } ) ;
1064+
1065+ if ( d3 . select ( "body" ) . select ( ".fair-ranking-tooltip" ) . empty ( ) ) {
1066+ d3 . select ( "body" ) . append ( "div" ) . attr ( "class" , "fair-ranking-tooltip" )
1067+ . style ( "position" , "fixed" )
1068+ . style ( "pointer-events" , "none" )
1069+ . style ( "opacity" , 0 )
1070+ . style ( "background" , "rgba(20,20,20,0.96)" )
1071+ . style ( "color" , "white" )
1072+ . style ( "border" , "1px solid rgba(255,255,255,0.18)" )
1073+ . style ( "border-radius" , "10px" )
1074+ . style ( "padding" , "10px 12px" )
1075+ . style ( "font-size" , "14px" )
1076+ . style ( "font-family" , "'Elms Sans', sans-serif" )
1077+ . style ( "line-height" , "1.35" )
1078+ . style ( "max-width" , "260px" )
1079+ . style ( "z-index" , "99999" )
1080+ . style ( "box-shadow" , "0 8px 25px rgba(0,0,0,0.35)" ) ;
1081+ }
10211082 }
10221083
10231084 function rankingTable ( title , rows , type ) {
10241085 return `
10251086 <div class="fair-ranking-card">
1026- <h2>${ title } </h2>
1027- <table class="fair-ranking-table">
1028- <thead>
1029- <tr>
1030- <th>#</th>
1031- <th>Country</th>
1032- <th>Gold</th>
1033- <th>Silver</th>
1034- <th>Bronze</th>
1035- </tr>
1036- </thead>
1037- <tbody>
1038- ${ rows . map ( ( row , i ) => `
1087+ <p>${ title } </p>
1088+ <div class="fair-ranking-scroll">
1089+ <table class="fair-ranking-table">
1090+ <thead>
10391091 <tr>
1040- <td> ${ i + 1 } </td >
1041- <td> ${ row . country } </td >
1042- <td> ${ formatFair ( row [ type ] . Gold ) } </td >
1043- <td> ${ formatFair ( row [ type ] . Silver ) } </td >
1044- <td> ${ formatFair ( row [ type ] . Bronze ) } </td >
1092+ <th>#</th >
1093+ <th>Country</th >
1094+ <th> Gold</th >
1095+ <th> Silver</th >
1096+ <th> Bronze</th >
10451097 </tr>
1046- ` ) . join ( "" ) }
1047- </tbody>
1048- </table>
1098+ </thead>
1099+ <tbody>
1100+ ${ rows . map ( ( row , i ) => `
1101+ <tr>
1102+ <td>${ i + 1 } </td>
1103+ <td>${ row . country } </td>
1104+ <td>${ formatFair ( row [ type ] . Gold ) } </td>
1105+ <td>${ formatFair ( row [ type ] . Silver ) } </td>
1106+ <td>${ formatFair ( row [ type ] . Bronze ) } </td>
1107+ </tr>
1108+ ` ) . join ( "" ) }
1109+ </tbody>
1110+ </table>
1111+ </div>
10491112 </div>
10501113 ` ;
10511114 }
11011164 </div>
11021165 ` ;
11031166 }
1167+
1168+ function highlightCountryInRanking ( country ) {
1169+ d3 . selectAll ( ".fair-ranking-table tbody tr" )
1170+ . each ( function ( ) {
1171+ const row = d3 . select ( this ) ;
1172+ const rowCountry = row . select ( "td:nth-child(2)" ) . text ( ) . trim ( ) ;
1173+ const isMatch = rowCountry === country ;
1174+
1175+ row . style ( "background" , isMatch ? "rgba(244, 180, 0, 0.25)" : null )
1176+ . style ( "color" , isMatch ? "#f4b400" : null )
1177+ . style ( "font-weight" , isMatch ? "800" : null )
1178+ . attr ( "data-highlighted" , isMatch ? "true" : null ) ;
1179+
1180+ if ( isMatch ) {
1181+ this . scrollIntoView ( { behavior : "smooth" , block : "nearest" } ) ;
1182+ }
1183+ } ) ;
1184+ }
1185+
1186+ function clearRankingHighlight ( ) {
1187+ d3 . selectAll ( ".fair-ranking-table tbody tr" )
1188+ . style ( "background" , null )
1189+ . style ( "color" , null )
1190+ . style ( "font-weight" , null )
1191+ . attr ( "data-highlighted" , null ) ;
1192+ }
11041193} ) ( ) ;
0 commit comments