Preserve tree scroll position

This commit is contained in:
Mikei386
2026-06-22 06:45:45 +02:00
parent d4f47c5bda
commit 91089328ce
7 changed files with 44 additions and 18 deletions
+4 -4
View File
@@ -9,12 +9,12 @@ Tag="URBM Unraid Restic Backup Manager backup snapshots restore"
<?php
$pluginRoot = '/plugins/urbm';
?>
<link rel="stylesheet" href="<?= $pluginRoot ?>/assets/urbm.css?v=20260622r001">
<link rel="stylesheet" href="<?= $pluginRoot ?>/assets/urbm.css?v=20260622r002">
<div id="urbm-app" data-api="<?= $pluginRoot ?>/api.php">
<header class="bu-header">
<div class="bu-brand">
<img class="bu-logo" src="<?= $pluginRoot ?>/images/urbm.png?v=20260622r001" alt="URBM-Logo">
<div><h1>URBM <span class="bu-version">2026.06.22.r001</span></h1><p>Restic Backup Manager für Unraid</p></div>
<img class="bu-logo" src="<?= $pluginRoot ?>/images/urbm.png?v=20260622r002" alt="URBM-Logo">
<div><h1>URBM <span class="bu-version">2026.06.22.r002</span></h1><p>Restic Backup Manager für Unraid</p></div>
</div>
<div id="bu-health" class="bu-health" tabindex="0" data-tooltip="Zeigt, ob die URBM-Hintergrundkomponente erreichbar ist. Beispiel: 'Daemon online' bedeutet, dass Jobs gestartet werden können.">Verbindung wird hergestellt...</div>
</header>
@@ -32,4 +32,4 @@ $pluginRoot = '/plugins/urbm';
<div id="bu-tooltip" role="tooltip" aria-hidden="true"></div>
</div>
<script>window.URBM_CSRF = <?= json_encode($var['csrf_token'] ?? '') ?>;</script>
<script src="<?= $pluginRoot ?>/assets/urbm-2026.06.22.r001.js"></script>
<script src="<?= $pluginRoot ?>/assets/urbm-2026.06.22.r002.js"></script>
+28 -10
View File
@@ -375,6 +375,28 @@
}
}
function captureScrollState(root=document) {
const trees=[...root.querySelectorAll('.bu-tree')].map((tree,index)=>({index,top:tree.scrollTop,left:tree.scrollLeft}));
return {windowX:window.scrollX,windowY:window.scrollY,trees};
}
function restoreScrollState(snapshot, root=document) {
if(!snapshot) return;
const apply=()=>{
const trees=[...root.querySelectorAll('.bu-tree')];
snapshot.trees.forEach(item=>{
const tree=trees[item.index];
if(tree) {
tree.scrollTop=item.top;
tree.scrollLeft=item.left;
}
});
window.scrollTo(snapshot.windowX, snapshot.windowY);
};
apply();
requestAnimationFrame(()=>{ apply(); setTimeout(apply, 0); });
}
function renderDashboard() {
const active = state.runs.filter(r => ['queued','running','paused'].includes(r.status)).length;
const orderedRuns = newestRuns(state.runs);
@@ -517,15 +539,13 @@
function renderSourceBrowser() {
const host=document.getElementById('bu-source-browser');
if(!host) return;
const windowX=window.scrollX, windowY=window.scrollY;
const previousScroll=host.querySelector('.bu-tree')?.scrollTop || 0;
const scrollSnapshot=captureScrollState(host);
const chosen=state.backupSelections.length?state.backupSelections.map(path=>`<span class="bu-selection">${esc(path)}</span>`).join(''):'<span class="bu-muted">Keine Ordner ausgewählt.</span>';
const tree=state.sourceTreeRoots.map(item=>sourceTreeNode(item,0)).join('');
host.innerHTML=`<section class="bu-browser-card"><div class="bu-toolbar"><div><h3>Backup-Quellen auswählen</h3><span class="bu-muted">Ordner mit dem Dreieck aufklappen. Unterordner werden erst bei Bedarf geladen.</span></div><span class="bu-count">Ausgewählt: ${state.backupSelections.length}</span></div><div class="bu-selection-list">${chosen}</div><div class="bu-tree" role="tree">${tree||'<div class="bu-empty">Keine Ordner verfügbar.</div>'}</div></section>`;
const treeElement=host.querySelector('.bu-tree'); if(treeElement) treeElement.scrollTop=previousScroll;
host.querySelectorAll('.bu-source-select[data-partial="true"]').forEach(box=>{box.indeterminate=true;box.setAttribute('aria-checked','mixed');});
enhanceTooltips(host);
window.scrollTo(windowX, windowY);
restoreScrollState(scrollSnapshot, host);
}
async function toggleSourceTree(path, forceOpen=false) {
@@ -569,13 +589,11 @@
function renderDirectoryTree(key) {
const tree=state.directoryTrees[key]; const host=document.getElementById(`bu-dir-tree-${key}`);
if(!tree||!host) return;
const windowX=window.scrollX, windowY=window.scrollY;
const previousScroll=host.querySelector('.bu-tree')?.scrollTop || 0;
const scrollSnapshot=captureScrollState(host);
const html=tree.roots.map(item=>directoryTreeNode(key,item,0)).join('');
host.innerHTML=`<section class="bu-browser-card"><div class="bu-toolbar"><div><h3>${esc(tree.title)}</h3><span class="bu-muted">Ordner mit dem Dreieck aufklappen und den gewünschten Zielordner auswählen.</span></div></div><div class="bu-tree" role="tree">${html||'<div class="bu-empty">Keine Ordner verfügbar.</div>'}</div></section>`;
const treeElement=host.querySelector('.bu-tree'); if(treeElement) treeElement.scrollTop=previousScroll;
enhanceTooltips(host);
window.scrollTo(windowX, windowY);
restoreScrollState(scrollSnapshot, host);
}
async function toggleDirectoryTree(key, path, forceOpen=false) {
@@ -755,9 +773,9 @@
return state.config;
}
function renderPreservingScroll() {
const windowX=window.scrollX, windowY=window.scrollY;
const scrollSnapshot=captureScrollState(content);
render();
window.scrollTo(windowX, windowY);
restoreScrollState(scrollSnapshot, content);
}
const lines = (value) => value.split('\n').map(v=>v.trim()).filter(Boolean);
const runTime = run => new Date(run.createdAt || run.startedAt || run.finishedAt || 0).getTime() || 0;