From bdf79336f938bedc5ccf488e7c8b65580acbcd71 Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Fri, 19 Jun 2026 14:26:49 -0400 Subject: [PATCH] Account for sticky section header when scrolling a row into view Arrow-key navigation scrolled the newly selected row flush with the top of the view, leaving it hidden behind the pinned sticky section header. Reserve a row's worth of space at the top so the row lands below the pinned header. Fixes #5959 --- .../content/zotero/components/virtualized-table.jsx | 11 ++++++++++- chrome/content/zotero/components/windowed-list.js | 9 ++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/chrome/content/zotero/components/virtualized-table.jsx b/chrome/content/zotero/components/virtualized-table.jsx index bf625ac3e7..8cdb02f908 100644 --- a/chrome/content/zotero/components/virtualized-table.jsx +++ b/chrome/content/zotero/components/virtualized-table.jsx @@ -813,7 +813,16 @@ class VirtualizedTable extends React.Component { * @param index {Number} */ scrollToRow(index) { - this._jsWindow && this._jsWindow.scrollToRow(index); + if (!this._jsWindow) return; + // When a sticky section header is pinned at the top of the view, it overlays the rows + // below it. Reserve a row's worth of space so a row scrolled up into view lands below the + // pinned header rather than behind it. + let topOffset = 0; + if (this.props.stickySectionHeaders && !this.props.isSectionHeader(index) + && this._getSectionHeaderIndices().some(i => i < index)) { + topOffset = this._rowHeight; + } + this._jsWindow.scrollToRow(index, false, topOffset); } /** diff --git a/chrome/content/zotero/components/windowed-list.js b/chrome/content/zotero/components/windowed-list.js index e99b971916..cd264e6851 100644 --- a/chrome/content/zotero/components/windowed-list.js +++ b/chrome/content/zotero/components/windowed-list.js @@ -205,8 +205,11 @@ module.exports = class { * @param {Integer} index * @param {Boolean} forceScrollToTop If true, the row will be scrolled to the top of the scrollbox * even if it is below the current scroll window. + * @param {Integer} topOffset Amount of space reserved at the top of the scrollbox (e.g. for a + * sticky section header that overlays the rows). When scrolling a row into view from above, the + * row is positioned below this offset rather than flush with the top edge. */ - scrollToRow(index, forceScrollToTop = false) { + scrollToRow(index, forceScrollToTop = false, topOffset = 0) { const { scrollOffset } = this; const itemCount = this._getItemCount(); const height = this.getWindowHeight(); @@ -221,8 +224,8 @@ module.exports = class { this.scrollTo(startPosition); return; } - if (startPosition < scrollOffset) { - this.scrollTo(startPosition); + if (startPosition - topOffset < scrollOffset) { + this.scrollTo(startPosition - topOffset); } else if (endPosition > scrollOffset + height) { this.scrollTo(endPosition - height - 1);