fix(ecommerce): CTA fijo de mobile ya no sigue hasta el footer
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
This commit is contained in:
1 parent
af9576f45d
commit
cd72962fa4
2 files changed
+21
-3
No files matched your search
@@ -101,7 +101,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
|||||||
carrusel. Confirmado en vivo: sin min-w-0 el grid medía 783px
|
carrusel. Confirmado en vivo: sin min-w-0 el grid medía 783px
|
||||||
en un viewport real de 380px; con min-w-0 vuelve a 380px. */}
|
en un viewport real de 380px; con min-w-0 vuelve a 380px. */}
|
||||||
{recommended.length > 0 && (
|
{recommended.length > 0 && (
|
||||||
<div className="min-w-0 pb-16 lg:col-span-2">
|
<div id="recommendations-section" className="min-w-0 pb-16 lg:col-span-2">
|
||||||
<h2 className="mb-7 text-3xl font-black">También te puede gustar</h2>
|
<h2 className="mb-7 text-3xl font-black">También te puede gustar</h2>
|
||||||
<ProductCarousel products={recommended} />
|
<ProductCarousel products={recommended} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const SCROLL_THRESHOLD_PX = 80;
|
|||||||
|
|
||||||
// Antes esta barra quedaba fija desde el primer render en mobile,
|
// Antes esta barra quedaba fija desde el primer render en mobile,
|
||||||
// tapando permanentemente parte de la imagen del producto solo para
|
// tapando permanentemente parte de la imagen del producto solo para
|
||||||
// duplicar un boton que ya está visible más abajo. Ahora combina dos
|
// duplicar un boton que ya está visible más abajo. Ahora combina tres
|
||||||
// condiciones, igual que el "Buy Now" sticky de Amazon:
|
// condiciones, igual que el "Buy Now" sticky de Amazon:
|
||||||
// - scrolled: recien aparece despues de que el usuario empieza a
|
// - scrolled: recien aparece despues de que el usuario empieza a
|
||||||
// scrollear (nunca sobre la vista inicial de la imagen/hero).
|
// scrollear (nunca sobre la vista inicial de la imagen/hero).
|
||||||
@@ -18,9 +18,16 @@ const SCROLL_THRESHOLD_PX = 80;
|
|||||||
// ProductPurchase, visible tambien en mobile) no esta en pantalla --
|
// ProductPurchase, visible tambien en mobile) no esta en pantalla --
|
||||||
// ya sea porque todavia no se llego a el, o porque se scrolleo mas
|
// ya sea porque todavia no se llego a el, o porque se scrolleo mas
|
||||||
// alla.
|
// 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 }) {
|
export function MobileAddToCart({ product }: { product: Product }) {
|
||||||
const addItem = useCartStore((state) => state.addItem);
|
const addItem = useCartStore((state) => state.addItem);
|
||||||
const [buttonVisible, setButtonVisible] = useState(true);
|
const [buttonVisible, setButtonVisible] = useState(true);
|
||||||
|
const [pastRecommendations, setPastRecommendations] = useState(false);
|
||||||
const [scrolled, setScrolled] = useState(false);
|
const [scrolled, setScrolled] = useState(false);
|
||||||
const outOfStock = product.variants[0]?.available === false;
|
const outOfStock = product.variants[0]?.available === false;
|
||||||
|
|
||||||
@@ -35,6 +42,17 @@ export function MobileAddToCart({ product }: { product: Product }) {
|
|||||||
return () => observer.disconnect();
|
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(() => {
|
useEffect(() => {
|
||||||
const onScroll = () => setScrolled(window.scrollY > SCROLL_THRESHOLD_PX);
|
const onScroll = () => setScrolled(window.scrollY > SCROLL_THRESHOLD_PX);
|
||||||
onScroll();
|
onScroll();
|
||||||
@@ -42,7 +60,7 @@ export function MobileAddToCart({ product }: { product: Product }) {
|
|||||||
return () => window.removeEventListener("scroll", onScroll);
|
return () => window.removeEventListener("scroll", onScroll);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const visible = scrolled && !buttonVisible;
|
const visible = scrolled && !buttonVisible && !pastRecommendations;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in new issue
Block a user