La barra de "Añadir al carrito" quedaba pegada abajo en mobile hasta el final de la página (incluso sobre el footer), mucho después de que el usuario ya pasó "También te puede gustar". Se agrega un tercer sentinel (#recommendations-section) observado por IntersectionObserver: una vez que su borde superior cruza el tope del viewport, la barra se oculta y no vuelve a aparecer bajando más. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012Y4df3Z7odr9Cg8F2nw6uV
85 lines
3.3 KiB
TypeScript
85 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { ShoppingBag } from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import type { Product } from "@/lib/headless/types";
|
|
import { useCartStore } from "@/store/cart-store";
|
|
|
|
const SCROLL_THRESHOLD_PX = 80;
|
|
|
|
// Antes esta barra quedaba fija desde el primer render en mobile,
|
|
// tapando permanentemente parte de la imagen del producto solo para
|
|
// duplicar un boton que ya está visible más abajo. Ahora combina tres
|
|
// condiciones, igual que el "Buy Now" sticky de Amazon:
|
|
// - scrolled: recien aparece despues de que el usuario empieza a
|
|
// scrollear (nunca sobre la vista inicial de la imagen/hero).
|
|
// - !buttonVisible: el boton real (#product-buy-button en
|
|
// ProductPurchase, visible tambien en mobile) no esta en pantalla --
|
|
// ya sea porque todavia no se llego a el, o porque se scrolleo mas
|
|
// alla.
|
|
// - !pastRecommendations: una vez que se llega a "También te puede
|
|
// gustar" (#recommendations-section en app/product/[slug]/page.tsx)
|
|
// la barra se oculta y ya no vuelve a aparecer bajando mas -- pedido
|
|
// explicito del usuario (antes quedaba pegada hasta el footer, sin
|
|
// sentido una vez que el contenido principal del producto ya quedó
|
|
// atrás).
|
|
export function MobileAddToCart({ product }: { product: Product }) {
|
|
const addItem = useCartStore((state) => state.addItem);
|
|
const [buttonVisible, setButtonVisible] = useState(true);
|
|
const [pastRecommendations, setPastRecommendations] = useState(false);
|
|
const [scrolled, setScrolled] = useState(false);
|
|
const outOfStock = product.variants[0]?.available === false;
|
|
|
|
useEffect(() => {
|
|
const target = document.getElementById("product-buy-button");
|
|
if (!target) return;
|
|
const observer = new IntersectionObserver((entries) => {
|
|
const entry = entries[0];
|
|
if (entry) setButtonVisible(entry.isIntersecting);
|
|
}, { threshold: 0 });
|
|
observer.observe(target);
|
|
return () => observer.disconnect();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const target = document.getElementById("recommendations-section");
|
|
if (!target) return;
|
|
const observer = new IntersectionObserver((entries) => {
|
|
const entry = entries[0];
|
|
if (entry) setPastRecommendations(entry.boundingClientRect.top <= 0);
|
|
}, { threshold: 0 });
|
|
observer.observe(target);
|
|
return () => observer.disconnect();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const onScroll = () => setScrolled(window.scrollY > SCROLL_THRESHOLD_PX);
|
|
onScroll();
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
return () => window.removeEventListener("scroll", onScroll);
|
|
}, []);
|
|
|
|
const visible = scrolled && !buttonVisible && !pastRecommendations;
|
|
|
|
return (
|
|
<div
|
|
aria-hidden={!visible}
|
|
className={`fixed inset-x-0 bottom-0 z-40 border-t border-slate-200 bg-white/95 p-3 shadow-2xl backdrop-blur transition-transform duration-200 lg:hidden ${
|
|
visible ? "translate-y-0" : "pointer-events-none translate-y-full"
|
|
}`}
|
|
>
|
|
<Button
|
|
variant="primary"
|
|
size="lg"
|
|
className="w-full"
|
|
disabled={outOfStock}
|
|
onClick={() => addItem(product, product.variants[0]?.id)}
|
|
>
|
|
<ShoppingBag />
|
|
{outOfStock ? "Agotado" : "Añadir al carrito"}
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|