Desktop polish: seller header contrast, collection image crop, infinite scroll
- seller: only the logo overlaps the cover now; name/handle/stats sit below the cover on white (fixes low-contrast name on the gradient) - collections (home rail, /collections grid, /collection cover): portrait 3/4 images so full-body figures aren't cropped; detail cover uses object-contain - MondrianProductList: add IntersectionObserver sentinel for reliable infinite scroll on desktop (window-scroll math missed under the footer) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
cf55e16a9a
commit
f57fe4e9d6
@ -131,7 +131,7 @@ const HorizontalCollectionsList = ({
|
||||
>
|
||||
<div className="flex flex-col gap-3 p-3 bg-WHITE2 rounded-2xl border border-inner-border hover:border-gray-300 transition-all duration-300 hover:shadow-lg h-full">
|
||||
{/* Collection Image */}
|
||||
<div className="relative overflow-hidden rounded-xl w-full h-64 bg-gray-100">
|
||||
<div className="relative overflow-hidden rounded-xl w-full h-64 lg:h-auto lg:aspect-[3/4] bg-gray-100">
|
||||
{collection.coverImageUrl ? (
|
||||
<img
|
||||
src={collection.coverImageUrl}
|
||||
|
||||
@ -87,6 +87,24 @@ const MondrianProductList = ({
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, [fetchNextPage, hasNextPage, isFetchingNextPage]);
|
||||
|
||||
// Sentinel-based trigger — more reliable than scroll math on desktop, where
|
||||
// the footer can keep the window-scroll threshold from being reached.
|
||||
const loadMoreRef = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
const el = loadMoreRef.current;
|
||||
if (!el) return;
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) {
|
||||
fetchNextPage();
|
||||
}
|
||||
},
|
||||
{ rootMargin: "600px" }
|
||||
);
|
||||
observer.observe(el);
|
||||
return () => observer.disconnect();
|
||||
}, [fetchNextPage, hasNextPage, isFetchingNextPage]);
|
||||
|
||||
// Memoize expensive filtering operations
|
||||
const { oddProducts, evenProducts } = useMemo(() => {
|
||||
const odd = products.filter((_, index) => index % 2 === 0);
|
||||
@ -177,6 +195,7 @@ const MondrianProductList = ({
|
||||
</div>
|
||||
|
||||
{hasNextPage && <MondrianSkeleton isMini={true} />}
|
||||
<div ref={loadMoreRef} className="h-px w-full" />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -143,12 +143,12 @@ export default function CollectionDetail() {
|
||||
{/* Collection Header */}
|
||||
<div className="flex flex-col gap-4 p-4 border-b border-inner-border lg:grid lg:grid-cols-[1.3fr_1fr] lg:gap-8 lg:items-center lg:p-0 lg:py-8 lg:border-0">
|
||||
{/* Cover Image */}
|
||||
<div className="relative overflow-hidden rounded-xl w-full mx-auto h-[65vh] bg-gray-100 lg:h-[460px]">
|
||||
<div className="relative overflow-hidden rounded-xl w-full mx-auto h-[65vh] bg-gray-100 lg:h-auto lg:aspect-[3/4] lg:bg-WHITE2">
|
||||
{collection.coverImageUrl ? (
|
||||
<img
|
||||
src={collection.coverImageUrl}
|
||||
alt={collection.title || "Collection"}
|
||||
className="w-full h-full object-cover"
|
||||
className="w-full h-full object-cover lg:object-contain"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center bg-WHITE2">
|
||||
|
||||
@ -148,7 +148,7 @@ export default function Collections() {
|
||||
>
|
||||
<div className="flex flex-col gap-3 p-4 bg-WHITE2 rounded-2xl border border-inner-border hover:border-gray-300 transition-all duration-300 hover:shadow-lg">
|
||||
{/* Collection Image - بزرگ */}
|
||||
<div className="relative overflow-hidden rounded-xl w-full h-48 bg-gray-100">
|
||||
<div className="relative overflow-hidden rounded-xl w-full h-48 lg:h-auto lg:aspect-[3/4] bg-gray-100">
|
||||
{collection.coverImageUrl ? (
|
||||
<img
|
||||
src={collection.coverImageUrl}
|
||||
|
||||
@ -198,8 +198,8 @@ export default function Seller() {
|
||||
<div className="relative h-[200px] rounded-[22px] overflow-hidden bg-gradient-to-br from-VITROWN_BLUE to-[#14213d]">
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/30 to-transparent" />
|
||||
</div>
|
||||
<div className="flex items-end gap-6 px-2 -mt-16 relative z-10">
|
||||
<div className="w-[130px] h-[130px] rounded-[28px] border-[5px] border-WHITE shadow-md overflow-hidden bg-WHITE3 shrink-0">
|
||||
<div className="flex items-start gap-6 px-2 mt-4 relative z-10">
|
||||
<div className="w-[130px] h-[130px] -mt-[86px] rounded-[28px] border-[5px] border-WHITE shadow-md overflow-hidden bg-WHITE3 shrink-0">
|
||||
{seller?.storeLogo ? (
|
||||
<img
|
||||
src={seller.storeLogo}
|
||||
@ -208,7 +208,7 @@ export default function Seller() {
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex-1 pb-2">
|
||||
<div className="flex-1">
|
||||
<h1 className="flex items-center gap-2 text-[28px] font-extrabold">
|
||||
{seller?.storeName}
|
||||
{seller?.isVerified ? (
|
||||
@ -246,7 +246,7 @@ export default function Seller() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2.5 pb-2">
|
||||
<div className="flex gap-2.5">
|
||||
<Button
|
||||
variant="dark"
|
||||
size="lg"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user