From d1b72ab1a12d47bc3d236037b12b3191a4888b1f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 18 Jun 2026 17:20:59 +0000 Subject: [PATCH] Preserve scroll position when viewing subtasks - Restore window.scrollY after gantt re-renders on desktop layouts where vertical scroll lives on the page, not inside .gscroll - Restore detail modal (.tbox) scroll when refreshing the same task after subtask edits (toggle done, change owner/size, etc.) Co-authored-by: Tanops --- src/app/main.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/app/main.js b/src/app/main.js index c9c30e4..c4a0425 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -326,6 +326,7 @@ function toggleFocus(){ focusToday=!focusToday; const b=$id("gfocusbtn"); if(b)b let GVIEW="proj"; // "proj" | "tasks" | "subs" let ganttScroll={left:0,top:0}; // preserved across re-renders so edits don't jump to top let ganttRestoring=false; +let pageScrollY=0; // window scroll on layouts where .gscroll is horizontal-only function setGView(v){ GVIEW=v; defer(renderGantt); } function setZoom(i){ ZOOM=i; setTimeout(renderAll,0); } // drives the scale window and the gantt zoom function onGanttScroll(){ @@ -343,6 +344,20 @@ function restoreGanttScroll(sc){ requestAnimationFrame(()=>{ apply(); requestAnimationFrame(()=>{ apply(); ganttRestoring=false; }); }); else ganttRestoring=false; } +function restorePageScroll(){ + const y=pageScrollY; + const apply=()=>{ window.scrollTo(0,y); }; + apply(); + if(typeof requestAnimationFrame!=="undefined") + requestAnimationFrame(()=>{ apply(); requestAnimationFrame(apply); }); +} +function restoreDetailScroll(box,top){ + if(!box) return; + const apply=()=>{ box.scrollTop=top; }; + apply(); + if(typeof requestAnimationFrame!=="undefined") + requestAnimationFrame(()=>{ apply(); requestAnimationFrame(apply); }); +} function applyBarDrag(n,mode,s,e,s0,e0){ if(typeof commitBarDrag==="function"){ commitBarDrag(n,mode,s,e,s0,e0); return; } if(mode==="move"){ n.due=dayIso(e); if(n.start) n.start=dayIso(s); } @@ -512,6 +527,7 @@ function renderGantt(){ } const prevSc=document.querySelector(".gscroll"); if(prevSc){ ganttScroll.left=prevSc.scrollLeft; ganttScroll.top=prevSc.scrollTop; } + pageScrollY=window.scrollY||document.documentElement.scrollTop||0; document.getElementById("gantt").innerHTML= `
`+ rows.join("")+ @@ -520,6 +536,7 @@ function renderGantt(){ const sc=document.querySelector(".gscroll"); sc.addEventListener("scroll",onGanttScroll,{passive:true}); restoreGanttScroll(sc); + restorePageScroll(); const gpane=document.querySelector(".gantt"); if(gpane&&!gpane._floatBound){ gpane._floatBound=true; gpane.addEventListener("scroll",placeFloat,{passive:true}); } pinFlags(); placeFloat(); placeOverflowTitles(); @@ -904,6 +921,9 @@ let DETAIL_ID=null; function openDetail(id){ const path=findPath(id); if(!path){ if(DETAIL_ID===id) closeSheet(); DETAIL_ID=null; return; } + const box=document.querySelector(".tbox"); + const keepScroll=DETAIL_ID===id; + const savedScroll=keepScroll&&box?box.scrollTop:0; DETAIL_ID=id; const n=path[path.length-1], leaf=!n.children.length; document.getElementById("dCrumb").innerHTML=path.length>1 @@ -950,6 +970,7 @@ function openDetail(id){ `; document.getElementById("tmodal").classList.add("show"); document.getElementById("scrim").classList.add("show"); + restoreDetailScroll(box,savedScroll); } function closeSheet(){ DETAIL_ID=null; document.getElementById("tmodal").classList.remove("show");