add modal scroll stabilizer in modals.js

Alessandro committed Mar 13, 2026 at 22:17 UTC 9b8db0e8c8d8fe6bbdc679d3221d27532f608eff
1 file changed +33
webui/js/modals.js
+33
@@ -5,6 +5,32 @@ import { callJsExtensions } from "/js/extensions.js";
5 // Modal functionality
6 const modalStack = [];
7
8 +function getModalScrollElement(modal) {
9 + return modal?.element?.querySelector(".modal-scroll");
10 +}
11 +
12 +function captureModalScrollSnapshot(modal) {
13 + const modalScroll = getModalScrollElement(modal);
14 + if (!modalScroll) return null;
15 + return {
16 + scrollTop: modalScroll.scrollTop,
17 + scrollLeft: modalScroll.scrollLeft,
18 + };
19 +}
20 +
21 +function restoreModalScrollSnapshot(modal) {
22 + const snapshot = modal?.savedScrollSnapshot;
23 + if (!snapshot) return;
24 +
25 + requestAnimationFrame(() => {
26 + const modalScroll = getModalScrollElement(modal);
27 + if (!modalScroll) return;
28 + modalScroll.scrollTop = snapshot.scrollTop;
29 + modalScroll.scrollLeft = snapshot.scrollLeft;
30 + modal.savedScrollSnapshot = null;
31 + });
32 +}
33 +
34 // Create a single backdrop for all modals
35 const backdrop = document.createElement("div");
36 backdrop.className = "modal-backdrop";
@@ -103,6 +129,7 @@ function createModalElement(path) {
129 styles: [],
130 scripts: [],
131 beforeClose: null,
132 + savedScrollSnapshot: null,
133 };
134 }
135
@@ -115,6 +142,11 @@ export async function openModal(modalPath, beforeClose = null) {
142
143 return new Promise((resolve) => {
144 try {
145 + const currentTopModal = modalStack[modalStack.length - 1];
146 + if (currentTopModal) {
147 + currentTopModal.savedScrollSnapshot = captureModalScrollSnapshot(currentTopModal);
148 + }
149 +
150 // Create new modal instance
151 const modal = createModalElement(modalPath);
152 modal.beforeClose = beforeClose;
@@ -271,6 +303,7 @@ export async function closeModal(modalPath = null) {
303 } else {
304 // Update modal z-indexes
305 updateModalZIndexes();
306 + restoreModalScrollSnapshot(modalStack[modalStack.length - 1]);
307 }
308
309 return true;