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">
|
<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 */}
|
{/* 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 ? (
|
{collection.coverImageUrl ? (
|
||||||
<img
|
<img
|
||||||
src={collection.coverImageUrl}
|
src={collection.coverImageUrl}
|
||||||
|
|||||||
@ -87,6 +87,24 @@ const MondrianProductList = ({
|
|||||||
return () => window.removeEventListener("scroll", handleScroll);
|
return () => window.removeEventListener("scroll", handleScroll);
|
||||||
}, [fetchNextPage, hasNextPage, isFetchingNextPage]);
|
}, [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
|
// Memoize expensive filtering operations
|
||||||
const { oddProducts, evenProducts } = useMemo(() => {
|
const { oddProducts, evenProducts } = useMemo(() => {
|
||||||
const odd = products.filter((_, index) => index % 2 === 0);
|
const odd = products.filter((_, index) => index % 2 === 0);
|
||||||
@ -177,6 +195,7 @@ const MondrianProductList = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasNextPage && <MondrianSkeleton isMini={true} />}
|
{hasNextPage && <MondrianSkeleton isMini={true} />}
|
||||||
|
<div ref={loadMoreRef} className="h-px w-full" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -143,12 +143,12 @@ export default function CollectionDetail() {
|
|||||||
{/* Collection Header */}
|
{/* 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">
|
<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 */}
|
{/* 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 ? (
|
{collection.coverImageUrl ? (
|
||||||
<img
|
<img
|
||||||
src={collection.coverImageUrl}
|
src={collection.coverImageUrl}
|
||||||
alt={collection.title || "Collection"}
|
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">
|
<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">
|
<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 - بزرگ */}
|
{/* 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 ? (
|
{collection.coverImageUrl ? (
|
||||||
<img
|
<img
|
||||||
src={collection.coverImageUrl}
|
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="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 className="absolute inset-0 bg-gradient-to-t from-black/30 to-transparent" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-end gap-6 px-2 -mt-16 relative z-10">
|
<div className="flex items-start gap-6 px-2 mt-4 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="w-[130px] h-[130px] -mt-[86px] rounded-[28px] border-[5px] border-WHITE shadow-md overflow-hidden bg-WHITE3 shrink-0">
|
||||||
{seller?.storeLogo ? (
|
{seller?.storeLogo ? (
|
||||||
<img
|
<img
|
||||||
src={seller.storeLogo}
|
src={seller.storeLogo}
|
||||||
@ -208,7 +208,7 @@ export default function Seller() {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 pb-2">
|
<div className="flex-1">
|
||||||
<h1 className="flex items-center gap-2 text-[28px] font-extrabold">
|
<h1 className="flex items-center gap-2 text-[28px] font-extrabold">
|
||||||
{seller?.storeName}
|
{seller?.storeName}
|
||||||
{seller?.isVerified ? (
|
{seller?.isVerified ? (
|
||||||
@ -246,7 +246,7 @@ export default function Seller() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2.5 pb-2">
|
<div className="flex gap-2.5">
|
||||||
<Button
|
<Button
|
||||||
variant="dark"
|
variant="dark"
|
||||||
size="lg"
|
size="lg"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user