/** * Full-screen navigation sheet for the web client (ticket #90): a mobile-first * menu — never a desktop dropdown — reused by both the "Panneaux" menu * ({@link WebWorkspace}) and the "Paramètres" menu ({@link WebApp}). Same shell * as {@link WebTicketPickerSheet}: fixed header with a close action, an optional * shared note, and a scrollable list of entries. The active entry is marked with * "●"; a disabled entry stays visible (never removed) so its reason is * discoverable, via `hint` (title tooltip) and/or `secondaryLabel` (always-visible * text — the only way to communicate "why" on a touch device with no hover). */ import { useEffect, useRef } from "react"; import { Button, cn, zIndex } from "@/shared"; export interface WebMenuEntry { id: string; label: string; active: boolean; disabled?: boolean; /** Tooltip shown on hover/focus (desktop browsers); not relied on for touch. */ hint?: string; /** Always-visible secondary text, e.g. "Desktop uniquement". */ secondaryLabel?: string; onSelect: () => void; } export interface WebMenuSheetProps { title: string; entries: WebMenuEntry[]; /** Shared note shown once above the list (e.g. why some entries are disabled). */ note?: string; onClose: () => void; } export function WebMenuSheet({ title, entries, note, onClose }: WebMenuSheetProps) { const closeRef = useRef(null); useEffect(() => { closeRef.current?.focus(); }, []); useEffect(() => { function onKeyDown(e: KeyboardEvent) { if (e.key === "Escape") onClose(); } window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, [onClose]); return (
{title}
{note && (

{note}

)}
); }