import { useLayoutEffect, useRef, useState, type CSSProperties } from 'react' // A dropdown that hangs off its own button in viewport coordinates. // // The tone and export menus used to be absolutely positioned inside their // button's wrapper, which was fine until that wrapper became ChromeStrip — a // horizontal scroller, and so a box that clips what overflows it. An absolute // menu inside it is 36px tall and scrolls away with the pills. Positioning the // menu against the viewport instead takes it out of the strip's hands entirely. // // Or rather: it does once the menu is also *portalled out* of it, which is the // part this originally got wrong. `position: fixed` is only relative to the // viewport while no ancestor establishes a containing block for it — and a // `mask-image` does, exactly like a transform. Both scrollers fade their edges // with a mask (that is how each says "there is more this way"), so on any screen // narrow enough for the fade to appear — i.e. every phone — the menu was pulled // back inside the very box it was trying to escape: painted underneath the // toolbar, and untappable. It looked open and did nothing. // // So the panel is rendered through a portal into . Nothing above it can // clip it, stack over it, or contain it, whatever the chrome does with masks // later. `panelRef` is returned for the outside-tap test, which can no longer // rely on the panel being a DOM descendant of the trigger's wrapper. // // The trade is that a fixed box doesn't follow its anchor, so anything that // moves the button — the page scrolling under it, the strip scrolling, the // window resizing — has to re-place the menu. // // The element type is a parameter because the two kinds of caller anchor // against different things: the tone and export pills hand it their own //