diff --git a/dist/urbm-2026.09.27.r003-x86_64-1.txz b/dist/urbm-2026.09.27.r003-x86_64-1.txz new file mode 100644 index 0000000..dbbb544 Binary files /dev/null and b/dist/urbm-2026.09.27.r003-x86_64-1.txz differ diff --git a/dist/urbm-2026.09.27.r003-x86_64-1.txz.sha256 b/dist/urbm-2026.09.27.r003-x86_64-1.txz.sha256 new file mode 100644 index 0000000..7f6abdc --- /dev/null +++ b/dist/urbm-2026.09.27.r003-x86_64-1.txz.sha256 @@ -0,0 +1 @@ +4f5ea23abab9a154aaebe8af9a0ed286ae645deb7ab47d5e495b6b343b841b71 urbm-2026.09.27.r003-x86_64-1.txz diff --git a/dist/urbm.plg b/dist/urbm.plg index 285a314..0813fa9 100644 --- a/dist/urbm.plg +++ b/dist/urbm.plg @@ -2,13 +2,17 @@ - + - + ]> +### 2026.09.27.r003 +- Close action menus synchronously before opening edit forms or running menu actions. +- Prevent delayed menu positioning from reopening closed or detached menus. + ### 2026.09.27.r002 - Collapse run logs by default and retain manually opened logs during refresh. - Log Rsync sources, destination, comparison and deletion options, execution phases, sample changes, errors and final totals. diff --git a/plugin/urbm.plg b/plugin/urbm.plg index 54b927f..b684c3b 100644 --- a/plugin/urbm.plg +++ b/plugin/urbm.plg @@ -2,13 +2,17 @@ - + ]> +### 2026.09.27.r003 +- Close action menus synchronously before opening edit forms or running menu actions. +- Prevent delayed menu positioning from reopening closed or detached menus. + ### 2026.09.27.r002 - Collapse run logs by default and retain manually opened logs during refresh. - Log Rsync sources, destination, comparison and deletion options, execution phases, sample changes, errors and final totals. diff --git a/webgui/URBM.page b/webgui/URBM.page index 960c3ec..37c244e 100644 --- a/webgui/URBM.page +++ b/webgui/URBM.page @@ -8,7 +8,7 @@ Tag="URBM Unraid Restic Backup Manager backup snapshots restore" --- diff --git a/webgui/assets/urbm.js b/webgui/assets/urbm.js index 5117d6c..f6081f6 100644 --- a/webgui/assets/urbm.js +++ b/webgui/assets/urbm.js @@ -431,6 +431,7 @@ function render() { const views = {dashboard:renderDashboard, jobs:renderJobs, repositories:renderRepositories, snapshots:renderSnapshots, restore:renderRestore, activity:renderActivity, logs:renderLogs, settings:renderSettings}; + document.querySelectorAll('.bu-action-menu[open]').forEach(closeActionMenu); menuPortal?.replaceChildren(); content.innerHTML = views[state.view](); enhanceTooltips(content); @@ -994,13 +995,26 @@ } document.querySelector('.bu-tabs').addEventListener('click', e => { const button=e.target.closest('[data-view]'); if(!button)return; state.view=button.dataset.view; document.querySelectorAll('.bu-tabs button').forEach(x=>x.classList.toggle('active',x===button)); render(); if(state.view==='logs'&&!state.logs&&!state.logsLoading)loadLogs(); }); + function closeActionMenu(menu) { + if(!menu) return; + menu.removeAttribute('open'); + menu.closest('tr')?.classList.remove('bu-menu-open'); + const popover=menu._popover; + if(popover) { + popover.removeAttribute('style'); + popover.classList.remove('portaled'); + delete popover._ownerMenu; + menu.appendChild(popover); + delete menu._popover; + } + } function handleActionClick(e) { const target=e.target.closest('[data-action]'); if(!target) return; e.preventDefault(); const popover=target.closest('.bu-action-menu-popover'); const menu=target.closest('.bu-action-menu')||popover?._ownerMenu; - menu?.removeAttribute('open'); + closeActionMenu(menu); handle(target.dataset.action,target); } content.addEventListener('click', handleActionClick); @@ -1015,7 +1029,7 @@ document.addEventListener('click', e=>{ document.querySelectorAll('.bu-action-menu[open]').forEach(menu=>{ const popover=menu._popover; - if(!menu.contains(e.target)&&!popover?.contains(e.target)) menu.removeAttribute('open'); + if(!menu.contains(e.target)&&!popover?.contains(e.target)) closeActionMenu(menu); }); }); content.addEventListener('toggle', e=>{ @@ -1024,18 +1038,12 @@ const row=menu.closest('tr'); row?.classList.toggle('bu-menu-open',menu.open); if(!menu.open) { - const popover=menu._popover; - if(popover) { - popover.removeAttribute('style'); - popover.classList.remove('portaled'); - delete popover._ownerMenu; - menu.appendChild(popover); - delete menu._popover; - } + closeActionMenu(menu); return; } - content.querySelectorAll('.bu-action-menu[open]').forEach(other=>{ if(other!==menu) other.removeAttribute('open'); }); + content.querySelectorAll('.bu-action-menu[open]').forEach(other=>{ if(other!==menu) closeActionMenu(other); }); requestAnimationFrame(()=>{ + if(!menu.open||!menu.isConnected) return; const summary=menu.querySelector('summary'); const popover=menu.querySelector('.bu-action-menu-popover'); if(!summary||!popover||!menuPortal) return; @@ -1056,7 +1064,7 @@ Object.assign(popover.style,{left:`${left}px`,right:'auto',top:`${top}px`,bottom:'auto',width:`${width}px`,maxHeight:`${height}px`}); }); },true); - window.addEventListener('resize',()=>document.querySelectorAll('.bu-action-menu[open]').forEach(menu=>menu.removeAttribute('open'))); + window.addEventListener('resize',()=>document.querySelectorAll('.bu-action-menu[open]').forEach(closeActionMenu)); content.addEventListener('change', e => { if(e.target.id==='bu-dashboard-job') { state.dashboardJobId=e.target.value;