LearnNewsExamplesServices
Frontmatter
id12757
titlegrid.View owns body-scroll orchestration (syncBodies relocation from Container)
stateClosed
labels
enhancementairefactoringarchitecturegrid
assigneesneo-opus-grace
createdAtJun 8, 2026, 1:05 PM
updatedAtJun 8, 2026, 1:34 PM
githubUrlhttps://github.com/neomjs/neo/issues/12757
authorneo-opus-grace
commentsCount0
parentIssue9872
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 8, 2026, 1:34 PM

grid.View owns body-scroll orchestration (syncBodies relocation from Container)

Closed v13.0.0/archive-v13-0-0-chunk-17 enhancementairefactoringarchitecturegrid
neo-opus-grace
neo-opus-grace commented on Jun 8, 2026, 1:05 PM

Context

A safe, standalone slice of #9872's "View as master body orchestrator" tier, extracted from PR #12754 (which was originally scoped wider — it also attempted the SelectionModel rework; that half was reverted and re-homed in the grid.View-owned SM migration follow-up after a REQUEST_CHANGES review). This leaf is the part that is behavior-preserving + E2E-verified safe.

The Problem

Body scroll-synchronization (syncBodies) lived on Neo.grid.Container, conflating the macro-routing layer with body orchestration. #9872 calls for grid.View to own the 1-3 bodies' lifecycle and row-synchronization. Without the back-reference, View also could not reach the macro layer (column distribution, ScrollManager) it does not own.

The Architectural Reality

  • Container.syncBodies(scrollTop) pushed synchronized scroll coords (startIndex/scrollLeft/scrollTop) into body/bodyStart/bodyEnd and then bumped view.update().
  • grid.View is the body-wrapper (hbox of the 1-3 bodies) but had no reference back to the Container macro layer.
  • Split bodies created in Container.onColumnsMutatecreateOrUpdateSubGrids() were not covered by the resize-driven measurement, so their containerWidth/columnPositions could stay unset and Body.createViewData short-circuit to an empty body.

The Fix

  • Move syncBodies to grid.View (reads gridContainer.scrollManager for horizontal position); Container.syncBodies delegates to this.view.syncBodies.
  • Add View.gridContainer back-reference (set at Container View-construction).
  • Split-body initial render: in Container.onColumnsMutate, after createOrUpdateSubGrids(), passSizeToBody() then updateMountedAndVisibleColumns() on each present body.
  • Preserves the row/cell pooling invariant (silent createViewData(true)).

Acceptance Criteria

  • syncBodies owned by grid.View; Container delegates.
  • View.gridContainer back-reference present.
  • Vertical + horizontal scroll-sync preserved — test/playwright/e2e/GridThumbDrag.spec.mjs green.
  • Multi-body render intact — GridSelectionMultiBody.spec.mjs cross-body selection test green.

Out of Scope

  • SelectionModel ownership / per-body model elimination → the grid.View-owned single SelectionModel follow-up.
  • Container macro-routing strip + grid.header.Wrapper orchestrator → remainder of #9872.

Related

Decision Record impact

aligned-with #9872 (delivers its View-orchestrator tier incrementally).

Origin Session ID: 3f9ffeb7-a284-4783-912b-f39912115d2e

tobiu referenced in commit 96da8a9 - "refactor(grid): grid.View owns body-scroll orchestration (#12757) (#12754) on Jun 8, 2026, 1:34 PM
tobiu closed this issue on Jun 8, 2026, 1:34 PM