@@ -13,7 +13,7 @@ export class RowNavigation extends Module {
1313 this . registerTableFunction ( 'goToRow' , this . goToRow . bind ( this ) ) ;
1414 }
1515
16- goToRow (
16+ async goToRow (
1717 row : RowComponent ,
1818 opts : GoToRowOptions = { scrollIfVisible : true , focusRow : true } ,
1919 ) : Promise < void > {
@@ -58,30 +58,27 @@ export class RowNavigation extends Module {
5858 this . tableHolder . focus ( ) ;
5959 }
6060
61- return this . _scrollToRow ( row , opts ) ;
61+ return new Promise < void > ( ( resolve ) => {
62+ // Need to wait for any pending redraws to finish before scrolling or it will not work
63+ setTimeout ( ( ) => {
64+ this . _scrollToRow ( row , opts ) . then ( resolve ) ;
65+ } ) ;
66+ } ) ;
6267 }
6368
64- _scrollToRow ( row : RowComponent , opts : GoToRowOptions ) : Promise < void > {
69+ async _scrollToRow ( row : RowComponent , opts : GoToRowOptions ) : Promise < void > {
6570 const { scrollIfVisible, focusRow } = opts ;
6671
67- return this . table
68- . scrollToRow ( row , 'center' , scrollIfVisible )
69- . catch ( ( ) => { } )
70- . then ( ( ) => {
71- return new Promise < void > ( ( resolve ) => {
72- const elem = row . getElement ( ) ;
73-
74- if ( scrollIfVisible || ! this . _isVisible ( elem ) ) {
75- elem . scrollIntoView ( { behavior : 'auto' , block : 'center' , inline : 'start' } ) ;
76- }
72+ await this . table . scrollToRow ( row , 'center' , scrollIfVisible ) ;
7773
78- if ( focusRow ) {
79- elem . focus ( ) ;
80- }
74+ const elem = row . getElement ( ) ;
75+ if ( scrollIfVisible || ! this . _isVisible ( elem ) ) {
76+ this . _centerRow ( elem ) ;
77+ }
8178
82- resolve ( ) ;
83- } ) ;
84- } ) ;
79+ if ( focusRow ) {
80+ elem . focus ( ) ;
81+ }
8582 }
8683
8784 _isVisible ( el : Element ) {
@@ -92,4 +89,38 @@ export class RowNavigation extends Module {
9289 const rect = el . getBoundingClientRect ( ) ;
9390 return rect . top >= holderRect . top && rect . bottom <= holderRect . bottom ;
9491 }
92+
93+ // TODO: Remove once fixed upstream in tabulator-tables.
94+ //
95+ // Tabulator bug: _addBottomRow zeroes vDomBottomPad when vDomBottom reaches the last
96+ // row index — even for mid-table rows in expanded trees. This shrinks scrollHeight,
97+ // clamping scrollTop so scrollToRow places the row at the viewport bottom not center.
98+ // Fix: restore the minimum vDomBottomPad needed for centering, then set scrollTop
99+ // directly via offsetTop.
100+ _centerRow ( elem : HTMLElement ) {
101+ if ( ! this . tableHolder ) return ;
102+
103+ // Only near-bottom rows have vDomBottomPad forced to 0 — skip the DOM write for
104+ // all other rows where Tabulator already set it correctly.
105+ const renderer = this . table . rowManager ?. renderer as Record < string , unknown > | undefined ;
106+ if ( renderer && this . tableHolder && ( ( renderer . vDomBottomPad as number ) ?? 0 ) === 0 ) {
107+ const displayRows : unknown [ ] = this . table . rowManager ?. getDisplayRows ?.( ) ?? [ ] ;
108+ const vDomBottom = ( renderer . vDomBottom as number ) ?? 0 ;
109+ const vDomRowHeight = ( renderer . vDomRowHeight as number ) ?? 24 ;
110+ const truePad = Math . max ( 0 , ( displayRows . length - vDomBottom - 1 ) * vDomRowHeight ) ;
111+ // Cap at clientHeight/2 — the maximum extra scroll range needed to center any row.
112+ // Avoids large paddingBottom values for mid-table rows. If truePad < clientHeight/2
113+ // the row is genuinely near the bottom and the browser clamps naturally — no blank space.
114+ const neededPad = Math . min ( truePad , this . tableHolder . clientHeight / 2 ) ;
115+ if ( neededPad > 0 ) {
116+ renderer . vDomBottomPad = neededPad ;
117+ ( renderer . tableElement as HTMLElement ) . style . paddingBottom = `${ neededPad } px` ;
118+ }
119+ }
120+
121+ // Reading elem.offsetTop forces a layout flush — paddingBottom is included in
122+ // scrollHeight before scrollTop is assigned.
123+ const holderHeight = this . tableHolder . clientHeight ;
124+ this . tableHolder . scrollTop = elem . offsetTop - holderHeight / 2 + elem . offsetHeight / 2 ;
125+ }
95126}
0 commit comments