Skip to content

diagram: Menu anchors Radix to a position:fixed proxy holding a stale getBoundingClientRect snapshot #710

Description

@bpowers

Problem

src/diagram/components/Menu.tsx positions the dropdown by anchoring Radix's DropdownMenu.Trigger to a position: fixed proxy <span> placed at a getBoundingClientRect() snapshot of anchorEl:

const anchorRect = React.useMemo(() => anchorEl?.getBoundingClientRect(), [anchorEl]);
// ...
<DropdownMenu.Trigger asChild>
  <span style={{ position: 'fixed', top: anchorRect?.bottom ?? 0, left: anchorRect?.left ?? 0, width: anchorRect?.width ?? 0, height: 0, pointerEvents: 'none' }} />
</DropdownMenu.Trigger>

The rect is memoized on anchorEl identity (line 29). Radix / floating-ui re-tracks the proxy span itself, but the span holds stale viewport coordinates captured at open time. If the true anchor element moves while the menu is open -- window resize, scroll of a non-fixed scroll container, layout reflow -- the rect is never recomputed (the memo key, anchorEl, is unchanged), so the menu detaches from its trigger and floats at the old coordinates.

Why it matters

Currently low impact: the only consumer (Home's account menu) is short-lived and opened in a stable layout, so the anchor rarely moves while open. But the pattern is structurally fragile -- any future consumer that opens a Menu anchored to an element inside a scrollable or resizable region will see the menu drift away from its trigger.

Component(s) affected

src/diagram/components/Menu.tsx (lines ~29 and ~34-43).

Possible approach

Adopt a real positioning approach: anchor Radix to the actual trigger element rather than a snapshot proxy. Radix supports rendering DropdownMenu.Trigger asChild around the real trigger, or using a virtual-element / anchorRef pattern that lets floating-ui re-measure on scroll/resize. Best fixed as part of converting the anchorEl-prop API (a Material-UI-ism) to Radix's native trigger composition.

How discovered

Identified during UI review of the diagram component library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions