import { useEffect, useRef, useState, type KeyboardEvent } from "react"; import type { Environment } from "../core/model"; import { EnvBadge } from "./EnvBadge"; import { Icon } from "./icons"; export function EnvironmentSwitcher({ environments, activeId, onSelect }: { environments: Environment[]; activeId: string | null; onSelect: (id: string | null) => void; }) { const [open, setOpen] = useState(false); const root = useRef(null); const trigger = useRef(null); const menu = useRef(null); const active = environments.find((env) => env.id === activeId); useEffect(() => { if (!open) return; const selected = menu.current?.querySelector('[aria-checked="true"]'); (selected ?? menu.current?.querySelector('[role="menuitemradio"]'))?.focus(); const closeOutside = (event: PointerEvent) => { if (event.target instanceof Node && !root.current?.contains(event.target)) setOpen(false); }; const closeOnResize = () => setOpen(false); window.addEventListener("pointerdown", closeOutside, true); window.addEventListener("resize", closeOnResize); return () => { window.removeEventListener("pointerdown", closeOutside, true); window.removeEventListener("resize", closeOnResize); }; }, [open]); const choose = (id: string | null) => { onSelect(id); setOpen(false); trigger.current?.focus(); }; const onMenuKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { event.preventDefault(); setOpen(false); trigger.current?.focus(); return; } if (event.key === "Tab") { setOpen(false); return; } if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return; event.preventDefault(); const options = Array.from(menu.current?.querySelectorAll('[role="menuitemradio"]') ?? []); if (!options.length) return; const current = options.indexOf(document.activeElement as HTMLButtonElement); const next = event.key === "Home" ? 0 : event.key === "End" ? options.length - 1 : (current + (event.key === "ArrowDown" ? 1 : -1) + options.length) % options.length; options[next].focus(); }; return
{open && ; }