198 lines
6.3 KiB
TypeScript
198 lines
6.3 KiB
TypeScript
"use client";
|
|
|
|
import { Menu, X } from "lucide-react";
|
|
import React, { useEffect, useRef, useState } from "react";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
NavigationMenu,
|
|
NavigationMenuItem,
|
|
NavigationMenuLink,
|
|
NavigationMenuList,
|
|
} from "@/components/ui/navigation-menu";
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from "@/components/ui/popover";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const NAV_LOGO = {
|
|
url: "https://www.ShadcnBlocks",
|
|
src: "/block-media/shadcnblocks/block/logos/ShadcnBlockscom-icon.svg",
|
|
alt: "لوگو",
|
|
title: "وندوک",
|
|
};
|
|
|
|
const NAV_ITEMS = [
|
|
{ name: "خانه", link: "#" },
|
|
{ name: "درباره ما", link: "#" },
|
|
{ name: "قیمتگذاری", link: "#" },
|
|
{ name: "تماس", link: "#" },
|
|
];
|
|
|
|
interface Navbar17Props {
|
|
className?: string;
|
|
}
|
|
|
|
const Navbar17 = ({ className }: Navbar17Props) => {
|
|
const [activeItem, setActiveItem] = useState(NAV_ITEMS[0].name);
|
|
|
|
const indicatorRef = useRef<HTMLDivElement>(null);
|
|
const menuRef = useRef<HTMLUListElement>(null);
|
|
|
|
useEffect(() => {
|
|
const updateIndicator = () => {
|
|
const activeEl = document.querySelector(
|
|
`[data-nav-item="${activeItem}"]`,
|
|
) as HTMLElement;
|
|
|
|
if (activeEl && indicatorRef.current && menuRef.current) {
|
|
const menuRect = menuRef.current.getBoundingClientRect();
|
|
const itemRect = activeEl.getBoundingClientRect();
|
|
|
|
indicatorRef.current.style.width = `${itemRect.width}px`;
|
|
indicatorRef.current.style.insetInlineStart = `${itemRect.left - menuRect.left}px`;
|
|
indicatorRef.current.style.left = "auto";
|
|
}
|
|
};
|
|
updateIndicator();
|
|
window.addEventListener("resize", updateIndicator);
|
|
|
|
return () => window.removeEventListener("resize", updateIndicator);
|
|
}, [activeItem]);
|
|
|
|
return (
|
|
<section lang="fa" className={cn("py-4 text-start", className)}>
|
|
<nav className="container flex items-center justify-between">
|
|
<a href={NAV_LOGO.url} className="flex items-center gap-2">
|
|
<img src={NAV_LOGO.src} className="max-h-8 w-8" alt={NAV_LOGO.alt} />
|
|
<span className="text-lg font-semibold tracking-tighter">
|
|
{NAV_LOGO.title}
|
|
</span>
|
|
</a>
|
|
|
|
<NavigationMenu className="hidden lg:block" dir="rtl">
|
|
<NavigationMenuList
|
|
ref={menuRef}
|
|
className="relative flex items-center gap-6 rounded-4xl px-8 py-3"
|
|
>
|
|
{NAV_ITEMS.map((item) => (
|
|
<NavigationMenuItem key={item.name}>
|
|
<NavigationMenuLink
|
|
data-nav-item={item.name}
|
|
onClick={() => setActiveItem(item.name)}
|
|
className={cn(
|
|
"relative cursor-pointer text-start text-sm font-medium hover:bg-transparent",
|
|
activeItem === item.name
|
|
? "text-foreground"
|
|
: "text-muted-foreground",
|
|
)}
|
|
>
|
|
{item.name}
|
|
</NavigationMenuLink>
|
|
</NavigationMenuItem>
|
|
))}
|
|
<div
|
|
ref={indicatorRef}
|
|
className="absolute bottom-2 flex h-1 items-center justify-center px-2 transition-all duration-300"
|
|
>
|
|
<div className="h-0.5 w-full rounded-t-none bg-foreground transition-all duration-300" />
|
|
</div>
|
|
</NavigationMenuList>
|
|
</NavigationMenu>
|
|
|
|
<MobileNav activeItem={activeItem} setActiveItem={setActiveItem} />
|
|
|
|
<div className="hidden items-center gap-2 lg:flex">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
className="h-10 py-2.5 text-sm font-normal"
|
|
>
|
|
شروع رایگان
|
|
</Button>
|
|
</div>
|
|
</nav>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Navbar17 };
|
|
|
|
const AnimatedHamburger = ({ isOpen }: { isOpen: boolean }) => {
|
|
return (
|
|
<div className="group relative size-full">
|
|
<div className="absolute flex size-full items-center justify-center">
|
|
<Menu
|
|
className={cn(
|
|
"absolute size-6 text-muted-foreground transition-all duration-300 group-hover:text-foreground",
|
|
isOpen ? "rotate-90 opacity-0" : "rotate-0 opacity-100",
|
|
)}
|
|
/>
|
|
<X
|
|
className={cn(
|
|
"absolute size-6 text-muted-foreground transition-all duration-300 group-hover:text-foreground",
|
|
isOpen ? "rotate-0 opacity-100" : "-rotate-90 opacity-0",
|
|
)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const MobileNav = ({
|
|
activeItem,
|
|
setActiveItem,
|
|
}: {
|
|
activeItem: string;
|
|
setActiveItem: (item: string) => void;
|
|
}) => {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
return (
|
|
<div className="flex h-full items-center lg:hidden">
|
|
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
aria-expanded={isOpen}
|
|
aria-label={isOpen ? "بستن منو" : "باز کردن منو"}
|
|
>
|
|
<AnimatedHamburger isOpen={isOpen} />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
|
|
<PopoverContent
|
|
dir="rtl"
|
|
align="start"
|
|
className="relative top-4 -end-4 block w-[calc(100vw-32px)] overflow-hidden rounded-xl p-0 text-start start-auto end-0 sm:top-auto sm:end-auto sm:w-80 lg:hidden"
|
|
>
|
|
<ul className="w-full bg-background py-4 text-start text-foreground">
|
|
{NAV_ITEMS.map((navItem) => (
|
|
<li key={navItem.name}>
|
|
<a
|
|
href={navItem.link}
|
|
onClick={() => setActiveItem(navItem.name)}
|
|
className={cn(
|
|
"flex items-center border-s-[3px] px-6 py-4 text-start text-sm font-medium transition-all duration-75",
|
|
activeItem === navItem.name
|
|
? "border-foreground text-foreground"
|
|
: "border-transparent text-muted-foreground hover:text-foreground",
|
|
)}
|
|
>
|
|
{navItem.name}
|
|
</a>
|
|
</li>
|
|
))}
|
|
<li className="flex flex-col px-7 py-2">
|
|
<Button variant="outline">شروع رایگان</Button>
|
|
</li>
|
|
</ul>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</div>
|
|
);
|
|
};
|