@@ -95,7 +95,18 @@ interface SortEntry {
9595
9696const sortState : Record < string , SortEntry > = { } ;
9797
98- function getSortValue ( td : Element ) : number | string {
98+ function getVisibleChild ( row : Element , index : number ) : Element | null {
99+ let count = 0 ;
100+ for ( let i = 0 ; i < row . children . length ; i ++ ) {
101+ if ( ( row . children [ i ] as HTMLElement ) . style . display === 'none' ) continue ;
102+ if ( count === index ) return row . children [ i ] ;
103+ count ++ ;
104+ }
105+ return null ;
106+ }
107+
108+ function getSortValue ( td : Element | null ) : number | string {
109+ if ( ! td ) return '' ;
99110 const order = td . getAttribute ( 'data-order' ) ;
100111 if ( order !== null ) return parseFloat ( order ) ;
101112 const text = ( td . textContent || '' ) . trim ( ) ;
@@ -115,8 +126,8 @@ function sortTable(table: Element, colIndex: number): void {
115126 const rows = Array . from ( tbody . querySelectorAll ( 'tr.t-file' ) ) ;
116127
117128 rows . sort ( ( a , b ) => {
118- const aVal = getSortValue ( a . children [ colIndex ] ) ;
119- const bVal = getSortValue ( b . children [ colIndex ] ) ;
129+ const aVal = getSortValue ( getVisibleChild ( a , colIndex ) ) ;
130+ const bVal = getSortValue ( getVisibleChild ( b , colIndex ) ) ;
120131 let cmp : number ;
121132 if ( typeof aVal === 'number' && typeof bVal === 'number' ) {
122133 cmp = aVal - bVal ;
@@ -129,11 +140,14 @@ function sortTable(table: Element, colIndex: number): void {
129140 rows . forEach ( row => tbody . appendChild ( row ) ) ;
130141
131142 // Update sort indicators
132- let idx = 0 ;
143+ let tdPos = 0 ;
133144 $$ ( 'thead tr:first-child th' , table ) . forEach ( ( th ) => {
145+ const span = parseInt ( th . getAttribute ( 'colspan' ) || '1' , 10 ) ;
134146 th . classList . remove ( 'sorting_asc' , 'sorting_desc' , 'sorting' ) ;
135- th . classList . add ( idx === colIndex ? ( dir === 'asc' ? 'sorting_asc' : 'sorting_desc' ) : 'sorting' ) ;
136- idx += parseInt ( th . getAttribute ( 'colspan' ) || '1' , 10 ) ;
147+ // colIndex falls within this th if it's >= tdPos and < tdPos + span
148+ const isActive = colIndex >= tdPos && colIndex < tdPos + span ;
149+ th . classList . add ( isActive ? ( dir === 'asc' ? 'sorting_asc' : 'sorting_desc' ) : 'sorting' ) ;
150+ tdPos += span ;
137151 } ) ;
138152}
139153
@@ -212,6 +226,7 @@ function filterTable(container: Element): void {
212226 } ) ;
213227
214228 updateTotalsRow ( container ) ;
229+ equalizeBarWidths ( ) ;
215230}
216231
217232function updateFilterOptions ( input : HTMLInputElement ) : void {
@@ -252,22 +267,16 @@ function updateTotalsRow(container: Element): void {
252267 const relevantLines = sumData ( 'relevantLines' ) ;
253268 updateCoverageCells ( container , '.t-totals__line' , coveredLines , relevantLines ) ;
254269
255- const numberCells = $$ ( '.totals-row .cell--number' , container ) ;
256- if ( numberCells [ 0 ] ) numberCells [ 0 ] . textContent = relevantLines ? fmtNum ( relevantLines ) : '' ;
257-
258270 if ( $ ( '.t-totals__branch-pct' , container ) ) {
259271 const coveredBranches = sumData ( 'coveredBranches' ) ;
260272 const totalBranches = sumData ( 'totalBranches' ) ;
261273 updateCoverageCells ( container , '.t-totals__branch' , coveredBranches , totalBranches ) ;
262- if ( numberCells [ 1 ] ) numberCells [ 1 ] . textContent = totalBranches ? fmtNum ( totalBranches ) : '' ;
263274 }
264275
265276 if ( $ ( '.t-totals__method-pct' , container ) ) {
266277 const coveredMethods = sumData ( 'coveredMethods' ) ;
267278 const totalMethods = sumData ( 'totalMethods' ) ;
268279 updateCoverageCells ( container , '.t-totals__method' , coveredMethods , totalMethods ) ;
269- const numIdx = $ ( '.t-totals__branch-pct' , container ) ? 2 : 1 ;
270- if ( numberCells [ numIdx ] ) numberCells [ numIdx ] . textContent = totalMethods ? fmtNum ( totalMethods ) : '' ;
271280 }
272281}
273282
@@ -292,18 +301,18 @@ function materializeSourceFile(sourceFileId: string): HTMLElement | null {
292301
293302// --- Bar width equalization ------------------------------------
294303
295- function setBarWidth ( bars : Element [ ] , headers : Element [ ] , px : number ) : void {
304+ function setBarWidth ( bars : Element [ ] , table : Element , px : number ) : void {
296305 const w = px + 'px' ;
297- headers . forEach ( h => h . setAttribute ( 'colspan' , '4 ' ) ) ;
306+ $$ ( 'th.cell--coverage' , table ) . forEach ( h => h . setAttribute ( 'colspan' , '2 ' ) ) ;
298307 bars . forEach ( b => {
299308 const s = ( b as HTMLElement ) . style ;
300309 s . display = '' ;
301310 s . width = w ; s . minWidth = w ; s . maxWidth = w ;
302311 } ) ;
303312}
304313
305- function hideBars ( bars : Element [ ] , headers : Element [ ] ) : void {
306- headers . forEach ( h => h . setAttribute ( 'colspan' , '3 ' ) ) ;
314+ function hideBars ( bars : Element [ ] , table : Element ) : void {
315+ $$ ( 'th.cell--coverage' , table ) . forEach ( h => h . setAttribute ( 'colspan' , '1 ' ) ) ;
307316 bars . forEach ( b => {
308317 const s = ( b as HTMLElement ) . style ;
309318 s . display = 'none' ; s . width = '' ; s . minWidth = '' ; s . maxWidth = '' ;
@@ -318,33 +327,43 @@ function equalizeBarWidths(): void {
318327 const table = $ ( 'table.file_list' , container ) as HTMLTableElement | null ;
319328 if ( ! table ) return ;
320329 const bars = $$ ( 'td.cell--bar' , table ) ;
321- const headers = $$ ( 'th[colspan]' , table ) ;
322330 if ( bars . length === 0 ) return ;
323331
324332 const wrapper = table . closest ( '.file_list--responsive' ) as HTMLElement | null ;
325333 if ( ! wrapper ) return ;
326334
327- const firstDataRow = $ ( 'tbody tr' , table ) || $ ( 'thead tr.totals-row' , table ) ;
328- const barsPerRow = firstDataRow ? $$ ( 'td.cell--bar' , firstDataRow ) . length : 1 ;
329-
330- // Step 1: Hide bars, measure content width with table at auto
331- hideBars ( bars , headers ) ;
332- table . style . width = 'auto' ;
333- void table . offsetWidth ;
334- const contentWidth = table . scrollWidth ;
335-
336- // Step 2: Restore table width, measure available space
337- table . style . width = '' ;
338- void table . offsetWidth ;
339- const availableWidth = wrapper . clientWidth ;
340-
341- // Step 3: Calculate bar width
342- const totalBarSpace = availableWidth - contentWidth ;
343- const perBar = Math . floor ( totalBarSpace / barsPerRow ) ;
344-
345- if ( perBar < 100 ) return ; // keep bars hidden
335+ // Hide during measurement to prevent flicker
336+ wrapper . style . visibility = 'hidden' ;
337+
338+ // Test whether bars at a given width fit without overflow
339+ const fitsAt = ( px : number ) : boolean => {
340+ setBarWidth ( bars , table , px ) ;
341+ table . style . width = 'auto' ;
342+ void table . offsetWidth ;
343+ const fits = table . scrollWidth <= wrapper . clientWidth ;
344+ table . style . width = '' ;
345+ return fits ;
346+ } ;
347+
348+ let barWidth = 240 ;
349+ if ( ! fitsAt ( 240 ) ) {
350+ if ( ! fitsAt ( 80 ) ) {
351+ hideBars ( bars , table ) ;
352+ wrapper . style . visibility = '' ;
353+ return ;
354+ }
355+ // Binary search for the widest bars that fit
356+ let lo = 80 , hi = 239 ;
357+ while ( lo < hi ) {
358+ const mid = Math . ceil ( ( lo + hi ) / 2 ) ;
359+ if ( fitsAt ( mid ) ) lo = mid ;
360+ else hi = mid - 1 ;
361+ }
362+ barWidth = lo ;
363+ }
346364
347- setBarWidth ( bars , headers , Math . min ( perBar , 200 ) ) ;
365+ setBarWidth ( bars , table , barWidth ) ;
366+ wrapper . style . visibility = '' ;
348367 } ) ;
349368}
350369
@@ -472,15 +491,22 @@ document.addEventListener('DOMContentLoaded', function () {
472491 } ) ;
473492 } ) ( ) ;
474493
475- // Table sorting — compute td index from th colspan
494+ // Table sorting — compute td index dynamically at click time
495+ function thToTdIndex ( table : Element , clickedTh : Element ) : number {
496+ let idx = 0 ;
497+ for ( const th of $$ ( 'thead tr:first-child th' , table ) ) {
498+ const span = parseInt ( th . getAttribute ( 'colspan' ) || '1' , 10 ) ;
499+ if ( th === clickedTh ) return idx + span - 1 ;
500+ idx += span ;
501+ }
502+ return idx ;
503+ }
504+
476505 $$ ( 'table.file_list' ) . forEach ( table => {
477- let tdIndex = 0 ;
478506 $$ ( 'thead tr:first-child th' , table ) . forEach ( ( th ) => {
479- const myTdIndex = tdIndex ;
480507 th . classList . add ( 'sorting' ) ;
481508 ( th as HTMLElement ) . style . cursor = 'pointer' ;
482- th . addEventListener ( 'click' , ( ) => sortTable ( table , myTdIndex ) ) ;
483- tdIndex += parseInt ( th . getAttribute ( 'colspan' ) || '1' , 10 ) ;
509+ th . addEventListener ( 'click' , ( ) => sortTable ( table , thToTdIndex ( table , th ) ) ) ;
484510 } ) ;
485511 } ) ;
486512
@@ -596,4 +622,5 @@ document.addEventListener('DOMContentLoaded', function () {
596622
597623 // Equalize bar widths now that wrapper is visible
598624 equalizeBarWidths ( ) ;
625+
599626} ) ;
0 commit comments