Skip to content

Commit ab43bdb

Browse files
committed
plan: stabilize background refresh to prevent row-span corruption and viewport jump
1 parent b0af4ac commit ab43bdb

1 file changed

Lines changed: 41 additions & 8 deletions

File tree

static/plan.js

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,8 @@
655655
rowIndex,
656656
renderedRows,
657657
nextRow ? nextRow.date : addDaysIso(row.date, 1),
658+
undefined,
659+
{ preserveFocus: true },
658660
);
659661
appliedCount += 1;
660662
}
@@ -798,17 +800,49 @@
798800
refreshQueuedAfterFlight = true;
799801
return;
800802
}
801-
if (!isIsoDateString(loadedEndDate)) return;
802-
const startDate = isIsoDateString(refreshFromDate) ? refreshFromDate : loadedStartDate;
803+
if (!isIsoDateString(loadedStartDate) || !isIsoDateString(loadedEndDate)) return;
804+
const activeEl = document.activeElement;
805+
const activeIsInput = activeEl instanceof HTMLInputElement || activeEl instanceof HTMLSelectElement;
806+
const activeClassName = activeIsInput ? String(activeEl.className || "") : "";
807+
const activeDate = activeIsInput && activeEl.dataset ? String(activeEl.dataset.date || "") : "";
808+
const activeSessionIndex = activeIsInput && activeEl.dataset
809+
? String(activeEl.dataset.sessionIndex || "0")
810+
: "0";
811+
const restoreSelector = (
812+
activeIsInput
813+
&& isIsoDateString(activeDate)
814+
&& (
815+
activeClassName.includes("plan-session-distance")
816+
|| activeClassName.includes("plan-session-type")
817+
|| activeClassName.includes("plan-session-workout")
818+
)
819+
)
820+
? `.${activeClassName.split(/\s+/).find((name) => name.startsWith("plan-session-"))}[data-date="${activeDate}"][data-session-index="${activeSessionIndex}"]`
821+
: "";
822+
const prevScrollTop = tableWrapEl ? tableWrapEl.scrollTop : 0;
823+
const prevScrollLeft = tableWrapEl ? tableWrapEl.scrollLeft : 0;
803824
refreshFromDate = "";
804825
refreshInFlight = true;
805826
try {
806827
await loadPlanRange({
807-
startDate,
828+
startDate: loadedStartDate,
808829
endDate: loadedEndDate,
809830
centerDateOverride: centerDateEl.value,
810-
append: true,
831+
append: false,
811832
});
833+
if (tableWrapEl) {
834+
tableWrapEl.scrollTop = prevScrollTop;
835+
tableWrapEl.scrollLeft = prevScrollLeft;
836+
}
837+
if (restoreSelector) {
838+
const restoreTarget = bodyEl.querySelector(restoreSelector);
839+
if (restoreTarget instanceof HTMLElement) {
840+
restoreTarget.focus();
841+
if (restoreTarget instanceof HTMLInputElement && typeof restoreTarget.select === "function") {
842+
restoreTarget.select();
843+
}
844+
}
845+
}
812846
} finally {
813847
refreshInFlight = false;
814848
if (refreshQueuedAfterFlight || isIsoDateString(refreshFromDate)) {
@@ -1042,15 +1076,14 @@
10421076
&& batchMaxNextFocusDate > loadedEndDate
10431077
);
10441078
if (needsAppendFuture) {
1045-
const anchorDate = isIsoDateString(minSavedDate) ? minSavedDate : loadedEndDate;
1046-
const appendStart = overlapStartForDate(anchorDate, loadedStartDate);
10471079
const appendTarget = addDaysIso(loadedEndDate, PLAN_APPEND_FUTURE_DAYS);
10481080
const appendEnd = batchMaxNextFocusDate > appendTarget ? batchMaxNextFocusDate : appendTarget;
1081+
const fullStart = isIsoDateString(loadedStartDate) ? loadedStartDate : overlapStartForDate(minSavedDate, "");
10491082
await loadPlanRange({
1050-
startDate: appendStart,
1083+
startDate: fullStart,
10511084
endDate: appendEnd,
10521085
centerDateOverride: centerDateEl.value,
1053-
append: true,
1086+
append: false,
10541087
});
10551088
} else if (
10561089
isIsoDateString(minSavedDate)

0 commit comments

Comments
 (0)