Skip to content

RG-2818: table auto-horizontal scroll - #9454

Merged
Daniel Kuroski (kuroski) merged 11 commits into
develop-8.0from
kuroski/rg-2818-table-auto-horizontal-scroll
Nov 2, 2026
Merged

Daniel Kuroski (kuroski) merged 11 commits into
develop-8.0from
kuroski/rg-2818-table-auto-horizontal-scroll

Conversation

@kuroski

@kuroski Daniel Kuroski (kuroski) commented Oct 29, 2026 •

Copy link
Copy Markdown

In this PR:

  • Extract and re-use vertical scroll re-order for horizontal scroll
  • Add auto-horizontal scroll when reordering table columns
  • Fix small bug in virtualisation by keeping the dragged item rendered to avoid unmount after it left viewport
demo-auto-scroll.mov

Notes

The auto-scroll logic moved out of ReorderHandle into startDragAutoScroll(axis, table, preferredScroller, onScroll), so rows (y) and columns (x) share the same code.

Differences from the previous row-only implementation:

  • Continuous scroll: a requestAnimationFrame loop scrolls while the pointer stays near an edge
    • Before, scrolling only happened on pointermove (throttled to 100px every 200ms), so holding the pointer still at the edge did nothing
  • Speed depends on the distance to the edge: from 1px to 16px per frame inside an edge area of min(100px, 30% of the visible size)
    • A pointer outside the scroller scrolls at max speed
    • The 30% cap keeps the middle of small scrollers free of auto scroll
  • Scroller detection:
    • Rows (y) use TableProps.scrollerRef when set
    • Columns (x) and rows without scrollerRef use the nearest ancestor that actually scrolls along the axis, falling back to the window
    • scrollerRef is skipped for columns because the vertical virtualization container may not scroll horizontally
  • Stops at the table edge: no scrolling once the table's start/end is already visible
  • behavior: 'instant': smooth scrolling would restart its animation on every frame
  • onScroll callback: called with the last pointer position on every frame, so the handle and insertion indicator stay under a stationary pointer after any scroll, including the user's wheel scroll

Virtualization fix

While dragging a row, the button follows the pointer but the original <tr> stays in place.

When auto scroll moves that row past the retention margin, virtualization collapses it into a spacer and ReorderHandle unmounts, which ends the drag in the middle.

ReorderHandle now sets the data-reordering attribute on the dragged row on pointerdown and removes it on cleanup.
onIntersectionChange() stays the single place that decides whether to collapse: DefaultItemRenderer returns undefined (keep rendered) while the attribute is set.
Custom renderers that call useItemVirtualization() should do the same check.

After release, a cancelled drag can leave the row rendered off-screen until the next intersection change, since IntersectionObserver doesn't fire again.

That's at most one extra row, so it's accepted.
A successful drop changes the indexes, which re-observes the row and collapses it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Cancelling a drag can leave an offscreen row mounted, and auto-scroll skips independently scrollable bodies.

2 open issues
What changed in this PR

Adds horizontal auto-scroll for table column reordering and shares the scrolling logic with row reordering. Also retains dragged rows during virtualization to prevent the drag handle from unmounting offscreen.

Changes:

  • Introduces continuous, edge-sensitive auto-scroll for both axes.
  • Updates drag visuals during scrolling and retains dragged rows until cleanup.
  • Adds horizontal-scroll tests and stories for page and container scrolling.
File Description
src/​table/​table.test.tsx Tests horizontal auto-scroll, drop cleanup, and table-edge stopping.
src/​table/​table.stories.tsx Adds wide-table examples demonstrating auto-scroll.
src/​table/​table.stories.css Styles wide tables and scroll containers.
src/​table/​internal/​virtualization.tsx Adds retention support for dragged rows.
src/​table/​internal/​reorder-handle.tsx Integrates shared auto-scroll, visual updates, and row retention.
src/​table/​internal/​drag-auto-scroll.ts Implements axis-aware scroller detection and continuous edge scrolling.

Review effort: Balanced

Comment thread src/table/internal/drag-auto-scroll.ts Resolved
Comment thread src/table/internal/virtualization.tsx Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Auto-scroll ignores ancestor clipping bounds, and drag cancellation can invoke a stale callback that restores spacers for removed rows after data shrinks.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Medium severity Constrain auto-scroll edges to ancestor clipping bounds

src/​table/​internal/​drag-auto-scroll.ts:42

This only clips the scroller's bounds to the window, not to overflow-clipping ancestors. For example, with an 800px-wide inner scroller clipped to 400px by an overflow: hidden parent, dragging at x=390 produces no auto-scroll: the helper still treats x=800 as the right edge, which the user cannot reach within the visible container. Please intersect the range with ancestor clipping bounds before calculating the edge area.

2 resolved since last review

Review effort: Balanced

Comment thread src/table/internal/virtualization.tsx Resolved

@aleksei-berezkin Aleksei Berezkin (aleksei-berezkin) left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thx for further improving the component. Please take a look on comments I left in drag-auto-scroll.ts and the API ideas in virtualization.tsx (above). Thank you.

Comment thread src/table/internal/drag-auto-scroll.ts Resolved
Comment thread src/table/internal/drag-auto-scroll.ts Resolved
Comment thread src/table/internal/drag-auto-scroll.ts Resolved
Comment thread src/table/internal/drag-auto-scroll.ts Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Column drags can target the wrong horizontal scroll container, and suppressed visibility notifications can leave offscreen rows mounted after drag cancellation.

2 open issues
1 resolved since last review

Review effort: Balanced

Comment thread src/table/internal/reorder-handle.tsx Resolved
Comment thread src/table/internal/reorder-handle.tsx Resolved
Comment thread src/table/table.stories.tsx Resolved
@kuroski
Daniel Kuroski (kuroski) merged commit 50576db into develop-8.0 Nov 2, 2026
3 checks passed
@kuroski
Daniel Kuroski (kuroski) deleted the kuroski/rg-2818-table-auto-horizontal-scroll branch November 2, 2026 08:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants