fix(simple-editor): preserve scroll position across loadAll() re-renders

Regression of an earlier fix. Operators reported the editor jumping
to the top of the page on every step save / insert / remove / promote.

Root cause: .o_fp_simple_editor is the overflow:auto scroll
container. loadAll() replaces state.steps with a fresh JSONRPC
payload — OWL tears down the t-foreach and rebuilds every row, which
snaps scrollTop back to 0. Every author action (Save Step, Add
Step, Remove, Promote, Demote, Reorder, Import Template) routes
through loadAll, so the symptom hit everywhere.

Fix: capture scrollTop before the RPC, restore in a double-rAF
after the response settles. rAF (microtask runs before paint in
OWL 2; we need the rebuilt DOM to exist). One choke point fix —
every caller benefits without per-handler changes.

Cheap: a single DOM lookup + an integer save/restore. No XML or
state-shape changes.

Module: fusion_plating 19.0.20.6.0 → 19.0.20.6.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-20 08:13:55 -04:00
parent ac1db177e1
commit afe0fd1206
2 changed files with 30 additions and 1 deletions

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating',
'version': '19.0.20.6.0',
'version': '19.0.20.6.1',
'category': 'Manufacturing/Plating',
'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.',
'description': """

View File

@@ -86,6 +86,20 @@ export class FpSimpleRecipeEditor extends Component {
}
async loadAll() {
// Preserve scroll position across the re-render. .o_fp_simple_editor
// is the overflow:auto scroll container — when `state.steps` is
// replaced with a fresh array, OWL tears down the t-foreach and
// rebuilds every row, which snaps scrollTop back to 0. Operators
// hate this: they save a step half-way down the recipe and the
// page jumps to the top. Capture the position before the RPC,
// restore it after the next paint.
//
// Regression note (2026-05-20): every save/insert/remove/promote
// handler calls loadAll, so this single choke point fixes scroll
// reset for the whole editor.
const scrollRoot = document.querySelector(".o_fp_simple_editor");
const savedScrollTop = scrollRoot ? scrollRoot.scrollTop : 0;
this.state.loading = true;
const [recipeData, libraryData, templateData] = await Promise.all([
rpc("/fp/simple_recipe/load", { recipe_id: this._recipeId }),
@@ -97,6 +111,21 @@ export class FpSimpleRecipeEditor extends Component {
this.state.library = libraryData.templates;
this.state.templateOptions = templateData.templates;
this.state.loading = false;
// Restore AFTER OWL repaints. Microtask runs before paint in OWL 2;
// we need rAF (or two of them, defensively) so the rebuilt DOM
// exists when we set scrollTop. Without this the assignment fires
// against the pre-render DOM and gets discarded.
if (savedScrollTop > 0) {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const el = document.querySelector(".o_fp_simple_editor");
if (el) {
el.scrollTop = savedScrollTop;
}
});
});
}
}
async onSearchLibrary(ev) {