AddToPlaylist, LanguageSwitcher, the Plex DetailCustomizeMenu and ViewSwitcher each
had their own anchoring, dismissal and (for ViewSwitcher) keyboard engine. All four now
use the platform, so their items are PopoverItems with real menuitemradio/aria-checked,
roving focus, typeahead and flip/shift anchoring.
Two platform additions this needed:
- `selectedIndex`: opening a single-choice menu must land on the choice already in
effect, which is what ViewSwitcher's hand-rolled engine did by seeding its roving
index to the current option.
- `ariaLabel` on PopoverSurface: an icon-only trigger leaves the panel with no name.
Fixed while wiring `selectedIndex` up: it stranded focus on the menu container. That
path focuses its item synchronously in a layout effect, and FloatingFocusManager's own
initial focus then ran afterwards and took focus back; the no-selectedIndex path only
escaped this because it defers focus to a microtask while waiting for the item list to
populate. On a roving list the list navigation owns initial focus, so the manager is now
told to take none (initialFocus -1).
Behaviour changes worth naming:
- The header LanguageSwitcher no longer opens on hover. The platform focuses the first
item on open, so a hover-open would yank focus off whatever the user was doing. Both
variants are now click-only, like every other menu in the app.
- PrefToggle became a PopoverItem, so the rows callers inject through the menu's `extra`
prop join the same roving list as the built-in ones.
- AddToPlaylist fetches its list on open, so on the first paint there are no items to
focus and focus stays on the trigger (as it did before the migration); arrows move
into the list once it lands.
Verified in-browser: ViewSwitcher opens on the checked mode, arrows rove, Escape closes
and refocuses the trigger; the ChannelTags pilot is unregressed; AddToPlaylist anchors
bottom-start without the click reaching the feed card underneath; the rail language menu
keeps its right-edge, bottom-aligned anchoring.