[Web] Activate ScrollView's native gesture on real scroll instead of pointer distance - #4420
[Web] Activate ScrollView's native gesture on real scroll instead of pointer distance#4420m-bert wants to merge 9 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: Your plan includes up to 8 reviews per rolling hour; 4 remain after this review. 📝 WalkthroughSummary by CodeRabbit
WalkthroughWeb native view gestures now receive scroll events. Scrollable handlers activate only after scrolling and pointer travel. Non-scrollable and role-less handlers retain distance-based activation. Tests cover the new behavior with a fake DOM. ChangesWeb scroll activation
Sequence Diagram(s)sequenceDiagram
participant Pointer
participant GestureHandlerWebDelegate
participant ScrollEventManager
participant NativeViewGestureHandler
Pointer->>NativeViewGestureHandler: begin touch
GestureHandlerWebDelegate->>ScrollEventManager: register scroll listener
ScrollEventManager->>NativeViewGestureHandler: forward adapted scroll event
Pointer->>NativeViewGestureHandler: move pointer
NativeViewGestureHandler->>NativeViewGestureHandler: activate after scroll and travel threshold
Merge Risk: ⚪ Minimal · up to This PR changes web ScrollView gesture activation to follow actual scrolling, preventing scroll gestures from incorrectly claiming unrelated drags. No actionable merge-blocking risk remains after normal checks and review. 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
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. Comment |
There was a problem hiding this comment.
Pull request overview
This PR updates the web implementation of NativeViewGestureHandler (v3 / hook-based API path) so ScrollView-role handlers activate based on actual DOM scroll events rather than pointer-distance slop, preventing scroll views from incorrectly claiming cross-axis drags and blocking delayed-activation pans inside scrollable containers.
Changes:
- Add
ScrollEventManagerand plumb a newonScrollcallback throughEventManager→GestureHandler. - Update
NativeViewGestureHandlerto use scroll-driven activation for theScrollViewrole with a small pointer-travel momentum guard. - Add Jest unit tests validating scroll-driven activation behavior and ensuring legacy/distance-based activation remains unchanged.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/react-native-gesture-handler/src/web/tools/ScrollEventManager.ts | New event manager that listens to DOM scroll and forwards it into the gesture pipeline. |
| packages/react-native-gesture-handler/src/web/tools/GestureHandlerWebDelegate.ts | Registers ScrollEventManager alongside existing web event managers. |
| packages/react-native-gesture-handler/src/web/tools/EventManager.ts | Adds onScroll callback plumbing to the shared event-manager abstraction. |
| packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts | Wires onScroll into handler attachment and provides a default no-op implementation. |
| packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts | Switches ScrollView-role activation to be scroll-driven (v3-only role path). |
| packages/react-native-gesture-handler/src/tests/webNativeViewGestureHandler.test.ts | New unit tests for scroll-driven activation, momentum guard behavior, and legacy path. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // Synthesize a pointer glued to the scrolled content - deltas between | ||
| // events equal the scrolled distance. getBoundingClientRect would only | ||
| // add a constant offset at the cost of a layout read on every event. |
There was a problem hiding this comment.
Doesn't the DOM event contain the layout info by chance?
There was a problem hiding this comment.
Unfortunately, scroll event is a generic event, so it doesn't have this info (source)
| } | ||
|
|
||
| this.scrollDetected = false; | ||
| this.isScrollDriven = this.computeIsScrollDriven(); |
There was a problem hiding this comment.
Why recalculate this on every pointer down? It doesn't change until the gesture is reattached to a different view.
There was a problem hiding this comment.
| handler.pointerDown(touchEvent(100, 100, EventTypes.DOWN)); | ||
| handler.pointerMove(touchEvent(100, 130, EventTypes.MOVE)); | ||
|
|
||
| view.dispatchEvent('scroll'); |
There was a problem hiding this comment.
Won't the browser dispatch pointercancel around that time, since it's taking over? That would mean the gesture gets canceled, no?
There was a problem hiding this comment.
It does - pointercancel maps to cancel(), and the scroll events that follow are ignored since the tracker is empty. That's the same outcome as before this PR (the browser takes over below the 15px slop). However, the spec doesn't define the ordering between pointercancel and scroll, so the handler must also handle scroll arriving while the pointer is still tracked - that's the case this test covers.
Description
On web,
NativeViewGestureHandleractivated after ~15pxof pointer movement in any direction, even though the browser does the scrolling itself. A verticalScrollViewwould claim horizontal drags, and once active,InteractionManagerfailed anyPanwhose activation criteria (minDistance,activeOffsetX, ...) delayed activation past the slop - such pans could never activate inside aScrollVieworFlatList.This PR adds
ScrollEventManagerwhich delivers the view'sscrollevents to handlers via a newonScrollhook. Handlers with theScrollViewrole now activate only when the view actually scrolls, with a2pxpointer travel requirement that ignores momentum-scroll ticks after a touch meant to stop a fling.Important
This covers only new, hook based API
Test plan
Tested on the following code: