Desktop responsive: search page, cart list, masonry grid

- MondrianProductList: add desktop 4-column masonry (mobile 2-col unchanged);
  improves home "for you", search results and seller feed at lg+
- search: hide mobile SearchTopBar at lg, add results heading + centered
  1320px container
- cart list: responsive 2-column grid of cart cards + desktop heading
- MyLayout: desktop chrome gate now covers / , /search/* and /cart

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Arda Samadi 2026-06-14 21:06:34 +03:30
parent e1c5899002
commit 9650073b04
4 changed files with 72 additions and 18 deletions

View File

@ -96,8 +96,17 @@ const MondrianProductList = ({
const oddHeight = useMemo(() => [220, 260, 320, 200, 280], []);
const evenHeight = useMemo(() => [280, 200, 260, 220, 320], []);
// Desktop masonry: distribute products across 4 columns (round-robin).
const desktopColumns = useMemo(() => {
const cols: MondrianList[][] = [[], [], [], []];
products.forEach((p, i) => cols[i % 4].push(p));
return cols;
}, [products]);
const desktopHeights = useMemo(() => [300, 360, 260, 340, 280, 320], []);
return (
<div className="px-4 flex flex-col w-full">
<div className="px-4 lg:px-0 flex flex-col w-full">
{!isCommonList ? (
<>
{isLoading ? (
@ -113,7 +122,8 @@ const MondrianProductList = ({
/>
) : (
<>
<div className="flex gap-1 w-full">
{/* Mobile / tablet: original two-column masonry (unchanged) */}
<div className="flex gap-1 w-full lg:hidden">
{/* Column 1 (Odd products - index 0, 2, ...) */}
<div className="flex flex-col gap-1 w-[calc(50%)]">
{oddProducts.map((product, index: number) => {
@ -146,6 +156,26 @@ const MondrianProductList = ({
})}
</div>
</div>
{/* Desktop: 4-column masonry */}
<div className="hidden lg:flex gap-4 w-full">
{desktopColumns.map((col, ci) => (
<div key={ci} className="flex flex-col gap-4 flex-1 min-w-0">
{col.map((product, index) => (
<ProductCard
key={product.id}
product={product}
height={
desktopHeights[(ci + index) % desktopHeights.length]
}
storeId={storeId}
storePage={storePage}
/>
))}
</div>
))}
</div>
{hasNextPage && <MondrianSkeleton isMini={true} />}
</>
)}

View File

@ -27,10 +27,12 @@ import productsOutline from "~/assets/icons/navbar/products-outline.svg";
*/
const MyLayout = React.memo(({ children }: { children: React.ReactNode }) => {
const location = useLocation();
// Desktop chrome is rolled out per-page. Only routes listed here get the
// Desktop chrome is rolled out per-page. Only routes matched here get the
// full-width desktop layout; everything else keeps the current (mobile)
// experience at all widths so nothing regresses while we build the rest.
const isDesktopReady = location.pathname === "/";
const p = location.pathname;
const isDesktopReady =
p === "/" || p.startsWith("/search/") || p === "/cart";
const isThread = !!location.pathname.split("threads")[1];
return (

View File

@ -59,11 +59,16 @@ export default function CartIndex() {
};
return (
<div className="bg-background">
{/* Header */}
<ProfilePagesHeader hideBackButton={true} title="لیست سبد خرید" />
{/* Header (mobile) */}
<div className="lg:hidden">
<ProfilePagesHeader hideBackButton={true} title="لیست سبد خرید" />
</div>
{/* Cart Content */}
<div className="flex flex-col gap-4 p-4">
<div className="flex flex-col gap-4 p-4 lg:max-w-[1320px] lg:mx-auto lg:w-full lg:px-8 lg:py-8">
<h1 className="hidden lg:block text-[28px] font-extrabold mb-2">
سبد خرید
</h1>
<UiProvider
isLoading={isLoading}
isError={isError}
@ -73,7 +78,7 @@ export default function CartIndex() {
}}
type="cart"
>
<div className="space-y-4 w-full">
<div className="space-y-4 w-full lg:space-y-0 lg:grid lg:grid-cols-2 lg:gap-5">
{carts.map((cart) => {
return (
<div

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react";
import { MetaFunction } from "@remix-run/node";
import { useNavigate, useParams } from "@remix-run/react";
import { Link, useNavigate, useParams } from "@remix-run/react";
import { onSearch } from "../utils/helpers";
import { PackageSearch } from "lucide-react";
import { useSearchInfinite } from "../requestHandler/use-search-hooks";
@ -31,15 +31,32 @@ export default function Search() {
} = useSearchInfinite(query);
return (
<div className="flex flex-col gap-6 bg-WHITE">
<SearchTopBar
searchValue={searchValue}
setSearchValue={setSearchValue}
placeholder="جستجو..."
minimumPrice={320000}
maximumPrice={1000000}
onSearch={() => onSearch(searchValue, navigate)}
/>
<div className="flex flex-col gap-6 bg-WHITE lg:max-w-[1320px] lg:mx-auto lg:w-full lg:px-8 lg:py-6">
<div className="lg:hidden">
<SearchTopBar
searchValue={searchValue}
setSearchValue={setSearchValue}
placeholder="جستجو..."
minimumPrice={320000}
maximumPrice={1000000}
onSearch={() => onSearch(searchValue, navigate)}
/>
</div>
{/* Desktop results heading */}
<div className="hidden lg:block">
<nav className="flex items-center gap-2 text-[13px] text-GRAY">
<Link to="/" className="hover:text-BLACK">
خانه
</Link>
<span>/</span>
<span>جستجو</span>
</nav>
<h1 className="text-[26px] font-extrabold mt-2">
نتایج برای «<span className="text-VITROWN_BLUE">{query}</span>»
</h1>
</div>
<MondrianProductList
products={
data?.pages