feat(home): search-first mobile top bar (redesign)
Replace the mobile home scroll-tab bar with the redesign's search-first chrome: logo + ویترون wordmark, notification (bell) and cart (bag) icon buttons with unread dot / count badges, and a prominent tap-to-search field that opens /explore. Mobile only (stays inside lg:hidden), so the desktop DesktopHeader is unaffected. Keeps the existing pinar font and color tokens; preserves the safe-area-inset top padding. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bac93568f5
commit
f8289898bc
@ -1,103 +1,74 @@
|
||||
import { useState, useEffect, useMemo, memo, useCallback } from "react";
|
||||
import { memo, useCallback } from "react";
|
||||
import { Link } from "@remix-run/react";
|
||||
import { Bell, Search, ShoppingBag } from "lucide-react";
|
||||
import logo from "../../assets/logo/SVG-07.svg";
|
||||
import { useCartCount } from "~/requestHandler/use-cart-hooks";
|
||||
import { useBadgeCounts } from "~/hooks/useBadgeCounts";
|
||||
import { useRootData } from "~/hooks/use-root-data";
|
||||
|
||||
const HomePageTopBar = memo(() => {
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const tabs = useMemo(() => ["تخفیفها", "برترین فروشگاهها", "برای شما"], []);
|
||||
|
||||
// Scroll to section when tab is clicked
|
||||
const scrollToSection = useCallback((sectionId: string) => {
|
||||
const section = document.getElementById(sectionId);
|
||||
if (section) {
|
||||
section.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Update active tab based on scroll position with throttling
|
||||
useEffect(() => {
|
||||
let ticking = false;
|
||||
const handleScroll = () => {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
const section1 = document.getElementById("section1");
|
||||
const section2 = document.getElementById("section2");
|
||||
const section3 = document.getElementById("section3");
|
||||
|
||||
if (section1 && section2 && section3) {
|
||||
const scrollPosition = window.scrollY;
|
||||
|
||||
if (scrollPosition >= section3.offsetTop - 100) {
|
||||
setActiveTab(2);
|
||||
} else if (scrollPosition >= section2.offsetTop - 100) {
|
||||
setActiveTab(1);
|
||||
} else {
|
||||
setActiveTab(0);
|
||||
}
|
||||
}
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
const { user } = useRootData();
|
||||
const isLoggedIn = !!user?.access_token;
|
||||
const cartCount = useCartCount();
|
||||
const { data: badgeCounts } = useBadgeCounts();
|
||||
const accountUnread =
|
||||
(badgeCounts?.chat || 0) + (badgeCounts?.order_updates || 0);
|
||||
|
||||
const scrollToTop = useCallback(() => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}, []);
|
||||
|
||||
const handleTabClick = useCallback(
|
||||
(index: number) => {
|
||||
setActiveTab(index);
|
||||
scrollToSection(`section${index + 1}`);
|
||||
},
|
||||
[scrollToSection]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 flex-row h-[calc(48px_+_env(safe-area-inset-top))] pt-[env(safe-area-inset-top)] w-full bg-WHITE items-center px-4 sticky top-0 z-30">
|
||||
<div className="flex relative items-center gap-2 justify-center w-full">
|
||||
<div
|
||||
className="absolute right-0"
|
||||
onClick={() => scrollToTop()}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
scrollToTop();
|
||||
}
|
||||
}}
|
||||
<div className="bg-WHITE sticky top-0 z-30 pt-[env(safe-area-inset-top)]">
|
||||
<div className="px-4 pt-2.5 pb-3 flex flex-col gap-3">
|
||||
{/* Brand row */}
|
||||
<div className="flex items-center gap-2.5">
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className="flex items-center gap-2"
|
||||
aria-label="ویترون"
|
||||
>
|
||||
<img src={logo} className="w-7 h-8 object-contain" alt="" />
|
||||
<b className="text-[21px] font-extrabold text-BLACK tracking-tight">
|
||||
ویترون
|
||||
</b>
|
||||
</button>
|
||||
<span className="flex-1" />
|
||||
<Link
|
||||
to="/profile"
|
||||
prefetch="intent"
|
||||
aria-label="اعلانها"
|
||||
className="relative w-10 h-10 rounded-xl bg-WHITE2 grid place-items-center text-BLACK"
|
||||
>
|
||||
{isLoggedIn && accountUnread > 0 ? (
|
||||
<span className="absolute top-2 left-2.5 w-2 h-2 rounded-full bg-RED border-[1.5px] border-WHITE2" />
|
||||
) : null}
|
||||
<Bell size={21} />
|
||||
</Link>
|
||||
<Link
|
||||
to="/cart"
|
||||
prefetch="intent"
|
||||
aria-label="سبد خرید"
|
||||
className="relative w-10 h-10 rounded-xl bg-WHITE2 grid place-items-center text-BLACK"
|
||||
>
|
||||
{isLoggedIn && cartCount > 0 ? (
|
||||
<span className="absolute top-1 left-1 min-w-[16px] h-4 px-1 rounded-full bg-RED text-white text-[10px] font-bold grid place-items-center border-[1.5px] border-WHITE2">
|
||||
{cartCount > 99 ? "99+" : cartCount}
|
||||
</span>
|
||||
) : null}
|
||||
<ShoppingBag size={21} />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Search-first */}
|
||||
<Link
|
||||
to="/explore"
|
||||
prefetch="intent"
|
||||
className="flex items-center gap-2.5 h-[46px] px-3.5 bg-WHITE3 rounded-2xl text-GRAY"
|
||||
>
|
||||
<img src={logo} className="w-8 h-8 object-contain" alt="logo" />
|
||||
</div>
|
||||
<div className="flex gap-4 justify-center">
|
||||
{tabs.map((tab, index) => (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => handleTabClick(index)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
handleTabClick(index);
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={index}
|
||||
className="flex items-center"
|
||||
>
|
||||
<p
|
||||
className={`text-B12 border-b-[2px] transition-all duration-300 ease-in-out py-1 ${
|
||||
index === activeTab
|
||||
? "text-BLACK border-BLACK font-bold"
|
||||
: "text-GRAY border-transparent"
|
||||
}`}
|
||||
>
|
||||
{tab}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Search size={19} className="shrink-0" />
|
||||
<span className="text-R14">جستجوی محصول، برند یا فروشگاه…</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user