182 lines
6 KiB
TypeScript
182 lines
6 KiB
TypeScript
import { Link, usePage } from "@inertiajs/react";
|
|
import { ChevronDown, Menu, X } from "lucide-react";
|
|
import { useState } from "react";
|
|
|
|
import logo from "&/images/global/logo-qualimaine-RVB-defaut.png";
|
|
import useTrans from "@/hooks/use-translations";
|
|
import { GetTranslation } from "@/lib/utils";
|
|
|
|
interface NavLinkResolved {
|
|
label: string;
|
|
href: string;
|
|
resolvedHref: string;
|
|
}
|
|
|
|
const NAV_LINKS = [
|
|
{ label: "front.menus.about_us", href: "front.whoAreWeSection.slug" },
|
|
{ label: "front.menus.engagements", href: "front.engagements.slug" },
|
|
{ label: "front.menus.normes", href: "norms.slug" },
|
|
{ label: "front.menus.contact", href: "footer.contactSlug" },
|
|
];
|
|
|
|
function useSectionHref(slugKey: string) {
|
|
const { url, props } = usePage() as unknown as {
|
|
url: string;
|
|
props: { appEnv: { locales: string[] } };
|
|
};
|
|
const slug = useTrans(slugKey);
|
|
|
|
const isHome =
|
|
url === "/" || props.appEnv.locales.some((locale) => url === `/${locale}`);
|
|
|
|
if (isHome) return `#${slug}`;
|
|
return `${route("front.homepage")}#${slug}`;
|
|
}
|
|
|
|
function LanguageDropdown() {
|
|
const { appEnv, localizedUrls } = usePage().props as unknown as {
|
|
appEnv: { locale: string; locales: string[] };
|
|
localizedUrls: Record<string, string>;
|
|
};
|
|
const [open, setOpen] = useState(false);
|
|
|
|
const otherLocales = appEnv.locales.filter(
|
|
(locale) => locale !== appEnv.locale,
|
|
);
|
|
|
|
return (
|
|
<div className="relative w-16">
|
|
<button
|
|
onClick={() => {
|
|
setOpen((v) => !v);
|
|
}}
|
|
className={`relative flex w-full items-center justify-center border border-neutral-700 bg-white/10 pl-4 pr-3 py-1 font-sans text-[16px] font-bold text-neutral-700 backdrop-blur-sm transition-colors hover:bg-white/10 ${
|
|
open ? "rounded-t-3xl border-b-0" : "rounded-3xl"
|
|
}`}
|
|
>
|
|
<span className="invisible w-3.5" aria-hidden="true" />
|
|
<span className="flex-1 text-center">
|
|
{appEnv.locale.toUpperCase()}
|
|
</span>
|
|
<ChevronDown
|
|
size={28}
|
|
className={`w-64 transition-transform ${open ? "rotate-180" : ""}`}
|
|
/>
|
|
</button>
|
|
|
|
{open && (
|
|
<div className="absolute inset-x-0 top-full z-50 overflow-hidden rounded-b-3xl border border-t-0 border-neutral-700 bg-white/10 backdrop-blur-sm">
|
|
{otherLocales.map((locale) => (
|
|
<a
|
|
key={locale}
|
|
href={localizedUrls[locale]}
|
|
className="flex items-center justify-end px-3 py-1.5 font-sans text-[16px] font-bold text-neutral-700"
|
|
>
|
|
<span className="rounded-full px-2.5 py-1 transition-colors hover:bg-white/60">
|
|
{locale.toUpperCase()}
|
|
</span>
|
|
</a>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function Navbar() {
|
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
const navLinks: NavLinkResolved[] = [
|
|
{ ...NAV_LINKS[0], resolvedHref: useSectionHref(NAV_LINKS[0].href) },
|
|
{ ...NAV_LINKS[1], resolvedHref: useSectionHref(NAV_LINKS[1].href) },
|
|
{ ...NAV_LINKS[2], resolvedHref: useSectionHref(NAV_LINKS[2].href) },
|
|
{ ...NAV_LINKS[3], resolvedHref: useSectionHref(NAV_LINKS[3].href) },
|
|
];
|
|
|
|
return (
|
|
<div className="relative z-20 w-full">
|
|
<div className="flex w-full items-center justify-between px-6 py-5 md:px-10">
|
|
<Link href={route("front.homepage")}>
|
|
<img
|
|
src={logo}
|
|
alt={useTrans("front.logo_alt")}
|
|
title={useTrans("front.logo_title")}
|
|
className="h-18 w-auto md:h-26 mt-4"
|
|
/>
|
|
</Link>
|
|
|
|
<nav className="hidden items-center gap-7 md:flex">
|
|
{navLinks.map((link) => (
|
|
<Link
|
|
key={GetTranslation(link.href)}
|
|
href={link.resolvedHref}
|
|
className="rounded-3xl px-2 py-1.5 font-sans text-lg font-bold text-neutral-800 transition-colors hover:bg-band"
|
|
>
|
|
{GetTranslation(link.label)}
|
|
</Link>
|
|
))}
|
|
<LanguageDropdown />
|
|
</nav>
|
|
|
|
<div className="flex items-center gap-3 md:hidden">
|
|
<LanguageDropdown />
|
|
<button
|
|
onClick={() => {
|
|
setMobileOpen((v) => !v);
|
|
}}
|
|
aria-label="Menu"
|
|
className="p-2"
|
|
>
|
|
{mobileOpen ? <X size={28} /> : <Menu size={28} />}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{mobileOpen && (
|
|
<div className="fixed inset-0 z-2000 flex flex-col bg-band md:hidden">
|
|
<div className="flex items-center justify-between px-6 py-5">
|
|
<Link
|
|
href="/"
|
|
onClick={() => {
|
|
setMobileOpen(false);
|
|
}}
|
|
>
|
|
<img
|
|
src={logo}
|
|
alt={GetTranslation("front.logo_alt")}
|
|
title={GetTranslation("front.logo_title")}
|
|
className="h-18 w-auto"
|
|
/>
|
|
</Link>
|
|
<div className="flex items-center gap-3">
|
|
<button
|
|
onClick={() => {
|
|
setMobileOpen(false);
|
|
}}
|
|
aria-label=""
|
|
className="p-2"
|
|
>
|
|
<X size={28} className="text-neutral-800" />
|
|
</button>
|
|
<LanguageDropdown />
|
|
</div>
|
|
</div>
|
|
|
|
<nav className="mt-[25%] flex flex-1 flex-col items-center justify-start gap-5 px-8">
|
|
{navLinks.map((link) => (
|
|
<Link
|
|
key={link.href}
|
|
href={link.resolvedHref}
|
|
className="w-full max-w-[280px] rounded-full bg-[#4b4b45] py-3 text-center font-sans text-[18px] font-bold text-white shadow-md transition-colors hover:bg-[#FFD41F] hover:text-black"
|
|
onClick={() => {
|
|
setMobileOpen(false);
|
|
}}
|
|
>
|
|
{GetTranslation(link.label)}
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|