Skip to content

refactor(graph): move the viewport translation members to PanningMixin - #1132

Open
redfish4ktc wants to merge 2 commits into
mainfrom
refactor/move_viewport_translation_to_panning_mixin
Open

refactor(graph): move the viewport translation members to PanningMixin#1132
redfish4ktc wants to merge 2 commits into
mainfrom
refactor/move_viewport_translation_to_panning_mixin

Conversation

@redfish4ktc

@redfish4ktc redfish4ktc commented Aug 10, 2026

Copy link
Copy Markdown
Member

Note

Stacked on top of #1131. The base of this PR is refactor/group_and_test_per_instance_properties, so the diff shown here contains only this step. Merge #1131 first, or let GitHub merge the stack.

Second implementation step of the plan recorded in ADR 0003 (#1130). No behavior change, and the public API is unchanged.

Problem

AbstractGraph still holds members that belong to the panning concern. They all move the viewport without touching the scale, which is exactly what PanningMixin already does with scrollCellToVisible and scrollRectToVisible.

PanningMixin.type.ts even documents scrollPointToVisible as if the method already lived there, in the timerAutoScroll and allowAutoPanning descriptions.

Changes

Moved into PanningMixin:

Member Why it belongs there
scrollPointToVisible Reads isTimerAutoScroll, isAllowAutoPanning, getPanDx, getPanDy and the PanningHandler plugin, all owned by PanningMixin
ignoreScrollbars, isIgnoreScrollbars Only consumers are EventsMixin and scrollPointToVisible
translateToScrollPosition, isTranslateToScrollPosition Same
center See below

AbstractGraph.ts loses 156 lines.

Why center lands here and not in FitPlugin

FitPlugin looks like the obvious target since it owns fit and fitCenter. It is the wrong one, and the name is what makes it look right.

  • center never touches view.scale. It only calls view.setTranslate and sets container.scrollLeft / scrollTop. Scale is the dividing line in this codebase: ZoomMixin and FitPlugin own scale, PanningMixin owns translate and scroll offset. FitPlugin.fit computes a new scale and fitCenter applies view.scaleAndTranslate.
  • It is built exactly like scrollRectToVisible, branching on hasScrollbars(container) and then handling two worlds: no scrollbars means adjust view.translate, scrollbars means adjust the scroll position. FitPlugin never reads or writes scrollLeft / scrollTop at all.
  • fitCenter and center answer different questions. fitCenter centers as a consequence of fitting, deriving the translation from the newly computed scale. center centers at the current scale.

One nuance for consumers

isIgnoreScrollbars and isTranslateToScrollPosition were arrow-function class properties, so they were bound to their instance and could be detached from the graph. They are now prototype methods, like every other mixin member.

graph.isIgnoreScrollbars() is unaffected. Only detached references break, for instance someArray.some(graph.isIgnoreScrollbars) or const f = graph.isIgnoreScrollbars, which now need an explicit bind.

This is recorded in the changelog under Breaking Changes rather than as a passing note, because TypeScript gives both forms the same type: a detached reference still compiles and fails only at runtime.

Validation

Run locally on Node 24 (.nvmrc): build, test-check, the full suite (505 tests, 60 suites), lint, check:circular-dependencies. All passing, and no test needed changing, which is the expected signal for a pure relocation.

Summary by CodeRabbit

  • New Features

    • Enhanced graph panning with options to ignore scrollbars and translate directly to scroll positions.
    • Added support for centering graphs and bringing specific points into view, with optional borders and directional controls.
  • Breaking Changes

    • Panning and centering controls are now provided through the graph’s panning capabilities.
    • Detached references to certain graph methods may require explicit binding or wrapping.

@redfish4ktc redfish4ktc added the refactor Code refactoring label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The change moves scrollbar-aware state and panning methods into PanningMixin. It adds declarations for these APIs and documents the loss of automatic binding for detached method references.

Changes

Scrollbar-aware panning

Layer / File(s) Summary
Panning contracts and configuration
packages/core/src/view/mixin/PanningMixin.ts, packages/core/src/view/mixin/PanningMixin.type.ts
The panning contracts define scrollbar settings, accessors, scrollPointToVisible, and center.
Scrollbar-aware panning implementation
packages/core/src/view/mixin/PanningMixin.ts, packages/core/src/view/AbstractGraph.ts, CHANGELOG.md
PanningMixin implements scrollbar-aware scrolling and centering with disabled-by-default settings. The unused import is removed, and the breaking method-binding behavior is documented.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: tbouffard

Sequence Diagram(s)

sequenceDiagram
  participant Graph
  participant PanningMixin
  participant PanningManager
  participant Container
  Graph->>PanningMixin: scrollPointToVisible(x, y)
  PanningMixin->>Container: adjust scroll offsets or clipping dimensions
  PanningMixin->>PanningManager: delegate automatic panning when enabled
  Graph->>PanningMixin: center(horizontal, vertical)
  PanningMixin->>Container: center scroll positions when scrollbars are present
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main refactor and follows Conventional Commits syntax.
Description check ✅ Passed The description explains the problem, design rationale, breaking change, scope, and validation results in sufficient detail.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@redfish4ktc
redfish4ktc force-pushed the refactor/move_viewport_translation_to_panning_mixin branch from 0244662 to 26dd9b8 Compare August 11, 2026 13:31
@tbouffard
tbouffard marked this pull request as draft August 11, 2026 13:49
@tbouffard
tbouffard marked this pull request as ready for review August 12, 2026 04:20
@redfish4ktc
redfish4ktc force-pushed the refactor/move_viewport_translation_to_panning_mixin branch from 26dd9b8 to f428118 Compare August 12, 2026 04:45
Base automatically changed from refactor/group_and_test_per_instance_properties to main August 12, 2026 04:50
AbstractGraph still held members that belong to the panning concern and have no reason to sit in the class. They all
adjust the viewport without touching the scale, which is what PanningMixin already does with scrollCellToVisible and
scrollRectToVisible.

Move scrollPointToVisible, center, and the ignoreScrollbars and translateToScrollPosition flags with their getters.
scrollPointToVisible reads isTimerAutoScroll, isAllowAutoPanning, getPanDx, getPanDy and the PanningHandler plugin,
which PanningMixin owns entirely, and PanningMixin.type.ts already referenced the method as if it lived there.

center is included on purpose although FitPlugin looks like the obvious target. It never touches view.scale, it only
updates view.translate and the container scroll position, and it branches on hasScrollbars exactly like
scrollRectToVisible. Scale is the dividing line in this codebase, ZoomMixin and FitPlugin own it while PanningMixin owns
translate and scroll offset, so center belongs here. The name is what makes FitPlugin look right: fitCenter centers as a
consequence of fitting, deriving the translation from a newly computed scale, whereas center works at the current scale.

The public API is unchanged, mixin members are merged into the AbstractGraph interface by declaration merging.

One nuance for consumers: isIgnoreScrollbars and isTranslateToScrollPosition were arrow-function class properties, so
they were bound to their instance and could be detached. They are now prototype methods, like every other mixin member,
so `const f = graph.isIgnoreScrollbars; f()` no longer works and needs a call on the graph, or an explicit bind.
isIgnoreScrollbars and isTranslateToScrollPosition were arrow function properties, so they were bound to their instance
and could be detached from the graph. As mixin members they are now prototype methods like every other one.

This deserves the breaking changes list rather than a passing mention: TypeScript gives both forms the same type, so a
detached reference still compiles and only fails at runtime.
@tbouffard
tbouffard force-pushed the refactor/move_viewport_translation_to_panning_mixin branch from f428118 to 70b6717 Compare August 12, 2026 04:50
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8ff00a8a-464f-455b-8e91-9e5ca1ab323b

📥 Commits

Reviewing files that changed from the base of the PR and between 34a0d3c and 70b6717.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • packages/core/src/view/AbstractGraph.ts
  • packages/core/src/view/mixin/PanningMixin.ts
  • packages/core/src/view/mixin/PanningMixin.type.ts

Comment on lines +106 to +122
/**
* Scrolls the graph to the given point, extending the graph container if specified.
*
* If the container has no scrollbars and {@link isAllowAutoPanning} returns `true`, the graph is panned through the
* {@link PanningHandler} plugin instead of being scrolled.
*
* @param x horizontal coordinate to make visible.
* @param y vertical coordinate to make visible.
* @param extend Optional boolean that specifies if the graph container should be extended. Default is `false`.
* @param border Optional distance in pixels to keep between the point and the container edge. Default is `20`.
*/
scrollPointToVisible: (
x: number,
y: number,
extend?: boolean,
border?: number
) => void;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the ignoreScrollbars exception.

When isIgnoreScrollbars() is true, scrollPointToVisible uses container scrolling even if the container has no scrollbars. Line 109 states unconditionally that this case delegates to PanningHandler. Qualify the fallback condition so the public API documentation matches PanningMixin.scrollPointToVisible.

Proposed documentation change
- * If the container has no scrollbars and {`@link` isAllowAutoPanning} returns `true`, the graph is panned through the
+ * If {`@link` isIgnoreScrollbars} returns `false`, the container has no scrollbars, and {`@link` isAllowAutoPanning}
+ * returns `true`, the graph is panned through the
  * {`@link` PanningHandler} plugin instead of being scrolled.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Scrolls the graph to the given point, extending the graph container if specified.
*
* If the container has no scrollbars and {@link isAllowAutoPanning} returns `true`, the graph is panned through the
* {@link PanningHandler} plugin instead of being scrolled.
*
* @param x horizontal coordinate to make visible.
* @param y vertical coordinate to make visible.
* @param extend Optional boolean that specifies if the graph container should be extended. Default is `false`.
* @param border Optional distance in pixels to keep between the point and the container edge. Default is `20`.
*/
scrollPointToVisible: (
x: number,
y: number,
extend?: boolean,
border?: number
) => void;
/**
* Scrolls the graph to the given point, extending the graph container if specified.
*
* If {@link isIgnoreScrollbars} returns `false`, the container has no scrollbars, and {@link isAllowAutoPanning}
* returns `true`, the graph is panned through the {@link PanningHandler} plugin instead of being scrolled.
*
* @param x horizontal coordinate to make visible.
* @param y vertical coordinate to make visible.
* @param extend Optional boolean that specifies if the graph container should be extended. Default is `false`.
* @param border Optional distance in pixels to keep between the point and the container edge. Default is `20`.
*/
scrollPointToVisible: (
x: number,
y: number,
extend?: boolean,
border?: number
) => void;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Code refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant