From 75e60e85194e7f2bf7e20e3891743ce801e05820 Mon Sep 17 00:00:00 2001 From: Stephan de Wit Date: Fri, 3 Apr 2026 13:47:26 +0200 Subject: [PATCH] bootgrid: maintain scrolling position for both datatree and command actions. Closes https://github.com/opnsense/core/issues/9151 The usage of scrollToRow has been considered, but this contains too much magic causing all kinds of jumps in scroll position, causing a user to lose track of the changes they made. The only downside to this is that when a datatree is expanded at the bottom of a grid, the associated rows aren't immediately visible until manually scrolled to. --- src/opnsense/www/js/opnsense_bootgrid.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/opnsense/www/js/opnsense_bootgrid.js b/src/opnsense/www/js/opnsense_bootgrid.js index 26937a622d..588c63a435 100644 --- a/src/opnsense/www/js/opnsense_bootgrid.js +++ b/src/opnsense/www/js/opnsense_bootgrid.js @@ -127,6 +127,7 @@ class UIBootgrid { this.treeStorageKey = `tabulator-${this.persistenceID}-openTree`; this.rememberedTreeIds = new Set(JSON.parse(localStorage.getItem(this.treeStorageKey) || '[]')); this.isVisible = false; + this.scrollPos = 0; // wrapper-specific options this.options = { @@ -756,6 +757,7 @@ class UIBootgrid { if (!id) return; open ? this.rememberedTreeIds.add(id) : this.rememberedTreeIds.delete(id); localStorage.setItem(this.treeStorageKey, JSON.stringify([...this.rememberedTreeIds])); + this._maintainScrollPosition(this.scrollPos); }; this.table.on('dataTreeRowExpanded', (row) => rememberTree(row, true)); @@ -876,6 +878,10 @@ class UIBootgrid { intersectObserver.observe(this.$element[0]); }); + + this.table.on('scrollVertical', (top) => { + this.scrollPos = top; + }); } _renderFooter() { @@ -1595,14 +1601,20 @@ class UIBootgrid { _reload(inplace=false) { let page = this.table.getPage(); + const scrollPos = this.scrollPos; + // both calls trigger an ajax request if (inplace) { - this.table.setPage(page); + this.table.setPage(page).then(() => this._maintainScrollPosition(scrollPos)); } else { - this.table.replaceData(); + this.table.replaceData().then(() => this._maintainScrollPosition(scrollPos)); } } + _maintainScrollPosition(pos) { + $(`#${this.id} > .tabulator-tableholder`)[0].scrollTop = pos; + } + _getPlaceholder() { return $(`#${this.id}-placeholder`); }