1014 lines
28 KiB
TypeScript
1014 lines
28 KiB
TypeScript
"use client";
|
||
import {
|
||
Bell,
|
||
Bookmark,
|
||
Bug,
|
||
ChevronDown,
|
||
CreditCard,
|
||
FileQuestion,
|
||
Heart,
|
||
HeartHandshake,
|
||
HelpCircle,
|
||
History,
|
||
LayoutGrid,
|
||
LifeBuoy,
|
||
LogOut,
|
||
type LucideIcon,
|
||
Menu,
|
||
MessageCircleHeart,
|
||
Search,
|
||
Settings,
|
||
ShoppingCart,
|
||
Star,
|
||
ThumbsUp,
|
||
User,
|
||
} from "lucide-react";
|
||
import {
|
||
type CSSProperties,
|
||
Fragment,
|
||
useEffect,
|
||
useMemo,
|
||
useState,
|
||
} from "react";
|
||
|
||
import {
|
||
Logo,
|
||
LogoImage,
|
||
LogoTextDesktop,
|
||
} from "@/components/shadcnblocks/logo";
|
||
import {
|
||
Accordion,
|
||
AccordionContent,
|
||
AccordionItem,
|
||
AccordionTrigger,
|
||
} from "@/components/ui/accordion";
|
||
import { Badge } from "@/components/ui/badge";
|
||
import { Button } from "@/components/ui/button";
|
||
import {
|
||
DropdownMenu,
|
||
DropdownMenuContent,
|
||
DropdownMenuItem,
|
||
DropdownMenuPortal,
|
||
DropdownMenuSub,
|
||
DropdownMenuSubContent,
|
||
DropdownMenuSubTrigger,
|
||
DropdownMenuTrigger,
|
||
} from "@/components/ui/dropdown-menu";
|
||
import {
|
||
NavigationMenu,
|
||
NavigationMenuContent,
|
||
NavigationMenuItem,
|
||
NavigationMenuLink,
|
||
NavigationMenuList,
|
||
NavigationMenuTrigger,
|
||
navigationMenuTriggerStyle,
|
||
} from "@/components/ui/navigation-menu";
|
||
import {
|
||
Sheet,
|
||
SheetContent,
|
||
SheetHeader,
|
||
SheetTrigger,
|
||
} from "@/components/ui/sheet";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
type HomeLink = {
|
||
href: string;
|
||
logo: {
|
||
src: string;
|
||
alt: string;
|
||
};
|
||
};
|
||
|
||
type Link = {
|
||
label: string;
|
||
href: string;
|
||
};
|
||
|
||
interface SideMenuItem extends Link {
|
||
icon: LucideIcon;
|
||
}
|
||
|
||
type HelpfullLinkGroup = {
|
||
group: string;
|
||
links: Link[];
|
||
};
|
||
|
||
type SideMenuType = Array<SideMenuItem[]>;
|
||
|
||
type MegaMenuSectionType = {
|
||
label: string;
|
||
href: string;
|
||
id: string;
|
||
items: {
|
||
label: string;
|
||
href: string;
|
||
}[];
|
||
};
|
||
|
||
type MenuItemType = {
|
||
label: string;
|
||
href?: string;
|
||
id?: string;
|
||
sections?: MegaMenuSectionType[];
|
||
};
|
||
|
||
type MenuType = MenuItemType[];
|
||
|
||
interface HelpfullLinksDropdownProps {
|
||
groups?: HelpfullLinkGroup[];
|
||
}
|
||
|
||
interface SecondaryMenuProps {
|
||
menu: MenuType;
|
||
}
|
||
|
||
interface SecondaryMenuMobileProps {
|
||
menu: MenuType;
|
||
}
|
||
|
||
type DesktopMenuDropdownItemProps = MenuItemType;
|
||
|
||
interface MobileMenuProps {
|
||
menu: SideMenuType;
|
||
homeLink: HomeLink;
|
||
}
|
||
|
||
interface EcommerceNavbar2Props {
|
||
home: HomeLink;
|
||
helpfullLinks?: HelpfullLinkGroup[];
|
||
menu: MenuType;
|
||
sideMenu: SideMenuType;
|
||
className?: string;
|
||
}
|
||
|
||
const HOME = {
|
||
href: "https://ShadcnBlocks",
|
||
logo: {
|
||
src: "/block-media/shadcnblocks/block/logos/ShadcnBlockscom-icon-white.svg",
|
||
alt: "لوگو",
|
||
},
|
||
};
|
||
|
||
const HELPFUL_LINKS = [
|
||
{
|
||
group: "پشتیبانی",
|
||
links: [
|
||
{ label: "پشتیبانی مشتریان", href: "#" },
|
||
{ label: "تماس با ما", href: "#" },
|
||
{ label: "سؤالات متداول", href: "#" },
|
||
{ label: "راهنمای محصول", href: "#" },
|
||
{ label: "گارانتی و تعمیر", href: "#" },
|
||
],
|
||
},
|
||
{
|
||
group: "سفارش و ارسال",
|
||
links: [
|
||
{ label: "پیگیری سفارش", href: "#" },
|
||
{ label: "اطلاعات ارسال", href: "#" },
|
||
{ label: "مرجوعی و بازپرداخت", href: "#" },
|
||
{ label: "لغو سفارش", href: "#" },
|
||
],
|
||
},
|
||
{
|
||
group: "ابزارهای خرید",
|
||
links: [
|
||
{ label: "یافتن فروشگاه", href: "#" },
|
||
{ label: "کارت هدیه", href: "#" },
|
||
{ label: "تخفیف دانشجویی", href: "#" },
|
||
{ label: "پیشنهادها و تخفیفها", href: "#" },
|
||
],
|
||
},
|
||
{
|
||
group: "شرکت و قوانین",
|
||
links: [
|
||
{ label: "درباره ما", href: "#" },
|
||
{ label: "فرصتهای شغلی", href: "#" },
|
||
{ label: "حریم خصوصی", href: "#" },
|
||
{ label: "شرایط و ضوابط", href: "#" },
|
||
{ label: "دسترسیپذیری", href: "#" },
|
||
],
|
||
},
|
||
];
|
||
|
||
const MENU = [
|
||
{
|
||
id: "shop",
|
||
label: "فروشگاه",
|
||
href: "#",
|
||
sections: [
|
||
{
|
||
label: "صورت",
|
||
id: "face",
|
||
href: "#",
|
||
items: [
|
||
"پاککنندهها",
|
||
"پاککنندههای روغنی",
|
||
"پاککنندههای فومی",
|
||
"تونرها",
|
||
"اسنسها",
|
||
"سرمها",
|
||
"مرطوبکنندهها",
|
||
"کرم دور چشم",
|
||
"روغنهای صورت",
|
||
"ماسک صورت",
|
||
"لایهبردارها",
|
||
"درمان لک",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "بدن",
|
||
id: "body",
|
||
href: "#",
|
||
items: [
|
||
"شوینده بدن",
|
||
"اسکراب بدن",
|
||
"لوسیون بدن",
|
||
"کرم بدن",
|
||
"روغن بدن",
|
||
"کرم دست",
|
||
"مراقبت پا",
|
||
"درمان سفتکننده",
|
||
"ملزومات حمام",
|
||
"مراقبت پس از آفتاب",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "محافظت در برابر آفتاب",
|
||
id: "sun",
|
||
href: "#",
|
||
items: [
|
||
"SPF روزانه",
|
||
"ضدآفتاب معدنی",
|
||
"ضدآفتاب شیمیایی",
|
||
"SPF رنگی",
|
||
"استیک SPF",
|
||
"ضدآفتاب صورت",
|
||
"ضدآفتاب بدن",
|
||
"SPF لب",
|
||
"مراقبت پس از آفتاب",
|
||
"اسپری ضدآفتاب",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "پاکسازی",
|
||
id: "cleansing",
|
||
href: "#",
|
||
items: [
|
||
"میسلار واتر",
|
||
"پاککننده ژلی",
|
||
"پاککننده کرمی",
|
||
"مرهم پاککننده",
|
||
"روغن پاککننده",
|
||
"پاککننده آرایش",
|
||
"پاککننده لایهبردار",
|
||
"پاککننده پوست حساس",
|
||
"سایز مسافرتی",
|
||
"بستههای شارژ",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "درمانها",
|
||
id: "treatments",
|
||
href: "#",
|
||
items: [
|
||
"درمان آکنه",
|
||
"درمان روشنکننده",
|
||
"درمان ضدپیری",
|
||
"تقویتکننده آبرسانی",
|
||
"پیلینگ",
|
||
"ماسک شبانه",
|
||
"آمپول",
|
||
"سرم اصلاحی",
|
||
"درمان آرامبخش",
|
||
"درمان ترمیمی",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "ابزار و دستگاه",
|
||
id: "tools",
|
||
href: "#",
|
||
items: [
|
||
"غلتک صورت",
|
||
"گua شا",
|
||
"ماسک LED",
|
||
"برس پاکسازی",
|
||
"ماساژور صورت",
|
||
"غلتک یخی",
|
||
"ابزار منافذ",
|
||
"دستگاه میکروجریان",
|
||
"سرهای یدکی",
|
||
"ابزار مسافرتی",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "ماسکها",
|
||
id: "masks",
|
||
href: "#",
|
||
items: [
|
||
"ماسک ورقهای",
|
||
"ماسک رس",
|
||
"ماسک خواب",
|
||
"ماسک لایهای",
|
||
"ماسک آبرسان",
|
||
"ماسک پاککننده",
|
||
"ماسک آرامبخش",
|
||
"ماسک روشنکننده",
|
||
"ماسک لایهبردار",
|
||
"ست مولتیماسک",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "مراقبت چشم و لب",
|
||
id: "eye-lip",
|
||
href: "#",
|
||
items: [
|
||
"کرم دور چشم",
|
||
"سرم دور چشم",
|
||
"ماسک چشم",
|
||
"مرهم لب",
|
||
"ماسک لب",
|
||
"اسکراب لب",
|
||
"درمان پف چشم",
|
||
"مراقبت تیرگی دور چشم",
|
||
"SPF لب",
|
||
"مراقبت شبانه لب",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "مو و پوست سر",
|
||
id: "hair",
|
||
href: "#",
|
||
items: [
|
||
"درمان پوست سر",
|
||
"اسکراب پوست سر",
|
||
"سرم مو",
|
||
"روغن مو",
|
||
"ماسک مو",
|
||
"ضدآفتاب مو",
|
||
"مراقبت شوره",
|
||
"درمان رشد مو",
|
||
"مراقبت بدون آبکشی",
|
||
"ابزار مو",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "حمام و سلامتی",
|
||
id: "wellness",
|
||
href: "#",
|
||
items: [
|
||
"روغن حمام",
|
||
"نمک حمام",
|
||
"محلولهای حمام",
|
||
"رایحهدرمانی",
|
||
"آرامبخش بدن",
|
||
"کاهش استرس",
|
||
"مراقبت خواب",
|
||
"روغن ماساژ",
|
||
"ست مراقبت شخصی",
|
||
"هدایای سلامتی",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "مراقبت آقایان",
|
||
id: "men",
|
||
href: "#",
|
||
items: [
|
||
"شوینده صورت",
|
||
"مرطوبکننده",
|
||
"پس از اصلاح",
|
||
"مراقبت ریش",
|
||
"کرم دور چشم",
|
||
"ضدپیری",
|
||
"کنترل چربی",
|
||
"مراقبت بدن",
|
||
"کیت مسافرتی",
|
||
"ست اصلاح",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "مراقبت پوست نوجوان",
|
||
id: "teen",
|
||
href: "#",
|
||
items: [
|
||
"پاککننده ملایم",
|
||
"مراقبت آکنه",
|
||
"کنترل چربی",
|
||
"مرطوبکننده آرامبخش",
|
||
"درمان لک",
|
||
"SPF",
|
||
"کیت شروع",
|
||
"پوست حساس",
|
||
"سایز مسافرتی",
|
||
"ملزومات روزانه",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "سایز مسافرتی",
|
||
id: "travel",
|
||
href: "#",
|
||
items: [
|
||
"پاککننده کوچک",
|
||
"سرم کوچک",
|
||
"مرطوبکننده کوچک",
|
||
"ماسک کوچک",
|
||
"کیت مسافرتی",
|
||
"ظرف قابل شارژ",
|
||
"مناسب کابین",
|
||
"ست نمونه",
|
||
"کیت آخر هفته",
|
||
"مراقبت در مسیر",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "هدیه و ستها",
|
||
id: "gifts",
|
||
href: "#",
|
||
items: [
|
||
"ست هدیه",
|
||
"کیت شروع",
|
||
"بسته روتین",
|
||
"پرفروشترینها",
|
||
"ست مناسبتی",
|
||
"نسخه محدود",
|
||
"ست کوچک",
|
||
"هدایای لوکس",
|
||
"زیر ۵۰ ریال",
|
||
"زیر ۱۰۰ ریال",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
],
|
||
},
|
||
|
||
{
|
||
id: "collections",
|
||
label: "مجموعهها",
|
||
href: "#",
|
||
sections: [
|
||
{
|
||
label: "بر اساس مشکل پوست",
|
||
id: "concerns",
|
||
href: "#",
|
||
items: [
|
||
"آکنه",
|
||
"خشکی",
|
||
"حساسیت",
|
||
"خطوط ریز",
|
||
"چین و چروک",
|
||
"لک",
|
||
"قرمزی",
|
||
"کدری",
|
||
"بافت ناهمگن",
|
||
"منافذ باز",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "بر اساس نوع پوست",
|
||
id: "skin-type",
|
||
href: "#",
|
||
items: [
|
||
"نرمال",
|
||
"خشک",
|
||
"چرب",
|
||
"مختلط",
|
||
"حساس",
|
||
"بالغ",
|
||
"آکنهای",
|
||
"کمآب",
|
||
"واکنشپذیر",
|
||
"همه انواع پوست",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "بر اساس ماده فعال",
|
||
id: "ingredients",
|
||
href: "#",
|
||
items: [
|
||
"هیالورونیک اسید",
|
||
"ویتامین C",
|
||
"رتینول",
|
||
"نیاسینامید",
|
||
"سرامید",
|
||
"پپتید",
|
||
"سالیسیلیک اسید",
|
||
"AHA و BHA",
|
||
"باکوچیول",
|
||
"سنتلا آسیاتica",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "بر اساس روتین",
|
||
id: "routine",
|
||
href: "#",
|
||
items: [
|
||
"روتین صبح",
|
||
"روتین شب",
|
||
"روتین مینیمال",
|
||
"روتین پیشرفته",
|
||
"درمانهای هفتگی",
|
||
"روتین مسافرتی",
|
||
"مراقبت پس از درمان",
|
||
"مراقبت فصلی",
|
||
"آیینهای مراقبت شخصی",
|
||
"الهامگرفته از حرفهایها",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
{
|
||
label: "زیبایی پاک و مسئولانه",
|
||
id: "clean",
|
||
href: "#",
|
||
items: [
|
||
"زیبایی پاک",
|
||
"وگان",
|
||
"بدون تست حیوانی",
|
||
"بدون بو",
|
||
"تستشده توسط متخصص پوست",
|
||
"مناسب پوست حساس",
|
||
"بستهبندی زیستمحیطی",
|
||
"قابل بازیافت",
|
||
"قابل شارژ",
|
||
"برندهای پایدار",
|
||
].map((label) => ({ label, href: "#" })),
|
||
},
|
||
],
|
||
},
|
||
|
||
{ label: "آزمون پوست", href: "#" },
|
||
{ label: "درباره ما", href: "#" },
|
||
{ label: "وبلاگ", href: "#" },
|
||
{ label: "حراج", href: "#" },
|
||
];
|
||
|
||
const SIDE_MENU = [
|
||
[
|
||
{
|
||
label: "حساب کاربری",
|
||
href: "#",
|
||
icon: User,
|
||
},
|
||
{
|
||
label: "سوابق خرید",
|
||
href: "#",
|
||
icon: History,
|
||
},
|
||
{
|
||
label: "روشهای پرداخت",
|
||
href: "#",
|
||
icon: CreditCard,
|
||
},
|
||
{
|
||
label: "تنظیمات حساب",
|
||
href: "#",
|
||
icon: Settings,
|
||
},
|
||
{
|
||
label: "خروج",
|
||
href: "#",
|
||
icon: LogOut,
|
||
},
|
||
],
|
||
[
|
||
{
|
||
label: "راهنما",
|
||
href: "#",
|
||
icon: HeartHandshake,
|
||
},
|
||
{
|
||
label: "راهنمای کاربران",
|
||
href: "#",
|
||
icon: HelpCircle,
|
||
},
|
||
{
|
||
label: "سؤالات متداول",
|
||
href: "#",
|
||
icon: FileQuestion,
|
||
},
|
||
{
|
||
label: "تیکت پشتیبانی",
|
||
href: "#",
|
||
icon: LifeBuoy,
|
||
},
|
||
],
|
||
[
|
||
{
|
||
label: "علاقهمندیها",
|
||
href: "#",
|
||
icon: Heart,
|
||
},
|
||
{
|
||
label: "ذخیرهشدهها",
|
||
href: "#",
|
||
icon: Bookmark,
|
||
},
|
||
{
|
||
label: "هشدار موجود شدن",
|
||
href: "#",
|
||
icon: Bell,
|
||
},
|
||
{
|
||
label: "اخیراً مشاهدهشده",
|
||
href: "#",
|
||
icon: Star,
|
||
},
|
||
],
|
||
[
|
||
{
|
||
label: "بازخورد",
|
||
href: "#",
|
||
icon: MessageCircleHeart,
|
||
},
|
||
{
|
||
label: "امتیازدهی به تجربه",
|
||
href: "#",
|
||
icon: ThumbsUp,
|
||
},
|
||
{
|
||
label: "گزارش مشکل",
|
||
href: "#",
|
||
icon: Bug,
|
||
},
|
||
],
|
||
];
|
||
|
||
const EcommerceNavbar2 = ({
|
||
home = HOME,
|
||
helpfullLinks = HELPFUL_LINKS,
|
||
menu = MENU,
|
||
sideMenu = SIDE_MENU,
|
||
className,
|
||
}: EcommerceNavbar2Props) => {
|
||
useEffect(() => {
|
||
document.documentElement.style.setProperty(
|
||
"--primary-nav-height",
|
||
"5.25rem",
|
||
);
|
||
document.documentElement.style.setProperty(
|
||
"--secondary-nav-height",
|
||
"3.25rem",
|
||
);
|
||
}, []);
|
||
|
||
return (
|
||
<header
|
||
dir="rtl"
|
||
lang="fa"
|
||
className={cn("fixed inset-x-0 top-0 isolate z-50 text-start", className)}
|
||
>
|
||
<div className="flex h-[var(--primary-nav-height)] items-center gap-3 bg-primary px-6 py-4 text-primary-foreground">
|
||
<Logo url={home.href} className="shrink-0 p-2">
|
||
<LogoImage
|
||
src={home.logo.src}
|
||
alt={home.logo.alt}
|
||
className="size-8"
|
||
/>
|
||
<LogoTextDesktop className="font-medium text-primary-foreground">
|
||
ری یو آی
|
||
</LogoTextDesktop>
|
||
</Logo>
|
||
<div className="ms-auto flex items-center gap-3">
|
||
<div className="hidden lg:contents">
|
||
<HelpfullLinksDropdown groups={helpfullLinks} />
|
||
<Button
|
||
variant="ghost"
|
||
className="text-primary-foreground hover:bg-primary-foreground/10 hover:text-primary-foreground"
|
||
>
|
||
<Heart />
|
||
علاقهمندیها
|
||
</Button>
|
||
<Button
|
||
variant="ghost"
|
||
className="text-primary-foreground hover:bg-primary-foreground/10 hover:text-primary-foreground"
|
||
>
|
||
<User />
|
||
حساب کاربری
|
||
</Button>
|
||
</div>
|
||
<SearchForm />
|
||
<div className="relative size-fit">
|
||
<Button
|
||
size="icon"
|
||
variant="ghost"
|
||
className="text-primary-foreground hover:bg-primary-foreground/10 hover:text-primary-foreground"
|
||
aria-label="سبد خرید"
|
||
>
|
||
<ShoppingCart />
|
||
</Button>
|
||
<Badge className="absolute top-0 end-0 translate-x-1/2 -translate-y-1/2 bg-amber-500 text-[0.625rem] font-medium text-foreground">
|
||
۰
|
||
</Badge>
|
||
</div>
|
||
<div className="contents lg:hidden">
|
||
<MobileMenu menu={sideMenu} homeLink={home} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="relative h-[var(--secondary-nav-height)] overflow-visible">
|
||
<SecondaryMenu menu={menu} />
|
||
</div>
|
||
</header>
|
||
);
|
||
};
|
||
|
||
const SearchForm = () => {
|
||
return (
|
||
<Button
|
||
size="icon"
|
||
variant="ghost"
|
||
className="text-primary-foreground hover:bg-primary-foreground/10 hover:text-primary-foreground"
|
||
aria-label="جستجو"
|
||
>
|
||
<Search />
|
||
</Button>
|
||
);
|
||
};
|
||
|
||
const HelpfullLinksDropdown = ({ groups }: HelpfullLinksDropdownProps) => {
|
||
if (!groups) return;
|
||
|
||
return (
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<Button
|
||
variant="ghost"
|
||
className="text-primary-foreground hover:bg-primary-foreground/10 hover:text-primary-foreground data-[state=open]:bg-primary-foreground/10 data-[state=open]:text-primary-foreground"
|
||
>
|
||
<HeartHandshake className="size-4" />
|
||
راهنما و پشتیبانی
|
||
<ChevronDown className="rtl:rotate-180" />
|
||
</Button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent
|
||
className="w-56 !border-primary-foreground/20 !bg-primary !text-primary-foreground"
|
||
align="start"
|
||
>
|
||
{groups.map(({ group, links }, index) => (
|
||
<DropdownMenuSub key={index}>
|
||
<DropdownMenuSubTrigger className="**:!text-primary-foreground focus:bg-primary-foreground/10 focus:!text-primary-foreground data-[state=open]:bg-primary-foreground/10 data-[state=open]:!text-primary-foreground">
|
||
{group}
|
||
</DropdownMenuSubTrigger>
|
||
<DropdownMenuPortal>
|
||
<DropdownMenuSubContent className="!border-primary-foreground/20 !bg-primary !text-primary-foreground">
|
||
{links.map(({ label, href }, index) => (
|
||
<DropdownMenuItem
|
||
asChild
|
||
key={`${label}-${index}`}
|
||
className="focus:bg-primary-foreground/10 focus:text-primary-foreground"
|
||
>
|
||
<a href={href}>{label}</a>
|
||
</DropdownMenuItem>
|
||
))}
|
||
</DropdownMenuSubContent>
|
||
</DropdownMenuPortal>
|
||
</DropdownMenuSub>
|
||
))}
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
);
|
||
};
|
||
|
||
const MobileMenu = ({ menu, homeLink }: MobileMenuProps) => {
|
||
return (
|
||
<Sheet>
|
||
<SheetTrigger asChild>
|
||
<Button
|
||
size="icon"
|
||
variant="ghost"
|
||
className="text-primary-foreground hover:bg-primary-foreground/10 hover:text-primary-foreground focus:bg-primary-foreground/10 focus:text-primary-foreground data-[state=open]:bg-primary-foreground/10 data-[state=open]:text-primary-foreground"
|
||
aria-label="باز کردن منو"
|
||
>
|
||
<Menu />
|
||
</Button>
|
||
</SheetTrigger>
|
||
<SheetContent side="right" className="hide-scrollbar overflow-auto">
|
||
<div className="px-4">
|
||
<SheetHeader>
|
||
<Logo url={homeLink.href} className="max-h-fit shrink-0 basis-11">
|
||
<LogoImage
|
||
src={homeLink.logo.src}
|
||
alt={homeLink.logo.alt}
|
||
className="size-11 shrink-0 invert dark:invert-0"
|
||
/>
|
||
</Logo>
|
||
</SheetHeader>
|
||
{menu.map((group, index) => (
|
||
<div key={index} className="py-6 not-last:border-b">
|
||
{group.map(({ href, label, icon: Icon }, index) => (
|
||
<Button
|
||
asChild
|
||
variant="ghost"
|
||
className="w-full justify-start text-start"
|
||
key={`side-menu-item-${index}`}
|
||
>
|
||
<a href={href}>
|
||
<Icon />
|
||
{label}
|
||
</a>
|
||
</Button>
|
||
))}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</SheetContent>
|
||
</Sheet>
|
||
);
|
||
};
|
||
|
||
const SecondaryMenu = ({ menu }: SecondaryMenuProps) => {
|
||
return (
|
||
<div className="relative overflow-visible bg-accent px-6 py-2">
|
||
<div className="hidden lg:contents">
|
||
<NavigationMenu
|
||
dir="rtl"
|
||
viewport={false}
|
||
className="relative z-50 justify-start overflow-visible text-start [&_a]:bg-transparent [&_a]:hover:bg-accent-foreground/10 [&_button]:bg-transparent [&_button]:hover:bg-accent-foreground/10"
|
||
>
|
||
<NavigationMenuList className="gap-3.5">
|
||
{menu.map(({ sections, ...item }, index) => (
|
||
<NavigationMenuItem value={`${index}`} key={index} className="static">
|
||
{sections ? (
|
||
<DesktopMenuDropdownItem sections={sections} {...item} />
|
||
) : (
|
||
<NavigationMenuLink
|
||
asChild
|
||
className={cn(
|
||
navigationMenuTriggerStyle(),
|
||
"bg-transparent hover:bg-accent-foreground/10",
|
||
)}
|
||
>
|
||
<a href={item.href}>{item.label}</a>
|
||
</NavigationMenuLink>
|
||
)}
|
||
</NavigationMenuItem>
|
||
))}
|
||
</NavigationMenuList>
|
||
</NavigationMenu>
|
||
</div>
|
||
<div className="lg:hidden">
|
||
<SecondaryMenuMobile menu={menu} />
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const SecondaryMenuMobile = ({ menu }: SecondaryMenuMobileProps) => {
|
||
if (!menu) return;
|
||
|
||
return (
|
||
<Sheet modal={false}>
|
||
<SheetTrigger asChild>
|
||
<Button variant="secondary">
|
||
<LayoutGrid />
|
||
دستهبندیها
|
||
</Button>
|
||
</SheetTrigger>
|
||
<SheetContent
|
||
side="top"
|
||
className="!top-[calc(var(--primary-nav-height)+var(--secondary-nav-height))] z-40 !h-[calc(100dvh-var(--primary-nav-height)-var(--secondary-nav-height))] overflow-hidden [&>button]:hidden"
|
||
>
|
||
<div className="min-h-0 flex-1 touch-pan-y overflow-y-auto overscroll-contain pb-10">
|
||
<div className="p-6">
|
||
{menu.map(({ sections, label, href }, index) => (
|
||
<div key={index}>
|
||
<h2 className="border-b py-4 text-lg leading-normal font-bold">
|
||
{href ? <a href={href}>{label}</a> : <span>{label}</span>}
|
||
</h2>
|
||
{sections && (
|
||
<Accordion type="multiple">
|
||
{sections.map(({ id, label, items }) => (
|
||
<AccordionItem value={id} key={`menu-section-${id}`}>
|
||
<AccordionTrigger>{label}</AccordionTrigger>
|
||
<AccordionContent>
|
||
<div className="flex flex-col">
|
||
{items.map(({ href, label }, index) => (
|
||
<Button
|
||
className="w-full justify-start"
|
||
asChild
|
||
variant="ghost"
|
||
size="sm"
|
||
key={`menu-item-${index}`}
|
||
>
|
||
<a href={href}>{label}</a>
|
||
</Button>
|
||
))}
|
||
</div>
|
||
</AccordionContent>
|
||
</AccordionItem>
|
||
))}
|
||
</Accordion>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</SheetContent>
|
||
</Sheet>
|
||
);
|
||
};
|
||
|
||
const DesktopMenuDropdownItem = ({
|
||
sections,
|
||
label,
|
||
href,
|
||
}: DesktopMenuDropdownItemProps) => {
|
||
const [activeSectionId, setActiveSectionId] = useState<string>();
|
||
|
||
const sharedClasses = "h-full space-y-2 overflow-auto pt-6 pb-2";
|
||
|
||
const MenuItemClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||
const selectedSectionId =
|
||
e.currentTarget.getAttribute("data-id") ?? undefined;
|
||
|
||
setActiveSectionId(selectedSectionId);
|
||
};
|
||
|
||
const MenuItemMouseEnter = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||
const selectedSectionId =
|
||
e.currentTarget.getAttribute("data-id") ?? undefined;
|
||
|
||
setActiveSectionId(selectedSectionId);
|
||
};
|
||
|
||
const activeSectionData = useMemo(
|
||
() => sections?.find((section) => section.id === activeSectionId),
|
||
[sections, activeSectionId],
|
||
);
|
||
|
||
if (!sections) return;
|
||
|
||
return (
|
||
<Fragment>
|
||
<NavigationMenuTrigger>{label}</NavigationMenuTrigger>
|
||
<NavigationMenuContent
|
||
dir="rtl"
|
||
className="start-0 end-auto z-50 !overflow-visible p-0"
|
||
>
|
||
<div
|
||
style={
|
||
{
|
||
"--menu-margin-bottom": "5rem",
|
||
} as CSSProperties
|
||
}
|
||
className="relative flex min-w-max items-stretch overflow-visible max-h-[calc(100dvh-var(--secondary-nav-height)-var(--primary-nav-height)-var(--menu-margin-bottom))]"
|
||
>
|
||
<div className="relative z-0 shrink-0">
|
||
<div className={cn("w-60", sharedClasses)}>
|
||
<h2 className="px-6 text-sm font-bold">
|
||
<a href={href}>{label}</a>
|
||
</h2>
|
||
<ol>
|
||
{sections.map((section, index) => (
|
||
<li key={index}>
|
||
<Button
|
||
variant="ghost"
|
||
data-id={section.id}
|
||
data-state={
|
||
section.id === activeSectionId ? "active" : "inactive"
|
||
}
|
||
onClick={MenuItemClick}
|
||
onMouseEnter={MenuItemMouseEnter}
|
||
className={cn(
|
||
"relative w-full justify-start rounded-none px-6 text-start font-normal",
|
||
"data-[state=active]:bg-accent",
|
||
"transition-opacity after:absolute after:inset-y-0 after:start-0 after:h-full after:w-1 after:bg-primary data-[state=inactive]:after:opacity-0",
|
||
)}
|
||
>
|
||
{section.label}
|
||
</Button>
|
||
</li>
|
||
))}
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
{activeSectionData && (
|
||
<div className="relative z-10 shrink-0">
|
||
<div
|
||
className={cn(
|
||
"w-50 border-s bg-popover shadow-sm",
|
||
sharedClasses,
|
||
)}
|
||
>
|
||
<h2 className="px-6 text-sm font-bold">
|
||
{activeSectionData.label}
|
||
</h2>
|
||
<ol>
|
||
{activeSectionData.items.map((item, index) => (
|
||
<li key={index}>
|
||
<NavigationMenuLink
|
||
className="px-6 hover:underline"
|
||
asChild
|
||
>
|
||
<a href={item.href}>{item.label}</a>
|
||
</NavigationMenuLink>
|
||
</li>
|
||
))}
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</NavigationMenuContent>
|
||
</Fragment>
|
||
);
|
||
};
|
||
|
||
export { EcommerceNavbar2 };
|