Skip to content

Context popup keeps its position when the selection it anchors to changes #171

Description

@Azganoth

Summary

The context popup measures its anchor live but nothing re-measures it when the selection changes, so a popup stays where it opened while the selection it acts on grows, shrinks, or moves under it. Scrolling still moves it, because scroll is what triggers a recompute.

Steps to reproduce

  1. Select a word with the pointer so the popup opens below it.
  2. Extend the selection to the lines below with Shift+Down, or sideways with Shift+Right.
  3. For contrast, scroll the document surface with the popup open.

Expected behavior

The popup follows the selection it acts on, staying against the visible part of it as the selection changes.

Must remain stable: a popup holding focus stays pinned where it was, a popup whose selection scrolls out of view is hidden and returns, and the dismissal rules are unaffected.

Actual behavior

Steps 1 and 2 leave the popup where it opened, pointing at text the selection has moved away from. Step 3 moves it correctly.

Related context

Done when

  • Extending or moving a selection with the popup open repositions it against the selection.
  • A popup holding focus stays pinned and is not moved out from under the user.
  • docs/specification.md — the popup follows the selection it acts on, not only the scroll.
  • Regression coverage that a fresh request re-measures the anchor.

Notes, logs, screenshots

Diagnosis

PopperAnchor registers the reference only when the object inside virtualRef changes identity — its effect compares previousAnchor !== anchorRef.current — and EditorContextPopup.tsx creates that object once, deliberately, so registration happens once. From then on floating-ui's autoUpdate recomputes only on ancestor scroll and resize, and a selection change is neither.

The plugin already issues a fresh request on every selection change and the anchor's getRect is live, so the measurement is current; nothing asks for it.

Implementation direction

Two routes the installed Radix supports:

  • Give the virtual anchor a new object identity whenever the request changes. One recompute per selection change, nothing while idle, and it keeps the no-per-frame-work property the virtual anchor was chosen for. The existing "created once" constraint guards against a new object per render, which is not the same thing.
  • Set updatePositionStrategy="always" on PopoverContent, which passes animationFrame: true to autoUpdate. One line, at the cost of re-measuring the selection every frame while the popup is open.

Either way the focus-holding pin must keep winning, so a popup the user is working in does not move.

Out of scope

Metadata

Metadata

Assignees

Labels

BugSomething isn't working

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions