spb/phi-conseil
Public
1"use client";23import { useEffect, useId, useRef, useState } from "react";4import { createPortal } from "react-dom";5import { Button } from "@/components/ui/Button";6import { NavLinks } from "./NavLinks";78type Item = { label: string; href: string };910export function MobileNav({11 items,12 cta,13 brandName,14}: {15 items: Item[];16 cta: Item;17 brandName: string;18}) {19 const [open, setOpen] = useState(false);20 const panelId = useId();21 const closeRef = useRef<HTMLButtonElement>(null);22 const openerRef = useRef<HTMLButtonElement>(null);2324 useEffect(() => {25 if (!open) return;26 const prev = document.body.style.overflow;27 const opener = openerRef.current;28 document.body.style.overflow = "hidden";29 closeRef.current?.focus();30 const onKey = (e: KeyboardEvent) => {31 if (e.key === "Escape") setOpen(false);32 };33 document.addEventListener("keydown", onKey);34 return () => {35 document.body.style.overflow = prev;36 document.removeEventListener("keydown", onKey);37 opener?.focus();38 };39 }, [open]);4041 // Le panneau est rendu dans <body> par un portail : l’en-tête utilise42 // backdrop-filter, qui ferait de lui le bloc conteneur d’un élément `fixed`.43 return (44 <div className="lg:hidden">45 <button46 ref={openerRef}47 type="button"48 className="text-navy-900 hover:bg-navy-900/5 inline-flex h-11 w-11 items-center justify-center rounded-md"49 aria-expanded={open}50 aria-controls={panelId}51 aria-label="Ouvrir le menu"52 onClick={() => setOpen(true)}53 >54 <svg55 viewBox="0 0 24 24"56 className="h-6 w-6"57 fill="none"58 stroke="currentColor"59 strokeWidth="1.6"60 strokeLinecap="round"61 aria-hidden="true"62 >63 <path d="M4 7h16M4 12h16M4 17h16" />64 </svg>65 </button>6667 {open68 ? createPortal(69 <div70 id={panelId}71 role="dialog"72 aria-modal="true"73 aria-label={`Menu ${brandName}`}74 className="bg-ivory-100 fixed inset-0 z-50 flex flex-col motion-safe:animate-[fade-in_var(--duration-base)_var(--ease-out)]"75 >76 <div className="container-site flex items-center justify-between py-4">77 <span className="text-navy-900 font-serif text-xl">{brandName}</span>78 <button79 ref={closeRef}80 type="button"81 className="text-navy-900 hover:bg-navy-900/5 inline-flex h-11 w-11 items-center justify-center rounded-md"82 aria-label="Fermer le menu"83 onClick={() => setOpen(false)}84 >85 <svg86 viewBox="0 0 24 24"87 className="h-6 w-6"88 fill="none"89 stroke="currentColor"90 strokeWidth="1.6"91 strokeLinecap="round"92 aria-hidden="true"93 >94 <path d="m6 6 12 12M18 6 6 18" />95 </svg>96 </button>97 </div>98 <nav99 aria-label="Navigation mobile"100 className="container-site flex-1 overflow-y-auto py-6"101 >102 <NavLinks103 items={items}104 vertical105 className="flex flex-col gap-1"106 onNavigate={() => setOpen(false)}107 />108 </nav>109 <div className="container-site pb-10">110 <Button href={cta.href} size="lg" className="w-full" onClick={() => setOpen(false)}>111 {cta.label}112 </Button>113 </div>114 </div>,115 document.body,116 )117 : null}118 </div>119 );120}121