create banner
This commit is contained in:
+264
-82
@@ -1,7 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { categories } from "@/data/sample";
|
||||
import { createPortal } from "react-dom";
|
||||
import type { Category, CategoryGroup } from "@/lib/meshkee";
|
||||
import { EmptyState } from "@/components/ui/EmptyState";
|
||||
|
||||
const navLinks = [
|
||||
{ label: "فروش ویژه", href: "#" },
|
||||
@@ -10,31 +12,202 @@ const navLinks = [
|
||||
{ label: "تماس با ما", href: "#" },
|
||||
];
|
||||
|
||||
const megaColumns = [
|
||||
{
|
||||
title: "سرمایش و گرمایش",
|
||||
en: "Cooling & Heating",
|
||||
items: ["یخچال و فریزر", "کولر گازی", "آبگرمکن", "بخاری"],
|
||||
},
|
||||
{
|
||||
title: "شستشو",
|
||||
en: "Laundry & Care",
|
||||
items: ["ماشین لباسشویی", "ماشین ظرفشویی", "خشککن", "اتو بخار"],
|
||||
},
|
||||
{
|
||||
title: "صوتی و تصویری",
|
||||
en: "AV & Display",
|
||||
items: ["تلویزیون", "ساندبار", "سینمای خانگی", "پروژکتور"],
|
||||
},
|
||||
{
|
||||
title: "آشپزخانه",
|
||||
en: "Kitchen",
|
||||
items: ["مایکروویو", "جاروبرقی", "هود", "اجاق گاز"],
|
||||
},
|
||||
];
|
||||
function Logo({ large = false }: { large?: boolean }) {
|
||||
return (
|
||||
<a href="/" className="flex shrink-0 items-center">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src="/images/logo.png"
|
||||
alt="سانیهوم"
|
||||
className={
|
||||
large
|
||||
? "h-[42px] w-auto object-contain md:h-[46px]"
|
||||
: "h-8 w-auto md:h-9"
|
||||
}
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
function ChevronDownIcon({ className = "" }: { className?: string }) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" className={className} aria-hidden>
|
||||
<path
|
||||
d="m6 9 6 6 6-6"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function SearchBar() {
|
||||
return (
|
||||
<div className="flex overflow-hidden rounded-xl border border-border bg-background">
|
||||
<input
|
||||
type="search"
|
||||
placeholder="جستجوی محصول، برند یا دستهبندی..."
|
||||
className="flex-1 bg-transparent px-4 py-2.5 text-sm outline-none placeholder:text-muted"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-red px-5 text-sm font-medium text-white transition-colors hover:bg-red-dark"
|
||||
>
|
||||
جستجو
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileDrawer({
|
||||
open,
|
||||
onClose,
|
||||
categoryGroups,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
categoryGroups: CategoryGroup[];
|
||||
}) {
|
||||
const [categoriesOpen, setCategoriesOpen] = useState(false);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.style.overflow = open ? "hidden" : "";
|
||||
return () => {
|
||||
document.body.style.overflow = "";
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") onClose();
|
||||
};
|
||||
document.addEventListener("keydown", onKeyDown);
|
||||
return () => document.removeEventListener("keydown", onKeyDown);
|
||||
}, [onClose]);
|
||||
|
||||
if (!mounted) return null;
|
||||
|
||||
return createPortal(
|
||||
<div className="min-[1200px]:hidden" aria-hidden={!open}>
|
||||
<div
|
||||
onClick={onClose}
|
||||
className={`fixed inset-0 z-[100] bg-black/50 backdrop-blur-sm transition-opacity duration-300 ${
|
||||
open ? "opacity-100" : "pointer-events-none opacity-0"
|
||||
}`}
|
||||
/>
|
||||
<div
|
||||
className={`fixed top-0 right-0 z-[110] h-dvh w-[85%] max-w-sm overflow-y-auto bg-surface shadow-2xl transition-transform duration-300 ease-out ${
|
||||
open ? "translate-x-0" : "translate-x-full"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between border-b border-border p-5">
|
||||
<Logo />
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
aria-label="بستن منو"
|
||||
className="flex h-9 w-9 items-center justify-center rounded-lg text-xl text-muted transition-colors hover:bg-red-light hover:text-red"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav className="flex flex-col text-sm">
|
||||
<a href="/" className="border-b border-border px-5 py-4 transition-colors hover:bg-red-light/40 hover:text-red">
|
||||
خانه
|
||||
</a>
|
||||
|
||||
<div className="border-b border-border">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCategoriesOpen((v) => !v)}
|
||||
aria-expanded={categoriesOpen}
|
||||
className="flex w-full items-center justify-between px-5 py-4 text-right transition-colors hover:bg-red-light/40 hover:text-red"
|
||||
>
|
||||
دستهبندیها
|
||||
<ChevronDownIcon
|
||||
className={`h-4 w-4 shrink-0 transition-transform duration-200 ${
|
||||
categoriesOpen ? "rotate-180" : ""
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className={`grid transition-all duration-300 ease-out ${
|
||||
categoriesOpen ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
|
||||
}`}
|
||||
>
|
||||
<div className="overflow-hidden">
|
||||
{categoryGroups.length === 0 ? (
|
||||
<div className="bg-background px-5 pb-4">
|
||||
<EmptyState message="هنوز دستهبندیای ثبت نشده است." />
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4 bg-background px-5 pb-4">
|
||||
{categoryGroups.map((group) => (
|
||||
<div key={group.id}>
|
||||
<p className="mb-1.5 pt-3 text-xs font-bold text-red-dark">
|
||||
{group.title}
|
||||
</p>
|
||||
<ul className="space-y-1">
|
||||
{group.items.map((item) => (
|
||||
<li key={item.name}>
|
||||
<a
|
||||
href="#"
|
||||
className="block rounded-lg px-3 py-1.5 text-muted transition-colors hover:bg-red-light hover:text-red"
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{navLinks.map((link) => (
|
||||
<a
|
||||
key={link.label}
|
||||
href={link.href}
|
||||
className="border-b border-border px-5 py-4 transition-colors hover:bg-red-light/40 hover:text-red"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-5 py-4 text-right font-medium transition-colors hover:bg-red-light/40 hover:text-red"
|
||||
>
|
||||
<span aria-hidden>👤</span>
|
||||
ورود
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
|
||||
export function Header({
|
||||
categoryGroups,
|
||||
categories,
|
||||
}: {
|
||||
categoryGroups: CategoryGroup[];
|
||||
categories: Category[];
|
||||
}) {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -50,37 +223,31 @@ export function Header() {
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b border-border bg-surface/95 backdrop-blur-sm">
|
||||
<div className="mx-auto max-w-7xl px-4">
|
||||
<div className="flex items-center justify-between gap-4 py-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-red text-lg font-bold text-white">
|
||||
S
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-display text-xl font-bold tracking-tight">
|
||||
<span className="text-red">SANI</span>
|
||||
<span className="text-foreground">HOME</span>
|
||||
</p>
|
||||
<p className="text-xs text-muted">فروشگاه لوازم خانگی</p>
|
||||
<div className="grid grid-cols-3 items-center gap-4 py-4 min-[1200px]:flex min-[1200px]:justify-between">
|
||||
<div className="flex items-center justify-start">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDrawerOpen(true)}
|
||||
aria-label="منو"
|
||||
className="flex h-10 w-10 items-center justify-center rounded-lg border border-border text-xl transition-colors hover:border-red hover:text-red min-[1200px]:hidden"
|
||||
>
|
||||
<span aria-hidden>☰</span>
|
||||
</button>
|
||||
<div className="hidden min-[1200px]:block">
|
||||
<Logo />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden flex-1 px-6 md:block">
|
||||
<div className="flex overflow-hidden rounded-xl border border-border bg-background">
|
||||
<input
|
||||
type="search"
|
||||
placeholder="جستجوی محصول، برند یا دستهبندی..."
|
||||
className="flex-1 bg-transparent px-4 py-2.5 text-sm outline-none placeholder:text-muted"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-red px-5 text-sm font-medium text-white transition-colors hover:bg-red-dark"
|
||||
>
|
||||
جستجو
|
||||
</button>
|
||||
<div className="flex items-center justify-center min-[1200px]:flex-1 min-[1200px]:px-6">
|
||||
<div className="min-[1200px]:hidden">
|
||||
<Logo large />
|
||||
</div>
|
||||
<div className="hidden w-full min-[1200px]:block">
|
||||
<SearchBar />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 text-sm">
|
||||
<div className="flex items-center justify-end gap-3 text-sm">
|
||||
<button
|
||||
type="button"
|
||||
className="hidden items-center gap-1.5 text-muted transition-colors hover:text-red sm:flex"
|
||||
@@ -101,7 +268,11 @@ export function Header() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="relative flex items-center gap-6 overflow-visible border-t border-border py-3 text-sm">
|
||||
<div className="border-t border-border py-3 min-[1200px]:hidden">
|
||||
<SearchBar />
|
||||
</div>
|
||||
|
||||
<nav className="relative hidden items-center gap-6 overflow-visible border-t border-border py-3 text-sm min-[1200px]:flex">
|
||||
<div
|
||||
ref={menuRef}
|
||||
className="relative"
|
||||
@@ -133,40 +304,45 @@ export function Header() {
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{megaColumns.map((column) => (
|
||||
<div key={column.title}>
|
||||
<p className="mb-0.5 text-sm font-bold text-red-dark">{column.title}</p>
|
||||
<p className="font-display mb-3 text-[10px] tracking-wider text-muted uppercase">
|
||||
{column.en}
|
||||
</p>
|
||||
<ul className="space-y-2">
|
||||
{column.items.map((item) => (
|
||||
<li key={item}>
|
||||
<a
|
||||
href="#"
|
||||
className="text-sm text-muted transition-colors hover:text-red"
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{categoryGroups.length === 0 ? (
|
||||
<EmptyState message="هنوز دستهبندیای ثبت نشده است." />
|
||||
) : (
|
||||
<>
|
||||
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{categoryGroups.map((column) => (
|
||||
<div key={column.id}>
|
||||
<p className="mb-3 text-sm font-bold text-red-dark">{column.title}</p>
|
||||
<ul className="space-y-2">
|
||||
{column.items.map((item) => (
|
||||
<li key={item.name}>
|
||||
<a
|
||||
href="#"
|
||||
className="text-sm text-muted transition-colors hover:text-red"
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-5 grid grid-cols-2 gap-2 border-t border-border pt-4 sm:grid-cols-4">
|
||||
{categories.slice(0, 4).map((cat) => (
|
||||
<a
|
||||
key={cat.id}
|
||||
href="#"
|
||||
className="rounded-lg bg-red-light/50 px-3 py-2 text-center text-xs font-medium text-red-dark transition-colors hover:bg-red-light"
|
||||
>
|
||||
{cat.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
{categories.length > 0 && (
|
||||
<div className="mt-5 grid grid-cols-2 gap-2 border-t border-border pt-4 sm:grid-cols-4">
|
||||
{categories.slice(0, 4).map((cat) => (
|
||||
<a
|
||||
key={cat.id}
|
||||
href="#"
|
||||
className="rounded-lg bg-red-light/50 px-3 py-2 text-center text-xs font-medium text-red-dark transition-colors hover:bg-red-light"
|
||||
>
|
||||
{cat.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -182,6 +358,12 @@ export function Header() {
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<MobileDrawer
|
||||
open={drawerOpen}
|
||||
onClose={() => setDrawerOpen(false)}
|
||||
categoryGroups={categoryGroups}
|
||||
/>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user