1019 lines
28 KiB
TypeScript
1019 lines
28 KiB
TypeScript
"use client";
|
||
import clsx from "clsx";
|
||
import {
|
||
ChevronLeft,
|
||
ChevronRight,
|
||
CircleUserRound,
|
||
type LucideIcon,
|
||
Menu,
|
||
Phone,
|
||
Search,
|
||
Send,
|
||
ShoppingBag,
|
||
X,
|
||
} from "lucide-react";
|
||
import { type CSSProperties, Fragment, useState } from "react";
|
||
|
||
import {
|
||
Logo,
|
||
LogoImage,
|
||
LogoTextDesktop,
|
||
} from "@/components/shadcnblocks/logo";
|
||
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
||
import { Badge } from "@/components/ui/badge";
|
||
import { Button } from "@/components/ui/button";
|
||
import {
|
||
NavigationMenu,
|
||
NavigationMenuContent,
|
||
NavigationMenuItem,
|
||
NavigationMenuLink,
|
||
NavigationMenuList,
|
||
NavigationMenuTrigger,
|
||
} from "@/components/ui/navigation-menu";
|
||
import {
|
||
Select,
|
||
SelectContent,
|
||
SelectItem,
|
||
SelectTrigger,
|
||
SelectValue,
|
||
} from "@/components/ui/select";
|
||
import {
|
||
Sheet,
|
||
SheetClose,
|
||
SheetContent,
|
||
SheetFooter,
|
||
SheetHeader,
|
||
SheetTitle,
|
||
} from "@/components/ui/sheet";
|
||
import { useIsMobile } from "@/hooks/use-mobile";
|
||
import usePreventScrollLock from "@/hooks/usePreventScrollLock";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
type MegaMenuSectionType = {
|
||
label: string;
|
||
href: string;
|
||
id: string;
|
||
imageSrc: string;
|
||
items: {
|
||
label: string;
|
||
href: string;
|
||
}[];
|
||
};
|
||
|
||
type FeaturedItemType = {
|
||
imageSrc: string;
|
||
href: string;
|
||
label: string;
|
||
};
|
||
|
||
type MenuItemType = {
|
||
label: string;
|
||
href?: string;
|
||
accentColor?: string;
|
||
id?: string;
|
||
megaMenu?: {
|
||
sections?: MegaMenuSectionType[];
|
||
featuredItems?: FeaturedItemType[];
|
||
};
|
||
};
|
||
|
||
type SocialIcon = {
|
||
title: string;
|
||
light: string;
|
||
dark: string;
|
||
};
|
||
|
||
type SocialLink = {
|
||
href: string;
|
||
icon: SocialIcon;
|
||
};
|
||
|
||
type HelpfullLink = {
|
||
href: string;
|
||
label: string;
|
||
};
|
||
|
||
type ContactInfoItem = {
|
||
href: string;
|
||
label: string;
|
||
icon: LucideIcon;
|
||
};
|
||
|
||
type HomeLink = {
|
||
href: string;
|
||
logo: {
|
||
src: string;
|
||
alt: string;
|
||
};
|
||
};
|
||
|
||
type MenuItemProps = MenuItemType;
|
||
|
||
interface EcommerceNavbar1Props {
|
||
menu: MenuItemType[];
|
||
socialLinks?: SocialLink[];
|
||
helpfulLinks?: HelpfullLink[];
|
||
contactInfo?: ContactInfoItem[];
|
||
home: HomeLink;
|
||
className?: string;
|
||
}
|
||
|
||
interface MenuLinksListProps {
|
||
lists?: MegaMenuSectionType[];
|
||
}
|
||
|
||
interface MenuFeaturedLinksProps {
|
||
lists?: FeaturedItemType[];
|
||
}
|
||
|
||
interface MenuLinksProps {
|
||
list: MegaMenuSectionType;
|
||
}
|
||
|
||
interface MobileNavProps {
|
||
menu: MenuItemType[];
|
||
socialLinks?: SocialLink[];
|
||
helpfulLinks?: HelpfullLink[];
|
||
contactInfo?: ContactInfoItem[];
|
||
}
|
||
|
||
type MenuFeaturedLinkProps = FeaturedItemType;
|
||
|
||
interface MegaMenuMobileProps {
|
||
menuItem?: MenuItemType;
|
||
open: boolean;
|
||
onOpenChange: (open: boolean) => void;
|
||
openSubMenu: boolean;
|
||
handleOnSubMenuClick: (id?: string) => void;
|
||
linkStyle?: string;
|
||
closeAll: () => void;
|
||
}
|
||
|
||
interface SubMenuMobileProps {
|
||
subMenu?: MegaMenuSectionType;
|
||
open: boolean;
|
||
onOpenChange: (open: boolean) => void;
|
||
linkStyle?: string;
|
||
closeAll: () => void;
|
||
}
|
||
|
||
interface SocialMediaSectionProps {
|
||
links?: SocialLink[];
|
||
}
|
||
|
||
interface HelpfullLinksProps {
|
||
links?: HelpfullLink[];
|
||
}
|
||
|
||
interface ContactInfoListProps {
|
||
links?: ContactInfoItem[];
|
||
}
|
||
|
||
const HELPFULL_LINKS = [
|
||
{
|
||
label: "مرجوعی و تعویض",
|
||
href: "#",
|
||
},
|
||
{
|
||
label: "ارسال و پیگیری",
|
||
href: "#",
|
||
},
|
||
{
|
||
label: "شرایط و ضوابط",
|
||
href: "#",
|
||
},
|
||
];
|
||
|
||
const CONTACT_INFO = [
|
||
{
|
||
label: "+1 855-987-4420",
|
||
href: "tel:+18559874420",
|
||
icon: Phone,
|
||
},
|
||
{
|
||
label: "support@fashion.co",
|
||
href: "mailto:support@fashion.co",
|
||
icon: Send,
|
||
},
|
||
];
|
||
|
||
const SOCIAL_ICONS = {
|
||
facebook: {
|
||
title: "Facebook",
|
||
light:
|
||
"/block-media/shadcnblocks/block/logos/facebook-icon.svg",
|
||
dark: "/block-media/shadcnblocks/block/logos/facebook-icon.svg",
|
||
},
|
||
x: {
|
||
title: "X",
|
||
light:
|
||
"/block-media/shadcnblocks/block/logos/x.svg",
|
||
dark: "/block-media/shadcnblocks/block/logos/x.svg",
|
||
},
|
||
instagram: {
|
||
title: "Instagram",
|
||
light:
|
||
"/block-media/shadcnblocks/block/logos/instagram-icon.svg",
|
||
dark: "/block-media/shadcnblocks/block/logos/instagram-icon.svg",
|
||
},
|
||
} as const;
|
||
|
||
const SOCIAL_MEDIA_LINKS = [
|
||
{ icon: SOCIAL_ICONS.facebook, href: "#" },
|
||
{ icon: SOCIAL_ICONS.x, href: "#" },
|
||
{ icon: SOCIAL_ICONS.instagram, href: "#" },
|
||
];
|
||
|
||
const MENU = [
|
||
{
|
||
id: "store",
|
||
label: "فروشگاه",
|
||
href: "#",
|
||
megaMenu: {
|
||
sections: [
|
||
{
|
||
label: "اکسسوری",
|
||
href: "#",
|
||
id: "accessories",
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/accessories/Elegant-Watch-on-Womans-Wrist-2.png",
|
||
items: [
|
||
{ label: "کیف و کوله", href: "#" },
|
||
{ label: "کلاه", href: "#" },
|
||
{ label: "کمربند", href: "#" },
|
||
{ label: "جواهرات", href: "#" },
|
||
{ label: "عینک آفتابی", href: "#" },
|
||
{ label: "ساعت", href: "#" },
|
||
],
|
||
},
|
||
{
|
||
label: "مردانه",
|
||
href: "#",
|
||
id: "men",
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/pexels-cottonbro-6764036-2.jpg",
|
||
items: [
|
||
{ label: "تازهها", href: "#" },
|
||
{ label: "تیشرت", href: "#" },
|
||
{ label: "پیراهن", href: "#" },
|
||
{ label: "کت و کاپشن", href: "#" },
|
||
{ label: "شلوار", href: "#" },
|
||
{ label: "کفش", href: "#" },
|
||
],
|
||
},
|
||
{
|
||
label: "زنانه",
|
||
href: "#",
|
||
id: "women",
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/pexels-429124762-19881454-2.jpg",
|
||
items: [
|
||
{ label: "تازهها", href: "#" },
|
||
{ label: "لباس", href: "#" },
|
||
{ label: "تاپ و بلوز", href: "#" },
|
||
{ label: "پوشاک بیرونی", href: "#" },
|
||
{ label: "دامن", href: "#" },
|
||
{ label: "کفش", href: "#" },
|
||
],
|
||
},
|
||
],
|
||
featuredItems: [
|
||
{
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/Summer-Fashion-Duo-2.png",
|
||
href: "#",
|
||
label: "پیشنهاد تابستانه",
|
||
},
|
||
{
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/Contemplative-Casual-Chic-2.png",
|
||
href: "#",
|
||
label: "پرفروشترینها",
|
||
},
|
||
],
|
||
},
|
||
},
|
||
{
|
||
id: "collections",
|
||
label: "مجموعهها",
|
||
href: "#",
|
||
megaMenu: {
|
||
sections: [
|
||
{
|
||
label: "بر اساس سبک",
|
||
href: "#",
|
||
id: "by-style",
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/Man-in-White-Hoodie-1.png",
|
||
items: [
|
||
{ label: "مینیمال", href: "#" },
|
||
{ label: "کلاسیک", href: "#" },
|
||
{ label: "مدرن", href: "#" },
|
||
{ label: "راحتی", href: "#" },
|
||
{ label: "رسمی", href: "#" },
|
||
{ label: "ورزشی", href: "#" },
|
||
],
|
||
},
|
||
{
|
||
label: "بر اساس مناسبت",
|
||
href: "#",
|
||
id: "by-occasion",
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/Chic-Fashion-Portrait-2.png",
|
||
items: [
|
||
{ label: "کار و اداری", href: "#" },
|
||
{ label: "آخر هفته", href: "#" },
|
||
{ label: "مهمانی", href: "#" },
|
||
{ label: "سفر", href: "#" },
|
||
{ label: "روزمره", href: "#" },
|
||
{ label: "فصل جدید", href: "#" },
|
||
],
|
||
},
|
||
{
|
||
label: "مجموعههای ویژه",
|
||
href: "#",
|
||
id: "curated",
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/Modern-Fashion-Portrait-2.png",
|
||
items: [
|
||
{ label: "محصولات ضروری", href: "#" },
|
||
{ label: "پرفروشترینها", href: "#" },
|
||
{ label: "نسخه محدود", href: "#" },
|
||
{ label: "ستهای آماده", href: "#" },
|
||
{ label: "هدایای منتخب", href: "#" },
|
||
{ label: "همکاریهای ویژه", href: "#" },
|
||
],
|
||
},
|
||
],
|
||
featuredItems: [
|
||
{
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/Man-in-White-Hoodie-1.png",
|
||
href: "#",
|
||
label: "محصولات ضروری",
|
||
},
|
||
{
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/Man-in-White-Hoodie-1.png",
|
||
href: "#",
|
||
label: "کلاسیک مینیمال",
|
||
},
|
||
{
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/Man-in-White-Hoodie-1.png",
|
||
href: "#",
|
||
label: "لباس کار مدرن",
|
||
},
|
||
{
|
||
imageSrc:
|
||
"/block-media/shadcnblocks/block/ecommerce/clothes/Man-in-White-Hoodie-1.png",
|
||
href: "#",
|
||
label: "لباس آخر هفته",
|
||
},
|
||
],
|
||
},
|
||
},
|
||
{
|
||
label: "حراج",
|
||
href: "#",
|
||
accentColor: "oklch(58.6% 0.253 17.585)",
|
||
},
|
||
{
|
||
label: "وبلاگ",
|
||
href: "#",
|
||
},
|
||
];
|
||
|
||
const HOME = {
|
||
href: "https://ShadcnBlocks",
|
||
logo: {
|
||
src: "/block-media/shadcnblocks/block/logos/ShadcnBlockscom-icon.svg",
|
||
alt: "لوگو",
|
||
},
|
||
};
|
||
|
||
const EcommerceNavbar1 = ({
|
||
menu = MENU,
|
||
home = HOME,
|
||
helpfulLinks = HELPFULL_LINKS,
|
||
socialLinks = SOCIAL_MEDIA_LINKS,
|
||
contactInfo = CONTACT_INFO,
|
||
className,
|
||
}: EcommerceNavbar1Props) => {
|
||
const isMobile = useIsMobile();
|
||
|
||
return (
|
||
<div dir="rtl" lang="fa" className={cn("relative w-full overflow-visible text-start", className)}>
|
||
{isMobile ? (
|
||
<div className="md:hidden">
|
||
<MobileNav
|
||
menu={menu}
|
||
helpfulLinks={helpfulLinks}
|
||
socialLinks={socialLinks}
|
||
contactInfo={contactInfo}
|
||
/>
|
||
</div>
|
||
) : (
|
||
<NavigationMenu
|
||
dir="rtl"
|
||
viewport={false}
|
||
className="relative z-50 w-full max-w-full overflow-visible border-b max-md:hidden [&>div]:w-full"
|
||
>
|
||
<NavigationMenuList asChild className="h-fit min-h-17.5 px-7.5">
|
||
<div className="flex items-center gap-6 overflow-visible">
|
||
<div>
|
||
<Logo url={home.href}>
|
||
<LogoImage
|
||
src={home.logo.src}
|
||
alt={home.logo.alt}
|
||
className="size-8 dark:invert"
|
||
/>
|
||
<LogoTextDesktop className="font-medium text-foreground">
|
||
ری یو آی
|
||
</LogoTextDesktop>
|
||
</Logo>
|
||
</div>
|
||
<div className="flex items-center gap-1">
|
||
{menu.map((item, index) => (
|
||
<NavigationMenuItem key={index} className="static">
|
||
<MenuItem {...item} />
|
||
</NavigationMenuItem>
|
||
))}
|
||
</div>
|
||
<div className="ms-auto flex items-center gap-4">
|
||
<CountrySelector />
|
||
<SecondaryNav />
|
||
</div>
|
||
</div>
|
||
</NavigationMenuList>
|
||
</NavigationMenu>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const MenuItem = ({ label, href, megaMenu, accentColor }: MenuItemProps) => {
|
||
const menuLinkStyle = "text-xs font-semibold px-4";
|
||
if (!megaMenu) {
|
||
return (
|
||
<NavigationMenuLink
|
||
asChild
|
||
style={{
|
||
color: accentColor,
|
||
}}
|
||
className={menuLinkStyle}
|
||
>
|
||
<a href={href}>{label}</a>
|
||
</NavigationMenuLink>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<Fragment>
|
||
<NavigationMenuTrigger
|
||
style={{
|
||
color: accentColor,
|
||
}}
|
||
className={menuLinkStyle}
|
||
>
|
||
{label}
|
||
</NavigationMenuTrigger>
|
||
<NavigationMenuContent
|
||
className={clsx(
|
||
"inset-x-0 start-0 end-0",
|
||
"z-50 mt-0! h-auto max-h-none min-h-0 rounded-none! p-0",
|
||
"!overflow-visible shadow-md",
|
||
"group-data-[viewport=false]/navigation-menu:!overflow-visible",
|
||
"group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-none! group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-none!",
|
||
)}
|
||
>
|
||
<div className="mx-auto w-full max-w-7xl px-7.5 py-10.5">
|
||
<div className="relative flex items-start justify-center gap-6 overflow-visible">
|
||
{megaMenu?.sections && (
|
||
<div
|
||
className={clsx(
|
||
"relative z-10 min-w-0",
|
||
megaMenu?.featuredItems && "basis-[60%]",
|
||
)}
|
||
>
|
||
<MenuLinksList lists={megaMenu?.sections} />
|
||
</div>
|
||
)}
|
||
{megaMenu?.featuredItems && (
|
||
<div
|
||
className={clsx(
|
||
"relative z-0 shrink-0",
|
||
megaMenu?.sections ? "basis-[40%]" : "flex-1",
|
||
)}
|
||
>
|
||
<MenuFeaturedLinks lists={megaMenu?.featuredItems} />
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</NavigationMenuContent>
|
||
</Fragment>
|
||
);
|
||
};
|
||
|
||
const MenuLinksList = ({ lists }: MenuLinksListProps) => {
|
||
if (!lists) return;
|
||
|
||
return (
|
||
<ul className="relative z-10 flex gap-7.5">
|
||
{lists.map((list, index) => (
|
||
<li key={index} className="basis-1/3">
|
||
<MenuLinks list={list} />
|
||
</li>
|
||
))}
|
||
</ul>
|
||
);
|
||
};
|
||
|
||
const MenuLinks = ({ list }: MenuLinksProps) => {
|
||
const { label, href, items } = list;
|
||
|
||
return (
|
||
<div className="space-y-1.5">
|
||
<h2 className="px-1 text-xs leading-relaxed font-semibold">
|
||
<a href={href}>{label}</a>
|
||
</h2>
|
||
<ul>
|
||
{items.map((item, index) => (
|
||
<li key={index}>
|
||
<a
|
||
className="block w-full rounded-sm px-1 py-0.5 text-xs leading-normal text-muted-foreground hover:bg-muted"
|
||
href={item.href}
|
||
>
|
||
{item.label}
|
||
</a>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const MenuFeaturedLinks = ({ lists }: MenuFeaturedLinksProps) => {
|
||
if (!lists) return;
|
||
|
||
return (
|
||
<div className="relative z-0 flex gap-7.5">
|
||
{lists.map((item, index) => (
|
||
<div className="flex-1" key={index}>
|
||
<MenuFeaturedLink {...item} />
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const MenuFeaturedLink = ({ href, imageSrc, label }: MenuFeaturedLinkProps) => {
|
||
return (
|
||
<a href={href} className="group/feat-link relative z-0 block space-y-3">
|
||
<AspectRatio className="relative overflow-hidden">
|
||
<img
|
||
src={imageSrc}
|
||
alt={label}
|
||
className="block size-full object-cover object-center transition-all duration-500 group-hover/feat-link:scale-105 group-hover/feat-link:opacity-80"
|
||
/>
|
||
</AspectRatio>
|
||
<div className="text-center text-xs leading-relaxed font-semibold">
|
||
{label}
|
||
</div>
|
||
</a>
|
||
);
|
||
};
|
||
|
||
const SecondaryNav = () => {
|
||
return (
|
||
<div className="flex items-center gap-1">
|
||
<Button variant="ghost" size="icon" aria-label="حساب کاربری">
|
||
<CircleUserRound />
|
||
</Button>
|
||
<div className="relative">
|
||
<Button variant="ghost" size="icon" aria-label="سبد خرید">
|
||
<ShoppingBag />
|
||
</Button>
|
||
<div className="absolute -top-4 start-1/2 -translate-x-1/2">
|
||
<Badge className="text-[0.5rem]">۰</Badge>
|
||
</div>
|
||
</div>
|
||
<Button variant="ghost" size="icon" aria-label="جستجو">
|
||
<Search />
|
||
</Button>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const CountrySelector = ({ className }: { className?: string }) => {
|
||
return (
|
||
<Select defaultValue="IR">
|
||
<SelectTrigger
|
||
className={cn(
|
||
"w-42 border-none text-xs leading-relaxed font-semibold text-muted-foreground shadow-none",
|
||
className,
|
||
)}
|
||
>
|
||
<SelectValue placeholder="انتخاب کشور" />
|
||
</SelectTrigger>
|
||
<SelectContent align="start">
|
||
<SelectItem value="IR">ایران (تومان)</SelectItem>
|
||
<SelectItem value="US">آمریکا (ریال)</SelectItem>
|
||
<SelectItem value="FR">فرانسه (یورو)</SelectItem>
|
||
<SelectItem value="AUS">استرالیا (ریال)</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
);
|
||
};
|
||
|
||
const MobileNav = ({
|
||
menu,
|
||
socialLinks,
|
||
helpfulLinks,
|
||
contactInfo,
|
||
}: MobileNavProps) => {
|
||
const [open, setOpen] = useState(false);
|
||
const [openMegaMenu, setOpenMegaMenu] = useState(false);
|
||
const [openSubMenu, setOpenSubMenu] = useState(false);
|
||
|
||
const [menuItem, setMenuItem] = useState<MenuItemType>();
|
||
const [subMenu, setSubMenu] = useState<MegaMenuSectionType>();
|
||
|
||
usePreventScrollLock(open);
|
||
|
||
const closeAll = () => {
|
||
setOpen(false);
|
||
setOpenMegaMenu(false);
|
||
setOpenSubMenu(false);
|
||
};
|
||
|
||
const handleMegaMenu = (value: boolean) => {
|
||
setOpenMegaMenu(value);
|
||
};
|
||
|
||
const handleSheet = () => {
|
||
setOpen((state) => !state);
|
||
};
|
||
|
||
const handleOnMenuItemClick = (id?: string) => {
|
||
const foundMenu = menu.find((item) => item.id === id);
|
||
|
||
setMenuItem(foundMenu);
|
||
setOpenMegaMenu(true);
|
||
};
|
||
|
||
const handleSubMenu = (value: boolean) => {
|
||
setOpenSubMenu(value);
|
||
};
|
||
|
||
const handleOnSubMenuClick = (id?: string) => {
|
||
const foundMenu = menuItem?.megaMenu?.sections?.find(
|
||
(item) => item.id === id,
|
||
);
|
||
|
||
setSubMenu(foundMenu);
|
||
setOpenSubMenu(true);
|
||
};
|
||
|
||
const linkStyle =
|
||
"flex w-full py-4 px-2 justify-between hover:bg-inherit rounded-none h-fit border-b font-semibold hover:no-underline text-sm leading-relaxed";
|
||
|
||
return (
|
||
<Fragment>
|
||
<nav className="h-15 border-b bg-background">
|
||
<div className="flex h-full items-center gap-4 px-4">
|
||
<Logo url="https://ShadcnBlocks">
|
||
<LogoImage
|
||
src="/block-media/shadcnblocks/block/logos/ShadcnBlockscom-icon.svg"
|
||
alt="لوگو"
|
||
className="size-8 dark:invert"
|
||
/>
|
||
</Logo>
|
||
<div className="ms-auto flex items-center gap-2">
|
||
<SecondaryNav />
|
||
<Button onClick={handleSheet} size="icon" variant="ghost" aria-label="باز کردن منو">
|
||
<Menu />
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
<Sheet open={open} onOpenChange={setOpen}>
|
||
<SheetContent
|
||
style={
|
||
{
|
||
"--padding-top": "1.25rem",
|
||
"--header-height": "2.25rem",
|
||
"--footer-height": "4rem",
|
||
"--sheet-width": "27.75rem",
|
||
} as CSSProperties
|
||
}
|
||
side="right"
|
||
className="w-full sm:max-w-[var(--sheet-width)] [&>button]:hidden"
|
||
aria-describedby={undefined}
|
||
onInteractOutside={(e: Event) => {
|
||
if (openMegaMenu) {
|
||
e.preventDefault();
|
||
}
|
||
}}
|
||
>
|
||
<div className="h-full pt-5">
|
||
<SheetHeader className="h-9 p-0 px-6">
|
||
<SheetTitle className="sr-only">منوی موبایل</SheetTitle>
|
||
<div className="flex items-center justify-between gap-5">
|
||
<SheetClose asChild>
|
||
<Button size="icon" variant="ghost" aria-label="بستن منو">
|
||
<X />
|
||
</Button>
|
||
</SheetClose>
|
||
<Button variant="ghost">
|
||
<CircleUserRound />
|
||
ورود
|
||
</Button>
|
||
</div>
|
||
</SheetHeader>
|
||
<div className="h-[calc(100dvh-var(--padding-top)-var(--header-height)-var(--footer-height))] overflow-auto">
|
||
<div className="px-6 py-5">
|
||
<div>
|
||
{menu.map(
|
||
({ megaMenu, id, label, accentColor, href }, index) =>
|
||
megaMenu ? (
|
||
<Button
|
||
key={index}
|
||
className={linkStyle}
|
||
variant="ghost"
|
||
onClick={() => handleOnMenuItemClick(id)}
|
||
>
|
||
{label}
|
||
<ChevronRight className="rtl:rotate-180" />
|
||
</Button>
|
||
) : (
|
||
<a
|
||
style={{
|
||
color: accentColor,
|
||
}}
|
||
className={linkStyle}
|
||
href={href}
|
||
key={index}
|
||
>
|
||
{label}
|
||
</a>
|
||
),
|
||
)}
|
||
</div>
|
||
<div className="mt-6 space-y-5">
|
||
<HelpfullLinks links={helpfulLinks} />
|
||
<div>
|
||
<ContactInfoList links={contactInfo} />
|
||
<SocialMediaSection links={socialLinks} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<SheetFooter className="absolute inset-x-0 bottom-0 border-t bg-background px-6 py-4">
|
||
<CountrySelector className="w-full" />
|
||
</SheetFooter>
|
||
</SheetContent>
|
||
</Sheet>
|
||
<MegaMenuMobile
|
||
menuItem={menuItem}
|
||
open={openMegaMenu}
|
||
onOpenChange={handleMegaMenu}
|
||
openSubMenu={openSubMenu}
|
||
handleOnSubMenuClick={handleOnSubMenuClick}
|
||
linkStyle={linkStyle}
|
||
closeAll={closeAll}
|
||
/>
|
||
<SubMenuMobile
|
||
subMenu={subMenu}
|
||
onOpenChange={handleSubMenu}
|
||
open={openSubMenu}
|
||
linkStyle={linkStyle}
|
||
closeAll={closeAll}
|
||
/>
|
||
</Fragment>
|
||
);
|
||
};
|
||
|
||
const MegaMenuMobile = ({
|
||
menuItem,
|
||
open,
|
||
onOpenChange,
|
||
openSubMenu,
|
||
handleOnSubMenuClick,
|
||
linkStyle,
|
||
closeAll,
|
||
}: MegaMenuMobileProps) => {
|
||
if (!menuItem) return;
|
||
|
||
return (
|
||
<Fragment>
|
||
<Sheet open={open} onOpenChange={onOpenChange} modal={false}>
|
||
<SheetContent
|
||
side="right"
|
||
aria-describedby={undefined}
|
||
className="w-full gap-0 overflow-auto px-6 pt-5 pb-10 sm:max-w-[27.75rem] [&>button]:hidden"
|
||
onInteractOutside={(e: Event) => {
|
||
if (openSubMenu) {
|
||
e.preventDefault();
|
||
}
|
||
}}
|
||
>
|
||
<SheetHeader className="p-0">
|
||
<div className="flex items-center justify-between gap-5">
|
||
<Button onClick={closeAll} size="icon" variant="ghost" aria-label="بستن منو">
|
||
<X />
|
||
</Button>
|
||
</div>
|
||
<div className="flex items-center py-4">
|
||
<Button
|
||
onClick={(e: React.MouseEvent) => {
|
||
e.stopPropagation();
|
||
onOpenChange(false);
|
||
}}
|
||
variant="ghost"
|
||
aria-label="بازگشت"
|
||
>
|
||
<ChevronLeft className="rtl:rotate-180" />
|
||
</Button>
|
||
<SheetTitle className="flex-1 text-center text-sm">
|
||
{menuItem.label}
|
||
</SheetTitle>
|
||
</div>
|
||
</SheetHeader>
|
||
<div className="space-y-6">
|
||
<div>
|
||
{menuItem?.megaMenu?.sections?.map(({ label, id }, index) => (
|
||
<Button
|
||
onClick={() => {
|
||
handleOnSubMenuClick(id);
|
||
}}
|
||
key={index}
|
||
className={linkStyle}
|
||
variant="ghost"
|
||
>
|
||
{label}
|
||
<ChevronRight className="rtl:rotate-180" />
|
||
</Button>
|
||
))}
|
||
</div>
|
||
<div className="grid grid-cols-2 gap-4.5">
|
||
{menuItem?.megaMenu?.featuredItems?.map((item, index) => (
|
||
<MenuFeaturedLink key={index} {...item} />
|
||
))}
|
||
</div>
|
||
<Button size="lg" className="w-full" variant="outline">
|
||
<a href={menuItem?.href}>مشاهده {menuItem?.label}</a>
|
||
</Button>
|
||
</div>
|
||
</SheetContent>
|
||
</Sheet>
|
||
</Fragment>
|
||
);
|
||
};
|
||
|
||
const SubMenuMobile = ({
|
||
subMenu,
|
||
open,
|
||
onOpenChange,
|
||
linkStyle,
|
||
closeAll,
|
||
}: SubMenuMobileProps) => {
|
||
if (!subMenu) return;
|
||
|
||
return (
|
||
<Sheet open={open} onOpenChange={onOpenChange} modal={false}>
|
||
<SheetContent
|
||
side="right"
|
||
aria-describedby={undefined}
|
||
className="w-full gap-0 overflow-auto sm:max-w-[27.75rem] [&>button]:hidden"
|
||
>
|
||
<div>
|
||
<AspectRatio ratio={1.77} className="overflow-hidden">
|
||
<img
|
||
src={subMenu.imageSrc}
|
||
alt={subMenu.label}
|
||
className="block size-full object-cover object-center"
|
||
/>
|
||
</AspectRatio>
|
||
</div>
|
||
<div className="px-6 pt-5 pb-10">
|
||
<SheetHeader className="p-0">
|
||
<div className="flex items-center justify-between gap-5">
|
||
<Button onClick={closeAll} size="icon" variant="ghost" aria-label="بستن منو">
|
||
<X />
|
||
</Button>
|
||
</div>
|
||
<div className="flex items-center py-4">
|
||
<Button
|
||
onClick={(e: React.MouseEvent) => {
|
||
e.stopPropagation();
|
||
onOpenChange(false);
|
||
}}
|
||
variant="ghost"
|
||
aria-label="بازگشت"
|
||
>
|
||
<ChevronLeft className="rtl:rotate-180" />
|
||
</Button>
|
||
<SheetTitle className="flex-1 text-center text-sm">
|
||
{subMenu.label}
|
||
</SheetTitle>
|
||
</div>
|
||
</SheetHeader>
|
||
<div className="space-y-6">
|
||
<div>
|
||
{subMenu?.items?.map(({ label, href }, index) => (
|
||
<Button
|
||
asChild
|
||
key={index}
|
||
className={linkStyle}
|
||
variant="ghost"
|
||
>
|
||
<a href={href}>
|
||
{label}
|
||
<ChevronRight className="rtl:rotate-180" />
|
||
</a>
|
||
</Button>
|
||
))}
|
||
</div>
|
||
<Button size="lg" className="w-full" variant="outline">
|
||
<a href={subMenu?.href}>مشاهده {subMenu?.label}</a>
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</SheetContent>
|
||
</Sheet>
|
||
);
|
||
};
|
||
|
||
const SocialMediaSection = ({ links }: SocialMediaSectionProps) => {
|
||
if (!links) return;
|
||
|
||
return (
|
||
<ul className="flex flex-wrap">
|
||
{links.map(({ icon, href }) => (
|
||
<li key={crypto.randomUUID()}>
|
||
<Button size="icon" variant="ghost" asChild className="rounded-full">
|
||
<a href={href}>
|
||
<img
|
||
className="hidden size-4 dark:block"
|
||
alt={icon.title}
|
||
src={icon.dark}
|
||
/>
|
||
<img
|
||
className="size-4 dark:hidden"
|
||
alt={icon.title}
|
||
src={icon.light}
|
||
/>
|
||
</a>
|
||
</Button>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
);
|
||
};
|
||
|
||
const HelpfullLinks = ({ links }: HelpfullLinksProps) => {
|
||
if (!links) return;
|
||
|
||
return (
|
||
<ul className="flex flex-col">
|
||
{links.map(({ href, label }, index) => (
|
||
<Button
|
||
asChild
|
||
key={index}
|
||
className="justify-start px-1 text-xs font-semibold text-muted-foreground"
|
||
variant="ghost"
|
||
>
|
||
<a href={href}>{label}</a>
|
||
</Button>
|
||
))}
|
||
</ul>
|
||
);
|
||
};
|
||
|
||
const ContactInfoList = ({ links }: ContactInfoListProps) => {
|
||
if (!links) return;
|
||
|
||
return (
|
||
<ul className="flex flex-col">
|
||
{links.map((item, index) => (
|
||
<Button
|
||
asChild
|
||
key={index}
|
||
className="justify-start px-1 text-xs font-semibold text-muted-foreground"
|
||
variant="ghost"
|
||
>
|
||
<a href={item.href}>
|
||
<item.icon />
|
||
{item.label}
|
||
</a>
|
||
</Button>
|
||
))}
|
||
</ul>
|
||
);
|
||
};
|
||
|
||
export { EcommerceNavbar1 };
|