Skip to content

fix: render grab controls in the resolved owner's window - #15

Merged
V3RON merged 1 commit into
mainfrom
fix/controls-in-owner-window
Aug 31, 2026
Merged

fix: render grab controls in the resolved owner's window#15
V3RON merged 1 commit into
mainfrom
fix/controls-in-owner-window

Conversation

@V3RON

@V3RON V3RON commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #14.

The problem

The control bar was always rendered by ReactNativeGrabRoot, as a sibling of the root selection owner, wrapped in FullScreenOverlay. That wrapper is FullWindowOverlay on iOS and Fragment everywhere else, so on Android the bar was just an absolutely positioned View inside the main window's React tree. A sheet is presented in its own window, which stacks above the activity's content view regardless of any zIndex inside it, so the bar was painted underneath it and there was no way to start a selection in the sheet from the controls. The same applied on iOS whenever react-native-screens is not installed and the require falls through to Fragment.

The change

The controls now belong to whichever owner resolves selection, so they render in the same native window as the content they act on. GrabSelectionOwnerView mounts them for the resolved owner and nothing else, which means a surface that owns selection hosts its own control bar inside the sheet's window.

Three details came with it:

  • The dev-menu registration stays at the root. It has to outlive selection moving between owners, otherwise the menu item is added and removed as ownership changes. ReactNativeGrabRoot now calls useDevMenu(toggleGrabMenu) directly and grab-root-controls.tsx is replaced by grab-controls.tsx.
  • Placement comes from the container's measured size, not Dimensions.get("window"). Whenever a surface owns selection that container is a sheet rather than the whole screen, so the initial position and the drag clamping are derived from onLayout and re-clamped when the container resizes. The measured node sits inside FullScreenOverlay, so on iOS it is still window-sized and placement is unchanged there.
  • The controls are anchored absolutely inside the owner. An owner is free to be a gap-spaced flex container — the sheet playground's surfaces are — and absolutely positioned children are not flex items, so mounting the controls cannot disturb the owner's layout or the height an auto detent measures.

FullScreenOverlay is deliberately kept. On iOS the bar continues to float above every window through FullWindowOverlay, so that platform's behaviour is unchanged; Android simply gets a bar that is in the right window to begin with.

Verification

npm run lint, npm run format:check, npm run build, and the 15 unit tests pass.

Not yet verified on device. This is a UI change and it needs a pass on both platforms before merging: on Android that the bar shows over a presented TrueSheet and that Select there starts a session in the sheet, and on iOS that placement, dragging, and the sheet playground's auto detent heights are unchanged.

@V3RON
V3RON merged commit 502b064 into main Aug 31, 2026
4 checks passed
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.

Android: Grab controls are hidden behind natively presented sheets

1 participant